Add support for line numbers in stack traces (Linux/Mac OS X only at this point)

* Add support for debug annotations on statement boundaries to the
  IR, to get accurate line/column information
* Use binary search to retrieve the function and line number for a given
  program counter
* Save the stack trace in the exception class and print it in v4
* Fix initial line number in QV4::Script to start a 1, just like the initial column
  in QQmlJS::Lexer also starts at 1

The native stack frame tracing is currently only implemented on Linux and Mac OS X.
The implementation for Windows using StackWalk64 is still missing.

Change-Id: I771fe44816397e29c69952772a772bf0d985236f
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
This commit is contained in:
Simon Hausmann 2013-05-25 15:31:23 +02:00 committed by Lars Knoll
parent 68827e982f
commit 8a822866b6
18 changed files with 336 additions and 103 deletions

View File

@ -26,6 +26,7 @@ protected:
virtual void visitCJump(V4IR::CJump *); virtual void visitCJump(V4IR::CJump *);
virtual void visitRet(V4IR::Ret *); virtual void visitRet(V4IR::Ret *);
virtual void visitTry(V4IR::Try *); virtual void visitTry(V4IR::Try *);
virtual void visitDebugAnnotation(V4IR::DebugAnnotation *) {}
virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result);
virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result);

View File

@ -116,6 +116,7 @@ struct ComputeUseDef: V4IR::StmtVisitor, V4IR::ExprVisitor
if (! _stmt->d->defs.contains(t->exceptionVar->index)) if (! _stmt->d->defs.contains(t->exceptionVar->index))
_stmt->d->defs.append(t->exceptionVar->index); _stmt->d->defs.append(t->exceptionVar->index);
} }
virtual void visitDebugAnnotation(V4IR::DebugAnnotation *) {}
virtual void visitTemp(V4IR::Temp *e) { virtual void visitTemp(V4IR::Temp *e) {
if (e->index < 0 || e->scope != 0) if (e->index < 0 || e->scope != 0)
@ -371,6 +372,7 @@ protected:
virtual void visitCJump(V4IR::CJump *s) { s->cond->accept(this); } virtual void visitCJump(V4IR::CJump *s) { s->cond->accept(this); }
virtual void visitRet(V4IR::Ret *s) { s->expr->accept(this); } virtual void visitRet(V4IR::Ret *s) { s->expr->accept(this); }
virtual void visitTry(V4IR::Try *) {} virtual void visitTry(V4IR::Try *) {}
virtual void visitDebugAnnotation(V4IR::DebugAnnotation *) {}
virtual void visitCall(V4IR::Call *e) { virtual void visitCall(V4IR::Call *e) {
e->base->accept(this); e->base->accept(this);
@ -550,6 +552,7 @@ protected:
virtual void visitCJump(V4IR::CJump *s) { s->cond->accept(this); } virtual void visitCJump(V4IR::CJump *s) { s->cond->accept(this); }
virtual void visitRet(V4IR::Ret *s) { s->expr->accept(this); } virtual void visitRet(V4IR::Ret *s) { s->expr->accept(this); }
virtual void visitTry(V4IR::Try *t) { visitTemp(t->exceptionVar); } virtual void visitTry(V4IR::Try *t) { visitTemp(t->exceptionVar); }
virtual void visitDebugAnnotation(V4IR::DebugAnnotation *) {}
virtual void visitTemp(V4IR::Temp *e) { virtual void visitTemp(V4IR::Temp *e) {
if (e->scope) // scoped local if (e->scope) // scoped local
@ -1340,6 +1343,7 @@ void Codegen::accept(Node *node)
void Codegen::statement(Statement *ast) void Codegen::statement(Statement *ast)
{ {
_block->DEBUGANNOTATION(ast->firstSourceLocation());
accept(ast); accept(ast);
} }

View File

@ -64,6 +64,10 @@
#include "qv4executableallocator_p.h" #include "qv4executableallocator_p.h"
#include "qv4sequenceobject_p.h" #include "qv4sequenceobject_p.h"
#if defined(Q_OS_LINUX) || defined(Q_OS_MAC)
#include <execinfo.h>
#endif
#ifdef V4_ENABLE_JIT #ifdef V4_ENABLE_JIT
# include "qv4isel_masm_p.h" # include "qv4isel_masm_p.h"
#else // !V4_ENABLE_JIT #else // !V4_ENABLE_JIT
@ -81,6 +85,7 @@ ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory)
, globalObject(0) , globalObject(0)
, globalCode(0) , globalCode(0)
, externalResourceComparison(0) , externalResourceComparison(0)
, functionsNeedSort(false)
, regExpCache(0) , regExpCache(0)
{ {
MemoryManager::GCBlocker gcBlocker(memoryManager); MemoryManager::GCBlocker gcBlocker(memoryManager);
@ -361,6 +366,7 @@ Function *ExecutionEngine::newFunction(const QString &name)
{ {
Function *f = new Function(newIdentifier(name)); Function *f = new Function(newIdentifier(name));
functions.append(f); functions.append(f);
functionsNeedSort = true;
return f; return f;
} }
@ -551,23 +557,100 @@ Object *ExecutionEngine::qmlContextObject() const
return static_cast<CallContext *>(ctx)->activation; return static_cast<CallContext *>(ctx)->activation;
} }
namespace {
struct NativeFrame {
Function *function;
int line;
};
struct NativeStackTrace
{
void *trace[100];
int nativeFrameCount;
int currentNativeFrame;
ExecutionEngine *engine;
NativeStackTrace(ExecutionContext *context)
{
engine = context->engine;
currentNativeFrame = 0;
#if defined(Q_OS_LINUX) || defined(Q_OS_MAC)
UnwindHelper::prepareForUnwind(context);
nativeFrameCount = backtrace(&trace[0], sizeof(trace) / sizeof(trace[0]));
#else
nativeFrameCount = 0;
#endif
}
NativeFrame nextFrame() {
NativeFrame frame;
frame.function = 0;
frame.line = -1;
for (; currentNativeFrame < nativeFrameCount && !frame.function; ++currentNativeFrame) {
quintptr pc = reinterpret_cast<quintptr>(trace[currentNativeFrame]);
// The pointers from the back trace point to the return address, but we are interested in
// the caller site.
pc = pc - 1;
Function *f = engine->functionForProgramCounter(pc);
if (!f)
continue;
frame.function = f;
frame.line = f->lineNumberForProgramCounter(pc);
}
return frame;
}
};
}
QVector<ExecutionEngine::StackFrame> ExecutionEngine::stackTrace(int frameLimit) const QVector<ExecutionEngine::StackFrame> ExecutionEngine::stackTrace(int frameLimit) const
{ {
NativeStackTrace nativeTrace(current);
QVector<StackFrame> stack; QVector<StackFrame> stack;
QV4::ExecutionContext *c = current; QV4::ExecutionContext *c = current;
while (c && frameLimit) { while (c && frameLimit) {
if (CallContext *c = c->asCallContext()) { if (CallContext *callCtx = c->asCallContext()) {
StackFrame frame; StackFrame frame;
frame.source = c->function->function->sourceFile; if (callCtx->function->function)
frame.function = c->function->name->toQString(); frame.source = callCtx->function->function->sourceFile;
frame.function = callCtx->function->name->toQString();
frame.line = -1; frame.line = -1;
frame.column = -1; frame.column = -1;
if (callCtx->function->function) {
// Try to complete the line number information
NativeFrame nativeFrame = nativeTrace.nextFrame();
if (nativeFrame.function == callCtx->function->function)
frame.line = nativeFrame.line;
}
stack.append(frame); stack.append(frame);
--frameLimit; --frameLimit;
} }
c = c->parent; c = c->parent;
} }
if (frameLimit && globalCode) {
StackFrame frame;
frame.source = globalCode->sourceFile;
frame.function = globalCode->name->toQString();
frame.line = -1;
frame.column = -1;
// Try to complete the line number information
NativeFrame nativeFrame = nativeTrace.nextFrame();
if (nativeFrame.function == globalCode)
frame.line = nativeFrame.line;
stack.append(frame);
}
return stack; return stack;
} }
@ -577,15 +660,10 @@ ExecutionEngine::StackFrame ExecutionEngine::currentStackFrame() const
frame.line = -1; frame.line = -1;
frame.column = -1; frame.column = -1;
QV4::ExecutionContext *c = current; QVector<StackFrame> trace = stackTrace(/*limit*/ 1);
while (c) { if (!trace.isEmpty())
if (CallContext *callCtx = c->asCallContext()) { frame = trace.first();
frame.source = callCtx->function->function->sourceFile;
frame.function = callCtx->function->name->toQString();
return frame;
}
c = c->parent;
}
return frame; return frame;
} }
@ -682,3 +760,70 @@ void ExecutionEngine::markObjects()
variantPrototype->mark(); variantPrototype->mark();
sequencePrototype->mark(); sequencePrototype->mark();
} }
namespace {
bool functionSortHelper(Function *lhs, Function *rhs)
{
return reinterpret_cast<quintptr>(lhs->code) < reinterpret_cast<quintptr>(rhs->code);
}
struct FindHelper
{
bool operator()(Function *function, quintptr pc)
{
return reinterpret_cast<quintptr>(function->code) < pc
&& (reinterpret_cast<quintptr>(function->code) + function->codeSize) < pc;
}
bool operator()(quintptr pc, Function *function)
{
return pc < reinterpret_cast<quintptr>(function->code);
}
};
}
Function *ExecutionEngine::functionForProgramCounter(quintptr pc) const
{
if (functionsNeedSort) {
qSort(functions.begin(), functions.end(), functionSortHelper);
functionsNeedSort = false;
}
QVector<Function*>::ConstIterator it = qBinaryFind(functions.constBegin(), functions.constEnd(),
pc, FindHelper());
if (it != functions.constEnd())
return *it;
return 0;
}
Exception::Exception(ExecutionContext *throwingContext, const Value &exceptionValue, int line)
: exception(exceptionValue)
, m_line(line)
{
m_file = throwingContext->currentFileName();
this->throwingContext = throwingContext->engine->current;
accepted = false;
m_stackTrace = throwingContext->engine->stackTrace();
}
Exception::~Exception()
{
assert(accepted);
}
void Exception::accept(ExecutionContext *catchingContext)
{
assert(!accepted);
accepted = true;
partiallyUnwindContext(catchingContext);
}
void Exception::partiallyUnwindContext(ExecutionContext *catchingContext)
{
if (!throwingContext)
return;
ExecutionContext *context = throwingContext;
while (context != catchingContext)
context = context->engine->popContext();
throwingContext = context;
}

View File

@ -200,7 +200,8 @@ struct Q_QML_EXPORT ExecutionEngine
String *id_uintMax; String *id_uintMax;
String *id_name; String *id_name;
QVector<Function *> functions; mutable QVector<Function *> functions;
mutable bool functionsNeedSort;
ExternalResourceComparison externalResourceComparison; ExternalResourceComparison externalResourceComparison;
@ -278,7 +279,8 @@ struct Q_QML_EXPORT ExecutionEngine
int line; int line;
int column; int column;
}; };
QVector<StackFrame> stackTrace(int frameLimit = -1) const; typedef QVector<StackFrame> StackTrace;
StackTrace stackTrace(int frameLimit = -1) const;
StackFrame currentStackFrame() const; StackFrame currentStackFrame() const;
void requireArgumentsAccessors(int n); void requireArgumentsAccessors(int n);
@ -288,6 +290,8 @@ struct Q_QML_EXPORT ExecutionEngine
void initRootContext(); void initRootContext();
InternalClass *newClass(const InternalClass &other); InternalClass *newClass(const InternalClass &other);
Function *functionForProgramCounter(quintptr pc) const;
}; };
inline void ExecutionEngine::pushContext(SimpleCallContext *context) inline void ExecutionEngine::pushContext(SimpleCallContext *context)
@ -309,6 +313,28 @@ inline ExecutionContext *ExecutionEngine::popContext()
return current; return current;
} }
struct Q_QML_EXPORT Exception {
explicit Exception(ExecutionContext *throwingContext, const Value &exceptionValue, int line);
~Exception();
void accept(ExecutionContext *catchingContext);
void partiallyUnwindContext(ExecutionContext *catchingContext);
Value value() const { return exception; }
QUrl file() const { return m_file; }
int lineNumber() const { return m_line; }
ExecutionEngine::StackTrace stackTrace() const { return m_stackTrace; }
private:
ExecutionContext *throwingContext;
bool accepted;
PersistentValue exception;
QUrl m_file;
int m_line;
ExecutionEngine::StackTrace m_stackTrace;
};
} // namespace QV4 } // namespace QV4

View File

@ -65,3 +65,24 @@ void Function::mark()
for (int i = 0; i < identifiers.size(); ++i) for (int i = 0; i < identifiers.size(); ++i)
identifiers.at(i)->mark(); identifiers.at(i)->mark();
} }
namespace {
bool operator<(const LineNumberMapping &mapping, quintptr pc)
{
return mapping.codeOffset < pc;
}
}
int Function::lineNumberForProgramCounter(quintptr pc) const
{
quint32 offset = pc - reinterpret_cast<quintptr>(code);
QVector<LineNumberMapping>::ConstIterator it = qLowerBound(lineNumberMappings.begin(), lineNumberMappings.end(), offset);
if (it != lineNumberMappings.constBegin() && lineNumberMappings.count() > 0)
--it;
if (it == lineNumberMappings.constEnd())
return -1;
return it->lineNumber;
}

View File

@ -80,6 +80,12 @@ struct URIErrorPrototype;
struct InternalClass; struct InternalClass;
struct Lookup; struct Lookup;
struct LineNumberMapping
{
quint32 codeOffset;
int lineNumber;
};
struct Function { struct Function {
String *name; String *name;
@ -104,6 +110,7 @@ struct Function {
bool isNamedExpression; bool isNamedExpression;
QUrl sourceFile; QUrl sourceFile;
QVector<LineNumberMapping> lineNumberMappings;
Function(String *name) Function(String *name)
: name(name) : name(name)
@ -123,9 +130,12 @@ struct Function {
inline bool needsActivation() const { return hasNestedFunctions || hasDirectEval || usesArgumentsObject; } inline bool needsActivation() const { return hasNestedFunctions || hasDirectEval || usesArgumentsObject; }
void mark(); void mark();
int lineNumberForProgramCounter(quintptr pc) const;
}; };
} }
Q_DECLARE_TYPEINFO(QV4::LineNumberMapping, Q_PRIMITIVE_TYPE);
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -430,6 +430,15 @@ static void printDisassembledOutputWithCalls(const char* output, const QHash<voi
} }
#endif #endif
void Assembler::recordLineNumber(int lineNumber)
{
CodeLineNumerMapping mapping;
mapping.location = label();
mapping.lineNumber = lineNumber;
codeLineNumberMappings << mapping;
}
void Assembler::link(QV4::Function *vmFunc) void Assembler::link(QV4::Function *vmFunc)
{ {
Label endOfCode = label(); Label endOfCode = label();
@ -455,6 +464,14 @@ void Assembler::link(QV4::Function *vmFunc)
JSC::LinkBuffer linkBuffer(dummy, this, 0); JSC::LinkBuffer linkBuffer(dummy, this, 0);
vmFunc->codeSize = linkBuffer.offsetOf(endOfCode); vmFunc->codeSize = linkBuffer.offsetOf(endOfCode);
vmFunc->lineNumberMappings.resize(codeLineNumberMappings.count());
for (int i = 0; i < codeLineNumberMappings.count(); ++i) {
QV4::LineNumberMapping mapping;
mapping.codeOffset = linkBuffer.offsetOf(codeLineNumberMappings.at(i).location);
mapping.lineNumber = codeLineNumberMappings.at(i).lineNumber;
vmFunc->lineNumberMappings[i] = mapping;
}
QHash<void*, const char*> functions; QHash<void*, const char*> functions;
foreach (CallToLink ctl, _callsToLink) { foreach (CallToLink ctl, _callsToLink) {
linkBuffer.link(ctl.call, ctl.externalFunction); linkBuffer.link(ctl.call, ctl.externalFunction);
@ -782,6 +799,11 @@ void InstructionSelection::visitTry(V4IR::Try *t)
_as->jump(Assembler::ReturnValueRegister); _as->jump(Assembler::ReturnValueRegister);
} }
void InstructionSelection::visitDebugAnnotation(V4IR::DebugAnnotation *annotation)
{
_as->recordLineNumber(annotation->location.startLine);
}
void InstructionSelection::callBuiltinFinishTry() void InstructionSelection::callBuiltinFinishTry()
{ {
// This assumes that we're in code that was called by tryWrapper, so we return to try wrapper // This assumes that we're in code that was called by tryWrapper, so we return to try wrapper

View File

@ -725,6 +725,8 @@ public:
void link(QV4::Function *vmFunc); void link(QV4::Function *vmFunc);
void recordLineNumber(int lineNumber);
private: private:
V4IR::Function *_function; V4IR::Function *_function;
QV4::Function *_vmFunction; QV4::Function *_vmFunction;
@ -741,6 +743,13 @@ private:
QHash<V4IR::BasicBlock *, QVector<DataLabelPtr> > _labelPatches; QHash<V4IR::BasicBlock *, QVector<DataLabelPtr> > _labelPatches;
QV4::ExecutionEngine *_engine; QV4::ExecutionEngine *_engine;
struct CodeLineNumerMapping
{
Assembler::Label location;
int lineNumber;
};
QVector<CodeLineNumerMapping> codeLineNumberMappings;
}; };
class Q_QML_EXPORT InstructionSelection: class Q_QML_EXPORT InstructionSelection:
@ -838,6 +847,7 @@ protected:
virtual void visitCJump(V4IR::CJump *); virtual void visitCJump(V4IR::CJump *);
virtual void visitRet(V4IR::Ret *); virtual void visitRet(V4IR::Ret *);
virtual void visitTry(V4IR::Try *); virtual void visitTry(V4IR::Try *);
virtual void visitDebugAnnotation(V4IR::DebugAnnotation *);
private: private:
#define isel_stringIfyx(s) #s #define isel_stringIfyx(s) #s

View File

@ -221,6 +221,8 @@ struct RemoveSharedExpressions: V4IR::StmtVisitor, V4IR::ExprVisitor
// nothing to do for Try statements // nothing to do for Try statements
} }
virtual void visitDebugAnnotation(DebugAnnotation *) {}
// expressions // expressions
virtual void visitConst(Const *) {} virtual void visitConst(Const *) {}
virtual void visitString(String *) {} virtual void visitString(String *) {}
@ -543,6 +545,11 @@ void Try::dump(QTextStream &out, Stmt::Mode mode)
out << " with the name " << exceptionVarName << " and go to L" << catchBlock->index << ';'; out << " with the name " << exceptionVarName << " and go to L" << catchBlock->index << ';';
} }
void DebugAnnotation::dump(QTextStream &out, Mode mode)
{
out << "// line: " << location.startLine << " ; column: " << location.startColumn;
}
Function *Module::newFunction(const QString &name, Function *outer) Function *Module::newFunction(const QString &name, Function *outer)
{ {
Function *f = new Function(this, outer, name); Function *f = new Function(this, outer, name);
@ -851,6 +858,16 @@ Stmt *BasicBlock::TRY(BasicBlock *tryBlock, BasicBlock *catchBlock, const QStrin
return t; return t;
} }
Stmt *BasicBlock::DEBUGANNOTATION(const AST::SourceLocation &location)
{
if (isTerminated())
return 0;
DebugAnnotation *t = function->New<DebugAnnotation>();
t->init(location);
statements.append(t);
}
void BasicBlock::dump(QTextStream &out, Stmt::Mode mode) void BasicBlock::dump(QTextStream &out, Stmt::Mode mode)
{ {
out << 'L' << index << ':' << endl; out << 'L' << index << ':' << endl;

View File

@ -54,6 +54,7 @@
#include "qv4global_p.h" #include "qv4global_p.h"
#include <private/qqmljsmemorypool_p.h> #include <private/qqmljsmemorypool_p.h>
#include <private/qqmljsastfwd_p.h>
#include <QtCore/QVector> #include <QtCore/QVector>
#include <QtCore/QString> #include <QtCore/QString>
@ -118,6 +119,7 @@ struct Jump;
struct CJump; struct CJump;
struct Ret; struct Ret;
struct Try; struct Try;
struct DebugAnnotation;
enum AluOp { enum AluOp {
OpInvalid = 0, OpInvalid = 0,
@ -198,6 +200,7 @@ struct StmtVisitor {
virtual void visitCJump(CJump *) = 0; virtual void visitCJump(CJump *) = 0;
virtual void visitRet(Ret *) = 0; virtual void visitRet(Ret *) = 0;
virtual void visitTry(Try *) = 0; virtual void visitTry(Try *) = 0;
virtual void visitDebugAnnotation(DebugAnnotation *) = 0;
}; };
struct Expr { struct Expr {
@ -492,6 +495,7 @@ struct Stmt {
virtual CJump *asCJump() { return 0; } virtual CJump *asCJump() { return 0; }
virtual Ret *asRet() { return 0; } virtual Ret *asRet() { return 0; }
virtual Try *asTry() { return 0; } virtual Try *asTry() { return 0; }
virtual DebugAnnotation *asDebugAnnotation() { return 0; }
virtual void dump(QTextStream &out, Mode mode = HIR) = 0; virtual void dump(QTextStream &out, Mode mode = HIR) = 0;
void destroyData() { void destroyData() {
@ -629,6 +633,20 @@ struct Try: Stmt {
virtual void dump(QTextStream &out, Mode mode); virtual void dump(QTextStream &out, Mode mode);
}; };
struct DebugAnnotation: Stmt {
AST::SourceLocation location;
void init(const AST::SourceLocation &location)
{
this->location = location;
}
virtual void accept(StmtVisitor *v) { v->visitDebugAnnotation(this); }
virtual DebugAnnotation *asDebugAnnotation() { return this; }
virtual void dump(QTextStream &out, Mode mode);
};
struct Q_QML_EXPORT Module { struct Q_QML_EXPORT Module {
MemoryPool pool; MemoryPool pool;
QVector<Function *> functions; QVector<Function *> functions;
@ -768,6 +786,7 @@ struct BasicBlock {
Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse); Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse);
Stmt *RET(Temp *expr); Stmt *RET(Temp *expr);
Stmt *TRY(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString &exceptionVarName, Temp *exceptionVar); Stmt *TRY(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString &exceptionVarName, Temp *exceptionVar);
Stmt *DEBUGANNOTATION(const AST::SourceLocation &location);
void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR);
}; };

View File

@ -69,38 +69,6 @@
using namespace QV4; using namespace QV4;
Exception::Exception(ExecutionContext *throwingContext, const Value &exceptionValue, int line)
: exception(exceptionValue)
, m_line(line)
{
m_file = throwingContext->currentFileName();
this->throwingContext = throwingContext->engine->current;
accepted = false;
}
Exception::~Exception()
{
assert(accepted);
}
void Exception::accept(ExecutionContext *catchingContext)
{
assert(!accepted);
accepted = true;
partiallyUnwindContext(catchingContext);
}
void Exception::partiallyUnwindContext(ExecutionContext *catchingContext)
{
if (!throwingContext)
return;
ExecutionContext *context = throwingContext;
while (context != catchingContext)
context = context->engine->popContext();
throwingContext = context;
}
extern "C" { extern "C" {
void __qmljs_numberToString(QString *result, double num, int radix) void __qmljs_numberToString(QString *result, double num, int radix)
@ -954,18 +922,7 @@ void __qmljs_throw(ExecutionContext *context, const Value &value, int line)
if (context->engine->debugger) if (context->engine->debugger)
context->engine->debugger->aboutToThrow(value); context->engine->debugger->aboutToThrow(value);
for (ExecutionContext *ctx = context; ctx; ctx = ctx->parent) { UnwindHelper::prepareForUnwind(context);
if (CallContext *callCtx = ctx->asCallContext())
if (FunctionObject *fobj = callCtx->function)
if (Function *fun = fobj->function)
UnwindHelper::ensureUnwindInfo(fun);
for (ExecutionContext::EvalCode *code = ctx->currentEvalCode;
code; code = code->next)
UnwindHelper::ensureUnwindInfo(code->function);
}
if (context->engine->globalCode)
UnwindHelper::ensureUnwindInfo(context->engine->globalCode);
#if USE(LIBUNWIND_DEBUG) #if USE(LIBUNWIND_DEBUG)
printf("about to throw exception. walking stack first with libunwind:\n"); printf("about to throw exception. walking stack first with libunwind:\n");

View File

@ -89,27 +89,6 @@ struct ArrayObject;
struct ErrorObject; struct ErrorObject;
struct ExecutionEngine; struct ExecutionEngine;
struct InternalClass; struct InternalClass;
struct Q_QML_EXPORT Exception {
explicit Exception(ExecutionContext *throwingContext, const Value &exceptionValue, int line);
~Exception();
void accept(ExecutionContext *catchingContext);
void partiallyUnwindContext(ExecutionContext *catchingContext);
Value value() const { return exception; }
QUrl file() const { return m_file; }
int lineNumber() const { return m_line; }
private:
ExecutionContext *throwingContext;
bool accepted;
PersistentValue exception;
QUrl m_file;
int m_line;
};
} }
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -51,11 +51,11 @@ namespace QV4 {
struct ExecutionContext; struct ExecutionContext;
struct Q_QML_EXPORT Script { struct Q_QML_EXPORT Script {
Script(ExecutionContext *scope, const QString &sourceCode, const QString &source = QString(), int line = 0, int column = 0) Script(ExecutionContext *scope, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0)
: sourceFile(source), line(line), column(column), sourceCode(sourceCode) : sourceFile(source), line(line), column(column), sourceCode(sourceCode)
, scope(scope), strictMode(false), inheritContext(false), parsed(false), qml(0) , scope(scope), strictMode(false), inheritContext(false), parsed(false), qml(0)
, vmFunction(0) {} , vmFunction(0) {}
Script(ExecutionEngine *engine, Object *qml, const QString &sourceCode, const QString &source = QString(), int line = 0, int column = 0) Script(ExecutionEngine *engine, Object *qml, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0)
: sourceFile(source), line(line), column(column), sourceCode(sourceCode) : sourceFile(source), line(line), column(column), sourceCode(sourceCode)
, scope(engine->rootContext), strictMode(true), inheritContext(true), parsed(false) , scope(engine->rootContext), strictMode(true), inheritContext(true), parsed(false)
, qml(qml), vmFunction(0) {} , qml(qml), vmFunction(0) {}

View File

@ -29,7 +29,7 @@
#ifdef USE_NULL_HELPER #ifdef USE_NULL_HELPER
using namespace QV4; using namespace QV4;
void UnwindHelper::ensureUnwindInfo(Function *function) {Q_UNUSED(function);} void UnwindHelper::prepareForUnwind(ExecutionContext *) {}
void UnwindHelper::registerFunction(Function *function) {Q_UNUSED(function);} void UnwindHelper::registerFunction(Function *function) {Q_UNUSED(function);}
void UnwindHelper::registerFunctions(const QVector<Function *> &functions) {Q_UNUSED(functions);} void UnwindHelper::registerFunctions(const QVector<Function *> &functions) {Q_UNUSED(functions);}
void UnwindHelper::deregisterFunction(Function *function) {Q_UNUSED(function);} void UnwindHelper::deregisterFunction(Function *function) {Q_UNUSED(function);}

View File

@ -116,9 +116,8 @@ void UnwindHelper::registerFunctions(const QVector<Function *> &functions)
allFunctions.insert(reinterpret_cast<quintptr>(f->code), f); allFunctions.insert(reinterpret_cast<quintptr>(f->code), f);
} }
void UnwindHelper::ensureUnwindInfo(Function *function) void UnwindHelper::prepareForUnwind(ExecutionContext *)
{ {
Q_UNUSED(function);
} }
int UnwindHelper::unwindInfoSize() int UnwindHelper::unwindInfoSize()

View File

@ -84,7 +84,7 @@ UnwindInfo::~UnwindInfo()
__deregister_frame(data.data() + fde_offset); __deregister_frame(data.data() + fde_offset);
} }
void UnwindHelper::ensureUnwindInfo(Function *f) static void ensureUnwindInfo(Function *f)
{ {
if (!f->codeRef) if (!f->codeRef)
return; // Not a JIT generated function return; // Not a JIT generated function
@ -112,6 +112,22 @@ void UnwindHelper::ensureUnwindInfo(Function *f)
chunk->unwindInfo = new UnwindInfo(info); chunk->unwindInfo = new UnwindInfo(info);
} }
void UnwindHelper::prepareForUnwind(ExecutionContext *context)
{
for (ExecutionContext *ctx = context; ctx; ctx = ctx->parent) {
if (CallContext *callCtx = ctx->asCallContext())
if (FunctionObject *fobj = callCtx->function)
if (Function *fun = fobj->function)
ensureUnwindInfo(fun);
for (ExecutionContext::EvalCode *code = ctx->currentEvalCode;
code; code = code->next)
ensureUnwindInfo(code->function);
}
if (context->engine->globalCode)
ensureUnwindInfo(context->engine->globalCode);
}
void UnwindHelper::registerFunction(Function *) void UnwindHelper::registerFunction(Function *)
{ {
} }

View File

@ -6,11 +6,12 @@
namespace QV4 { namespace QV4 {
struct Function; struct Function;
struct ExecutionContext;
class UnwindHelper class UnwindHelper
{ {
public: public:
static void ensureUnwindInfo(Function *function); static void prepareForUnwind(ExecutionContext *ctx);
static void registerFunction(Function *function); static void registerFunction(Function *function);
static void registerFunctions(const QVector<Function *> &functions); static void registerFunctions(const QVector<Function *> &functions);
static void deregisterFunction(Function *function); static void deregisterFunction(Function *function);

View File

@ -120,26 +120,32 @@ DEFINE_MANAGED_VTABLE(GC);
} // builtins } // builtins
static void showException(QV4::ExecutionContext *ctx, const QV4::Value &exception) static void showException(QV4::ExecutionContext *ctx, const QV4::Exception &exception)
{ {
QV4::ErrorObject *e = exception.asErrorObject(); QV4::ErrorObject *e = exception.value().asErrorObject();
if (!e) { if (!e) {
std::cerr << "Uncaught exception: " << qPrintable(exception.toString(ctx)->toQString()) << std::endl; std::cerr << "Uncaught exception: " << qPrintable(exception.value().toString(ctx)->toQString()) << std::endl;
return; } else {
if (QV4::SyntaxErrorObject *err = e->asSyntaxError()) {
QV4::DiagnosticMessage *msg = err->message();
if (!msg) {
std::cerr << "Uncaught exception: Syntax error" << std::endl;
return;
}
for (; msg; msg = msg->next) {
std::cerr << qPrintable(msg->buildFullMessage(ctx)->toQString()) << std::endl;
}
} else {
std::cerr << "Uncaught exception: " << qPrintable(e->get(ctx, ctx->engine->newString(QStringLiteral("message")), 0).toString(ctx)->toQString()) << std::endl;
}
} }
if (QV4::SyntaxErrorObject *err = e->asSyntaxError()) { foreach (const QV4::ExecutionEngine::StackFrame &frame, exception.stackTrace()) {
QV4::DiagnosticMessage *msg = err->message(); std::cerr << " at " << qPrintable(frame.function) << " (" << qPrintable(frame.source.toLocalFile());
if (!msg) { if (frame.line >= 0)
std::cerr << "Uncaught exception: Syntax error" << std::endl; std::cerr << ":" << frame.line;
return; std::cerr << ")" << std::endl;
}
for (; msg; msg = msg->next) {
std::cerr << qPrintable(msg->buildFullMessage(ctx)->toQString()) << std::endl;
}
} else {
std::cerr << "Uncaught exception: " << qPrintable(e->get(ctx, ctx->engine->newString(QStringLiteral("message")), 0).toString(ctx)->toQString()) << std::endl;
} }
} }
@ -390,7 +396,7 @@ int main(int argc, char *argv[])
} }
} catch (QV4::Exception& ex) { } catch (QV4::Exception& ex) {
ex.accept(ctx); ex.accept(ctx);
showException(ctx, ex.value()); showException(ctx, ex);
return EXIT_FAILURE; return EXIT_FAILURE;
} }