Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QJSEngine copy?



  • Is there a way how to copy one instance of QJSEngine to another including variables and functions?

      QJSEngine engine1, engine2;
    
      engine1.evaluate("function sum(x, y){ return x + y;}");
      engine1.globalObject().setProperty("a", 1);
      engine1.evaluate("var b = 2;");
    
      //something like engine2 = engine1;
    
      qDebug() << engine2.evaluate("sum(a,b)").toString(); //3
    

  • Moderators

    Hi, and welcome!

    @Tarae said in QJSEngine copy?:

    Is there a way how to copy one instance of QJSEngine to another

    No. QJSEngine is a QObject, and QObjects cannot be copied.

    However, you can copy the engine's Global Object (JavaScript object): https://doc.qt.io/qt-5/qjsengine.html#globalObject



  • @JKSH Thank you.
    But I can't see, how a copy of global object can solve my problem.
    Is there a way how to create another engine with copy of global object?

    // I have engine1 with variables and functions.
      QJSEngine engine1;
      engine1.evaluate("function sum(x, y){ return x + y;}");
      engine1.globalObject().setProperty("a", 1);
      engine1.evaluate("var b = 2;");
    
    // I create copy of global object, how you suggest.
     QJSValue globalObject = engine1.globalObject();
     QJSValue copyOfGlobalObject = deepCopy(globalObject);
     //BTW How to do deep copy of QJSValue if it is a object (isObject() returns true)?
     
    //And how to create new engine when I have copy of global object?
    // I would need something like this:  
     QJSEngine engine2 = QJSEngine(copyOfGlobalObject); //creates instance of engine with global object
    // or this:
    QJSEngine engine2;
    engine2.globalObject() = copyOfGlobalObject; // sets global object of engine
    
    
    qDebug() << engine2.evaluate("sum(a,b)").toString(); //3
    

  • Moderators

    @Tarae The documentation says (https://doc.qt.io/qt-5.12/qjsvalue.html#details ):

    Note that a QJSValue for which isObject() is true only carries a reference to an actual object; copying the QJSValue will only copy the object reference, not the object itself. If you want to clone an object (i.e. copy an object's properties to another object), you can do so with the help of a for-in statement in script code, or QJSValueIterator in C++.

    I believe you can use QJSValueIterator to iterate across every property in the 1st global object. Then, you can copy each property into the 2nd global object.



  • @JKSH OK, thank you for help, it looked great at first, but there is another problem: cannot set value created in a different engine

          QJSEngine engine1, engine2;
          engine1.evaluate("function sum(x, y){ return x + y;}");
          engine1.globalObject().setProperty("a", 1);
          engine1.evaluate("var b = 2;");
    
          QJSValueIterator it(engine1.globalObject());
          while (it.hasNext()) {
              it.next();
              qDebug() << it.name() << ": " << it.value().toString();
              engine2.globalObject().setProperty(it.name(), it.value());
          }
    
          qDebug() << engine2.evaluate("sum(a,b)").toString(); //3
    

    output looks like this:

    ...
    "sum" :  "function sum() { [native code] }"
    QJSValue::setProperty(sum) failed: cannot set value created in a different engine 
    "a" :  "1"
    QJSValue::setProperty(a) failed: cannot set value created in a different engine
    "b" :  "2"
    QJSValue::setProperty(b) failed: cannot set value created in a different engine
    "sum" :  "function sum() { [native code] }"
    QJSValue::setProperty(sum) failed: cannot set value created in a different engine
    "a" :  "1"
    QJSValue::setProperty(a) failed: cannot set value created in a different engine
    "b" :  "2"
    QJSValue::setProperty(b) failed: cannot set value created in a different engine
    "ReferenceError: a is not defined"
    

    Any idea how to solve this?


  • Moderators

    Hmm... That's unfortunate.

    For things like numbers/strings you could bypass the restriction by calling QJSValue::toNumber()/QJSValue::toString() first before calling setProperty(). I can't think of a way to copy functions though.

    Can you elaborate on why you want to copy the engine?



  • @JKSH What I want to accomplish is creating a lot of engines. All engines share some functions and variables. But not all of them, I can't use one engine for everything. I could define all functions and variables in each engine, but running JSEngine::execute() takes a lot of time. So I wanted to create one engine, define shared functions and variables in it and copy it to others engines.

    Code:
    What I have:

    QString sharedScript = readFile("shared_script.js"); //large file
    
    QJSEngine engine1;
    engine1.evaluate(sharedScript); //takes long
    QString engine1script = readFile("script1.js");
    engine1.evaluate(engine1script);
    
    //...
    
    QJSEngine engine1000;
    engine1000.evaluate(sharedScript); //takes long
    QString engine1000script = readFile("script1000.js");
    engine1000.evaluate(engine1000script);
    

    What I want:

    QString sharedScript = readFile("shared_script.js"); //long file
    QJSEngine sharedEngine;
    sharedEngine.evaluate(sharedScript); //takes long 
    
    QJSEngine engine1 = copyOf(sharedEngine); //fast
    QString engine1script = readFile("script1.js");
    engine1.evaluate(engine1script);
    
    //...
    
    QJSEngine engine1000 = copyOf(sharedEngine); //fast
    QString engine1000script = readFile("script1000.js");
    engine1000.evaluate(engine1script);
    

  • Moderators

    @Tarae As a workaround, try splitting shared_script.js into 2 parts:

    (i) A part that contains just the function definitions
    (ii) A part that contains function calls.

    My hypothesis is that evaluate()-ing (i) should be faster than (ii).

    So, you could try:

    1. Run (i) and (ii) in engine1
    2. Run only (i) in engine2
    3. Use QJSValueIterator to copy non-function properties to engine2, using QJSValue::toNumber() / QJSValue::toString() / QJSValue::toVariant() first

    Another thing you could try is putting different engines in different threads (although you must be careful with thread programming)


Log in to reply