diff options
author | Lars Knoll <lars.knoll@digia.com> | 2013-05-22 10:47:36 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@digia.com> | 2013-05-22 12:27:21 +0200 |
commit | d7f2868e427299c1cb9ec33b3c1939c6ad4e78a7 (patch) | |
tree | 9e642a0f8f238c6f70afa4fb70c286a92a701b1c | |
parent | 3bd678c76d8ab699e79e5a3cfe43cff2db03fb8f (diff) |
Add support for generating stack traces to QV4::ExecutionEngine
This makes it possible to remove the v8::StackTrace API
Change-Id: I53eee022a1030f0f6bf9a9268ca7cd3d5975724d
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
-rw-r--r-- | src/imports/testlib/main.cpp | 24 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4engine.cpp | 38 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4engine_p.h | 9 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4v8.cpp | 72 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4v8_p.h | 110 | ||||
-rw-r--r-- | src/qml/qml/v8/qqmlbuiltinfunctions.cpp | 118 | ||||
-rw-r--r-- | src/qml/qml/v8/qv8qobjectwrapper.cpp | 14 | ||||
-rw-r--r-- | src/qml/qml/v8/qv8valuetypewrapper.cpp | 12 |
8 files changed, 109 insertions, 288 deletions
diff --git a/src/imports/testlib/main.cpp b/src/imports/testlib/main.cpp index d854a26af5..1192861856 100644 --- a/src/imports/testlib/main.cpp +++ b/src/imports/testlib/main.cpp @@ -106,22 +106,22 @@ public Q_SLOTS: QQmlV4Handle callerFile(int frameIndex = 0) const { - v8::Handle<v8::StackTrace> stacks = v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); - int count = stacks->GetFrameCount(); - if (count >= frameIndex + 1) { - v8::Handle<v8::StackFrame> frame = stacks->GetFrame(frameIndex + 1); - return QQmlV4Handle(frame->GetScriptNameOrSourceURL()->v4Value()); - } + QQmlEngine *engine = qmlEngine(this); + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine->handle()); + + QVector<QV4::ExecutionEngine::StackFrame> stack = v4->stackTrace(frameIndex + 1); + if (stack.size() > frameIndex) + return QQmlV4Handle(QV4::Value::fromString(v4->newString(stack.at(frameIndex).source.url()))); return QQmlV4Handle(); } int callerLine(int frameIndex = 0) const { - v8::Handle<v8::StackTrace> stacks = v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); - int count = stacks->GetFrameCount(); - if (count >= frameIndex + 1) { - v8::Handle<v8::StackFrame> frame = stacks->GetFrame(frameIndex + 1); - return frame->GetLineNumber(); - } + QQmlEngine *engine = qmlEngine(this); + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine->handle()); + + QVector<QV4::ExecutionEngine::StackFrame> stack = v4->stackTrace(frameIndex + 1); + if (stack.size() > frameIndex) + return stack.at(frameIndex).line; return -1; } }; diff --git a/src/qml/qml/v4/qv4engine.cpp b/src/qml/qml/v4/qv4engine.cpp index d0604d93d6..8fafddf182 100644 --- a/src/qml/qml/v4/qv4engine.cpp +++ b/src/qml/qml/v4/qv4engine.cpp @@ -540,6 +540,44 @@ Object *ExecutionEngine::qmlContextObject() const return static_cast<CallContext *>(ctx)->activation; } +QVector<ExecutionEngine::StackFrame> ExecutionEngine::stackTrace(int frameLimit) const +{ + QVector<StackFrame> stack; + + QV4::ExecutionContext *c = current; + while (c && frameLimit) { + if (CallContext *c = c->asCallContext()) { + StackFrame frame; + frame.source = c->function->function->sourceFile; + frame.function = c->function->name->toQString(); + frame.line = -1; + frame.column = -1; + stack.append(frame); + --frameLimit; + } + c = c->parent; + } + return stack; +} + +ExecutionEngine::StackFrame ExecutionEngine::currentStackFrame() const +{ + StackFrame frame; + frame.line = -1; + frame.column = -1; + + QV4::ExecutionContext *c = current; + while (c) { + if (CallContext *c = c->asCallContext()) { + frame.source = c->function->function->sourceFile; + frame.function = c->function->name->toQString(); + return frame; + } + c = c->parent; + } + return frame; +} + void ExecutionEngine::requireArgumentsAccessors(int n) { if (n <= argumentsAccessors.size()) diff --git a/src/qml/qml/v4/qv4engine_p.h b/src/qml/qml/v4/qv4engine_p.h index be74de6d38..6d1ae60da2 100644 --- a/src/qml/qml/v4/qv4engine_p.h +++ b/src/qml/qml/v4/qv4engine_p.h @@ -271,6 +271,15 @@ struct Q_QML_EXPORT ExecutionEngine Object *qmlContextObject() const; + struct StackFrame { + QUrl source; + QString function; + int line; + int column; + }; + QVector<StackFrame> stackTrace(int frameLimit = -1) const; + StackFrame currentStackFrame() const; + void requireArgumentsAccessors(int n); void markObjects(); diff --git a/src/qml/qml/v4/qv4v8.cpp b/src/qml/qml/v4/qv4v8.cpp index 40a5c5f157..e730eb1a17 100644 --- a/src/qml/qml/v4/qv4v8.cpp +++ b/src/qml/qml/v4/qv4v8.cpp @@ -282,78 +282,6 @@ void Script::SetData(Handle<String> data) } -Handle<StackFrame> StackTrace::GetFrame(uint32_t index) const -{ - if (index >= (uint)frames.size()) - return Handle<StackFrame>(); - return frames.at(index); -} - -int StackTrace::GetFrameCount() const -{ - return frames.size(); -} - -Handle<Array> StackTrace::AsArray() -{ - Q_UNIMPLEMENTED(); - return Handle<Array>(); -} - -Handle<StackTrace> StackTrace::CurrentStackTrace(int frame_limit, StackTrace::StackTraceOptions options) -{ - StackTrace *trace = new StackTrace; - QV4::ExecutionEngine *engine = currentEngine(); - QV4::ExecutionContext *current = engine->current; - while (current && frame_limit) { - if (CallContext *c = current->asCallContext()) { - StackFrame *frame = new StackFrame(QV4::Value::fromString(current, c->currentFileName().url()), - QV4::Value::fromString(c->function->name), - c->currentLineNumber(), 0); - trace->frames.append(v8::Handle<v8::StackFrame>(frame)); - --frame_limit; - } - current = current->parent; - } - - return Handle<StackTrace>(trace); -} - - -int StackFrame::GetLineNumber() const -{ - return m_lineNumber; -} - -int StackFrame::GetColumn() const -{ - return m_columnNumber; -} - -Handle<String> StackFrame::GetScriptName() const -{ - return m_scriptName.value(); -} - -Handle<String> StackFrame::GetScriptNameOrSourceURL() const -{ - return m_scriptName.value(); -} - -Handle<String> StackFrame::GetFunctionName() const -{ - return m_functionName.value(); -} - -StackFrame::StackFrame(Handle<String> script, Handle<String> function, int line, int column) - : m_lineNumber(line) - , m_columnNumber(column) -{ - m_scriptName = script->v4Value(); - m_functionName = function->v4Value(); -} - - bool Value::IsUndefined() const { return ConstValuePtr(this)->isUndefined(); diff --git a/src/qml/qml/v4/qv4v8_p.h b/src/qml/qml/v4/qv4v8_p.h index dcd44d5916..002c31cd90 100644 --- a/src/qml/qml/v4/qv4v8_p.h +++ b/src/qml/qml/v4/qv4v8_p.h @@ -122,8 +122,6 @@ class FunctionTemplate; class ObjectTemplate; class Data; class AccessorInfo; -class StackTrace; -class StackFrame; class Isolate; class TryCatch; @@ -554,114 +552,6 @@ private: DEFINE_REFCOUNTED_HANDLE_OPERATIONS(Script) -/** - * Representation of a JavaScript stack trace. The information collected is a - * snapshot of the execution stack and the information remains valid after - * execution continues. - */ -class V8EXPORT StackTrace : public QSharedData -{ - public: - /** - * Flags that determine what information is placed captured for each - * StackFrame when grabbing the current stack trace. - */ - enum StackTraceOptions { - kLineNumber = 1, - kColumnOffset = 1 << 1 | kLineNumber, - kScriptName = 1 << 2, - kFunctionName = 1 << 3, - kIsEval = 1 << 4, - kIsConstructor = 1 << 5, - kScriptNameOrSourceURL = 1 << 6, - kOverview = kLineNumber | kColumnOffset | kScriptName | kFunctionName, - kDetailed = kOverview | kIsEval | kIsConstructor | kScriptNameOrSourceURL - }; - - /** - * Returns a StackFrame at a particular index. - */ - Handle<StackFrame> GetFrame(uint32_t index) const; - - /** - * Returns the number of StackFrames. - */ - int GetFrameCount() const; - - /** - * Returns StackTrace as a v8::Array that contains StackFrame objects. - */ - Handle<Array> AsArray(); - - /** - * Grab a snapshot of the current JavaScript execution stack. - * - * \param frame_limit The maximum number of stack frames we want to capture. - * \param options Enumerates the set of things we will capture for each - * StackFrame. - */ - static Handle<StackTrace> CurrentStackTrace( - int frame_limit, - StackTraceOptions options = kOverview); - - private: - QVector<Handle<StackFrame> > frames; -}; - -DEFINE_REFCOUNTED_HANDLE_OPERATIONS(StackTrace) - - -/** - * A single JavaScript stack frame. - */ -class V8EXPORT StackFrame : public QSharedData { - public: - /** - * Returns the number, 1-based, of the line for the associate function call. - * This method will return Message::kNoLineNumberInfo if it is unable to - * retrieve the line number, or if kLineNumber was not passed as an option - * when capturing the StackTrace. - */ - int GetLineNumber() const; - - /** - * Returns the 1-based column offset on the line for the associated function - * call. - * This method will return Message::kNoColumnInfo if it is unable to retrieve - * the column number, or if kColumnOffset was not passed as an option when - * capturing the StackTrace. - */ - int GetColumn() const; - - /** - * Returns the name of the resource that contains the script for the - * function for this StackFrame. - */ - Handle<String> GetScriptName() const; - - /** - * Returns the name of the resource that contains the script for the - * function for this StackFrame or sourceURL value if the script name - * is undefined and its source ends with //@ sourceURL=... string. - */ - Handle<String> GetScriptNameOrSourceURL() const; - - /** - * Returns the name of the function associated with this stack frame. - */ - Handle<String> GetFunctionName() const; - -private: - friend class StackTrace; - StackFrame(Handle<String> script, Handle<String> function, int line, int column); - int m_lineNumber; - int m_columnNumber; - QV4::PersistentValue m_scriptName; - QV4::PersistentValue m_functionName; -}; - -DEFINE_REFCOUNTED_HANDLE_OPERATIONS(StackFrame) - // --- Value --- diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index a24fb5d25a..88f7d28aa6 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -74,39 +74,22 @@ enum ConsoleLogTypes { Error }; -static void jsContext(QString &file, int *line, QString &function) { - v8::Handle<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(1); - if (stackTrace->GetFrameCount()) { - v8::Handle<v8::StackFrame> frame = stackTrace->GetFrame(0); - file = frame->GetScriptName()->v4Value().toQString(); - *line = frame->GetLineNumber(); - function = frame->GetFunctionName()->v4Value().toQString(); - } -} - -static QString jsStack() { - QStringList stackFrames; +static QString jsStack(QV4::ExecutionEngine *engine) { + QString stack; - //The v8 default is currently 10 stack frames. - v8::Handle<v8::StackTrace> stackTrace = - v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview); - int stackCount = stackTrace->GetFrameCount(); + QVector<QV4::ExecutionEngine::StackFrame> stackTrace = engine->stackTrace(10); - for (int i = 0; i < stackCount; i++) { - v8::Handle<v8::StackFrame> frame = stackTrace->GetFrame(i); - v8::Handle<v8::String> function(frame->GetFunctionName()); - v8::Handle<v8::String> script(frame->GetScriptName()); - int lineNumber = frame->GetLineNumber(); - int columnNumber = frame->GetColumn(); + for (int i = 0; i < stackTrace.count(); i++) { + const QV4::ExecutionEngine::StackFrame &frame = stackTrace.at(i); QString stackFrame = - QString::fromLatin1("%1 (%2:%3:%4)").arg(function->v4Value().asString()->toQString(), - script->v4Value().asString()->toQString(), - QString::number(lineNumber), - QString::number(columnNumber)); - stackFrames.append(stackFrame); + QString::fromLatin1("%1 (%2:%3:%4)\n").arg(frame.function, + frame.source.url(), + QString::number(frame.line), + QString::number(frame.column)); + stack += stackFrame; } - return stackFrames.join(QLatin1String("\n")); + return stack; } QV4::Value console(ConsoleLogTypes logType, const v8::Arguments &args, @@ -114,6 +97,8 @@ QV4::Value console(ConsoleLogTypes logType, const v8::Arguments &args, { QString result; QV8Engine *engine = V8ENGINE(); + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + for (int i = 0; i < args.Length(); ++i) { if (i != 0) result.append(QLatin1Char(' ')); @@ -124,16 +109,11 @@ QV4::Value console(ConsoleLogTypes logType, const v8::Arguments &args, if (printStack) { result.append(QLatin1String("\n")); - result.append(jsStack()); + result.append(jsStack(v4)); } - QString file; - QString function; - int line; - - jsContext(file, &line, function); - - QMessageLogger logger(file.toUtf8().constData(), line, function.toUtf8().constData()); + QV4::ExecutionEngine::StackFrame frame = v4->currentStackFrame(); + QMessageLogger logger(frame.source.url().toUtf8().constData(), frame.line, frame.function.toUtf8().constData()); switch (logType) { case Log: logger.debug("%s", qPrintable(result)); @@ -180,15 +160,11 @@ QV4::Value consoleProfile(const v8::Arguments &args) //we do not allow that. Hence, we pass an empty(default) title Q_UNUSED(args); QString title; + QV8Engine *engine = V8ENGINE(); + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); - - - QString file; - QString function; - int line; - jsContext(file, &line, function); - - QMessageLogger logger(file.toUtf8().constData(), line, function.toUtf8().constData()); + QV4::ExecutionEngine::StackFrame frame = v4->currentStackFrame(); + QMessageLogger logger(frame.source.url().toUtf8().constData(), frame.line, frame.function.toUtf8().constData()); if (QQmlProfilerService::startProfiling()) { QV8ProfilerService::instance()->startProfiling(title); @@ -208,12 +184,11 @@ QV4::Value consoleProfileEnd(const v8::Arguments &args) Q_UNUSED(args); QString title; - QString file; - QString function; - int line; - jsContext(file, &line, function); + QV8Engine *engine = V8ENGINE(); + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); - QMessageLogger logger(file.toUtf8().constData(), line, function.toUtf8().constData()); + QV4::ExecutionEngine::StackFrame frame = v4->currentStackFrame(); + QMessageLogger logger(frame.source.url().toUtf8().constData(), frame.line, frame.function.toUtf8().constData()); if (QQmlProfilerService::stopProfiling()) { QV8ProfilerService *profiler = QV8ProfilerService::instance(); @@ -258,23 +233,16 @@ QV4::Value consoleCount(const v8::Arguments &args) if (args.Length() > 0) name = args[0]->v4Value().toQString(); - v8::Handle<v8::StackTrace> stackTrace = - v8::StackTrace::CurrentStackTrace(1, v8::StackTrace::kOverview); + QV4::ExecutionEngine *v4 = QV8Engine::getV4(V8ENGINE()); + QV4::ExecutionEngine::StackFrame frame = v4->currentStackFrame(); - if (stackTrace->GetFrameCount()) { - v8::Handle<v8::StackFrame> frame = stackTrace->GetFrame(0); + QString scriptName = frame.source.url(); - QString scriptName = frame->GetScriptName()->v4Value().toQString(); - QString functionName = frame->GetFunctionName()->v4Value().toQString(); - int line = frame->GetLineNumber(); - int column = frame->GetColumn(); + int value = V8ENGINE()->consoleCountHelper(scriptName, frame.line, frame.column); + QString message = name + QLatin1String(": ") + QString::number(value); - int value = V8ENGINE()->consoleCountHelper(scriptName, line, column); - QString message = name + QLatin1String(": ") + QString::number(value); - - QMessageLogger(qPrintable(scriptName), line, - qPrintable(functionName)).debug("%s", qPrintable(message)); - } + QMessageLogger(qPrintable(scriptName), frame.line, + qPrintable(frame.function)).debug("%s", qPrintable(message)); return QV4::Value::undefinedValue(); } @@ -284,14 +252,13 @@ QV4::Value consoleTrace(const v8::Arguments &args) if (args.Length() != 0) V4THROW_ERROR("console.trace(): Invalid arguments"); - QString stack = jsStack(); + QV8Engine *engine = V8ENGINE(); + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); - QString file; - QString function; - int line; - jsContext(file, &line, function); + QString stack = jsStack(v4); - QMessageLogger logger(file.toUtf8().constData(), line, function.toUtf8().constData()); + QV4::ExecutionEngine::StackFrame frame = v4->currentStackFrame(); + QMessageLogger logger(frame.source.url().toUtf8().constData(), frame.line, frame.function.toUtf8().constData()); logger.debug("%s", qPrintable(stack)); return QV4::Value::undefinedValue(); @@ -307,6 +274,9 @@ QV4::Value consoleAssert(const v8::Arguments &args) if (args.Length() == 0) V4THROW_ERROR("console.assert(): Missing argument"); + QV8Engine *engine = V8ENGINE(); + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + if (!args[0]->v4Value().booleanValue()) { QString message; for (int i = 1; i < args.Length(); ++i) { @@ -317,14 +287,10 @@ QV4::Value consoleAssert(const v8::Arguments &args) message.append(value->v4Value().toQString()); } - QString stack = jsStack(); - - QString file; - QString function; - int line; - jsContext(file, &line, function); + QString stack = jsStack(v4); - QMessageLogger logger(file.toUtf8().constData(), line, function.toUtf8().constData()); + QV4::ExecutionEngine::StackFrame frame = v4->currentStackFrame(); + QMessageLogger logger(frame.source.url().toUtf8().constData(), frame.line, frame.function.toUtf8().constData()); logger.critical("%s\n%s", qPrintable(message), qPrintable(stack)); } diff --git a/src/qml/qml/v8/qv8qobjectwrapper.cpp b/src/qml/qml/v8/qv8qobjectwrapper.cpp index 88f41e8c5d..c4e0b0d0e7 100644 --- a/src/qml/qml/v8/qv8qobjectwrapper.cpp +++ b/src/qml/qml/v8/qv8qobjectwrapper.cpp @@ -619,15 +619,11 @@ static inline void StoreProperty(QV8Engine *engine, QObject *object, QQmlPropert QQmlContextData *context = engine->callingContext(); v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value); - v8::Handle<v8::StackTrace> trace = - v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber | - v8::StackTrace::kScriptName)); - v8::Handle<v8::StackFrame> frame = trace->GetFrame(0); - int lineNumber = frame->GetLineNumber(); - int columnNumber = frame->GetColumn(); - QString url = frame->GetScriptName()->v4Value().toQString(); - - newBinding = new QQmlBinding(&function, object, context, url, qmlSourceCoordinate(lineNumber), qmlSourceCoordinate(columnNumber)); + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + QV4::ExecutionEngine::StackFrame frame = v4->currentStackFrame(); + + newBinding = new QQmlBinding(&function, object, context, frame.source.url(), + qmlSourceCoordinate(frame.line), qmlSourceCoordinate(frame.column)); newBinding->setTarget(object, *property, context); newBinding->setEvaluateFlags(newBinding->evaluateFlags() | QQmlBinding::RequiresThisObject); diff --git a/src/qml/qml/v8/qv8valuetypewrapper.cpp b/src/qml/qml/v8/qv8valuetypewrapper.cpp index 9bf1169c21..9297df3d17 100644 --- a/src/qml/qml/v8/qv8valuetypewrapper.cpp +++ b/src/qml/qml/v8/qv8valuetypewrapper.cpp @@ -392,17 +392,11 @@ v8::Handle<v8::Value> QV8ValueTypeWrapper::Setter(v8::Handle<v8::String> propert cacheData.valueTypeCoreIndex = index; cacheData.valueTypePropType = p.userType(); - v8::Handle<v8::StackTrace> trace = - v8::StackTrace::CurrentStackTrace(1, - (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber | - v8::StackTrace::kScriptName)); - v8::Handle<v8::StackFrame> frame = trace->GetFrame(0); - int lineNumber = frame->GetLineNumber(); - int columnNumber = frame->GetColumn(); - QString url = frame->GetScriptName()->v4Value().toQString(); + QV4::ExecutionEngine *v4 = QV8Engine::getV4(r->engine); + QV4::ExecutionEngine::StackFrame frame = v4->currentStackFrame(); newBinding = new QQmlBinding(&function, reference->object, context, - url, qmlSourceCoordinate(lineNumber), qmlSourceCoordinate(columnNumber)); + frame.source.url(), qmlSourceCoordinate(frame.line), qmlSourceCoordinate(frame.column)); newBinding->setTarget(reference->object, cacheData, context); newBinding->setEvaluateFlags(newBinding->evaluateFlags() | QQmlBinding::RequiresThisObject); |