1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qjsengine.h"
41#include "qjsengine_p.h"
42#include "qjsvalue.h"
43#include "qjsvalue_p.h"
44
45#include "private/qv4engine_p.h"
46#include "private/qv4mm_p.h"
47#include "private/qv4errorobject_p.h"
48#include "private/qv4globalobject_p.h"
49#include "private/qv4script_p.h"
50#include "private/qv4runtime_p.h"
51#include <private/qqmlbuiltinfunctions_p.h>
52#include <private/qqmldebugconnector_p.h>
53#include <private/qv4qobjectwrapper_p.h>
54#include <private/qv4stackframe_p.h>
55#include <private/qv4module_p.h>
56
57#include <QtCore/qdatetime.h>
58#include <QtCore/qmetaobject.h>
59#include <QtCore/qstringlist.h>
60#include <QtCore/qvariant.h>
61#include <QtCore/qdatetime.h>
62
63#include <QtCore/qcoreapplication.h>
64#include <QtCore/qdir.h>
65#include <QtCore/qfile.h>
66#include <QtCore/qfileinfo.h>
67#include <QtCore/qpluginloader.h>
68#include <qthread.h>
69#include <qmutex.h>
70#include <qwaitcondition.h>
71#include <private/qqmlglobal_p.h>
72#include <qqmlengine.h>
73
74Q_DECLARE_METATYPE(QList<int>)
75
76/*!
77 \since 5.0
78 \class QJSEngine
79 \reentrant
80
81 \brief The QJSEngine class provides an environment for evaluating JavaScript code.
82
83 \ingroup qtjavascript
84 \inmodule QtQml
85
86 \section1 Evaluating Scripts
87
88 Use evaluate() to evaluate script code.
89
90 \snippet code/src_script_qjsengine.cpp 0
91
92 evaluate() returns a QJSValue that holds the result of the
93 evaluation. The QJSValue class provides functions for converting
94 the result to various C++ types (e.g. QJSValue::toString()
95 and QJSValue::toNumber()).
96
97 The following code snippet shows how a script function can be
98 defined and then invoked from C++ using QJSValue::call():
99
100 \snippet code/src_script_qjsengine.cpp 1
101
102 As can be seen from the above snippets, a script is provided to the
103 engine in the form of a string. One common way of loading scripts is
104 by reading the contents of a file and passing it to evaluate():
105
106 \snippet code/src_script_qjsengine.cpp 2
107
108 Here we pass the name of the file as the second argument to
109 evaluate(). This does not affect evaluation in any way; the second
110 argument is a general-purpose string that is stored in the \c Error
111 object for debugging purposes.
112
113 For larger pieces of functionality, you may want to encapsulate
114 your code and data into modules. A module is a file that contains
115 script code, variables, etc., and uses export statements to describe
116 its interface towards the rest of the application. With the help of
117 import statements, a module can refer to functionality from other modules.
118 This allows building a scripted application from smaller connected building blocks
119 in a safe way. In contrast, the approach of using evaluate() carries the risk
120 that internal variables or functions from one evaluate() call accidentally pollute the
121 global object and affect subsequent evaluations.
122
123 The following example provides a module that can add numbers:
124
125 \code
126 export function sum(left, right)
127 {
128 return left + right
129 }
130 \endcode
131
132 This module can be loaded with QJSEngine::import() if it is saved under
133 the name \c{math.mjs}:
134
135 \code
136 QJSvalue module = myEngine.importModule("./math.mjs");
137 QJSValue sumFunction = module.property("sum");
138 QJSValue result = sumFunction.call(args);
139 \endcode
140
141 Modules can also use functionality from other modules using import
142 statements:
143
144 \code
145 import { sum } from "./math.mjs";
146 export function addTwice(left, right)
147 {
148 return sum(left, right) * 2;
149 }
150 \endcode
151
152 \section1 Engine Configuration
153
154 The globalObject() function returns the \b {Global Object}
155 associated with the script engine. Properties of the Global Object
156 are accessible from any script code (i.e. they are global
157 variables). Typically, before evaluating "user" scripts, you will
158 want to configure a script engine by adding one or more properties
159 to the Global Object:
160
161 \snippet code/src_script_qjsengine.cpp 3
162
163 Adding custom properties to the scripting environment is one of the
164 standard means of providing a scripting API that is specific to your
165 application. Usually these custom properties are objects created by
166 the newQObject() or newObject() functions.
167
168 \section1 Script Exceptions
169
170 evaluate() can throw a script exception (e.g. due to a syntax
171 error). If it does, then evaluate() returns the value that was thrown
172 (typically an \c{Error} object). Use \l QJSValue::isError() to check
173 for exceptions.
174
175 For detailed information about the error, use \l QJSValue::toString() to
176 obtain an error message, and use \l QJSValue::property() to query the
177 properties of the \c Error object. The following properties are available:
178
179 \list
180 \li \c name
181 \li \c message
182 \li \c fileName
183 \li \c lineNumber
184 \li \c stack
185 \endlist
186
187 \snippet code/src_script_qjsengine.cpp 4
188
189 \section1 Script Object Creation
190
191 Use newObject() to create a JavaScript object; this is the
192 C++ equivalent of the script statement \c{new Object()}. You can use
193 the object-specific functionality in QJSValue to manipulate the
194 script object (e.g. QJSValue::setProperty()). Similarly, use
195 newArray() to create a JavaScript array object.
196
197 \section1 QObject Integration
198
199 Use newQObject() to wrap a QObject (or subclass)
200 pointer. newQObject() returns a proxy script object; properties,
201 children, and signals and slots of the QObject are available as
202 properties of the proxy object. No binding code is needed because it
203 is done dynamically using the Qt meta object system.
204
205 \snippet code/src_script_qjsengine.cpp 5
206
207 Use newQMetaObject() to wrap a QMetaObject; this gives you a
208 "script representation" of a QObject-based class. newQMetaObject()
209 returns a proxy script object; enum values of the class are available
210 as properties of the proxy object.
211
212 Constructors exposed to the meta-object system (using Q_INVOKABLE) can be
213 called from the script to create a new QObject instance with
214 JavaScriptOwnership. For example, given the following class definition:
215
216 \snippet code/src_script_qjsengine.cpp 7
217
218 The \c staticMetaObject for the class can be exposed to JavaScript like so:
219
220 \snippet code/src_script_qjsengine.cpp 8
221
222 Instances of the class can then be created in JavaScript:
223
224 \snippet code/src_script_qjsengine.cpp 9
225
226 \note Currently only classes using the Q_OBJECT macro are supported; it is
227 not possible to expose the \c staticMetaObject of a Q_GADGET class to
228 JavaScript.
229
230 \section2 Dynamic QObject Properties
231
232 Dynamic QObject properties are not supported. For example, the following code
233 will not work:
234
235 \snippet code/src_script_qjsengine.cpp 6
236
237 \section1 Extensions
238
239 QJSEngine provides a compliant ECMAScript implementation. By default,
240 familiar utilities like logging are not available, but they can can be
241 installed via the \l installExtensions() function.
242
243 \sa QJSValue, {Making Applications Scriptable},
244 {List of JavaScript Objects and Functions}
245
246*/
247
248/*!
249 \enum QJSEngine::Extension
250
251 This enum is used to specify extensions to be installed via
252 \l installExtensions().
253
254 \value TranslationExtension Indicates that translation functions (\c qsTr(),
255 for example) should be installed. This also installs the Qt.uiLanguage property.
256
257 \value ConsoleExtension Indicates that console functions (\c console.log(),
258 for example) should be installed.
259
260 \value GarbageCollectionExtension Indicates that garbage collection
261 functions (\c gc(), for example) should be installed.
262
263 \value AllExtensions Indicates that all extension should be installed.
264
265 \b TranslationExtension
266
267 The relation between script translation functions and C++ translation
268 functions is described in the following table:
269
270 \table
271 \header \li Script Function \li Corresponding C++ Function
272 \row \li qsTr() \li QObject::tr()
273 \row \li QT_TR_NOOP() \li QT_TR_NOOP()
274 \row \li qsTranslate() \li QCoreApplication::translate()
275 \row \li QT_TRANSLATE_NOOP() \li QT_TRANSLATE_NOOP()
276 \row \li qsTrId() \li qtTrId()
277 \row \li QT_TRID_NOOP() \li QT_TRID_NOOP()
278 \endtable
279
280 This flag also adds an \c arg() function to the string prototype.
281
282 For more information, see the \l {Internationalization with Qt}
283 documentation.
284
285 \b ConsoleExtension
286
287 The \l {Console API}{console} object implements a subset of the
288 \l {https://developer.mozilla.org/en-US/docs/Web/API/Console}{Console API},
289 which provides familiar logging functions, such as \c console.log().
290
291 The list of functions added is as follows:
292
293 \list
294 \li \c console.assert()
295 \li \c console.debug()
296 \li \c console.exception()
297 \li \c console.info()
298 \li \c console.log() (equivalent to \c console.debug())
299 \li \c console.error()
300 \li \c console.time()
301 \li \c console.timeEnd()
302 \li \c console.trace()
303 \li \c console.count()
304 \li \c console.warn()
305 \li \c {print()} (equivalent to \c console.debug())
306 \endlist
307
308 For more information, see the \l {Console API} documentation.
309
310 \b GarbageCollectionExtension
311
312 The \c gc() function is equivalent to calling \l collectGarbage().
313*/
314
315QT_BEGIN_NAMESPACE
316
317static void checkForApplicationInstance()
318{
319 if (!QCoreApplication::instance())
320 qFatal(msg: "QJSEngine: Must construct a QCoreApplication before a QJSEngine");
321}
322
323/*!
324 Constructs a QJSEngine object.
325
326 The globalObject() is initialized to have properties as described in
327 \l{ECMA-262}, Section 15.1.
328*/
329QJSEngine::QJSEngine()
330 : QJSEngine(nullptr)
331{
332}
333
334/*!
335 Constructs a QJSEngine object with the given \a parent.
336
337 The globalObject() is initialized to have properties as described in
338 \l{ECMA-262}, Section 15.1.
339*/
340
341QJSEngine::QJSEngine(QObject *parent)
342 : QObject(*new QJSEnginePrivate, parent)
343 , m_v4Engine(new QV4::ExecutionEngine(this))
344{
345 checkForApplicationInstance();
346
347 QJSEnginePrivate::addToDebugServer(q: this);
348}
349
350/*!
351 \internal
352*/
353QJSEngine::QJSEngine(QJSEnginePrivate &dd, QObject *parent)
354 : QObject(dd, parent)
355 , m_v4Engine(new QV4::ExecutionEngine(this))
356{
357 checkForApplicationInstance();
358}
359
360/*!
361 Destroys this QJSEngine.
362
363 Garbage is not collected from the persistent JS heap during QJSEngine
364 destruction. If you need all memory freed, call collectGarbage manually
365 right before destroying the QJSEngine.
366*/
367QJSEngine::~QJSEngine()
368{
369 QJSEnginePrivate::removeFromDebugServer(q: this);
370 delete m_v4Engine;
371}
372
373/*!
374 \fn QV4::ExecutionEngine *QJSEngine::handle() const
375 \internal
376*/
377
378/*!
379 Runs the garbage collector.
380
381 The garbage collector will attempt to reclaim memory by locating and disposing of objects that are
382 no longer reachable in the script environment.
383
384 Normally you don't need to call this function; the garbage collector will automatically be invoked
385 when the QJSEngine decides that it's wise to do so (i.e. when a certain number of new objects
386 have been created). However, you can call this function to explicitly request that garbage
387 collection should be performed as soon as possible.
388*/
389void QJSEngine::collectGarbage()
390{
391 m_v4Engine->memoryManager->runGC();
392}
393
394#if QT_DEPRECATED_SINCE(5, 6)
395
396/*!
397 \since 5.4
398 \obsolete
399
400 Installs translator functions on the given \a object, or on the Global
401 Object if no object is specified.
402
403 The relation between script translator functions and C++ translator
404 functions is described in the following table:
405
406 \table
407 \header \li Script Function \li Corresponding C++ Function
408 \row \li qsTr() \li QObject::tr()
409 \row \li QT_TR_NOOP() \li QT_TR_NOOP()
410 \row \li qsTranslate() \li QCoreApplication::translate()
411 \row \li QT_TRANSLATE_NOOP() \li QT_TRANSLATE_NOOP()
412 \row \li qsTrId() \li qtTrId()
413 \row \li QT_TRID_NOOP() \li QT_TRID_NOOP()
414 \endtable
415
416 It also adds an arg() method to the string prototype.
417
418 \sa {Internationalization with Qt}
419*/
420void QJSEngine::installTranslatorFunctions(const QJSValue &object)
421{
422 installExtensions(extensions: TranslationExtension, object);
423}
424
425#endif // QT_DEPRECATED_SINCE(5, 6)
426
427
428/*!
429 \since 5.6
430
431 Installs JavaScript \a extensions to add functionality that is not
432 available in a standard ECMAScript implementation.
433
434 The extensions are installed on the given \a object, or on the
435 \l {globalObject()}{Global Object} if no object is specified.
436
437 Several extensions can be installed at once by \c {OR}-ing the enum values:
438
439 \code
440 installExtensions(QJSEngine::TranslationExtension | QJSEngine::ConsoleExtension);
441 \endcode
442
443 \sa Extension
444*/
445void QJSEngine::installExtensions(QJSEngine::Extensions extensions, const QJSValue &object)
446{
447 QV4::ExecutionEngine *otherEngine = QJSValuePrivate::engine(jsval: &object);
448 if (otherEngine && otherEngine != m_v4Engine) {
449 qWarning(msg: "QJSEngine: Trying to install extensions from a different engine");
450 return;
451 }
452
453 QV4::Scope scope(m_v4Engine);
454 QV4::ScopedObject obj(scope);
455 QV4::Value *val = QJSValuePrivate::getValue(jsval: &object);
456 if (val)
457 obj = val;
458 if (!obj)
459 obj = scope.engine->globalObject;
460
461 QV4::GlobalExtensions::init(globalObject: obj, extensions);
462}
463
464/*!
465 \since 5.14
466 Interrupts or re-enables JavaScript execution.
467
468 If \a interrupted is \c true, any JavaScript executed by this engine
469 immediately aborts and returns an error object until this function is
470 called again with a value of \c false for \a interrupted.
471
472 This function is thread safe. You may call it from a different thread
473 in order to interrupt, for example, an infinite loop in JavaScript.
474*/
475void QJSEngine::setInterrupted(bool interrupted)
476{
477 m_v4Engine->isInterrupted = interrupted;
478}
479
480/*!
481 \since 5.14
482 Returns whether JavaScript execution is currently interrupted.
483
484 \sa setInterrupted()
485*/
486bool QJSEngine::isInterrupted() const
487{
488 return m_v4Engine->isInterrupted.loadAcquire();
489}
490
491static QUrl urlForFileName(const QString &fileName)
492{
493 if (!fileName.startsWith(c: QLatin1Char(':')))
494 return QUrl::fromLocalFile(localfile: fileName);
495
496 QUrl url;
497 url.setPath(path: fileName.mid(position: 1));
498 url.setScheme(QLatin1String("qrc"));
499 return url;
500}
501
502/*!
503 Evaluates \a program, using \a lineNumber as the base line number,
504 and returns the result of the evaluation.
505
506 The script code will be evaluated in the context of the global object.
507
508 The evaluation of \a program can cause an \l{Script Exceptions}{exception} in the
509 engine; in this case the return value will be the exception
510 that was thrown (typically an \c{Error} object; see
511 QJSValue::isError()).
512
513 \a lineNumber is used to specify a starting line number for \a
514 program; line number information reported by the engine that pertains
515 to this evaluation will be based on this argument. For example, if
516 \a program consists of two lines of code, and the statement on the
517 second line causes a script exception, the exception line number
518 would be \a lineNumber plus one. When no starting line number is
519 specified, line numbers will be 1-based.
520
521 \a fileName is used for error reporting. For example, in error objects
522 the file name is accessible through the "fileName" property if it is
523 provided with this function.
524
525 \note If an exception was thrown and the exception value is not an
526 Error instance (i.e., QJSValue::isError() returns \c false), the
527 exception value will still be returned, but there is currently no
528 API for detecting that an exception did occur in this case.
529*/
530QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, int lineNumber)
531{
532 QV4::ExecutionEngine *v4 = m_v4Engine;
533 QV4::Scope scope(v4);
534 QV4::ScopedValue result(scope);
535
536 QV4::Script script(v4->rootContext(), QV4::Compiler::ContextType::Global, program, urlForFileName(fileName).toString(), lineNumber);
537 script.strictMode = false;
538 if (v4->currentStackFrame)
539 script.strictMode = v4->currentStackFrame->v4Function->isStrict();
540 else if (v4->globalCode)
541 script.strictMode = v4->globalCode->isStrict();
542 script.inheritContext = true;
543 script.parse();
544 if (!scope.engine->hasException)
545 result = script.run();
546 if (scope.engine->hasException)
547 result = v4->catchException();
548 if (v4->isInterrupted.loadAcquire())
549 result = v4->newErrorObject(QStringLiteral("Interrupted"));
550
551 QJSValue retval(v4, result->asReturnedValue());
552
553 return retval;
554}
555
556/*!
557 Imports the module located at \a fileName and returns a module namespace object that
558 contains all exported variables, constants and functions as properties.
559
560 If this is the first time the module is imported in the engine, the file is loaded
561 from the specified location in either the local file system or the Qt resource system
562 and evaluated as an ECMAScript module. The file is expected to be encoded in UTF-8 text.
563
564 Subsequent imports of the same module will return the previously imported instance. Modules
565 are singletons and remain around until the engine is destroyed.
566
567 The specified \a fileName will internally be normalized using \l QFileInfo::canonicalFilePath().
568 That means that multiple imports of the same file on disk using different relative paths will
569 load the file only once.
570
571 \note If an exception is thrown during the loading of the module, the return value
572 will be the exception (typically an \c{Error} object; see QJSValue::isError()).
573
574 \since 5.12
575 */
576QJSValue QJSEngine::importModule(const QString &fileName)
577{
578 const QUrl url = urlForFileName(fileName: QFileInfo(fileName).canonicalFilePath());
579 auto moduleUnit = m_v4Engine->loadModule(url: url);
580 if (m_v4Engine->hasException)
581 return QJSValue(m_v4Engine, m_v4Engine->catchException());
582
583 QV4::Scope scope(m_v4Engine);
584 QV4::Scoped<QV4::Module> moduleNamespace(scope, moduleUnit->instantiate(engine: m_v4Engine));
585 if (m_v4Engine->hasException)
586 return QJSValue(m_v4Engine, m_v4Engine->catchException());
587 moduleUnit->evaluate();
588 if (!m_v4Engine->isInterrupted.loadAcquire())
589 return QJSValue(m_v4Engine, moduleNamespace->asReturnedValue());
590
591 return QJSValue(
592 m_v4Engine,
593 m_v4Engine->newErrorObject(QStringLiteral("Interrupted"))->asReturnedValue());
594}
595
596/*!
597 Creates a JavaScript object of class Object.
598
599 The prototype of the created object will be the Object
600 prototype object.
601
602 \sa newArray(), QJSValue::setProperty()
603*/
604QJSValue QJSEngine::newObject()
605{
606 QV4::Scope scope(m_v4Engine);
607 QV4::ScopedValue v(scope, m_v4Engine->newObject());
608 return QJSValue(m_v4Engine, v->asReturnedValue());
609}
610
611/*!
612 \since 5.12
613
614 Creates a JavaScript object of class Error, with \a message as the error
615 message.
616
617 The prototype of the created object will be \a errorType.
618
619 \sa newObject(), throwError(), QJSValue::isError()
620*/
621QJSValue QJSEngine::newErrorObject(QJSValue::ErrorType errorType, const QString &message)
622{
623 QV4::Scope scope(m_v4Engine);
624 QV4::ScopedObject error(scope);
625 switch (errorType) {
626 case QJSValue::RangeError:
627 error = m_v4Engine->newRangeErrorObject(message);
628 break;
629 case QJSValue::SyntaxError:
630 error = m_v4Engine->newSyntaxErrorObject(message);
631 break;
632 case QJSValue::TypeError:
633 error = m_v4Engine->newTypeErrorObject(message);
634 break;
635 case QJSValue::URIError:
636 error = m_v4Engine->newURIErrorObject(message);
637 break;
638 case QJSValue::ReferenceError:
639 error = m_v4Engine->newReferenceErrorObject(message);
640 break;
641 case QJSValue::EvalError:
642 error = m_v4Engine->newEvalErrorObject(message);
643 break;
644 case QJSValue::GenericError:
645 error = m_v4Engine->newErrorObject(message);
646 break;
647 case QJSValue::NoError:
648 return QJSValue::UndefinedValue;
649 }
650 return QJSValue(m_v4Engine, error->asReturnedValue());
651}
652
653/*!
654 Creates a JavaScript object of class Array with the given \a length.
655
656 \sa newObject()
657*/
658QJSValue QJSEngine::newArray(uint length)
659{
660 QV4::Scope scope(m_v4Engine);
661 QV4::ScopedArrayObject array(scope, m_v4Engine->newArrayObject());
662 if (length < 0x1000)
663 array->arrayReserve(n: length);
664 array->setArrayLengthUnchecked(length);
665 return QJSValue(m_v4Engine, array.asReturnedValue());
666}
667
668/*!
669 Creates a JavaScript object that wraps the given QObject \a
670 object, using JavaScriptOwnership.
671
672 Signals and slots, properties and children of \a object are
673 available as properties of the created QJSValue.
674
675 If \a object is a null pointer, this function returns a null value.
676
677 If a default prototype has been registered for the \a object's class
678 (or its superclass, recursively), the prototype of the new script
679 object will be set to be that default prototype.
680
681 If the given \a object is deleted outside of the engine's control, any
682 attempt to access the deleted QObject's members through the JavaScript
683 wrapper object (either by script code or C++) will result in a
684 \l{Script Exceptions}{script exception}.
685
686 \sa QJSValue::toQObject()
687*/
688QJSValue QJSEngine::newQObject(QObject *object)
689{
690 QV4::ExecutionEngine *v4 = m_v4Engine;
691 QV4::Scope scope(v4);
692 if (object) {
693 QQmlData *ddata = QQmlData::get(object, create: true);
694 if (!ddata || !ddata->explicitIndestructibleSet)
695 QQmlEngine::setObjectOwnership(object, QQmlEngine::JavaScriptOwnership);
696 }
697 QV4::ScopedValue v(scope, QV4::QObjectWrapper::wrap(engine: v4, object));
698 return QJSValue(v4, v->asReturnedValue());
699}
700
701/*!
702 \since 5.8
703
704 Creates a JavaScript object that wraps the given QMetaObject
705 The \a metaObject must outlive the script engine. It is recommended to only
706 use this method with static metaobjects.
707
708
709 When called as a constructor, a new instance of the class will be created.
710 Only constructors exposed by Q_INVOKABLE will be visible from the script engine.
711
712 \sa newQObject(), {QObject Integration}
713*/
714
715QJSValue QJSEngine::newQMetaObject(const QMetaObject* metaObject) {
716 QV4::ExecutionEngine *v4 = m_v4Engine;
717 QV4::Scope scope(v4);
718 QV4::ScopedValue v(scope, QV4::QMetaObjectWrapper::create(engine: v4, metaObject));
719 return QJSValue(v4, v->asReturnedValue());
720}
721
722/*! \fn template <typename T> QJSValue QJSEngine::newQMetaObject()
723
724 \since 5.8
725 Creates a JavaScript object that wraps the static QMetaObject associated
726 with class \c{T}.
727
728 \sa newQObject(), {QObject Integration}
729*/
730
731
732/*!
733 Returns this engine's Global Object.
734
735 By default, the Global Object contains the built-in objects that are
736 part of \l{ECMA-262}, such as Math, Date and String. Additionally,
737 you can set properties of the Global Object to make your own
738 extensions available to all script code. Non-local variables in
739 script code will be created as properties of the Global Object, as
740 well as local variables in global code.
741*/
742QJSValue QJSEngine::globalObject() const
743{
744 QV4::Scope scope(m_v4Engine);
745 QV4::ScopedValue v(scope, m_v4Engine->globalObject);
746 return QJSValue(m_v4Engine, v->asReturnedValue());
747}
748
749/*!
750 * \internal
751 * used by QJSEngine::toScriptValue
752 */
753QJSValue QJSEngine::create(int type, const void *ptr)
754{
755 QV4::Scope scope(m_v4Engine);
756 QV4::ScopedValue v(scope, scope.engine->metaTypeToJS(type, data: ptr));
757 return QJSValue(m_v4Engine, v->asReturnedValue());
758}
759
760/*!
761 \internal
762 convert \a value to \a type, store the result in \a ptr
763*/
764bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr)
765{
766 QV4::ExecutionEngine *v4 = QJSValuePrivate::engine(jsval: &value);
767 QV4::Value scratch;
768 QV4::Value *val = QJSValuePrivate::valueForData(jsval: &value, scratch: &scratch);
769 if (v4) {
770 QV4::Scope scope(v4);
771 QV4::ScopedValue v(scope, *val);
772 return scope.engine->metaTypeFromJS(value: v, type, data: ptr);
773 }
774
775 if (!val) {
776 QVariant *variant = QJSValuePrivate::getVariant(jsval: &value);
777 Q_ASSERT(variant);
778
779 if (variant->userType() == QMetaType::QString) {
780 QString string = variant->toString();
781 // have a string based value without engine. Do conversion manually
782 if (type == QMetaType::Bool) {
783 *reinterpret_cast<bool*>(ptr) = string.length() != 0;
784 return true;
785 }
786 if (type == QMetaType::QString) {
787 *reinterpret_cast<QString*>(ptr) = string;
788 return true;
789 }
790 double d = QV4::RuntimeHelpers::stringToNumber(s: string);
791 switch (type) {
792 case QMetaType::Int:
793 *reinterpret_cast<int*>(ptr) = QV4::Value::toInt32(d);
794 return true;
795 case QMetaType::UInt:
796 *reinterpret_cast<uint*>(ptr) = QV4::Value::toUInt32(d);
797 return true;
798 case QMetaType::LongLong:
799 *reinterpret_cast<qlonglong*>(ptr) = QV4::Value::toInteger(d);
800 return true;
801 case QMetaType::ULongLong:
802 *reinterpret_cast<qulonglong*>(ptr) = QV4::Value::toInteger(d);
803 return true;
804 case QMetaType::Double:
805 *reinterpret_cast<double*>(ptr) = d;
806 return true;
807 case QMetaType::Float:
808 *reinterpret_cast<float*>(ptr) = d;
809 return true;
810 case QMetaType::Short:
811 *reinterpret_cast<short*>(ptr) = QV4::Value::toInt32(d);
812 return true;
813 case QMetaType::UShort:
814 *reinterpret_cast<unsigned short*>(ptr) = QV4::Value::toUInt32(d);
815 return true;
816 case QMetaType::Char:
817 *reinterpret_cast<char*>(ptr) = QV4::Value::toInt32(d);
818 return true;
819 case QMetaType::UChar:
820 *reinterpret_cast<unsigned char*>(ptr) = QV4::Value::toUInt32(d);
821 return true;
822 case QMetaType::QChar:
823 *reinterpret_cast<QChar*>(ptr) = QV4::Value::toUInt32(d);
824 return true;
825 default:
826 return false;
827 }
828 } else {
829 return QMetaType::convert(from: &variant->data_ptr(), fromTypeId: variant->userType(), to: ptr, toTypeId: type);
830 }
831 }
832
833 Q_ASSERT(val);
834
835 switch (type) {
836 case QMetaType::Bool:
837 *reinterpret_cast<bool*>(ptr) = val->toBoolean();
838 return true;
839 case QMetaType::Int:
840 *reinterpret_cast<int*>(ptr) = val->toInt32();
841 return true;
842 case QMetaType::UInt:
843 *reinterpret_cast<uint*>(ptr) = val->toUInt32();
844 return true;
845 case QMetaType::LongLong:
846 *reinterpret_cast<qlonglong*>(ptr) = val->toInteger();
847 return true;
848 case QMetaType::ULongLong:
849 *reinterpret_cast<qulonglong*>(ptr) = val->toInteger();
850 return true;
851 case QMetaType::Double:
852 *reinterpret_cast<double*>(ptr) = val->toNumber();
853 return true;
854 case QMetaType::QString:
855 *reinterpret_cast<QString*>(ptr) = val->toQStringNoThrow();
856 return true;
857 case QMetaType::Float:
858 *reinterpret_cast<float*>(ptr) = val->toNumber();
859 return true;
860 case QMetaType::Short:
861 *reinterpret_cast<short*>(ptr) = val->toInt32();
862 return true;
863 case QMetaType::UShort:
864 *reinterpret_cast<unsigned short*>(ptr) = val->toUInt16();
865 return true;
866 case QMetaType::Char:
867 *reinterpret_cast<char*>(ptr) = val->toInt32();
868 return true;
869 case QMetaType::UChar:
870 *reinterpret_cast<unsigned char*>(ptr) = val->toUInt16();
871 return true;
872 case QMetaType::QChar:
873 *reinterpret_cast<QChar*>(ptr) = val->toUInt16();
874 return true;
875 default:
876 return false;
877 }
878}
879
880/*! \fn template <typename T> QJSValue QJSEngine::toScriptValue(const T &value)
881
882 Creates a QJSValue with the given \a value.
883
884 \sa fromScriptValue()
885*/
886
887/*! \fn template <typename T> T QJSEngine::fromScriptValue(const QJSValue &value)
888
889 Returns the given \a value converted to the template type \c{T}.
890
891 \sa toScriptValue()
892*/
893
894/*!
895 Throws a run-time error (exception) with the given \a message.
896
897 This method is the C++ counterpart of a \c throw() expression in
898 JavaScript. It enables C++ code to report run-time errors to QJSEngine.
899 Therefore it should only be called from C++ code that was invoked by a
900 JavaScript function through QJSEngine.
901
902 When returning from C++, the engine will interrupt the normal flow of
903 execution and call the the next pre-registered exception handler with
904 an error object that contains the given \a message. The error object
905 will point to the location of the top-most context on the JavaScript
906 caller stack; specifically, it will have properties \c lineNumber,
907 \c fileName and \c stack. These properties are described in
908 \l{Script Exceptions}.
909
910 In the following example a C++ method in \e FileAccess.cpp throws an error
911 in \e qmlFile.qml at the position where \c readFileAsText() is called:
912
913 \code
914 // qmlFile.qml
915 function someFunction() {
916 ...
917 var text = FileAccess.readFileAsText("/path/to/file.txt");
918 }
919 \endcode
920
921 \code
922 // FileAccess.cpp
923 // Assuming that FileAccess is a QObject-derived class that has been
924 // registered as a singleton type and provides an invokable method
925 // readFileAsText()
926
927 QJSValue FileAccess::readFileAsText(const QString & filePath) {
928 QFile file(filePath);
929
930 if (!file.open(QIODevice::ReadOnly)) {
931 jsEngine->throwError(file.errorString());
932 return QString();
933 }
934
935 ...
936 return content;
937 }
938 \endcode
939
940 It is also possible to catch the thrown error in JavaScript:
941 \code
942 // qmlFile.qml
943 function someFunction() {
944 ...
945 var text;
946 try {
947 text = FileAccess.readFileAsText("/path/to/file.txt");
948 } catch (error) {
949 console.warn("In " + error.fileName + ":" + "error.lineNumber" +
950 ": " + error.message);
951 }
952 }
953 \endcode
954
955 If you need a more specific run-time error to describe an exception, you can use the
956 \l {QJSEngine::}{throwError(QJSValue::ErrorType errorType, const QString &message)}
957 overload.
958
959 \since Qt 5.12
960 \sa {Script Exceptions}
961*/
962void QJSEngine::throwError(const QString &message)
963{
964 m_v4Engine->throwError(message);
965}
966
967/*!
968 \overload throwError()
969
970 Throws a run-time error (exception) with the given \a errorType and
971 \a message.
972
973 \code
974 // Assuming that DataEntry is a QObject-derived class that has been
975 // registered as a singleton type and provides an invokable method
976 // setAge().
977
978 void DataEntry::setAge(int age) {
979 if (age < 0 || age > 200) {
980 jsEngine->throwError(QJSValue::RangeError,
981 "Age must be between 0 and 200");
982 }
983 ...
984 }
985 \endcode
986
987 \since Qt 5.12
988 \sa {Script Exceptions}, newErrorObject()
989*/
990void QJSEngine::throwError(QJSValue::ErrorType errorType, const QString &message)
991{
992 QV4::Scope scope(m_v4Engine);
993 QJSValue error = newErrorObject(errorType, message);
994 QV4::ScopedObject e(scope, QJSValuePrivate::getValue(jsval: &error));
995 if (!e)
996 return;
997 m_v4Engine->throwError(value: e);
998}
999
1000/*!
1001 \property QJSEngine::uiLanguage
1002 \brief the language to be used for translating user interface strings
1003 \since 5.15
1004
1005 This property holds the name of the language to be used for user interface
1006 string translations. It is exposed for reading and writing as \c{Qt.uiLanguage} when
1007 the QJSEngine::TranslationExtension is installed on the engine. It is always exposed
1008 in instances of QQmlEngine.
1009
1010 You can set the value freely and use it in bindings. It is recommended to set it
1011 after installing translators in your application. By convention, an empty string
1012 means no translation from the language used in the source code is intended to occur.
1013*/
1014void QJSEngine::setUiLanguage(const QString &language)
1015{
1016 Q_D(QJSEngine);
1017 if (language == d->uiLanguage)
1018 return;
1019 d->uiLanguage = language;
1020 emit uiLanguageChanged();
1021}
1022
1023QString QJSEngine::uiLanguage() const
1024{
1025 Q_D(const QJSEngine);
1026 return d->uiLanguage;
1027}
1028
1029QJSEnginePrivate *QJSEnginePrivate::get(QV4::ExecutionEngine *e)
1030{
1031 return e->jsEngine()->d_func();
1032}
1033
1034QJSEnginePrivate::~QJSEnginePrivate()
1035{
1036 QQmlMetaType::freeUnusedTypesAndCaches();
1037}
1038
1039void QJSEnginePrivate::addToDebugServer(QJSEngine *q)
1040{
1041 if (QCoreApplication::instance()->thread() != q->thread())
1042 return;
1043
1044 QQmlDebugConnector *server = QQmlDebugConnector::instance();
1045 if (!server || server->hasEngine(engine: q))
1046 return;
1047
1048 server->open();
1049 server->addEngine(engine: q);
1050}
1051
1052void QJSEnginePrivate::removeFromDebugServer(QJSEngine *q)
1053{
1054 QQmlDebugConnector *server = QQmlDebugConnector::instance();
1055 if (server && server->hasEngine(engine: q))
1056 server->removeEngine(engine: q);
1057}
1058
1059/*!
1060 \since 5.5
1061 \relates QJSEngine
1062
1063 Returns the QJSEngine associated with \a object, if any.
1064
1065 This function is useful if you have exposed a QObject to the JavaScript environment
1066 and later in your program would like to regain access. It does not require you to
1067 keep the wrapper around that was returned from QJSEngine::newQObject().
1068 */
1069QJSEngine *qjsEngine(const QObject *object)
1070{
1071 QQmlData *data = QQmlData::get(object, create: false);
1072 if (!data || data->jsWrapper.isNullOrUndefined())
1073 return nullptr;
1074 return data->jsWrapper.engine()->jsEngine();
1075}
1076
1077QT_END_NAMESPACE
1078
1079#include "moc_qjsengine.cpp"
1080

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