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 
Q_DECLARE_METATYPE(QList<int>)74 Q_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 
315 QT_BEGIN_NAMESPACE
316 
317 static void checkForApplicationInstance()
318 {
319     if (!QCoreApplication::instance())
320         qFatal("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 */
QJSEngine()329 QJSEngine::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 
QJSEngine(QObject * parent)341 QJSEngine::QJSEngine(QObject *parent)
342     : QObject(*new QJSEnginePrivate, parent)
343     , m_v4Engine(new QV4::ExecutionEngine(this))
344 {
345     checkForApplicationInstance();
346 
347     QJSEnginePrivate::addToDebugServer(this);
348 }
349 
350 /*!
351     \internal
352 */
QJSEngine(QJSEnginePrivate & dd,QObject * parent)353 QJSEngine::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 */
~QJSEngine()367 QJSEngine::~QJSEngine()
368 {
369     QJSEnginePrivate::removeFromDebugServer(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 */
collectGarbage()389 void 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 */
installTranslatorFunctions(const QJSValue & object)420 void QJSEngine::installTranslatorFunctions(const QJSValue &object)
421 {
422     installExtensions(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 */
installExtensions(QJSEngine::Extensions extensions,const QJSValue & object)445 void QJSEngine::installExtensions(QJSEngine::Extensions extensions, const QJSValue &object)
446 {
447     QV4::ExecutionEngine *otherEngine = QJSValuePrivate::engine(&object);
448     if (otherEngine && otherEngine != m_v4Engine) {
449         qWarning("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(&object);
456     if (val)
457         obj = val;
458     if (!obj)
459         obj = scope.engine->globalObject;
460 
461     QV4::GlobalExtensions::init(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 */
setInterrupted(bool interrupted)475 void 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 */
isInterrupted() const486 bool QJSEngine::isInterrupted() const
487 {
488     return m_v4Engine->isInterrupted.loadAcquire();
489 }
490 
urlForFileName(const QString & fileName)491 static QUrl urlForFileName(const QString &fileName)
492 {
493     if (!fileName.startsWith(QLatin1Char(':')))
494         return QUrl::fromLocalFile(fileName);
495 
496     QUrl url;
497     url.setPath(fileName.mid(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 */
evaluate(const QString & program,const QString & fileName,int lineNumber)530 QJSValue 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  */
importModule(const QString & fileName)576 QJSValue QJSEngine::importModule(const QString &fileName)
577 {
578     const QUrl url = urlForFileName(QFileInfo(fileName).canonicalFilePath());
579     auto moduleUnit = m_v4Engine->loadModule(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(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 */
newObject()604 QJSValue 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 */
newErrorObject(QJSValue::ErrorType errorType,const QString & message)621 QJSValue 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 */
newArray(uint length)658 QJSValue 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(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 */
newQObject(QObject * object)688 QJSValue 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, true);
694         if (!ddata || !ddata->explicitIndestructibleSet)
695             QQmlEngine::setObjectOwnership(object, QQmlEngine::JavaScriptOwnership);
696     }
697     QV4::ScopedValue v(scope, QV4::QObjectWrapper::wrap(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 
newQMetaObject(const QMetaObject * metaObject)715 QJSValue QJSEngine::newQMetaObject(const QMetaObject* metaObject) {
716     QV4::ExecutionEngine *v4 = m_v4Engine;
717     QV4::Scope scope(v4);
718     QV4::ScopedValue v(scope, QV4::QMetaObjectWrapper::create(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 */
globalObject() const742 QJSValue 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  */
create(int type,const void * ptr)753 QJSValue QJSEngine::create(int type, const void *ptr)
754 {
755     QV4::Scope scope(m_v4Engine);
756     QV4::ScopedValue v(scope, scope.engine->metaTypeToJS(type, 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 */
convertV2(const QJSValue & value,int type,void * ptr)764 bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr)
765 {
766     QV4::ExecutionEngine *v4 = QJSValuePrivate::engine(&value);
767     QV4::Value scratch;
768     QV4::Value *val = QJSValuePrivate::valueForData(&value, &scratch);
769     if (v4) {
770         QV4::Scope scope(v4);
771         QV4::ScopedValue v(scope, *val);
772         return scope.engine->metaTypeFromJS(v, type, ptr);
773     }
774 
775     if (!val) {
776         QVariant *variant = QJSValuePrivate::getVariant(&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(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(&variant->data_ptr(), variant->userType(), ptr, 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 */
throwError(const QString & message)962 void 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 */
throwError(QJSValue::ErrorType errorType,const QString & message)990 void 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(&error));
995     if (!e)
996         return;
997     m_v4Engine->throwError(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 */
setUiLanguage(const QString & language)1014 void 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 
uiLanguage() const1023 QString QJSEngine::uiLanguage() const
1024 {
1025     Q_D(const QJSEngine);
1026     return d->uiLanguage;
1027 }
1028 
get(QV4::ExecutionEngine * e)1029 QJSEnginePrivate *QJSEnginePrivate::get(QV4::ExecutionEngine *e)
1030 {
1031     return e->jsEngine()->d_func();
1032 }
1033 
~QJSEnginePrivate()1034 QJSEnginePrivate::~QJSEnginePrivate()
1035 {
1036     QQmlMetaType::freeUnusedTypesAndCaches();
1037 }
1038 
addToDebugServer(QJSEngine * q)1039 void 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(q))
1046         return;
1047 
1048     server->open();
1049     server->addEngine(q);
1050 }
1051 
removeFromDebugServer(QJSEngine * q)1052 void QJSEnginePrivate::removeFromDebugServer(QJSEngine *q)
1053 {
1054     QQmlDebugConnector *server = QQmlDebugConnector::instance();
1055     if (server && server->hasEngine(q))
1056         server->removeEngine(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  */
qjsEngine(const QObject * object)1069 QJSEngine *qjsEngine(const QObject *object)
1070 {
1071     QQmlData *data = QQmlData::get(object, false);
1072     if (!data || data->jsWrapper.isNullOrUndefined())
1073         return nullptr;
1074     return data->jsWrapper.engine()->jsEngine();
1075 }
1076 
1077 QT_END_NAMESPACE
1078 
1079 #include "moc_qjsengine.cpp"
1080