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 <QtCore/qstring.h> |
5 | #include <QtCore/qvarlengtharray.h> |
6 | #include <QtCore/qdatetime.h> |
7 | #include "qjsvalue.h" |
8 | #include "qjsprimitivevalue.h" |
9 | #include "qjsmanagedvalue.h" |
10 | #include "qjsvalue_p.h" |
11 | #include "qv4value_p.h" |
12 | #include "qv4object_p.h" |
13 | #include "qv4functionobject_p.h" |
14 | #include "qv4dateobject_p.h" |
15 | #include "qv4runtime_p.h" |
16 | #include "qv4variantobject_p.h" |
17 | #include "qv4regexpobject_p.h" |
18 | #include "qv4errorobject_p.h" |
19 | #include <private/qv4mm_p.h> |
20 | #include <private/qv4jscall_p.h> |
21 | #include <private/qv4qobjectwrapper_p.h> |
22 | #include <private/qv4qmetaobjectwrapper_p.h> |
23 | #include <private/qv4urlobject_p.h> |
24 | #include <private/qqmlbuiltins_p.h> |
25 | |
26 | /*! |
27 | \since 5.0 |
28 | \class QJSValue |
29 | |
30 | \brief The QJSValue class acts as a container for Qt/JavaScript data types. |
31 | |
32 | \ingroup qtjavascript |
33 | \inmodule QtQml |
34 | |
35 | QJSValue supports the types defined in the \l{ECMA-262} |
36 | standard: The primitive types, which are Undefined, Null, Boolean, |
37 | Number, and String; and the Object and Array types. Additionally, built-in |
38 | support is provided for Qt/C++ types such as QVariant and QObject. |
39 | |
40 | For the object-based types (including Date and RegExp), use the |
41 | newT() functions in QJSEngine (e.g. QJSEngine::newObject()) |
42 | to create a QJSValue of the desired type. For the primitive types, |
43 | use one of the QJSValue constructor overloads. For other types, e.g. |
44 | registered gadget types such as QPoint, you can use QJSEngine::toScriptValue. |
45 | |
46 | The methods named isT() (e.g. isBool(), isUndefined()) can be |
47 | used to test if a value is of a certain type. The methods named |
48 | toT() (e.g. toBool(), toString()) can be used to convert a |
49 | QJSValue to another type. You can also use the generic |
50 | qjsvalue_cast() function. |
51 | |
52 | Object values have zero or more properties which are themselves |
53 | QJSValues. Use setProperty() to set a property of an object, and |
54 | call property() to retrieve the value of a property. |
55 | |
56 | \snippet code/src_script_qjsvalue.cpp 0 |
57 | |
58 | If you want to iterate over the properties of a script object, use |
59 | the QJSValueIterator class. |
60 | |
61 | Object values have an internal \c{prototype} property, which can be |
62 | accessed with prototype() and setPrototype(). |
63 | |
64 | Function objects (objects for which isCallable()) returns true) can |
65 | be invoked by calling call(). Constructor functions can be used to |
66 | construct new objects by calling callAsConstructor(). |
67 | |
68 | Use equals() or strictlyEquals() to compare a QJSValue to another. |
69 | |
70 | Note that a QJSValue for which isObject() is true only carries a |
71 | reference to an actual object; copying the QJSValue will only |
72 | copy the object reference, not the object itself. If you want to |
73 | clone an object (i.e. copy an object's properties to another |
74 | object), you can do so with the help of a \c{for-in} statement in |
75 | script code, or QJSValueIterator in C++. |
76 | |
77 | \sa QJSEngine, QJSValueIterator |
78 | |
79 | \section1 Working With Arrays |
80 | |
81 | To create an array using QJSValue, use \l QJSEngine::newArray(): |
82 | |
83 | \code |
84 | // Assumes that this class was declared in QML. |
85 | QJSValue jsArray = engine->newArray(3); |
86 | \endcode |
87 | |
88 | To set individual elements in the array, use |
89 | the \l {QJSValue::}{setProperty(quint32 arrayIndex, const QJSValue &value)} |
90 | overload. For example, to fill the array above with integers: |
91 | |
92 | \code |
93 | for (int i = 0; i < 3; ++i) { |
94 | jsArray.setProperty(i, QRandomGenerator::global().generate()); |
95 | } |
96 | \endcode |
97 | |
98 | To determine the length of the array, access the \c "length" property. |
99 | To access array elements, use the |
100 | \l {QJSValue::}{property(quint32 arrayIndex)} overload. The following code |
101 | reads the array we created above back into a list: |
102 | |
103 | \code |
104 | QVector<int> integers; |
105 | const int length = jsArray.property("length").toInt(); |
106 | for (int i = 0; i < length; ++i) { |
107 | integers.append(jsArray.property(i).toInt()); |
108 | } |
109 | \endcode |
110 | |
111 | \section2 Converting to JSON |
112 | |
113 | It's possible to convert a QJSValue to a JSON type. For example, |
114 | to convert to an array, use \l QJSEngine::fromScriptValue(): |
115 | |
116 | \code |
117 | const QJsonValue jsonValue = engine.fromScriptValue<QJsonValue>(jsValue); |
118 | const QJsonArray jsonArray = jsonValue.toArray(); |
119 | \endcode |
120 | */ |
121 | |
122 | /*! |
123 | \enum QJSValue::SpecialValue |
124 | |
125 | This enum is used to specify a single-valued type. |
126 | |
127 | \value UndefinedValue An undefined value. |
128 | |
129 | \value NullValue A null value. |
130 | */ |
131 | |
132 | /*! |
133 | \typedef QJSValueList |
134 | \relates QJSValue |
135 | |
136 | This is a typedef for a QList<QJSValue>. |
137 | */ |
138 | |
139 | /*! |
140 | \enum QJSValue::ErrorType |
141 | \since 5.12 |
142 | |
143 | Use this enum for JavaScript language-specific types of Error objects. |
144 | |
145 | They may be useful when emulating language features in C++ requires the use |
146 | of specialized exception types. In addition, they may help to more clearly |
147 | communicate certain typical conditions, instead of throwing a generic |
148 | JavaScript exception. For example, code that deals with networking and |
149 | resource locators may find it useful to propagate errors related to |
150 | malformed locators using the URIError type. |
151 | |
152 | \omitvalue NoError |
153 | \value GenericError A generic Error object, but not of a specific sub-type. |
154 | \omitvalue EvalError |
155 | \value RangeError A value did not match the expected set or range. |
156 | \value ReferenceError A non-existing variable referenced. |
157 | \value SyntaxError An invalid token or sequence of tokens was encountered |
158 | that does not conform with the syntax of the language. |
159 | \value TypeError An operand or argument is incompatible with the type |
160 | expected. |
161 | \value URIError A URI handling function was used incorrectly or the URI |
162 | provided is malformed. |
163 | */ |
164 | |
165 | /*! |
166 | \enum QJSValue::ObjectConversionBehavior |
167 | |
168 | This enum is used to specify how JavaScript objects and symbols without an equivalent |
169 | native Qt type should be treated when converting to QVariant. |
170 | |
171 | \value ConvertJSObjects A best-effort, possibly lossy, conversion is attempted. |
172 | Symbols are converted to QString. |
173 | |
174 | \value RetainJSObjects The value is retained as QJSValue wrapped in QVariant. |
175 | */ |
176 | |
177 | QT_BEGIN_NAMESPACE |
178 | |
179 | using namespace QV4; |
180 | |
181 | /*! |
182 | Constructs a new QJSValue with a boolean \a value. |
183 | */ |
184 | QJSValue::QJSValue(bool value) : d(QJSValuePrivate::encode(boolValue: value)) |
185 | { |
186 | } |
187 | |
188 | /*! |
189 | Constructs a new QJSValue with a number \a value. |
190 | */ |
191 | QJSValue::QJSValue(int value) : d(QJSValuePrivate::encode(intValue: value)) |
192 | { |
193 | } |
194 | |
195 | /*! |
196 | Constructs a new QJSValue with a number \a value. |
197 | */ |
198 | QJSValue::QJSValue(uint value) : d(QJSValuePrivate::encode(uintValue: value)) |
199 | { |
200 | } |
201 | |
202 | /*! |
203 | Constructs a new QJSValue with a number \a value. |
204 | */ |
205 | QJSValue::QJSValue(double value) : d(QJSValuePrivate::encode(doubleValue: value)) |
206 | { |
207 | } |
208 | |
209 | /*! |
210 | Constructs a new QJSValue with a string \a value. |
211 | */ |
212 | QJSValue::QJSValue(const QString &value) : d(QJSValuePrivate::encode(stringValue: value)) |
213 | { |
214 | } |
215 | |
216 | /*! |
217 | Constructs a new QJSValue with a special \a value. |
218 | */ |
219 | QJSValue::QJSValue(SpecialValue value) |
220 | : d(value == NullValue ? QJSValuePrivate::encodeNull() : QJSValuePrivate::encodeUndefined()) |
221 | { |
222 | } |
223 | |
224 | /*! |
225 | Constructs a new QJSValue with a string \a value. |
226 | */ |
227 | QJSValue::QJSValue(const QLatin1String &value) : d(QJSValuePrivate::encode(stringValue: value)) |
228 | { |
229 | } |
230 | |
231 | /*! |
232 | Constructs a new QJSValue with a string \a value. |
233 | */ |
234 | #ifndef QT_NO_CAST_FROM_ASCII |
235 | QJSValue::QJSValue(const char *value) : d(QJSValuePrivate::encode(stringValue: QString::fromUtf8(utf8: value))) |
236 | { |
237 | } |
238 | #endif |
239 | |
240 | /*! |
241 | Constructs a new QJSValue that is a copy of \a other. |
242 | |
243 | Note that if \a other is an object (i.e., isObject() would return |
244 | true), then only a reference to the underlying object is copied into |
245 | the new script value (i.e., the object itself is not copied). |
246 | */ |
247 | QJSValue::QJSValue(const QJSValue &other) : d(other.d) |
248 | { |
249 | switch (QJSValuePrivate::tag(raw: d)) { |
250 | case QJSValuePrivate::Kind::Undefined: |
251 | case QJSValuePrivate::Kind::Null: |
252 | case QJSValuePrivate::Kind::IntValue: |
253 | case QJSValuePrivate::Kind::BoolValue: |
254 | return; |
255 | case QJSValuePrivate::Kind::DoublePtr: |
256 | d = QJSValuePrivate::encode(doubleValue: *QJSValuePrivate::doublePtr(v: d)); |
257 | return; |
258 | case QJSValuePrivate::Kind::QV4ValuePtr: |
259 | d = QJSValuePrivate::encode(qv4Value: *QJSValuePrivate::qv4ValuePtr(v: d)); |
260 | return; |
261 | case QJSValuePrivate::Kind::QStringPtr: |
262 | d = QJSValuePrivate::encode(stringValue: *QJSValuePrivate::qStringPtr(v: d)); |
263 | break; |
264 | } |
265 | } |
266 | |
267 | /*! |
268 | \fn QJSValue::QJSValue(QJSValue && other) |
269 | |
270 | Move constructor. Moves from \a other into this QJSValue object. |
271 | */ |
272 | |
273 | /*! |
274 | \fn QJSValue &QJSValue::operator=(QJSValue && other) |
275 | |
276 | Move-assigns \a other to this QJSValue object. |
277 | */ |
278 | |
279 | /*! |
280 | Destroys this QJSValue. |
281 | */ |
282 | QJSValue::~QJSValue() |
283 | { |
284 | QJSValuePrivate::free(jsval: this); |
285 | } |
286 | |
287 | /*! |
288 | Returns true if this QJSValue is of the primitive type Boolean; |
289 | otherwise returns false. |
290 | |
291 | \sa toBool() |
292 | */ |
293 | bool QJSValue::isBool() const |
294 | { |
295 | return QJSValuePrivate::tag(raw: d) == QJSValuePrivate::Kind::BoolValue; |
296 | } |
297 | |
298 | /*! |
299 | Returns true if this QJSValue is of the primitive type Number; |
300 | otherwise returns false. |
301 | |
302 | \sa toNumber() |
303 | */ |
304 | bool QJSValue::isNumber() const |
305 | { |
306 | switch (QJSValuePrivate::tag(raw: d)) { |
307 | case QJSValuePrivate::Kind::IntValue: |
308 | case QJSValuePrivate::Kind::DoublePtr: |
309 | return true; |
310 | default: |
311 | break; |
312 | } |
313 | |
314 | return false; |
315 | } |
316 | |
317 | /*! |
318 | Returns true if this QJSValue is of the primitive type Null; |
319 | otherwise returns false. |
320 | */ |
321 | bool QJSValue::isNull() const |
322 | { |
323 | return QJSValuePrivate::tag(raw: d) == QJSValuePrivate::Kind::Null; |
324 | } |
325 | |
326 | /*! |
327 | Returns true if this QJSValue is of the primitive type String; |
328 | otherwise returns false. |
329 | |
330 | \sa toString() |
331 | */ |
332 | bool QJSValue::isString() const |
333 | { |
334 | switch (QJSValuePrivate::tag(raw: d)) { |
335 | case QJSValuePrivate::Kind::QStringPtr: |
336 | return true; |
337 | case QJSValuePrivate::Kind::QV4ValuePtr: { |
338 | return QJSValuePrivate::qv4ValuePtr(v: d)->isString(); |
339 | } |
340 | default: |
341 | break; |
342 | } |
343 | |
344 | return false; |
345 | } |
346 | |
347 | /*! |
348 | Returns true if this QJSValue is of the primitive type Undefined or if the managed value |
349 | has been cleared (by deleting the engine). Otherwise returns false. |
350 | */ |
351 | bool QJSValue::isUndefined() const |
352 | { |
353 | switch (QJSValuePrivate::tag(raw: d)) { |
354 | case QJSValuePrivate::Kind::Undefined: |
355 | return true; |
356 | case QJSValuePrivate::Kind::QV4ValuePtr: |
357 | return QJSValuePrivate::qv4ValuePtr(v: d)->isUndefined(); |
358 | default: |
359 | break; |
360 | } |
361 | |
362 | return false; |
363 | } |
364 | |
365 | /*! |
366 | Returns true if this QJSValue is an object of the Error class; |
367 | otherwise returns false. |
368 | |
369 | \sa errorType(), {QJSEngine#Script Exceptions}{QJSEngine - Script Exceptions} |
370 | */ |
371 | bool QJSValue::isError() const |
372 | { |
373 | return QJSValuePrivate::asManagedType<ErrorObject>(jsval: this); |
374 | } |
375 | |
376 | /*! |
377 | Returns true if this QJSValue is an object of the URL JavaScript class; |
378 | otherwise returns false. |
379 | |
380 | \note For a QJSValue that contains a QUrl, this function returns false. |
381 | However, \c{toVariant().value<QUrl>()} works in both cases. |
382 | */ |
383 | bool QJSValue::isUrl() const |
384 | { |
385 | return QJSValuePrivate::asManagedType<UrlObject>(jsval: this); |
386 | } |
387 | |
388 | /*! |
389 | \since 5.12 |
390 | Returns the error type this QJSValue represents if it is an Error object. |
391 | Otherwise, returns \c NoError." |
392 | |
393 | \sa isError(), {QJSEngine#Script Exceptions}{QJSEngine - Script Exceptions} |
394 | */ |
395 | QJSValue::ErrorType QJSValue::errorType() const |
396 | { |
397 | const QV4::ErrorObject *error = QJSValuePrivate::asManagedType<ErrorObject>(jsval: this); |
398 | if (!error) |
399 | return NoError; |
400 | switch (error->d()->errorType) { |
401 | case QV4::Heap::ErrorObject::Error: |
402 | return GenericError; |
403 | case QV4::Heap::ErrorObject::EvalError: |
404 | return EvalError; |
405 | case QV4::Heap::ErrorObject::RangeError: |
406 | return RangeError; |
407 | case QV4::Heap::ErrorObject::ReferenceError: |
408 | return ReferenceError; |
409 | case QV4::Heap::ErrorObject::SyntaxError: |
410 | return SyntaxError; |
411 | case QV4::Heap::ErrorObject::TypeError: |
412 | return TypeError; |
413 | case QV4::Heap::ErrorObject::URIError: |
414 | return URIError; |
415 | } |
416 | Q_UNREACHABLE_RETURN(NoError); |
417 | } |
418 | |
419 | /*! |
420 | Returns true if this QJSValue is an object of the Array class; |
421 | otherwise returns false. |
422 | |
423 | \note This method is the equivalent of \e Array.isArray() in JavaScript. You |
424 | can use it to identify JavaScript arrays, but it will return \c false |
425 | for any array-like objects that are not JavaScript arrays. This includes |
426 | QML \e list objects for either value types or object types, JavaScript |
427 | typed arrays, JavaScript ArrayBuffer objects, and any custom array-like |
428 | objects you may create yourself. All of these \e behave like JavaScript |
429 | arrays, though: They generally expose the same methods and the |
430 | subscript operator can be used on them. Therefore, using this method to |
431 | determine whether an object could be used like an array is not |
432 | advisable. |
433 | |
434 | \sa QJSEngine::newArray() |
435 | */ |
436 | bool QJSValue::isArray() const |
437 | { |
438 | return QJSValuePrivate::asManagedType<ArrayObject>(jsval: this); |
439 | } |
440 | |
441 | /*! |
442 | Returns true if this QJSValue is of the Object type; otherwise |
443 | returns false. |
444 | |
445 | Note that function values, variant values, and QObject values are |
446 | objects, so this function returns true for such values. |
447 | |
448 | \sa QJSEngine::newObject() |
449 | */ |
450 | bool QJSValue::isObject() const |
451 | { |
452 | return QJSValuePrivate::asManagedType<QV4::Object>(jsval: this); |
453 | } |
454 | |
455 | /*! |
456 | Returns true if this QJSValue is a function, otherwise |
457 | returns false. |
458 | |
459 | \sa call() |
460 | */ |
461 | bool QJSValue::isCallable() const |
462 | { |
463 | return QJSValuePrivate::asManagedType<FunctionObject>(jsval: this); |
464 | } |
465 | |
466 | #if QT_DEPRECATED_SINCE(6, 9) |
467 | /*! |
468 | \deprecated [6.9] |
469 | Returns true if this QJSValue is a variant value; |
470 | otherwise returns false. |
471 | |
472 | \warning This function is likely to give unexpected results. |
473 | A variant value is only constructed by the QJSEngine in a very |
474 | limited number of cases. This used to be different before Qt |
475 | 5.14, where \l{QJSEngine::toScriptValue} would have created |
476 | them for more types instead of corresponding ECMAScript types. |
477 | You can get a valid \l QVariant via \l toVariant for many values |
478 | for which \c{isVariant} returns false. |
479 | |
480 | \sa toVariant() |
481 | */ |
482 | bool QJSValue::isVariant() const |
483 | { |
484 | if (QJSValuePrivate::asManagedType<QV4::VariantObject>(jsval: this)) |
485 | return true; |
486 | if (auto vt = QJSValuePrivate::asManagedType<QV4::QQmlValueTypeWrapper>(jsval: this)) |
487 | if (vt->metaObject() == &QQmlVarForeign::staticMetaObject) |
488 | return true; |
489 | return false; |
490 | } |
491 | #endif |
492 | |
493 | /*! |
494 | Returns the string value of this QJSValue, as defined in |
495 | \l{ECMA-262} section 9.8, "ToString". |
496 | |
497 | Note that if this QJSValue is an object, calling this function |
498 | has side effects on the script engine, since the engine will call |
499 | the object's toString() function (and possibly valueOf()) in an |
500 | attempt to convert the object to a primitive value (possibly |
501 | resulting in an uncaught script exception). |
502 | |
503 | \sa isString() |
504 | */ |
505 | QString QJSValue::toString() const |
506 | { |
507 | if (const QString *string = QJSValuePrivate::asQString(jsval: this)) |
508 | return *string; |
509 | |
510 | return QV4::Value::fromReturnedValue(val: QJSValuePrivate::asReturnedValue(jsval: this)).toQStringNoThrow(); |
511 | } |
512 | |
513 | template<typename T> |
514 | T caughtResult(const QJSValue *v, T (QV4::Value::*convert)() const) |
515 | { |
516 | const T result = (QV4::Value::fromReturnedValue(val: QJSValuePrivate::asReturnedValue(jsval: v)).*convert)(); |
517 | QV4::ExecutionEngine *engine = QJSValuePrivate::engine(jsval: v); |
518 | if (engine && engine->hasException) { |
519 | engine->catchException(); |
520 | return T(); |
521 | } |
522 | return result; |
523 | } |
524 | |
525 | /*! |
526 | Returns the number value of this QJSValue, as defined in |
527 | \l{ECMA-262} section 9.3, "ToNumber". |
528 | |
529 | Note that if this QJSValue is an object, calling this function |
530 | has side effects on the script engine, since the engine will call |
531 | the object's valueOf() function (and possibly toString()) in an |
532 | attempt to convert the object to a primitive value (possibly |
533 | resulting in an uncaught script exception). |
534 | |
535 | \sa isNumber(), toInt(), toUInt() |
536 | */ |
537 | double QJSValue::toNumber() const |
538 | { |
539 | if (const QString *string = QJSValuePrivate::asQString(jsval: this)) |
540 | return RuntimeHelpers::stringToNumber(s: *string); |
541 | |
542 | return caughtResult<double>(v: this, convert: &QV4::Value::toNumber); |
543 | } |
544 | |
545 | /*! |
546 | Returns the boolean value of this QJSValue, using the conversion |
547 | rules described in \l{ECMA-262} section 9.2, "ToBoolean". |
548 | |
549 | Note that if this QJSValue is an object, calling this function |
550 | has side effects on the script engine, since the engine will call |
551 | the object's valueOf() function (and possibly toString()) in an |
552 | attempt to convert the object to a primitive value (possibly |
553 | resulting in an uncaught script exception). |
554 | |
555 | \sa isBool() |
556 | */ |
557 | bool QJSValue::toBool() const |
558 | { |
559 | if (const QString *string = QJSValuePrivate::asQString(jsval: this)) |
560 | return string->size() > 0; |
561 | |
562 | return caughtResult<bool>(v: this, convert: &QV4::Value::toBoolean); |
563 | } |
564 | |
565 | /*! |
566 | Returns the signed 32-bit integer value of this QJSValue, using |
567 | the conversion rules described in \l{ECMA-262} section 9.5, "ToInt32". |
568 | |
569 | Note that if this QJSValue is an object, calling this function |
570 | has side effects on the script engine, since the engine will call |
571 | the object's valueOf() function (and possibly toString()) in an |
572 | attempt to convert the object to a primitive value (possibly |
573 | resulting in an uncaught script exception). |
574 | |
575 | \sa toNumber(), toUInt() |
576 | */ |
577 | qint32 QJSValue::toInt() const |
578 | { |
579 | if (const QString *string = QJSValuePrivate::asQString(jsval: this)) |
580 | return QV4::Value::toInt32(d: RuntimeHelpers::stringToNumber(s: *string)); |
581 | |
582 | return caughtResult<qint32>(v: this, convert: &QV4::Value::toInt32); |
583 | } |
584 | |
585 | /*! |
586 | Returns the unsigned 32-bit integer value of this QJSValue, using |
587 | the conversion rules described in \l{ECMA-262} section 9.6, "ToUint32". |
588 | |
589 | Note that if this QJSValue is an object, calling this function |
590 | has side effects on the script engine, since the engine will call |
591 | the object's valueOf() function (and possibly toString()) in an |
592 | attempt to convert the object to a primitive value (possibly |
593 | resulting in an uncaught script exception). |
594 | |
595 | \sa toNumber(), toInt() |
596 | */ |
597 | quint32 QJSValue::toUInt() const |
598 | { |
599 | if (const QString *string = QJSValuePrivate::asQString(jsval: this)) |
600 | return QV4::Value::toUInt32(d: RuntimeHelpers::stringToNumber(s: *string)); |
601 | |
602 | return caughtResult<quint32>(v: this, convert: &QV4::Value::toUInt32); |
603 | } |
604 | |
605 | /*! |
606 | \overload |
607 | |
608 | Returns toVariant(ConvertJSObjects). |
609 | |
610 | \sa isVariant() |
611 | */ |
612 | QVariant QJSValue::toVariant() const |
613 | { |
614 | return toVariant(behavior: ConvertJSObjects); |
615 | } |
616 | |
617 | /*! |
618 | Returns the QVariant value of this QJSValue, if it can be |
619 | converted to a QVariant; otherwise returns an invalid QVariant. |
620 | Some JavaScript types and objects have native expressions in Qt. |
621 | Those are converted to their native expressions. For example: |
622 | |
623 | \table |
624 | \header \li Input Type \li Result |
625 | \row \li Undefined \li An invalid QVariant. |
626 | \row \li Null \li A QVariant containing a null pointer (QMetaType::Nullptr). |
627 | \row \li Boolean \li A QVariant containing the value of the boolean. |
628 | \row \li Number \li A QVariant containing the value of the number. |
629 | \row \li String \li A QVariant containing the value of the string. |
630 | \row \li QVariant Object \li The result is the QVariant value of the object (no conversion). |
631 | \row \li QObject Object \li A QVariant containing a pointer to the QObject. |
632 | \row \li Date Object \li A QVariant containing the date value (toDateTime()). |
633 | \row \li RegularExpression Object \li A QVariant containing the regular expression value. |
634 | \endtable |
635 | |
636 | For other types the \a behavior parameter is relevant. If |
637 | \c ConvertJSObjects is given, a best effort but possibly lossy conversion is |
638 | attempted. Generic JavaScript objects are converted to QVariantMap. |
639 | JavaScript arrays are converted to QVariantList. Each property or element is |
640 | converted to a QVariant, recursively; cyclic references are not followed. |
641 | JavaScript function objects are dropped. If \c RetainJSObjects is given, the |
642 | QJSValue is wrapped into a QVariant via QVariant::fromValue(). The resulting |
643 | conversion is lossless but the internal structure of the objects is not |
644 | immediately accessible. |
645 | |
646 | \sa isVariant() |
647 | */ |
648 | QVariant QJSValue::toVariant(QJSValue::ObjectConversionBehavior behavior) const |
649 | { |
650 | if (const QString *string = QJSValuePrivate::asQString(jsval: this)) |
651 | return QVariant(*string); |
652 | |
653 | QV4::Value val = QV4::Value::fromReturnedValue(val: QJSValuePrivate::asReturnedValue(jsval: this)); |
654 | if (val.isUndefined()) |
655 | return QVariant(); |
656 | if (val.isNull()) |
657 | return QVariant(QMetaType::fromType<std::nullptr_t>(), nullptr); |
658 | if (val.isBoolean()) |
659 | return QVariant(val.booleanValue()); |
660 | if (val.isInt32()) // Includes doubles that can be losslessly casted to int |
661 | return QVariant(val.integerValue()); |
662 | if (val.isNumber()) |
663 | return QVariant(val.doubleValue()); |
664 | |
665 | Q_ASSERT(val.isManaged()); |
666 | |
667 | if (val.isString()) |
668 | return QVariant(val.toQString()); |
669 | if (val.as<QV4::Managed>()) { |
670 | if (behavior == RetainJSObjects) |
671 | return QV4::ExecutionEngine::toVariant( |
672 | value: val, /*typeHint*/ QMetaType{}, /*createJSValueForObjectsAndSymbols=*/ true); |
673 | else |
674 | return QV4::ExecutionEngine::toVariantLossy(value: val); |
675 | } |
676 | |
677 | Q_ASSERT(false); |
678 | return QVariant(); |
679 | } |
680 | |
681 | /*! |
682 | * Converts the value to a QJSPrimitiveValue. If the value holds a type |
683 | * supported by QJSPrimitiveValue, the value is copied. Otherwise the |
684 | * value is converted to a string, and the string is stored in |
685 | * QJSPrimitiveValue. |
686 | * |
687 | * \note Conversion of a managed value to a string can throw an exception. In |
688 | * particular, symbols cannot be coerced into strings, or a custom |
689 | * toString() method may throw. In this case the result is the undefined |
690 | * value and the engine carries an error after the conversion. |
691 | */ |
692 | QJSPrimitiveValue QJSValue::toPrimitive() const |
693 | { |
694 | if (const QString *string = QJSValuePrivate::asQString(jsval: this)) |
695 | return *string; |
696 | |
697 | const QV4::Value val = QV4::Value::fromReturnedValue(val: QJSValuePrivate::asReturnedValue(jsval: this)); |
698 | return QV4::ExecutionEngine::createPrimitive(v: &val); |
699 | } |
700 | |
701 | /*! |
702 | Calls this QJSValue as a function, passing \a args as arguments |
703 | to the function, and using the globalObject() as the "this"-object. |
704 | Returns the value returned from the function. |
705 | |
706 | If this QJSValue is not callable, call() does nothing and |
707 | returns an undefined QJSValue. |
708 | |
709 | Calling call() can cause an exception to occur in the script engine; |
710 | in that case, call() returns the value that was thrown (typically an |
711 | \c{Error} object). You can call isError() on the return value to |
712 | determine whether an exception occurred. |
713 | |
714 | \sa isCallable(), callWithInstance(), callAsConstructor() |
715 | */ |
716 | QJSValue QJSValue::call(const QJSValueList &args) const |
717 | { |
718 | const FunctionObject *f = QJSValuePrivate::asManagedType<FunctionObject>(jsval: this); |
719 | if (!f) |
720 | return QJSValue(); |
721 | |
722 | QV4::ExecutionEngine *engine = QJSValuePrivate::engine(jsval: this); |
723 | Q_ASSERT(engine); |
724 | |
725 | Scope scope(engine); |
726 | JSCallArguments jsCallData(scope, args.size()); |
727 | *jsCallData.thisObject = engine->globalObject; |
728 | for (int i = 0; i < args.size(); ++i) { |
729 | if (!QJSValuePrivate::checkEngine(e: engine, jsval: args.at(i))) { |
730 | qWarning(msg: "QJSValue::call() failed: cannot call function with argument created in a different engine" ); |
731 | return QJSValue(); |
732 | } |
733 | jsCallData.args[i] = QJSValuePrivate::convertToReturnedValue(e: engine, jsval: args.at(i)); |
734 | } |
735 | |
736 | ScopedValue result(scope, f->call(data: jsCallData)); |
737 | if (engine->hasException) |
738 | result = engine->catchException(); |
739 | if (engine->isInterrupted.loadRelaxed()) |
740 | result = engine->newErrorObject(QStringLiteral("Interrupted" )); |
741 | |
742 | return QJSValuePrivate::fromReturnedValue(d: result->asReturnedValue()); |
743 | } |
744 | |
745 | /*! |
746 | Calls this QJSValue as a function, using \a instance as |
747 | the `this' object in the function call, and passing \a args |
748 | as arguments to the function. Returns the value returned from |
749 | the function. |
750 | |
751 | If this QJSValue is not a function, call() does nothing |
752 | and returns an undefined QJSValue. |
753 | |
754 | Note that if \a instance is not an object, the global object |
755 | (see \l{QJSEngine::globalObject()}) will be used as the |
756 | `this' object. |
757 | |
758 | Calling call() can cause an exception to occur in the script engine; |
759 | in that case, call() returns the value that was thrown (typically an |
760 | \c{Error} object). You can call isError() on the return value to |
761 | determine whether an exception occurred. |
762 | |
763 | \sa call() |
764 | */ |
765 | QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList &args) const |
766 | { |
767 | const FunctionObject *f = QJSValuePrivate::asManagedType<FunctionObject>(jsval: this); |
768 | if (!f) |
769 | return QJSValue(); |
770 | |
771 | QV4::ExecutionEngine *engine = QJSValuePrivate::engine(jsval: this); |
772 | Q_ASSERT(engine); |
773 | Scope scope(engine); |
774 | |
775 | if (!QJSValuePrivate::checkEngine(e: engine, jsval: instance)) { |
776 | qWarning(msg: "QJSValue::call() failed: cannot call function with thisObject created in a different engine" ); |
777 | return QJSValue(); |
778 | } |
779 | |
780 | JSCallArguments jsCallData(scope, args.size()); |
781 | *jsCallData.thisObject = QJSValuePrivate::convertToReturnedValue(e: engine, jsval: instance); |
782 | for (int i = 0; i < args.size(); ++i) { |
783 | if (!QJSValuePrivate::checkEngine(e: engine, jsval: args.at(i))) { |
784 | qWarning(msg: "QJSValue::call() failed: cannot call function with argument created in a different engine" ); |
785 | return QJSValue(); |
786 | } |
787 | jsCallData.args[i] = QJSValuePrivate::convertToReturnedValue(e: engine, jsval: args.at(i)); |
788 | } |
789 | |
790 | ScopedValue result(scope, f->call(data: jsCallData)); |
791 | if (engine->hasException) |
792 | result = engine->catchException(); |
793 | if (engine->isInterrupted.loadRelaxed()) |
794 | result = engine->newErrorObject(QStringLiteral("Interrupted" )); |
795 | |
796 | return QJSValuePrivate::fromReturnedValue(d: result->asReturnedValue()); |
797 | } |
798 | |
799 | /*! |
800 | Creates a new \c{Object} and calls this QJSValue as a |
801 | constructor, using the created object as the `this' object and |
802 | passing \a args as arguments. If the return value from the |
803 | constructor call is an object, then that object is returned; |
804 | otherwise the default constructed object is returned. |
805 | |
806 | If this QJSValue is not a function, callAsConstructor() does |
807 | nothing and returns an undefined QJSValue. |
808 | |
809 | Calling this function can cause an exception to occur in the |
810 | script engine; in that case, the value that was thrown |
811 | (typically an \c{Error} object) is returned. You can call |
812 | isError() on the return value to determine whether an |
813 | exception occurred. |
814 | |
815 | \sa call(), QJSEngine::newObject() |
816 | */ |
817 | QJSValue QJSValue::callAsConstructor(const QJSValueList &args) const |
818 | { |
819 | const FunctionObject *f = QJSValuePrivate::asManagedType<FunctionObject>(jsval: this); |
820 | if (!f) |
821 | return QJSValue(); |
822 | |
823 | QV4::ExecutionEngine *engine = QJSValuePrivate::engine(jsval: this); |
824 | Q_ASSERT(engine); |
825 | |
826 | Scope scope(engine); |
827 | JSCallArguments jsCallData(scope, args.size()); |
828 | for (int i = 0; i < args.size(); ++i) { |
829 | if (!QJSValuePrivate::checkEngine(e: engine, jsval: args.at(i))) { |
830 | qWarning(msg: "QJSValue::callAsConstructor() failed: cannot construct function with argument created in a different engine" ); |
831 | return QJSValue(); |
832 | } |
833 | jsCallData.args[i] = QJSValuePrivate::convertToReturnedValue(e: engine, jsval: args.at(i)); |
834 | } |
835 | |
836 | ScopedValue result(scope, f->callAsConstructor(data: jsCallData)); |
837 | if (engine->hasException) |
838 | result = engine->catchException(); |
839 | if (engine->isInterrupted.loadRelaxed()) |
840 | result = engine->newErrorObject(QStringLiteral("Interrupted" )); |
841 | |
842 | return QJSValuePrivate::fromReturnedValue(d: result->asReturnedValue()); |
843 | } |
844 | |
845 | /*! |
846 | If this QJSValue is an object, returns the internal prototype |
847 | (\c{__proto__} property) of this object; otherwise returns an |
848 | undefined QJSValue. |
849 | |
850 | \sa setPrototype(), isObject() |
851 | */ |
852 | QJSValue QJSValue::prototype() const |
853 | { |
854 | QV4::ExecutionEngine *engine = QJSValuePrivate::engine(jsval: this); |
855 | if (!engine) |
856 | return QJSValue(); |
857 | QV4::Scope scope(engine); |
858 | ScopedObject o(scope, QJSValuePrivate::asManagedType<QV4::Object>(jsval: this)); |
859 | if (!o) |
860 | return QJSValue(); |
861 | ScopedObject p(scope, o->getPrototypeOf()); |
862 | if (!p) |
863 | return QJSValue(NullValue); |
864 | return QJSValuePrivate::fromReturnedValue(d: p.asReturnedValue()); |
865 | } |
866 | |
867 | /*! |
868 | If this QJSValue is an object, sets the internal prototype |
869 | (\c{__proto__} property) of this object to be \a prototype; |
870 | if the QJSValue is null, it sets the prototype to null; |
871 | otherwise does nothing. |
872 | |
873 | The internal prototype should not be confused with the public |
874 | property with name "prototype"; the public prototype is usually |
875 | only set on functions that act as constructors. |
876 | |
877 | \sa prototype(), isObject() |
878 | */ |
879 | void QJSValue::setPrototype(const QJSValue& prototype) |
880 | { |
881 | QV4::ExecutionEngine *engine = QJSValuePrivate::engine(jsval: this); |
882 | if (!engine) |
883 | return; |
884 | Scope scope(engine); |
885 | ScopedObject o(scope, QJSValuePrivate::asReturnedValue(jsval: this)); |
886 | if (!o) |
887 | return; |
888 | QV4::Value val = QV4::Value::fromReturnedValue(val: QJSValuePrivate::asReturnedValue(jsval: &prototype)); |
889 | if (val.isNull()) { |
890 | o->setPrototypeOf(nullptr); |
891 | return; |
892 | } |
893 | |
894 | ScopedObject p(scope, val); |
895 | if (!p) |
896 | return; |
897 | if (o->engine() != p->engine()) { |
898 | qWarning(msg: "QJSValue::setPrototype() failed: cannot set a prototype created in a different engine" ); |
899 | return; |
900 | } |
901 | if (!o->setPrototypeOf(p)) |
902 | qWarning(msg: "QJSValue::setPrototype() failed: cyclic prototype value" ); |
903 | } |
904 | |
905 | /*! |
906 | Assigns the \a other value to this QJSValue. |
907 | |
908 | Note that if \a other is an object (isObject() returns true), |
909 | only a reference to the underlying object will be assigned; |
910 | the object itself will not be copied. |
911 | */ |
912 | QJSValue& QJSValue::operator=(const QJSValue& other) |
913 | { |
914 | if (d == other.d) |
915 | return *this; |
916 | |
917 | QJSValuePrivate::free(jsval: this); |
918 | d = 0; |
919 | |
920 | if (const QString *string = QJSValuePrivate::asQString(jsval: &other)) |
921 | QJSValuePrivate::setString(jsval: this, s: *string); |
922 | else |
923 | // fomReturnedValue is safe, as the QJSValue still has a persistent reference |
924 | QJSValuePrivate::setValue( |
925 | jsval: this, |
926 | v: QV4::Value::fromReturnedValue(val: QJSValuePrivate::asReturnedValue(jsval: &other))); |
927 | |
928 | return *this; |
929 | } |
930 | |
931 | QJSValue::QJSValue(QJSPrimitiveValue &&value) |
932 | { |
933 | switch (value.type()) { |
934 | case QJSPrimitiveValue::Undefined: |
935 | d = QJSValuePrivate::encodeUndefined(); |
936 | return; |
937 | case QJSPrimitiveValue::Null: |
938 | d = QJSValuePrivate::encodeNull(); |
939 | return; |
940 | case QJSPrimitiveValue::Boolean: |
941 | d = QJSValuePrivate::encode(boolValue: value.asBoolean()); |
942 | return; |
943 | case QJSPrimitiveValue::Integer: |
944 | d = QJSValuePrivate::encode(intValue: value.asInteger()); |
945 | return; |
946 | case QJSPrimitiveValue::Double: |
947 | d = QJSValuePrivate::encode(doubleValue: value.asDouble()); |
948 | return; |
949 | case QJSPrimitiveValue::String: |
950 | d = QJSValuePrivate::encode(stringValue: value.asString()); |
951 | return; |
952 | } |
953 | |
954 | Q_UNREACHABLE(); |
955 | } |
956 | |
957 | QJSValue::QJSValue(QJSManagedValue &&value) |
958 | { |
959 | if (!value.d) { |
960 | d = QV4::Encode::undefined(); |
961 | } else if (value.d->isManaged()) { |
962 | // If it's managed, we can adopt the persistent value. |
963 | QJSValuePrivate::adoptPersistentValue(jsval: this, v: value.d); |
964 | value.d = nullptr; |
965 | } else { |
966 | d = QJSValuePrivate::encode(qv4Value: *value.d); |
967 | QV4::PersistentValueStorage::free(v: value.d); |
968 | value.d = nullptr; |
969 | } |
970 | } |
971 | |
972 | static bool js_equal(const QString &string, const QV4::Value &value) |
973 | { |
974 | if (String *s = value.stringValue()) |
975 | return string == s->toQString(); |
976 | if (value.isNumber()) |
977 | return RuntimeHelpers::stringToNumber(s: string) == value.asDouble(); |
978 | if (value.isBoolean()) |
979 | return RuntimeHelpers::stringToNumber(s: string) == double(value.booleanValue()); |
980 | if (QV4::Object *o = value.objectValue()) { |
981 | Scope scope(o->engine()); |
982 | ScopedValue p(scope, RuntimeHelpers::toPrimitive(value, typeHint: PREFERREDTYPE_HINT)); |
983 | return js_equal(string, value: p); |
984 | } |
985 | return false; |
986 | } |
987 | |
988 | /*! |
989 | Returns true if this QJSValue is equal to \a other, otherwise |
990 | returns false. The comparison follows the behavior described in |
991 | \l{ECMA-262} section 11.9.3, "The Abstract Equality Comparison |
992 | Algorithm". |
993 | |
994 | This function can return true even if the type of this QJSValue |
995 | is different from the type of the \a other value; i.e. the |
996 | comparison is not strict. For example, comparing the number 9 to |
997 | the string "9" returns true; comparing an undefined value to a null |
998 | value returns true; comparing a \c{Number} object whose primitive |
999 | value is 6 to a \c{String} object whose primitive value is "6" |
1000 | returns true; and comparing the number 1 to the boolean value |
1001 | \c{true} returns true. If you want to perform a comparison |
1002 | without such implicit value conversion, use strictlyEquals(). |
1003 | |
1004 | Note that if this QJSValue or the \a other value are objects, |
1005 | calling this function has side effects on the script engine, since |
1006 | the engine will call the object's valueOf() function (and possibly |
1007 | toString()) in an attempt to convert the object to a primitive value |
1008 | (possibly resulting in an uncaught script exception). |
1009 | |
1010 | \sa strictlyEquals() |
1011 | */ |
1012 | bool QJSValue::equals(const QJSValue& other) const |
1013 | { |
1014 | // QJSValue stores heap items in persistent values, which already ensures marking |
1015 | // therefore, fromReturnedValue below is safe |
1016 | if (const QString *string = QJSValuePrivate::asQString(jsval: this)) { |
1017 | if (const QString *otherString = QJSValuePrivate::asQString(jsval: &other)) |
1018 | return *string == *otherString; |
1019 | return js_equal(string: *string, value: Value::fromReturnedValue(val: QJSValuePrivate::asReturnedValue(jsval: &other))); |
1020 | } |
1021 | |
1022 | if (const QString *otherString = QJSValuePrivate::asQString(jsval: &other)) |
1023 | return js_equal(string: *otherString, value: Value::fromReturnedValue(val: QJSValuePrivate::asReturnedValue(jsval: this))); |
1024 | |
1025 | return Runtime::CompareEqual::call(Value::fromReturnedValue(val: QJSValuePrivate::asReturnedValue(jsval: this)), |
1026 | Value::fromReturnedValue(val: QJSValuePrivate::asReturnedValue(jsval: &other))); |
1027 | } |
1028 | |
1029 | /*! |
1030 | Returns true if this QJSValue is equal to \a other using strict |
1031 | comparison (no conversion), otherwise returns false. The comparison |
1032 | follows the behavior described in \l{ECMA-262} section 11.9.6, "The |
1033 | Strict Equality Comparison Algorithm". |
1034 | |
1035 | If the type of this QJSValue is different from the type of the |
1036 | \a other value, this function returns false. If the types are equal, |
1037 | the result depends on the type, as shown in the following table: |
1038 | |
1039 | \table |
1040 | \header \li Type \li Result |
1041 | \row \li Undefined \li true |
1042 | \row \li Null \li true |
1043 | \row \li Boolean \li true if both values are true, false otherwise |
1044 | \row \li Number \li false if either value is NaN (Not-a-Number); true if values are equal, false otherwise |
1045 | \row \li String \li true if both values are exactly the same sequence of characters, false otherwise |
1046 | \row \li Object \li true if both values refer to the same object, false otherwise |
1047 | \endtable |
1048 | |
1049 | \sa equals() |
1050 | */ |
1051 | bool QJSValue::strictlyEquals(const QJSValue& other) const |
1052 | { |
1053 | if (const QString *string = QJSValuePrivate::asQString(jsval: this)) { |
1054 | if (const QString *otherString = QJSValuePrivate::asQString(jsval: &other)) |
1055 | return *string == *otherString; |
1056 | if (const String *s = QJSValuePrivate::asManagedType<String>(jsval: &other)) |
1057 | return *string == s->toQString(); |
1058 | return false; |
1059 | } |
1060 | |
1061 | if (const QString *otherString = QJSValuePrivate::asQString(jsval: &other)) { |
1062 | if (const String *s = QJSValuePrivate::asManagedType<String>(jsval: this)) |
1063 | return *otherString == s->toQString(); |
1064 | return false; |
1065 | } |
1066 | |
1067 | // QJSValue stores heap objects persistently, so we can be sure that they'll be marked |
1068 | // thus we can safely use fromReturnedValue |
1069 | return RuntimeHelpers::strictEqual(x: Value::fromReturnedValue(val: QJSValuePrivate::asReturnedValue(jsval: this)), |
1070 | y: Value::fromReturnedValue(val: QJSValuePrivate::asReturnedValue(jsval: &other))); |
1071 | } |
1072 | |
1073 | /*! |
1074 | Returns the value of this QJSValue's property with the given \a name. |
1075 | If no such property exists, an undefined QJSValue is returned. |
1076 | |
1077 | If the property is implemented using a getter function (i.e. has the |
1078 | PropertyGetter flag set), calling property() has side-effects on the |
1079 | script engine, since the getter function will be called (possibly |
1080 | resulting in an uncaught script exception). If an exception |
1081 | occurred, property() returns the value that was thrown (typically |
1082 | an \c{Error} object). |
1083 | |
1084 | To access array elements, use the |
1085 | \l {QJSValue::}{setProperty(quint32 arrayIndex, const QJSValue &value)} |
1086 | overload instead. |
1087 | |
1088 | \sa setProperty(), hasProperty(), QJSValueIterator |
1089 | */ |
1090 | QJSValue QJSValue::property(const QString& name) const |
1091 | { |
1092 | QV4::ExecutionEngine *engine = QJSValuePrivate::engine(jsval: this); |
1093 | if (!engine) |
1094 | return QJSValue(); |
1095 | |
1096 | QV4::Scope scope(engine); |
1097 | ScopedObject o(scope, QJSValuePrivate::asReturnedValue(jsval: this)); |
1098 | if (!o) |
1099 | return QJSValue(); |
1100 | |
1101 | ScopedString s(scope, engine->newString(s: name)); |
1102 | QV4::ScopedValue result(scope, o->get(id: s->toPropertyKey())); |
1103 | if (engine->hasException) |
1104 | result = engine->catchException(); |
1105 | |
1106 | return QJSValuePrivate::fromReturnedValue(d: result->asReturnedValue()); |
1107 | } |
1108 | |
1109 | /*! |
1110 | \overload |
1111 | |
1112 | Returns the property at the given \a arrayIndex. |
1113 | |
1114 | It is possible to access elements in an array in two ways. The first is to |
1115 | use the array index as the property name: |
1116 | |
1117 | \code |
1118 | qDebug() << jsValueArray.property(QLatin1String("4")).toString(); |
1119 | \endcode |
1120 | |
1121 | The second is to use the overload that takes an index: |
1122 | |
1123 | \code |
1124 | qDebug() << jsValueArray.property(4).toString(); |
1125 | \endcode |
1126 | |
1127 | Both of these approaches achieve the same result, except that the latter: |
1128 | |
1129 | \list |
1130 | \li Is easier to use (can use an integer directly) |
1131 | \li Is faster (no conversion to integer) |
1132 | \endlist |
1133 | |
1134 | If this QJSValue is not an Array object, this function behaves |
1135 | as if property() was called with the string representation of \a |
1136 | arrayIndex. |
1137 | */ |
1138 | QJSValue QJSValue::property(quint32 arrayIndex) const |
1139 | { |
1140 | QV4::ExecutionEngine *engine = QJSValuePrivate::engine(jsval: this); |
1141 | if (!engine) |
1142 | return QJSValue(); |
1143 | |
1144 | QV4::Scope scope(engine); |
1145 | ScopedObject o(scope, QJSValuePrivate::asReturnedValue(jsval: this)); |
1146 | if (!o) |
1147 | return QJSValue(); |
1148 | |
1149 | QV4::ScopedValue result(scope, arrayIndex == UINT_MAX ? o->get(name: engine->id_uintMax()) : o->get(idx: arrayIndex)); |
1150 | if (engine->hasException) |
1151 | engine->catchException(); |
1152 | return QJSValuePrivate::fromReturnedValue(d: result->asReturnedValue()); |
1153 | } |
1154 | |
1155 | /*! |
1156 | Sets the value of this QJSValue's property with the given \a name to |
1157 | the given \a value. |
1158 | |
1159 | If this QJSValue is not an object, this function does nothing. |
1160 | |
1161 | If this QJSValue does not already have a property with name \a name, |
1162 | a new property is created. |
1163 | |
1164 | To modify array elements, use the |
1165 | \l {QJSValue::}{setProperty(quint32 arrayIndex, const QJSValue &value)} |
1166 | overload instead. |
1167 | |
1168 | \sa property(), deleteProperty() |
1169 | */ |
1170 | void QJSValue::setProperty(const QString& name, const QJSValue& value) |
1171 | { |
1172 | QV4::ExecutionEngine *engine = QJSValuePrivate::engine(jsval: this); |
1173 | if (!engine) |
1174 | return; |
1175 | Scope scope(engine); |
1176 | |
1177 | ScopedObject o(scope, QJSValuePrivate::asReturnedValue(jsval: this)); |
1178 | if (!o) |
1179 | return; |
1180 | |
1181 | if (!QJSValuePrivate::checkEngine(e: engine, jsval: value)) { |
1182 | qWarning(msg: "QJSValue::setProperty(%s) failed: cannot set value created in a different engine" , name.toUtf8().constData()); |
1183 | return; |
1184 | } |
1185 | |
1186 | ScopedString s(scope, engine->newString(s: name)); |
1187 | QV4::ScopedValue v(scope, QJSValuePrivate::convertToReturnedValue(e: engine, jsval: value)); |
1188 | o->put(id: s->toPropertyKey(), v); |
1189 | if (engine->hasException) |
1190 | engine->catchException(); |
1191 | } |
1192 | |
1193 | /*! |
1194 | \overload |
1195 | |
1196 | Sets the property at the given \a arrayIndex to the given \a value. |
1197 | |
1198 | It is possible to modify elements in an array in two ways. The first is to |
1199 | use the array index as the property name: |
1200 | |
1201 | \code |
1202 | jsValueArray.setProperty(QLatin1String("4"), value); |
1203 | \endcode |
1204 | |
1205 | The second is to use the overload that takes an index: |
1206 | |
1207 | \code |
1208 | jsValueArray.setProperty(4, value); |
1209 | \endcode |
1210 | |
1211 | Both of these approaches achieve the same result, except that the latter: |
1212 | |
1213 | \list |
1214 | \li Is easier to use (can use an integer directly) |
1215 | \li Is faster (no conversion to integer) |
1216 | \endlist |
1217 | |
1218 | If this QJSValue is not an Array object, this function behaves |
1219 | as if setProperty() was called with the string representation of \a |
1220 | arrayIndex. |
1221 | |
1222 | \sa {QJSValue::}{property(quint32 arrayIndex)}, {Working With Arrays} |
1223 | */ |
1224 | void QJSValue::setProperty(quint32 arrayIndex, const QJSValue& value) |
1225 | { |
1226 | QV4::ExecutionEngine *engine = QJSValuePrivate::engine(jsval: this); |
1227 | if (!engine) |
1228 | return; |
1229 | Scope scope(engine); |
1230 | |
1231 | ScopedObject o(scope, QJSValuePrivate::asReturnedValue(jsval: this)); |
1232 | if (!o) |
1233 | return; |
1234 | |
1235 | if (!QJSValuePrivate::checkEngine(e: engine, jsval: value)) { |
1236 | qWarning(msg: "QJSValue::setProperty(%d) failed: cannot set value created in a different engine" , arrayIndex); |
1237 | return; |
1238 | } |
1239 | |
1240 | QV4::ScopedValue v(scope, QJSValuePrivate::convertToReturnedValue(e: engine, jsval: value)); |
1241 | PropertyKey id = arrayIndex != UINT_MAX ? PropertyKey::fromArrayIndex(idx: arrayIndex) : engine->id_uintMax()->propertyKey(); |
1242 | o->put(id, v); |
1243 | if (engine->hasException) |
1244 | engine->catchException(); |
1245 | } |
1246 | |
1247 | /*! |
1248 | Attempts to delete this object's property of the given \a name. |
1249 | Returns true if the property was deleted, otherwise returns false. |
1250 | |
1251 | The behavior of this function is consistent with the JavaScript |
1252 | delete operator. In particular: |
1253 | |
1254 | \list |
1255 | \li Non-configurable properties cannot be deleted. |
1256 | \li This function will return true even if this object doesn't |
1257 | have a property of the given \a name (i.e., non-existent |
1258 | properties are "trivially deletable"). |
1259 | \li If this object doesn't have an own property of the given |
1260 | \a name, but an object in the prototype() chain does, the |
1261 | prototype object's property is not deleted, and this function |
1262 | returns true. |
1263 | \endlist |
1264 | |
1265 | \sa setProperty(), hasOwnProperty() |
1266 | */ |
1267 | bool QJSValue::deleteProperty(const QString &name) |
1268 | { |
1269 | QV4::ExecutionEngine *engine = QJSValuePrivate::engine(jsval: this); |
1270 | if (!engine) |
1271 | return false; |
1272 | |
1273 | Scope scope(engine); |
1274 | ScopedObject o(scope, QJSValuePrivate::asReturnedValue(jsval: this)); |
1275 | if (!o) |
1276 | return false; |
1277 | |
1278 | ScopedString s(scope, engine->newString(s: name)); |
1279 | return o->deleteProperty(id: s->toPropertyKey()); |
1280 | } |
1281 | |
1282 | /*! |
1283 | Returns true if this object has a property of the given \a name, |
1284 | otherwise returns false. |
1285 | |
1286 | \sa property(), hasOwnProperty() |
1287 | */ |
1288 | bool QJSValue::hasProperty(const QString &name) const |
1289 | { |
1290 | QV4::ExecutionEngine *engine = QJSValuePrivate::engine(jsval: this); |
1291 | if (!engine) |
1292 | return false; |
1293 | |
1294 | Scope scope(engine); |
1295 | ScopedObject o(scope, QJSValuePrivate::asReturnedValue(jsval: this)); |
1296 | if (!o) |
1297 | return false; |
1298 | |
1299 | ScopedString s(scope, engine->newString(s: name)); |
1300 | return o->hasProperty(id: s->toPropertyKey()); |
1301 | } |
1302 | |
1303 | /*! |
1304 | Returns true if this object has an own (not prototype-inherited) |
1305 | property of the given \a name, otherwise returns false. |
1306 | |
1307 | \sa property(), hasProperty() |
1308 | */ |
1309 | bool QJSValue::hasOwnProperty(const QString &name) const |
1310 | { |
1311 | QV4::ExecutionEngine *engine = QJSValuePrivate::engine(jsval: this); |
1312 | if (!engine) |
1313 | return false; |
1314 | |
1315 | Scope scope(engine); |
1316 | ScopedObject o(scope, QJSValuePrivate::asReturnedValue(jsval: this)); |
1317 | if (!o) |
1318 | return false; |
1319 | |
1320 | ScopedString s(scope, engine->newIdentifier(text: name)); |
1321 | return o->getOwnProperty(id: s->propertyKey()) != Attr_Invalid; |
1322 | } |
1323 | |
1324 | /*! |
1325 | * If this QJSValue is a QObject, returns the QObject pointer |
1326 | * that the QJSValue represents; otherwise, returns \nullptr. |
1327 | * |
1328 | * If the QObject that this QJSValue wraps has been deleted, |
1329 | * this function returns \nullptr (i.e. it is possible for toQObject() |
1330 | * to return \nullptr even when isQObject() returns true). |
1331 | * |
1332 | * \sa isQObject() |
1333 | */ |
1334 | QObject *QJSValue::toQObject() const |
1335 | { |
1336 | QV4::ExecutionEngine *engine = QJSValuePrivate::engine(jsval: this); |
1337 | if (!engine) |
1338 | return nullptr; |
1339 | QV4::Scope scope(engine); |
1340 | QV4::Scoped<QV4::QObjectWrapper> wrapper(scope, QJSValuePrivate::asReturnedValue(jsval: this)); |
1341 | if (!wrapper) |
1342 | return nullptr; |
1343 | |
1344 | return wrapper->object(); |
1345 | } |
1346 | |
1347 | /*! |
1348 | \since 5.8 |
1349 | |
1350 | * If this QJSValue is a QMetaObject, returns the QMetaObject pointer |
1351 | * that the QJSValue represents; otherwise, returns \nullptr. |
1352 | * |
1353 | * \sa isQMetaObject() |
1354 | */ |
1355 | const QMetaObject *QJSValue::toQMetaObject() const |
1356 | { |
1357 | QV4::ExecutionEngine *engine = QJSValuePrivate::engine(jsval: this); |
1358 | if (!engine) |
1359 | return nullptr; |
1360 | QV4::Scope scope(engine); |
1361 | QV4::Scoped<QV4::QMetaObjectWrapper> wrapper(scope, QJSValuePrivate::asReturnedValue(jsval: this)); |
1362 | if (!wrapper) |
1363 | return nullptr; |
1364 | |
1365 | return wrapper->metaObject(); |
1366 | } |
1367 | |
1368 | |
1369 | /*! |
1370 | Returns a QDateTime representation of this value, in local time. |
1371 | If this QJSValue is not a date, or the value of the date is NaN |
1372 | (Not-a-Number), an invalid QDateTime is returned. |
1373 | |
1374 | \sa isDate() |
1375 | */ |
1376 | QDateTime QJSValue::toDateTime() const |
1377 | { |
1378 | if (const QV4::DateObject *date = QJSValuePrivate::asManagedType<DateObject>(jsval: this)) |
1379 | return date->toQDateTime(); |
1380 | return QDateTime(); |
1381 | } |
1382 | |
1383 | /*! |
1384 | Returns true if this QJSValue is an object of the Date class; |
1385 | otherwise returns false. |
1386 | */ |
1387 | bool QJSValue::isDate() const |
1388 | { |
1389 | return QJSValuePrivate::asManagedType<DateObject>(jsval: this); |
1390 | } |
1391 | |
1392 | /*! |
1393 | Returns true if this QJSValue is an object of the RegExp class; |
1394 | otherwise returns false. |
1395 | */ |
1396 | bool QJSValue::isRegExp() const |
1397 | { |
1398 | return QJSValuePrivate::asManagedType<RegExpObject>(jsval: this); |
1399 | } |
1400 | |
1401 | /*! |
1402 | Returns true if this QJSValue is a QObject; otherwise returns |
1403 | false. |
1404 | |
1405 | Note: This function returns true even if the QObject that this |
1406 | QJSValue wraps has been deleted. |
1407 | |
1408 | \sa toQObject(), QJSEngine::newQObject() |
1409 | */ |
1410 | bool QJSValue::isQObject() const |
1411 | { |
1412 | return QJSValuePrivate::asManagedType<QV4::QObjectWrapper>(jsval: this); |
1413 | } |
1414 | |
1415 | /*! |
1416 | \since 5.8 |
1417 | |
1418 | Returns true if this QJSValue is a QMetaObject; otherwise returns |
1419 | false. |
1420 | |
1421 | \sa toQMetaObject(), QJSEngine::newQMetaObject() |
1422 | */ |
1423 | bool QJSValue::isQMetaObject() const |
1424 | { |
1425 | return QJSValuePrivate::asManagedType<QV4::QMetaObjectWrapper>(jsval: this); |
1426 | } |
1427 | |
1428 | #ifndef QT_NO_DATASTREAM |
1429 | QDataStream &operator<<(QDataStream &stream, const QJSValue &jsv) |
1430 | { |
1431 | quint32 isNullOrUndefined = 0; |
1432 | if (jsv.isNull()) |
1433 | isNullOrUndefined |= 0x1; |
1434 | if (jsv.isUndefined()) |
1435 | isNullOrUndefined |= 0x2; |
1436 | stream << isNullOrUndefined; |
1437 | if (!isNullOrUndefined) { |
1438 | const QVariant v = jsv.toVariant(); |
1439 | switch (v.userType()) { |
1440 | case QMetaType::Bool: |
1441 | case QMetaType::Double: |
1442 | case QMetaType::Int: |
1443 | case QMetaType::QString: |
1444 | v.save(ds&: stream); |
1445 | break; |
1446 | default: |
1447 | qWarning() << "QDataStream::operator<< was to save a non-trivial QJSValue." |
1448 | << "This is not supported anymore, please stream a QVariant instead." ; |
1449 | QVariant().save(ds&: stream); |
1450 | break; |
1451 | } |
1452 | |
1453 | } |
1454 | return stream; |
1455 | } |
1456 | |
1457 | QDataStream &operator>>(QDataStream &stream, QJSValue &jsv) |
1458 | { |
1459 | quint32 isNullOrUndefined; |
1460 | stream >> isNullOrUndefined; |
1461 | |
1462 | if (isNullOrUndefined & 0x1) { |
1463 | jsv = QJSValue(QJSValue::NullValue); |
1464 | } else if (isNullOrUndefined & 0x2) { |
1465 | jsv = QJSValue(); |
1466 | } else { |
1467 | QVariant v; |
1468 | v.load(ds&: stream); |
1469 | |
1470 | switch (v.userType()) { |
1471 | case QMetaType::Bool: |
1472 | jsv = QJSValue(v.toBool()); |
1473 | break; |
1474 | case QMetaType::Double: |
1475 | jsv = QJSValue(v.toDouble()); |
1476 | break; |
1477 | case QMetaType::Int: |
1478 | jsv = QJSValue(v.toInt()); |
1479 | break; |
1480 | case QMetaType::QString: |
1481 | jsv = QJSValue(v.toString()); |
1482 | break; |
1483 | default: |
1484 | qWarning() << "QDataStream::operator>> to restore a non-trivial QJSValue." |
1485 | << "This is not supported anymore, please stream a QVariant instead." ; |
1486 | break; |
1487 | } |
1488 | } |
1489 | return stream; |
1490 | } |
1491 | #endif |
1492 | |
1493 | QT_END_NAMESPACE |
1494 | |