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:
parent
68827e982f
commit
8a822866b6
|
@ -26,6 +26,7 @@ protected:
|
|||
virtual void visitCJump(V4IR::CJump *);
|
||||
virtual void visitRet(V4IR::Ret *);
|
||||
virtual void visitTry(V4IR::Try *);
|
||||
virtual void visitDebugAnnotation(V4IR::DebugAnnotation *) {}
|
||||
|
||||
virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result);
|
||||
virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result);
|
||||
|
|
|
@ -116,6 +116,7 @@ struct ComputeUseDef: V4IR::StmtVisitor, V4IR::ExprVisitor
|
|||
if (! _stmt->d->defs.contains(t->exceptionVar->index))
|
||||
_stmt->d->defs.append(t->exceptionVar->index);
|
||||
}
|
||||
virtual void visitDebugAnnotation(V4IR::DebugAnnotation *) {}
|
||||
|
||||
virtual void visitTemp(V4IR::Temp *e) {
|
||||
if (e->index < 0 || e->scope != 0)
|
||||
|
@ -371,6 +372,7 @@ protected:
|
|||
virtual void visitCJump(V4IR::CJump *s) { s->cond->accept(this); }
|
||||
virtual void visitRet(V4IR::Ret *s) { s->expr->accept(this); }
|
||||
virtual void visitTry(V4IR::Try *) {}
|
||||
virtual void visitDebugAnnotation(V4IR::DebugAnnotation *) {}
|
||||
|
||||
virtual void visitCall(V4IR::Call *e) {
|
||||
e->base->accept(this);
|
||||
|
@ -550,6 +552,7 @@ protected:
|
|||
virtual void visitCJump(V4IR::CJump *s) { s->cond->accept(this); }
|
||||
virtual void visitRet(V4IR::Ret *s) { s->expr->accept(this); }
|
||||
virtual void visitTry(V4IR::Try *t) { visitTemp(t->exceptionVar); }
|
||||
virtual void visitDebugAnnotation(V4IR::DebugAnnotation *) {}
|
||||
|
||||
virtual void visitTemp(V4IR::Temp *e) {
|
||||
if (e->scope) // scoped local
|
||||
|
@ -1340,6 +1343,7 @@ void Codegen::accept(Node *node)
|
|||
|
||||
void Codegen::statement(Statement *ast)
|
||||
{
|
||||
_block->DEBUGANNOTATION(ast->firstSourceLocation());
|
||||
accept(ast);
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,10 @@
|
|||
#include "qv4executableallocator_p.h"
|
||||
#include "qv4sequenceobject_p.h"
|
||||
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_MAC)
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
|
||||
#ifdef V4_ENABLE_JIT
|
||||
# include "qv4isel_masm_p.h"
|
||||
#else // !V4_ENABLE_JIT
|
||||
|
@ -81,6 +85,7 @@ ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory)
|
|||
, globalObject(0)
|
||||
, globalCode(0)
|
||||
, externalResourceComparison(0)
|
||||
, functionsNeedSort(false)
|
||||
, regExpCache(0)
|
||||
{
|
||||
MemoryManager::GCBlocker gcBlocker(memoryManager);
|
||||
|
@ -361,6 +366,7 @@ Function *ExecutionEngine::newFunction(const QString &name)
|
|||
{
|
||||
Function *f = new Function(newIdentifier(name));
|
||||
functions.append(f);
|
||||
functionsNeedSort = true;
|
||||
return f;
|
||||
}
|
||||
|
||||
|
@ -551,23 +557,100 @@ Object *ExecutionEngine::qmlContextObject() const
|
|||
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
|
||||
{
|
||||
NativeStackTrace nativeTrace(current);
|
||||
|
||||
QVector<StackFrame> stack;
|
||||
|
||||
QV4::ExecutionContext *c = current;
|
||||
while (c && frameLimit) {
|
||||
if (CallContext *c = c->asCallContext()) {
|
||||
if (CallContext *callCtx = c->asCallContext()) {
|
||||
StackFrame frame;
|
||||
frame.source = c->function->function->sourceFile;
|
||||
frame.function = c->function->name->toQString();
|
||||
if (callCtx->function->function)
|
||||
frame.source = callCtx->function->function->sourceFile;
|
||||
frame.function = callCtx->function->name->toQString();
|
||||
frame.line = -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);
|
||||
--frameLimit;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -577,15 +660,10 @@ ExecutionEngine::StackFrame ExecutionEngine::currentStackFrame() const
|
|||
frame.line = -1;
|
||||
frame.column = -1;
|
||||
|
||||
QV4::ExecutionContext *c = current;
|
||||
while (c) {
|
||||
if (CallContext *callCtx = c->asCallContext()) {
|
||||
frame.source = callCtx->function->function->sourceFile;
|
||||
frame.function = callCtx->function->name->toQString();
|
||||
return frame;
|
||||
}
|
||||
c = c->parent;
|
||||
}
|
||||
QVector<StackFrame> trace = stackTrace(/*limit*/ 1);
|
||||
if (!trace.isEmpty())
|
||||
frame = trace.first();
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
|
@ -682,3 +760,70 @@ void ExecutionEngine::markObjects()
|
|||
variantPrototype->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;
|
||||
}
|
||||
|
|
|
@ -200,7 +200,8 @@ struct Q_QML_EXPORT ExecutionEngine
|
|||
String *id_uintMax;
|
||||
String *id_name;
|
||||
|
||||
QVector<Function *> functions;
|
||||
mutable QVector<Function *> functions;
|
||||
mutable bool functionsNeedSort;
|
||||
|
||||
ExternalResourceComparison externalResourceComparison;
|
||||
|
||||
|
@ -278,7 +279,8 @@ struct Q_QML_EXPORT ExecutionEngine
|
|||
int line;
|
||||
int column;
|
||||
};
|
||||
QVector<StackFrame> stackTrace(int frameLimit = -1) const;
|
||||
typedef QVector<StackFrame> StackTrace;
|
||||
StackTrace stackTrace(int frameLimit = -1) const;
|
||||
StackFrame currentStackFrame() const;
|
||||
|
||||
void requireArgumentsAccessors(int n);
|
||||
|
@ -288,6 +290,8 @@ struct Q_QML_EXPORT ExecutionEngine
|
|||
void initRootContext();
|
||||
|
||||
InternalClass *newClass(const InternalClass &other);
|
||||
|
||||
Function *functionForProgramCounter(quintptr pc) const;
|
||||
};
|
||||
|
||||
inline void ExecutionEngine::pushContext(SimpleCallContext *context)
|
||||
|
@ -309,6 +313,28 @@ inline ExecutionContext *ExecutionEngine::popContext()
|
|||
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
|
||||
|
||||
|
|
|
@ -65,3 +65,24 @@ void Function::mark()
|
|||
for (int i = 0; i < identifiers.size(); ++i)
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -80,6 +80,12 @@ struct URIErrorPrototype;
|
|||
struct InternalClass;
|
||||
struct Lookup;
|
||||
|
||||
struct LineNumberMapping
|
||||
{
|
||||
quint32 codeOffset;
|
||||
int lineNumber;
|
||||
};
|
||||
|
||||
struct Function {
|
||||
String *name;
|
||||
|
||||
|
@ -104,6 +110,7 @@ struct Function {
|
|||
bool isNamedExpression;
|
||||
|
||||
QUrl sourceFile;
|
||||
QVector<LineNumberMapping> lineNumberMappings;
|
||||
|
||||
Function(String *name)
|
||||
: name(name)
|
||||
|
@ -123,9 +130,12 @@ struct Function {
|
|||
inline bool needsActivation() const { return hasNestedFunctions || hasDirectEval || usesArgumentsObject; }
|
||||
|
||||
void mark();
|
||||
|
||||
int lineNumberForProgramCounter(quintptr pc) const;
|
||||
};
|
||||
|
||||
}
|
||||
Q_DECLARE_TYPEINFO(QV4::LineNumberMapping, Q_PRIMITIVE_TYPE);
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
|
|
@ -430,6 +430,15 @@ static void printDisassembledOutputWithCalls(const char* output, const QHash<voi
|
|||
}
|
||||
#endif
|
||||
|
||||
void Assembler::recordLineNumber(int lineNumber)
|
||||
{
|
||||
CodeLineNumerMapping mapping;
|
||||
mapping.location = label();
|
||||
mapping.lineNumber = lineNumber;
|
||||
codeLineNumberMappings << mapping;
|
||||
}
|
||||
|
||||
|
||||
void Assembler::link(QV4::Function *vmFunc)
|
||||
{
|
||||
Label endOfCode = label();
|
||||
|
@ -455,6 +464,14 @@ void Assembler::link(QV4::Function *vmFunc)
|
|||
JSC::LinkBuffer linkBuffer(dummy, this, 0);
|
||||
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;
|
||||
foreach (CallToLink ctl, _callsToLink) {
|
||||
linkBuffer.link(ctl.call, ctl.externalFunction);
|
||||
|
@ -782,6 +799,11 @@ void InstructionSelection::visitTry(V4IR::Try *t)
|
|||
_as->jump(Assembler::ReturnValueRegister);
|
||||
}
|
||||
|
||||
void InstructionSelection::visitDebugAnnotation(V4IR::DebugAnnotation *annotation)
|
||||
{
|
||||
_as->recordLineNumber(annotation->location.startLine);
|
||||
}
|
||||
|
||||
void InstructionSelection::callBuiltinFinishTry()
|
||||
{
|
||||
// This assumes that we're in code that was called by tryWrapper, so we return to try wrapper
|
||||
|
|
|
@ -725,6 +725,8 @@ public:
|
|||
|
||||
void link(QV4::Function *vmFunc);
|
||||
|
||||
void recordLineNumber(int lineNumber);
|
||||
|
||||
private:
|
||||
V4IR::Function *_function;
|
||||
QV4::Function *_vmFunction;
|
||||
|
@ -741,6 +743,13 @@ private:
|
|||
QHash<V4IR::BasicBlock *, QVector<DataLabelPtr> > _labelPatches;
|
||||
|
||||
QV4::ExecutionEngine *_engine;
|
||||
|
||||
struct CodeLineNumerMapping
|
||||
{
|
||||
Assembler::Label location;
|
||||
int lineNumber;
|
||||
};
|
||||
QVector<CodeLineNumerMapping> codeLineNumberMappings;
|
||||
};
|
||||
|
||||
class Q_QML_EXPORT InstructionSelection:
|
||||
|
@ -838,6 +847,7 @@ protected:
|
|||
virtual void visitCJump(V4IR::CJump *);
|
||||
virtual void visitRet(V4IR::Ret *);
|
||||
virtual void visitTry(V4IR::Try *);
|
||||
virtual void visitDebugAnnotation(V4IR::DebugAnnotation *);
|
||||
|
||||
private:
|
||||
#define isel_stringIfyx(s) #s
|
||||
|
|
|
@ -221,6 +221,8 @@ struct RemoveSharedExpressions: V4IR::StmtVisitor, V4IR::ExprVisitor
|
|||
// nothing to do for Try statements
|
||||
}
|
||||
|
||||
virtual void visitDebugAnnotation(DebugAnnotation *) {}
|
||||
|
||||
// expressions
|
||||
virtual void visitConst(Const *) {}
|
||||
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 << ';';
|
||||
}
|
||||
|
||||
void DebugAnnotation::dump(QTextStream &out, Mode mode)
|
||||
{
|
||||
out << "// line: " << location.startLine << " ; column: " << location.startColumn;
|
||||
}
|
||||
|
||||
Function *Module::newFunction(const QString &name, Function *outer)
|
||||
{
|
||||
Function *f = new Function(this, outer, name);
|
||||
|
@ -851,6 +858,16 @@ Stmt *BasicBlock::TRY(BasicBlock *tryBlock, BasicBlock *catchBlock, const QStrin
|
|||
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)
|
||||
{
|
||||
out << 'L' << index << ':' << endl;
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
|
||||
#include "qv4global_p.h"
|
||||
#include <private/qqmljsmemorypool_p.h>
|
||||
#include <private/qqmljsastfwd_p.h>
|
||||
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/QString>
|
||||
|
@ -118,6 +119,7 @@ struct Jump;
|
|||
struct CJump;
|
||||
struct Ret;
|
||||
struct Try;
|
||||
struct DebugAnnotation;
|
||||
|
||||
enum AluOp {
|
||||
OpInvalid = 0,
|
||||
|
@ -198,6 +200,7 @@ struct StmtVisitor {
|
|||
virtual void visitCJump(CJump *) = 0;
|
||||
virtual void visitRet(Ret *) = 0;
|
||||
virtual void visitTry(Try *) = 0;
|
||||
virtual void visitDebugAnnotation(DebugAnnotation *) = 0;
|
||||
};
|
||||
|
||||
struct Expr {
|
||||
|
@ -492,6 +495,7 @@ struct Stmt {
|
|||
virtual CJump *asCJump() { return 0; }
|
||||
virtual Ret *asRet() { return 0; }
|
||||
virtual Try *asTry() { return 0; }
|
||||
virtual DebugAnnotation *asDebugAnnotation() { return 0; }
|
||||
virtual void dump(QTextStream &out, Mode mode = HIR) = 0;
|
||||
|
||||
void destroyData() {
|
||||
|
@ -629,6 +633,20 @@ struct Try: Stmt {
|
|||
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 {
|
||||
MemoryPool pool;
|
||||
QVector<Function *> functions;
|
||||
|
@ -768,6 +786,7 @@ struct BasicBlock {
|
|||
Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse);
|
||||
Stmt *RET(Temp *expr);
|
||||
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);
|
||||
};
|
||||
|
|
|
@ -69,38 +69,6 @@
|
|||
|
||||
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" {
|
||||
|
||||
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)
|
||||
context->engine->debugger->aboutToThrow(value);
|
||||
|
||||
for (ExecutionContext *ctx = context; ctx; ctx = ctx->parent) {
|
||||
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);
|
||||
UnwindHelper::prepareForUnwind(context);
|
||||
|
||||
#if USE(LIBUNWIND_DEBUG)
|
||||
printf("about to throw exception. walking stack first with libunwind:\n");
|
||||
|
|
|
@ -89,27 +89,6 @@ struct ArrayObject;
|
|||
struct ErrorObject;
|
||||
struct ExecutionEngine;
|
||||
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
|
||||
|
|
|
@ -51,11 +51,11 @@ namespace QV4 {
|
|||
struct ExecutionContext;
|
||||
|
||||
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)
|
||||
, scope(scope), strictMode(false), inheritContext(false), parsed(false), qml(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)
|
||||
, scope(engine->rootContext), strictMode(true), inheritContext(true), parsed(false)
|
||||
, qml(qml), vmFunction(0) {}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
#ifdef USE_NULL_HELPER
|
||||
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::registerFunctions(const QVector<Function *> &functions) {Q_UNUSED(functions);}
|
||||
void UnwindHelper::deregisterFunction(Function *function) {Q_UNUSED(function);}
|
||||
|
|
|
@ -116,9 +116,8 @@ void UnwindHelper::registerFunctions(const QVector<Function *> &functions)
|
|||
allFunctions.insert(reinterpret_cast<quintptr>(f->code), f);
|
||||
}
|
||||
|
||||
void UnwindHelper::ensureUnwindInfo(Function *function)
|
||||
void UnwindHelper::prepareForUnwind(ExecutionContext *)
|
||||
{
|
||||
Q_UNUSED(function);
|
||||
}
|
||||
|
||||
int UnwindHelper::unwindInfoSize()
|
||||
|
|
|
@ -84,7 +84,7 @@ UnwindInfo::~UnwindInfo()
|
|||
__deregister_frame(data.data() + fde_offset);
|
||||
}
|
||||
|
||||
void UnwindHelper::ensureUnwindInfo(Function *f)
|
||||
static void ensureUnwindInfo(Function *f)
|
||||
{
|
||||
if (!f->codeRef)
|
||||
return; // Not a JIT generated function
|
||||
|
@ -112,6 +112,22 @@ void UnwindHelper::ensureUnwindInfo(Function *f)
|
|||
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 *)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
namespace QV4 {
|
||||
|
||||
struct Function;
|
||||
struct ExecutionContext;
|
||||
|
||||
class UnwindHelper
|
||||
{
|
||||
public:
|
||||
static void ensureUnwindInfo(Function *function);
|
||||
static void prepareForUnwind(ExecutionContext *ctx);
|
||||
static void registerFunction(Function *function);
|
||||
static void registerFunctions(const QVector<Function *> &functions);
|
||||
static void deregisterFunction(Function *function);
|
||||
|
|
|
@ -120,26 +120,32 @@ DEFINE_MANAGED_VTABLE(GC);
|
|||
|
||||
} // 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) {
|
||||
std::cerr << "Uncaught exception: " << qPrintable(exception.toString(ctx)->toQString()) << std::endl;
|
||||
return;
|
||||
std::cerr << "Uncaught exception: " << qPrintable(exception.value().toString(ctx)->toQString()) << std::endl;
|
||||
} 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()) {
|
||||
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;
|
||||
foreach (const QV4::ExecutionEngine::StackFrame &frame, exception.stackTrace()) {
|
||||
std::cerr << " at " << qPrintable(frame.function) << " (" << qPrintable(frame.source.toLocalFile());
|
||||
if (frame.line >= 0)
|
||||
std::cerr << ":" << frame.line;
|
||||
std::cerr << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -390,7 +396,7 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
} catch (QV4::Exception& ex) {
|
||||
ex.accept(ctx);
|
||||
showException(ctx, ex.value());
|
||||
showException(ctx, ex);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue