Using Nt Qml
How to run JavaScript in an application, and let scripts use its QObjects.
Nt Qml runs JavaScript. QJSEngine evaluates programs, QJSValue holds what they make, and QObjects take part in scripts with their properties, methods and signals. It is the base of QML, which follows.
find_package(Nt6 REQUIRED)
add_executable(app main.cpp)
target_link_libraries(app PRIVATE Nt::Qml)
Running Code
evaluate() runs a program and returns the value of its last statement. Functions come back as values that C++ can call:
QJSEngine engine;
engine.installExtensions(QJSEngine::ConsoleExtension);
QJSValue result = engine.evaluate("[1, 2, 3].map(x => x * x).join(' + ')");
qDebug() << result.toString(); // "1 + 4 + 9"
QJSValue square = engine.evaluate("(function(x) { return x * x; })");
qDebug() << square.call({12}).toInt(); // 144
QJSValue error = engine.evaluate("undefinedFunction()", "inline.js");
if (error.isError())
qDebug() << "line" << error.property("lineNumber").toInt() << error.toString();
Errors do not escape as C++ exceptions: evaluate() and QJSValue::call() return the error, an object with name, message, fileName, lineNumber and stack.
QObjects in Scripts
newQObject() gives scripts a QObject. They read and write its Q_PROPERTYs, call its slots and Q_INVOKABLE methods, emit its signals by calling them, and connect them to functions with signal.connect(f) or signal.connect(thisObject, f):
Counter counter;
engine.globalObject().setProperty("counter", engine.newQObject(&counter));
engine.evaluate(R"(
counter.valueChanged.connect(v => console.log("counter is now", v));
counter.value = 10;
counter.add(5);
)");
qDebug() << counter.value(); // 15
Enumeration properties take their keys as strings, "Heating", or their values. Overloaded methods are chosen by the number and types of the arguments. Properties that scripts add to a QObject are kept on its script value. A script calling a method of a deleted object gets a TypeError.
The Language
Nt implements a large part of ECMAScript 2017, interpreted:
var,letandconst, with block scopes and closures that capture each loop iteration;functions, arrow functions, default and rest parameters, spread arguments and array elements;
object literals with methods, getters, setters, computed keys and spread; prototypes and
new;?.,??,**, logical assignment, template literals and regular expression literals;for-in,for-ofover arrays and strings, labels,switch,trywithfinally;Object, Function, Array, String, Number, Boolean, Math, JSON, Date, RegExp, the errors, and the global functions;
consoleandprintwith QJSEngine::ConsoleExtension.
Not supported: classes, generators, async functions and promises, destructuring, symbols and iterators (for-of works on arrays, strings and array-likes), Map and Set, Proxy and Reflect, typed arrays, getters on arrays, tagged templates, with, and making functions from strings. Arrays have no holes: writing past the end fills the gap with undefined. Regular expressions run on QRegularExpression, whose syntax is that of JavaScript for the common cases.
Untrusted Scripts
Source code is treated as untrusted input. Nesting in the source, recursion at run time and the size of arrays and strings are limited, so a hostile script fails with an error instead of exhausting the stack or memory. QJSEngine::setInterrupted() stops a script that runs too long; call it from a timer or another thread. A script can do only what the objects given to it allow: there is no file or network access of its own.
The js_scripting example runs the snippets above, or a file given on its command line.