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

source code of qtdeclarative/src/qml/jsruntime/qv4engine.cpp