static QScriptValue run(QScriptEngine* engine, const QString& fileName, bool recursive) { QFile file(fileName); if (file.open(QIODevice::ReadOnly)) { QTextStream stream(&file); const QString sourceCode = stream.readAll(); if ( recursive ) { // A script context is pushed by the include function call. // Link that context to the one of the script that called include // Only do that, however, when called from the scipt QScriptContext *context = engine->currentContext(); QScriptContext *parent=context->parentContext(); context->setActivationObject(parent->activationObject()); context->setThisObject(parent->thisObject()); } QScriptValue result = engine->evaluate(sourceCode, fileName); if (engine->hasUncaughtException() && errorFileName.isNull() ) errorFileName = fileName; return result; } else { Core::MessageManager::instance()->printToOutputPane(QObject::tr("Error: %1 doesn't exist.\n").arg(fileName), Core::MessageManager::Flash); engine->abortEvaluation(); return QScriptValue(); } }
void JavascriptInstance::IncludeFile(const QString &path) { for(uint i = 0; i < includedFiles.size(); ++i) if (includedFiles[i].toLower() == path.toLower()) { LogDebug("JavascriptInstance::IncludeFile: Not including already included file " + path); return; } QString script = LoadScript(path); QScriptContext *context = engine_->currentContext(); assert(context); if (!context) { LogError("JavascriptInstance::IncludeFile: QScriptEngine::currentContext() returned null!"); return; } QScriptContext *parent = context->parentContext(); if (!parent) { LogError("JavascriptInstance::IncludeFile: QScriptEngine::parentContext() returned null!"); return; } context->setActivationObject(context->parentContext()->activationObject()); context->setThisObject(context->parentContext()->thisObject()); QScriptSyntaxCheckResult syntaxResult = engine_->checkSyntax(script); if(syntaxResult.state() != QScriptSyntaxCheckResult::Valid) { LogError("JavascriptInstance::IncludeFile: Syntax error in " + path + ". " + syntaxResult.errorMessage() + " In line:" + QString::number(syntaxResult.errorLineNumber())); return; } QScriptValue result = engine_->evaluate(script, path); includedFiles.push_back(path); if (engine_->hasUncaughtException()) LogError(result.toString()); }
int ScriptDebuggerPrivate::frameCount() const { int count = 0; QScriptContext *ctx = engine()->currentContext(); while (ctx) { ++count; ctx = ctx->parentContext(); } return count; }
QScriptContext *ScriptDebuggerPrivate::frameContext(int index) const { QScriptContext *ctx = engine()->currentContext(); for (int i = 0; i < index; ++i) { ctx = ctx->parentContext(); if (!ctx) break; } return ctx; }
/*! Returns the number of contexts (frames). */ int QScriptDebuggerBackend::contextCount() const { if (!engine()) return 0; int count = 0; QScriptContext *ctx = engine()->currentContext(); while (ctx) { ++count; ctx = ctx->parentContext(); } return count; }
QList<JSAgentStackData> QJSDebuggerAgent::backtrace() { SetupExecEnv execEnv(d); QList<JSAgentStackData> backtrace; for (QScriptContext *ctx = engine()->currentContext(); ctx; ctx = ctx->parentContext()) { QScriptContextInfo info(ctx); JSAgentStackData frame; frame.functionName = info.functionName().toUtf8(); if (frame.functionName.isEmpty()) { if (ctx->parentContext()) { switch (info.functionType()) { case QScriptContextInfo::ScriptFunction: frame.functionName = "<anonymous>"; break; case QScriptContextInfo::NativeFunction: frame.functionName = "<native>"; break; case QScriptContextInfo::QtFunction: case QScriptContextInfo::QtPropertyFunction: frame.functionName = "<native slot>"; break; } } else { frame.functionName = "<global>"; } } frame.lineNumber = info.lineNumber(); // if the line number is unknown, fallback to the function line number if (frame.lineNumber == -1) frame.lineNumber = info.functionStartLineNumber(); frame.fileUrl = info.fileName().toUtf8(); backtrace.append(frame); } return backtrace; }
/*! Returns the context for the frame with the given \a index. */ QScriptContext *QScriptDebuggerBackend::context(int index) const { if (index < 0) return 0; QScriptContext *ctx = engine()->currentContext(); while (ctx) { if (index == 0) return ctx; ctx = ctx->parentContext(); --index; } return 0; }
QList<JSAgentWatchData> QJSDebuggerAgent::localsAtFrame(int frameId) { SetupExecEnv execEnv(d); int deep = 0; QScriptContext *ctx = engine()->currentContext(); while (ctx && deep < frameId) { ctx = ctx->parentContext(); deep++; } return d->getLocals(ctx); }
/*! Constructs a new agent for the given \a engine. The agent will report debugging-related events (e.g. step completion) to the given \a backend. */ QScriptDebuggerAgent::QScriptDebuggerAgent( QScriptDebuggerBackendPrivate *backend, QScriptEngine *engine) : QScriptEngineAgent(engine), d_ptr(new QScriptDebuggerAgentPrivate()) { Q_D(QScriptDebuggerAgent); d->backend = backend; QScriptContext *ctx = engine->currentContext(); while (ctx) { d->scriptIdStack.append(QList<qint64>()); d->contextIdStack.append(d->nextContextId); ++d->nextContextId; ctx = ctx->parentContext(); } }
static PyObject *meth_QScriptContext_parentContext(PyObject *sipSelf, PyObject *sipArgs) { PyObject *sipParseErr = NULL; { QScriptContext *sipCpp; if (sipParseArgs(&sipParseErr, sipArgs, "B", &sipSelf, sipType_QScriptContext, &sipCpp)) { QScriptContext *sipRes; Py_BEGIN_ALLOW_THREADS sipRes = sipCpp->parentContext(); Py_END_ALLOW_THREADS return sipConvertFromType(sipRes,sipType_QScriptContext,NULL); } }
bool ScriptDebuggerPrivate::executeCommand(const QString &command, const QStringList &args) { if (command == QLatin1String("c") || command == QLatin1String("continue")) { setMode(Run); return true; } else if (command == QLatin1String("s") || command == QLatin1String("step")) { setMode(StepInto); return true; } else if (command == QLatin1String("n") || command == QLatin1String("next")) { setMode(StepOver); m_stepDepth = 0; return true; } else if (command == QLatin1String("f") || command == QLatin1String("frame")) { bool ok = false; int index = args.value(0).toInt(&ok); if (ok) { if (index < 0 || index >= frameCount()) { errorMessage("No such frame."); } else { setCurrentFrameIndex(index); QScriptContext *ctx = currentFrameContext(); message(QString::fromLatin1("#%0 %1").arg(index).arg(ctx->toString())); } } } else if (command == QLatin1String("bt") || command == QLatin1String("backtrace")) { QScriptContext *ctx = engine()->currentContext(); int index = -1; while (ctx) { ++index; QString line = ctx->toString(); message(QString::fromLatin1("#%0 %1").arg(index).arg(line)); ctx = ctx->parentContext(); } } else if (command == QLatin1String("up")) { int index = currentFrameIndex() + 1; if (index == frameCount()) { errorMessage(QString::fromLatin1("Initial frame selected; you cannot go up.")); } else { setCurrentFrameIndex(index); QScriptContext *ctx = currentFrameContext(); message(QString::fromLatin1("#%0 %1").arg(index).arg(ctx->toString())); } } else if (command == QLatin1String("down")) { int index = currentFrameIndex() - 1; if (index < 0) { errorMessage(QString::fromLatin1("Bottom (innermost) frame selected; you cannot go down.")); } else { setCurrentFrameIndex(index); QScriptContext *ctx = currentFrameContext(); message(QString::fromLatin1("#%0 %1").arg(index).arg(ctx->toString())); } } else if (command == QLatin1String("b") || command == QLatin1String("break")) { QString str = args.value(0); int colonIndex = str.indexOf(QLatin1Char(':')); if (colonIndex != -1) { // filename:line form QString fileName = str.left(colonIndex); int lineNumber = str.mid(colonIndex+1).toInt(); int id = m_bpManager->setBreakpoint(fileName, lineNumber); message(QString::fromLatin1("Breakpoint %0 at %1, line %2.").arg(id+1).arg(fileName).arg(lineNumber)); } else { // function QScriptValue fun = engine()->globalObject().property(str); if (fun.isFunction()) { int id = m_bpManager->setBreakpoint(fun); message(QString::fromLatin1("Breakpoint %0 at %1().").arg(id+1).arg(str)); } } } else if (command == QLatin1String("d") || command == QLatin1String("delete")) { int id = args.value(0).toInt() - 1; m_bpManager->removeBreakpoint(id); } else if (command == QLatin1String("disable")) { int id = args.value(0).toInt() - 1; m_bpManager->setBreakpointEnabled(id, false); } else if (command == QLatin1String("enable")) { int id = args.value(0).toInt() - 1; m_bpManager->setBreakpointEnabled(id, true); } else if (command == QLatin1String("list")) { QScriptContext *ctx = currentFrameContext(); ScriptInfo *progInfo = scriptInfo(ctx); if (!progInfo) { errorMessage("No source text available for this frame."); } else { QScriptContextInfo ctxInfo(ctx); bool ok; int line = args.value(0).toInt(&ok); if (ok) { line = qMax(1, line - 5); } else { line = listLineNumber(); if (line == -1) line = qMax(progInfo->lineNumber(), ctxInfo.lineNumber() - 5); } for (int i = line; i < line + 10; ++i) { message(QString::fromLatin1("%0\t%1").arg(i).arg(progInfo->lineText(i))); } setListLineNumber(line + 10); } } else if (command == QLatin1String("info")) { if (args.size() < 1) { } else { QString what = args.value(0); if (what == QLatin1String("locals")) { QScriptValueIterator it(currentFrameContext()->activationObject()); while (it.hasNext()) { it.next(); QString line; line.append(it.name()); line.append(QLatin1String(" = ")); line.append(safeValueToString(it.value())); message(line); } } } } else if (command == QLatin1String("help")) { message("continue - continue execution\n" "step - step into statement\n" "next - step over statement\n" "list - show where you are\n" "\n" "break - set breakpoint\n" "delete - remove breakpoint\n" "disable - disable breakpoint\n" "enable - enable breakpoint\n" "\n" "backtrace - show backtrace\n" "up - one frame up\n" "down - one frame down\n" "frame - set frame\n" "\n" "info locals - show local variables"); } else { errorMessage(QString::fromLatin1("Undefined command \"%0\". Try \"help\".") .arg(command)); } return false; }
QScriptValue include( QScriptContext *context, QScriptEngine *engine ) { // Check argument count, include() call in global context? const QScriptContextInfo contextInfo( context->parentContext() ); if ( context->argumentCount() < 1 ) { context->throwError( i18nc("@info/plain", "One argument expected for <icode>include()</icode>") ); return engine->undefinedValue(); } else if ( context->parentContext() && context->parentContext()->parentContext() ) { QScriptContext *parentContext = context->parentContext()->parentContext(); bool error = false; while ( parentContext ) { const QScriptContextInfo parentContextInfo( parentContext ); if ( !parentContextInfo.fileName().isEmpty() && parentContextInfo.fileName() == contextInfo.fileName() ) { // Parent context is in the same file, error error = true; break; } parentContext = parentContext->parentContext(); } if ( error ) { context->throwError( i18nc("@info/plain", "<icode>include()</icode> calls must be in global context") ); return engine->undefinedValue(); } } // Check if this include() call is before all other statements QVariantHash includeData = context->callee().data().toVariant().toHash(); if ( includeData.contains(contextInfo.fileName()) ) { const quint16 maxIncludeLine = includeData[ contextInfo.fileName() ].toInt(); if ( contextInfo.lineNumber() > maxIncludeLine ) { context->throwError( i18nc("@info/plain", "<icode>include()</icode> calls must be the first statements") ); return engine->undefinedValue(); } } // Get argument and check that it's not pointing to another directory const QString fileName = context->argument(0).toString(); if ( fileName.contains('/') ) { context->throwError( i18nc("@info/plain", "Cannot include files from other directories") ); return engine->undefinedValue(); } // Get path of the main script QString path; QScriptContext *fileInfoContext = context; do { path = QFileInfo( QScriptContextInfo(fileInfoContext).fileName() ).path(); fileInfoContext = fileInfoContext->parentContext(); } while ( path.isEmpty() || path == QLatin1String(".") ); // Construct file path to the file to be included and check if the file is already included const QString filePath = path + '/' + fileName; QStringList includedFiles = engine->globalObject().property( "includedFiles" ).toVariant().toStringList(); if ( includedFiles.contains(filePath) ) { kWarning() << "File already included" << filePath; return engine->undefinedValue(); } // Try to open the file to be included QFile scriptFile( filePath ); if ( !scriptFile.open(QIODevice::ReadOnly) ) { context->throwError( i18nc("@info/plain", "Cannot find file to be included: " "<filename>%1</filename>", filePath) ); return engine->undefinedValue(); } // Read the file QTextStream stream( &scriptFile ); const QString program = stream.readAll(); scriptFile.close(); if ( !includeData.contains(scriptFile.fileName()) ) { includeData[ scriptFile.fileName() ] = maxIncludeLine( program ); QScriptValue includeFunction = engine->globalObject().property("include"); Q_ASSERT( includeFunction.isValid() ); includeFunction.setData( qScriptValueFromValue(engine, includeData) ); engine->globalObject().setProperty( "include", includeFunction, QScriptValue::KeepExistingFlags ); } // Set script context QScriptContext *parent = context->parentContext(); if ( parent ) { context->setActivationObject( parent->activationObject() ); context->setThisObject( parent->thisObject() ); } // Store included files in global property "includedFiles" includedFiles << filePath; includedFiles.removeDuplicates(); QScriptValue::PropertyFlags flags = QScriptValue::ReadOnly | QScriptValue::Undeletable; engine->globalObject().setProperty( "includedFiles", engine->newVariant(includedFiles), flags ); // Evaluate script return engine->evaluate( program, filePath ); }
void tst_QScriptContext::pushAndPopContext() { QScriptEngine eng; QScriptContext *topLevel = eng.currentContext(); QCOMPARE(topLevel->engine(), &eng); QScriptContext *ctx = eng.pushContext(); QVERIFY(ctx != 0); QCOMPARE(ctx->parentContext(), topLevel); QCOMPARE(eng.currentContext(), ctx); QCOMPARE(ctx->engine(), &eng); QCOMPARE(ctx->state(), QScriptContext::NormalState); QCOMPARE(ctx->isCalledAsConstructor(), false); QCOMPARE(ctx->argumentCount(), 0); QCOMPARE(ctx->argument(0).isUndefined(), true); QVERIFY(!ctx->argument(-1).isValid()); QCOMPARE(ctx->argumentsObject().isObject(), true); QCOMPARE(ctx->activationObject().isObject(), true); QCOMPARE(ctx->callee().isValid(), false); QCOMPARE(ctx->thisObject().strictlyEquals(eng.globalObject()), true); QCOMPARE(ctx->scopeChain().size(), 2); QVERIFY(ctx->scopeChain().at(0).equals(ctx->activationObject())); QVERIFY(ctx->scopeChain().at(1).equals(eng.globalObject())); QScriptContext *ctx2 = eng.pushContext(); QCOMPARE(ctx2->parentContext(), ctx); QCOMPARE(eng.currentContext(), ctx2); eng.popContext(); QCOMPARE(eng.currentContext(), ctx); eng.popContext(); QCOMPARE(eng.currentContext(), topLevel); // popping the top-level context is not allowed QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::popContext() doesn't match with pushContext()"); eng.popContext(); QCOMPARE(eng.currentContext(), topLevel); { QScriptContext *ctx3 = eng.pushContext(); ctx3->activationObject().setProperty("foo", QScriptValue(&eng, 123)); QVERIFY(eng.evaluate("foo").strictlyEquals(QScriptValue(&eng, 123))); eng.evaluate("var bar = 'ciao'"); QVERIFY(ctx3->activationObject().property("bar", QScriptValue::ResolveLocal).strictlyEquals(QScriptValue(&eng, "ciao"))); eng.popContext(); } { QScriptContext *ctx4 = eng.pushContext(); QScriptValue obj = eng.newObject(); obj.setProperty("prop", QScriptValue(&eng, 456)); ctx4->setThisObject(obj); QScriptValue ret = eng.evaluate("var tmp = this.prop; tmp + 1"); QCOMPARE(eng.currentContext(), ctx4); QVERIFY(ret.strictlyEquals(QScriptValue(&eng, 457))); eng.popContext(); } // throwing an exception { QScriptContext *ctx5 = eng.pushContext(); QScriptValue ret = eng.evaluate("throw new Error('oops')"); QVERIFY(ret.isError()); QVERIFY(eng.hasUncaughtException()); QCOMPARE(eng.currentContext(), ctx5); eng.popContext(); } }