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