6

In Qt 4.8's scripting engine, "local" variables can be set by obtaining a QScriptContext from QScriptEngine::pushContext then setting the properties of its activation object. This can only be done within push/pop calls, since that's the only place a QScriptContext is available and AFAICT there is no equivalent of QScriptEngine#evaluate that takes a QScriptContext to use as the environment:

QScriptEngine engine;
QScriptContext *local;

local = engine.pushContext();
local->activationObject().setProperty("value", 2); // set value=2
qDebug() << engine.evaluate("value").toNumber(); // outputs 2
engine.popContext();

Is there some way to maintain an environment to use with evaluations outside of the push/pop calls? For example, I've tried creating a QScriptValue to use as the activation object and then setting it:

QScriptEngine engine;
QScriptContext *local;

// Use ao as activation object, set variables here, prior to pushContext.
QScriptValue ao;
ao.setProperty("value", 1);

// Test with ao:
local = engine.pushContext();
local->setActivationObject(ao);
qDebug() << engine.evaluate("value").toNumber();
engine.popContext();

But that doesn't work. It outputs nan instead of 1, as value is undefined. For some reason setActivationObject didn't change the value.

My general goal is:

  1. Set up a local environment outside of the evaluation code.
  2. Then use that pre-configured local environment when evaluating scripts between pushContext and popContext calls, without having to re-set all the variables in that environment every time.

So:

  • Is there a way to do this?
  • Is it possible I'm on the right track but I set up ao improperly? For example, there is an undocumented QScriptEngine#newActivationObject() that yields an "unimplemented" error when used, perhaps this is a hint?

How can I set up a local context but basically not have to re-configure it every time I push a context (since it's essentially lost for good every time I pop the context).

Community
  • 1
  • 1
Jason C
  • 38,729
  • 14
  • 126
  • 182

1 Answers1

2

You could use global object. It will share property value accross all evaluations:

#include <QCoreApplication>
#include <QDebug>
#include <QtScript/QScriptEngine>
#include <QtScript/QScriptContext>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QScriptEngine engine;

    engine.globalObject().setProperty("value", 2);
    engine.globalObject().setProperty("value2", 3);

    qDebug() << engine.evaluate("value").toNumber(); // outputs 2
    qDebug() << engine.evaluate("value2").toNumber(); // outputs 3


    return a.exec();
}

Or if you do not want global scope :

#include <QCoreApplication>
#include <QDebug>
#include <QtScript/QScriptEngine>
#include <QtScript/QScriptContext>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QScriptEngine engine;
    QScriptContext *context;

    QScriptValue scope = engine.newObject();
    scope.setProperty("value", 1);
    scope.setProperty("value2", 2);

    context = engine.pushContext();

    context->pushScope(scope);
    qDebug() << engine.evaluate("value").toNumber(); // outputs 1
    qDebug() << engine.evaluate("value2").toNumber(); // outputs 2

    engine.popContext();

    return a.exec();
}
j2ko
  • 2,479
  • 1
  • 16
  • 29
  • Oh cool, thanks. It appears `QScriptContext#pushScope` isn't documented in any version of Qt; so I asked about it on the Qt forums. Apparently it's internal and not really for public use, which theoretically means it could go away at any time, but on the other hand QtScript is deprecated as a whole in newer Qt versions so it doesn't really add any risk that isn't there already. And it *does* work and meets my requirements, I don't think there's anything better. Thanks! – Jason C Sep 04 '16 at 02:11