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 | |
41 | Q_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 | |
322 | QT_BEGIN_NAMESPACE |
323 | |
324 | static 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 | */ |
336 | QJSEngine::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 | |
348 | QJSEngine::QJSEngine(QObject *parent) |
349 | : QJSEngine(*new QJSEnginePrivate, parent) |
350 | { |
351 | QJSEnginePrivate::addToDebugServer(q: this); |
352 | } |
353 | |
354 | /*! |
355 | \internal |
356 | */ |
357 | QJSEngine::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 | */ |
371 | QJSEngine::~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 | */ |
398 | void QJSEngine::collectGarbage() |
399 | { |
400 | m_v4Engine->memoryManager->runGC(); |
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 | */ |
420 | void 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 | */ |
447 | void 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 | */ |
458 | bool QJSEngine::isInterrupted() const |
459 | { |
460 | return m_v4Engine->isInterrupted.loadRelaxed(); |
461 | } |
462 | |
463 | static 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 | */ |
516 | QJSValue 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 | return QJSValuePrivate::fromReturnedValue(d: result->asReturnedValue()); |
551 | } |
552 | |
553 | /*! |
554 | Imports the module located at \a fileName and returns a module namespace object that |
555 | contains all exported variables, constants and functions as properties. |
556 | |
557 | If this is the first time the module is imported in the engine, the file is loaded |
558 | from the specified location in either the local file system or the Qt resource system |
559 | and evaluated as an ECMAScript module. The file is expected to be encoded in UTF-8 text. |
560 | |
561 | Subsequent imports of the same module will return the previously imported instance. Modules |
562 | are singletons and remain around until the engine is destroyed. |
563 | |
564 | The specified \a fileName will internally be normalized using \l QFileInfo::canonicalFilePath(). |
565 | That means that multiple imports of the same file on disk using different relative paths will |
566 | load the file only once. |
567 | |
568 | \note If an exception is thrown during the loading of the module, the return value |
569 | will be the exception (typically an \c{Error} object; see QJSValue::isError()). |
570 | |
571 | \sa registerModule() |
572 | |
573 | \since 5.12 |
574 | */ |
575 | QJSValue QJSEngine::importModule(const QString &fileName) |
576 | { |
577 | const QUrl url = urlForFileName(fileName: QFileInfo(fileName).canonicalFilePath()); |
578 | const auto module = m_v4Engine->loadModule(url: url); |
579 | if (m_v4Engine->hasException) |
580 | return QJSValuePrivate::fromReturnedValue(d: m_v4Engine->catchException()); |
581 | |
582 | QV4::Scope scope(m_v4Engine); |
583 | if (const auto compiled = module.compiled) { |
584 | QV4::Scoped<QV4::Module> moduleNamespace(scope, compiled->instantiate()); |
585 | if (m_v4Engine->hasException) |
586 | return QJSValuePrivate::fromReturnedValue(d: m_v4Engine->catchException()); |
587 | compiled->evaluate(); |
588 | if (!m_v4Engine->isInterrupted.loadRelaxed()) |
589 | return QJSValuePrivate::fromReturnedValue(d: moduleNamespace->asReturnedValue()); |
590 | return QJSValuePrivate::fromReturnedValue( |
591 | d: m_v4Engine->newErrorObject(QStringLiteral("Interrupted"))->asReturnedValue()); |
592 | } |
593 | |
594 | // If there is neither a native nor a compiled module, we should have seen an exception |
595 | Q_ASSERT(module.native); |
596 | |
597 | return QJSValuePrivate::fromReturnedValue(d: module.native->asReturnedValue()); |
598 | } |
599 | |
600 | /*! |
601 | Registers a QJSValue to serve as a module. After this function is called, |
602 | all modules that import \a moduleName will import the value of \a value |
603 | instead of loading \a moduleName from the filesystem. |
604 | |
605 | Any valid QJSValue can be registered, but named exports (i.e. |
606 | \c {import { name } from "info"} are treated as members of an object, so |
607 | the default export must be created with one of the newXYZ methods of |
608 | QJSEngine. |
609 | |
610 | Because this allows modules that do not exist on the filesystem to be imported, |
611 | scripting applications can use this to provide built-in modules, similar to |
612 | Node.js. |
613 | |
614 | Returns \c true on success, \c false otherwise. |
615 | |
616 | \note The QJSValue \a value is not called or read until it is used by another module. |
617 | This means that there is no code to evaluate, so no errors will be seen until |
618 | another module throws an exception while trying to load this module. |
619 | |
620 | \warning Attempting to access a named export from a QJSValue that is not an |
621 | object will trigger a \l{Script Exceptions}{exception}. |
622 | |
623 | \sa importModule() |
624 | */ |
625 | bool QJSEngine::registerModule(const QString &moduleName, const QJSValue &value) |
626 | { |
627 | QV4::Scope scope(m_v4Engine); |
628 | QV4::ScopedValue v4Value(scope, QJSValuePrivate::asReturnedValue(jsval: &value)); |
629 | m_v4Engine->registerNativeModule(url: QUrl(moduleName), module: v4Value); |
630 | if (m_v4Engine->hasException) |
631 | return false; |
632 | return true; |
633 | } |
634 | |
635 | /*! |
636 | Creates a JavaScript object of class Object. |
637 | |
638 | The prototype of the created object will be the Object |
639 | prototype object. |
640 | |
641 | \sa newArray(), QJSValue::setProperty() |
642 | */ |
643 | QJSValue QJSEngine::newObject() |
644 | { |
645 | QV4::Scope scope(m_v4Engine); |
646 | QV4::ScopedValue v(scope, m_v4Engine->newObject()); |
647 | return QJSValuePrivate::fromReturnedValue(d: v->asReturnedValue()); |
648 | } |
649 | |
650 | /*! |
651 | \since 6.2 |
652 | |
653 | Creates a JavaScript object of class Symbol, with value \a name. |
654 | |
655 | The prototype of the created object will be the Symbol prototype object. |
656 | |
657 | \sa newObject() |
658 | */ |
659 | QJSValue QJSEngine::newSymbol(const QString &name) |
660 | { |
661 | QV4::Scope scope(m_v4Engine); |
662 | QV4::ScopedValue v(scope, QV4::Symbol::create(e: m_v4Engine, s: u'@' + name)); |
663 | return QJSValuePrivate::fromReturnedValue(d: v->asReturnedValue()); |
664 | } |
665 | |
666 | /*! |
667 | \since 5.12 |
668 | |
669 | Creates a JavaScript object of class Error, with \a message as the error |
670 | message. |
671 | |
672 | The prototype of the created object will be \a errorType. |
673 | |
674 | \sa newObject(), throwError(), QJSValue::isError() |
675 | */ |
676 | QJSValue QJSEngine::newErrorObject(QJSValue::ErrorType errorType, const QString &message) |
677 | { |
678 | QV4::Scope scope(m_v4Engine); |
679 | QV4::ScopedObject error(scope); |
680 | switch (errorType) { |
681 | case QJSValue::RangeError: |
682 | error = m_v4Engine->newRangeErrorObject(message); |
683 | break; |
684 | case QJSValue::SyntaxError: |
685 | error = m_v4Engine->newSyntaxErrorObject(message); |
686 | break; |
687 | case QJSValue::TypeError: |
688 | error = m_v4Engine->newTypeErrorObject(message); |
689 | break; |
690 | case QJSValue::URIError: |
691 | error = m_v4Engine->newURIErrorObject(message); |
692 | break; |
693 | case QJSValue::ReferenceError: |
694 | error = m_v4Engine->newReferenceErrorObject(message); |
695 | break; |
696 | case QJSValue::EvalError: |
697 | error = m_v4Engine->newEvalErrorObject(message); |
698 | break; |
699 | case QJSValue::GenericError: |
700 | error = m_v4Engine->newErrorObject(message); |
701 | break; |
702 | case QJSValue::NoError: |
703 | return QJSValue::UndefinedValue; |
704 | } |
705 | return QJSValuePrivate::fromReturnedValue(d: error->asReturnedValue()); |
706 | } |
707 | |
708 | /*! |
709 | Creates a JavaScript object of class Array with the given \a length. |
710 | |
711 | \sa newObject() |
712 | */ |
713 | QJSValue QJSEngine::newArray(uint length) |
714 | { |
715 | QV4::Scope scope(m_v4Engine); |
716 | QV4::ScopedArrayObject array(scope, m_v4Engine->newArrayObject()); |
717 | if (length < 0x1000) |
718 | array->arrayReserve(n: length); |
719 | array->setArrayLengthUnchecked(length); |
720 | return QJSValuePrivate::fromReturnedValue(d: array.asReturnedValue()); |
721 | } |
722 | |
723 | /*! |
724 | Creates a JavaScript object that wraps the given QObject \a |
725 | object, using JavaScriptOwnership. |
726 | |
727 | Signals and slots, properties and children of \a object are |
728 | available as properties of the created QJSValue. |
729 | |
730 | If \a object is a null pointer, this function returns a null value. |
731 | |
732 | If a default prototype has been registered for the \a object's class |
733 | (or its superclass, recursively), the prototype of the new script |
734 | object will be set to be that default prototype. |
735 | |
736 | If the given \a object is deleted outside of the engine's control, any |
737 | attempt to access the deleted QObject's members through the JavaScript |
738 | wrapper object (either by script code or C++) will result in a |
739 | \l{Script Exceptions}{script exception}. |
740 | |
741 | \sa QJSValue::toQObject() |
742 | */ |
743 | QJSValue QJSEngine::newQObject(QObject *object) |
744 | { |
745 | QV4::ExecutionEngine *v4 = m_v4Engine; |
746 | QV4::Scope scope(v4); |
747 | if (object) { |
748 | QQmlData *ddata = QQmlData::get(object, create: true); |
749 | if (!ddata || !ddata->explicitIndestructibleSet) |
750 | QQmlEngine::setObjectOwnership(object, QQmlEngine::JavaScriptOwnership); |
751 | } |
752 | QV4::ScopedValue v(scope, QV4::QObjectWrapper::wrap(engine: v4, object)); |
753 | return QJSValuePrivate::fromReturnedValue(d: v->asReturnedValue()); |
754 | } |
755 | |
756 | /*! |
757 | \since 5.8 |
758 | |
759 | Creates a JavaScript object that wraps the given QMetaObject |
760 | The \a metaObject must outlive the script engine. It is recommended to only |
761 | use this method with static metaobjects. |
762 | |
763 | |
764 | When called as a constructor, a new instance of the class will be created. |
765 | Only constructors exposed by Q_INVOKABLE will be visible from the script engine. |
766 | |
767 | \sa newQObject(), {QObject Integration} |
768 | */ |
769 | |
770 | QJSValue QJSEngine::newQMetaObject(const QMetaObject* metaObject) { |
771 | QV4::ExecutionEngine *v4 = m_v4Engine; |
772 | QV4::Scope scope(v4); |
773 | QV4::ScopedValue v(scope, QV4::QMetaObjectWrapper::create(engine: v4, metaObject)); |
774 | return QJSValuePrivate::fromReturnedValue(d: v->asReturnedValue()); |
775 | } |
776 | |
777 | /*! \fn template <typename T> QJSValue QJSEngine::newQMetaObject() |
778 | |
779 | \since 5.8 |
780 | Creates a JavaScript object that wraps the static QMetaObject associated |
781 | with class \c{T}. |
782 | |
783 | \sa newQObject(), {QObject Integration} |
784 | */ |
785 | |
786 | |
787 | /*! |
788 | Returns this engine's Global Object. |
789 | |
790 | By default, the Global Object contains the built-in objects that are |
791 | part of \l{ECMA-262}, such as Math, Date and String. Additionally, |
792 | you can set properties of the Global Object to make your own |
793 | extensions available to all script code. Non-local variables in |
794 | script code will be created as properties of the Global Object, as |
795 | well as local variables in global code. |
796 | */ |
797 | QJSValue QJSEngine::globalObject() const |
798 | { |
799 | QV4::Scope scope(m_v4Engine); |
800 | QV4::ScopedValue v(scope, m_v4Engine->globalObject); |
801 | return QJSValuePrivate::fromReturnedValue(d: v->asReturnedValue()); |
802 | } |
803 | |
804 | QJSPrimitiveValue QJSEngine::createPrimitive(QMetaType type, const void *ptr) |
805 | { |
806 | QV4::Scope scope(m_v4Engine); |
807 | QV4::ScopedValue v(scope, m_v4Engine->metaTypeToJS(type, data: ptr)); |
808 | return QV4::ExecutionEngine::createPrimitive(v); |
809 | } |
810 | |
811 | QJSManagedValue QJSEngine::createManaged(QMetaType type, const void *ptr) |
812 | { |
813 | QJSManagedValue result(m_v4Engine); |
814 | *result.d = m_v4Engine->metaTypeToJS(type, data: ptr); |
815 | return result; |
816 | } |
817 | |
818 | /*! |
819 | * \internal |
820 | * used by QJSEngine::toScriptValue |
821 | */ |
822 | QJSValue QJSEngine::create(QMetaType type, const void *ptr) |
823 | { |
824 | QV4::Scope scope(m_v4Engine); |
825 | QV4::ScopedValue v(scope, scope.engine->metaTypeToJS(type, data: ptr)); |
826 | return QJSValuePrivate::fromReturnedValue(d: v->asReturnedValue()); |
827 | } |
828 | |
829 | bool QJSEngine::convertPrimitive(const QJSPrimitiveValue &value, QMetaType type, void *ptr) |
830 | { |
831 | switch (value.type()) { |
832 | case QJSPrimitiveValue::Undefined: |
833 | return QV4::ExecutionEngine::metaTypeFromJS(value: QV4::Value::undefinedValue(), type, data: ptr); |
834 | case QJSPrimitiveValue::Null: |
835 | return QV4::ExecutionEngine::metaTypeFromJS(value: QV4::Value::nullValue(), type, data: ptr); |
836 | case QJSPrimitiveValue::Boolean: |
837 | return QV4::ExecutionEngine::metaTypeFromJS(value: QV4::Value::fromBoolean(b: value.toBoolean()), type, data: ptr); |
838 | case QJSPrimitiveValue::Integer: |
839 | return QV4::ExecutionEngine::metaTypeFromJS(value: QV4::Value::fromInt32(i: value.toInteger()), type, data: ptr); |
840 | case QJSPrimitiveValue::Double: |
841 | return QV4::ExecutionEngine::metaTypeFromJS(value: QV4::Value::fromDouble(d: value.toDouble()), type, data: ptr); |
842 | case QJSPrimitiveValue::String: |
843 | return convertString(string: value.toString(), metaType: type, ptr); |
844 | } |
845 | |
846 | Q_UNREACHABLE_RETURN(false); |
847 | } |
848 | |
849 | bool QJSEngine::convertManaged(const QJSManagedValue &value, int type, void *ptr) |
850 | { |
851 | return convertManaged(value, type: QMetaType(type), ptr); |
852 | } |
853 | |
854 | bool QJSEngine::convertManaged(const QJSManagedValue &value, QMetaType type, void *ptr) |
855 | { |
856 | return QV4::ExecutionEngine::metaTypeFromJS(value: *value.d, type, data: ptr); |
857 | } |
858 | |
859 | bool QJSEngine::convertString(const QString &string, QMetaType metaType, void *ptr) |
860 | { |
861 | // have a string based value without engine. Do conversion manually |
862 | if (metaType == QMetaType::fromType<bool>()) { |
863 | *reinterpret_cast<bool*>(ptr) = string.size() != 0; |
864 | return true; |
865 | } |
866 | if (metaType == QMetaType::fromType<QString>()) { |
867 | *reinterpret_cast<QString*>(ptr) = string; |
868 | return true; |
869 | } |
870 | if (metaType == QMetaType::fromType<QUrl>()) { |
871 | *reinterpret_cast<QUrl *>(ptr) = QUrl(string); |
872 | return true; |
873 | } |
874 | |
875 | double d = QV4::RuntimeHelpers::stringToNumber(s: string); |
876 | switch (metaType.id()) { |
877 | case QMetaType::Int: |
878 | *reinterpret_cast<int*>(ptr) = QV4::Value::toInt32(d); |
879 | return true; |
880 | case QMetaType::UInt: |
881 | *reinterpret_cast<uint*>(ptr) = QV4::Value::toUInt32(d); |
882 | return true; |
883 | case QMetaType::Long: |
884 | *reinterpret_cast<long*>(ptr) = QV4::Value::toInteger(d); |
885 | return true; |
886 | case QMetaType::ULong: |
887 | *reinterpret_cast<ulong*>(ptr) = QV4::Value::toInteger(d); |
888 | return true; |
889 | case QMetaType::LongLong: |
890 | *reinterpret_cast<qlonglong*>(ptr) = QV4::Value::toInteger(d); |
891 | return true; |
892 | case QMetaType::ULongLong: |
893 | *reinterpret_cast<qulonglong*>(ptr) = QV4::Value::toInteger(d); |
894 | return true; |
895 | case QMetaType::Double: |
896 | *reinterpret_cast<double*>(ptr) = d; |
897 | return true; |
898 | case QMetaType::Float: |
899 | *reinterpret_cast<float*>(ptr) = d; |
900 | return true; |
901 | case QMetaType::Short: |
902 | *reinterpret_cast<short*>(ptr) = QV4::Value::toInt32(d); |
903 | return true; |
904 | case QMetaType::UShort: |
905 | *reinterpret_cast<unsigned short*>(ptr) = QV4::Value::toUInt32(d); |
906 | return true; |
907 | case QMetaType::Char: |
908 | *reinterpret_cast<char*>(ptr) = QV4::Value::toInt32(d); |
909 | return true; |
910 | case QMetaType::UChar: |
911 | *reinterpret_cast<unsigned char*>(ptr) = QV4::Value::toUInt32(d); |
912 | return true; |
913 | case QMetaType::QChar: |
914 | *reinterpret_cast<QChar*>(ptr) = QChar(QV4::Value::toUInt32(d)); |
915 | return true; |
916 | case QMetaType::Char16: |
917 | *reinterpret_cast<char16_t *>(ptr) = QV4::Value::toUInt32(d); |
918 | return true; |
919 | default: |
920 | return false; |
921 | } |
922 | } |
923 | |
924 | /*! |
925 | \internal |
926 | convert \a value to \a type, store the result in \a ptr |
927 | */ |
928 | bool QJSEngine::convertV2(const QJSValue &value, QMetaType metaType, void *ptr) |
929 | { |
930 | if (const QString *string = QJSValuePrivate::asQString(jsval: &value)) |
931 | return convertString(string: *string, metaType, ptr); |
932 | |
933 | // Does not need scoping since QJSValue still holds on to the value. |
934 | return QV4::ExecutionEngine::metaTypeFromJS(value: QJSValuePrivate::asReturnedValue(jsval: &value), type: metaType, data: ptr); |
935 | } |
936 | |
937 | bool QJSEngine::convertVariant(const QVariant &value, QMetaType metaType, void *ptr) |
938 | { |
939 | // TODO: We could probably avoid creating a QV4::Value in many cases, but we'd have to |
940 | // duplicate much of metaTypeFromJS and some methods of QV4::Value itself here. |
941 | QV4::Scope scope(handle()); |
942 | QV4::ScopedValue scoped(scope, scope.engine->fromVariant(value)); |
943 | return QV4::ExecutionEngine::metaTypeFromJS(value: scoped, type: metaType, data: ptr); |
944 | } |
945 | |
946 | bool QJSEngine::convertMetaType(QMetaType fromType, const void *from, QMetaType toType, void *to) |
947 | { |
948 | // TODO: We could probably avoid creating a QV4::Value in many cases, but we'd have to |
949 | // duplicate much of metaTypeFromJS and some methods of QV4::Value itself here. |
950 | QV4::Scope scope(handle()); |
951 | QV4::ScopedValue scoped(scope, scope.engine->fromData(type: fromType, ptr: from)); |
952 | return QV4::ExecutionEngine::metaTypeFromJS(value: scoped, type: toType, data: to); |
953 | } |
954 | |
955 | QString QJSEngine::convertQObjectToString(QObject *object) |
956 | { |
957 | return QV4::QObjectWrapper::objectToString( |
958 | engine: handle(), metaObject: object ? object->metaObject() : nullptr, object); |
959 | } |
960 | |
961 | QString QJSEngine::convertDateTimeToString(const QDateTime &dateTime) |
962 | { |
963 | return QV4::DateObject::dateTimeToString(dateTime, engine: handle()); |
964 | } |
965 | |
966 | double QJSEngine::convertDateTimeToNumber(const QDateTime &dateTime) |
967 | { |
968 | return QV4::DateObject::dateTimeToNumber(dateTime); |
969 | } |
970 | |
971 | QDate QJSEngine::convertDateTimeToDate(const QDateTime &dateTime) |
972 | { |
973 | return QV4::DateObject::dateTimeToDate(dateTime); |
974 | } |
975 | |
976 | /*! \fn template <typename T> QJSValue QJSEngine::toScriptValue(const T &value) |
977 | |
978 | Creates a QJSValue with the given \a value. |
979 | |
980 | \sa fromScriptValue(), coerceValue() |
981 | */ |
982 | |
983 | /*! \fn template <typename T> QJSManagedValue QJSEngine::toManagedValue(const T &value) |
984 | |
985 | Creates a QJSManagedValue with the given \a value. |
986 | |
987 | \sa fromManagedValue(), coerceValue() |
988 | */ |
989 | |
990 | /*! \fn template <typename T> QJSPrimitiveValue QJSEngine::toPrimitiveValue(const T &value) |
991 | |
992 | Creates a QJSPrimitiveValue with the given \a value. |
993 | |
994 | Since QJSPrimitiveValue can only hold int, bool, double, QString, and the |
995 | equivalents of JavaScript \c null and \c undefined, the value will be |
996 | coerced aggressively if you pass any other type. |
997 | |
998 | \sa fromPrimitiveValue(), coerceValue() |
999 | */ |
1000 | |
1001 | /*! \fn template <typename T> T QJSEngine::fromScriptValue(const QJSValue &value) |
1002 | |
1003 | Returns the given \a value converted to the template type \c{T}. |
1004 | |
1005 | \sa toScriptValue(), coerceValue() |
1006 | */ |
1007 | |
1008 | /*! \fn template <typename T> T QJSEngine::fromManagedValue(const QJSManagedValue &value) |
1009 | |
1010 | Returns the given \a value converted to the template type \c{T}. |
1011 | |
1012 | \sa toManagedValue(), coerceValue() |
1013 | */ |
1014 | |
1015 | /*! \fn template <typename T> T QJSEngine::fromPrimitiveValue(const QJSPrimitiveValue &value) |
1016 | |
1017 | Returns the given \a value converted to the template type \c{T}. |
1018 | |
1019 | Since QJSPrimitiveValue can only hold int, bool, double, QString, and the |
1020 | equivalents of JavaScript \c null and \c undefined, the value will be |
1021 | coerced aggressively if you request any other type. |
1022 | |
1023 | \sa toPrimitiveValue(), coerceValue() |
1024 | */ |
1025 | |
1026 | /*! \fn template <typename T> T QJSEngine::fromVariant(const QVariant &value) |
1027 | |
1028 | Returns the given \a value converted to the template type \c{T}. |
1029 | The conversion is done in JavaScript semantics. Those differ from |
1030 | qvariant_cast's semantics. There are a number of implicit |
1031 | conversions between JavaScript-equivalent types that are not |
1032 | performed by qvariant_cast by default. |
1033 | |
1034 | \sa coerceValue(), fromScriptValue(), {QVariant::}{qvariant_cast()} |
1035 | */ |
1036 | |
1037 | /*! \fn template <typename From, typename To> T QJSEngine::coerceValue(const From &from) |
1038 | |
1039 | Returns the given \a from converted to the template type \c{To}. |
1040 | The conversion is done in JavaScript semantics. Those differ from |
1041 | qvariant_cast's semantics. There are a number of implicit |
1042 | conversions between JavaScript-equivalent types that are not |
1043 | performed by qvariant_cast by default. This method is a generalization of |
1044 | all the other conversion methods in this class. |
1045 | |
1046 | \sa fromVariant(), {QVariant::}{qvariant_cast()}, fromScriptValue(), toScriptValue() |
1047 | */ |
1048 | |
1049 | /*! |
1050 | Throws a run-time error (exception) with the given \a message. |
1051 | |
1052 | This method is the C++ counterpart of a \c throw() expression in |
1053 | JavaScript. It enables C++ code to report run-time errors to QJSEngine. |
1054 | Therefore it should only be called from C++ code that was invoked by a |
1055 | JavaScript function through QJSEngine. |
1056 | |
1057 | When returning from C++, the engine will interrupt the normal flow of |
1058 | execution and call the next pre-registered exception handler with |
1059 | an error object that contains the given \a message. The error object |
1060 | will point to the location of the top-most context on the JavaScript |
1061 | caller stack; specifically, it will have properties \c lineNumber, |
1062 | \c fileName and \c stack. These properties are described in |
1063 | \l{Script Exceptions}. |
1064 | |
1065 | In the following example a C++ method in \e FileAccess.cpp throws an error |
1066 | in \e qmlFile.qml at the position where \c readFileAsText() is called: |
1067 | |
1068 | \code |
1069 | // qmlFile.qml |
1070 | function someFunction() { |
1071 | ... |
1072 | var text = FileAccess.readFileAsText("/path/to/file.txt"); |
1073 | } |
1074 | \endcode |
1075 | |
1076 | \code |
1077 | // FileAccess.cpp |
1078 | // Assuming that FileAccess is a QObject-derived class that has been |
1079 | // registered as a singleton type and provides an invokable method |
1080 | // readFileAsText() |
1081 | |
1082 | QJSValue FileAccess::readFileAsText(const QString & filePath) { |
1083 | QFile file(filePath); |
1084 | |
1085 | if (!file.open(QIODevice::ReadOnly)) { |
1086 | jsEngine->throwError(file.errorString()); |
1087 | return QString(); |
1088 | } |
1089 | |
1090 | ... |
1091 | return content; |
1092 | } |
1093 | \endcode |
1094 | |
1095 | It is also possible to catch the thrown error in JavaScript: |
1096 | \code |
1097 | // qmlFile.qml |
1098 | function someFunction() { |
1099 | ... |
1100 | var text; |
1101 | try { |
1102 | text = FileAccess.readFileAsText("/path/to/file.txt"); |
1103 | } catch (error) { |
1104 | console.warn("In " + error.fileName + ":" + "error.lineNumber" + |
1105 | ": " + error.message); |
1106 | } |
1107 | } |
1108 | \endcode |
1109 | |
1110 | If you need a more specific run-time error to describe an exception, you can use the |
1111 | \l {QJSEngine::}{throwError(QJSValue::ErrorType errorType, const QString &message)} |
1112 | overload. |
1113 | |
1114 | \since Qt 5.12 |
1115 | \sa {Script Exceptions} |
1116 | */ |
1117 | void QJSEngine::throwError(const QString &message) |
1118 | { |
1119 | m_v4Engine->throwError(message); |
1120 | } |
1121 | |
1122 | /*! |
1123 | \overload throwError() |
1124 | |
1125 | Throws a run-time error (exception) with the given \a errorType and |
1126 | \a message. |
1127 | |
1128 | \code |
1129 | // Assuming that DataEntry is a QObject-derived class that has been |
1130 | // registered as a singleton type and provides an invokable method |
1131 | // setAge(). |
1132 | |
1133 | void DataEntry::setAge(int age) { |
1134 | if (age < 0 || age > 200) { |
1135 | jsEngine->throwError(QJSValue::RangeError, |
1136 | "Age must be between 0 and 200"); |
1137 | } |
1138 | ... |
1139 | } |
1140 | \endcode |
1141 | |
1142 | \since Qt 5.12 |
1143 | \sa {Script Exceptions}, newErrorObject() |
1144 | */ |
1145 | void QJSEngine::throwError(QJSValue::ErrorType errorType, const QString &message) |
1146 | { |
1147 | QV4::Scope scope(m_v4Engine); |
1148 | QJSValue error = newErrorObject(errorType, message); |
1149 | QV4::ScopedObject e(scope, QJSValuePrivate::asReturnedValue(jsval: &error)); |
1150 | if (!e) |
1151 | return; |
1152 | m_v4Engine->throwError(value: e); |
1153 | } |
1154 | |
1155 | /*! |
1156 | \overload throwError() |
1157 | |
1158 | Throws a pre-constructed run-time \a error (exception). This way you can |
1159 | use \l newErrorObject() to create the error and customize it as necessary. |
1160 | |
1161 | \since 6.1 |
1162 | \sa {Script Exceptions}, newErrorObject() |
1163 | */ |
1164 | void QJSEngine::throwError(const QJSValue &error) |
1165 | { |
1166 | m_v4Engine->throwError(value: QJSValuePrivate::asReturnedValue(jsval: &error)); |
1167 | } |
1168 | |
1169 | /*! |
1170 | * Returns \c true if the last JavaScript execution resulted in an exception or |
1171 | * if throwError() was called. Otherwise returns \c false. Mind that evaluate() |
1172 | * catches any exceptions thrown in the evaluated code. |
1173 | * |
1174 | * \since Qt 6.1 |
1175 | */ |
1176 | bool QJSEngine::hasError() const |
1177 | { |
1178 | return m_v4Engine->hasException; |
1179 | } |
1180 | |
1181 | /*! |
1182 | * If an exception is currently pending, catches it and returns it as a |
1183 | * QJSValue. Otherwise returns undefined as QJSValue. After calling this method |
1184 | * hasError() returns \c false. |
1185 | * |
1186 | * \since Qt 6.1 |
1187 | */ |
1188 | QJSValue QJSEngine::catchError() |
1189 | { |
1190 | if (m_v4Engine->hasException) |
1191 | return QJSValuePrivate::fromReturnedValue(d: m_v4Engine->catchException()); |
1192 | else |
1193 | return QJSValue(); |
1194 | } |
1195 | |
1196 | /*! |
1197 | \property QJSEngine::uiLanguage |
1198 | \brief the language to be used for translating user interface strings |
1199 | \since 5.15 |
1200 | |
1201 | This property holds the name of the language to be used for user interface |
1202 | string translations. It is exposed for reading and writing as \c{Qt.uiLanguage} when |
1203 | the QJSEngine::TranslationExtension is installed on the engine. It is always exposed |
1204 | in instances of QQmlEngine. |
1205 | |
1206 | You can set the value freely and use it in bindings. It is recommended to set it |
1207 | after installing translators in your application. By convention, an empty string |
1208 | means no translation from the language used in the source code is intended to occur. |
1209 | */ |
1210 | void QJSEngine::setUiLanguage(const QString &language) { |
1211 | Q_D(QJSEngine); |
1212 | d->uiLanguage = language; // property takes care of signal emission if necessary |
1213 | } |
1214 | |
1215 | QString QJSEngine::uiLanguage() const |
1216 | { |
1217 | Q_D(const QJSEngine); |
1218 | return d->uiLanguage; |
1219 | } |
1220 | |
1221 | QJSEnginePrivate *QJSEnginePrivate::get(QV4::ExecutionEngine *e) |
1222 | { |
1223 | return e->jsEngine()->d_func(); |
1224 | } |
1225 | |
1226 | QJSEnginePrivate::~QJSEnginePrivate() |
1227 | { |
1228 | QQmlMetaType::freeUnusedTypesAndCaches(); |
1229 | } |
1230 | |
1231 | void QJSEnginePrivate::addToDebugServer(QJSEngine *q) |
1232 | { |
1233 | if (QCoreApplication::instance()->thread() != q->thread()) |
1234 | return; |
1235 | |
1236 | QQmlDebugConnector *server = QQmlDebugConnector::instance(); |
1237 | if (!server || server->hasEngine(engine: q)) |
1238 | return; |
1239 | |
1240 | server->open(); |
1241 | server->addEngine(engine: q); |
1242 | } |
1243 | |
1244 | void QJSEnginePrivate::removeFromDebugServer(QJSEngine *q) |
1245 | { |
1246 | QQmlDebugConnector *server = QQmlDebugConnector::instance(); |
1247 | if (server && server->hasEngine(engine: q)) |
1248 | server->removeEngine(engine: q); |
1249 | } |
1250 | |
1251 | /*! |
1252 | \since 5.5 |
1253 | \relates QJSEngine |
1254 | |
1255 | Returns the QJSEngine associated with \a object, if any. |
1256 | |
1257 | This function is useful if you have exposed a QObject to the JavaScript environment |
1258 | and later in your program would like to regain access. It does not require you to |
1259 | keep the wrapper around that was returned from QJSEngine::newQObject(). |
1260 | */ |
1261 | QJSEngine *qjsEngine(const QObject *object) |
1262 | { |
1263 | QQmlData *data = QQmlData::get(object); |
1264 | if (!data || data->jsWrapper.isNullOrUndefined()) |
1265 | return nullptr; |
1266 | return data->jsWrapper.engine()->jsEngine(); |
1267 | } |
1268 | |
1269 | |
1270 | /*! |
1271 | \enum QJSEngine::ObjectOwnership |
1272 | |
1273 | ObjectOwnership controls whether or not the JavaScript memory manager automatically destroys the |
1274 | QObject when the corresponding JavaScript object is garbage collected by the |
1275 | engine. The two ownership options are: |
1276 | |
1277 | \value CppOwnership The object is owned by C++ code and the JavaScript memory manager will never |
1278 | delete it. The JavaScript destroy() method cannot be used on these objects. This |
1279 | option is similar to QScriptEngine::QtOwnership. |
1280 | |
1281 | \value JavaScriptOwnership The object is owned by JavaScript. When the object |
1282 | is returned to the JavaScript memory manager as the return value of a method call, the JavaScript |
1283 | memory manager will track it and delete it if there are no remaining JavaScript references to it |
1284 | and it has no QObject::parent(). An object tracked by one QJSEngine will be deleted during that |
1285 | QJSEngine's destructor. Thus, JavaScript references between objects with JavaScriptOwnership from |
1286 | two different engines will not be valid if one of these engines is deleted. This option is similar |
1287 | to QScriptEngine::ScriptOwnership. |
1288 | |
1289 | Generally an application doesn't need to set an object's ownership explicitly. The JavaScript |
1290 | memory manager uses a heuristic to set the default ownership. By default, an object that is |
1291 | created by the JavaScript memory manager has JavaScriptOwnership. The exception to this are the |
1292 | root objects created by calling QQmlComponent::create() or QQmlComponent::beginCreate(), which |
1293 | have CppOwnership by default. The ownership of these root-level objects is considered to have been |
1294 | transferred to the C++ caller. |
1295 | |
1296 | Objects not-created by the JavaScript memory manager have CppOwnership by default. The exception |
1297 | to this are objects returned from C++ method calls; their ownership will be set to |
1298 | JavaScriptOwnership. This applies only to explicit invocations of Q_INVOKABLE methods or slots, |
1299 | but not to property getter invocations. |
1300 | |
1301 | Calling setObjectOwnership() overrides the default ownership. |
1302 | |
1303 | \sa {Data Ownership} |
1304 | */ |
1305 | |
1306 | /*! |
1307 | Sets the \a ownership of \a object. |
1308 | |
1309 | An object with \c JavaScriptOwnership is not garbage collected as long |
1310 | as it still has a parent, even if there are no references to it. |
1311 | |
1312 | \sa QJSEngine::ObjectOwnership |
1313 | */ |
1314 | void QJSEngine::setObjectOwnership(QObject *object, ObjectOwnership ownership) |
1315 | { |
1316 | if (!object) |
1317 | return; |
1318 | |
1319 | QQmlData *ddata = QQmlData::get(object, create: true); |
1320 | if (!ddata) |
1321 | return; |
1322 | |
1323 | ddata->indestructible = (ownership == CppOwnership)?true:false; |
1324 | ddata->explicitIndestructibleSet = true; |
1325 | } |
1326 | |
1327 | /*! |
1328 | Returns the ownership of \a object. |
1329 | |
1330 | \sa QJSEngine::ObjectOwnership |
1331 | */ |
1332 | QJSEngine::ObjectOwnership QJSEngine::objectOwnership(QObject *object) |
1333 | { |
1334 | if (!object) |
1335 | return CppOwnership; |
1336 | |
1337 | QQmlData *ddata = QQmlData::get(object, create: false); |
1338 | if (!ddata) |
1339 | return CppOwnership; |
1340 | else |
1341 | return ddata->indestructible?CppOwnership:JavaScriptOwnership; |
1342 | } |
1343 | |
1344 | QT_END_NAMESPACE |
1345 | |
1346 | #include "moc_qjsengine.cpp" |
1347 |
Definitions
- checkForApplicationInstance
- QJSEngine
- QJSEngine
- QJSEngine
- ~QJSEngine
- collectGarbage
- installExtensions
- setInterrupted
- isInterrupted
- urlForFileName
- evaluate
- importModule
- registerModule
- newObject
- newSymbol
- newErrorObject
- newArray
- newQObject
- newQMetaObject
- globalObject
- createPrimitive
- createManaged
- create
- convertPrimitive
- convertManaged
- convertManaged
- convertString
- convertV2
- convertVariant
- convertMetaType
- convertQObjectToString
- convertDateTimeToString
- convertDateTimeToNumber
- convertDateTimeToDate
- throwError
- throwError
- throwError
- hasError
- catchError
- setUiLanguage
- uiLanguage
- get
- ~QJSEnginePrivate
- addToDebugServer
- removeFromDebugServer
- qjsEngine
- setObjectOwnership
Start learning QML with our Intro Training
Find out more