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

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