Add some debugging infrastructure to the interpreter.

This currently mainly intended to be useful in a C++ debugger. The
infrastructure makes it a lot easier to access (parent) contexts, find
function names, etc.

Change-Id: I0493d3a3bd4bf5c3a03379c1a2b545ed76862cd5
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
This commit is contained in:
Erik Verbruggen 2012-11-29 14:41:26 +01:00 committed by Lars Knoll
parent 36356a4b27
commit a14e7549c4
16 changed files with 421 additions and 11 deletions

183
debugging.cpp Normal file
View File

@ -0,0 +1,183 @@
/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "debugging.h"
#include "qmljs_objects.h"
#include <iostream>
using namespace QQmlJS;
using namespace QQmlJS::Debugging;
FunctionState::FunctionState(VM::ExecutionContext *context)
: _context(context)
{
if (debugger())
debugger()->enterFunction(this);
}
FunctionState::~FunctionState()
{
if (debugger())
debugger()->leaveFunction(this);
}
VM::Value *FunctionState::argument(unsigned idx)
{
if (idx < _context->variableEnvironment->argumentCount)
return _context->variableEnvironment->arguments + idx;
else
return 0;
}
VM::Value *FunctionState::local(unsigned idx)
{
if (idx < _context->variableEnvironment->varCount)
return _context->variableEnvironment->locals + idx;
else
return 0;
}
Debugger::Debugger(VM::ExecutionEngine *engine)
: _engine(engine)
{
}
Debugger::~Debugger()
{
qDeleteAll(_functionInfo.values());
}
void Debugger::addFunction(IR::Function *function)
{
_functionInfo.insert(function, new FunctionDebugInfo(function));
}
void Debugger::addaddBasicBlockOffset(IR::Function *function, IR::BasicBlock *block, ptrdiff_t blockOffset)
{
_functionInfo[function]->addBasicBlockOffset(block, blockOffset);
}
void Debugger::setSourceLocation(IR::Function *function, unsigned line, unsigned column)
{
_functionInfo[function]->setSourceLocation(line, column);
}
FunctionDebugInfo *Debugger::debugInfo(VM::FunctionObject *function) const
{
if (!function)
return 0;
if (VM::ScriptFunction *sf = function->asScriptFunction())
return _functionInfo[sf->function];
else
return 0;
}
QString Debugger::name(VM::FunctionObject *function) const
{
if (FunctionDebugInfo *i = debugInfo(function))
if (i->function)
if (const QString *n = i->function->name)
return *n;
return QString();
}
void Debugger::aboutToCall(VM::FunctionObject *function, VM::ExecutionContext *context)
{
_callStack.append(CallInfo(context, function));
}
void Debugger::justLeft(VM::ExecutionContext *context)
{
int idx = callIndex(context);
if (idx < 0)
qDebug() << "Oops, leaving a function that was not registered...?";
else
_callStack.resize(idx);
}
void Debugger::enterFunction(FunctionState *state)
{
_callStack[callIndex(state->context())].state = state;
#ifdef DO_TRACE_INSTR
QString n = name(_callStack[callIndex(state->context())].function);
std::cerr << "*** Entering \"" << qPrintable(n) << "\" with" << state->context()->variableEnvironment->argumentCount << "args" << std::endl;
for (unsigned i = 0; i < state->context()->variableEnvironment->argumentCount; ++i)
std::cerr << " " << i << ": " << currentArg(i) << std::endl;
#endif // DO_TRACE_INSTR
}
void Debugger::leaveFunction(FunctionState *state)
{
_callStack[callIndex(state->context())].state = 0;
}
void Debugger::aboutToThrow(VM::Value *value)
{
qDebug() << "*** We are about to throw...:" << value->toString(currentState()->context())->toQString();
}
FunctionState *Debugger::currentState() const
{
if (_callStack.isEmpty())
return 0;
else
return _callStack.last().state;
}
const char *Debugger::currentArg(unsigned idx) const
{
FunctionState *state = currentState();
return qPrintable(state->argument(idx)->toString(state->context())->toQString());
}
const char *Debugger::currentLocal(unsigned idx) const
{
FunctionState *state = currentState();
return qPrintable(state->local(idx)->toString(state->context())->toQString());
}
const char *Debugger::currentTemp(unsigned idx) const
{
FunctionState *state = currentState();
return qPrintable(state->temp(idx)->toString(state->context())->toQString());
}
int Debugger::callIndex(VM::ExecutionContext *context)
{
for (int idx = _callStack.size() - 1; idx >= 0; --idx) {
if (_callStack[idx].context == context)
return idx;
}
return -1;
}

137
debugging.h Normal file
View File

@ -0,0 +1,137 @@
/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef DEBUGGING_H
#define DEBUGGING_H
#include "qmljs_engine.h"
#include "qmljs_environment.h"
#include <QHash>
namespace QQmlJS {
namespace IR {
struct BasicBlock;
struct Function;
} // namespace IR
namespace Debugging {
class Debugger;
struct FunctionDebugInfo { // TODO: use opaque d-pointers here
IR::Function *function;
QHash<ptrdiff_t, IR::BasicBlock *> blockOffsets;
unsigned startLine, startColumn;
FunctionDebugInfo(IR::Function *function): function(function), startLine(0), startColumn(0) {}
void addBasicBlockOffset(IR::BasicBlock *block, ptrdiff_t offset) {
blockOffsets.insert(offset, block);
}
void setSourceLocation(unsigned line, unsigned column)
{ startLine = line; startColumn = column; }
};
class FunctionState
{
public:
FunctionState(VM::ExecutionContext *context);
virtual ~FunctionState();
virtual VM::Value *argument(unsigned idx);
virtual VM::Value *local(unsigned idx);
virtual VM::Value *temp(unsigned idx) = 0;
VM::ExecutionContext *context() const
{ return _context; }
Debugger *debugger() const
{ return _context->engine->debugger; }
private:
VM::ExecutionContext *_context;
};
struct CallInfo
{
VM::ExecutionContext *context;
VM::FunctionObject *function;
FunctionState *state;
CallInfo(VM::ExecutionContext *context = 0, VM::FunctionObject *function = 0, FunctionState *state = 0)
: context(context)
, function(function)
, state(state)
{}
};
class Debugger
{
public:
Debugger(VM::ExecutionEngine *_engine);
~Debugger();
public: // compile-time interface
void addFunction(IR::Function *function);
void addaddBasicBlockOffset(IR::Function *function, IR::BasicBlock *block, ptrdiff_t blockOffset);
void setSourceLocation(IR::Function *function, unsigned line, unsigned column);
public: // run-time querying interface
FunctionDebugInfo *debugInfo(VM::FunctionObject *function) const;
QString name(VM::FunctionObject *function) const;
public: // execution hooks
void aboutToCall(VM::FunctionObject *function, VM::ExecutionContext *context);
void justLeft(VM::ExecutionContext *context);
void enterFunction(FunctionState *state);
void leaveFunction(FunctionState *state);
void aboutToThrow(VM::Value *value);
public: // debugging hooks
FunctionState *currentState() const;
const char *currentArg(unsigned idx) const;
const char *currentLocal(unsigned idx) const;
const char *currentTemp(unsigned idx) const;
private:
int callIndex(VM::ExecutionContext *context);
private: // TODO: use opaque d-pointers here
VM::ExecutionEngine *_engine;
QHash<IR::Function *, FunctionDebugInfo *> _functionInfo;
QVector<CallInfo> _callStack;
};
} // namespace Debugging
} // namespace QQmlJS
#endif // DEBUGGING_H

View File

@ -43,6 +43,7 @@
# include "qv4_llvm_p.h" # include "qv4_llvm_p.h"
#endif #endif
#include "debugging.h"
#include "qmljs_objects.h" #include "qmljs_objects.h"
#include "qmljs_runtime.h" #include "qmljs_runtime.h"
#include "qv4codegen_p.h" #include "qv4codegen_p.h"
@ -183,7 +184,7 @@ int compile(const QString &fileName, const QString &source, QQmlJS::LLVMOutputTy
using namespace AST; using namespace AST;
Program *program = AST::cast<Program *>(parser.rootNode()); Program *program = AST::cast<Program *>(parser.rootNode());
Codegen cg; Codegen cg(0);
// FIXME: if the program is empty, we should we generate an empty %entry, or give an error? // FIXME: if the program is empty, we should we generate an empty %entry, or give an error?
/*IR::Function *globalCode =*/ cg(program, &module); /*IR::Function *globalCode =*/ cg(program, &module);
@ -246,6 +247,14 @@ int main(int argc, char *argv[])
#ifndef QMLJS_NO_LLVM #ifndef QMLJS_NO_LLVM
QQmlJS::LLVMOutputType fileType = QQmlJS::LLVMOutputObject; QQmlJS::LLVMOutputType fileType = QQmlJS::LLVMOutputObject;
#endif // QMLJS_NO_LLVM #endif // QMLJS_NO_LLVM
bool enableDebugging = false;
if (!args.isEmpty()) {
if (args.first() == QLatin1String("-d") || args.first() == QLatin1String("--debug")) {
enableDebugging = true;
args.removeFirst();
}
}
if (!args.isEmpty()) { if (!args.isEmpty()) {
if (args.first() == QLatin1String("--jit")) { if (args.first() == QLatin1String("--jit")) {
@ -287,7 +296,7 @@ int main(int argc, char *argv[])
} }
#endif // QMLJS_NO_LLVM #endif // QMLJS_NO_LLVM
if (args.first() == QLatin1String("--help")) { if (args.first() == QLatin1String("--help")) {
std::cerr << "Usage: v4 [|--jit|--interpret|--compile|--aot|--llvm-jit] file..." << std::endl; std::cerr << "Usage: v4 [|--debug|-d] [|--jit|--interpret|--compile|--aot|--llvm-jit] file..." << std::endl;
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
} }
@ -314,7 +323,14 @@ int main(int argc, char *argv[])
iSelFactory.reset(new QQmlJS::Moth::ISelFactory); iSelFactory.reset(new QQmlJS::Moth::ISelFactory);
else else
iSelFactory.reset(new QQmlJS::MASM::ISelFactory); iSelFactory.reset(new QQmlJS::MASM::ISelFactory);
QQmlJS::VM::ExecutionEngine vm(iSelFactory.data()); QQmlJS::VM::ExecutionEngine vm(iSelFactory.data());
QScopedPointer<QQmlJS::Debugging::Debugger> debugger;
if (enableDebugging)
debugger.reset(new QQmlJS::Debugging::Debugger(&vm));
vm.debugger = debugger.data();
QQmlJS::VM::ExecutionContext *ctx = vm.rootContext; QQmlJS::VM::ExecutionContext *ctx = vm.rootContext;
QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue();
@ -338,11 +354,15 @@ int main(int argc, char *argv[])
return EXIT_FAILURE; return EXIT_FAILURE;
} }
QQmlJS::IR::Function *f = QQmlJS::VM::EvalFunction::parseSource(vm.rootContext, fn, code, QQmlJS::Codegen::GlobalCode); QQmlJS::IR::Function *f = QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode);
if (!f) if (!f)
continue; continue;
ctx->lexicalEnvironment->strictMode = f->isStrict; ctx->lexicalEnvironment->strictMode = f->isStrict;
if (debugger)
debugger->aboutToCall(0, ctx);
QQmlJS::VM::Value result = f->code(ctx, f->codeData); QQmlJS::VM::Value result = f->code(ctx, f->codeData);
if (debugger)
debugger->justLeft(ctx);
if (!result.isUndefined()) { if (!result.isUndefined()) {
if (! qgetenv("SHOW_EXIT_VALUE").isEmpty()) if (! qgetenv("SHOW_EXIT_VALUE").isEmpty())
std::cout << "exit value: " << qPrintable(result.toString(ctx)->toQString()) << std::endl; std::cout << "exit value: " << qPrintable(result.toString(ctx)->toQString()) << std::endl;

View File

@ -1,6 +1,7 @@
#include "qv4isel_util_p.h" #include "qv4isel_util_p.h"
#include "qv4isel_moth_p.h" #include "qv4isel_moth_p.h"
#include "qv4vme_moth_p.h" #include "qv4vme_moth_p.h"
#include "debugging.h"
using namespace QQmlJS; using namespace QQmlJS;
using namespace QQmlJS::Moth; using namespace QQmlJS::Moth;
@ -209,7 +210,10 @@ void InstructionSelection::operator()(IR::Function *function)
addInstruction(push); addInstruction(push);
foreach (_block, _function->basicBlocks) { foreach (_block, _function->basicBlocks) {
ptrdiff_t blockOffset = _ccode - _code;
_addrs.insert(_block, _ccode - _code); _addrs.insert(_block, _ccode - _code);
if (_engine->debugger)
_engine->debugger->addaddBasicBlockOffset(_function, _block, blockOffset);
foreach (IR::Stmt *s, _block->statements) foreach (IR::Stmt *s, _block->statements)
s->accept(this); s->accept(this);

View File

@ -1,6 +1,9 @@
#include "qv4vme_moth_p.h" #include "qv4vme_moth_p.h"
#include "qv4instr_moth_p.h" #include "qv4instr_moth_p.h"
#include "qmljs_value.h" #include "qmljs_value.h"
#include "debugging.h"
#include <iostream>
#ifdef DO_TRACE_INSTR #ifdef DO_TRACE_INSTR
# define TRACE_INSTR(I) fprintf(stderr, "executing a %s\n", #I); # define TRACE_INSTR(I) fprintf(stderr, "executing a %s\n", #I);
@ -92,6 +95,22 @@ static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, QVecto
} }
} }
class FunctionState: public Debugging::FunctionState
{
public:
FunctionState(QQmlJS::VM::ExecutionContext *context, QVector<VM::Value> *stack, const uchar **code)
: Debugging::FunctionState(context)
, stack(stack)
, code(code)
{}
virtual VM::Value *temp(unsigned idx) { return stack->data() + idx; }
private:
QVector<VM::Value> *stack;
const uchar **code;
};
#define TEMP(index) *tempValue(context, stack, index) #define TEMP(index) *tempValue(context, stack, index)
VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *code VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *code
@ -114,6 +133,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co
#endif #endif
QVector<VM::Value> stack; QVector<VM::Value> stack;
FunctionState state(context, &stack, &code);
#ifdef MOTH_THREADED_INTERPRETER #ifdef MOTH_THREADED_INTERPRETER
const Instr *genericInstr = reinterpret_cast<const Instr *>(code); const Instr *genericInstr = reinterpret_cast<const Instr *>(code);

View File

@ -62,6 +62,7 @@ struct StringPool
ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
: iselFactory(factory) : iselFactory(factory)
, debugger(0)
{ {
stringPool = new StringPool; stringPool = new StringPool;

View File

@ -47,6 +47,11 @@
#include <setjmp.h> #include <setjmp.h>
namespace QQmlJS { namespace QQmlJS {
namespace Debugging {
class Debugger;
} // namespace Debugging
namespace VM { namespace VM {
struct Value; struct Value;
@ -87,6 +92,8 @@ struct ExecutionEngine
ExecutionContext *current; ExecutionContext *current;
ExecutionContext *rootContext; ExecutionContext *rootContext;
Debugging::Debugger *debugger;
Value globalObject; Value globalObject;
Value objectCtor; Value objectCtor;
@ -184,7 +191,6 @@ struct ExecutionEngine
Object *newForEachIteratorObject(Object *o); Object *newForEachIteratorObject(Object *o);
}; };
} // namespace VM } // namespace VM
} // namespace QQmlJS } // namespace QQmlJS

View File

@ -38,6 +38,8 @@
** $QT_END_LICENSE$ ** $QT_END_LICENSE$
** **
****************************************************************************/ ****************************************************************************/
#include "debugging.h"
#include <qmljs_environment.h> #include <qmljs_environment.h>
#include <qmljs_objects.h> #include <qmljs_objects.h>
#include <qv4ecmaobjects_p.h> #include <qv4ecmaobjects_p.h>
@ -315,6 +317,9 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha
lexicalEnvironment = variableEnvironment; lexicalEnvironment = variableEnvironment;
thisObject = that; thisObject = that;
if (engine->debugger)
engine->debugger->aboutToCall(f, this);
} }
void ExecutionContext::leaveCallContext() void ExecutionContext::leaveCallContext()
@ -327,6 +332,9 @@ void ExecutionContext::leaveCallContext()
delete variableEnvironment; delete variableEnvironment;
variableEnvironment = 0; variableEnvironment = 0;
if (engine->debugger)
engine->debugger->justLeft(this);
} }
void ExecutionContext::initConstructorContext(ExecutionContext *parent, Value that, FunctionObject *f, Value *args, unsigned argc) void ExecutionContext::initConstructorContext(ExecutionContext *parent, Value that, FunctionObject *f, Value *args, unsigned argc)

View File

@ -39,7 +39,6 @@
** **
****************************************************************************/ ****************************************************************************/
#include "qmljs_objects.h" #include "qmljs_objects.h"
#include "qv4ir_p.h" #include "qv4ir_p.h"
#include "qv4isel_p.h" #include "qv4isel_p.h"
@ -564,7 +563,7 @@ QQmlJS::IR::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct
return 0; return 0;
} }
Codegen cg; Codegen cg(ctx->engine->debugger);
globalCode = cg(program, &module, mode); globalCode = cg(program, &module, mode);
if (globalCode) { if (globalCode) {
// only generate other functions if global code generation succeeded. // only generate other functions if global code generation succeeded.

View File

@ -516,6 +516,8 @@ struct FunctionObject: Object {
Value construct(ExecutionContext *context, Value *args, int argc); Value construct(ExecutionContext *context, Value *args, int argc);
virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc);
virtual struct ScriptFunction *asScriptFunction() { return 0; }
protected: protected:
virtual Value call(ExecutionContext *ctx); virtual Value call(ExecutionContext *ctx);
virtual Value construct(ExecutionContext *ctx); virtual Value construct(ExecutionContext *ctx);
@ -537,6 +539,8 @@ struct ScriptFunction: FunctionObject {
virtual Value call(ExecutionContext *ctx); virtual Value call(ExecutionContext *ctx);
virtual Value construct(ExecutionContext *ctx); virtual Value construct(ExecutionContext *ctx);
virtual ScriptFunction *asScriptFunction() { return this; }
}; };
struct EvalFunction : FunctionObject struct EvalFunction : FunctionObject

View File

@ -39,6 +39,7 @@
** **
****************************************************************************/ ****************************************************************************/
#include "debugging.h"
#include "qmljs_runtime.h" #include "qmljs_runtime.h"
#include "qmljs_objects.h" #include "qmljs_objects.h"
#include "qv4ir_p.h" #include "qv4ir_p.h"
@ -777,6 +778,9 @@ void __qmljs_throw(Value value, ExecutionContext *context)
{ {
assert(!context->engine->unwindStack.isEmpty()); assert(!context->engine->unwindStack.isEmpty());
if (context->engine->debugger)
context->engine->debugger->aboutToThrow(&value);
ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last();
// clean up call contexts // clean up call contexts

View File

@ -40,6 +40,7 @@
****************************************************************************/ ****************************************************************************/
#include "qv4codegen_p.h" #include "qv4codegen_p.h"
#include "debugging.h"
#include <QtCore/QStringList> #include <QtCore/QStringList>
#include <QtCore/QSet> #include <QtCore/QSet>
@ -352,7 +353,7 @@ protected:
QStack<Environment *> _envStack; QStack<Environment *> _envStack;
}; };
Codegen::Codegen() Codegen::Codegen(Debugging::Debugger *debugger)
: _module(0) : _module(0)
, _function(0) , _function(0)
, _block(0) , _block(0)
@ -363,6 +364,7 @@ Codegen::Codegen()
, _env(0) , _env(0)
, _loop(0) , _loop(0)
, _labelledStatement(0) , _labelledStatement(0)
, _debugger(debugger)
{ {
} }
@ -377,6 +379,12 @@ IR::Function *Codegen::operator()(Program *node, IR::Module *module, Mode mode)
scan(node); scan(node);
IR::Function *globalCode = defineFunction(QStringLiteral("%entry"), node, 0, node->elements, mode); IR::Function *globalCode = defineFunction(QStringLiteral("%entry"), node, 0, node->elements, mode);
if (_debugger) {
if (node->elements->element) {
SourceLocation loc = node->elements->element->firstSourceLocation();
_debugger->setSourceLocation(globalCode, loc.startLine, loc.startColumn);
}
}
foreach (IR::Function *function, _module->functions) { foreach (IR::Function *function, _module->functions) {
linearize(function); linearize(function);
@ -397,6 +405,8 @@ IR::Function *Codegen::operator()(AST::FunctionExpression *ast, IR::Module *modu
scan(ast); scan(ast);
IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0);
if (_debugger)
_debugger->setSourceLocation(function, ast->functionToken.startLine, ast->functionToken.startColumn);
foreach (IR::Function *function, _module->functions) { foreach (IR::Function *function, _module->functions) {
linearize(function); linearize(function);
@ -1168,6 +1178,8 @@ bool Codegen::visit(FieldMemberExpression *ast)
bool Codegen::visit(FunctionExpression *ast) bool Codegen::visit(FunctionExpression *ast)
{ {
IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0);
if (_debugger)
_debugger->setSourceLocation(function, ast->functionToken.startLine, ast->functionToken.startColumn);
_expr.code = _block->CLOSURE(function); _expr.code = _block->CLOSURE(function);
return false; return false;
} }
@ -1570,6 +1582,8 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast,
enterEnvironment(ast); enterEnvironment(ast);
IR::Function *function = _module->newFunction(name); IR::Function *function = _module->newFunction(name);
if (_debugger)
_debugger->addFunction(function);
IR::BasicBlock *entryBlock = function->newBasicBlock(); IR::BasicBlock *entryBlock = function->newBasicBlock();
IR::BasicBlock *exitBlock = function->newBasicBlock(IR::Function::DontInsertBlock); IR::BasicBlock *exitBlock = function->newBasicBlock(IR::Function::DontInsertBlock);
IR::BasicBlock *throwBlock = function->newBasicBlock(); IR::BasicBlock *throwBlock = function->newBasicBlock();
@ -1626,6 +1640,8 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast,
foreach (AST::FunctionDeclaration *f, _env->functions) { foreach (AST::FunctionDeclaration *f, _env->functions) {
IR::Function *function = defineFunction(f->name.toString(), f, f->formals, IR::Function *function = defineFunction(f->name.toString(), f, f->formals,
f->body ? f->body->elements : 0); f->body ? f->body->elements : 0);
if (_debugger)
_debugger->setSourceLocation(function, f->functionToken.startLine, f->functionToken.startColumn);
if (! _env->parent) if (! _env->parent)
move(_block->NAME(f->name.toString(), f->identifierToken.startLine, f->identifierToken.startColumn), move(_block->NAME(f->name.toString(), f->identifierToken.startLine, f->identifierToken.startColumn),
_block->CLOSURE(function)); _block->CLOSURE(function));

View File

@ -50,10 +50,14 @@ namespace AST {
class UiParameterList; class UiParameterList;
} }
namespace Debugging {
class Debugger;
} // namespace Debugging
class Codegen: protected AST::Visitor class Codegen: protected AST::Visitor
{ {
public: public:
Codegen(); Codegen(Debugging::Debugger *debugger);
enum Mode { enum Mode {
GlobalCode, GlobalCode,
@ -330,6 +334,7 @@ private:
AST::LabelledStatement *_labelledStatement; AST::LabelledStatement *_labelledStatement;
QHash<AST::Node *, Environment *> _envMap; QHash<AST::Node *, Environment *> _envMap;
QHash<AST::FunctionExpression *, int> _functionMap; QHash<AST::FunctionExpression *, int> _functionMap;
Debugging::Debugger *_debugger;
class ScanFunctions; class ScanFunctions;
}; };

View File

@ -1892,7 +1892,7 @@ Value FunctionCtor::construct(ExecutionContext *ctx)
IR::Module module; IR::Module module;
Codegen cg; Codegen cg(ctx->engine->debugger);
IR::Function *irf = cg(fe, &module); IR::Function *irf = cg(fe, &module);
EvalInstructionSelection *isel = ctx->engine->iselFactory->create(ctx->engine); EvalInstructionSelection *isel = ctx->engine->iselFactory->create(ctx->engine);

View File

@ -720,6 +720,7 @@ void LLVMInstructionSelection::visitClosure(IR::Closure *e)
{ {
llvm::Value *tmp = newLLVMTemp(_valueTy); llvm::Value *tmp = newLLVMTemp(_valueTy);
llvm::Value *clos = getLLVMFunction(e->value); llvm::Value *clos = getLLVMFunction(e->value);
assert("!broken: pass function name!");
CreateCall3(_llvmModule->getFunction("__qmljs_llvm_init_native_function"), CreateCall3(_llvmModule->getFunction("__qmljs_llvm_init_native_function"),
_llvmFunction->arg_begin(), tmp, clos); _llvmFunction->arg_begin(), tmp, clos);
_llvmValue = CreateLoad(tmp); _llvmValue = CreateLoad(tmp);

6
v4.pro
View File

@ -23,7 +23,8 @@ SOURCES += main.cpp \
qv4array.cpp \ qv4array.cpp \
qv4isel_masm.cpp \ qv4isel_masm.cpp \
llvm_runtime.cpp \ llvm_runtime.cpp \
qv4isel_p.cpp qv4isel_p.cpp \
debugging.cpp
HEADERS += \ HEADERS += \
qv4codegen_p.h \ qv4codegen_p.h \
@ -39,7 +40,8 @@ HEADERS += \
qv4array_p.h \ qv4array_p.h \
qv4isel_masm_p.h \ qv4isel_masm_p.h \
qv4isel_p.h \ qv4isel_p.h \
qv4isel_util_p.h qv4isel_util_p.h \
debugging.h
llvm { llvm {