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