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