Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qjsmanagedvalue.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 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// Qt-Security score:significant
4
5#include <QtQml/qjsmanagedvalue.h>
6#include <QtQml/qjsengine.h>
7#include <QtQml/private/qv4persistent_p.h>
8#include <QtQml/private/qv4engine_p.h>
9#include <QtQml/private/qv4mm_p.h>
10#include <QtQml/private/qjsvalue_p.h>
11#include <QtQml/private/qv4runtime_p.h>
12#include <QtQml/private/qv4functionobject_p.h>
13#include <QtQml/private/qv4jscall_p.h>
14#include <QtQml/private/qv4urlobject_p.h>
15#include <QtQml/private/qv4variantobject_p.h>
16#include <QtQml/private/qv4qobjectwrapper_p.h>
17#include <QtQml/private/qv4qmetaobjectwrapper_p.h>
18#include <QtQml/private/qv4regexpobject_p.h>
19#include <QtQml/private/qv4dateobject_p.h>
20#include <QtQml/private/qv4errorobject_p.h>
21#include <QtQml/private/qv4identifiertable_p.h>
22
23#include <QtCore/qregularexpression.h>
24#include <QtCore/qurl.h>
25#include <QtCore/qdatetime.h>
26
27QT_BEGIN_NAMESPACE
28
29/*!
30 * \class QJSManagedValue
31 * \inmodule QtQml
32 * \since 6.1
33 *
34 * \inmodule QtQml
35 *
36 * \brief QJSManagedValue represents a value on the JavaScript heap belonging to a QJSEngine.
37 *
38 * The QJSManagedValue class allows interaction with JavaScript values in most
39 * ways you can interact with them from JavaScript itself. You can get and set
40 * properties and prototypes, and you can access arrays. Additionally, you can
41 * transform the value into the Qt counterparts of JavaScript objects. For
42 * example, a Url object may be transformed into a QUrl.
43 *
44 * A QJSManagedValue is always bound to a particular QJSEngine. You cannot use
45 * it independently. This means that you cannot have a QJSManagedValue from one
46 * engine be a property or a proptotype of a QJSManagedValue from a different
47 * engine.
48 *
49 * In contrast to QJSValue, almost all values held by QJSManagedValue live on
50 * the JavaScript heap. There is no inline or unmanaged storage. Therefore, you
51 * can get the prototype of a primitive value, and you can get the \c length
52 * property of a string.
53 *
54 * Only default-constructed or moved-from QJSManagedValues do not hold a value
55 * on the JavaScript heap. They represent \c undefined, which doesn't have any
56 * properties or prototypes.
57 *
58 * Also in contrast to QJSValue, QJSManagedValue does not catch any JavaScript
59 * exceptions. If an operation on a QJSManagedValue causes an error, it will
60 * generally return an \c undefined value and QJSEngine::hasError() will return
61 * \c true afterwards. You can then catch the exception using
62 * QJSEngine::catchError(), or pass it up the stack, at your own discretion.
63 *
64 * \note As the reference to the value on the JavaScript heap has to be freed
65 * on destruction, you cannot move a QJSManagedValue to a different thread.
66 * The destruction would take place in the new thread, which would create a race
67 * condition with the garbage collector on the original thread. This also means
68 * that you cannot hold a QJSManagedValue beyond the lifespan of its engine.
69 *
70 * The recommended way of working with a QJSManagedValue is creating it
71 * on the stack, possibly by moving a QJSValue and adding an engine, then
72 * performing the necessary operations on it, and finally moving it back into a
73 * QJSValue for storage. Moving between QJSManagedValue and QJSValue is fast.
74 */
75
76/*!
77 * \enum QJSManagedValue::Type
78 *
79 * This enum represents the JavaScript native types, as specified by
80 * \l{ECMA-262}.
81 *
82 * \value Undefined The \c undefined type
83 * \value Boolean The \c boolean type
84 * \value Number The \c number type
85 * \value String The \c string type
86 * \value Object The \c object type
87 * \value Symbol The \c symbol type
88 * \value Function The \c function type
89 *
90 * Note that the \c null value is not a type of itself but rather a special kind
91 * of object. You can query a QJSManagedValue for this condition using the
92 * isNull() method. Furthermore, JavaScript has no integer type, but it knows a
93 * special treatment of numbers in preparation for integer only operations. You
94 * can query a QJSManagedValue to find out whether it holds the result of such a
95 * treatment by using the isInteger() method.
96 */
97
98/*!
99 * \fn QJSManagedValue::QJSManagedValue()
100 *
101 * Creates a QJSManagedValue that represents the JavaScript \c undefined value.
102 * This is the only value not stored on the JavaScript heap. Calling engine()
103 * on a default-constructed QJSManagedValue will return nullptr.
104 */
105
106static QV4::ExecutionEngine *v4Engine(QV4::Value *d)
107{
108 if (!d)
109 return nullptr;
110
111 QV4::ExecutionEngine *v4 = QV4::PersistentValueStorage::getEngine(d);
112 Q_ASSERT(v4);
113 return v4;
114}
115
116/*!
117 * Creates a QJSManagedValue from \a value, using the heap of \a engine. If
118 * \a value is itself managed and the engine it belongs to is not \a engine,
119 * the result is an \c undefined value, and a warning is generated.
120 */
121QJSManagedValue::QJSManagedValue(QJSValue value, QJSEngine *engine)
122{
123 QV4::ExecutionEngine *v4 = engine->handle();
124
125 if (QV4::Value *m = QJSValuePrivate::takeManagedValue(&value)) {
126 if (Q_UNLIKELY(v4Engine(m) != v4)) {
127 qWarning("QJSManagedValue(QJSValue, QJSEngine *) failed: "
128 "Value was created in different engine.");
129 QV4::PersistentValueStorage::free(m);
130 return;
131 }
132
133 d = m;
134 return;
135 }
136
137 d = v4->memoryManager->m_persistentValues->allocate();
138
139 if (const QString *string = QJSValuePrivate::asQString(&value))
140 *d = v4->newString(*string);
141 else
142 *d = QJSValuePrivate::asReturnedValue(&value);
143}
144
145/*!
146 * Creates a QJSManagedValue from \a value using the heap of \a engine.
147 */
148QJSManagedValue::QJSManagedValue(const QJSPrimitiveValue &value, QJSEngine *engine) :
149 QJSManagedValue(engine->handle())
150{
151 switch (value.type()) {
152 case QJSPrimitiveValue::Undefined:
153 *d = QV4::Encode::undefined();
154 return;
155 case QJSPrimitiveValue::Null:
156 *d = QV4::Encode::null();
157 return;
158 case QJSPrimitiveValue::Boolean:
159 *d = QV4::Encode(value.asBoolean());
160 return;
161 case QJSPrimitiveValue::Integer:
162 *d = QV4::Encode(value.asInteger());
163 return;
164 case QJSPrimitiveValue::Double:
165 *d = QV4::Encode(value.asDouble());
166 return;
167 case QJSPrimitiveValue::String:
168 *d = engine->handle()->newString(value.asString());
169 return;
170 }
171
172 Q_UNREACHABLE();
173}
174
175/*!
176 * Creates a QJSManagedValue from \a variant using the heap of \a engine.
177 */
178QJSManagedValue::QJSManagedValue(const QVariant &variant, QJSEngine *engine) :
179 QJSManagedValue(engine->handle())
180{
181 *d = engine->handle()->fromVariant(variant);
182}
183
184/*!
185 * Creates a QJSManagedValue from \a string using the heap of \a engine.
186 */
187QJSManagedValue::QJSManagedValue(const QString &string, QJSEngine *engine) :
188 QJSManagedValue(engine->handle())
189{
190 *d = engine->handle()->newString(string);
191}
192
193/*!
194 * Destroys the QJSManagedValue.
195 *
196 * \note This frees the memory slot it holds on the JavaScript heap. You must
197 * not destroy a QJSManagedValue from a different thread than the one
198 * where the QJSEngine it belongs to lives.
199 */
200QJSManagedValue::~QJSManagedValue()
201{
202 QV4::PersistentValueStorage::free(d);
203}
204
205/*!
206 * Move-constructs a QJSManagedValue from \a other. This leaves \a other in
207 * the default-constructed state where it represents undefined and does not
208 * belong to any engine.
209 */
210QJSManagedValue::QJSManagedValue(QJSManagedValue &&other)
211{
212 qSwap(d, other.d);
213}
214
215/*!
216 * Move-assigns a QJSManagedValue from \a other. This leaves \a other in
217 * the default-constructed state where it represents undefined and does not
218 * belong to any engine.
219 *
220 * \note This frees the memory slot this QJSManagedValue holds on the
221 * JavaScript heap. You must not move-assign a QJSManagedValue on a
222 * different thread than the one where the QJSEngine it belongs to lives.
223 */
224QJSManagedValue &QJSManagedValue::operator=(QJSManagedValue &&other)
225{
226 if (this != &other) {
227 QV4::PersistentValueStorage::free(d);
228 d = nullptr;
229 qSwap(d, other.d);
230 }
231 return *this;
232}
233
234/*!
235 * Invokes the JavaScript '==' operator on this QJSManagedValue and \a other,
236 * and returns the result.
237 *
238 * \sa strictlyEquals
239 */
240bool QJSManagedValue::equals(const QJSManagedValue &other) const
241{
242 if (!d)
243 return !other.d || other.d->isNullOrUndefined();
244 if (!other.d)
245 return d->isNullOrUndefined();
246
247 return QV4::Runtime::CompareEqual::call(*d, *other.d);
248}
249
250/*!
251 * Invokes the JavaScript '===' operator on this QJSManagedValue and \a other,
252 * and returns the result.
253 *
254 * \sa equals
255 */
256bool QJSManagedValue::strictlyEquals(const QJSManagedValue &other) const
257{
258 if (!d)
259 return !other.d || other.d->isUndefined();
260 if (!other.d)
261 return d->isUndefined();
262
263 return QV4::RuntimeHelpers::strictEqual(*d, *other.d);
264}
265
266/*!
267 * Returns the QJSEngine this QJSManagedValue belongs to. Mind that the engine
268 * is always valid, unless the QJSManagedValue is default-constructed or moved
269 * from. In the latter case a nullptr is returned.
270 */
271QJSEngine *QJSManagedValue::engine() const
272{
273 if (!d)
274 return nullptr;
275 if (QV4::ExecutionEngine *v4 = QV4::PersistentValueStorage::getEngine(d))
276 return v4->jsEngine();
277 return nullptr;
278}
279
280/*!
281 * Returns the prototype for this QJSManagedValue. This works on any value. You
282 * can, for example retrieve the JavaScript \c boolean prototype from a \c boolean
283 * value.
284 */
285QJSManagedValue QJSManagedValue::prototype() const
286{
287 if (!d)
288 return QJSManagedValue();
289
290 QV4::ExecutionEngine *v4 = v4Engine(d);
291 QJSManagedValue result(v4);
292
293 if (auto object = d->as<QV4::Object>())
294 *result.d = object->getPrototypeOf();
295 else if (auto managed = d->as<QV4::Managed>())
296 *result.d = managed->internalClass()->prototype;
297 else if (d->isBoolean())
298 *result.d = v4->booleanPrototype();
299 else if (d->isNumber())
300 *result.d = v4->numberPrototype();
301
302 // If the prototype appears to be undefined, then it's actually null in JS terms.
303 if (result.d->isUndefined())
304 *result.d = QV4::Encode::null();
305
306 return result;
307}
308
309/*!
310 * Sets the prototype of this QJSManagedValue to \a prototype. A precondition
311 * is that \a prototype belongs to the same QJSEngine as this QJSManagedValue
312 * and is an object (including null). Furthermore, this QJSManagedValue has to
313 * be an object (excluding null), too, and you cannot create prototype cycles.
314 */
315void QJSManagedValue::setPrototype(const QJSManagedValue &prototype)
316{
317 auto object = d ? d->as<QV4::Object>() : nullptr;
318 if (!object) {
319 qWarning("QJSManagedValue::setPrototype() failed: "
320 "Can only set a prototype on an object (excluding null).");
321 return;
322 }
323
324 // Object includes null ...
325 if (prototype.type() != QJSManagedValue::Object) {
326 qWarning("QJSManagedValue::setPrototype() failed: "
327 "Can only set objects (including null) as prototypes.");
328 return;
329 }
330
331 if (Q_UNLIKELY(object->engine() != v4Engine(prototype.d))) {
332 qWarning("QJSManagedValue::setPrototype() failed: "
333 "Prototype was created in differen engine.");
334 return;
335 }
336
337 // ... Null becomes nullptr here. That is why it appears as undefined later.
338 if (!object->setPrototypeOf(prototype.d->as<QV4::Object>())) {
339 qWarning("QJSManagedValue::setPrototype() failed: "
340 "Prototype cycle detected.");
341 }
342}
343
344/*!
345 * Returns the JavaScript type of this QJSManagedValue.
346 */
347QJSManagedValue::Type QJSManagedValue::type() const
348{
349 if (!d || d->isUndefined())
350 return Undefined;
351 if (d->isBoolean())
352 return Boolean;
353 if (d->isNumber())
354 return Number;
355 if (d->isString())
356 return String;
357 if (d->isSymbol())
358 return Symbol;
359 if (d->isFunctionObject())
360 return Function;
361 return Object;
362}
363
364/*!
365 * \fn QJSManagedValue::isUndefined() const
366 *
367 * Returns \c true if the type of this QJSManagedValue is \c undefined,
368 * or \c false otherwise.
369 */
370
371/*!
372 * \fn QJSManagedValue::isBoolean() const
373 *
374 * Returns \c true if the type of this QJSManagedValue is \c boolean,
375 * or \c false otherwise.
376 */
377
378/*!
379 * \fn QJSManagedValue::isNumber() const
380 *
381 * Returns \c true if the type of this QJSManagedValue is \c number,
382 * or \c false otherwise.
383 */
384
385/*!
386 * \fn QJSManagedValue::isString() const
387 *
388 * Returns \c true if the type of this QJSManagedValue is \c string,
389 * or \c false otherwise.
390 */
391
392/*!
393 * \fn QJSManagedValue::isSymbol() const
394 *
395 * Returns \c true if the type of this QJSManagedValue is \c symbol,
396 * or \c false otherwise.
397 */
398
399/*!
400 * \fn QJSManagedValue::isObject() const
401 *
402 * Returns \c true if the type of this QJSManagedValue is \c object,
403 * or \c false otherwise.
404 */
405
406/*!
407 * \fn QJSManagedValue::isFunction() const
408 *
409 * Returns \c true if the type of this QJSManagedValue is \c function,
410 * \c false otherwise.
411 */
412
413/*!
414 * Returns \c true if this QJSManagedValue holds the JavaScript \c null value,
415 * or \c false otherwise.
416 */
417bool QJSManagedValue::isNull() const
418{
419 return d && d->isNull();
420}
421
422/*!
423 * Returns \c true if this QJSManagedValue holds an integer value, or \c false
424 * otherwise. The storage format of a number does not affect the result of any
425 * operations performed on it, but if an integer is stored, many operations are
426 * faster.
427 */
428bool QJSManagedValue::isInteger() const
429{
430 return d && d->isInteger();
431}
432
433/*!
434 * Returns \c true if this value represents a JavaScript regular expression
435 * object, or \c false otherwise.
436 */
437bool QJSManagedValue::isRegularExpression() const
438{
439 return d && d->as<QV4::RegExpObject>();
440}
441
442/*!
443 * Returns \c true if this value represents a JavaScript Array
444 * object, or \c false otherwise.
445 */
446bool QJSManagedValue::isArray() const
447{
448 return d && d->as<QV4::ArrayObject>();
449}
450
451/*!
452 * Returns \c true if this value represents a JavaScript Url
453 * object, or \c false otherwise.
454 */
455bool QJSManagedValue::isUrl() const
456{
457 return d && d->as<QV4::UrlObject>();
458}
459
460/*!
461 * Returns \c true if this value represents a QVariant managed on the JavaScript
462 * heap, or \c false otherwise.
463 */
464bool QJSManagedValue::isVariant() const
465{
466 return d && d->as<QV4::VariantObject>();
467}
468
469/*!
470 * Returns \c true if this value represents a QObject pointer managed on the
471 * JavaScript heap, or \c false otherwise.
472 */
473bool QJSManagedValue::isQObject() const
474{
475 return d && d->as<QV4::QObjectWrapper>();
476}
477
478/*!
479 * Returns \c true if this value represents a QMetaObject pointer managed on the
480 * JavaScript heap, or \c false otherwise.
481 */
482bool QJSManagedValue::isQMetaObject() const
483{
484 return d && d->as<QV4::QMetaObjectWrapper>();
485}
486
487/*!
488 * Returns \c true if this value represents a JavaScript Date object, or
489 * \c false otherwise.
490 */
491bool QJSManagedValue::isDate() const
492{
493 return d && d->as<QV4::DateObject>();
494}
495
496/*!
497 * Returns \c true if this value represents a JavaScript Error object, or
498 * \c false otherwise.
499 */
500bool QJSManagedValue::isError() const
501{
502 return d && d->as<QV4::ErrorObject>();
503}
504
505/*!
506 * \internal
507 *
508 * Returns \c true if this value represents a JavaScript meta type, or \c false
509 * otherwise.
510 */
511bool QJSManagedValue::isJsMetaType() const
512{
513 return d && d->as<QV4::InternalClass>();
514}
515
516/*!
517 * Converts the manged value to a string. If the managed value holds a string,
518 * that one is returned. Otherwise a string coercion by JavaScript rules is
519 * performed.
520 *
521 * \note Conversion of a managed value to a string can throw an exception. In
522 * particular, symbols cannot be coerced into strings, or a custom
523 * toString() method may throw. In this case the result is an empty
524 * string and the engine carries an error after the conversion.
525 */
526QString QJSManagedValue::toString() const
527{
528 return d ? d->toQString() : QStringLiteral("undefined");
529}
530
531/*!
532 * Converts the manged value to a number. If the managed value holds a number,
533 * that one is returned. Otherwise a number coercion by JavaScript rules is
534 * performed.
535 *
536 * \note Conversion of a managed value to a number can throw an exception. In
537 * particular, symbols cannot be coerced into numbers, or a custom
538 * valueOf() method may throw. In this case the result is 0 and the
539 * engine carries an error after the conversion.
540 */
541double QJSManagedValue::toNumber() const
542{
543 return d ? d->toNumber() : 0;
544}
545
546/*!
547 * Converts the manged value to a boolean. If the managed value holds a boolean,
548 * that one is returned. Otherwise a boolean coercion by JavaScript rules is
549 * performed.
550 */
551bool QJSManagedValue::toBoolean() const
552{
553 return d ? d->toBoolean() : false;
554}
555
556/*!
557 * Converts the manged value to an integer. This first converts the value to a
558 * number by the rules of toNumber(), and then clamps it into the integer range
559 * by the rules given for coercing the arguments to JavaScript bit shift
560 * operators into 32bit integers.
561 *
562 * Internally, the value may already be stored as an integer, in which case a
563 * fast path is taken.
564 *
565 * \note Conversion of a managed value to a number can throw an exception. In
566 * particular, symbols cannot be coerced into numbers, or a custom
567 * valueOf() method may throw. In this case the result is 0 and the
568 * engine carries an error after the conversion.
569 *
570 * \note The JavaScript rules for coercing numbers into 32bit integers are
571 * unintuitive.
572 */
573int QJSManagedValue::toInteger() const
574{
575 return d ? d->toInt32() : 0;
576}
577
578/*!
579 * Converts the manged value to a QJSPrimitiveValue. If the managed value holds
580 * a type supported by QJSPrimitiveValue, the value is copied. Otherwise the
581 * value is converted to a string, and the string is stored in
582 * QJSPrimitiveValue.
583 *
584 * \note Conversion of a managed value to a string can throw an exception. In
585 * particular, symbols cannot be coerced into strings, or a custom
586 * toString() method may throw. In this case the result is the undefined
587 * value and the engine carries an error after the conversion.
588 */
589QJSPrimitiveValue QJSManagedValue::toPrimitive() const
590{
591 if (!d || d->isUndefined())
592 return QJSPrimitiveUndefined();
593 if (d->isNull())
594 return QJSPrimitiveNull();
595 if (d->isBoolean())
596 return d->booleanValue();
597 if (d->isInteger())
598 return d->integerValue();
599 if (d->isNumber())
600 return d->doubleValue();
601
602 bool ok;
603 const QString result = d->toQString(&ok);
604 return ok ? QJSPrimitiveValue(result) : QJSPrimitiveValue(QJSPrimitiveUndefined());
605}
606
607/*!
608 * Copies this QJSManagedValue into a new QJSValue. This is less efficient than
609 * move-constructing a QJSValue from a QJSManagedValue, but retains the
610 * QJSManagedValue.
611 */
612QJSValue QJSManagedValue::toJSValue() const
613{
614 return d ? QJSValuePrivate::fromReturnedValue(d->asReturnedValue()) : QJSValue();
615}
616
617/*!
618 * Copies this QJSManagedValue into a new QVariant. This also creates a useful
619 * QVariant if QJSManagedValue::isVariant() returns false. QVariant can hold all
620 * types supported by QJSManagedValue.
621 */
622QVariant QJSManagedValue::toVariant() const
623{
624 if (!d || d->isUndefined())
625 return QVariant();
626 if (d->isNull())
627 return QVariant(QMetaType::fromType<std::nullptr_t>(), nullptr);
628 if (d->isBoolean())
629 return QVariant(d->booleanValue());
630 if (d->isInteger())
631 return QVariant(d->integerValue());
632 if (d->isNumber())
633 return QVariant(d->doubleValue());
634 if (d->isString())
635 return QVariant(d->toQString());
636 if (d->as<QV4::Managed>())
637 return QV4::ExecutionEngine::toVariant(*d, QMetaType{}, true);
638
639 Q_UNREACHABLE_RETURN(QVariant());
640}
641
642/*!
643 * If this QJSManagedValue holds a JavaScript regular expression object, returns
644 * an equivalent QRegularExpression. Otherwise returns an invalid one.
645 */
646QRegularExpression QJSManagedValue::toRegularExpression() const
647{
648 if (const auto *r = d ? d->as<QV4::RegExpObject>() : nullptr)
649 return r->toQRegularExpression();
650 return {};
651}
652
653/*!
654 * If this QJSManagedValue holds a JavaScript Url object, returns
655 * an equivalent QUrl. Otherwise returns an invalid one.
656 */
657QUrl QJSManagedValue::toUrl() const
658{
659 if (const auto *u = d ? d->as<QV4::UrlObject>() : nullptr)
660 return u->toQUrl();
661 return {};
662}
663
664/*!
665 * If this QJSManagedValue holds a QObject pointer, returns it. Otherwise
666 * returns nullptr.
667 */
668QObject *QJSManagedValue::toQObject() const
669{
670 if (const auto *o = d ? d->as<QV4::QObjectWrapper>() : nullptr)
671 return o->object();
672 return {};
673}
674
675/*!
676 * If this QJSManagedValue holds a QMetaObject pointer, returns it.
677 * Otherwise returns nullptr.
678 */
679const QMetaObject *QJSManagedValue::toQMetaObject() const
680{
681 if (const auto *m = d ? d->as<QV4::QMetaObjectWrapper>() : nullptr)
682 return m->metaObject();
683 return {};
684}
685
686/*!
687 * If this QJSManagedValue holds a JavaScript Date object, returns an equivalent
688 * QDateTime. Otherwise returns an invalid one.
689 */
690QDateTime QJSManagedValue::toDateTime() const
691{
692 if (const auto *t = d ? d->as<QV4::DateObject>() : nullptr)
693 return t->toQDateTime();
694 return {};
695}
696
697/*!
698 * Returns \c true if this QJSManagedValue has a property \a name, otherwise
699 * returns \c false. The properties of the prototype chain are considered.
700 */
701bool QJSManagedValue::hasProperty(const QString &name) const
702{
703 if (!d || d->isNullOrUndefined())
704 return false;
705
706 if (d->isString() && name == QStringLiteral("length"))
707 return true;
708
709 if (QV4::Object *obj = d->as<QV4::Object>()) {
710 QV4::Scope scope(obj->engine());
711 QV4::ScopedPropertyKey key(scope, scope.engine->identifierTable->asPropertyKey(name));
712 return obj->hasProperty(key);
713 }
714
715 return prototype().hasProperty(name);
716}
717
718/*!
719 * Returns \c true if this QJSManagedValue has a property \a name, otherwise
720 * returns \c false. The properties of the prototype chain are not considered.
721 */
722bool QJSManagedValue::hasOwnProperty(const QString &name) const
723{
724 if (!d || d->isNullOrUndefined())
725 return false;
726
727 if (d->isString() && name == QStringLiteral("length"))
728 return true;
729
730 if (QV4::Object *obj = d->as<QV4::Object>()) {
731 QV4::Scope scope(obj->engine());
732 QV4::ScopedPropertyKey key(scope, scope.engine->identifierTable->asPropertyKey(name));
733 return obj->getOwnProperty(key) != QV4::Attr_Invalid;
734 }
735
736 return false;
737}
738
739/*!
740 * Returns the property \a name of this QJSManagedValue. The prototype chain
741 * is searched if the property is not found on the actual object.
742 */
743QJSValue QJSManagedValue::property(const QString &name) const
744{
745 if (!d)
746 return QJSValue();
747
748 if (d->isNullOrUndefined()) {
749 QV4::ExecutionEngine *e = v4Engine(d);
750 e->throwTypeError(QStringLiteral("Cannot read property '%1' of null").arg(name));
751 return QJSValue();
752 }
753
754 if (QV4::String *string = d->as<QV4::String>()) {
755 if (name == QStringLiteral("length"))
756 return QJSValue(string->d()->length());
757 }
758
759 if (QV4::Object *obj = d->as<QV4::Object>()) {
760 QV4::Scope scope(obj->engine());
761 QV4::ScopedPropertyKey key(scope, scope.engine->identifierTable->asPropertyKey(name));
762 return QJSValuePrivate::fromReturnedValue(obj->get(key));
763 }
764
765 return prototype().property(name);
766}
767
768/*!
769 * Sets the property \a name to \a value on this QJSManagedValue. This can only
770 * be done on JavaScript values of type \c object. Furhermore, \a value has to be
771 * either a primitive or belong to the same engine as this value.
772 */
773void QJSManagedValue::setProperty(const QString &name, const QJSValue &value)
774{
775 if (!d)
776 return;
777
778 if (d->isNullOrUndefined()) {
779 v4Engine(d)->throwTypeError(
780 QStringLiteral("Value is null and could not be converted to an object"));
781 }
782
783 if (QV4::Object *obj = d->as<QV4::Object>()) {
784 QV4::Scope scope(obj->engine());
785 QV4::ExecutionEngine *v4 = QJSValuePrivate::engine(&value);
786 if (Q_UNLIKELY(v4 && v4 != scope.engine)) {
787 qWarning("QJSManagedValue::setProperty() failed: "
788 "Value was created in different engine.");
789 return;
790 }
791 QV4::ScopedPropertyKey key(scope, scope.engine->identifierTable->asPropertyKey(name));
792 QV4::ScopedValue val(scope, QJSValuePrivate::convertToReturnedValue(scope.engine, value));
793 obj->put(key, val);
794 }
795}
796
797/*!
798 * Deletes the property \a name from this QJSManagedValue. Returns \c true if
799 * the deletion succeeded, or \c false otherwise.
800 */
801bool QJSManagedValue::deleteProperty(const QString &name)
802{
803 if (!d)
804 return false;
805
806 if (QV4::Object *obj = d->as<QV4::Object>()) {
807 QV4::Scope scope(obj->engine());
808 QV4::ScopedPropertyKey key(scope, scope.engine->identifierTable->asPropertyKey(name));
809 return obj->deleteProperty(key);
810 }
811
812 return false;
813}
814
815/*!
816 * Returns \c true if this QJSManagedValue has an array index \a arrayIndex,
817 * otherwise returns \c false. The properties of the prototype chain are
818 * considered.
819 */
820bool QJSManagedValue::hasProperty(quint32 arrayIndex) const
821{
822 if (!d || d->isNullOrUndefined())
823 return false;
824
825 if (QV4::String *string = d->as<QV4::String>())
826 return arrayIndex < quint32(string->d()->length());
827
828 if (QV4::Object *obj = d->as<QV4::Object>()) {
829 bool hasProperty = false;
830 if (arrayIndex == std::numeric_limits<quint32>::max())
831 obj->get(obj->engine()->id_uintMax(), &hasProperty);
832 else
833 obj->get(arrayIndex, &hasProperty);
834 return hasProperty;
835 }
836
837 return prototype().hasProperty(arrayIndex);
838}
839
840/*!
841 * Returns \c true if this QJSManagedValue has an array index \a arrayIndex,
842 * otherwise returns \c false. The properties of the prototype chain are not
843 * considered.
844 */
845bool QJSManagedValue::hasOwnProperty(quint32 arrayIndex) const
846{
847 if (!d || d->isNullOrUndefined())
848 return false;
849
850 if (QV4::String *string = d->as<QV4::String>())
851 return arrayIndex < quint32(string->d()->length());
852
853 if (QV4::Object *obj = d->as<QV4::Object>()) {
854 if (arrayIndex == std::numeric_limits<quint32>::max()) {
855 return obj->getOwnProperty(obj->engine()->id_uintMax()->toPropertyKey())
856 != QV4::Attr_Invalid;
857 } else {
858 return obj->getOwnProperty(QV4::PropertyKey::fromArrayIndex(arrayIndex))
859 != QV4::Attr_Invalid;
860 }
861 }
862
863 return false;
864}
865
866/*!
867 * Returns the property stored at \a arrayIndex of this QJSManagedValue. The
868 * prototype chain is searched if the property is not found on the actual
869 * object.
870 */
871QJSValue QJSManagedValue::property(quint32 arrayIndex) const
872{
873 if (!d || d->isNullOrUndefined())
874 return QJSValue();
875
876 if (QV4::String *string = d->as<QV4::String>()) {
877 const QString qString = string->toQString();
878 if (arrayIndex < quint32(qString.size()))
879 return qString.sliced(arrayIndex, 1);
880 return QJSValue();
881 }
882
883 if (QV4::Object *obj = d->as<QV4::Object>()) {
884 if (arrayIndex == std::numeric_limits<quint32>::max())
885 return QJSValuePrivate::fromReturnedValue(obj->get(obj->engine()->id_uintMax()));
886 else
887 return QJSValuePrivate::fromReturnedValue(obj->get(arrayIndex));
888 }
889
890 return prototype().property(arrayIndex);
891}
892
893/*!
894 * Stores the \a value at \a arrayIndex in this QJSManagedValue. This can only
895 * be done on JavaScript values of type \c object, and it's not recommended if the
896 * value is not an array. Furhermore, \a value has to be either a primitive or
897 * belong to the same engine as this value.
898 */
899void QJSManagedValue::setProperty(quint32 arrayIndex, const QJSValue &value)
900{
901 if (!d)
902 return;
903
904 if (QV4::Object *obj = d->as<QV4::Object>()) {
905 QV4::ExecutionEngine *v4 = QJSValuePrivate::engine(&value);
906 if (Q_UNLIKELY(v4 && v4 != obj->engine())) {
907 qWarning("QJSManagedValue::setProperty() failed: "
908 "Value was created in different engine.");
909 return;
910 }
911 v4 = obj->engine(); // in case value was primitive
912 QV4::Scope scope(v4);
913 QV4::ScopedValue v(scope, QJSValuePrivate::convertToReturnedValue(v4, value));
914 obj->put(arrayIndex, v);
915 }
916}
917
918/*!
919 * Deletes the value stored at \a arrayIndex from this QJSManagedValue. Returns
920 * \c true if the deletion succeeded, or \c false otherwise.
921 */
922bool QJSManagedValue::deleteProperty(quint32 arrayIndex)
923{
924 if (!d)
925 return false;
926
927 if (QV4::Object *obj = d->as<QV4::Object>())
928 return obj->deleteProperty(QV4::PropertyKey::fromArrayIndex(arrayIndex));
929
930 return false;
931}
932
933static const QV4::FunctionObject *functionObjectForCall(QV4::Value *d)
934{
935 if (Q_UNLIKELY(!d)) {
936 qWarning("QJSManagedValue: Calling a default-constructed or moved-from managed value"
937 "should throw an exception, but there is no engine to receive it.");
938 return nullptr;
939 }
940
941 if (const QV4::FunctionObject *f = d->as<QV4::FunctionObject>())
942 return f;
943
944 v4Engine(d)->throwTypeError(QStringLiteral("Value is not a function"));
945 return nullptr;
946}
947
948/*!
949 * If this QJSManagedValue represents a JavaScript FunctionObject, calls it with
950 * the given \a arguments, and returns the result. Otherwise returns a
951 * JavaScript \c undefined value.
952 *
953 * The \a arguments have to be either primitive values or belong to the same
954 * QJSEngine as this QJSManagedValue. Otherwise the call is not carried
955 * out and a JavaScript \c undefined value is returned.
956 */
957QJSValue QJSManagedValue::call(const QJSValueList &arguments) const
958{
959 const QV4::FunctionObject *f = functionObjectForCall(d);
960 if (!f)
961 return QJSValue();
962
963 QV4::ExecutionEngine *engine = f->engine();
964
965 QV4::Scope scope(engine);
966 QV4::JSCallArguments jsCallData(scope, arguments.size());
967 *jsCallData.thisObject = engine->globalObject;
968 int i = 0;
969 for (const QJSValue &arg : arguments) {
970 if (Q_UNLIKELY(!QJSValuePrivate::checkEngine(engine, arg))) {
971 qWarning("QJSManagedValue::call() failed: Argument was created in different engine.");
972 return QJSValue();
973 }
974 jsCallData.args[i++] = QJSValuePrivate::convertToReturnedValue(engine, arg);
975 }
976
977 return QJSValuePrivate::fromReturnedValue(f->call(jsCallData));
978}
979
980/*!
981 * If this QJSManagedValue represents a JavaScript FunctionObject, calls it on
982 * \a instance with the given \a arguments, and returns the result. Otherwise
983 * returns a JavaScript \c undefined value.
984 *
985 * The \a arguments and the \a instance have to be either primitive values or
986 * belong to the same QJSEngine as this QJSManagedValue. Otherwise the call is
987 * not carried out and a JavaScript \c undefined value is returned.
988 */
989QJSValue QJSManagedValue::callWithInstance(const QJSValue &instance,
990 const QJSValueList &arguments) const
991{
992 const QV4::FunctionObject *f = functionObjectForCall(d);
993 if (!f)
994 return QJSValue();
995
996 QV4::ExecutionEngine *engine = f->engine();
997
998 if (Q_UNLIKELY(!QJSValuePrivate::checkEngine(engine, instance))) {
999 qWarning("QJSManagedValue::callWithInstance() failed: "
1000 "Instance was created in different engine.");
1001 return QJSValue();
1002 }
1003
1004 QV4::Scope scope(engine);
1005 QV4::JSCallArguments jsCallData(scope, arguments.size());
1006 *jsCallData.thisObject = QJSValuePrivate::convertToReturnedValue(engine, instance);
1007 int i = 0;
1008 for (const QJSValue &arg : arguments) {
1009 if (Q_UNLIKELY(!QJSValuePrivate::checkEngine(engine, arg))) {
1010 qWarning("QJSManagedValue::callWithInstance() failed: "
1011 "Argument was created in different engine.");
1012 return QJSValue();
1013 }
1014 jsCallData.args[i++] = QJSValuePrivate::convertToReturnedValue(engine, arg);
1015 }
1016
1017 return QJSValuePrivate::fromReturnedValue(f->call(jsCallData));
1018}
1019
1020/*!
1021 * If this QJSManagedValue represents a JavaScript FunctionObject, calls it as
1022 * constructor with the given \a arguments, and returns the result. Otherwise
1023 * returns a JavaScript \c undefined value.
1024 *
1025 * The \a arguments have to be either primitive values or belong to the same
1026 * QJSEngine as this QJSManagedValue. Otherwise the call is not carried
1027 * out and a JavaScript \c undefined value is returned.
1028 */
1029QJSValue QJSManagedValue::callAsConstructor(const QJSValueList &arguments) const
1030{
1031 const QV4::FunctionObject *f = functionObjectForCall(d);
1032 if (!f)
1033 return QJSValue();
1034
1035 QV4::ExecutionEngine *engine = f->engine();
1036
1037 QV4::Scope scope(engine);
1038 QV4::JSCallArguments jsCallData(scope, arguments.size());
1039 int i = 0;
1040 for (const QJSValue &arg : arguments) {
1041 if (Q_UNLIKELY(!QJSValuePrivate::checkEngine(engine, arg))) {
1042 qWarning("QJSManagedValue::callAsConstructor() failed: "
1043 "Argument was created in different engine.");
1044 return QJSValue();
1045 }
1046 jsCallData.args[i++] = QJSValuePrivate::convertToReturnedValue(engine, arg);
1047 }
1048
1049 return QJSValuePrivate::fromReturnedValue(f->callAsConstructor(jsCallData));
1050}
1051
1052/*!
1053 * \internal
1054 *
1055 * Retrieves the JavaScript meta type of this value. The JavaScript meta type
1056 * represents the layout of members in an object. Instantiating a meta type is
1057 * faster than re-constructing the same object using a sequence of setProperty()
1058 * calls on a new object.
1059 *
1060 * \sa members(), instantiate()
1061 */
1062QJSManagedValue QJSManagedValue::jsMetaType() const
1063{
1064 if (!d)
1065 return QJSManagedValue();
1066
1067 QJSManagedValue result(v4Engine(d));
1068 if (QV4::Managed *m = d->as<QV4::Managed>())
1069 *result.d = m->internalClass();
1070
1071 return result;
1072}
1073
1074/*!
1075 * \internal
1076 *
1077 * If this value is a JavaScript meta type, retrieves the names of its members
1078 * The ordering of the names corresponds to the ordering of the values to be
1079 * passed to instantiate().
1080 *
1081 * If the value is not a meta type, an empty list is returned.
1082 *
1083 * \sa isMetaType(), metaType(), instantiate()
1084 */
1085QStringList QJSManagedValue::jsMetaMembers() const
1086{
1087 if (!d)
1088 return {};
1089
1090 if (QV4::InternalClass *c = d->as<QV4::InternalClass>()) {
1091 const auto heapClass = c->d();
1092 const int size = heapClass->size;
1093 QStringList result;
1094 result.reserve(size);
1095 QV4::Scope scope(c->engine());
1096 for (int i = 0; i < size; ++i) {
1097 QV4::ScopedValue key(scope, heapClass->keyAt(i));
1098 result.append(key->toQString());
1099 }
1100 return result;
1101 }
1102
1103 return {};
1104}
1105
1106/*!
1107 * \internal
1108 *
1109 * If this value is a JavaScript meta type, instantiates it using the
1110 * \a values, and returns the result. Otherwise returns undefined.
1111 *
1112 * The values are expected in the same order as the keys in the return value of
1113 * members(), and that is the order in which properties were added to the object
1114 * this meta type originally belongs to.
1115 *
1116 * \sa members(), metaType(), isMetaType().
1117 */
1118QJSManagedValue QJSManagedValue::jsMetaInstantiate(const QJSValueList &values) const
1119{
1120 if (!d)
1121 return {};
1122
1123 if (QV4::InternalClass *c = d->as<QV4::InternalClass>()) {
1124 QV4::ExecutionEngine *engine = c->engine();
1125 QJSManagedValue result(engine);
1126 *result.d = c->engine()->newObject(c->d());
1127 QV4::Object *o = result.d->as<QV4::Object>();
1128
1129 QV4::Scope scope(engine);
1130 QV4::ScopedValue val(scope);
1131 for (uint i = 0, end = qMin(qsizetype(c->d()->size), values.size()); i < end; ++i) {
1132 const QJSValue &arg = values[i];
1133 if (Q_UNLIKELY(!QJSValuePrivate::checkEngine(engine, arg))) {
1134 qWarning("QJSManagedValue::instantiate() failed: "
1135 "Argument was created in different engine.");
1136 return QJSManagedValue();
1137 }
1138 val = QJSValuePrivate::convertToReturnedValue(engine, arg);
1139 o->setProperty(i, val);
1140 }
1141
1142 return result;
1143 }
1144
1145 return {};
1146}
1147
1148QJSManagedValue::QJSManagedValue(QV4::ExecutionEngine *engine) :
1149 d(engine->memoryManager->m_persistentValues->allocate())
1150{
1151}
1152
1153QT_END_NAMESPACE
static const QV4::FunctionObject * functionObjectForCall(QV4::Value *d)