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;
}
Exemple #5
0
/*!
  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;
}
Exemple #7
0
/*!
  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();
    }
}