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"
|
# 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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
6
v4.pro
|
@ -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 {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue