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>
This commit is contained in:
Lars Knoll 2013-05-22 10:47:36 +02:00 committed by Simon Hausmann
parent 3bd678c76d
commit d7f2868e42
8 changed files with 108 additions and 287 deletions

View File

@ -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;
}
};

View File

@ -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())

View File

@ -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();

View File

@ -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();

View File

@ -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 ---

View File

@ -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(QV4::ExecutionEngine *engine) {
QString stack;
static QString jsStack() {
QStringList stackFrames;
QVector<QV4::ExecutionEngine::StackFrame> stackTrace = engine->stackTrace(10);
//The v8 default is currently 10 stack frames.
v8::Handle<v8::StackTrace> stackTrace =
v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview);
int stackCount = stackTrace->GetFrameCount();
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 stack = 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());
logger.critical("%s\n%s", qPrintable(message), qPrintable(stack));
}

View File

@ -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();
QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine);
QV4::ExecutionEngine::StackFrame frame = v4->currentStackFrame();
newBinding = new QQmlBinding(&function, object, context, url, qmlSourceCoordinate(lineNumber), qmlSourceCoordinate(columnNumber));
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);

View File

@ -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);