1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qjsengine.h"
5#include "qjsengine_p.h"
6#include "qjsvalue.h"
7#include "qjsvalue_p.h"
8
9#include "private/qv4engine_p.h"
10#include "private/qv4mm_p.h"
11#include "private/qv4errorobject_p.h"
12#include "private/qv4globalobject_p.h"
13#include "private/qv4script_p.h"
14#include "private/qv4runtime_p.h"
15#include <private/qv4dateobject_p.h>
16#include <private/qqmlbuiltinfunctions_p.h>
17#include <private/qqmldebugconnector_p.h>
18#include <private/qv4qobjectwrapper_p.h>
19#include <private/qv4stackframe_p.h>
20#include <private/qv4module_p.h>
21#include <private/qv4symbol_p.h>
22
23#include <QtCore/qdatetime.h>
24#include <QtCore/qmetaobject.h>
25#include <QtCore/qstringlist.h>
26#include <QtCore/qvariant.h>
27#include <QtCore/qdatetime.h>
28
29#include <QtCore/qcoreapplication.h>
30#include <QtCore/qdir.h>
31#include <QtCore/qfile.h>
32#include <QtCore/qfileinfo.h>
33#include <QtCore/qpluginloader.h>
34#include <qthread.h>
35#include <qmutex.h>
36#include <qwaitcondition.h>
37#include <private/qqmlglobal_p.h>
38#include <qqmlengine.h>
39
40Q_DECLARE_METATYPE(QList<int>)
41
42/*!
43 \since 5.0
44 \class QJSEngine
45 \reentrant
46
47 \brief The QJSEngine class provides an environment for evaluating JavaScript code.
48
49 \ingroup qtjavascript
50 \inmodule QtQml
51
52 \section1 Evaluating Scripts
53
54 Use evaluate() to evaluate script code.
55
56 \snippet code/src_script_qjsengine.cpp 0
57
58 evaluate() returns a QJSValue that holds the result of the
59 evaluation. The QJSValue class provides functions for converting
60 the result to various C++ types (e.g. QJSValue::toString()
61 and QJSValue::toNumber()).
62
63 The following code snippet shows how a script function can be
64 defined and then invoked from C++ using QJSValue::call():
65
66 \snippet code/src_script_qjsengine.cpp 1
67
68 As can be seen from the above snippets, a script is provided to the
69 engine in the form of a string. One common way of loading scripts is
70 by reading the contents of a file and passing it to evaluate():
71
72 \snippet code/src_script_qjsengine.cpp 2
73
74 Here we pass the name of the file as the second argument to
75 evaluate(). This does not affect evaluation in any way; the second
76 argument is a general-purpose string that is stored in the \c Error
77 object for debugging purposes.
78
79 For larger pieces of functionality, you may want to encapsulate
80 your code and data into modules. A module is a file that contains
81 script code, variables, etc., and uses export statements to describe
82 its interface towards the rest of the application. With the help of
83 import statements, a module can refer to functionality from other modules.
84 This allows building a scripted application from smaller connected building blocks
85 in a safe way. In contrast, the approach of using evaluate() carries the risk
86 that internal variables or functions from one evaluate() call accidentally pollute the
87 global object and affect subsequent evaluations.
88
89 The following example provides a module that can add numbers:
90
91 \code
92 export function sum(left, right)
93 {
94 return left + right
95 }
96 \endcode
97
98 This module can be loaded with QJSEngine::import() if it is saved under
99 the name \c{math.mjs}:
100
101 \code
102 QJSvalue module = myEngine.importModule("./math.mjs");
103 QJSValue sumFunction = module.property("sum");
104 QJSValue result = sumFunction.call(args);
105 \endcode
106
107 Modules can also use functionality from other modules using import
108 statements:
109
110 \code
111 import { sum } from "./math.mjs";
112 export function addTwice(left, right)
113 {
114 return sum(left, right) * 2;
115 }
116 \endcode
117
118 Modules don't have to be files. They can be values registered with
119 QJSEngine::registerModule():
120
121 \code
122 import version from "version";
123
124 export function getVersion()
125 {
126 return version;
127 }
128 \endcode
129
130 \code
131 QJSValue version(610);
132 myEngine.registerModule("version", version);
133 QJSValue module = myEngine.importModule("./myprint.mjs");
134 QJSValue getVersion = module.property("getVersion");
135 QJSValue result = getVersion.call();
136 \endcode
137
138 Named exports are supported, but because they are treated as members of an
139 object, the default export must be an ECMAScript object. Most of the newXYZ
140 functions in QJSValue will return an object.
141
142 \code
143 QJSValue name("Qt6");
144 QJSValue obj = myEngine.newObject();
145 obj.setProperty("name", name);
146 myEngine.registerModule("info", obj);
147 \endcode
148
149 \code
150 import { name } from "info";
151
152 export function getName()
153 {
154 return name;
155 }
156 \endcode
157
158 \section1 Engine Configuration
159
160 The globalObject() function returns the \b {Global Object}
161 associated with the script engine. Properties of the Global Object
162 are accessible from any script code (i.e. they are global
163 variables). Typically, before evaluating "user" scripts, you will
164 want to configure a script engine by adding one or more properties
165 to the Global Object:
166
167 \snippet code/src_script_qjsengine.cpp 3
168
169 Adding custom properties to the scripting environment is one of the
170 standard means of providing a scripting API that is specific to your
171 application. Usually these custom properties are objects created by
172 the newQObject() or newObject() functions.
173
174 \section1 Script Exceptions
175
176 evaluate() can throw a script exception (e.g. due to a syntax
177 error). If it does, then evaluate() returns the value that was thrown
178 (typically an \c{Error} object). Use \l QJSValue::isError() to check
179 for exceptions.
180
181 For detailed information about the error, use \l QJSValue::toString() to
182 obtain an error message, and use \l QJSValue::property() to query the
183 properties of the \c Error object. The following properties are available:
184
185 \list
186 \li \c name
187 \li \c message
188 \li \c fileName
189 \li \c lineNumber
190 \li \c stack
191 \endlist
192
193 \snippet code/src_script_qjsengine.cpp 4
194
195 \section1 Script Object Creation
196
197 Use newObject() to create a JavaScript object; this is the
198 C++ equivalent of the script statement \c{new Object()}. You can use
199 the object-specific functionality in QJSValue to manipulate the
200 script object (e.g. QJSValue::setProperty()). Similarly, use
201 newArray() to create a JavaScript array object.
202
203 \section1 QObject Integration
204
205 Use newQObject() to wrap a QObject (or subclass)
206 pointer. newQObject() returns a proxy script object; properties,
207 children, and signals and slots of the QObject are available as
208 properties of the proxy object. No binding code is needed because it
209 is done dynamically using the Qt meta object system.
210
211 \snippet code/src_script_qjsengine.cpp 5
212
213 Use newQMetaObject() to wrap a QMetaObject; this gives you a
214 "script representation" of a QObject-based class. newQMetaObject()
215 returns a proxy script object; enum values of the class are available
216 as properties of the proxy object.
217
218 Constructors exposed to the meta-object system (using Q_INVOKABLE) can be
219 called from the script to create a new QObject instance with
220 JavaScriptOwnership. For example, given the following class definition:
221
222 \snippet code/src_script_qjsengine.cpp 7
223
224 The \c staticMetaObject for the class can be exposed to JavaScript like so:
225
226 \snippet code/src_script_qjsengine.cpp 8
227
228 Instances of the class can then be created in JavaScript:
229
230 \snippet code/src_script_qjsengine.cpp 9
231
232 \note Currently only classes using the Q_OBJECT macro are supported; it is
233 not possible to expose the \c staticMetaObject of a Q_GADGET class to
234 JavaScript.
235
236 \section2 Dynamic QObject Properties
237
238 Dynamic QObject properties are not supported. For example, the following code
239 will not work:
240
241 \snippet code/src_script_qjsengine.cpp 6
242
243 \section1 Extensions
244
245 QJSEngine provides a compliant ECMAScript implementation. By default,
246 familiar utilities like logging are not available, but they can be
247 installed via the \l installExtensions() function.
248
249 \sa QJSValue, {Making Applications Scriptable},
250 {List of JavaScript Objects and Functions}
251
252*/
253
254/*!
255 \enum QJSEngine::Extension
256
257 This enum is used to specify extensions to be installed via
258 \l installExtensions().
259
260 \value TranslationExtension Indicates that translation functions (\c qsTr(),
261 for example) should be installed. This also installs the Qt.uiLanguage property.
262
263 \value ConsoleExtension Indicates that console functions (\c console.log(),
264 for example) should be installed.
265
266 \value GarbageCollectionExtension Indicates that garbage collection
267 functions (\c gc(), for example) should be installed.
268
269 \value AllExtensions Indicates that all extension should be installed.
270
271 \b TranslationExtension
272
273 The relation between script translation functions and C++ translation
274 functions is described in the following table:
275
276 \table
277 \header \li Script Function \li Corresponding C++ Function
278 \row \li qsTr() \li QObject::tr()
279 \row \li QT_TR_NOOP() \li QT_TR_NOOP()
280 \row \li qsTranslate() \li QCoreApplication::translate()
281 \row \li QT_TRANSLATE_NOOP() \li QT_TRANSLATE_NOOP()
282 \row \li qsTrId() \li qtTrId()
283 \row \li QT_TRID_NOOP() \li QT_TRID_NOOP()
284 \endtable
285
286 This flag also adds an \c arg() function to the string prototype.
287
288 For more information, see the \l {Internationalization with Qt}
289 documentation.
290
291 \b ConsoleExtension
292
293 The \l {Console API}{console} object implements a subset of the
294 \l {https://developer.mozilla.org/en-US/docs/Web/API/Console}{Console API},
295 which provides familiar logging functions, such as \c console.log().
296
297 The list of functions added is as follows:
298
299 \list
300 \li \c console.assert()
301 \li \c console.debug()
302 \li \c console.exception()
303 \li \c console.info()
304 \li \c console.log() (equivalent to \c console.debug())
305 \li \c console.error()
306 \li \c console.time()
307 \li \c console.timeEnd()
308 \li \c console.trace()
309 \li \c console.count()
310 \li \c console.warn()
311 \li \c {print()} (equivalent to \c console.debug())
312 \endlist
313
314 For more information, see the \l {Console API} documentation.
315
316 \b GarbageCollectionExtension
317
318 The \c gc() function is equivalent to calling \l collectGarbage().
319*/
320
321QT_BEGIN_NAMESPACE
322
323static void checkForApplicationInstance()
324{
325 if (!QCoreApplication::instance())
326 qFatal(msg: "QJSEngine: Must construct a QCoreApplication before a QJSEngine");
327}
328
329/*!
330 Constructs a QJSEngine object.
331
332 The globalObject() is initialized to have properties as described in
333 \l{ECMA-262}, Section 15.1.
334*/
335QJSEngine::QJSEngine()
336 : QJSEngine(nullptr)
337{
338}
339
340/*!
341 Constructs a QJSEngine object with the given \a parent.
342
343 The globalObject() is initialized to have properties as described in
344 \l{ECMA-262}, Section 15.1.
345*/
346
347QJSEngine::QJSEngine(QObject *parent)
348 : QObject(*new QJSEnginePrivate, parent)
349 , m_v4Engine(new QV4::ExecutionEngine(this))
350{
351 checkForApplicationInstance();
352
353 QJSEnginePrivate::addToDebugServer(q: this);
354}
355
356/*!
357 \internal
358*/
359QJSEngine::QJSEngine(QJSEnginePrivate &dd, QObject *parent)
360 : QObject(dd, parent)
361 , m_v4Engine(new QV4::ExecutionEngine(this))
362{
363 checkForApplicationInstance();
364}
365
366/*!
367 Destroys this QJSEngine.
368
369 Garbage is not collected from the persistent JS heap during QJSEngine
370 destruction. If you need all memory freed, call collectGarbage() manually
371 right before destroying the QJSEngine.
372*/
373QJSEngine::~QJSEngine()
374{
375 QJSEnginePrivate::removeFromDebugServer(q: this);
376 delete m_v4Engine;
377}
378
379/*!
380 \fn QV4::ExecutionEngine *QJSEngine::handle() const
381 \internal
382*/
383
384/*!
385 Runs the garbage collector.
386
387 The garbage collector will attempt to reclaim memory by locating and disposing of objects that are
388 no longer reachable in the script environment.
389
390 Normally you don't need to call this function; the garbage collector will automatically be invoked
391 when the QJSEngine decides that it's wise to do so (i.e. when a certain number of new objects
392 have been created). However, you can call this function to explicitly request that garbage
393 collection should be performed as soon as possible.
394*/
395void QJSEngine::collectGarbage()
396{
397 m_v4Engine->memoryManager->runGC();
398}
399
400/*!
401 \since 5.6
402
403 Installs JavaScript \a extensions to add functionality that is not
404 available in a standard ECMAScript implementation.
405
406 The extensions are installed on the given \a object, or on the
407 \l {globalObject()}{Global Object} if no object is specified.
408
409 Several extensions can be installed at once by \c {OR}-ing the enum values:
410
411 \code
412 installExtensions(QJSEngine::TranslationExtension | QJSEngine::ConsoleExtension);
413 \endcode
414
415 \sa Extension
416*/
417void QJSEngine::installExtensions(QJSEngine::Extensions extensions, const QJSValue &object)
418{
419 QV4::ExecutionEngine *otherEngine = QJSValuePrivate::engine(jsval: &object);
420 if (otherEngine && otherEngine != m_v4Engine) {
421 qWarning(msg: "QJSEngine: Trying to install extensions from a different engine");
422 return;
423 }
424
425 QV4::Scope scope(m_v4Engine);
426 QV4::ScopedObject obj(scope, QJSValuePrivate::asReturnedValue(jsval: &object));
427 if (!obj)
428 obj = scope.engine->globalObject;
429
430 QV4::GlobalExtensions::init(globalObject: obj, extensions);
431}
432
433/*!
434 \since 5.14
435 Interrupts or re-enables JavaScript execution.
436
437 If \a interrupted is \c true, any JavaScript executed by this engine
438 immediately aborts and returns an error object until this function is
439 called again with a value of \c false for \a interrupted.
440
441 This function is thread safe. You may call it from a different thread
442 in order to interrupt, for example, an infinite loop in JavaScript.
443*/
444void QJSEngine::setInterrupted(bool interrupted)
445{
446 m_v4Engine->isInterrupted.storeRelaxed(newValue: interrupted);
447}
448
449/*!
450 \since 5.14
451 Returns whether JavaScript execution is currently interrupted.
452
453 \sa setInterrupted()
454*/
455bool QJSEngine::isInterrupted() const
456{
457 return m_v4Engine->isInterrupted.loadRelaxed();
458}
459
460static QUrl urlForFileName(const QString &fileName)
461{
462 if (!fileName.startsWith(c: QLatin1Char(':')))
463 return QUrl::fromLocalFile(localfile: fileName);
464
465 QUrl url;
466 url.setPath(path: fileName.mid(position: 1));
467 url.setScheme(QLatin1String("qrc"));
468 return url;
469}
470
471/*!
472 Evaluates \a program, using \a lineNumber as the base line number,
473 and returns the result of the evaluation.
474
475 The script code will be evaluated in the context of the global object.
476
477 \note If you need to evaluate inside a QML context, use \l QQmlExpression
478 instead.
479
480 The evaluation of \a program can cause an \l{Script Exceptions}{exception} in the
481 engine; in this case the return value will be the exception
482 that was thrown (typically an \c{Error} object; see
483 QJSValue::isError()).
484
485 \a lineNumber is used to specify a starting line number for \a
486 program; line number information reported by the engine that pertains
487 to this evaluation will be based on this argument. For example, if
488 \a program consists of two lines of code, and the statement on the
489 second line causes a script exception, the exception line number
490 would be \a lineNumber plus one. When no starting line number is
491 specified, line numbers will be 1-based.
492
493 \a fileName is used for error reporting. For example, in error objects
494 the file name is accessible through the "fileName" property if it is
495 provided with this function.
496
497 \a exceptionStackTrace is used to report whether an uncaught exception was
498 thrown. If you pass a non-null pointer to a QStringList to it, it will set
499 it to list of "stackframe messages" if the script threw an unhandled
500 exception, or an empty list otherwise. A stackframe message has the format
501 function name:line number:column:file name
502 \note In some cases, e.g. for native functions, function name and file name
503 can be empty and line number and column can be -1.
504
505 \note If an exception was thrown and the exception value is not an
506 Error instance (i.e., QJSValue::isError() returns \c false), the
507 exception value will still be returned. Use \c exceptionStackTrace->isEmpty()
508 to distinguish whether the value was a normal or an exceptional return
509 value.
510
511 \sa QQmlExpression::evaluate
512*/
513QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, int lineNumber, QStringList *exceptionStackTrace)
514{
515 QV4::ExecutionEngine *v4 = m_v4Engine;
516 QV4::Scope scope(v4);
517 QV4::ScopedValue result(scope);
518
519 QV4::Script script(v4->rootContext(), QV4::Compiler::ContextType::Global, program, urlForFileName(fileName).toString(), lineNumber);
520 script.strictMode = false;
521 if (v4->currentStackFrame)
522 script.strictMode = v4->currentStackFrame->v4Function->isStrict();
523 else if (v4->globalCode)
524 script.strictMode = v4->globalCode->isStrict();
525 script.inheritContext = true;
526 script.parse();
527 if (!scope.hasException())
528 result = script.run();
529 if (exceptionStackTrace)
530 exceptionStackTrace->clear();
531 if (scope.hasException()) {
532 QV4::StackTrace trace;
533 result = v4->catchException(trace: &trace);
534 if (exceptionStackTrace) {
535 for (auto &&frame: trace)
536 exceptionStackTrace->push_back(t: QString::fromLatin1(ba: "%1:%2:%3:%4").arg(
537 args&: frame.function,
538 args: QString::number(qAbs(t: frame.line)),
539 args: QString::number(frame.column),
540 args&: frame.source)
541 );
542 }
543 }
544 if (v4->isInterrupted.loadRelaxed())
545 result = v4->newErrorObject(QStringLiteral("Interrupted"));
546
547 return QJSValuePrivate::fromReturnedValue(d: result->asReturnedValue());
548}
549
550/*!
551 Imports the module located at \a fileName and returns a module namespace object that
552 contains all exported variables, constants and functions as properties.
553
554 If this is the first time the module is imported in the engine, the file is loaded
555 from the specified location in either the local file system or the Qt resource system
556 and evaluated as an ECMAScript module. The file is expected to be encoded in UTF-8 text.
557
558 Subsequent imports of the same module will return the previously imported instance. Modules
559 are singletons and remain around until the engine is destroyed.
560
561 The specified \a fileName will internally be normalized using \l QFileInfo::canonicalFilePath().
562 That means that multiple imports of the same file on disk using different relative paths will
563 load the file only once.
564
565 \note If an exception is thrown during the loading of the module, the return value
566 will be the exception (typically an \c{Error} object; see QJSValue::isError()).
567
568 \sa registerModule()
569
570 \since 5.12
571 */
572QJSValue QJSEngine::importModule(const QString &fileName)
573{
574 const QUrl url = urlForFileName(fileName: QFileInfo(fileName).canonicalFilePath());
575 const auto module = m_v4Engine->loadModule(url: url);
576 if (m_v4Engine->hasException)
577 return QJSValuePrivate::fromReturnedValue(d: m_v4Engine->catchException());
578
579 QV4::Scope scope(m_v4Engine);
580 if (const auto compiled = module.compiled) {
581 QV4::Scoped<QV4::Module> moduleNamespace(scope, compiled->instantiate(engine: m_v4Engine));
582 if (m_v4Engine->hasException)
583 return QJSValuePrivate::fromReturnedValue(d: m_v4Engine->catchException());
584 compiled->evaluate();
585 if (!m_v4Engine->isInterrupted.loadRelaxed())
586 return QJSValuePrivate::fromReturnedValue(d: moduleNamespace->asReturnedValue());
587 return QJSValuePrivate::fromReturnedValue(
588 d: m_v4Engine->newErrorObject(QStringLiteral("Interrupted"))->asReturnedValue());
589 }
590
591 // If there is neither a native nor a compiled module, we should have seen an exception
592 Q_ASSERT(module.native);
593
594 return QJSValuePrivate::fromReturnedValue(d: module.native->asReturnedValue());
595}
596
597/*!
598 Registers a QJSValue to serve as a module. After this function is called,
599 all modules that import \a moduleName will import the value of \a value
600 instead of loading \a moduleName from the filesystem.
601
602 Any valid QJSValue can be registered, but named exports (i.e.
603 \c {import { name } from "info"} are treated as members of an object, so
604 the default export must be created with one of the newXYZ methods of
605 QJSEngine.
606
607 Because this allows modules that do not exist on the filesystem to be imported,
608 scripting applications can use this to provide built-in modules, similar to
609 Node.js.
610
611 Returns \c true on success, \c false otherwise.
612
613 \note The QJSValue \a value is not called or read until it is used by another module.
614 This means that there is no code to evaluate, so no errors will be seen until
615 another module throws an exception while trying to load this module.
616
617 \warning Attempting to access a named export from a QJSValue that is not an
618 object will trigger a \l{Script Exceptions}{exception}.
619
620 \sa importModule()
621 */
622bool QJSEngine::registerModule(const QString &moduleName, const QJSValue &value)
623{
624 QV4::Scope scope(m_v4Engine);
625 QV4::ScopedValue v4Value(scope, QJSValuePrivate::asReturnedValue(jsval: &value));
626 m_v4Engine->registerNativeModule(url: QUrl(moduleName), module: v4Value);
627 if (m_v4Engine->hasException)
628 return false;
629 return true;
630}
631
632/*!
633 Creates a JavaScript object of class Object.
634
635 The prototype of the created object will be the Object
636 prototype object.
637
638 \sa newArray(), QJSValue::setProperty()
639*/
640QJSValue QJSEngine::newObject()
641{
642 QV4::Scope scope(m_v4Engine);
643 QV4::ScopedValue v(scope, m_v4Engine->newObject());
644 return QJSValuePrivate::fromReturnedValue(d: v->asReturnedValue());
645}
646
647/*!
648 \since 6.2
649
650 Creates a JavaScript object of class Symbol, with value \a name.
651
652 The prototype of the created object will be the Symbol prototype object.
653
654 \sa newObject()
655*/
656QJSValue QJSEngine::newSymbol(const QString &name)
657{
658 QV4::Scope scope(m_v4Engine);
659 QV4::ScopedValue v(scope, QV4::Symbol::create(e: m_v4Engine, s: u'@' + name));
660 return QJSValuePrivate::fromReturnedValue(d: v->asReturnedValue());
661}
662
663/*!
664 \since 5.12
665
666 Creates a JavaScript object of class Error, with \a message as the error
667 message.
668
669 The prototype of the created object will be \a errorType.
670
671 \sa newObject(), throwError(), QJSValue::isError()
672*/
673QJSValue QJSEngine::newErrorObject(QJSValue::ErrorType errorType, const QString &message)
674{
675 QV4::Scope scope(m_v4Engine);
676 QV4::ScopedObject error(scope);
677 switch (errorType) {
678 case QJSValue::RangeError:
679 error = m_v4Engine->newRangeErrorObject(message);
680 break;
681 case QJSValue::SyntaxError:
682 error = m_v4Engine->newSyntaxErrorObject(message);
683 break;
684 case QJSValue::TypeError:
685 error = m_v4Engine->newTypeErrorObject(message);
686 break;
687 case QJSValue::URIError:
688 error = m_v4Engine->newURIErrorObject(message);
689 break;
690 case QJSValue::ReferenceError:
691 error = m_v4Engine->newReferenceErrorObject(message);
692 break;
693 case QJSValue::EvalError:
694 error = m_v4Engine->newEvalErrorObject(message);
695 break;
696 case QJSValue::GenericError:
697 error = m_v4Engine->newErrorObject(message);
698 break;
699 case QJSValue::NoError:
700 return QJSValue::UndefinedValue;
701 }
702 return QJSValuePrivate::fromReturnedValue(d: error->asReturnedValue());
703}
704
705/*!
706 Creates a JavaScript object of class Array with the given \a length.
707
708 \sa newObject()
709*/
710QJSValue QJSEngine::newArray(uint length)
711{
712 QV4::Scope scope(m_v4Engine);
713 QV4::ScopedArrayObject array(scope, m_v4Engine->newArrayObject());
714 if (length < 0x1000)
715 array->arrayReserve(n: length);
716 array->setArrayLengthUnchecked(length);
717 return QJSValuePrivate::fromReturnedValue(d: array.asReturnedValue());
718}
719
720/*!
721 Creates a JavaScript object that wraps the given QObject \a
722 object, using JavaScriptOwnership.
723
724 Signals and slots, properties and children of \a object are
725 available as properties of the created QJSValue.
726
727 If \a object is a null pointer, this function returns a null value.
728
729 If a default prototype has been registered for the \a object's class
730 (or its superclass, recursively), the prototype of the new script
731 object will be set to be that default prototype.
732
733 If the given \a object is deleted outside of the engine's control, any
734 attempt to access the deleted QObject's members through the JavaScript
735 wrapper object (either by script code or C++) will result in a
736 \l{Script Exceptions}{script exception}.
737
738 \sa QJSValue::toQObject()
739*/
740QJSValue QJSEngine::newQObject(QObject *object)
741{
742 QV4::ExecutionEngine *v4 = m_v4Engine;
743 QV4::Scope scope(v4);
744 if (object) {
745 QQmlData *ddata = QQmlData::get(object, create: true);
746 if (!ddata || !ddata->explicitIndestructibleSet)
747 QQmlEngine::setObjectOwnership(object, QQmlEngine::JavaScriptOwnership);
748 }
749 QV4::ScopedValue v(scope, QV4::QObjectWrapper::wrap(engine: v4, object));
750 return QJSValuePrivate::fromReturnedValue(d: v->asReturnedValue());
751}
752
753/*!
754 \since 5.8
755
756 Creates a JavaScript object that wraps the given QMetaObject
757 The \a metaObject must outlive the script engine. It is recommended to only
758 use this method with static metaobjects.
759
760
761 When called as a constructor, a new instance of the class will be created.
762 Only constructors exposed by Q_INVOKABLE will be visible from the script engine.
763
764 \sa newQObject(), {QObject Integration}
765*/
766
767QJSValue QJSEngine::newQMetaObject(const QMetaObject* metaObject) {
768 QV4::ExecutionEngine *v4 = m_v4Engine;
769 QV4::Scope scope(v4);
770 QV4::ScopedValue v(scope, QV4::QMetaObjectWrapper::create(engine: v4, metaObject));
771 return QJSValuePrivate::fromReturnedValue(d: v->asReturnedValue());
772}
773
774/*! \fn template <typename T> QJSValue QJSEngine::newQMetaObject()
775
776 \since 5.8
777 Creates a JavaScript object that wraps the static QMetaObject associated
778 with class \c{T}.
779
780 \sa newQObject(), {QObject Integration}
781*/
782
783
784/*!
785 Returns this engine's Global Object.
786
787 By default, the Global Object contains the built-in objects that are
788 part of \l{ECMA-262}, such as Math, Date and String. Additionally,
789 you can set properties of the Global Object to make your own
790 extensions available to all script code. Non-local variables in
791 script code will be created as properties of the Global Object, as
792 well as local variables in global code.
793*/
794QJSValue QJSEngine::globalObject() const
795{
796 QV4::Scope scope(m_v4Engine);
797 QV4::ScopedValue v(scope, m_v4Engine->globalObject);
798 return QJSValuePrivate::fromReturnedValue(d: v->asReturnedValue());
799}
800
801QJSPrimitiveValue QJSEngine::createPrimitive(QMetaType type, const void *ptr)
802{
803 QV4::Scope scope(m_v4Engine);
804 QV4::ScopedValue v(scope, m_v4Engine->metaTypeToJS(type, data: ptr));
805 return QV4::ExecutionEngine::createPrimitive(v);
806}
807
808QJSManagedValue QJSEngine::createManaged(QMetaType type, const void *ptr)
809{
810 QJSManagedValue result(m_v4Engine);
811 *result.d = m_v4Engine->metaTypeToJS(type, data: ptr);
812 return result;
813}
814
815/*!
816 * \internal
817 * used by QJSEngine::toScriptValue
818 */
819QJSValue QJSEngine::create(QMetaType type, const void *ptr)
820{
821 QV4::Scope scope(m_v4Engine);
822 QV4::ScopedValue v(scope, scope.engine->metaTypeToJS(type, data: ptr));
823 return QJSValuePrivate::fromReturnedValue(d: v->asReturnedValue());
824}
825
826bool QJSEngine::convertPrimitive(const QJSPrimitiveValue &value, QMetaType type, void *ptr)
827{
828 switch (value.type()) {
829 case QJSPrimitiveValue::Undefined:
830 return QV4::ExecutionEngine::metaTypeFromJS(value: QV4::Value::undefinedValue(), type, data: ptr);
831 case QJSPrimitiveValue::Null:
832 return QV4::ExecutionEngine::metaTypeFromJS(value: QV4::Value::nullValue(), type, data: ptr);
833 case QJSPrimitiveValue::Boolean:
834 return QV4::ExecutionEngine::metaTypeFromJS(value: QV4::Value::fromBoolean(b: value.toBoolean()), type, data: ptr);
835 case QJSPrimitiveValue::Integer:
836 return QV4::ExecutionEngine::metaTypeFromJS(value: QV4::Value::fromInt32(i: value.toInteger()), type, data: ptr);
837 case QJSPrimitiveValue::Double:
838 return QV4::ExecutionEngine::metaTypeFromJS(value: QV4::Value::fromDouble(d: value.toDouble()), type, data: ptr);
839 case QJSPrimitiveValue::String:
840 return convertString(string: value.toString(), metaType: type, ptr);
841 }
842
843 Q_UNREACHABLE_RETURN(false);
844}
845
846bool QJSEngine::convertManaged(const QJSManagedValue &value, int type, void *ptr)
847{
848 return convertManaged(value, type: QMetaType(type), ptr);
849}
850
851bool QJSEngine::convertManaged(const QJSManagedValue &value, QMetaType type, void *ptr)
852{
853 return QV4::ExecutionEngine::metaTypeFromJS(value: *value.d, type, data: ptr);
854}
855
856bool QJSEngine::convertString(const QString &string, QMetaType metaType, void *ptr)
857{
858 // have a string based value without engine. Do conversion manually
859 if (metaType == QMetaType::fromType<bool>()) {
860 *reinterpret_cast<bool*>(ptr) = string.size() != 0;
861 return true;
862 }
863 if (metaType == QMetaType::fromType<QString>()) {
864 *reinterpret_cast<QString*>(ptr) = string;
865 return true;
866 }
867 if (metaType == QMetaType::fromType<QUrl>()) {
868 *reinterpret_cast<QUrl *>(ptr) = QUrl(string);
869 return true;
870 }
871
872 double d = QV4::RuntimeHelpers::stringToNumber(s: string);
873 switch (metaType.id()) {
874 case QMetaType::Int:
875 *reinterpret_cast<int*>(ptr) = QV4::Value::toInt32(d);
876 return true;
877 case QMetaType::UInt:
878 *reinterpret_cast<uint*>(ptr) = QV4::Value::toUInt32(d);
879 return true;
880 case QMetaType::Long:
881 *reinterpret_cast<long*>(ptr) = QV4::Value::toInteger(d);
882 return true;
883 case QMetaType::ULong:
884 *reinterpret_cast<ulong*>(ptr) = QV4::Value::toInteger(d);
885 return true;
886 case QMetaType::LongLong:
887 *reinterpret_cast<qlonglong*>(ptr) = QV4::Value::toInteger(d);
888 return true;
889 case QMetaType::ULongLong:
890 *reinterpret_cast<qulonglong*>(ptr) = QV4::Value::toInteger(d);
891 return true;
892 case QMetaType::Double:
893 *reinterpret_cast<double*>(ptr) = d;
894 return true;
895 case QMetaType::Float:
896 *reinterpret_cast<float*>(ptr) = d;
897 return true;
898 case QMetaType::Short:
899 *reinterpret_cast<short*>(ptr) = QV4::Value::toInt32(d);
900 return true;
901 case QMetaType::UShort:
902 *reinterpret_cast<unsigned short*>(ptr) = QV4::Value::toUInt32(d);
903 return true;
904 case QMetaType::Char:
905 *reinterpret_cast<char*>(ptr) = QV4::Value::toInt32(d);
906 return true;
907 case QMetaType::UChar:
908 *reinterpret_cast<unsigned char*>(ptr) = QV4::Value::toUInt32(d);
909 return true;
910 case QMetaType::QChar:
911 *reinterpret_cast<QChar*>(ptr) = QChar(QV4::Value::toUInt32(d));
912 return true;
913 case QMetaType::Char16:
914 *reinterpret_cast<char16_t *>(ptr) = QV4::Value::toUInt32(d);
915 return true;
916 default:
917 return false;
918 }
919}
920
921/*!
922 \internal
923 convert \a value to \a type, store the result in \a ptr
924*/
925bool QJSEngine::convertV2(const QJSValue &value, QMetaType metaType, void *ptr)
926{
927 if (const QString *string = QJSValuePrivate::asQString(jsval: &value))
928 return convertString(string: *string, metaType, ptr);
929
930 // Does not need scoping since QJSValue still holds on to the value.
931 return QV4::ExecutionEngine::metaTypeFromJS(value: QJSValuePrivate::asReturnedValue(jsval: &value), type: metaType, data: ptr);
932}
933
934bool QJSEngine::convertVariant(const QVariant &value, QMetaType metaType, void *ptr)
935{
936 // TODO: We could probably avoid creating a QV4::Value in many cases, but we'd have to
937 // duplicate much of metaTypeFromJS and some methods of QV4::Value itself here.
938 QV4::Scope scope(handle());
939 QV4::ScopedValue scoped(scope, scope.engine->fromVariant(value));
940 return QV4::ExecutionEngine::metaTypeFromJS(value: scoped, type: metaType, data: ptr);
941}
942
943bool QJSEngine::convertMetaType(QMetaType fromType, const void *from, QMetaType toType, void *to)
944{
945 // TODO: We could probably avoid creating a QV4::Value in many cases, but we'd have to
946 // duplicate much of metaTypeFromJS and some methods of QV4::Value itself here.
947 QV4::Scope scope(handle());
948 QV4::ScopedValue scoped(scope, scope.engine->fromData(type: fromType, ptr: from));
949 return QV4::ExecutionEngine::metaTypeFromJS(value: scoped, type: toType, data: to);
950}
951
952QString QJSEngine::convertQObjectToString(QObject *object)
953{
954 return QV4::QObjectWrapper::objectToString(
955 engine: handle(), metaObject: object ? object->metaObject() : nullptr, object);
956}
957
958QString QJSEngine::convertDateTimeToString(const QDateTime &dateTime)
959{
960 return QV4::DateObject::dateTimeToString(dateTime, engine: handle());
961}
962
963QDate QJSEngine::convertDateTimeToDate(const QDateTime &dateTime)
964{
965 return QV4::DateObject::dateTimeToDate(dateTime);
966}
967
968/*! \fn template <typename T> QJSValue QJSEngine::toScriptValue(const T &value)
969
970 Creates a QJSValue with the given \a value.
971
972 \sa fromScriptValue(), coerceValue()
973*/
974
975/*! \fn template <typename T> QJSManagedValue QJSEngine::toManagedValue(const T &value)
976
977 Creates a QJSManagedValue with the given \a value.
978
979 \sa fromManagedValue(), coerceValue()
980*/
981
982/*! \fn template <typename T> QJSPrimitiveValue QJSEngine::toPrimitiveValue(const T &value)
983
984 Creates a QJSPrimitiveValue with the given \a value.
985
986 Since QJSPrimitiveValue can only hold int, bool, double, QString, and the
987 equivalents of JavaScript \c null and \c undefined, the value will be
988 coerced aggressively if you pass any other type.
989
990 \sa fromPrimitiveValue(), coerceValue()
991*/
992
993/*! \fn template <typename T> T QJSEngine::fromScriptValue(const QJSValue &value)
994
995 Returns the given \a value converted to the template type \c{T}.
996
997 \sa toScriptValue(), coerceValue()
998*/
999
1000/*! \fn template <typename T> T QJSEngine::fromManagedValue(const QJSManagedValue &value)
1001
1002 Returns the given \a value converted to the template type \c{T}.
1003
1004 \sa toManagedValue(), coerceValue()
1005*/
1006
1007/*! \fn template <typename T> T QJSEngine::fromPrimitiveValue(const QJSPrimitiveValue &value)
1008
1009 Returns the given \a value converted to the template type \c{T}.
1010
1011 Since QJSPrimitiveValue can only hold int, bool, double, QString, and the
1012 equivalents of JavaScript \c null and \c undefined, the value will be
1013 coerced aggressively if you request any other type.
1014
1015 \sa toPrimitiveValue(), coerceValue()
1016*/
1017
1018/*! \fn template <typename T> T QJSEngine::fromVariant(const QVariant &value)
1019
1020 Returns the given \a value converted to the template type \c{T}.
1021 The conversion is done in JavaScript semantics. Those differ from
1022 qvariant_cast's semantics. There are a number of implicit
1023 conversions between JavaScript-equivalent types that are not
1024 performed by qvariant_cast by default.
1025
1026 \sa coerceValue(), fromScriptValue(), qvariant_cast()
1027*/
1028
1029/*! \fn template <typename From, typename To> T QJSEngine::coerceValue(const From &from)
1030
1031 Returns the given \a from converted to the template type \c{To}.
1032 The conversion is done in JavaScript semantics. Those differ from
1033 qvariant_cast's semantics. There are a number of implicit
1034 conversions between JavaScript-equivalent types that are not
1035 performed by qvariant_cast by default. This method is a generalization of
1036 all the other conversion methods in this class.
1037
1038 \sa fromVariant(), qvariant_cast(), fromScriptValue(), toScriptValue()
1039*/
1040
1041/*!
1042 Throws a run-time error (exception) with the given \a message.
1043
1044 This method is the C++ counterpart of a \c throw() expression in
1045 JavaScript. It enables C++ code to report run-time errors to QJSEngine.
1046 Therefore it should only be called from C++ code that was invoked by a
1047 JavaScript function through QJSEngine.
1048
1049 When returning from C++, the engine will interrupt the normal flow of
1050 execution and call the next pre-registered exception handler with
1051 an error object that contains the given \a message. The error object
1052 will point to the location of the top-most context on the JavaScript
1053 caller stack; specifically, it will have properties \c lineNumber,
1054 \c fileName and \c stack. These properties are described in
1055 \l{Script Exceptions}.
1056
1057 In the following example a C++ method in \e FileAccess.cpp throws an error
1058 in \e qmlFile.qml at the position where \c readFileAsText() is called:
1059
1060 \code
1061 // qmlFile.qml
1062 function someFunction() {
1063 ...
1064 var text = FileAccess.readFileAsText("/path/to/file.txt");
1065 }
1066 \endcode
1067
1068 \code
1069 // FileAccess.cpp
1070 // Assuming that FileAccess is a QObject-derived class that has been
1071 // registered as a singleton type and provides an invokable method
1072 // readFileAsText()
1073
1074 QJSValue FileAccess::readFileAsText(const QString & filePath) {
1075 QFile file(filePath);
1076
1077 if (!file.open(QIODevice::ReadOnly)) {
1078 jsEngine->throwError(file.errorString());
1079 return QString();
1080 }
1081
1082 ...
1083 return content;
1084 }
1085 \endcode
1086
1087 It is also possible to catch the thrown error in JavaScript:
1088 \code
1089 // qmlFile.qml
1090 function someFunction() {
1091 ...
1092 var text;
1093 try {
1094 text = FileAccess.readFileAsText("/path/to/file.txt");
1095 } catch (error) {
1096 console.warn("In " + error.fileName + ":" + "error.lineNumber" +
1097 ": " + error.message);
1098 }
1099 }
1100 \endcode
1101
1102 If you need a more specific run-time error to describe an exception, you can use the
1103 \l {QJSEngine::}{throwError(QJSValue::ErrorType errorType, const QString &message)}
1104 overload.
1105
1106 \since Qt 5.12
1107 \sa {Script Exceptions}
1108*/
1109void QJSEngine::throwError(const QString &message)
1110{
1111 m_v4Engine->throwError(message);
1112}
1113
1114/*!
1115 \overload throwError()
1116
1117 Throws a run-time error (exception) with the given \a errorType and
1118 \a message.
1119
1120 \code
1121 // Assuming that DataEntry is a QObject-derived class that has been
1122 // registered as a singleton type and provides an invokable method
1123 // setAge().
1124
1125 void DataEntry::setAge(int age) {
1126 if (age < 0 || age > 200) {
1127 jsEngine->throwError(QJSValue::RangeError,
1128 "Age must be between 0 and 200");
1129 }
1130 ...
1131 }
1132 \endcode
1133
1134 \since Qt 5.12
1135 \sa {Script Exceptions}, newErrorObject()
1136*/
1137void QJSEngine::throwError(QJSValue::ErrorType errorType, const QString &message)
1138{
1139 QV4::Scope scope(m_v4Engine);
1140 QJSValue error = newErrorObject(errorType, message);
1141 QV4::ScopedObject e(scope, QJSValuePrivate::asReturnedValue(jsval: &error));
1142 if (!e)
1143 return;
1144 m_v4Engine->throwError(value: e);
1145}
1146
1147/*!
1148 \overload throwError()
1149
1150 Throws a pre-constructed run-time \a error (exception). This way you can
1151 use \l newErrorObject() to create the error and customize it as necessary.
1152
1153 \since 6.1
1154 \sa {Script Exceptions}, newErrorObject()
1155*/
1156void QJSEngine::throwError(const QJSValue &error)
1157{
1158 m_v4Engine->throwError(value: QJSValuePrivate::asReturnedValue(jsval: &error));
1159}
1160
1161/*!
1162 * Returns \c true if the last JavaScript execution resulted in an exception or
1163 * if throwError() was called. Otherwise returns \c false. Mind that evaluate()
1164 * catches any exceptions thrown in the evaluated code.
1165 *
1166 * \since Qt 6.1
1167 */
1168bool QJSEngine::hasError() const
1169{
1170 return m_v4Engine->hasException;
1171}
1172
1173/*!
1174 * If an exception is currently pending, catches it and returns it as a
1175 * QJSValue. Otherwise returns undefined as QJSValue. After calling this method
1176 * hasError() returns \c false.
1177 *
1178 * \since Qt 6.1
1179 */
1180QJSValue QJSEngine::catchError()
1181{
1182 if (m_v4Engine->hasException)
1183 return QJSValuePrivate::fromReturnedValue(d: m_v4Engine->catchException());
1184 else
1185 return QJSValue();
1186}
1187
1188/*!
1189 \property QJSEngine::uiLanguage
1190 \brief the language to be used for translating user interface strings
1191 \since 5.15
1192
1193 This property holds the name of the language to be used for user interface
1194 string translations. It is exposed for reading and writing as \c{Qt.uiLanguage} when
1195 the QJSEngine::TranslationExtension is installed on the engine. It is always exposed
1196 in instances of QQmlEngine.
1197
1198 You can set the value freely and use it in bindings. It is recommended to set it
1199 after installing translators in your application. By convention, an empty string
1200 means no translation from the language used in the source code is intended to occur.
1201*/
1202void QJSEngine::setUiLanguage(const QString &language) {
1203 Q_D(QJSEngine);
1204 d->uiLanguage = language; // property takes care of signal emission if necessary
1205}
1206
1207QString QJSEngine::uiLanguage() const
1208{
1209 Q_D(const QJSEngine);
1210 return d->uiLanguage;
1211}
1212
1213QJSEnginePrivate *QJSEnginePrivate::get(QV4::ExecutionEngine *e)
1214{
1215 return e->jsEngine()->d_func();
1216}
1217
1218QJSEnginePrivate::~QJSEnginePrivate()
1219{
1220 QQmlMetaType::freeUnusedTypesAndCaches();
1221}
1222
1223void QJSEnginePrivate::addToDebugServer(QJSEngine *q)
1224{
1225 if (QCoreApplication::instance()->thread() != q->thread())
1226 return;
1227
1228 QQmlDebugConnector *server = QQmlDebugConnector::instance();
1229 if (!server || server->hasEngine(engine: q))
1230 return;
1231
1232 server->open();
1233 server->addEngine(engine: q);
1234}
1235
1236void QJSEnginePrivate::removeFromDebugServer(QJSEngine *q)
1237{
1238 QQmlDebugConnector *server = QQmlDebugConnector::instance();
1239 if (server && server->hasEngine(engine: q))
1240 server->removeEngine(engine: q);
1241}
1242
1243/*!
1244 \since 5.5
1245 \relates QJSEngine
1246
1247 Returns the QJSEngine associated with \a object, if any.
1248
1249 This function is useful if you have exposed a QObject to the JavaScript environment
1250 and later in your program would like to regain access. It does not require you to
1251 keep the wrapper around that was returned from QJSEngine::newQObject().
1252 */
1253QJSEngine *qjsEngine(const QObject *object)
1254{
1255 QQmlData *data = QQmlData::get(object);
1256 if (!data || data->jsWrapper.isNullOrUndefined())
1257 return nullptr;
1258 return data->jsWrapper.engine()->jsEngine();
1259}
1260
1261
1262/*!
1263 \enum QJSEngine::ObjectOwnership
1264
1265 ObjectOwnership controls whether or not the JavaScript memory manager automatically destroys the
1266 QObject when the corresponding JavaScript object is garbage collected by the
1267 engine. The two ownership options are:
1268
1269 \value CppOwnership The object is owned by C++ code and the JavaScript memory manager will never
1270 delete it. The JavaScript destroy() method cannot be used on these objects. This
1271 option is similar to QScriptEngine::QtOwnership.
1272
1273 \value JavaScriptOwnership The object is owned by JavaScript. When the object
1274 is returned to the JavaScript memory manager as the return value of a method call, the JavaScript
1275 memory manager will track it and delete it if there are no remaining JavaScript references to it
1276 and it has no QObject::parent(). An object tracked by one QJSEngine will be deleted during that
1277 QJSEngine's destructor. Thus, JavaScript references between objects with JavaScriptOwnership from
1278 two different engines will not be valid if one of these engines is deleted. This option is similar
1279 to QScriptEngine::ScriptOwnership.
1280
1281 Generally an application doesn't need to set an object's ownership explicitly. The JavaScript
1282 memory manager uses a heuristic to set the default ownership. By default, an object that is
1283 created by the JavaScript memory manager has JavaScriptOwnership. The exception to this are the
1284 root objects created by calling QQmlComponent::create() or QQmlComponent::beginCreate(), which
1285 have CppOwnership by default. The ownership of these root-level objects is considered to have been
1286 transferred to the C++ caller.
1287
1288 Objects not-created by the JavaScript memory manager have CppOwnership by default. The exception
1289 to this are objects returned from C++ method calls; their ownership will be set to
1290 JavaScriptOwnership. This applies only to explicit invocations of Q_INVOKABLE methods or slots,
1291 but not to property getter invocations.
1292
1293 Calling setObjectOwnership() overrides the default ownership.
1294
1295 \sa {Data Ownership}
1296*/
1297
1298/*!
1299 Sets the \a ownership of \a object.
1300
1301 An object with \c JavaScriptOwnership is not garbage collected as long
1302 as it still has a parent, even if there are no references to it.
1303
1304 \sa QJSEngine::ObjectOwnership
1305*/
1306void QJSEngine::setObjectOwnership(QObject *object, ObjectOwnership ownership)
1307{
1308 if (!object)
1309 return;
1310
1311 QQmlData *ddata = QQmlData::get(object, create: true);
1312 if (!ddata)
1313 return;
1314
1315 ddata->indestructible = (ownership == CppOwnership)?true:false;
1316 ddata->explicitIndestructibleSet = true;
1317}
1318
1319/*!
1320 Returns the ownership of \a object.
1321
1322 \sa QJSEngine::ObjectOwnership
1323*/
1324QJSEngine::ObjectOwnership QJSEngine::objectOwnership(QObject *object)
1325{
1326 if (!object)
1327 return CppOwnership;
1328
1329 QQmlData *ddata = QQmlData::get(object, create: false);
1330 if (!ddata)
1331 return CppOwnership;
1332 else
1333 return ddata->indestructible?CppOwnership:JavaScriptOwnership;
1334}
1335
1336QT_END_NAMESPACE
1337
1338#include "moc_qjsengine.cpp"
1339

source code of qtdeclarative/src/qml/jsapi/qjsengine.cpp