javascriptc++qtqjsengine

QJSEngine, global and script, what is causing the syntax error


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

Solution

  • 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: