1

I am using the QJSEngine, I added a global to the object:

QJSValue objGlobal = pobjScriptEng->globalObject();

pobjScriptEng is a pointer to an instance of QJSEngine.

I have a map of globals, the map type definition:

std::map<QString, QString> mpGlobals;

I iterate through the globals map adding them to the engine:

for( mpGlobals::iterator itr=rmpGlobals.begin(); itr!=rmpGlobals.end(); itr++ ) {
    QString strName(itr->first);
    if ( objGlobal.hasProperty(strName) == false ) {
        QString strData(itr->second);
        QJSValue result = pobjScriptEng->evaluate(strData);
        objGlobal.setProperty(strName, result);
    }
}

rmpGlobals is a reference to the globals map.

I have a script which includes a reference to a global, the global is called db and is a JSON object that contains:

{"db":"test","host":"localhost","usr":"root","pass":"123456"}

I added some debug logs in the loop that calls setProperty and this is what's displayed in the Application Output:

itr->first "db" 
itr->second "{\"db\":\"test\",\"host\":\"localhost\",\"usr\":\"root\",\"pass\":\"resuocra\"}"

The syntax error is coming from the JSON, but why there is nothing wrong with it. I dumped result.toString() to the console and it contains:

SyntaxError: Expected token `,'

The script:

function test() {
    try{
        console.info("---------------");
        console.info("test(), Line 4");

        if ( db === undefined ) {
            console.info("db is undefined");
            return;
        }
        if ( typeof db === "object" ) {
            var mbr;
            console.info("test(), Line 7");
            console.info(db);
            console.info("test(), Line 9");

            for( mbr in db ) {
                console.info("test(), Line12");
                console.info(mbr);
                console.info("test(), Line14");
            }
            console.info("test(), Line 14");            
        }
        console.info("test(), Line 16");        
        console.info("---------------");
    } catch( e ) {
        console.warn( "test() WARNING: " + e );
    }
}

When run my application and the script is evaluated I see the following in the "Application Output":

2020-04-08 08:21:36.320693+0100 XMLMPAM[3657:59571] [js] ---------------
2020-04-08 08:21:36.320732+0100 XMLMPAM[3657:59571] [js] test(), Line 4
2020-04-08 08:21:36.320747+0100 XMLMPAM[3657:59571] [js] test(), Line 7
2020-04-08 08:21:36.320762+0100 XMLMPAM[3657:59571] [js] SyntaxError: Expected token `,'
2020-04-08 08:21:36.320769+0100 XMLMPAM[3657:59571] [js] test(), Line 9
2020-04-08 08:21:36.320790+0100 XMLMPAM[3657:59571] [js] test(), Line 14
2020-04-08 08:21:36.320798+0100 XMLMPAM[3657:59571] [js] test(), Line 16
2020-04-08 08:21:36.320804+0100 XMLMPAM[3657:59571] [js] ---------------

Ignore everything before the [js] that's my timestamp and debug information, after the [js] is all the console output.

What is the:

SyntaxError: Expected token `,'

I can see nothing wrong in the global or the script.

If I modify the script and insert:

var db =  {"db":"test","host":"localhost","usr":"root","pass":"123456"};

As the first line, the syntax error is not displayed and everything is ok, so what is wrong with global added using setProperty?

Here is the code that adds a global to the map:

void clsXMLnode::addGlobal(QString strGlobal) {
    QStringList slstGlobal = strGlobal.split(clsXMLnode::msccGlobalDelimiter);

    if ( slstGlobal.length() == clsXMLnode::mscintAssignmentParts ) {
        QString strName(slstGlobal[clsXMLnode::mscintGlobalName].trimmed())
       ,strValue(slstGlobal[clsXMLnode::mscintGlobalValue].trimmed());
        msmpGlobals.insert(std::make_pair(strName, strValue));
    }
}

Some definitions:

clsXMLnode::msccGlobalDelimiter        is "="
clsXMLnode::mscintAssignmentParts      is 2
clsXMLnode::mscintGlobalName           is 0
clsXMLnode::mscintGlobalValue          is 1
Azeem
  • 11,148
  • 4
  • 27
  • 40
SPlatten
  • 5,334
  • 11
  • 57
  • 128
  • Please add global JSON object and the relevant code where you're setting it. – Azeem Apr 08 '20 at 08:03
  • @Azeem, have you read the post? The first line gets the global object from the engine and the global JSON definition is shown in the post. – SPlatten Apr 08 '20 at 08:10
  • I'm asking about the code where the JSON parsing has been done of the JSON object. Can you show that part of the C++ code? Or, maybe, you can print it to make sure that it's valid JSON for JS code. – Azeem Apr 08 '20 at 08:17
  • There is no special code doing the parsing, the JSON is read from a file and put into a QString, from the QString is split into variable and data using the "=", then the variable and data are put into the map which is where mpGlobals is used. I've edited post to show this function. – SPlatten Apr 08 '20 at 08:20
  • No use of `QJsonDocument`? Can you verify by printing all the values before setting them? You can print those in that loop before the call of `setProperty`. – Azeem Apr 08 '20 at 08:24
  • And, this JSON `{"db":"test","host":"localhost","usr":"root","pass":"123456"}` is stored in the confguration file, right? – Azeem Apr 08 '20 at 08:25
  • @Azeem, yes thats correct. – SPlatten Apr 08 '20 at 08:27

1 Answers1

0

In these lines, you're setting the properties for globalObject:

QJSValue result = pobjScriptEng->evaluate( strData );
objGlobal.setProperty( strName, result );

The exception:

SyntaxError: Expected token `,'

is due to the reason that the config JSON is missing the enclosing parentheses () so the evaluation is unsuccessful. You need to check this with QJSValue::isError() method of result.

Example (See documentation):

QJSValue result = pobjScriptEng->evaluate( strData );
if ( result.isError() )
{
    qDebug() << "Uncaught exception at line"
             << result.property("lineNumber").toInt()
             << ":" << result.toString();
    return -1;
}

Once enclosed properly, the JSON config would be evaluated successfully and it should work.

Here's a complete working example with raw string literals for JSON and JavaScript code:

#include <QCoreApplication>
#include <QJSEngine>
#include <QJSValue>
#include <QDebug>

int main( int argc, char** argv )
{
    QCoreApplication app{ argc, argv };

    const auto raw_config =
    R"json((
    {
        "db": "test",
        "host": "localhost",
        "usr": "root",
        "pass": "123456"
    }
    ))json";

    QJSEngine engine;
    engine.installExtensions( QJSEngine::ConsoleExtension );

    const auto json_config = engine.evaluate( raw_config );
    if ( json_config.isError() )
    {
        qDebug() << "Uncaught exception at line"
                 << json_config.property("lineNumber").toInt()
                 << ":" << json_config.toString();
        return -1;
    }

    engine.globalObject().setProperty( "db", json_config );

    const auto test_script =
    R"javascript((
    function test() {
        try{
            console.info("---------------");

            if ( db === undefined ) {
                console.info("db is undefined");
                return;
            }
            else if ( typeof db === "object" ) {
                for( k in db ) {
                    console.info( k + ": " + db[k] );
                }
            }
            console.info("---------------");
        } catch( e ) {
            console.warn( "test() WARNING: " + e );
        }
    }
    ))javascript";

    auto test_func = engine.evaluate( test_script );
    if ( test_func.isError() )
    {
        qDebug() << "Uncaught exception at line"
                 << test_func.property("lineNumber").toInt()
                 << ":" << test_func.toString();
        return -1;
    }

    const auto result = test_func.call();
    if ( result.isError() )
    {
        qDebug() << "Uncaught exception at line"
                 << result.property("lineNumber").toInt()
                 << ":" << result.toString();
        return -1;
    }

    return 0;
}

Output:

js: ---------------
js: db: test
js: host: localhost
js: usr: root
js: pass: 123456
js: ---------------

Relevant JavaScript-specific thread on the grouping operator () syntax:

Azeem
  • 11,148
  • 4
  • 27
  • 40
  • Was that a new addition to the objects? The requirement for () ? – SPlatten Apr 08 '20 at 11:38
  • @SPlatten: You're welcome! The [documentation](https://doc.qt.io/qt-5/qjsengine.html#evaluating-scripts) uses this syntax. [Here](https://doc.qt.io/qt-5/qtscript-index.html#using-signals-and-slots) too. But, I could not find any specific mention of the syntax in the documentation. – Azeem Apr 08 '20 at 11:54
  • Why couldn't the developers just have modified the evaluate routine to encapsulate everything passed in () ? – SPlatten Apr 08 '20 at 11:58
  • @SPlatten: Here's the [reason](https://stackoverflow.com/questions/440739/what-do-parentheses-surrounding-an-object-function-class-declaration-mean) and it's from JavaScript itself. Just tested it my browser's console and it makes sense. For functions to be called later, the caller needs an object to invoke later and enclosing in `()` returns that callable. Maybe, it's the same for JSON objects also. Example: `var t = (function test() { console.log("hello"); });` and then `t()` would call it. So, the anonymous functions make more sense as the name is not so important entity in this case. – Azeem Apr 08 '20 at 12:34