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:
parent
36356a4b27
commit
a14e7549c4
|
@ -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;
|
||||
}
|
|
@ -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
|
26
main.cpp
26
main.cpp
|
@ -43,6 +43,7 @@
|
|||
# include "qv4_llvm_p.h"
|
||||
#endif
|
||||
|
||||
#include "debugging.h"
|
||||
#include "qmljs_objects.h"
|
||||
#include "qmljs_runtime.h"
|
||||
#include "qv4codegen_p.h"
|
||||
|
@ -183,7 +184,7 @@ int compile(const QString &fileName, const QString &source, QQmlJS::LLVMOutputTy
|
|||
using namespace AST;
|
||||
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?
|
||||
/*IR::Function *globalCode =*/ cg(program, &module);
|
||||
|
||||
|
@ -246,6 +247,14 @@ int main(int argc, char *argv[])
|
|||
#ifndef QMLJS_NO_LLVM
|
||||
QQmlJS::LLVMOutputType fileType = QQmlJS::LLVMOutputObject;
|
||||
#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.first() == QLatin1String("--jit")) {
|
||||
|
@ -287,7 +296,7 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
#endif // QMLJS_NO_LLVM
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -314,7 +323,14 @@ int main(int argc, char *argv[])
|
|||
iSelFactory.reset(new QQmlJS::Moth::ISelFactory);
|
||||
else
|
||||
iSelFactory.reset(new QQmlJS::MASM::ISelFactory);
|
||||
|
||||
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::Object *globalObject = vm.globalObject.objectValue();
|
||||
|
@ -338,11 +354,15 @@ int main(int argc, char *argv[])
|
|||
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)
|
||||
continue;
|
||||
ctx->lexicalEnvironment->strictMode = f->isStrict;
|
||||
if (debugger)
|
||||
debugger->aboutToCall(0, ctx);
|
||||
QQmlJS::VM::Value result = f->code(ctx, f->codeData);
|
||||
if (debugger)
|
||||
debugger->justLeft(ctx);
|
||||
if (!result.isUndefined()) {
|
||||
if (! qgetenv("SHOW_EXIT_VALUE").isEmpty())
|
||||
std::cout << "exit value: " << qPrintable(result.toString(ctx)->toQString()) << std::endl;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "qv4isel_util_p.h"
|
||||
#include "qv4isel_moth_p.h"
|
||||
#include "qv4vme_moth_p.h"
|
||||
#include "debugging.h"
|
||||
|
||||
using namespace QQmlJS;
|
||||
using namespace QQmlJS::Moth;
|
||||
|
@ -209,7 +210,10 @@ void InstructionSelection::operator()(IR::Function *function)
|
|||
addInstruction(push);
|
||||
|
||||
foreach (_block, _function->basicBlocks) {
|
||||
ptrdiff_t blockOffset = _ccode - _code;
|
||||
_addrs.insert(_block, _ccode - _code);
|
||||
if (_engine->debugger)
|
||||
_engine->debugger->addaddBasicBlockOffset(_function, _block, blockOffset);
|
||||
|
||||
foreach (IR::Stmt *s, _block->statements)
|
||||
s->accept(this);
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#include "qv4vme_moth_p.h"
|
||||
#include "qv4instr_moth_p.h"
|
||||
#include "qmljs_value.h"
|
||||
#include "debugging.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef DO_TRACE_INSTR
|
||||
# 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)
|
||||
|
||||
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
|
||||
|
||||
QVector<VM::Value> stack;
|
||||
FunctionState state(context, &stack, &code);
|
||||
|
||||
#ifdef MOTH_THREADED_INTERPRETER
|
||||
const Instr *genericInstr = reinterpret_cast<const Instr *>(code);
|
||||
|
|
|
@ -62,6 +62,7 @@ struct StringPool
|
|||
|
||||
ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
|
||||
: iselFactory(factory)
|
||||
, debugger(0)
|
||||
{
|
||||
stringPool = new StringPool;
|
||||
|
||||
|
|
|
@ -47,6 +47,11 @@
|
|||
#include <setjmp.h>
|
||||
|
||||
namespace QQmlJS {
|
||||
|
||||
namespace Debugging {
|
||||
class Debugger;
|
||||
} // namespace Debugging
|
||||
|
||||
namespace VM {
|
||||
|
||||
struct Value;
|
||||
|
@ -87,6 +92,8 @@ struct ExecutionEngine
|
|||
ExecutionContext *current;
|
||||
ExecutionContext *rootContext;
|
||||
|
||||
Debugging::Debugger *debugger;
|
||||
|
||||
Value globalObject;
|
||||
|
||||
Value objectCtor;
|
||||
|
@ -184,7 +191,6 @@ struct ExecutionEngine
|
|||
Object *newForEachIteratorObject(Object *o);
|
||||
};
|
||||
|
||||
|
||||
} // namespace VM
|
||||
} // namespace QQmlJS
|
||||
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "debugging.h"
|
||||
#include <qmljs_environment.h>
|
||||
#include <qmljs_objects.h>
|
||||
#include <qv4ecmaobjects_p.h>
|
||||
|
@ -315,6 +317,9 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha
|
|||
lexicalEnvironment = variableEnvironment;
|
||||
|
||||
thisObject = that;
|
||||
|
||||
if (engine->debugger)
|
||||
engine->debugger->aboutToCall(f, this);
|
||||
}
|
||||
|
||||
void ExecutionContext::leaveCallContext()
|
||||
|
@ -327,6 +332,9 @@ void ExecutionContext::leaveCallContext()
|
|||
|
||||
delete variableEnvironment;
|
||||
variableEnvironment = 0;
|
||||
|
||||
if (engine->debugger)
|
||||
engine->debugger->justLeft(this);
|
||||
}
|
||||
|
||||
void ExecutionContext::initConstructorContext(ExecutionContext *parent, Value that, FunctionObject *f, Value *args, unsigned argc)
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#include "qmljs_objects.h"
|
||||
#include "qv4ir_p.h"
|
||||
#include "qv4isel_p.h"
|
||||
|
@ -564,7 +563,7 @@ QQmlJS::IR::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct
|
|||
return 0;
|
||||
}
|
||||
|
||||
Codegen cg;
|
||||
Codegen cg(ctx->engine->debugger);
|
||||
globalCode = cg(program, &module, mode);
|
||||
if (globalCode) {
|
||||
// only generate other functions if global code generation succeeded.
|
||||
|
|
|
@ -516,6 +516,8 @@ struct FunctionObject: Object {
|
|||
Value construct(ExecutionContext *context, Value *args, int argc);
|
||||
virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc);
|
||||
|
||||
virtual struct ScriptFunction *asScriptFunction() { return 0; }
|
||||
|
||||
protected:
|
||||
virtual Value call(ExecutionContext *ctx);
|
||||
virtual Value construct(ExecutionContext *ctx);
|
||||
|
@ -537,6 +539,8 @@ struct ScriptFunction: FunctionObject {
|
|||
|
||||
virtual Value call(ExecutionContext *ctx);
|
||||
virtual Value construct(ExecutionContext *ctx);
|
||||
|
||||
virtual ScriptFunction *asScriptFunction() { return this; }
|
||||
};
|
||||
|
||||
struct EvalFunction : FunctionObject
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "debugging.h"
|
||||
#include "qmljs_runtime.h"
|
||||
#include "qmljs_objects.h"
|
||||
#include "qv4ir_p.h"
|
||||
|
@ -777,6 +778,9 @@ void __qmljs_throw(Value value, ExecutionContext *context)
|
|||
{
|
||||
assert(!context->engine->unwindStack.isEmpty());
|
||||
|
||||
if (context->engine->debugger)
|
||||
context->engine->debugger->aboutToThrow(&value);
|
||||
|
||||
ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last();
|
||||
|
||||
// clean up call contexts
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
****************************************************************************/
|
||||
|
||||
#include "qv4codegen_p.h"
|
||||
#include "debugging.h"
|
||||
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QSet>
|
||||
|
@ -352,7 +353,7 @@ protected:
|
|||
QStack<Environment *> _envStack;
|
||||
};
|
||||
|
||||
Codegen::Codegen()
|
||||
Codegen::Codegen(Debugging::Debugger *debugger)
|
||||
: _module(0)
|
||||
, _function(0)
|
||||
, _block(0)
|
||||
|
@ -363,6 +364,7 @@ Codegen::Codegen()
|
|||
, _env(0)
|
||||
, _loop(0)
|
||||
, _labelledStatement(0)
|
||||
, _debugger(debugger)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -377,6 +379,12 @@ IR::Function *Codegen::operator()(Program *node, IR::Module *module, Mode mode)
|
|||
scan(node);
|
||||
|
||||
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) {
|
||||
linearize(function);
|
||||
|
@ -397,6 +405,8 @@ IR::Function *Codegen::operator()(AST::FunctionExpression *ast, IR::Module *modu
|
|||
scan(ast);
|
||||
|
||||
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) {
|
||||
linearize(function);
|
||||
|
@ -1168,6 +1178,8 @@ bool Codegen::visit(FieldMemberExpression *ast)
|
|||
bool Codegen::visit(FunctionExpression *ast)
|
||||
{
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
|
@ -1570,6 +1582,8 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast,
|
|||
|
||||
enterEnvironment(ast);
|
||||
IR::Function *function = _module->newFunction(name);
|
||||
if (_debugger)
|
||||
_debugger->addFunction(function);
|
||||
IR::BasicBlock *entryBlock = function->newBasicBlock();
|
||||
IR::BasicBlock *exitBlock = function->newBasicBlock(IR::Function::DontInsertBlock);
|
||||
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) {
|
||||
IR::Function *function = defineFunction(f->name.toString(), f, f->formals,
|
||||
f->body ? f->body->elements : 0);
|
||||
if (_debugger)
|
||||
_debugger->setSourceLocation(function, f->functionToken.startLine, f->functionToken.startColumn);
|
||||
if (! _env->parent)
|
||||
move(_block->NAME(f->name.toString(), f->identifierToken.startLine, f->identifierToken.startColumn),
|
||||
_block->CLOSURE(function));
|
||||
|
|
|
@ -50,10 +50,14 @@ namespace AST {
|
|||
class UiParameterList;
|
||||
}
|
||||
|
||||
namespace Debugging {
|
||||
class Debugger;
|
||||
} // namespace Debugging
|
||||
|
||||
class Codegen: protected AST::Visitor
|
||||
{
|
||||
public:
|
||||
Codegen();
|
||||
Codegen(Debugging::Debugger *debugger);
|
||||
|
||||
enum Mode {
|
||||
GlobalCode,
|
||||
|
@ -330,6 +334,7 @@ private:
|
|||
AST::LabelledStatement *_labelledStatement;
|
||||
QHash<AST::Node *, Environment *> _envMap;
|
||||
QHash<AST::FunctionExpression *, int> _functionMap;
|
||||
Debugging::Debugger *_debugger;
|
||||
|
||||
class ScanFunctions;
|
||||
};
|
||||
|
|
|
@ -1892,7 +1892,7 @@ Value FunctionCtor::construct(ExecutionContext *ctx)
|
|||
|
||||
IR::Module module;
|
||||
|
||||
Codegen cg;
|
||||
Codegen cg(ctx->engine->debugger);
|
||||
IR::Function *irf = cg(fe, &module);
|
||||
|
||||
EvalInstructionSelection *isel = ctx->engine->iselFactory->create(ctx->engine);
|
||||
|
|
|
@ -720,6 +720,7 @@ void LLVMInstructionSelection::visitClosure(IR::Closure *e)
|
|||
{
|
||||
llvm::Value *tmp = newLLVMTemp(_valueTy);
|
||||
llvm::Value *clos = getLLVMFunction(e->value);
|
||||
assert("!broken: pass function name!");
|
||||
CreateCall3(_llvmModule->getFunction("__qmljs_llvm_init_native_function"),
|
||||
_llvmFunction->arg_begin(), tmp, clos);
|
||||
_llvmValue = CreateLoad(tmp);
|
||||
|
|
6
v4.pro
6
v4.pro
|
@ -23,7 +23,8 @@ SOURCES += main.cpp \
|
|||
qv4array.cpp \
|
||||
qv4isel_masm.cpp \
|
||||
llvm_runtime.cpp \
|
||||
qv4isel_p.cpp
|
||||
qv4isel_p.cpp \
|
||||
debugging.cpp
|
||||
|
||||
HEADERS += \
|
||||
qv4codegen_p.h \
|
||||
|
@ -39,7 +40,8 @@ HEADERS += \
|
|||
qv4array_p.h \
|
||||
qv4isel_masm_p.h \
|
||||
qv4isel_p.h \
|
||||
qv4isel_util_p.h
|
||||
qv4isel_util_p.h \
|
||||
debugging.h
|
||||
|
||||
llvm {
|
||||
|
||||
|
|
Loading…
Reference in New Issue