Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qv4engine.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 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 "qv4engine_p.h"
6
7#include <wtf/BumpPointerAllocator.h>
8#include <wtf/OSAllocator.h>
9#include <wtf/PageAllocation.h>
10
11#include <private/qjsvalue_p.h>
12#include <private/qqmlbuiltinfunctions_p.h>
13#include <private/qqmlengine_p.h>
14#include <private/qqmljsdiagnosticmessage_p.h>
15#include <private/qqmllist_p.h>
16#include <private/qqmllistwrapper_p.h>
17#include <private/qqmlscriptdata_p.h>
18#include <private/qqmltypeloader_p.h>
19#include <private/qqmltypewrapper_p.h>
20#include <private/qqmlvaluetype_p.h>
21#include <private/qqmlvaluetypewrapper_p.h>
22#include <private/qv4argumentsobject_p.h>
23#include <private/qv4arraybuffer_p.h>
24#include <private/qv4arrayiterator_p.h>
25#include <private/qv4arrayobject_p.h>
26#include <private/qv4atomics_p.h>
27#include <private/qv4booleanobject_p.h>
28#include <private/qv4codegen_p.h>
29#include <private/qv4compileddata_p.h>
30#include <private/qv4dataview_p.h>
31#include <private/qv4dateobject_p.h>
32#include <private/qv4debugging_p.h>
33#include <private/qv4errorobject_p.h>
34#include <private/qv4executableallocator_p.h>
35#include <private/qv4function_p.h>
36#include <private/qv4functionobject_p.h>
37#include <private/qv4generatorobject_p.h>
38#include <private/qv4globalobject_p.h>
39#include <private/qv4identifiertable_p.h>
40#include <private/qv4iterator_p.h>
41#include <private/qv4jsonobject_p.h>
42#include <private/qv4mapiterator_p.h>
43#include <private/qv4mapobject_p.h>
44#include <private/qv4mathobject_p.h>
45#include <private/qv4memberdata_p.h>
46#include <private/qv4mm_p.h>
47#include <private/qv4module_p.h>
48#include <private/qv4numberobject_p.h>
49#include <private/qv4object_p.h>
50#include <private/qv4objectiterator_p.h>
51#include <private/qv4objectproto_p.h>
52#include <private/qv4profiling_p.h>
53#include <private/qv4promiseobject_p.h>
54#include <private/qv4proxy_p.h>
55#include <private/qv4qmetaobjectwrapper_p.h>
56#include <private/qv4qmlcontext_p.h>
57#include <private/qv4qobjectwrapper_p.h>
58#include <private/qv4reflect_p.h>
59#include <private/qv4regexp_p.h>
60#include <private/qv4regexpobject_p.h>
61#include <private/qv4runtime_p.h>
62#include <private/qv4sequenceobject_p.h>
63#include <private/qv4setiterator_p.h>
64#include <private/qv4setobject_p.h>
65#include <private/qv4sqlerrors_p.h>
66#include <private/qv4stackframe_p.h>
67#include <private/qv4stacklimits_p.h>
68#include <private/qv4stringiterator_p.h>
69#include <private/qv4stringobject_p.h>
70#include <private/qv4symbol_p.h>
71#include <private/qv4typedarray_p.h>
72#include <private/qv4urlobject_p.h>
73#include <private/qv4value_p.h>
74#include <private/qv4variantassociationobject_p.h>
75#include <private/qv4variantobject_p.h>
76
77#include <QtQml/qqmlfile.h>
78
79#include <QtCore/qdatetime.h>
80#include <QtCore/qdir.h>
81#include <QtCore/qfileinfo.h>
82#include <QtCore/qiterable.h>
83#include <QtCore/qloggingcategory.h>
84#include <QtCore/qmetatype.h>
85#include <QtCore/qsequentialiterable.h>
86#include <QtCore/qtextstream.h>
87#include <QtCore/qtimezone.h>
88
89#if QT_CONFIG(regularexpression)
90#include <QtCore/qregularexpression.h>
91#endif
92#if QT_CONFIG(qml_locale)
93#include <private/qqmllocale_p.h>
94#endif
95#if QT_CONFIG(qml_xml_http_request)
96#include <private/qv4domerrors_p.h>
97#include <private/qqmlxmlhttprequest_p.h>
98#endif
99#ifdef V4_USE_VALGRIND
100#include <valgrind/memcheck.h>
101#endif
102
104
105DEFINE_BOOL_CONFIG_OPTION(disableDiskCache, QML_DISABLE_DISK_CACHE);
107
108using namespace QV4;
109
110// While engineSerial is odd the statics haven't been initialized. The engine that receives ID 1
111// initializes the statics and sets engineSerial to 2 afterwards.
112// Each engine does engineSerial.fetchAndAddOrdered(2) on creation. Therefore engineSerial stays
113// odd while the statics are being initialized, and stays even afterwards.
114// Any further engines created while the statics are being initialized busy-wait until engineSerial
115// is even.
116Q_CONSTINIT static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1);
117Q_CONSTINIT static QBasicAtomicInt hasPreview = Q_BASIC_ATOMIC_INITIALIZER(0);
118int ExecutionEngine::s_maxCallDepth = -1;
119int ExecutionEngine::s_jitCallCountThreshold = 3;
120int ExecutionEngine::s_maxJSStackSize = 4 * 1024 * 1024;
121int ExecutionEngine::s_maxGCStackSize = 2 * 1024 * 1024;
122
123ReturnedValue throwTypeError(const FunctionObject *b, const QV4::Value *, const QV4::Value *, int)
124{
125 return b->engine()->throwTypeError();
126}
127
128
129template <typename ReturnType>
130ReturnType convertJSValueToVariantType(const QJSValue &value)
131{
132 const QVariant variant = value.toVariant();
133 return variant.metaType() == QMetaType::fromType<QJSValue>()
134 ? ReturnType()
135 : variant.value<ReturnType>();
136}
137
142
143namespace {
144void createNewIteratorIfNonExisting(void **iterator) {
145 if (*iterator == nullptr)
146 *iterator = new JSArrayIterator;
147}
148}
149
151{
152 // set up some functions so that non-array QSequentialIterables do not crash
153 // but instead appear as an empty sequence
154
155 using namespace QtMetaContainerPrivate;
156 QMetaSequenceInterface iface;
157 iface.sizeFn = [](const void *) { return qsizetype(0); };
158 iface.valueAtIndexFn = [](const void *, qsizetype, void *) {};
159 iface.createIteratorFn = [](void *, QMetaSequenceInterface::Position) -> void * {
160 return nullptr;
161 };
162 iface.advanceIteratorFn = [](void *, qsizetype) {};
163 iface.compareIteratorFn = [](const void *, const void *) {
164 return true; /*all iterators are nullptr*/
165 };
166 iface.destroyIteratorFn = [](const void *) {};
167 iface.copyIteratorFn = [](void *, const void *) {};
168 iface.diffIteratorFn = [](const void *, const void *) { return qsizetype(0); };
169 return iface;
170}
171
173{
174 using namespace QtMetaContainerPrivate;
175 QMetaSequenceInterface iface;
176 iface.valueMetaType = QtPrivate::qMetaTypeInterfaceForType<QVariant>();
177 iface.iteratorCapabilities = RandomAccessCapability | BiDirectionalCapability | ForwardCapability;
178 iface.addRemoveCapabilities = CanAddAtEnd;
179 iface.sizeFn = [](const void *p) -> qsizetype {
180 return static_cast<QJSValue const *>(p)->property(QString::fromLatin1("length")).toInt();
181 };
182
183 /* Lifetime management notes:
184 * valueAtIndexFn and valueAtIteratorFn return a pointer to a JSValue allocated via
185 * QMetaType::create Because we set QVariantConstructionFlags::ShouldDeleteVariantData,
186 * QSequentialIterable::at and QSequentialIterable::operator*() will free that memory
187 */
188
189 iface.valueAtIndexFn = [](const void *iterable, qsizetype index, void *dataPtr) -> void {
190 auto *data = static_cast<QVariant *>(dataPtr);
191 *data = static_cast<QJSValue const *>(iterable)->property(quint32(index)).toVariant();
192 };
193 iface.createIteratorFn = [](void *iterable, QMetaSequenceInterface::Position pos) {
194 void *iterator = nullptr;
195 createNewIteratorIfNonExisting(&iterator);
196 auto jsArrayIterator = static_cast<JSArrayIterator *>(iterator);
197 jsArrayIterator->index = 0;
198 jsArrayIterator->data = reinterpret_cast<QJSValue const*>(iterable);
199 if (pos == QMetaSequenceInterface::AtEnd) {
200 auto length = static_cast<QJSValue const *>(iterable)->property(
201 QString::fromLatin1("length")).toInt();
202 jsArrayIterator->index = quint32(length);
203 }
204 return iterator;
205 };
206 iface.createConstIteratorFn = [](const void *iterable, QMetaSequenceInterface::Position pos) {
207 void *iterator = nullptr;
208 createNewIteratorIfNonExisting(&iterator);
209 auto jsArrayIterator = static_cast<JSArrayIterator *>(iterator);
210 jsArrayIterator->index = 0;
211 jsArrayIterator->data = reinterpret_cast<QJSValue const*>(iterable);
212 if (pos == QMetaSequenceInterface::AtEnd) {
213 auto length = static_cast<QJSValue const *>(iterable)->property(
214 QString::fromLatin1("length")).toInt();
215 jsArrayIterator->index = quint32(length);
216 }
217 return iterator;
218 };
219 iface.advanceIteratorFn = [](void *iterator, qsizetype advanceBy) {
220 static_cast<JSArrayIterator *>(iterator)->index += quint32(advanceBy);
221 };
222 iface.advanceConstIteratorFn = [](void *iterator, qsizetype advanceBy) {
223 static_cast<JSArrayIterator *>(iterator)->index += quint32(advanceBy);
224 };
225 iface.valueAtIteratorFn = [](const void *iterator, void *dataPtr) -> void {
226 const auto *arrayIterator = static_cast<const JSArrayIterator *>(iterator);
227 const QJSValue *jsArray = arrayIterator->data;
228 auto *data = static_cast<QVariant *>(dataPtr);
229 *data = jsArray->property(arrayIterator->index).toVariant();
230 };
231 iface.valueAtConstIteratorFn = [](const void *iterator, void *dataPtr) -> void {
232 const auto *arrayIterator = static_cast<const JSArrayIterator *>(iterator);
233 const QJSValue *jsArray = arrayIterator->data;
234 auto *data = static_cast<QVariant *>(dataPtr);
235 *data = jsArray->property(arrayIterator->index).toVariant();
236 };
237 iface.destroyIteratorFn = [](const void *iterator) {
238 delete static_cast<const JSArrayIterator *>(iterator);
239 };
240 iface.destroyConstIteratorFn = [](const void *iterator) {
241 delete static_cast<const JSArrayIterator *>(iterator);
242 };
243 iface.compareIteratorFn = [](const void *p, const void *other) {
244 auto this_ = static_cast<const JSArrayIterator *>(p);
245 auto that_ = static_cast<const JSArrayIterator *>(other);
246 return this_->index == that_->index && this_->data == that_->data;
247 };
248 iface.compareConstIteratorFn = [](const void *p, const void *other) {
249 auto this_ = static_cast<const JSArrayIterator *>(p);
250 auto that_ = static_cast<const JSArrayIterator *>(other);
251 return this_->index == that_->index && this_->data == that_->data;
252 };
253 iface.copyIteratorFn = [](void *iterator, const void *otherIterator) {
254 auto *otherIter = (static_cast<JSArrayIterator const *>(otherIterator));
255 static_cast<JSArrayIterator *>(iterator)->index = otherIter->index;
256 static_cast<JSArrayIterator *>(iterator)->data = otherIter->data;
257 };
258 iface.copyConstIteratorFn = [](void *iterator, const void *otherIterator) {
259 auto *otherIter = (static_cast<JSArrayIterator const *>(otherIterator));
260 static_cast<JSArrayIterator *>(iterator)->index = otherIter->index;
261 static_cast<JSArrayIterator *>(iterator)->data = otherIter->data;
262 };
263 iface.diffIteratorFn = [](const void *iterator, const void *otherIterator) -> qsizetype {
264 const auto *self = static_cast<const JSArrayIterator *>(iterator);
265 const auto *other = static_cast<const JSArrayIterator *>(otherIterator);
266 return self->index - other->index;
267 };
268 iface.diffConstIteratorFn = [](const void *iterator, const void *otherIterator) -> qsizetype {
269 const auto *self = static_cast<const JSArrayIterator *>(iterator);
270 const auto *other = static_cast<const JSArrayIterator *>(otherIterator);
271 return self->index - other->index;
272 };
273 iface.addValueFn = [](void *iterable, const void *data, QMetaSequenceInterface::Position) {
274 auto *jsvalue = static_cast<QJSValue *>(iterable);
275 QV4::Scope scope(QJSValuePrivate::engine(jsvalue));
276 QV4::ScopedArrayObject a(scope, QJSValuePrivate::asManagedType<QV4::ArrayObject>(jsvalue));
277 QV4::ScopedValue v(scope, scope.engine->fromVariant(*static_cast<const QVariant *>(data)));
278 if (!a)
279 return;
280 int len = a->getLength();
281 a->setIndexed(len, v, QV4::Object::DoNotThrow);
282 };
283 return iface;
284}
285
286static QSequentialIterable jsvalueToSequence (const QJSValue& value) {
287 using namespace QtMetaTypePrivate;
288 using namespace QtMetaContainerPrivate;
289
290
291 if (!value.isArray()) {
292 static QMetaSequenceInterface emptySequence = emptySequenceInterface();
293 return QSequentialIterable(QMetaSequence(&emptySequence), nullptr);
294 }
295
296 static QMetaSequenceInterface sequence = sequenceInterface();
297 return QSequentialIterable(QMetaSequence(&sequence), &value);
298}
299
300void ExecutionEngine::initializeStaticMembers()
301{
302 bool ok = false;
303
304 const int envMaxJSStackSize = qEnvironmentVariableIntValue("QV4_JS_MAX_STACK_SIZE", &ok);
305 if (ok && envMaxJSStackSize > 0)
306 s_maxJSStackSize = envMaxJSStackSize;
307
308 const int envMaxGCStackSize = qEnvironmentVariableIntValue("QV4_GC_MAX_STACK_SIZE", &ok);
309 if (ok && envMaxGCStackSize > 0)
310 s_maxGCStackSize = envMaxGCStackSize;
311
312 if (qEnvironmentVariableIsSet("QV4_CRASH_ON_STACKOVERFLOW")) {
313 s_maxCallDepth = std::numeric_limits<qint32>::max();
314 } else {
315 ok = false;
316 s_maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok);
317 if (!ok || s_maxCallDepth <= 0)
318 s_maxCallDepth = -1;
319 }
320
321 ok = false;
322 s_jitCallCountThreshold = qEnvironmentVariableIntValue("QV4_JIT_CALL_THRESHOLD", &ok);
323 if (!ok)
324 s_jitCallCountThreshold = 3;
325 if (qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER"))
326 s_jitCallCountThreshold = std::numeric_limits<int>::max();
327
328 qMetaTypeId<QJSValue>();
329 qMetaTypeId<QList<int> >();
330
331 if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantMap>())
332 QMetaType::registerConverter<QJSValue, QVariantMap>(convertJSValueToVariantType<QVariantMap>);
333 if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantHash>())
334 QMetaType::registerConverter<QJSValue, QVariantHash>(convertJSValueToVariantType<QVariantHash>);
335 if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantList>())
336 QMetaType::registerConverter<QJSValue, QVariantList>(convertJSValueToVariantType<QVariantList>);
337 if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QStringList>())
338 QMetaType::registerConverter<QJSValue, QStringList>(convertJSValueToVariantType<QStringList>);
339 if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QSequentialIterable>())
340 QMetaType::registerConverter<QJSValue, QSequentialIterable>(jsvalueToSequence);
341}
342
343ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
344 : executableAllocator(new QV4::ExecutableAllocator)
345 , regExpAllocator(new QV4::ExecutableAllocator)
346 , bumperPointerAllocator(new WTF::BumpPointerAllocator)
347 , jsStack(new WTF::PageAllocation)
348 , gcStack(new WTF::PageAllocation)
349 , publicEngine(jsEngine)
350 , m_engineId(engineSerial.fetchAndAddOrdered(2))
351#if QT_CONFIG(qml_jit)
352 , m_canAllocateExecutableMemory(OSAllocator::canAllocateExecutableMemory())
353#endif
354{
355 if (m_engineId == 1) {
356 initializeStaticMembers();
357 engineSerial.storeRelease(2); // make it even
358 } else if (Q_UNLIKELY(m_engineId & 1)) {
359 // This should be rare. You usually don't create lots of engines at the same time.
360 while (engineSerial.loadAcquire() & 1) {
361 QThread::yieldCurrentThread();
362 }
363 }
364
365 if (s_maxCallDepth < 0) {
366 const StackProperties stack = stackProperties();
367 cppStackBase = stack.base;
368 cppStackLimit = stack.softLimit;
369 } else {
370 callDepth = 0;
371 }
372
373 // We allocate guard pages around our stacks.
374 const size_t guardPages = 2 * WTF::pageSize();
375
376 memoryManager = new QV4::MemoryManager(this);
377 // we don't want to run the gc while the initial setup is not done; not even in aggressive mode
378 GCCriticalSection gcCriticalSection(this);
379 // reserve space for the JS stack
380 // we allow it to grow to a bit more than m_maxJSStackSize, as we can overshoot due to ScopedValues
381 // allocated outside of JIT'ed methods.
382 *jsStack = WTF::PageAllocation::allocate(
383 s_maxJSStackSize + 256*1024 + guardPages, WTF::OSAllocator::JSVMStackPages,
384 /* writable */ true, /* executable */ false, /* includesGuardPages */ true);
385 jsStackBase = (Value *)jsStack->base();
386#ifdef V4_USE_VALGRIND
387 VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, s_maxJSStackSize + 256*1024);
388#endif
389
390 jsStackTop = jsStackBase;
391
392 *gcStack = WTF::PageAllocation::allocate(
393 s_maxGCStackSize + guardPages, WTF::OSAllocator::JSVMStackPages,
394 /* writable */ true, /* executable */ false, /* includesGuardPages */ true);
395
396 exceptionValue = jsAlloca(1);
397 *exceptionValue = Encode::undefined();
398 globalObject = static_cast<Object *>(jsAlloca(1));
399 jsObjects = jsAlloca(NJSObjects);
400 typedArrayPrototype = static_cast<Object *>(jsAlloca(NTypedArrayTypes));
401 typedArrayCtors = static_cast<FunctionObject *>(jsAlloca(NTypedArrayTypes));
402 jsStrings = jsAlloca(NJSStrings);
403 jsSymbols = jsAlloca(NJSSymbols);
404
405 // set up stack limits
406 jsStackLimit = jsStackBase + s_maxJSStackSize/sizeof(Value);
407
408 identifierTable = new IdentifierTable(this);
409
410 memset(classes, 0, sizeof(classes));
411 classes[Class_Empty] = memoryManager->allocIC<InternalClass>();
412 classes[Class_Empty]->init(this);
413
414 classes[Class_MemberData] = classes[Class_Empty]->changeVTable(QV4::MemberData::staticVTable());
415 classes[Class_SimpleArrayData] = classes[Class_Empty]->changeVTable(QV4::SimpleArrayData::staticVTable());
416 classes[Class_SparseArrayData] = classes[Class_Empty]->changeVTable(QV4::SparseArrayData::staticVTable());
417 classes[Class_ExecutionContext] = classes[Class_Empty]->changeVTable(QV4::ExecutionContext::staticVTable());
418 classes[Class_CallContext] = classes[Class_Empty]->changeVTable(QV4::CallContext::staticVTable());
419 classes[Class_QmlContext] = classes[Class_Empty]->changeVTable(QV4::QmlContext::staticVTable());
420
421 Scope scope(this);
422 Scoped<InternalClass> ic(scope);
423 ic = classes[Class_Empty]->changeVTable(QV4::Object::staticVTable());
424 jsObjects[ObjectProto] = memoryManager->allocObject<ObjectPrototype>(ic->d());
425 classes[Class_Object] = ic->changePrototype(objectPrototype()->d());
426 classes[Class_QmlContextWrapper] = classes[Class_Object]->changeVTable(QV4::QQmlContextWrapper::staticVTable());
427
428 ic = newInternalClass(QV4::StringObject::staticVTable(), objectPrototype());
429 jsObjects[StringProto] = memoryManager->allocObject<StringPrototype>(ic->d(), /*init =*/ false);
430 classes[Class_String] = classes[Class_Empty]->changeVTable(QV4::String::staticVTable())->changePrototype(stringPrototype()->d());
431 Q_ASSERT(stringPrototype()->d() && classes[Class_String]->prototype);
432
433 jsObjects[SymbolProto] = memoryManager->allocate<SymbolPrototype>();
434 classes[Class_Symbol] = classes[EngineBase::Class_Empty]->changeVTable(QV4::Symbol::staticVTable())->changePrototype(symbolPrototype()->d());
435
436 jsStrings[String_Empty] = newIdentifier(QString());
437 jsStrings[String_undefined] = newIdentifier(QStringLiteral("undefined"));
438 jsStrings[String_null] = newIdentifier(QStringLiteral("null"));
439 jsStrings[String_true] = newIdentifier(QStringLiteral("true"));
440 jsStrings[String_false] = newIdentifier(QStringLiteral("false"));
441 jsStrings[String_boolean] = newIdentifier(QStringLiteral("boolean"));
442 jsStrings[String_number] = newIdentifier(QStringLiteral("number"));
443 jsStrings[String_string] = newIdentifier(QStringLiteral("string"));
444 jsStrings[String_default] = newIdentifier(QStringLiteral("default"));
445 jsStrings[String_symbol] = newIdentifier(QStringLiteral("symbol"));
446 jsStrings[String_object] = newIdentifier(QStringLiteral("object"));
447 jsStrings[String_function] = newIdentifier(QStringLiteral("function"));
448 jsStrings[String_length] = newIdentifier(QStringLiteral("length"));
449 jsStrings[String_prototype] = newIdentifier(QStringLiteral("prototype"));
450 jsStrings[String_constructor] = newIdentifier(QStringLiteral("constructor"));
451 jsStrings[String_arguments] = newIdentifier(QStringLiteral("arguments"));
452 jsStrings[String_caller] = newIdentifier(QStringLiteral("caller"));
453 jsStrings[String_callee] = newIdentifier(QStringLiteral("callee"));
454 jsStrings[String_this] = newIdentifier(QStringLiteral("this"));
455 jsStrings[String___proto__] = newIdentifier(QStringLiteral("__proto__"));
456 jsStrings[String_enumerable] = newIdentifier(QStringLiteral("enumerable"));
457 jsStrings[String_configurable] = newIdentifier(QStringLiteral("configurable"));
458 jsStrings[String_writable] = newIdentifier(QStringLiteral("writable"));
459 jsStrings[String_value] = newIdentifier(QStringLiteral("value"));
460 jsStrings[String_get] = newIdentifier(QStringLiteral("get"));
461 jsStrings[String_set] = newIdentifier(QStringLiteral("set"));
462 jsStrings[String_eval] = newIdentifier(QStringLiteral("eval"));
463 jsStrings[String_uintMax] = newIdentifier(QStringLiteral("4294967295"));
464 jsStrings[String_name] = newIdentifier(QStringLiteral("name"));
465 jsStrings[String_index] = newIdentifier(QStringLiteral("index"));
466 jsStrings[String_input] = newIdentifier(QStringLiteral("input"));
467 jsStrings[String_toString] = newIdentifier(QStringLiteral("toString"));
468 jsStrings[String_toLocaleString] = newIdentifier(QStringLiteral("toLocaleString"));
469 jsStrings[String_destroy] = newIdentifier(QStringLiteral("destroy"));
470 jsStrings[String_valueOf] = newIdentifier(QStringLiteral("valueOf"));
471 jsStrings[String_byteLength] = newIdentifier(QStringLiteral("byteLength"));
472 jsStrings[String_byteOffset] = newIdentifier(QStringLiteral("byteOffset"));
473 jsStrings[String_buffer] = newIdentifier(QStringLiteral("buffer"));
474 jsStrings[String_lastIndex] = newIdentifier(QStringLiteral("lastIndex"));
475 jsStrings[String_next] = newIdentifier(QStringLiteral("next"));
476 jsStrings[String_done] = newIdentifier(QStringLiteral("done"));
477 jsStrings[String_return] = newIdentifier(QStringLiteral("return"));
478 jsStrings[String_throw] = newIdentifier(QStringLiteral("throw"));
479 jsStrings[String_global] = newIdentifier(QStringLiteral("global"));
480 jsStrings[String_ignoreCase] = newIdentifier(QStringLiteral("ignoreCase"));
481 jsStrings[String_multiline] = newIdentifier(QStringLiteral("multiline"));
482 jsStrings[String_unicode] = newIdentifier(QStringLiteral("unicode"));
483 jsStrings[String_sticky] = newIdentifier(QStringLiteral("sticky"));
484 jsStrings[String_source] = newIdentifier(QStringLiteral("source"));
485 jsStrings[String_flags] = newIdentifier(QStringLiteral("flags"));
486
487 jsSymbols[Symbol_hasInstance] = Symbol::create(this, QStringLiteral("@Symbol.hasInstance"));
488 jsSymbols[Symbol_isConcatSpreadable] = Symbol::create(this, QStringLiteral("@Symbol.isConcatSpreadable"));
489 jsSymbols[Symbol_iterator] = Symbol::create(this, QStringLiteral("@Symbol.iterator"));
490 jsSymbols[Symbol_match] = Symbol::create(this, QStringLiteral("@Symbol.match"));
491 jsSymbols[Symbol_replace] = Symbol::create(this, QStringLiteral("@Symbol.replace"));
492 jsSymbols[Symbol_search] = Symbol::create(this, QStringLiteral("@Symbol.search"));
493 jsSymbols[Symbol_species] = Symbol::create(this, QStringLiteral("@Symbol.species"));
494 jsSymbols[Symbol_split] = Symbol::create(this, QStringLiteral("@Symbol.split"));
495 jsSymbols[Symbol_toPrimitive] = Symbol::create(this, QStringLiteral("@Symbol.toPrimitive"));
496 jsSymbols[Symbol_toStringTag] = Symbol::create(this, QStringLiteral("@Symbol.toStringTag"));
497 jsSymbols[Symbol_unscopables] = Symbol::create(this, QStringLiteral("@Symbol.unscopables"));
498 jsSymbols[Symbol_revokableProxy] = Symbol::create(this, QStringLiteral("@Proxy.revokableProxy"));
499
500 ic = newInternalClass(ArrayPrototype::staticVTable(), objectPrototype());
501 Q_ASSERT(ic->d()->prototype);
502 ic = ic->addMember(id_length()->propertyKey(), Attr_NotConfigurable|Attr_NotEnumerable);
503 Q_ASSERT(ic->d()->prototype);
504 jsObjects[ArrayProto] = memoryManager->allocObject<ArrayPrototype>(ic->d());
505 classes[Class_ArrayObject] = ic->changePrototype(arrayPrototype()->d());
506 jsObjects[PropertyListProto] = memoryManager->allocate<PropertyListPrototype>();
507
508 Scoped<InternalClass> argsClass(scope);
509 argsClass = newInternalClass(ArgumentsObject::staticVTable(), objectPrototype());
510 argsClass = argsClass->addMember(id_length()->propertyKey(), Attr_NotEnumerable);
511 argsClass = argsClass->addMember(symbol_iterator()->propertyKey(), Attr_Data|Attr_NotEnumerable);
512 classes[Class_ArgumentsObject] = argsClass->addMember(id_callee()->propertyKey(), Attr_Data|Attr_NotEnumerable);
513 argsClass = newInternalClass(StrictArgumentsObject::staticVTable(), objectPrototype());
514 argsClass = argsClass->addMember(id_length()->propertyKey(), Attr_NotEnumerable);
515 argsClass = argsClass->addMember(symbol_iterator()->propertyKey(), Attr_Data|Attr_NotEnumerable);
516 classes[Class_StrictArgumentsObject] = argsClass->addMember(id_callee()->propertyKey(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
517
518 *static_cast<Value *>(globalObject) = newObject();
519 Q_ASSERT(globalObject->d()->vtable());
520 initRootContext();
521
522 ic = newInternalClass(QV4::StringObject::staticVTable(), objectPrototype());
523 ic = ic->addMember(id_length()->propertyKey(), Attr_ReadOnly);
524 classes[Class_StringObject] = ic->changePrototype(stringPrototype()->d());
525 Q_ASSERT(classes[Class_StringObject]->verifyIndex(id_length()->propertyKey(), Heap::StringObject::LengthPropertyIndex));
526
527 classes[Class_SymbolObject] = newInternalClass(QV4::SymbolObject::staticVTable(), symbolPrototype());
528
529 jsObjects[NumberProto] = memoryManager->allocate<NumberPrototype>();
530 jsObjects[BooleanProto] = memoryManager->allocate<BooleanPrototype>();
531 jsObjects[DateProto] = memoryManager->allocate<DatePrototype>();
532
533#if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS)
534 InternalClassEntry *index = nullptr;
535#else
536 InternalClassEntry _index;
537 auto *index = &_index;
538#endif
539 ic = newInternalClass(QV4::FunctionPrototype::staticVTable(), objectPrototype());
540 auto addProtoHasInstance = [&] {
541 // Add an invalid prototype slot, so that all function objects have the same layout
542 // This helps speed up instanceof operations and other things where we need to query
543 // prototype property (as we always know it's location)
544 ic = ic->addMember(id_prototype()->propertyKey(), Attr_Invalid, index);
545 Q_ASSERT(index->index == Heap::FunctionObject::Index_Prototype);
546 // add an invalid @hasInstance slot, so that we can quickly track whether the
547 // hasInstance method has been reimplemented. This is required for a fast
548 // instanceof implementation
549 ic = ic->addMember(symbol_hasInstance()->propertyKey(), Attr_Invalid, index);
550 Q_ASSERT(index->index == Heap::FunctionObject::Index_HasInstance);
551 };
552 addProtoHasInstance();
553 jsObjects[FunctionProto] = memoryManager->allocObject<FunctionPrototype>(ic->d());
554 ic = newInternalClass(FunctionObject::staticVTable(), functionPrototype());
555 addProtoHasInstance();
556 classes[Class_FunctionObject] = ic->d();
557 ic = ic->addMember(id_name()->propertyKey(), Attr_ReadOnly, index);
558 Q_ASSERT(index->index == Heap::ArrowFunction::Index_Name);
559 ic = ic->addMember(id_length()->propertyKey(), Attr_ReadOnly_ButConfigurable, index);
560 Q_ASSERT(index->index == Heap::ArrowFunction::Index_Length);
561 classes[Class_ArrowFunction] = ic->changeVTable(ArrowFunction::staticVTable());
562 ic = ic->changeVTable(MemberFunction::staticVTable());
563 classes[Class_MemberFunction] = ic->d();
564 ic = ic->changeVTable(GeneratorFunction::staticVTable());
565 classes[Class_GeneratorFunction] = ic->d();
566 ic = ic->changeVTable(MemberGeneratorFunction::staticVTable());
567 classes[Class_MemberGeneratorFunction] = ic->d();
568
569 ic = ic->changeMember(id_prototype()->propertyKey(), Attr_NotConfigurable|Attr_NotEnumerable);
570 ic = ic->changeVTable(ScriptFunction::staticVTable());
571 classes[Class_ScriptFunction] = ic->d();
572 ic = ic->changeVTable(ConstructorFunction::staticVTable());
573 classes[Class_ConstructorFunction] = ic->d();
574
575 classes[Class_ObjectProto] = classes[Class_Object]->addMember(id_constructor()->propertyKey(), Attr_NotEnumerable, index);
576 Q_ASSERT(index->index == Heap::FunctionObject::Index_ProtoConstructor);
577
578 jsObjects[GeneratorProto] = memoryManager->allocObject<GeneratorPrototype>(classes[Class_Object]);
579 classes[Class_GeneratorObject] = newInternalClass(QV4::GeneratorObject::staticVTable(), generatorPrototype());
580
581 ScopedString str(scope);
582 classes[Class_RegExp] = classes[Class_Empty]->changeVTable(QV4::RegExp::staticVTable());
583 ic = newInternalClass(QV4::RegExpObject::staticVTable(), objectPrototype());
584 ic = ic->addMember(id_lastIndex()->propertyKey(), Attr_NotEnumerable|Attr_NotConfigurable, index);
585 Q_ASSERT(index->index == RegExpObject::Index_LastIndex);
586 jsObjects[RegExpProto] = memoryManager->allocObject<RegExpPrototype>(classes[Class_Object]);
587 classes[Class_RegExpObject] = ic->changePrototype(regExpPrototype()->d());
588
589 ic = classes[Class_ArrayObject]->addMember(id_index()->propertyKey(), Attr_Data, index);
590 Q_ASSERT(index->index == RegExpObject::Index_ArrayIndex);
591 classes[Class_RegExpExecArray] = ic->addMember(id_input()->propertyKey(), Attr_Data, index);
592 Q_ASSERT(index->index == RegExpObject::Index_ArrayInput);
593
594 ic = newInternalClass(ErrorObject::staticVTable(), nullptr);
595 ic = ic->addMember((str = newIdentifier(QStringLiteral("stack")))->propertyKey(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, index);
596 Q_ASSERT(index->index == ErrorObject::Index_Stack);
597 Q_ASSERT(index->setterIndex == ErrorObject::Index_StackSetter);
598 ic = ic->addMember((str = newIdentifier(QStringLiteral("fileName")))->propertyKey(), Attr_Data|Attr_NotEnumerable, index);
599 Q_ASSERT(index->index == ErrorObject::Index_FileName);
600 ic = ic->addMember((str = newIdentifier(QStringLiteral("lineNumber")))->propertyKey(), Attr_Data|Attr_NotEnumerable, index);
601 classes[Class_ErrorObject] = ic->d();
602 Q_ASSERT(index->index == ErrorObject::Index_LineNumber);
603 classes[Class_ErrorObjectWithMessage] = ic->addMember((str = newIdentifier(QStringLiteral("message")))->propertyKey(), Attr_Data|Attr_NotEnumerable, index);
604 Q_ASSERT(index->index == ErrorObject::Index_Message);
605 ic = newInternalClass(Object::staticVTable(), objectPrototype());
606 ic = ic->addMember(id_constructor()->propertyKey(), Attr_Data|Attr_NotEnumerable, index);
607 Q_ASSERT(index->index == ErrorPrototype::Index_Constructor);
608 ic = ic->addMember((str = newIdentifier(QStringLiteral("message")))->propertyKey(), Attr_Data|Attr_NotEnumerable, index);
609 Q_ASSERT(index->index == ErrorPrototype::Index_Message);
610 classes[Class_ErrorProto] = ic->addMember(id_name()->propertyKey(), Attr_Data|Attr_NotEnumerable, index);
611 Q_ASSERT(index->index == ErrorPrototype::Index_Name);
612
613 classes[Class_ProxyObject] = classes[Class_Empty]->changeVTable(ProxyObject::staticVTable());
614 classes[Class_ProxyFunctionObject] = classes[Class_Empty]->changeVTable(ProxyFunctionObject::staticVTable());
615
616 jsObjects[GetStack_Function] = FunctionObject::createBuiltinFunction(this, str = newIdentifier(QStringLiteral("stack")), ErrorObject::method_get_stack, 0);
617
618 jsObjects[ErrorProto] = memoryManager->allocObject<ErrorPrototype>(classes[Class_ErrorProto]);
619 ic = classes[Class_ErrorProto]->changePrototype(errorPrototype()->d());
620 jsObjects[EvalErrorProto] = memoryManager->allocObject<EvalErrorPrototype>(ic->d());
621 jsObjects[RangeErrorProto] = memoryManager->allocObject<RangeErrorPrototype>(ic->d());
622 jsObjects[ReferenceErrorProto] = memoryManager->allocObject<ReferenceErrorPrototype>(ic->d());
623 jsObjects[SyntaxErrorProto] = memoryManager->allocObject<SyntaxErrorPrototype>(ic->d());
624 jsObjects[TypeErrorProto] = memoryManager->allocObject<TypeErrorPrototype>(ic->d());
625 jsObjects[URIErrorProto] = memoryManager->allocObject<URIErrorPrototype>(ic->d());
626
627 jsObjects[VariantProto] = memoryManager->allocate<VariantPrototype>();
628 Q_ASSERT(variantPrototype()->getPrototypeOf() == objectPrototype()->d());
629
630 jsObjects[VariantAssociationProto] = memoryManager->allocate<VariantAssociationPrototype>();
631 Q_ASSERT(variantAssociationPrototype()->getPrototypeOf() == objectPrototype()->d());
632
633 ic = newInternalClass(SequencePrototype::staticVTable(), SequencePrototype::defaultPrototype(this));
634 jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(ic->d()));
635
636 jsObjects[Object_Ctor] = memoryManager->allocate<ObjectCtor>(this);
637 jsObjects[String_Ctor] = memoryManager->allocate<StringCtor>(this);
638 jsObjects[Symbol_Ctor] = memoryManager->allocate<SymbolCtor>(this);
639 jsObjects[Number_Ctor] = memoryManager->allocate<NumberCtor>(this);
640 jsObjects[Boolean_Ctor] = memoryManager->allocate<BooleanCtor>(this);
641 jsObjects[Array_Ctor] = memoryManager->allocate<ArrayCtor>(this);
642 jsObjects[Function_Ctor] = memoryManager->allocate<FunctionCtor>(this);
643 jsObjects[GeneratorFunction_Ctor] = memoryManager->allocate<GeneratorFunctionCtor>(this);
644 jsObjects[Date_Ctor] = memoryManager->allocate<DateCtor>(this);
645 jsObjects[RegExp_Ctor] = memoryManager->allocate<RegExpCtor>(this);
646 jsObjects[Error_Ctor] = memoryManager->allocate<ErrorCtor>(this);
647 jsObjects[EvalError_Ctor] = memoryManager->allocate<EvalErrorCtor>(this);
648 jsObjects[RangeError_Ctor] = memoryManager->allocate<RangeErrorCtor>(this);
649 jsObjects[ReferenceError_Ctor] = memoryManager->allocate<ReferenceErrorCtor>(this);
650 jsObjects[SyntaxError_Ctor] = memoryManager->allocate<SyntaxErrorCtor>(this);
651 jsObjects[TypeError_Ctor] = memoryManager->allocate<TypeErrorCtor>(this);
652 jsObjects[URIError_Ctor] = memoryManager->allocate<URIErrorCtor>(this);
653 jsObjects[IteratorProto] = memoryManager->allocate<IteratorPrototype>();
654
655 ic = newInternalClass(ForInIteratorPrototype::staticVTable(), iteratorPrototype());
656 jsObjects[ForInIteratorProto] = memoryManager->allocObject<ForInIteratorPrototype>(ic);
657 ic = newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype());
658 jsObjects[MapIteratorProto] = memoryManager->allocObject<MapIteratorPrototype>(ic);
659 ic = newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype());
660 jsObjects[SetIteratorProto] = memoryManager->allocObject<SetIteratorPrototype>(ic);
661 ic = newInternalClass(ArrayIteratorPrototype::staticVTable(), iteratorPrototype());
662 jsObjects[ArrayIteratorProto] = memoryManager->allocObject<ArrayIteratorPrototype>(ic);
663 ic = newInternalClass(StringIteratorPrototype::staticVTable(), iteratorPrototype());
664 jsObjects[StringIteratorProto] = memoryManager->allocObject<StringIteratorPrototype>(ic);
665
666 //
667 // url
668 //
669
670 jsObjects[Url_Ctor] = memoryManager->allocate<UrlCtor>(this);
671 jsObjects[UrlProto] = memoryManager->allocate<UrlPrototype>();
672 jsObjects[UrlSearchParams_Ctor] = memoryManager->allocate<UrlSearchParamsCtor>(this);
673 jsObjects[UrlSearchParamsProto] = memoryManager->allocate<UrlSearchParamsPrototype>();
674
675 str = newString(QStringLiteral("get [Symbol.species]"));
676 jsObjects[GetSymbolSpecies] = FunctionObject::createBuiltinFunction(this, str, ArrayPrototype::method_get_species, 0);
677
678 static_cast<ObjectPrototype *>(objectPrototype())->init(this, objectCtor());
679 static_cast<StringPrototype *>(stringPrototype())->init(this, stringCtor());
680 static_cast<SymbolPrototype *>(symbolPrototype())->init(this, symbolCtor());
681 static_cast<NumberPrototype *>(numberPrototype())->init(this, numberCtor());
682 static_cast<BooleanPrototype *>(booleanPrototype())->init(this, booleanCtor());
683 static_cast<ArrayPrototype *>(arrayPrototype())->init(this, arrayCtor());
684 static_cast<PropertyListPrototype *>(propertyListPrototype())->init();
685 static_cast<DatePrototype *>(datePrototype())->init(this, dateCtor());
686 static_cast<FunctionPrototype *>(functionPrototype())->init(this, functionCtor());
687 static_cast<GeneratorPrototype *>(generatorPrototype())->init(this, generatorFunctionCtor());
688 static_cast<RegExpPrototype *>(regExpPrototype())->init(this, regExpCtor());
689 static_cast<ErrorPrototype *>(errorPrototype())->init(this, errorCtor());
690 static_cast<EvalErrorPrototype *>(evalErrorPrototype())->init(this, evalErrorCtor());
691 static_cast<RangeErrorPrototype *>(rangeErrorPrototype())->init(this, rangeErrorCtor());
692 static_cast<ReferenceErrorPrototype *>(referenceErrorPrototype())->init(this, referenceErrorCtor());
693 static_cast<SyntaxErrorPrototype *>(syntaxErrorPrototype())->init(this, syntaxErrorCtor());
694 static_cast<TypeErrorPrototype *>(typeErrorPrototype())->init(this, typeErrorCtor());
695 static_cast<URIErrorPrototype *>(uRIErrorPrototype())->init(this, uRIErrorCtor());
696 static_cast<UrlPrototype *>(urlPrototype())->init(this, urlCtor());
697 static_cast<UrlSearchParamsPrototype *>(urlSearchParamsPrototype())->init(this, urlSearchParamsCtor());
698
699 static_cast<IteratorPrototype *>(iteratorPrototype())->init(this);
700 static_cast<ForInIteratorPrototype *>(forInIteratorPrototype())->init(this);
701 static_cast<MapIteratorPrototype *>(mapIteratorPrototype())->init(this);
702 static_cast<SetIteratorPrototype *>(setIteratorPrototype())->init(this);
703 static_cast<ArrayIteratorPrototype *>(arrayIteratorPrototype())->init(this);
704 static_cast<StringIteratorPrototype *>(stringIteratorPrototype())->init(this);
705
706 static_cast<VariantPrototype *>(variantPrototype())->init();
707
708 sequencePrototype()->cast<SequencePrototype>()->init();
709
710 jsObjects[WeakMap_Ctor] = memoryManager->allocate<WeakMapCtor>(this);
711 jsObjects[WeakMapProto] = memoryManager->allocate<WeakMapPrototype>();
712 static_cast<WeakMapPrototype *>(weakMapPrototype())->init(this, weakMapCtor());
713
714 jsObjects[Map_Ctor] = memoryManager->allocate<MapCtor>(this);
715 jsObjects[MapProto] = memoryManager->allocate<MapPrototype>();
716 static_cast<MapPrototype *>(mapPrototype())->init(this, mapCtor());
717
718 jsObjects[WeakSet_Ctor] = memoryManager->allocate<WeakSetCtor>(this);
719 jsObjects[WeakSetProto] = memoryManager->allocate<WeakSetPrototype>();
720 static_cast<WeakSetPrototype *>(weakSetPrototype())->init(this, weakSetCtor());
721
722 jsObjects[Set_Ctor] = memoryManager->allocate<SetCtor>(this);
723 jsObjects[SetProto] = memoryManager->allocate<SetPrototype>();
724 static_cast<SetPrototype *>(setPrototype())->init(this, setCtor());
725
726 //
727 // promises
728 //
729
730 jsObjects[Promise_Ctor] = memoryManager->allocate<PromiseCtor>(this);
731 jsObjects[PromiseProto] = memoryManager->allocate<PromisePrototype>();
732 static_cast<PromisePrototype *>(promisePrototype())->init(this, promiseCtor());
733
734 // typed arrays
735
736 jsObjects[SharedArrayBuffer_Ctor] = memoryManager->allocate<SharedArrayBufferCtor>(this);
737 jsObjects[SharedArrayBufferProto] = memoryManager->allocate<SharedArrayBufferPrototype>();
738 static_cast<SharedArrayBufferPrototype *>(sharedArrayBufferPrototype())->init(this, sharedArrayBufferCtor());
739
740 jsObjects[ArrayBuffer_Ctor] = memoryManager->allocate<ArrayBufferCtor>(this);
741 jsObjects[ArrayBufferProto] = memoryManager->allocate<ArrayBufferPrototype>();
742 static_cast<ArrayBufferPrototype *>(arrayBufferPrototype())->init(this, arrayBufferCtor());
743
744 jsObjects[DataView_Ctor] = memoryManager->allocate<DataViewCtor>(this);
745 jsObjects[DataViewProto] = memoryManager->allocate<DataViewPrototype>();
746 static_cast<DataViewPrototype *>(dataViewPrototype())->init(this, dataViewCtor());
747 jsObjects[ValueTypeProto] = (Heap::Base *) nullptr;
748 jsObjects[SignalHandlerProto] = (Heap::Base *) nullptr;
749 jsObjects[TypeWrapperProto] = (Heap::Base *) nullptr;
750
751 jsObjects[IntrinsicTypedArray_Ctor] = memoryManager->allocate<IntrinsicTypedArrayCtor>(this);
752 jsObjects[IntrinsicTypedArrayProto] = memoryManager->allocate<IntrinsicTypedArrayPrototype>();
753 static_cast<IntrinsicTypedArrayPrototype *>(intrinsicTypedArrayPrototype())
754 ->init(this, static_cast<IntrinsicTypedArrayCtor *>(intrinsicTypedArrayCtor()));
755
756 for (int i = 0; i < NTypedArrayTypes; ++i) {
757 static_cast<Value &>(typedArrayCtors[i]) = memoryManager->allocate<TypedArrayCtor>(this, Heap::TypedArray::Type(i));
758 static_cast<Value &>(typedArrayPrototype[i]) = memoryManager->allocate<TypedArrayPrototype>(Heap::TypedArray::Type(i));
759 typedArrayPrototype[i].as<TypedArrayPrototype>()->init(this, static_cast<TypedArrayCtor *>(typedArrayCtors[i].as<Object>()));
760 }
761
762 //
763 // set up the global object
764 //
765 rootContext()->d()->activation.set(scope.engine, globalObject->d());
766 Q_ASSERT(globalObject->d()->vtable());
767
768 globalObject->defineDefaultProperty(QStringLiteral("Object"), *objectCtor());
769 globalObject->defineDefaultProperty(QStringLiteral("String"), *stringCtor());
770 globalObject->defineDefaultProperty(QStringLiteral("Symbol"), *symbolCtor());
771 FunctionObject *numberObject = numberCtor();
772 globalObject->defineDefaultProperty(QStringLiteral("Number"), *numberObject);
773 globalObject->defineDefaultProperty(QStringLiteral("Boolean"), *booleanCtor());
774 globalObject->defineDefaultProperty(QStringLiteral("Array"), *arrayCtor());
775 globalObject->defineDefaultProperty(QStringLiteral("Function"), *functionCtor());
776 globalObject->defineDefaultProperty(QStringLiteral("Date"), *dateCtor());
777 globalObject->defineDefaultProperty(QStringLiteral("RegExp"), *regExpCtor());
778 globalObject->defineDefaultProperty(QStringLiteral("Error"), *errorCtor());
779 globalObject->defineDefaultProperty(QStringLiteral("EvalError"), *evalErrorCtor());
780 globalObject->defineDefaultProperty(QStringLiteral("RangeError"), *rangeErrorCtor());
781 globalObject->defineDefaultProperty(QStringLiteral("ReferenceError"), *referenceErrorCtor());
782 globalObject->defineDefaultProperty(QStringLiteral("SyntaxError"), *syntaxErrorCtor());
783 globalObject->defineDefaultProperty(QStringLiteral("TypeError"), *typeErrorCtor());
784 globalObject->defineDefaultProperty(QStringLiteral("URIError"), *uRIErrorCtor());
785 globalObject->defineDefaultProperty(QStringLiteral("Promise"), *promiseCtor());
786 globalObject->defineDefaultProperty(QStringLiteral("URL"), *urlCtor());
787 globalObject->defineDefaultProperty(QStringLiteral("URLSearchParams"), *urlSearchParamsCtor());
788
789 globalObject->defineDefaultProperty(QStringLiteral("SharedArrayBuffer"), *sharedArrayBufferCtor());
790 globalObject->defineDefaultProperty(QStringLiteral("ArrayBuffer"), *arrayBufferCtor());
791 globalObject->defineDefaultProperty(QStringLiteral("DataView"), *dataViewCtor());
792 globalObject->defineDefaultProperty(QStringLiteral("WeakSet"), *weakSetCtor());
793 globalObject->defineDefaultProperty(QStringLiteral("Set"), *setCtor());
794 globalObject->defineDefaultProperty(QStringLiteral("WeakMap"), *weakMapCtor());
795 globalObject->defineDefaultProperty(QStringLiteral("Map"), *mapCtor());
796
797 for (int i = 0; i < NTypedArrayTypes; ++i)
798 globalObject->defineDefaultProperty((str = typedArrayCtors[i].as<FunctionObject>()->name()), typedArrayCtors[i]);
799 ScopedObject o(scope);
800 globalObject->defineDefaultProperty(QStringLiteral("Atomics"), (o = memoryManager->allocate<Atomics>()));
801 globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->allocate<MathObject>()));
802 globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->allocate<JsonObject>()));
803 globalObject->defineDefaultProperty(QStringLiteral("Reflect"), (o = memoryManager->allocate<Reflect>()));
804 globalObject->defineDefaultProperty(QStringLiteral("Proxy"), (o = memoryManager->allocate<Proxy>(this)));
805
806 globalObject->defineReadonlyProperty(QStringLiteral("undefined"), Value::undefinedValue());
807 globalObject->defineReadonlyProperty(QStringLiteral("NaN"), Value::fromDouble(std::numeric_limits<double>::quiet_NaN()));
808 globalObject->defineReadonlyProperty(QStringLiteral("Infinity"), Value::fromDouble(Q_INFINITY));
809
810
811 jsObjects[Eval_Function] = memoryManager->allocate<EvalFunction>(this);
812 globalObject->defineDefaultProperty(QStringLiteral("eval"), *evalFunction());
813
814 // ES6: 20.1.2.12 & 20.1.2.13:
815 // parseInt and parseFloat must be the same FunctionObject on the global &
816 // Number object.
817 {
818 QString piString(QStringLiteral("parseInt"));
819 QString pfString(QStringLiteral("parseFloat"));
820 Scope scope(this);
821 ScopedString pi(scope, newIdentifier(piString));
822 ScopedString pf(scope, newIdentifier(pfString));
823 ScopedFunctionObject parseIntFn(scope, FunctionObject::createBuiltinFunction(this, pi, GlobalFunctions::method_parseInt, 2));
824 ScopedFunctionObject parseFloatFn(scope, FunctionObject::createBuiltinFunction(this, pf, GlobalFunctions::method_parseFloat, 1));
825 globalObject->defineDefaultProperty(piString, parseIntFn);
826 globalObject->defineDefaultProperty(pfString, parseFloatFn);
827 numberObject->defineDefaultProperty(piString, parseIntFn);
828 numberObject->defineDefaultProperty(pfString, parseFloatFn);
829 }
830
831 globalObject->defineDefaultProperty(QStringLiteral("isNaN"), GlobalFunctions::method_isNaN, 1);
832 globalObject->defineDefaultProperty(QStringLiteral("isFinite"), GlobalFunctions::method_isFinite, 1);
833 globalObject->defineDefaultProperty(QStringLiteral("decodeURI"), GlobalFunctions::method_decodeURI, 1);
834 globalObject->defineDefaultProperty(QStringLiteral("decodeURIComponent"), GlobalFunctions::method_decodeURIComponent, 1);
835 globalObject->defineDefaultProperty(QStringLiteral("encodeURI"), GlobalFunctions::method_encodeURI, 1);
836 globalObject->defineDefaultProperty(QStringLiteral("encodeURIComponent"), GlobalFunctions::method_encodeURIComponent, 1);
837 globalObject->defineDefaultProperty(QStringLiteral("escape"), GlobalFunctions::method_escape, 1);
838 globalObject->defineDefaultProperty(QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1);
839
840 ScopedFunctionObject t(
841 scope,
842 memoryManager->allocate<DynamicFunctionObject>(this, nullptr, ::throwTypeError));
843 t->defineReadonlyProperty(id_length(), Value::fromInt32(0));
844 t->setInternalClass(t->internalClass()->cryopreserved());
845 jsObjects[ThrowerObject] = t;
846
847 ScopedProperty pd(scope);
848 pd->value = thrower();
849 pd->set = thrower();
850 functionPrototype()->insertMember(id_caller(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable);
851 functionPrototype()->insertMember(id_arguments(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable);
852
853 QV4::QObjectWrapper::initializeBindings(this);
854
855 m_delayedCallQueue.init(this);
856 isInitialized = true;
857}
858
859ExecutionEngine::~ExecutionEngine()
860{
861#if QT_CONFIG(qml_network)
862 delete networkAccessManager;
863#endif
864 m_typeLoader.reset();
865 qDeleteAll(m_extensionData);
866 delete m_multiplyWrappedQObjects;
867 m_multiplyWrappedQObjects = nullptr;
868 delete identifierTable;
869 delete memoryManager;
870
871 for (const auto &cu : std::as_const(m_compilationUnits)) {
872 Q_ASSERT(cu->engine == this);
873 cu->clear();
874 cu->engine = nullptr;
875 }
876 m_compilationUnits.clear();
877
878 delete bumperPointerAllocator;
879 delete regExpCache;
880 delete regExpAllocator;
881 delete executableAllocator;
882 jsStack->deallocate();
883 delete jsStack;
884 gcStack->deallocate();
885 delete gcStack;
886
887#if QT_CONFIG(qml_xml_http_request)
888 qt_rem_qmlxmlhttprequest(this, m_xmlHttpRequestData);
889 m_xmlHttpRequestData = nullptr;
890#endif
891
892 QQmlMetaType::freeUnusedTypesAndCaches();
893}
894
895#if QT_CONFIG(qml_network)
896QNetworkAccessManager *ExecutionEngine::getNetworkAccessManager()
897{
898 if (!networkAccessManager)
899 networkAccessManager = typeLoader()->createNetworkAccessManager(nullptr);
900 return networkAccessManager;
901}
902#endif
903
904#if QT_CONFIG(qml_debug)
905void ExecutionEngine::setDebugger(Debugging::Debugger *debugger)
906{
907 Q_ASSERT(!m_debugger);
908 m_debugger.reset(debugger);
909}
910
911void ExecutionEngine::setProfiler(Profiling::Profiler *profiler)
912{
913 Q_ASSERT(!m_profiler);
914 m_profiler.reset(profiler);
915}
916
917void ExecutionEngine::setPreviewing(bool enabled)
918{
919 hasPreview.storeRelease(enabled);
920}
921
922#endif // QT_CONFIG(qml_debug)
923
924void ExecutionEngine::initRootContext()
925{
926 Scope scope(this);
927 Scoped<ExecutionContext> r(scope, memoryManager->allocManaged<ExecutionContext>());
928 r->d_unchecked()->init(Heap::ExecutionContext::Type_GlobalContext);
929 r->d()->activation.set(this, globalObject->d());
930 jsObjects[RootContext] = r;
931 jsObjects[ScriptContext] = r;
932 jsObjects[IntegerNull] = Encode((int)0);
933}
934
935Heap::InternalClass *ExecutionEngine::newClass(Heap::InternalClass *other)
936{
937 Heap::InternalClass *ic = memoryManager->allocIC<InternalClass>();
938 ic->init(other);
939 return ic;
940}
941
942Heap::InternalClass *ExecutionEngine::newInternalClass(const VTable *vtable, Object *prototype)
943{
944 Scope scope(this);
945 Scoped<InternalClass> ic(scope, internalClasses(Class_Empty)->changeVTable(vtable));
946 return ic->changePrototype(prototype ? prototype->d() : nullptr);
947}
948
949Heap::Object *ExecutionEngine::newObject()
950{
951 return memoryManager->allocate<Object>();
952}
953
954Heap::Object *ExecutionEngine::newObject(Heap::InternalClass *internalClass)
955{
956 return memoryManager->allocObject<Object>(internalClass);
957}
958
959Heap::String *ExecutionEngine::newString(const QString &s)
960{
961 return memoryManager->allocWithStringData<String>(s.size() * sizeof(QChar), s);
962}
963
964Heap::String *ExecutionEngine::newIdentifier(const QString &text)
965{
966 Scope scope(this);
967 ScopedString s(scope, memoryManager->allocWithStringData<String>(text.size() * sizeof(QChar), text));
968 s->toPropertyKey();
969 return s->d();
970}
971
972Heap::Object *ExecutionEngine::newStringObject(const String *string)
973{
974 return memoryManager->allocate<StringObject>(string);
975}
976
977Heap::Object *ExecutionEngine::newSymbolObject(const Symbol *symbol)
978{
979 return memoryManager->allocObject<SymbolObject>(classes[Class_SymbolObject], symbol);
980}
981
982Heap::Object *ExecutionEngine::newNumberObject(double value)
983{
984 return memoryManager->allocate<NumberObject>(value);
985}
986
987Heap::Object *ExecutionEngine::newBooleanObject(bool b)
988{
989 return memoryManager->allocate<BooleanObject>(b);
990}
991
992Heap::ArrayObject *ExecutionEngine::newArrayObject(int count)
993{
994 Scope scope(this);
995 ScopedArrayObject object(scope, memoryManager->allocate<ArrayObject>());
996
997 if (count) {
998 if (count < 0x1000)
999 object->arrayReserve(count);
1000 object->setArrayLengthUnchecked(count);
1001 }
1002 return object->d();
1003}
1004
1005Heap::ArrayObject *ExecutionEngine::newArrayObject(const Value *values, int length)
1006{
1007 Scope scope(this);
1008 ScopedArrayObject a(scope, memoryManager->allocate<ArrayObject>());
1009
1010 if (length) {
1011 size_t size = sizeof(Heap::ArrayData) + (length-1)*sizeof(Value);
1012 Heap::SimpleArrayData *d = scope.engine->memoryManager->allocManaged<SimpleArrayData>(size);
1013 d->init();
1014 d->type = Heap::ArrayData::Simple;
1015 d->offset = 0;
1016 d->values.alloc = length;
1017 d->values.size = length;
1018 // this doesn't require a write barrier, things will be ok, when the new array data gets inserted into
1019 // the parent object
1020 memcpy(&d->values.values, values, length*sizeof(Value));
1021 a->d()->arrayData.set(this, d);
1022 a->setArrayLengthUnchecked(length);
1023 }
1024 return a->d();
1025}
1026
1027Heap::ArrayObject *ExecutionEngine::newArrayObject(const QStringList &list)
1028{
1029 return memoryManager->allocate<ArrayObject>(list);
1030}
1031
1032Heap::ArrayObject *ExecutionEngine::newArrayObject(Heap::InternalClass *internalClass)
1033{
1034 return memoryManager->allocObject<ArrayObject>(internalClass);
1035}
1036
1037Heap::ArrayBuffer *ExecutionEngine::newArrayBuffer(const QByteArray &array)
1038{
1039 return memoryManager->allocate<ArrayBuffer>(array);
1040}
1041
1042Heap::ArrayBuffer *ExecutionEngine::newArrayBuffer(size_t length)
1043{
1044 return memoryManager->allocate<ArrayBuffer>(length);
1045}
1046
1047Heap::DateObject *ExecutionEngine::newDateObject(double dateTime)
1048{
1049 return memoryManager->allocate<DateObject>(dateTime);
1050}
1051
1052Heap::DateObject *ExecutionEngine::newDateObject(const QDateTime &dateTime)
1053{
1054 return memoryManager->allocate<DateObject>(dateTime);
1055}
1056
1057Heap::DateObject *ExecutionEngine::newDateObject(
1058 QDate date, Heap::Object *parent, int index, uint flags)
1059{
1060 return memoryManager->allocate<DateObject>(
1061 date, parent, index, Heap::ReferenceObject::Flags(flags));
1062}
1063
1064Heap::DateObject *ExecutionEngine::newDateObject(
1065 QTime time, Heap::Object *parent, int index, uint flags)
1066{
1067 return memoryManager->allocate<DateObject>(
1068 time, parent, index, Heap::ReferenceObject::Flags(flags));
1069}
1070
1071Heap::DateObject *ExecutionEngine::newDateObject(
1072 QDateTime dateTime, Heap::Object *parent, int index, uint flags)
1073{
1074 return memoryManager->allocate<DateObject>(
1075 dateTime, parent, index, Heap::ReferenceObject::Flags(flags));
1076}
1077
1078Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags)
1079{
1080 Scope scope(this);
1081 Scoped<RegExp> re(scope, RegExp::create(this, pattern, static_cast<CompiledData::RegExp::Flags>(flags)));
1082 return newRegExpObject(re);
1083}
1084
1085Heap::RegExpObject *ExecutionEngine::newRegExpObject(RegExp *re)
1086{
1087 return memoryManager->allocate<RegExpObject>(re);
1088}
1089
1090#if QT_CONFIG(regularexpression)
1091Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegularExpression &re)
1092{
1093 return memoryManager->allocate<RegExpObject>(re);
1094}
1095#endif
1096
1097Heap::UrlObject *ExecutionEngine::newUrlObject()
1098{
1099 return memoryManager->allocate<UrlObject>();
1100}
1101
1102Heap::UrlObject *ExecutionEngine::newUrlObject(const QUrl &url)
1103{
1104 Scope scope(this);
1105 Scoped<UrlObject> urlObject(scope, newUrlObject());
1106 urlObject->setUrl(url);
1107 return urlObject->d();
1108}
1109
1110Heap::UrlSearchParamsObject *ExecutionEngine::newUrlSearchParamsObject()
1111{
1112 return memoryManager->allocate<UrlSearchParamsObject>();
1113}
1114
1115Heap::Object *ExecutionEngine::newErrorObject(const Value &value)
1116{
1117 return ErrorObject::create<ErrorObject>(this, value, errorCtor());
1118}
1119
1120Heap::Object *ExecutionEngine::newErrorObject(const QString &message)
1121{
1122 return ErrorObject::create<ErrorObject>(this, message);
1123}
1124
1125Heap::Object *ExecutionEngine::newSyntaxErrorObject(const QString &message)
1126{
1127 return ErrorObject::create<SyntaxErrorObject>(this, message);
1128}
1129
1130Heap::Object *ExecutionEngine::newSyntaxErrorObject(const QString &message, const QString &fileName, int line, int column)
1131{
1132 return ErrorObject::create<SyntaxErrorObject>(this, message, fileName, line, column);
1133}
1134
1135
1136Heap::Object *ExecutionEngine::newReferenceErrorObject(const QString &message)
1137{
1138 return ErrorObject::create<ReferenceErrorObject>(this, message);
1139}
1140
1141Heap::Object *ExecutionEngine::newReferenceErrorObject(const QString &message, const QString &fileName, int line, int column)
1142{
1143 return ErrorObject::create<ReferenceErrorObject>(this, message, fileName, line, column);
1144}
1145
1146
1147Heap::Object *ExecutionEngine::newTypeErrorObject(const QString &message)
1148{
1149 return ErrorObject::create<TypeErrorObject>(this, message);
1150}
1151
1152Heap::Object *ExecutionEngine::newRangeErrorObject(const QString &message)
1153{
1154 return ErrorObject::create<RangeErrorObject>(this, message);
1155}
1156
1157Heap::Object *ExecutionEngine::newURIErrorObject(const Value &message)
1158{
1159 return ErrorObject::create<URIErrorObject>(this, message, uRIErrorCtor());
1160}
1161
1162Heap::PromiseObject *ExecutionEngine::newPromiseObject()
1163{
1164 if (!m_reactionHandler) {
1165 m_reactionHandler.reset(new Promise::ReactionHandler);
1166 }
1167
1168 Scope scope(this);
1169 Scoped<PromiseObject> object(scope, memoryManager->allocate<PromiseObject>(this));
1170 return object->d();
1171}
1172
1173Heap::Object *ExecutionEngine::newPromiseObject(const QV4::FunctionObject *thisObject, const QV4::PromiseCapability *capability)
1174{
1175 if (!m_reactionHandler) {
1176 m_reactionHandler.reset(new Promise::ReactionHandler);
1177 }
1178
1179 Scope scope(this);
1180 Scoped<CapabilitiesExecutorWrapper> executor(scope, memoryManager->allocate<CapabilitiesExecutorWrapper>());
1181 executor->d()->capabilities.set(this, capability->d());
1182 executor->insertMember(id_length(), Primitive::fromInt32(2), Attr_NotWritable|Attr_NotEnumerable);
1183
1184 ScopedObject object(scope, thisObject->callAsConstructor(executor, 1));
1185 return object->d();
1186}
1187
1188Promise::ReactionHandler *ExecutionEngine::getPromiseReactionHandler()
1189{
1190 Q_ASSERT(m_reactionHandler);
1191 return m_reactionHandler.data();
1192}
1193
1194Heap::Object *ExecutionEngine::newURIErrorObject(const QString &message)
1195{
1196 return ErrorObject::create<URIErrorObject>(this, message);
1197}
1198
1199Heap::Object *ExecutionEngine::newEvalErrorObject(const QString &message)
1200{
1201 return ErrorObject::create<EvalErrorObject>(this, message);
1202}
1203
1204Heap::Object *ExecutionEngine::newVariantObject(const QMetaType type, const void *data)
1205{
1206 return memoryManager->allocate<VariantObject>(type, data);
1207}
1208
1209Heap::Object *ExecutionEngine::newForInIteratorObject(Object *o)
1210{
1211 Scope scope(this);
1212 ScopedObject obj(scope, memoryManager->allocate<ForInIteratorObject>(o));
1213 return obj->d();
1214}
1215
1216Heap::Object *ExecutionEngine::newMapIteratorObject(Object *o)
1217{
1218 return memoryManager->allocate<MapIteratorObject>(o->d(), this);
1219}
1220
1221Heap::Object *ExecutionEngine::newSetIteratorObject(Object *o)
1222{
1223 return memoryManager->allocate<SetIteratorObject>(o->d(), this);
1224}
1225
1226Heap::Object *ExecutionEngine::newArrayIteratorObject(Object *o)
1227{
1228 return memoryManager->allocate<ArrayIteratorObject>(o->d(), this);
1229}
1230
1231Heap::QmlContext *ExecutionEngine::qmlContext() const
1232{
1233 return currentStackFrame
1234 ? static_cast<Heap::QmlContext *>(qmlContext(currentContext()->d()))
1235 : nullptr;
1236}
1237
1238QObject *ExecutionEngine::qmlScopeObject() const
1239{
1240 Heap::QmlContext *ctx = qmlContext();
1241 if (!ctx)
1242 return nullptr;
1243
1244 return ctx->qml()->scopeObject;
1245}
1246
1247QQmlRefPointer<QQmlContextData> ExecutionEngine::callingQmlContext() const
1248{
1249 Heap::QmlContext *ctx = qmlContext();
1250 if (!ctx)
1251 return nullptr;
1252
1253 return ctx->qml()->context;
1254}
1255
1256StackTrace ExecutionEngine::stackTrace(int frameLimit) const
1257{
1258 StackTrace stack;
1259
1260 CppStackFrame *f = currentStackFrame;
1261 while (f && frameLimit) {
1262 QV4::StackFrame frame;
1263 frame.source = f->source();
1264 frame.function = f->function();
1265 frame.line = f->lineNumber();
1266
1267 stack.append(frame);
1268 if (f->isJSTypesFrame()) {
1269 if (static_cast<JSTypesStackFrame *>(f)->isTailCalling()) {
1270 QV4::StackFrame frame;
1271 frame.function = QStringLiteral("[elided tail calls]");
1272 stack.append(frame);
1273 }
1274 }
1275 --frameLimit;
1276 f = f->parentFrame();
1277 }
1278
1279 return stack;
1280}
1281
1282/* Helper and "C" linkage exported function to format a GDBMI stacktrace for
1283 * invocation by a debugger.
1284 * Sample GDB invocation: print qt_v4StackTrace((void*)0x7fffffffb290)
1285 * Sample CDB invocation: .call Qt5Qmld!qt_v4StackTrace(0x7fffffffb290) ; gh
1286 * Note: The helper is there to suppress MSVC warning 4190 about anything
1287 * with UDT return types in a "C" linkage function. */
1288
1289static inline char *v4StackTrace(const ExecutionContext *context)
1290{
1291 QString result;
1292 QTextStream str(&result);
1293 str << "stack=[";
1294 if (context && context->engine()) {
1295 const QVector<StackFrame> stackTrace = context->engine()->stackTrace(20);
1296 for (int i = 0; i < stackTrace.size(); ++i) {
1297 if (i)
1298 str << ',';
1299 const QUrl url(stackTrace.at(i).source);
1300 const QString fileName = url.isLocalFile() ? url.toLocalFile() : url.toString();
1301 str << "frame={level=\"" << i << "\",func=\"" << stackTrace.at(i).function
1302 << "\",file=\"" << fileName << "\",fullname=\"" << fileName
1303 << "\",line=\"" << qAbs(stackTrace.at(i).line) << "\",language=\"js\"}";
1304 }
1305 }
1306 str << ']';
1307 return qstrdup(result.toLocal8Bit().constData());
1308}
1309
1310extern "C" Q_QML_EXPORT char *qt_v4StackTrace(void *executionContext)
1311{
1312 return v4StackTrace(reinterpret_cast<const ExecutionContext *>(executionContext));
1313}
1314
1315extern "C" Q_QML_EXPORT char *qt_v4StackTraceForEngine(void *executionEngine)
1316{
1317 auto engine = (reinterpret_cast<const ExecutionEngine *>(executionEngine));
1318 return v4StackTrace(engine->currentContext());
1319}
1320
1321QUrl ExecutionEngine::resolvedUrl(const QString &file)
1322{
1323 QUrl src(file);
1324 if (!src.isRelative())
1325 return src;
1326
1327 QUrl base;
1328 CppStackFrame *f = currentStackFrame;
1329 while (f) {
1330 if (f->v4Function) {
1331 base = f->v4Function->finalUrl();
1332 break;
1333 }
1334 f = f->parentFrame();
1335 }
1336
1337 if (base.isEmpty() && globalCode)
1338 base = globalCode->finalUrl();
1339
1340 if (base.isEmpty())
1341 return src;
1342
1343 return base.resolved(src);
1344}
1345
1346void ExecutionEngine::markObjects(MarkStack *markStack)
1347{
1348 for (int i = 0; i < NClasses; ++i) {
1349 if (Heap::InternalClass *c = classes[i])
1350 c->mark(markStack);
1351 }
1352
1353 identifierTable->markObjects(markStack);
1354
1355 for (const auto &compilationUnit : std::as_const(m_compilationUnits))
1356 compilationUnit->markObjects(markStack);
1357}
1358
1359ReturnedValue ExecutionEngine::throwError(const Value &value)
1360{
1361 // we can get in here with an exception already set, as the runtime
1362 // doesn't check after every operation that can throw.
1363 // in this case preserve the first exception to give correct error
1364 // information
1365 if (hasException)
1366 return Encode::undefined();
1367
1368 hasException = true;
1369 *exceptionValue = value;
1370 QV4::Scope scope(this);
1371 QV4::Scoped<ErrorObject> error(scope, value);
1372 if (!!error)
1373 exceptionStackTrace = *error->d()->stackTrace;
1374 else
1375 exceptionStackTrace = stackTrace();
1376
1377 if (QV4::Debugging::Debugger *debug = debugger())
1378 debug->aboutToThrow();
1379
1380 return Encode::undefined();
1381}
1382
1383ReturnedValue ExecutionEngine::catchException(StackTrace *trace)
1384{
1385 Q_ASSERT(hasException);
1386 if (trace)
1387 *trace = exceptionStackTrace;
1388 exceptionStackTrace.clear();
1389 hasException = false;
1390 ReturnedValue res = exceptionValue->asReturnedValue();
1391 *exceptionValue = Value::emptyValue();
1392 return res;
1393}
1394
1395ReturnedValue ExecutionEngine::throwError(const QString &message)
1396{
1397 Scope scope(this);
1398 ScopedValue v(scope, newString(message));
1399 v = newErrorObject(v);
1400 return throwError(v);
1401}
1402
1403ReturnedValue ExecutionEngine::throwSyntaxError(const QString &message, const QString &fileName, int line, int column)
1404{
1405 Scope scope(this);
1406 ScopedObject error(scope, newSyntaxErrorObject(message, fileName, line, column));
1407 return throwError(error);
1408}
1409
1410ReturnedValue ExecutionEngine::throwSyntaxError(const QString &message)
1411{
1412 Scope scope(this);
1413 ScopedObject error(scope, newSyntaxErrorObject(message));
1414 return throwError(error);
1415}
1416
1417
1418ReturnedValue ExecutionEngine::throwTypeError()
1419{
1420 Scope scope(this);
1421 ScopedObject error(scope, newTypeErrorObject(QStringLiteral("Type error")));
1422 return throwError(error);
1423}
1424
1425ReturnedValue ExecutionEngine::throwTypeError(const QString &message)
1426{
1427 Scope scope(this);
1428 ScopedObject error(scope, newTypeErrorObject(message));
1429 return throwError(error);
1430}
1431
1432ReturnedValue ExecutionEngine::throwReferenceError(const QString &name)
1433{
1434 Scope scope(this);
1435 QString msg = name + QLatin1String(" is not defined");
1436 ScopedObject error(scope, newReferenceErrorObject(msg));
1437 return throwError(error);
1438}
1439
1440ReturnedValue ExecutionEngine::throwReferenceError(const Value &value)
1441{
1442 Scope scope(this);
1443 ScopedString s(scope, value.toString(this));
1444 QString msg = s->toQString() + QLatin1String(" is not defined");
1445 ScopedObject error(scope, newReferenceErrorObject(msg));
1446 return throwError(error);
1447}
1448
1449ReturnedValue ExecutionEngine::throwReferenceError(const QString &message, const QString &fileName, int line, int column)
1450{
1451 Scope scope(this);
1452 QString msg = message;
1453 ScopedObject error(scope, newReferenceErrorObject(msg, fileName, line, column));
1454 return throwError(error);
1455}
1456
1457ReturnedValue ExecutionEngine::throwRangeError(const QString &message)
1458{
1459 Scope scope(this);
1460 ScopedObject error(scope, newRangeErrorObject(message));
1461 return throwError(error);
1462}
1463
1464ReturnedValue ExecutionEngine::throwRangeError(const Value &value)
1465{
1466 Scope scope(this);
1467 ScopedString s(scope, value.toString(this));
1468 QString msg = s->toQString() + QLatin1String(" out of range");
1469 ScopedObject error(scope, newRangeErrorObject(msg));
1470 return throwError(error);
1471}
1472
1473ReturnedValue ExecutionEngine::throwURIError(const Value &msg)
1474{
1475 Scope scope(this);
1476 ScopedObject error(scope, newURIErrorObject(msg));
1477 return throwError(error);
1478}
1479
1480ReturnedValue ExecutionEngine::throwUnimplemented(const QString &message)
1481{
1482 Scope scope(this);
1483 ScopedValue v(scope, newString(QLatin1String("Unimplemented ") + message));
1484 v = newErrorObject(v);
1485 return throwError(v);
1486}
1487
1488
1489QQmlError ExecutionEngine::catchExceptionAsQmlError()
1490{
1491 QV4::StackTrace trace;
1492 QV4::Scope scope(this);
1493 QV4::ScopedValue exception(scope, catchException(&trace));
1494 QQmlError error;
1495 if (!trace.isEmpty()) {
1496 QV4::StackFrame frame = trace.constFirst();
1497 error.setUrl(QUrl(frame.source));
1498 error.setLine(qAbs(frame.line));
1499 error.setColumn(frame.column);
1500 }
1501 QV4::Scoped<QV4::ErrorObject> errorObj(scope, exception);
1502 error.setDescription(exception->toQStringNoThrow());
1503 return error;
1504}
1505
1506void ExecutionEngine::amendException()
1507{
1508 const int missingLineNumber = currentStackFrame->missingLineNumber();
1509 const int lineNumber = currentStackFrame->lineNumber();
1510 Q_ASSERT(missingLineNumber != lineNumber);
1511
1512 auto amendStackTrace = [&](QV4::StackTrace *stackTrace) {
1513 for (auto it = stackTrace->begin(), end = stackTrace->end(); it != end; ++it) {
1514 if (it->line == missingLineNumber) {
1515 it->line = lineNumber;
1516 break;
1517 }
1518 }
1519 };
1520
1521 amendStackTrace(&exceptionStackTrace);
1522
1523 QV4::Scope scope(this);
1524 QV4::Scoped<QV4::ErrorObject> error(scope, *exceptionValue);
1525 if (error) // else some other value was thrown
1526 amendStackTrace(error->d()->stackTrace);
1527}
1528
1529// Variant conversion code
1530
1533static QVariant toVariant(
1534 const QV4::Value &value, QMetaType typeHint, JSToQVariantConversionBehavior conversionBehavior,
1535 V4ObjectSet *visitedObjects);
1536static QObject *qtObjectFromJS(const QV4::Value &value);
1537static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObjects = nullptr,
1539static bool convertToNativeQObject(const QV4::Value &value, QMetaType targetType, void **result);
1540
1541static QVariant toVariant(const QV4::Value &value, QMetaType metaType, JSToQVariantConversionBehavior conversionBehavior,
1542 V4ObjectSet *visitedObjects)
1543{
1544 Q_ASSERT (!value.isEmpty());
1545
1546 if (const QV4::VariantObject *v = value.as<QV4::VariantObject>())
1547 return v->d()->data();
1548
1549 if (metaType == QMetaType::fromType<bool>())
1550 return QVariant(value.toBoolean());
1551
1552 if (metaType == QMetaType::fromType<double>())
1553 return QVariant(value.toNumber());
1554
1555 if (metaType == QMetaType::fromType<float>())
1556 return QVariant(float(value.toNumber()));
1557
1558 if (metaType == QMetaType::fromType<QJsonValue>())
1559 return QVariant::fromValue(QV4::JsonObject::toJsonValue(value));
1560
1561 if (metaType == QMetaType::fromType<QJSValue>())
1562 return QVariant::fromValue(QJSValuePrivate::fromReturnedValue(value.asReturnedValue()));
1563
1564 if (const QV4::Object *o = value.as<QV4::Object>()) {
1565 QV4::Scope scope(o->engine());
1566 QV4::ScopedObject object(scope, o);
1567 if (metaType == QMetaType::fromType<QJsonObject>()
1568 && !value.as<ArrayObject>() && !value.as<FunctionObject>()) {
1569 return QVariant::fromValue(QV4::JsonObject::toJsonObject(object));
1570 } else if (QV4::QObjectWrapper *wrapper = object->as<QV4::QObjectWrapper>()) {
1571 return QVariant::fromValue<QObject *>(wrapper->object());
1572 } else if (object->as<QV4::QQmlContextWrapper>()) {
1573 return QVariant();
1574 } else if (QV4::QQmlTypeWrapper *w = object->as<QV4::QQmlTypeWrapper>()) {
1575 return w->toVariant();
1576 } else if (QV4::QQmlValueTypeWrapper *v = object->as<QV4::QQmlValueTypeWrapper>()) {
1577 return v->toVariant();
1578 } else if (QV4::QmlListWrapper *l = object->as<QV4::QmlListWrapper>()) {
1579 return l->toVariant();
1580 } else if (QV4::Sequence *s = object->as<QV4::Sequence>()) {
1581 if (metaType.isValid()
1582 && metaType != QMetaType::fromType<QVariant>()
1583 && metaType != s->d()->listType()) {
1584 // If we can, produce an accurate result.
1585 const QVariant result = QV4::SequencePrototype::toVariant(value, metaType);
1586 if (result.isValid())
1587 return result;
1588 }
1589
1590 // Otherwise produce the "natural" type of the sequence.
1591 return QV4::SequencePrototype::toVariant(s);
1592 } else if (auto association = object->as<QV4::VariantAssociationObject>()) {
1593 return association->d()->toVariant();
1594 }
1595 }
1596
1597 if (const QV4::ArrayObject *o = value.as<ArrayObject>()) {
1598 QV4::Scope scope(o->engine());
1599 QV4::ScopedArrayObject a(scope, o);
1600 if (metaType == QMetaType::fromType<QList<QObject *>>()) {
1601 QList<QObject *> list;
1602 uint length = a->getLength();
1603 QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope);
1604 for (uint ii = 0; ii < length; ++ii) {
1605 qobjectWrapper = a->get(ii);
1606 if (!!qobjectWrapper) {
1607 list << qobjectWrapper->object();
1608 } else {
1609 list << 0;
1610 }
1611 }
1612
1613 return QVariant::fromValue<QList<QObject*> >(list);
1614 } else if (metaType == QMetaType::fromType<QJsonArray>()) {
1615 return QVariant::fromValue(QV4::JsonObject::toJsonArray(a));
1616 }
1617
1618 QVariant retn = QV4::SequencePrototype::toVariant(value, metaType);
1619 if (retn.isValid())
1620 return retn;
1621 }
1622
1623 if (value.isUndefined())
1624 return QVariant();
1625 if (value.isNull())
1626 return QVariant::fromValue(nullptr);
1627 if (value.isBoolean())
1628 return value.booleanValue();
1629 if (value.isInteger())
1630 return value.integerValue();
1631 if (value.isNumber())
1632 return value.asDouble();
1633 if (String *s = value.stringValue()) {
1634 const QString &str = s->toQString();
1635 // QChars are stored as a strings
1636 if (metaType == QMetaType::fromType<QChar>() && str.size() == 1)
1637 return str.at(0);
1638 return str;
1639 }
1640 if (const QV4::DateObject *d = value.as<DateObject>()) {
1641 // NOTE: since we convert QTime to JS Date,
1642 // round trip will change the variant type (to QDateTime)!
1643
1644 if (metaType == QMetaType::fromType<QDate>())
1645 return DateObject::dateTimeToDate(d->toQDateTime());
1646
1647 if (metaType == QMetaType::fromType<QTime>())
1648 return d->toQDateTime().time();
1649
1650 if (metaType == QMetaType::fromType<QString>())
1651 return d->toString();
1652
1653 return d->toQDateTime();
1654 }
1655 if (const QV4::UrlObject *d = value.as<UrlObject>())
1656 return d->toQUrl();
1657 if (const ArrayBuffer *d = value.as<ArrayBuffer>())
1658 return d->asByteArray();
1659 if (const Symbol *symbol = value.as<Symbol>()) {
1660 return conversionBehavior == JSToQVariantConversionBehavior::Never
1661 ? QVariant::fromValue(QJSValuePrivate::fromReturnedValue(symbol->asReturnedValue()))
1662 : symbol->descriptiveString();
1663 }
1664
1665 const QV4::Object *object = value.as<QV4::Object>();
1666 Q_ASSERT(object);
1667 QV4::Scope scope(object->engine());
1668 QV4::ScopedObject o(scope, object);
1669
1670#if QT_CONFIG(regularexpression)
1671 if (QV4::RegExpObject *re = o->as<QV4::RegExpObject>())
1672 return re->toQRegularExpression();
1673#endif
1674
1675 if (metaType.isValid() && !(metaType.flags() & QMetaType::PointerToQObject)) {
1676 const QVariant result
1677 = QQmlValueTypeProvider::createValueType(value, metaType, scope.engine);
1678 if (result.isValid())
1679 return result;
1680 }
1681
1682 if (conversionBehavior == JSToQVariantConversionBehavior::Never)
1683 return QVariant::fromValue(QJSValuePrivate::fromReturnedValue(o->asReturnedValue()));
1684
1685 return objectToVariant(o, visitedObjects, conversionBehavior);
1686}
1687
1688QVariant ExecutionEngine::toVariantLossy(const Value &value)
1689{
1690 return ::toVariant(value, QMetaType(), JSToQVariantConversionBehavior::Aggressive, nullptr);
1691}
1692
1693QVariant ExecutionEngine::toVariant(
1694 const Value &value, QMetaType typeHint, bool createJSValueForObjectsAndSymbols)
1695{
1696 auto behavior = createJSValueForObjectsAndSymbols ? JSToQVariantConversionBehavior::Never
1697 : JSToQVariantConversionBehavior::Safish;
1698 return ::toVariant(value, typeHint, behavior, nullptr);
1699}
1700
1701template<typename Association>
1703 const QV4::Object *o, V4ObjectSet *visitedObjects,
1704 JSToQVariantConversionBehavior conversionBehvior)
1705{
1706 Association association;
1707 QV4::Scope scope(o->engine());
1708 QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly);
1709 QV4::ScopedValue name(scope);
1710 QV4::ScopedValue val(scope);
1711 while (1) {
1712 name = it.nextPropertyNameAsString(val);
1713 if (name->isNull())
1714 break;
1715
1716 QString key = name->toQStringNoThrow();
1717 association.insert(key, ::toVariant(
1718 val, /*type hint*/ QMetaType {},
1719 conversionBehvior, visitedObjects));
1720 }
1721 return association;
1722}
1723
1724static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObjects,
1725 JSToQVariantConversionBehavior conversionBehvior)
1726{
1727 Q_ASSERT(o);
1728
1729 V4ObjectSet recursionGuardSet;
1730 if (!visitedObjects) {
1731 visitedObjects = &recursionGuardSet;
1732 } else if (visitedObjects->contains(o->d())) {
1733 // Avoid recursion.
1734 // For compatibility with QVariant{List,Map} conversion, we return an
1735 // empty object (and no error is thrown).
1736 if (o->as<ArrayObject>())
1737 return QVariantList();
1738 return QVariantMap();
1739 }
1740 visitedObjects->insert(o->d());
1741
1742 QVariant result;
1743
1744 if (o->as<ArrayObject>()) {
1745 QV4::Scope scope(o->engine());
1746 QV4::ScopedArrayObject a(scope, o->asReturnedValue());
1747 QV4::ScopedValue v(scope);
1748 QVariantList list;
1749
1750 int length = a->getLength();
1751 for (int ii = 0; ii < length; ++ii) {
1752 v = a->get(ii);
1753 list << ::toVariant(v, QMetaType {}, conversionBehvior,
1754 visitedObjects);
1755 }
1756
1757 result = list;
1758 } else if (o->getPrototypeOf() == o->engine()->objectPrototype()->d()
1759 || (conversionBehvior == JSToQVariantConversionBehavior::Aggressive &&
1760 !o->as<QV4::FunctionObject>())) {
1761 /* FunctionObject is excluded for historical reasons, even though
1762 objects with a custom prototype risk losing information
1763 But the Aggressive path is used only in QJSValue::toVariant
1764 which is documented to be lossy
1765 */
1766 result = objectToVariantAssociation<QVariantMap>(o, visitedObjects, conversionBehvior);
1767 } else {
1768 // If it's not a plain object, we can only save it as QJSValue.
1769 result = QVariant::fromValue(QJSValuePrivate::fromReturnedValue(o->asReturnedValue()));
1770 }
1771
1772 visitedObjects->remove(o->d());
1773 return result;
1774}
1775
1776/*!
1777 \internal
1778
1779 Transform the given \a metaType and \a ptr into a JavaScript representation.
1780 */
1781QV4::ReturnedValue ExecutionEngine::fromData(
1782 QMetaType metaType, const void *ptr,
1783 QV4::Heap::Object *container, int property, uint flags)
1784{
1785 const auto createSequence = [&](const QMetaSequence metaSequence) {
1786 QV4::Scope scope(this);
1787 QV4::Scoped<Sequence> sequence(scope);
1788 if (container) {
1789 return QV4::SequencePrototype::newSequence(
1790 this, metaType, metaSequence, ptr,
1791 container, property, Heap::ReferenceObject::Flags(flags));
1792 } else {
1793 return QV4::SequencePrototype::fromData(this, metaType, metaSequence, ptr);
1794 }
1795 };
1796
1797 const int type = metaType.id();
1798 if (type < QMetaType::User) {
1799 switch (QMetaType::Type(type)) {
1800 case QMetaType::UnknownType:
1801 case QMetaType::Void:
1802 return QV4::Encode::undefined();
1803 case QMetaType::Nullptr:
1804 case QMetaType::VoidStar:
1805 return QV4::Encode::null();
1806 case QMetaType::Bool:
1807 return QV4::Encode(*reinterpret_cast<const bool*>(ptr));
1808 case QMetaType::Int:
1809 return QV4::Encode(*reinterpret_cast<const int*>(ptr));
1810 case QMetaType::UInt:
1811 return QV4::Encode(*reinterpret_cast<const uint*>(ptr));
1812 case QMetaType::Long:
1813 return QV4::Encode((double)*reinterpret_cast<const long *>(ptr));
1814 case QMetaType::ULong:
1815 return QV4::Encode((double)*reinterpret_cast<const ulong *>(ptr));
1816 case QMetaType::LongLong:
1817 return QV4::Encode((double)*reinterpret_cast<const qlonglong*>(ptr));
1818 case QMetaType::ULongLong:
1819 return QV4::Encode((double)*reinterpret_cast<const qulonglong*>(ptr));
1820 case QMetaType::Double:
1821 return QV4::Encode(*reinterpret_cast<const double*>(ptr));
1822 case QMetaType::QString:
1823 return newString(*reinterpret_cast<const QString*>(ptr))->asReturnedValue();
1824 case QMetaType::QByteArray:
1825 return newArrayBuffer(*reinterpret_cast<const QByteArray*>(ptr))->asReturnedValue();
1826 case QMetaType::Float:
1827 return QV4::Encode(*reinterpret_cast<const float*>(ptr));
1828 case QMetaType::Short:
1829 return QV4::Encode((int)*reinterpret_cast<const short*>(ptr));
1830 case QMetaType::UShort:
1831 return QV4::Encode((int)*reinterpret_cast<const unsigned short*>(ptr));
1832 case QMetaType::Char:
1833 return QV4::Encode((int)*reinterpret_cast<const char*>(ptr));
1834 case QMetaType::UChar:
1835 return QV4::Encode((int)*reinterpret_cast<const unsigned char*>(ptr));
1836 case QMetaType::SChar:
1837 return QV4::Encode((int)*reinterpret_cast<const signed char*>(ptr));
1838 case QMetaType::QChar:
1839 return newString(*reinterpret_cast<const QChar *>(ptr))->asReturnedValue();
1840 case QMetaType::Char16:
1841 return newString(QChar(*reinterpret_cast<const char16_t *>(ptr)))->asReturnedValue();
1842 case QMetaType::QDateTime:
1843 return QV4::Encode(newDateObject(
1844 *reinterpret_cast<const QDateTime *>(ptr),
1845 container, property, flags));
1846 case QMetaType::QDate:
1847 return QV4::Encode(newDateObject(
1848 *reinterpret_cast<const QDate *>(ptr),
1849 container, property, flags));
1850 case QMetaType::QTime:
1851 return QV4::Encode(newDateObject(
1852 *reinterpret_cast<const QTime *>(ptr),
1853 container, property, flags));
1854#if QT_CONFIG(regularexpression)
1855 case QMetaType::QRegularExpression:
1856 return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegularExpression *>(ptr)));
1857#endif
1858 case QMetaType::QObjectStar:
1859 return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr));
1860 case QMetaType::QStringList:
1861 return createSequence(QMetaSequence::fromContainer<QStringList>());
1862 case QMetaType::QVariantList:
1863 return createSequence(QMetaSequence::fromContainer<QVariantList>());
1864 case QMetaType::QVariantMap:
1865 return VariantAssociationPrototype::fromQVariantMap(
1866 this,
1867 *reinterpret_cast<const QVariantMap *>(ptr),
1868 container, property, Heap::ReferenceObject::Flags(flags));
1869 case QMetaType::QVariantHash:
1870 return VariantAssociationPrototype::fromQVariantHash(
1871 this,
1872 *reinterpret_cast<const QVariantHash *>(ptr),
1873 container, property, Heap::ReferenceObject::Flags(flags));
1874 case QMetaType::QJsonValue:
1875 return QV4::JsonObject::fromJsonValue(this, *reinterpret_cast<const QJsonValue *>(ptr));
1876 case QMetaType::QJsonObject:
1877 return QV4::JsonObject::fromJsonObject(this, *reinterpret_cast<const QJsonObject *>(ptr));
1878 case QMetaType::QJsonArray:
1879 return QV4::JsonObject::fromJsonArray(this, *reinterpret_cast<const QJsonArray *>(ptr));
1880 case QMetaType::QPixmap:
1881 case QMetaType::QImage:
1882 // Scarce value types
1883 return QV4::Encode(newVariantObject(metaType, ptr));
1884 default:
1885 break;
1886 }
1887 }
1888
1889 if (metaType.flags() & QMetaType::IsEnumeration)
1890 return fromData(metaType.underlyingType(), ptr, container, property, flags);
1891
1892 QV4::Scope scope(this);
1893 if (metaType == QMetaType::fromType<QQmlListReference>()) {
1894 typedef QQmlListReferencePrivate QDLRP;
1895 QDLRP *p = QDLRP::get((QQmlListReference*)const_cast<void *>(ptr));
1896 if (p->object)
1897 return QV4::QmlListWrapper::create(scope.engine, p->property, p->propertyType);
1898 else
1899 return QV4::Encode::null();
1900 } else if (auto flags = metaType.flags(); flags & QMetaType::IsQmlList) {
1901 // casting to QQmlListProperty<QObject> is slightly nasty, but it's the
1902 // same QQmlListReference does.
1903 const auto *p = static_cast<const QQmlListProperty<QObject> *>(ptr);
1904 if (p->object)
1905 return QV4::QmlListWrapper::create(scope.engine, *p, metaType);
1906 else
1907 return QV4::Encode::null();
1908 } else if (metaType == QMetaType::fromType<QJSValue>()) {
1909 return QJSValuePrivate::convertToReturnedValue(
1910 this, *reinterpret_cast<const QJSValue *>(ptr));
1911 } else if (metaType == QMetaType::fromType<QList<QObject *> >()) {
1912 // XXX Can this be made more by using Array as a prototype and implementing
1913 // directly against QList<QObject*>?
1914 const QList<QObject *> &list = *(const QList<QObject *>*)ptr;
1915 QV4::ScopedArrayObject a(scope, newArrayObject());
1916 a->arrayReserve(list.size());
1917 QV4::ScopedValue v(scope);
1918 for (int ii = 0; ii < list.size(); ++ii)
1919 a->arrayPut(ii, (v = QV4::QObjectWrapper::wrap(this, list.at(ii))));
1920 a->setArrayLengthUnchecked(list.size());
1921 return a.asReturnedValue();
1922 } else if (auto flags = metaType.flags(); flags & QMetaType::PointerToQObject) {
1923 if (flags.testFlag(QMetaType::IsConst))
1924 return QV4::QObjectWrapper::wrapConst(this, *reinterpret_cast<QObject* const *>(ptr));
1925 else
1926 return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr));
1927 } else if (metaType == QMetaType::fromType<QJSPrimitiveValue>()) {
1928 const QJSPrimitiveValue *primitive = static_cast<const QJSPrimitiveValue *>(ptr);
1929 switch (primitive->type()) {
1930 case QJSPrimitiveValue::Boolean:
1931 return Encode(primitive->asBoolean());
1932 case QJSPrimitiveValue::Integer:
1933 return Encode(primitive->asInteger());
1934 case QJSPrimitiveValue::String:
1935 return newString(primitive->asString())->asReturnedValue();
1936 case QJSPrimitiveValue::Undefined:
1937 return Encode::undefined();
1938 case QJSPrimitiveValue::Null:
1939 return Encode::null();
1940 case QJSPrimitiveValue::Double:
1941 return Encode(primitive->asDouble());
1942 }
1943 }
1944
1945 if (const QMetaObject *vtmo = QQmlMetaType::metaObjectForValueType(metaType)) {
1946 if (container) {
1947 return QV4::QQmlValueTypeWrapper::create(
1948 this, ptr, vtmo, metaType,
1949 container, property, Heap::ReferenceObject::Flags(flags));
1950 } else {
1951 return QV4::QQmlValueTypeWrapper::create(this, ptr, vtmo, metaType);
1952 }
1953 }
1954
1955 const QQmlType listType = QQmlMetaType::qmlListType(metaType);
1956 if (listType.isSequentialContainer())
1957 return createSequence(listType.listMetaSequence());
1958
1959 QSequentialIterable iterable;
1960 if (QMetaType::convert(metaType, ptr, QMetaType::fromType<QSequentialIterable>(), &iterable)) {
1961
1962 // If the resulting iterable is useful for anything, turn it into a QV4::Sequence.
1963 const QMetaSequence sequence = iterable.metaContainer();
1964 if (sequence.hasSize() && sequence.canGetValueAtIndex())
1965 return createSequence(sequence);
1966
1967 // As a last resort, try to read the contents of the container via an iterator
1968 // and build a JS array from them.
1969 if (sequence.hasConstIterator() && sequence.canGetValueAtConstIterator()) {
1970 QV4::ScopedArrayObject a(scope, newArrayObject());
1971 QV4::ScopedValue v(scope);
1972 for (auto it = iterable.constBegin(), end = iterable.constEnd(); it != end; ++it) {
1973 v = fromVariant(*it);
1974 a->push_back(v);
1975 }
1976 return a.asReturnedValue();
1977 }
1978 }
1979
1980 return QV4::Encode(newVariantObject(metaType, ptr));
1981}
1982
1983QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
1984{
1985 return fromData(variant.metaType(), variant.constData());
1986}
1987
1988ReturnedValue ExecutionEngine::fromVariant(
1989 const QVariant &variant, Heap::Object *parent, int property, uint flags)
1990{
1991 return fromData(variant.metaType(), variant.constData(), parent, property, flags);
1992}
1993
1994QVariantMap ExecutionEngine::variantMapFromJS(const Object *o)
1995{
1996 Q_ASSERT(o);
1997 V4ObjectSet visitedObjects;
1998 visitedObjects.insert(o->d());
1999 return objectToVariantAssociation<QVariantMap>(
2000 o, &visitedObjects, JSToQVariantConversionBehavior::Safish);
2001}
2002
2003QVariantHash ExecutionEngine::variantHashFromJS(const Object *o)
2004{
2005 Q_ASSERT(o);
2006 V4ObjectSet visitedObjects;
2007 visitedObjects.insert(o->d());
2008 return objectToVariantAssociation<QVariantHash>(
2009 o, &visitedObjects, JSToQVariantConversionBehavior::Safish);
2010}
2011
2012// Converts the meta-type defined by the given type and data to JS.
2013// Returns the value if conversion succeeded, an empty handle otherwise.
2014QV4::ReturnedValue ExecutionEngine::metaTypeToJS(QMetaType type, const void *data)
2015{
2016 Q_ASSERT(data != nullptr);
2017
2018 if (type == QMetaType::fromType<QVariant>()) {
2019 // unwrap it: this is tested in QJSEngine, and makes the most sense for
2020 // end-user code too.
2021 return fromVariant(*reinterpret_cast<const QVariant*>(data));
2022 } else if (type == QMetaType::fromType<QUrl>()) {
2023 // Create a proper URL object here, rather than a variant.
2024 return newUrlObject(*reinterpret_cast<const QUrl *>(data))->asReturnedValue();
2025 }
2026
2027 return fromData(type, data);
2028}
2029
2030int ExecutionEngine::maxJSStackSize() const
2031{
2032 return s_maxJSStackSize;
2033}
2034
2035int ExecutionEngine::maxGCStackSize() const
2036{
2037 return s_maxGCStackSize;
2038}
2039
2040/*!
2041 \internal
2042 Returns \a length converted to int if its safe to
2043 pass to \c Scope::alloc.
2044 Otherwise it throws a RangeError, and returns 0.
2045 */
2046int ExecutionEngine::safeForAllocLength(qint64 len64)
2047{
2048 if (len64 < 0ll || len64 > qint64(std::numeric_limits<int>::max())) {
2049 throwRangeError(QStringLiteral("Invalid array length."));
2050 return 0;
2051 }
2052 if (len64 > qint64(this->jsStackLimit - this->jsStackTop)) {
2053 throwRangeError(QStringLiteral("Array too large for apply()."));
2054 return 0;
2055 }
2056 return len64;
2057}
2058
2059ReturnedValue ExecutionEngine::global()
2060{
2061 return globalObject->asReturnedValue();
2062}
2063
2064QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::compilationUnitForUrl(const QUrl &url) const
2065{
2066 // Gives the _most recently inserted_ CU of that URL. That's what we want.
2067 return m_compilationUnits.value(url);
2068}
2069
2070QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::executableCompilationUnit(
2071 QQmlRefPointer<CompiledData::CompilationUnit> &&unit)
2072{
2073 const QUrl url = unit->finalUrl();
2074 auto [begin, end] = std::as_const(m_compilationUnits).equal_range(url);
2075
2076 for (auto it = begin; it != end; ++it) {
2077 if ((*it)->baseCompilationUnit() == unit)
2078 return *it;
2079 }
2080
2081 auto executableUnit = m_compilationUnits.insert(
2082 url, ExecutableCompilationUnit::create(std::move(unit), this));
2083 // runtime data should not be initialized yet, so we don't need to mark the CU
2084 Q_ASSERT(!(*executableUnit)->runtimeStrings);
2085 return *executableUnit;
2086}
2087
2088QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::insertCompilationUnit(QQmlRefPointer<CompiledData::CompilationUnit> &&unit) {
2089 QUrl url = unit->finalUrl();
2090 auto executableUnit = ExecutableCompilationUnit::create(std::move(unit), this);
2091 /* Compilation Units stored in the engine are part of the gc roots,
2092 so we don't trigger any write-barrier when they are added. Use
2093 markCustom to make sure they are still marked when we insert them */
2094 QV4::WriteBarrier::markCustom(this, [&executableUnit](QV4::MarkStack *ms) {
2095 executableUnit->markObjects(ms);
2096 });
2097 return *m_compilationUnits.insert(std::move(url), std::move(executableUnit));
2098}
2099
2100void ExecutionEngine::trimCompilationUnits()
2101{
2102 for (auto it = m_compilationUnits.begin(); it != m_compilationUnits.end();) {
2103 if ((*it)->count() == 1)
2104 it = m_compilationUnits.erase(it);
2105 else
2106 ++it;
2107 }
2108}
2109
2110void ExecutionEngine::trimCompilationUnitsForUrl(const QUrl &url)
2111{
2112 auto it = m_compilationUnits.find(url);
2113 while (it != m_compilationUnits.end() && it.key() == url) {
2114 if ((*it)->count() == 1)
2115 it = m_compilationUnits.erase(it);
2116 else
2117 ++it;
2118 }
2119}
2120
2121/*!
2122 * Returns any _existing_ module for the given \a url and \a referrer. This
2123 * does not actually load anything. You must guarantee the existence of the
2124 * module by keeping a (direct or indirect) reference to it somewhere, or you
2125 * must live with a possible nullptr being returned.
2126 */
2127ExecutionEngine::Module ExecutionEngine::moduleForUrl(
2128 const QUrl &url, const ExecutableCompilationUnit *referrer)
2129{
2130 QUrl resolved = referrer
2131 ? referrer->finalUrl().resolved(QQmlMetaType::normalizedUrl(url))
2132 : QQmlMetaType::normalizedUrl(url);
2133 if (!resolved.path().endsWith(QLatin1String(".mjs")))
2134 resolved.setFragment(QLatin1String("module"));
2135
2136 // Executable compilation unit already present in engine
2137 auto existingModule = m_compilationUnits.constFind(resolved);
2138 if (existingModule != m_compilationUnits.constEnd())
2139 return *existingModule;
2140
2141 // Also try with the relative url, to support native modules.
2142 if (resolved != url) {
2143 existingModule = m_compilationUnits.constFind(url);
2144 if (existingModule != m_compilationUnits.constEnd())
2145 return *existingModule;
2146 }
2147
2148 // Compilation Unit present in QQmlMetaTypeData
2149 if (auto cu = QQmlMetaType::obtainCompilationUnit(resolved))
2150 return executableCompilationUnit(std::move(cu));
2151
2152 // Compilation Unit readily loadable from .qmlc file or binary
2153 QQmlMetaType::CachedUnitLookupError cacheError = QQmlMetaType::CachedUnitLookupError::NoError;
2154 const DiskCacheOptions options = diskCacheOptions();
2155 if (const QQmlPrivate::CachedQmlUnit *cachedUnit = (options & DiskCache::Aot)
2156 ? QQmlMetaType::findCachedCompilationUnit(
2157 resolved,
2158 (options & DiskCache::AotByteCode)
2159 ? QQmlMetaType::AcceptUntyped
2160 : QQmlMetaType::RequireFullyTyped,
2161 &cacheError)
2162 : nullptr) {
2163 const auto cu = executableCompilationUnit(
2164 QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>(
2165 cachedUnit->qmlData, cachedUnit->aotCompiledFunctions,
2166 resolved.fileName(), resolved.toString()));
2167 const auto data = cu->unitData();
2168 for (uint i = 0, end = data->moduleRequestTableSize; i < end; ++i) {
2169 const auto dependency = moduleForUrl(cu->urlAt(data->moduleRequestTable()[i]), cu.data());
2170 QQmlRefPointer<QQmlScriptData> scriptData(
2171 new QQmlScriptData, QQmlRefPointer<QQmlScriptData>::Adopt);
2172 scriptData->url = dependency->finalUrl();
2173 scriptData->urlString = dependency->finalUrlString();
2174 scriptData->m_precompiledScript = dependency->baseCompilationUnit();
2175 cu->baseCompilationUnit()->dependentScripts.append(std::move(scriptData));
2176 }
2177 return cu;
2178 }
2179
2180 // Unavailable
2181 return Module();
2182}
2183
2184ExecutionEngine::Module ExecutionEngine::registerNativeModule(
2185 const QUrl &url, const QV4::Value &value)
2186{
2187 Q_ASSERT(!m_compilationUnits.contains(url));
2188
2189 const QV4::CompiledData::Unit *unit = Compiler::Codegen::generateNativeModuleUnitData(
2190 /*debugMode*/debugger() != nullptr, url.toString(), value);
2191 if (!unit)
2192 return Module();
2193
2194 QQmlRefPointer<CompiledData::CompilationUnit> cu;
2195
2196 // Make sure the type loader doesn't try to resolve the module anymore.
2197 // If some other code requests that same module, we need to produce the same CU.
2198 cu = typeLoader()->injectModule(url, unit);
2199
2200 QQmlRefPointer<ExecutableCompilationUnit> newModule = insertCompilationUnit(std::move(cu));
2201
2202 Q_ASSERT(m_compilationUnits.contains(url, newModule));
2203
2204 Scope scope(this);
2205 Scoped<QV4::Module> instance(scope, newModule->instantiate());
2206 Scoped<CallContext> context(scope, instance->d()->scope);
2207
2208 const CompiledData::Unit *unitData = newModule->baseCompilationUnit()->data;
2209 const CompiledData::ExportEntry *exportEntries = unitData->localExportEntryTable();
2210
2211 ScopedObject object(scope, value);
2212 for (uint i = 0, end = unitData->localExportEntryTableSize; i < end; ++i) {
2213 const CompiledData::ExportEntry *localExport = exportEntries + i;
2214 ScopedString localName(scope, newModule->runtimeStrings[localExport->localName]);
2215 const uint index
2216 = context->internalClass()->indexOfValueOrGetter(localName->toPropertyKey());
2217 QV4::Heap::CallContext *cc = context->d();
2218 Q_ASSERT(index < cc->locals.size);
2219 if (localName->toQString() == QLatin1String("default")) {
2220 cc->locals.set(this, index, value);
2221 continue;
2222 }
2223
2224 Q_ASSERT(object);
2225
2226 ScopedValue localValue(scope, object->get(localName));
2227 cc->locals.set(this, index, localValue);
2228 }
2229
2230 instance->d()->evaluated = true;
2231 return newModule;
2232}
2233
2235{
2236 using DiskCache = ExecutionEngine::DiskCache;
2237
2238 if (v == nullptr)
2239 return DiskCache::Enabled;
2240
2241 ExecutionEngine::DiskCacheOptions result = DiskCache::Disabled;
2242 const QList<QByteArray> options = QByteArray(v).split(',');
2243 for (const QByteArray &option : options) {
2244 if (option == "aot-bytecode")
2245 result |= DiskCache::AotByteCode;
2246 else if (option == "aot-native")
2247 result |= DiskCache::AotNative;
2248 else if (option == "aot")
2249 result |= DiskCache::Aot;
2250 else if (option == "qmlc-read")
2251 result |= DiskCache::QmlcRead;
2252 else if (option == "qmlc-write")
2253 result |= DiskCache::QmlcWrite;
2254 else if (option == "qmlc")
2255 result |= DiskCache::Qmlc;
2256 else
2257 qWarning() << "Ignoring unknown option to QML_DISK_CACHE:" << option;
2258 }
2259
2260 return result;
2261}
2262
2263ExecutionEngine::DiskCacheOptions ExecutionEngine::diskCacheOptions() const
2264{
2265 if (forceDiskCache())
2266 return DiskCache::Enabled;
2267 if (disableDiskCache() || debugger())
2268 return DiskCache::Disabled;
2269 static const DiskCacheOptions options = qmlGetConfigOption<
2270 DiskCacheOptions, transFormDiskCache>("QML_DISK_CACHE");
2271 return hasPreview.loadAcquire()
2272 ? (options & ~DiskCacheOptions(DiskCache::Aot)) // Disable AOT if preview enabled
2273 : options;
2274}
2275
2276void ExecutionEngine::callInContext(QV4::Function *function, QObject *self,
2277 QV4::ExecutionContext *context, int argc, void **args,
2278 QMetaType *types)
2279{
2280 if (!args) {
2281 Q_ASSERT(argc == 0);
2282 void *dummyArgs[] = { nullptr };
2283 QMetaType dummyTypes[] = { QMetaType::fromType<void>() };
2284 function->call(self, dummyArgs, dummyTypes, argc, context);
2285 return;
2286 }
2287 Q_ASSERT(types); // both args and types must be present
2288 // implicitly sets the return value, which is args[0]
2289 function->call(self, args, types, argc, context);
2290}
2291
2292QV4::ReturnedValue ExecutionEngine::callInContext(QV4::Function *function, QObject *self,
2293 QV4::ExecutionContext *context, int argc,
2294 const QV4::Value *argv)
2295{
2296 QV4::Scope scope(this);
2297 QV4::ScopedObject jsSelf(scope, QV4::QObjectWrapper::wrap(this, self));
2298 Q_ASSERT(jsSelf);
2299 return function->call(jsSelf, argv, argc, context);
2300}
2301
2302void ExecutionEngine::initQmlGlobalObject()
2303{
2304 initializeGlobal();
2305 lockObject(*globalObject);
2306}
2307
2308static bool globalNamesAreStaticallyKnown(QV4::Object *globalObject)
2309{
2310 const Heap::InternalClass *ic = globalObject->internalClass();
2311 const SharedInternalClassData<PropertyKey> &nameMap = ic->nameMap;
2312 bool clean = true;
2313 for (uint i = 0, end = ic->size; i < end; ++i) {
2314 const QV4::PropertyKey id = nameMap.at(i);
2315 if (id.isString()) {
2316 if (!Compiler::Codegen::isNameGlobal(id.toQString())) {
2317 qCritical() << id.toQString()
2318 << "is part of the JavaScript global object "
2319 "but not statically known to be global";
2320 clean = false;
2321 }
2322 }
2323 }
2324 return clean;
2325}
2326
2327#if QT_CONFIG(qml_xml_http_request)
2328void ExecutionEngine::setupXmlHttpRequestExtension()
2329{
2330 qt_add_domexceptions(this);
2331 m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(this);
2332}
2333#endif
2334
2335void ExecutionEngine::initializeGlobal()
2336{
2337 createQtObject();
2338
2339 QV4::GlobalExtensions::init(globalObject, QJSEngine::AllExtensions);
2340
2341#if QT_CONFIG(qml_locale)
2342 QQmlLocale::registerStringLocaleCompare(this);
2343 QQmlDateExtension::registerExtension(this);
2344 QQmlNumberExtension::registerExtension(this);
2345#endif
2346
2347#if QT_CONFIG(qml_xml_http_request)
2348 setupXmlHttpRequestExtension();
2349#endif
2350
2351 qt_add_sqlexceptions(this);
2352
2353 Q_ASSERT(globalNamesAreStaticallyKnown(globalObject));
2354}
2355
2356void ExecutionEngine::createQtObject()
2357{
2358 QV4::Scope scope(this);
2359 QtObject *qtObject = new QtObject(this);
2360 QJSEngine::setObjectOwnership(qtObject, QJSEngine::JavaScriptOwnership);
2361
2362 QV4::ScopedObject qtObjectWrapper(
2363 scope, QV4::QObjectWrapper::wrap(this, qtObject));
2364 QV4::ScopedObject qtNamespaceWrapper(
2365 scope, QV4::QMetaObjectWrapper::create(this, &Qt::staticMetaObject));
2366 QV4::ScopedObject qtObjectProtoWrapper(
2367 scope, qtObjectWrapper->getPrototypeOf());
2368
2369 qtNamespaceWrapper->setPrototypeOf(qtObjectProtoWrapper);
2370 qtObjectWrapper->setPrototypeOf(qtNamespaceWrapper);
2371
2372 globalObject->defineDefaultProperty(QStringLiteral("Qt"), qtObjectWrapper);
2373}
2374
2375void ExecutionEngine::setQmlEngine(QQmlEngine *engine)
2376{
2377 // Second stage of initialization. We're updating some more prototypes here.
2378 isInitialized = false;
2379 m_qmlEngine = engine;
2380 initQmlGlobalObject();
2381 isInitialized = true;
2382}
2383
2384static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object)
2385{
2386 if (object->as<QV4::QObjectWrapper>() || object->internalClass()->isFrozen())
2387 return;
2388
2389 QV4::Scope scope(v4);
2390
2391 bool instanceOfObject = false;
2392 QV4::ScopedObject p(scope, object->getPrototypeOf());
2393 while (p) {
2394 if (p->d() == v4->objectPrototype()->d()) {
2395 instanceOfObject = true;
2396 break;
2397 }
2398 p = p->getPrototypeOf();
2399 }
2400 if (!instanceOfObject)
2401 return;
2402
2403 Heap::InternalClass *frozen = object->internalClass()->frozen();
2404 object->setInternalClass(frozen); // Immediately assign frozen to prevent it from getting GC'd
2405
2406 QV4::ScopedObject o(scope);
2407 for (uint i = 0; i < frozen->size; ++i) {
2408 if (!frozen->nameMap.at(i).isStringOrSymbol())
2409 continue;
2410 o = *object->propertyData(i);
2411 if (o)
2412 freeze_recursive(v4, o);
2413 }
2414}
2415
2416void ExecutionEngine::freezeObject(const QV4::Value &value)
2417{
2418 QV4::Scope scope(this);
2419 QV4::ScopedObject o(scope, value);
2420 freeze_recursive(this, o);
2421}
2422
2423void ExecutionEngine::lockObject(const QV4::Value &value)
2424{
2425 QV4::Scope scope(this);
2426 ScopedObject object(scope, value);
2427 if (!object)
2428 return;
2429
2430 std::vector<Heap::Object *> stack { object->d() };
2431
2432 // Methods meant to be overridden
2433 const PropertyKey writableMembers[] = {
2434 id_toString()->propertyKey(),
2435 id_toLocaleString()->propertyKey(),
2436 id_valueOf()->propertyKey(),
2437 id_constructor()->propertyKey()
2438 };
2439 const auto writableBegin = std::begin(writableMembers);
2440 const auto writableEnd = std::end(writableMembers);
2441
2442 while (!stack.empty()) {
2443 object = stack.back();
2444 stack.pop_back();
2445
2446 if (object->as<QV4::QObjectWrapper>() || object->internalClass()->isLocked())
2447 continue;
2448
2449 Scoped<InternalClass> locked(scope, object->internalClass()->locked());
2450 QV4::ScopedObject member(scope);
2451
2452 // Taking this copy is cheap. It's refcounted. This avoids keeping a reference
2453 // to the original IC.
2454 const SharedInternalClassData<PropertyKey> nameMap = locked->d()->nameMap;
2455
2456 for (uint i = 0, end = locked->d()->size; i < end; ++i) {
2457 const PropertyKey key = nameMap.at(i);
2458 if (!key.isStringOrSymbol())
2459 continue;
2460 if ((member = *object->propertyData(i))) {
2461 stack.push_back(member->d());
2462 if (std::find(writableBegin, writableEnd, key) == writableEnd) {
2463 PropertyAttributes attributes = locked->d()->find(key).attributes;
2464 attributes.setConfigurable(false);
2465 attributes.setWritable(false);
2466 locked = locked->changeMember(key, attributes);
2467 }
2468 }
2469 }
2470
2471 object->setInternalClass(locked->d());
2472 }
2473}
2474
2475void ExecutionEngine::startTimer(const QString &timerName)
2476{
2477 if (!m_time.isValid())
2478 m_time.start();
2479 m_startedTimers[timerName] = m_time.elapsed();
2480}
2481
2482qint64 ExecutionEngine::stopTimer(const QString &timerName, bool *wasRunning)
2483{
2484 if (!m_startedTimers.contains(timerName)) {
2485 *wasRunning = false;
2486 return 0;
2487 }
2488 *wasRunning = true;
2489 qint64 startedAt = m_startedTimers.take(timerName);
2490 return m_time.elapsed() - startedAt;
2491}
2492
2493int ExecutionEngine::consoleCountHelper(const QString &file, quint16 line, quint16 column)
2494{
2495 const QString key = file + QString::number(line) + QString::number(column);
2496 int number = m_consoleCount.value(key, 0);
2497 number++;
2498 m_consoleCount.insert(key, number);
2499 return number;
2500}
2501
2502void ExecutionEngine::setExtensionData(int index, Deletable *data)
2503{
2504 if (m_extensionData.size() <= index)
2505 m_extensionData.resize(index + 1);
2506
2507 if (m_extensionData.at(index))
2508 delete m_extensionData.at(index);
2509
2510 m_extensionData[index] = data;
2511}
2512
2513template<typename Source>
2514bool convertToIterable(QMetaType metaType, void *data, Source *sequence)
2515{
2516 QSequentialIterable iterable;
2517 if (!QMetaType::view(metaType, data, QMetaType::fromType<QSequentialIterable>(), &iterable))
2518 return false;
2519
2520 // Clear the sequence before appending. There may be stale data in there.
2521 metaType.destruct(data);
2522 metaType.construct(data);
2523
2524 QV4::Scope scope(sequence->engine());
2525 QV4::ScopedValue v(scope);
2526
2527 const QMetaType elementMetaType = iterable.valueMetaType();
2528 QVariant element;
2529 void *elementData = nullptr;
2530 if (elementMetaType == QMetaType::fromType<QVariant>()) {
2531 elementData = &element;
2532 } else {
2533 element = QVariant(elementMetaType);
2534 elementData = element.data();
2535 }
2536
2537 for (qsizetype i = 0, end = sequence->getLength(); i < end; ++i) {
2538 v = sequence->get(i);
2539 ExecutionEngine::metaTypeFromJS(v, elementMetaType, elementData);
2540 iterable.addValue(element, QSequentialIterable::AtEnd);
2541 }
2542
2543 return true;
2544}
2545
2546/*!
2547 * \internal
2548 *
2549 * Converts a JS value to a meta-type.
2550 * \a data must point to a default-constructed instance of \a metaType.
2551 * Returns \c true if conversion succeeded, \c false otherwise. In the latter case,
2552 * \a data is not modified.
2553 */
2554bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, void *data)
2555{
2556 // check if it's one of the types we know
2557 switch (metaType.id()) {
2558 case QMetaType::Bool:
2559 *reinterpret_cast<bool*>(data) = value.toBoolean();
2560 return true;
2561 case QMetaType::Int:
2562 *reinterpret_cast<int*>(data) = value.toInt32();
2563 return true;
2564 case QMetaType::UInt:
2565 *reinterpret_cast<uint*>(data) = value.toUInt32();
2566 return true;
2567 case QMetaType::Long:
2568 *reinterpret_cast<long*>(data) = long(value.toInteger());
2569 return true;
2570 case QMetaType::ULong:
2571 *reinterpret_cast<ulong*>(data) = ulong(value.toInteger());
2572 return true;
2573 case QMetaType::LongLong:
2574 *reinterpret_cast<qlonglong*>(data) = qlonglong(value.toInteger());
2575 return true;
2576 case QMetaType::ULongLong:
2577 *reinterpret_cast<qulonglong*>(data) = qulonglong(value.toInteger());
2578 return true;
2579 case QMetaType::Double:
2580 *reinterpret_cast<double*>(data) = value.toNumber();
2581 return true;
2582 case QMetaType::QString:
2583 if (value.isUndefined())
2584 *reinterpret_cast<QString*>(data) = QStringLiteral("undefined");
2585 else if (value.isNull())
2586 *reinterpret_cast<QString*>(data) = QStringLiteral("null");
2587 else
2588 *reinterpret_cast<QString*>(data) = value.toQString();
2589 return true;
2590 case QMetaType::QByteArray:
2591 if (const ArrayBuffer *ab = value.as<ArrayBuffer>()) {
2592 *reinterpret_cast<QByteArray*>(data) = ab->asByteArray();
2593 } else if (const String *string = value.as<String>()) {
2594 *reinterpret_cast<QByteArray*>(data) = string->toQString().toUtf8();
2595 } else if (const ArrayObject *ao = value.as<ArrayObject>()) {
2596 // Since QByteArray is sequentially iterable, we have to construct it from a JS Array.
2597 QByteArray result;
2598 const qint64 length = ao->getLength();
2599 result.reserve(length);
2600 QV4::Scope scope(ao->engine());
2601 QV4::ScopedValue v(scope);
2602 for (qint64 i = 0; i < length; ++i) {
2603 char value = 0;
2604 v = ao->get(i);
2605 ExecutionEngine::metaTypeFromJS(v, QMetaType::fromType<char>(), &value);
2606 result.push_back(value);
2607 }
2608 *reinterpret_cast<QByteArray*>(data) = std::move(result);
2609 } else {
2610 *reinterpret_cast<QByteArray*>(data) = QByteArray();
2611 }
2612 return true;
2613 case QMetaType::Float:
2614 *reinterpret_cast<float*>(data) = value.toNumber();
2615 return true;
2616 case QMetaType::Short:
2617 *reinterpret_cast<short*>(data) = short(value.toInt32());
2618 return true;
2619 case QMetaType::UShort:
2620 *reinterpret_cast<unsigned short*>(data) = value.toUInt16();
2621 return true;
2622 case QMetaType::Char:
2623 *reinterpret_cast<char*>(data) = char(value.toInt32());
2624 return true;
2625 case QMetaType::UChar:
2626 *reinterpret_cast<unsigned char*>(data) = (unsigned char)(value.toInt32());
2627 return true;
2628 case QMetaType::SChar:
2629 *reinterpret_cast<signed char*>(data) = (signed char)(value.toInt32());
2630 return true;
2631 case QMetaType::QChar:
2632 if (String *s = value.stringValue()) {
2633 QString str = s->toQString();
2634 *reinterpret_cast<QChar*>(data) = str.isEmpty() ? QChar() : str.at(0);
2635 } else {
2636 *reinterpret_cast<QChar*>(data) = QChar(ushort(value.toUInt16()));
2637 }
2638 return true;
2639 case QMetaType::QDateTime:
2640 if (const QV4::DateObject *d = value.as<DateObject>()) {
2641 *reinterpret_cast<QDateTime *>(data) = d->toQDateTime();
2642 return true;
2643 } break;
2644 case QMetaType::QDate:
2645 if (const QV4::DateObject *d = value.as<DateObject>()) {
2646 *reinterpret_cast<QDate *>(data) = DateObject::dateTimeToDate(d->toQDateTime());
2647 return true;
2648 } break;
2649 case QMetaType::QTime:
2650 if (const QV4::DateObject *d = value.as<DateObject>()) {
2651 *reinterpret_cast<QTime *>(data) = d->toQDateTime().time();
2652 return true;
2653 } break;
2654 case QMetaType::QUrl:
2655 if (String *s = value.stringValue()) {
2656 *reinterpret_cast<QUrl *>(data) = QUrl(s->toQString());
2657 return true;
2658 } else if (const QV4::UrlObject *d = value.as<UrlObject>()) {
2659 *reinterpret_cast<QUrl *>(data) = d->toQUrl();
2660 return true;
2661 } else if (const QV4::VariantObject *d = value.as<VariantObject>()) {
2662 const QVariant *variant = &d->d()->data();
2663 if (variant->metaType() == QMetaType::fromType<QUrl>()) {
2664 *reinterpret_cast<QUrl *>(data)
2665 = *reinterpret_cast<const QUrl *>(variant->constData());
2666 return true;
2667 }
2668 }
2669 break;
2670#if QT_CONFIG(regularexpression)
2671 case QMetaType::QRegularExpression:
2672 if (const QV4::RegExpObject *r = value.as<QV4::RegExpObject>()) {
2673 *reinterpret_cast<QRegularExpression *>(data) = r->toQRegularExpression();
2674 return true;
2675 } break;
2676#endif
2677 case QMetaType::QObjectStar: {
2678 if (value.isNull()) {
2679 *reinterpret_cast<QObject* *>(data) = nullptr;
2680 return true;
2681 }
2682 if (value.as<QV4::QObjectWrapper>()) {
2683 *reinterpret_cast<QObject* *>(data) = qtObjectFromJS(value);
2684 return true;
2685 }
2686 break;
2687 }
2688 case QMetaType::QStringList: {
2689 const QV4::ArrayObject *a = value.as<QV4::ArrayObject>();
2690 if (a) {
2691 *reinterpret_cast<QStringList *>(data) = a->toQStringList();
2692 return true;
2693 }
2694 break;
2695 }
2696 case QMetaType::QVariantList: {
2697 const QV4::ArrayObject *a = value.as<QV4::ArrayObject>();
2698 if (a) {
2699 *reinterpret_cast<QVariantList *>(data) = ExecutionEngine::toVariant(
2700 *a, /*typeHint*/QMetaType{}, /*createJSValueForObjectsAndSymbols*/false)
2701 .toList();
2702 return true;
2703 }
2704 break;
2705 }
2706 case QMetaType::QVariantMap: {
2707 const QV4::Object *o = value.as<QV4::Object>();
2708 if (o) {
2709 *reinterpret_cast<QVariantMap *>(data) = o->engine()->variantMapFromJS(o);
2710 return true;
2711 }
2712 break;
2713 }
2714 case QMetaType::QVariant:
2715 if (value.as<QV4::Managed>()) {
2716 *reinterpret_cast<QVariant*>(data) = ExecutionEngine::toVariant(
2717 value, /*typeHint*/QMetaType{}, /*createJSValueForObjectsAndSymbols*/false);
2718 } else if (value.isNull()) {
2719 *reinterpret_cast<QVariant*>(data) = QVariant::fromValue(nullptr);
2720 } else if (value.isUndefined()) {
2721 *reinterpret_cast<QVariant*>(data) = QVariant();
2722 } else if (value.isBoolean()) {
2723 *reinterpret_cast<QVariant*>(data) = QVariant(value.booleanValue());
2724 } else if (value.isInteger()) {
2725 *reinterpret_cast<QVariant*>(data) = QVariant(value.integerValue());
2726 } else if (value.isDouble()) {
2727 *reinterpret_cast<QVariant*>(data) = QVariant(value.doubleValue());
2728 }
2729 return true;
2730 case QMetaType::QJsonValue:
2731 *reinterpret_cast<QJsonValue *>(data) = QV4::JsonObject::toJsonValue(value);
2732 return true;
2733 case QMetaType::QJsonObject: {
2734 *reinterpret_cast<QJsonObject *>(data) = QV4::JsonObject::toJsonObject(value.as<Object>());
2735 return true;
2736 }
2737 case QMetaType::QJsonArray: {
2738 const QV4::ArrayObject *a = value.as<ArrayObject>();
2739 if (a) {
2740 *reinterpret_cast<QJsonArray *>(data) = JsonObject::toJsonArray(a);
2741 return true;
2742 }
2743 break;
2744 }
2745 default:
2746 break;
2747 }
2748
2749 if (metaType.flags() & QMetaType::IsEnumeration) {
2750 *reinterpret_cast<int *>(data) = value.toInt32();
2751 return true;
2752 }
2753
2754 if (const QV4::QmlListWrapper *wrapper = value.as<QV4::QmlListWrapper>()) {
2755 if (metaType == QMetaType::fromType<QQmlListReference>()) {
2756 *reinterpret_cast<QQmlListReference *>(data) = wrapper->toListReference();
2757 return true;
2758 }
2759
2760 const auto wrapperPrivate = wrapper->d();
2761 if (metaType == QMetaType::fromType<QQmlListProperty<QObject> *>()
2762 || metaType == wrapperPrivate->propertyType()) {
2763 *reinterpret_cast<QQmlListProperty<QObject> *>(data) = *wrapperPrivate->property();
2764 return true;
2765 }
2766
2767 if (metaType == QMetaType::fromType<QObjectList>()) {
2768 *reinterpret_cast<QObjectList *>(data)
2769 = wrapperPrivate->property()->toList<QObjectList>();
2770 return true;
2771 }
2772
2773 if (convertToIterable(metaType, data, wrapper))
2774 return true;
2775 }
2776
2777 if (const QQmlValueTypeWrapper *vtw = value.as<QQmlValueTypeWrapper>()) {
2778 const QMetaType valueType = vtw->type();
2779 if (valueType == metaType)
2780 return vtw->toGadget(data);
2781
2782 Heap::QQmlValueTypeWrapper *d = vtw->d();
2783 if (d->isReference())
2784 d->readReference();
2785
2786 if (void *gadgetPtr = d->gadgetPtr()) {
2787 if (QQmlValueTypeProvider::populateValueType(
2788 metaType, data, valueType, gadgetPtr, vtw->engine())) {
2789 return true;
2790 }
2791 if (QMetaType::canConvert(valueType, metaType))
2792 return QMetaType::convert(valueType, gadgetPtr, metaType, data);
2793 } else {
2794 QVariant empty(valueType);
2795 if (QQmlValueTypeProvider::populateValueType(
2796 metaType, data, valueType, empty.data(), vtw->engine())) {
2797 return true;
2798 }
2799 if (QMetaType::canConvert(valueType, metaType))
2800 return QMetaType::convert(valueType, empty.data(), metaType, data);
2801 }
2802 }
2803
2804 // Try to use magic; for compatibility with qjsvalue_cast.
2805
2806 if (convertToNativeQObject(value, metaType, reinterpret_cast<void **>(data)))
2807 return true;
2808
2809 const bool isPointer = (metaType.flags() & QMetaType::IsPointer);
2810 const QV4::VariantObject *variantObject = value.as<QV4::VariantObject>();
2811 if (variantObject) {
2812 // Actually a reference, because we're poking it for its data() below and we want
2813 // the _original_ data, not some copy.
2814 QVariant &var = variantObject->d()->data();
2815
2816 if (var.metaType() == metaType) {
2817 metaType.destruct(data);
2818 metaType.construct(data, var.data());
2819 return true;
2820 }
2821
2822 if (isPointer) {
2823 const QByteArray pointedToTypeName = QByteArray(metaType.name()).chopped(1);
2824 const QMetaType valueType = QMetaType::fromName(pointedToTypeName);
2825
2826 if (valueType == var.metaType()) {
2827 // ### Qt7: Remove this. Returning pointers to potentially gc'd data is crazy.
2828 // We have T t, T* is requested, so return &t.
2829 *reinterpret_cast<const void **>(data) = var.data();
2830 return true;
2831 } else if (Object *o = value.objectValue()) {
2832 // Look in the prototype chain.
2833 QV4::Scope scope(o->engine());
2834 QV4::ScopedObject proto(scope, o->getPrototypeOf());
2835 while (proto) {
2836 bool canCast = false;
2837 if (QV4::VariantObject *vo = proto->as<QV4::VariantObject>()) {
2838 const QVariant &v = vo->d()->data();
2839 canCast = (metaType == v.metaType());
2840 }
2841 else if (proto->as<QV4::QObjectWrapper>()) {
2842 QV4::ScopedObject p(scope, proto.getPointer());
2843 if (QObject *qobject = qtObjectFromJS(p)) {
2844 if (const QMetaObject *metaObject = metaType.metaObject())
2845 canCast = metaObject->cast(qobject) != nullptr;
2846 else
2847 canCast = qobject->qt_metacast(pointedToTypeName);
2848 }
2849 }
2850 if (canCast) {
2851 const QMetaType varType = var.metaType();
2852 if (varType.flags() & QMetaType::IsPointer) {
2853 *reinterpret_cast<const void **>(data)
2854 = *reinterpret_cast<void *const *>(var.data());
2855 } else {
2856 *reinterpret_cast<const void **>(data) = var.data();
2857 }
2858 return true;
2859 }
2860 proto = proto->getPrototypeOf();
2861 }
2862 }
2863 } else if (QQmlValueTypeProvider::populateValueType(
2864 metaType, data, var.metaType(), var.data(), variantObject->engine())) {
2865 return true;
2866 }
2867 } else if (value.isNull() && isPointer) {
2868 *reinterpret_cast<void* *>(data) = nullptr;
2869 return true;
2870 } else if (metaType == QMetaType::fromType<QJSValue>()) {
2871 QJSValuePrivate::setValue(reinterpret_cast<QJSValue*>(data), value);
2872 return true;
2873 } else if (metaType == QMetaType::fromType<QJSPrimitiveValue>()) {
2874 *reinterpret_cast<QJSPrimitiveValue *>(data) = createPrimitive(&value);
2875 return true;
2876 } else if (!isPointer) {
2877 const QV4::Managed *managed = value.as<QV4::Managed>();
2878 if (QQmlValueTypeProvider::populateValueType(
2879 metaType, data, value, managed ? managed->engine() : nullptr)) {
2880 return true;
2881 }
2882 }
2883
2884 if (const QV4::Sequence *sequence = value.as<Sequence>()) {
2885 const QVariant result = QV4::SequencePrototype::toVariant(sequence);
2886 if (result.metaType() == metaType) {
2887 metaType.destruct(data);
2888 metaType.construct(data, result.constData());
2889 return true;
2890 }
2891
2892 if (convertToIterable(metaType, data, sequence))
2893 return true;
2894 }
2895
2896 if (const QV4::ArrayObject *array = value.as<ArrayObject>()) {
2897 if (convertToIterable(metaType, data, array))
2898 return true;
2899 }
2900
2901 return false;
2902}
2903
2904static bool convertToNativeQObject(const QV4::Value &value, QMetaType targetType, void **result)
2905{
2906 if (!(targetType.flags() & QMetaType::IsPointer))
2907 return false;
2908 if (QObject *qobject = qtObjectFromJS(value)) {
2909 // If the target type has a metaObject, use that for casting.
2910 if (const QMetaObject *targetMetaObject = targetType.metaObject()) {
2911 if (QObject *instance = targetMetaObject->cast(qobject)) {
2912 *result = instance;
2913 return true;
2914 }
2915 return false;
2916 }
2917
2918 // We have to call the generated qt_metacast rather than metaObject->cast() here so that
2919 // it works for types without QMetaObject, such as QStandardItem.
2920 const QByteArray targetTypeName = targetType.name();
2921 const int start = targetTypeName.startsWith("const ") ? 6 : 0;
2922 const QByteArray className = targetTypeName.mid(start, targetTypeName.size() - start - 1);
2923 if (void *instance = qobject->qt_metacast(className)) {
2924 *result = instance;
2925 return true;
2926 }
2927 }
2928 return false;
2929}
2930
2931static QObject *qtObjectFromJS(const QV4::Value &value)
2932{
2933 if (!value.isObject())
2934 return nullptr;
2935
2936 QV4::Scope scope(value.as<QV4::Managed>()->engine());
2937 QV4::Scoped<QV4::VariantObject> v(scope, value);
2938
2939 if (v) {
2940 QVariant variant = v->d()->data();
2941 int type = variant.userType();
2942 if (type == QMetaType::QObjectStar)
2943 return *reinterpret_cast<QObject* const *>(variant.constData());
2944 }
2945 QV4::Scoped<QV4::QObjectWrapper> wrapper(scope, value);
2946 if (wrapper)
2947 return wrapper->object();
2948
2949 QV4::Scoped<QV4::QQmlTypeWrapper> typeWrapper(scope, value);
2950 if (typeWrapper)
2951 return typeWrapper->object();
2952
2953 return nullptr;
2954}
2955
2964
2965QMutex *ExecutionEngine::registrationMutex()
2966{
2967 return &registrationData()->mutex;
2968}
2969
2970int ExecutionEngine::registerExtension()
2971{
2972 return registrationData()->extensionCount++;
2973}
2974
2975QT_END_NAMESPACE
Definition qjsvalue.h:23
static char * v4StackTrace(const ExecutionContext *context)
static bool globalNamesAreStaticallyKnown(QV4::Object *globalObject)
static QObject * qtObjectFromJS(const QV4::Value &value)
static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObjects=nullptr, JSToQVariantConversionBehavior behavior=JSToQVariantConversionBehavior::Safish)
Q_QML_EXPORT char * qt_v4StackTraceForEngine(void *executionEngine)
DEFINE_BOOL_CONFIG_OPTION(forceDiskCache, QML_FORCE_DISK_CACHE)
static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object)
static QSequentialIterable jsvalueToSequence(const QJSValue &value)
ReturnType convertJSValueToVariantType(const QJSValue &value)
static QVariant toVariant(const QV4::Value &value, QMetaType typeHint, JSToQVariantConversionBehavior conversionBehavior, V4ObjectSet *visitedObjects)
JSToQVariantConversionBehavior
static QtMetaContainerPrivate::QMetaSequenceInterface emptySequenceInterface()
static ExecutionEngine::DiskCacheOptions transFormDiskCache(const char *v)
Q_GLOBAL_STATIC(QV4EngineRegistrationData, registrationData)
static QtMetaContainerPrivate::QMetaSequenceInterface sequenceInterface()
Q_QML_EXPORT char * qt_v4StackTrace(void *executionContext)
static bool convertToNativeQObject(const QV4::Value &value, QMetaType targetType, void **result)
Association objectToVariantAssociation(const QV4::Object *o, V4ObjectSet *visitedObjects, JSToQVariantConversionBehavior conversionBehvior)
bool convertToIterable(QMetaType metaType, void *data, Source *sequence)
QSet< QV4::Heap::Object * > V4ObjectSet
ReturnedValue throwTypeError(const FunctionObject *b, const QV4::Value *, const QV4::Value *, int)
QJSValue const * data