1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qml/qqmlprivate.h"
5#include "qv4engine_p.h"
6#include "qv4executablecompilationunit_p.h"
7
8#include <private/qv4engine_p.h>
9#include <private/qv4regexp_p.h>
10#include <private/qv4lookup_p.h>
11#include <private/qv4qmlcontext_p.h>
12#include <private/qv4identifiertable_p.h>
13#include <private/qv4objectproto_p.h>
14#include <private/qqmlengine_p.h>
15#include <private/qv4qobjectwrapper_p.h>
16#include <private/qqmlvaluetypewrapper_p.h>
17#include <private/qqmlscriptdata_p.h>
18#include <private/qv4module_p.h>
19#include <private/qv4compilationunitmapper_p.h>
20#include <private/qqmltypewrapper_p.h>
21#include <private/qv4resolvedtypereference_p.h>
22#include <private/qv4objectiterator_p.h>
23
24#include <QtQml/qqmlpropertymap.h>
25
26#include <QtCore/qfileinfo.h>
27#include <QtCore/qcryptographichash.h>
28
29QT_BEGIN_NAMESPACE
30
31namespace QV4 {
32
33ExecutableCompilationUnit::ExecutableCompilationUnit() = default;
34
35ExecutableCompilationUnit::ExecutableCompilationUnit(
36 QQmlRefPointer<CompiledData::CompilationUnit> &&compilationUnit)
37 : m_compilationUnit(std::move(compilationUnit))
38{
39 constants = m_compilationUnit->constants;
40}
41
42ExecutableCompilationUnit::~ExecutableCompilationUnit()
43{
44 if (engine)
45 clear();
46}
47
48static QString toString(QV4::ReturnedValue v)
49{
50 Value val = Value::fromReturnedValue(val: v);
51 QString result;
52 if (val.isInt32())
53 result = QLatin1String("int ");
54 else if (val.isDouble())
55 result = QLatin1String("double ");
56 if (val.isEmpty())
57 result += QLatin1String("empty");
58 else
59 result += val.toQStringNoThrow();
60 return result;
61}
62
63static void dumpConstantTable(const StaticValue *constants, uint count)
64{
65 QDebug d = qDebug();
66 d.nospace() << Qt::right;
67 for (uint i = 0; i < count; ++i) {
68 d << qSetFieldWidth(width: 8) << i << qSetFieldWidth(width: 0) << ": "
69 << toString(v: constants[i].asReturnedValue()).toUtf8().constData() << "\n";
70 }
71}
72
73void ExecutableCompilationUnit::populate()
74{
75 /* In general, we should use QV4::Scope whenever we allocate heap objects, and employ write barriers
76 for member variables pointing to heap objects. However, ExecutableCompilationUnit is special, as it
77 is always part of the root set. So instead of using scopde allocations and write barriers, we use a
78 slightly different approach: We temporarily block the gc from running. Afterwards, at the end of the
79 function we check whether the gc was already running, and mark the ExecutableCompilationUnit. This
80 ensures that all the newly allocated objects of the compilation unit will be marked in turn.
81 If the gc was not running, we don't have to do anything, because everything will be marked when the
82 gc starts marking the root set at the start of a run.
83 */
84 const CompiledData::Unit *data = m_compilationUnit->data;
85 GCCriticalSection<ExecutableCompilationUnit> criticalSection(engine, this);
86
87 Q_ASSERT(!runtimeStrings);
88 Q_ASSERT(engine);
89 Q_ASSERT(data);
90 const quint32 stringCount = totalStringCount();
91 runtimeStrings = (QV4::Heap::String **)calloc(nmemb: stringCount, size: sizeof(QV4::Heap::String*));
92 for (uint i = 0; i < stringCount; ++i)
93 runtimeStrings[i] = engine->newString(s: stringAt(index: i));
94
95 runtimeRegularExpressions
96 = new QV4::Value[data->regexpTableSize];
97 for (uint i = 0; i < data->regexpTableSize; ++i) {
98 const CompiledData::RegExp *re = data->regexpAt(index: i);
99 uint f = re->flags();
100 const CompiledData::RegExp::Flags flags = static_cast<CompiledData::RegExp::Flags>(f);
101 runtimeRegularExpressions[i] = QV4::RegExp::create(
102 engine, pattern: stringAt(index: re->stringIndex()), flags);
103 }
104
105 if (data->lookupTableSize) {
106 runtimeLookups = new QV4::Lookup[data->lookupTableSize];
107 memset(s: runtimeLookups, c: 0, n: data->lookupTableSize * sizeof(QV4::Lookup));
108 const CompiledData::Lookup *compiledLookups = data->lookupTable();
109 for (uint i = 0; i < data->lookupTableSize; ++i) {
110 QV4::Lookup *l = runtimeLookups + i;
111
112 CompiledData::Lookup::Type type
113 = CompiledData::Lookup::Type(uint(compiledLookups[i].type()));
114 if (type == CompiledData::Lookup::Type_Getter)
115 l->getter = QV4::Lookup::getterGeneric;
116 else if (type == CompiledData::Lookup::Type_Setter)
117 l->setter = QV4::Lookup::setterGeneric;
118 else if (type == CompiledData::Lookup::Type_GlobalGetter)
119 l->globalGetter = QV4::Lookup::globalGetterGeneric;
120 else if (type == CompiledData::Lookup::Type_QmlContextPropertyGetter)
121 l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
122 l->forCall = compiledLookups[i].mode() == CompiledData::Lookup::Mode_ForCall;
123 l->nameIndex = compiledLookups[i].nameIndex();
124 }
125 }
126
127 if (data->jsClassTableSize) {
128 runtimeClasses
129 = (QV4::Heap::InternalClass **)calloc(nmemb: data->jsClassTableSize,
130 size: sizeof(QV4::Heap::InternalClass *));
131
132 for (uint i = 0; i < data->jsClassTableSize; ++i) {
133 int memberCount = 0;
134 const CompiledData::JSClassMember *member
135 = data->jsClassAt(idx: i, nMembers: &memberCount);
136 runtimeClasses[i]
137 = engine->internalClasses(icType: QV4::ExecutionEngine::Class_Object);
138 for (int j = 0; j < memberCount; ++j, ++member)
139 runtimeClasses[i]
140 = runtimeClasses[i]->addMember(
141 identifier: engine->identifierTable->asPropertyKey(
142 str: runtimeStrings[member->nameOffset()]),
143 data: member->isAccessor()
144 ? QV4::Attr_Accessor
145 : QV4::Attr_Data);
146 }
147 }
148
149 runtimeFunctions.resize(size: data->functionTableSize);
150 static bool ignoreAotCompiledFunctions
151 = qEnvironmentVariableIsSet(varName: "QV4_FORCE_INTERPRETER")
152 || !(engine->diskCacheOptions() & ExecutionEngine::DiskCache::AotNative);
153
154 const QQmlPrivate::AOTCompiledFunction *aotFunction
155 = ignoreAotCompiledFunctions ? nullptr : m_compilationUnit->aotCompiledFunctions;
156
157 auto advanceAotFunction = [&](int i) -> const QQmlPrivate::AOTCompiledFunction * {
158 if (aotFunction) {
159 if (aotFunction->functionPtr) {
160 if (aotFunction->functionIndex == i)
161 return aotFunction++;
162 } else {
163 aotFunction = nullptr;
164 }
165 }
166 return nullptr;
167 };
168
169 for (int i = 0 ;i < runtimeFunctions.size(); ++i) {
170 const QV4::CompiledData::Function *compiledFunction = data->functionAt(idx: i);
171 runtimeFunctions[i] = QV4::Function::create(engine, unit: this, function: compiledFunction,
172 aotFunction: advanceAotFunction(i));
173 }
174
175 Scope scope(engine);
176 Scoped<InternalClass> ic(scope);
177
178 runtimeBlocks.resize(size: data->blockTableSize);
179 for (int i = 0 ;i < runtimeBlocks.size(); ++i) {
180 const QV4::CompiledData::Block *compiledBlock = data->blockAt(idx: i);
181 ic = engine->internalClasses(icType: EngineBase::Class_CallContext);
182
183 // first locals
184 const quint32_le *localsIndices = compiledBlock->localsTable();
185 for (quint32 j = 0; j < compiledBlock->nLocals; ++j)
186 ic = ic->addMember(
187 identifier: engine->identifierTable->asPropertyKey(str: runtimeStrings[localsIndices[j]]),
188 data: Attr_NotConfigurable);
189 runtimeBlocks[i] = ic->d();
190 }
191
192 static const bool showCode = qEnvironmentVariableIsSet(varName: "QV4_SHOW_BYTECODE");
193 if (showCode) {
194 qDebug() << "=== Constant table";
195 dumpConstantTable(constants, count: data->constantTableSize);
196 qDebug() << "=== String table";
197 for (uint i = 0, end = totalStringCount(); i < end; ++i)
198 qDebug() << " " << i << ":" << runtimeStrings[i]->toQString();
199 qDebug() << "=== Closure table";
200 for (uint i = 0; i < data->functionTableSize; ++i)
201 qDebug() << " " << i << ":" << runtimeFunctions[i]->name()->toQString();
202 qDebug() << "root function at index "
203 << (data->indexOfRootFunction != -1
204 ? data->indexOfRootFunction : 0);
205 }
206}
207
208Heap::Object *ExecutableCompilationUnit::templateObjectAt(int index) const
209{
210 const CompiledData::Unit *data = m_compilationUnit->data;
211 Q_ASSERT(data);
212 Q_ASSERT(engine);
213
214 Q_ASSERT(index < int(data->templateObjectTableSize));
215 if (!templateObjects.size())
216 templateObjects.resize(size: data->templateObjectTableSize);
217 Heap::Object *o = templateObjects.at(i: index);
218 if (o)
219 return o;
220
221 // create the template object
222 Scope scope(engine);
223 const CompiledData::TemplateObject *t = data->templateObjectAt(idx: index);
224 Scoped<ArrayObject> a(scope, engine->newArrayObject(count: t->size));
225 Scoped<ArrayObject> raw(scope, engine->newArrayObject(count: t->size));
226 ScopedValue s(scope);
227 for (uint i = 0; i < t->size; ++i) {
228 s = runtimeStrings[t->stringIndexAt(i)];
229 a->arraySet(index: i, value: s);
230 s = runtimeStrings[t->rawStringIndexAt(i)];
231 raw->arraySet(index: i, value: s);
232 }
233
234 ObjectPrototype::method_freeze(engine->functionCtor(), thisObject: nullptr, argv: raw, argc: 1);
235 a->defineReadonlyProperty(QStringLiteral("raw"), value: raw);
236 ObjectPrototype::method_freeze(engine->functionCtor(), thisObject: nullptr, argv: a, argc: 1);
237
238 templateObjects[index] = a->objectValue()->d();
239 return templateObjects.at(i: index);
240}
241
242void ExecutableCompilationUnit::clear()
243{
244 delete [] imports;
245 imports = nullptr;
246
247 if (runtimeLookups) {
248 const uint lookupTableSize = unitData()->lookupTableSize;
249 for (uint i = 0; i < lookupTableSize; ++i)
250 runtimeLookups[i].releasePropertyCache();
251 }
252
253 delete [] runtimeLookups;
254 runtimeLookups = nullptr;
255
256 for (QV4::Function *f : std::as_const(t&: runtimeFunctions))
257 f->destroy();
258 runtimeFunctions.clear();
259
260 free(ptr: runtimeStrings);
261 runtimeStrings = nullptr;
262 delete [] runtimeRegularExpressions;
263 runtimeRegularExpressions = nullptr;
264 free(ptr: runtimeClasses);
265 runtimeClasses = nullptr;
266}
267
268void ExecutableCompilationUnit::markObjects(QV4::MarkStack *markStack) const
269{
270 const CompiledData::Unit *data = m_compilationUnit->data;
271
272 if (runtimeStrings) {
273 for (uint i = 0, end = totalStringCount(); i < end; ++i)
274 if (runtimeStrings[i])
275 runtimeStrings[i]->mark(markStack);
276 }
277 if (runtimeRegularExpressions) {
278 for (uint i = 0; i < data->regexpTableSize; ++i)
279 Value::fromStaticValue(staticValue: runtimeRegularExpressions[i]).mark(markStack);
280 }
281 if (runtimeClasses) {
282 for (uint i = 0; i < data->jsClassTableSize; ++i)
283 if (runtimeClasses[i])
284 runtimeClasses[i]->mark(markStack);
285 }
286 for (QV4::Function *f : std::as_const(t: runtimeFunctions))
287 if (f && f->internalClass)
288 f->internalClass->mark(markStack);
289 for (QV4::Heap::InternalClass *c : std::as_const(t: runtimeBlocks))
290 if (c)
291 c->mark(markStack);
292
293 for (QV4::Heap::Object *o : std::as_const(t&: templateObjects))
294 if (o)
295 o->mark(markStack);
296
297 if (runtimeLookups) {
298 for (uint i = 0; i < data->lookupTableSize; ++i)
299 runtimeLookups[i].markObjects(stack: markStack);
300 }
301
302 if (Heap::Base *v = m_valueOrModule.heapObject())
303 v->mark(markStack);
304}
305
306IdentifierHash ExecutableCompilationUnit::createNamedObjectsPerComponent(int componentObjectIndex)
307{
308 IdentifierHash namedObjectCache(engine);
309 const CompiledData::Object *component = objectAt(index: componentObjectIndex);
310 const quint32_le *namedObjectIndexPtr = component->namedObjectsInComponentTable();
311 for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) {
312 const CompiledData::Object *namedObject = objectAt(index: *namedObjectIndexPtr);
313 namedObjectCache.add(str: runtimeStrings[namedObject->idNameIndex], value: namedObject->objectId());
314 }
315 Q_ASSERT(!namedObjectCache.isEmpty());
316 return *namedObjectsPerComponentCache.insert(key: componentObjectIndex, value: namedObjectCache);
317}
318
319QQmlRefPointer<ExecutableCompilationUnit> ExecutableCompilationUnit::create(
320 QQmlRefPointer<CompiledData::CompilationUnit> &&compilationUnit, ExecutionEngine *engine)
321{
322 auto result = QQmlRefPointer<ExecutableCompilationUnit>(
323 new ExecutableCompilationUnit(std::move(compilationUnit)),
324 QQmlRefPointer<ExecutableCompilationUnit>::Adopt);
325 result->engine = engine;
326 return result;
327}
328
329Heap::Module *ExecutableCompilationUnit::instantiate()
330{
331 const CompiledData::Unit *data = m_compilationUnit->data;
332
333 if (isESModule() && module())
334 return module();
335
336 if (data->indexOfRootFunction < 0)
337 return nullptr;
338
339 Q_ASSERT(engine);
340 if (!runtimeStrings)
341 populate();
342
343 Scope scope(engine);
344 Scoped<Module> module(scope, engine->memoryManager->allocate<Module>(args&: engine, args: this));
345
346 if (isESModule())
347 setModule(module->d());
348
349 const QStringList moduleRequests = m_compilationUnit->moduleRequests();
350 for (const QString &request: moduleRequests) {
351 const QUrl url(request);
352 const auto dependentModuleUnit = engine->loadModule(url: url, referrer: this);
353 if (engine->hasException)
354 return nullptr;
355 if (dependentModuleUnit.compiled)
356 dependentModuleUnit.compiled->instantiate();
357 }
358
359 ScopedString importName(scope);
360
361 const uint importCount = data->importEntryTableSize;
362 if (importCount > 0) {
363 imports = new const StaticValue *[importCount];
364 memset(s: imports, c: 0, n: importCount * sizeof(StaticValue *));
365 }
366 for (uint i = 0; i < importCount; ++i) {
367 const CompiledData::ImportEntry &entry = data->importEntryTable()[i];
368 QUrl url = urlAt(index: entry.moduleRequest);
369 importName = runtimeStrings[entry.importName];
370
371 const auto module = engine->loadModule(url: url, referrer: this);
372 if (module.compiled) {
373 const Value *valuePtr = module.compiled->resolveExport(exportName: importName);
374 if (!valuePtr) {
375 QString referenceErrorMessage = QStringLiteral("Unable to resolve import reference ");
376 referenceErrorMessage += importName->toQString();
377 engine->throwReferenceError(
378 value: referenceErrorMessage, fileName: fileName(),
379 lineNumber: entry.location.line(), column: entry.location.column());
380 return nullptr;
381 }
382 imports[i] = valuePtr;
383 } else if (Value *value = module.native) {
384 const QString name = importName->toQString();
385 if (value->isNullOrUndefined()) {
386 QString errorMessage = name;
387 errorMessage += QStringLiteral(" from ");
388 errorMessage += url.toString();
389 errorMessage += QStringLiteral(" is null");
390 engine->throwError(message: errorMessage);
391 return nullptr;
392 }
393
394 if (name == QStringLiteral("default")) {
395 imports[i] = value;
396 } else {
397 url.setFragment(fragment: name);
398 const auto fragment = engine->moduleForUrl(url: url, referrer: this);
399 if (fragment.native) {
400 imports[i] = fragment.native;
401 } else {
402 Scope scope(this->engine);
403 ScopedObject o(scope, value);
404 if (!o) {
405 QString referenceErrorMessage = QStringLiteral("Unable to resolve import reference ");
406 referenceErrorMessage += name;
407 referenceErrorMessage += QStringLiteral(" because ");
408 referenceErrorMessage += url.toString(options: QUrl::RemoveFragment);
409 referenceErrorMessage += QStringLiteral(" is not an object");
410 engine->throwReferenceError(
411 value: referenceErrorMessage, fileName: fileName(),
412 lineNumber: entry.location.line(), column: entry.location.column());
413 return nullptr;
414 }
415
416 const ScopedPropertyKey key(scope, scope.engine->identifierTable->asPropertyKey(s: name));
417 const ScopedValue result(scope, o->get(id: key));
418 imports[i] = engine->registerNativeModule(url, module: result);
419 }
420 }
421 }
422 }
423
424 const auto throwReferenceError = [&](const CompiledData::ExportEntry &entry, const QString &importName) {
425 QString referenceErrorMessage = QStringLiteral("Unable to resolve re-export reference ");
426 referenceErrorMessage += importName;
427 engine->throwReferenceError(
428 value: referenceErrorMessage, fileName: fileName(),
429 lineNumber: entry.location.line(), column: entry.location.column());
430 };
431
432 for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) {
433 const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i];
434 auto dependentModule = engine->loadModule(url: urlAt(index: entry.moduleRequest), referrer: this);
435 ScopedString importName(scope, runtimeStrings[entry.importName]);
436 if (const auto dependentModuleUnit = dependentModule.compiled) {
437 if (!dependentModuleUnit->resolveExport(exportName: importName)) {
438 throwReferenceError(entry, importName->toQString());
439 return nullptr;
440 }
441 } else if (const auto native = dependentModule.native) {
442 ScopedObject o(scope, native);
443 const ScopedPropertyKey key(scope, scope.engine->identifierTable->asPropertyKey(str: importName));
444 const ScopedValue result(scope, o->get(id: key));
445 if (result->isUndefined()) {
446 throwReferenceError(entry, importName->toQString());
447 return nullptr;
448 }
449 }
450 }
451
452 return module->d();
453}
454
455const Value *ExecutableCompilationUnit::resolveExportRecursively(
456 QV4::String *exportName, QVector<ResolveSetEntry> *resolveSet)
457{
458 if (!module())
459 return nullptr;
460
461 for (const auto &entry: *resolveSet)
462 if (entry.module == this && entry.exportName->isEqualTo(other: exportName))
463 return nullptr;
464
465 (*resolveSet) << ResolveSetEntry(this, exportName);
466
467 if (exportName->toQString() == QLatin1String("*"))
468 return &module()->self;
469
470 const CompiledData::Unit *data = m_compilationUnit->data;
471
472 Q_ASSERT(data);
473 Q_ASSERT(engine);
474
475 Scope scope(engine);
476
477 if (auto localExport = lookupNameInExportTable(
478 firstExportEntry: data->localExportEntryTable(), tableSize: data->localExportEntryTableSize, name: exportName)) {
479 ScopedString localName(scope, runtimeStrings[localExport->localName]);
480 uint index = module()->scope->internalClass->indexOfValueOrGetter(id: localName->toPropertyKey());
481 if (index == UINT_MAX)
482 return nullptr;
483 if (index >= module()->scope->locals.size)
484 return &(imports[index - module()->scope->locals.size]->asValue<Value>());
485 return &module()->scope->locals[index];
486 }
487
488 if (auto indirectExport = lookupNameInExportTable(
489 firstExportEntry: data->indirectExportEntryTable(), tableSize: data->indirectExportEntryTableSize, name: exportName)) {
490 QUrl request = urlAt(index: indirectExport->moduleRequest);
491 auto dependentModule = engine->loadModule(url: request, referrer: this);
492 ScopedString importName(scope, runtimeStrings[indirectExport->importName]);
493 if (dependentModule.compiled) {
494 return dependentModule.compiled->resolveExportRecursively(exportName: importName, resolveSet);
495 } else if (dependentModule.native) {
496 if (exportName->toQString() == QLatin1String("*"))
497 return dependentModule.native;
498 if (exportName->toQString() == QLatin1String("default"))
499 return nullptr;
500
501 request.setFragment(fragment: importName->toQString());
502 const auto fragment = engine->moduleForUrl(url: request);
503 if (fragment.native)
504 return fragment.native;
505
506 ScopedObject o(scope, dependentModule.native);
507 if (o)
508 return engine->registerNativeModule(url: request, module: o->get(name: importName));
509
510 return nullptr;
511 } else {
512 return nullptr;
513 }
514 }
515
516 if (exportName->toQString() == QLatin1String("default"))
517 return nullptr;
518
519 const Value *starResolution = nullptr;
520
521 for (uint i = 0; i < data->starExportEntryTableSize; ++i) {
522 const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i];
523 QUrl request = urlAt(index: entry.moduleRequest);
524 auto dependentModule = engine->loadModule(url: request, referrer: this);
525 const Value *resolution = nullptr;
526 if (dependentModule.compiled) {
527 resolution = dependentModule.compiled->resolveExportRecursively(
528 exportName, resolveSet);
529 } else if (dependentModule.native) {
530 if (exportName->toQString() == QLatin1String("*")) {
531 resolution = dependentModule.native;
532 } else if (exportName->toQString() != QLatin1String("default")) {
533 request.setFragment(fragment: exportName->toQString());
534 const auto fragment = engine->moduleForUrl(url: request);
535 if (fragment.native) {
536 resolution = fragment.native;
537 } else {
538 ScopedObject o(scope, dependentModule.native);
539 if (o)
540 resolution = engine->registerNativeModule(url: request, module: o->get(name: exportName));
541 }
542 }
543 }
544
545 // ### handle ambiguous
546 if (resolution) {
547 if (!starResolution) {
548 starResolution = resolution;
549 continue;
550 }
551 if (resolution != starResolution)
552 return nullptr;
553 }
554 }
555
556 return starResolution;
557}
558
559const CompiledData::ExportEntry *ExecutableCompilationUnit::lookupNameInExportTable(
560 const CompiledData::ExportEntry *firstExportEntry, int tableSize, QV4::String *name) const
561{
562 const CompiledData::ExportEntry *lastExportEntry = firstExportEntry + tableSize;
563 auto matchingExport = std::lower_bound(first: firstExportEntry, last: lastExportEntry, val: name, comp: [this](const CompiledData::ExportEntry &lhs, QV4::String *name) {
564 return stringAt(index: lhs.exportName) < name->toQString();
565 });
566 if (matchingExport == lastExportEntry || stringAt(index: matchingExport->exportName) != name->toQString())
567 return nullptr;
568 return matchingExport;
569}
570
571void ExecutableCompilationUnit::getExportedNamesRecursively(
572 QStringList *names, QVector<const ExecutableCompilationUnit*> *exportNameSet,
573 bool includeDefaultExport) const
574{
575 if (exportNameSet->contains(t: this))
576 return;
577 exportNameSet->append(t: this);
578
579 const auto append = [names, includeDefaultExport](const QString &name) {
580 if (!includeDefaultExport && name == QLatin1String("default"))
581 return;
582 names->append(t: name);
583 };
584
585 const CompiledData::Unit *data = m_compilationUnit->data;
586
587 Q_ASSERT(data);
588 Q_ASSERT(engine);
589
590 for (uint i = 0; i < data->localExportEntryTableSize; ++i) {
591 const CompiledData::ExportEntry &entry = data->localExportEntryTable()[i];
592 append(stringAt(index: entry.exportName));
593 }
594
595 for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) {
596 const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i];
597 append(stringAt(index: entry.exportName));
598 }
599
600 for (uint i = 0; i < data->starExportEntryTableSize; ++i) {
601 const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i];
602 auto dependentModule = engine->loadModule(url: urlAt(index: entry.moduleRequest), referrer: this);
603 if (dependentModule.compiled) {
604 dependentModule.compiled->getExportedNamesRecursively(
605 names, exportNameSet, /*includeDefaultExport*/false);
606 } else if (dependentModule.native) {
607 Scope scope(engine);
608 ScopedObject o(scope, dependentModule.native);
609 ObjectIterator iterator(scope, o, ObjectIterator::EnumerableOnly);
610 while (true) {
611 ScopedValue val(scope, iterator.nextPropertyNameAsString());
612 if (val->isNull())
613 break;
614 append(val->toQString());
615 }
616 }
617 }
618}
619
620void ExecutableCompilationUnit::evaluate()
621{
622 Q_ASSERT(engine);
623
624 QV4::Scope scope(engine);
625 QV4::Scoped<Module> mod(scope, module());
626 mod->evaluate();
627}
628
629void ExecutableCompilationUnit::evaluateModuleRequests()
630{
631 Q_ASSERT(engine);
632
633 const QStringList moduleRequests = m_compilationUnit->moduleRequests();
634 for (const QString &request: moduleRequests) {
635 auto dependentModule = engine->loadModule(url: QUrl(request), referrer: this);
636 if (dependentModule.native)
637 continue;
638
639 if (engine->hasException)
640 return;
641
642 Q_ASSERT(dependentModule.compiled);
643 dependentModule.compiled->evaluate();
644 if (engine->hasException)
645 return;
646 }
647}
648
649QString ExecutableCompilationUnit::bindingValueAsString(const CompiledData::Binding *binding) const
650{
651#if QT_CONFIG(translation)
652 using namespace CompiledData;
653 bool byId = false;
654 switch (binding->type()) {
655 case Binding::Type_TranslationById:
656 byId = true;
657 Q_FALLTHROUGH();
658 case Binding::Type_Translation: {
659 return translateFrom(index: { .index: binding->value.translationDataIndex, .byId: byId });
660 }
661 default:
662 break;
663 }
664#endif
665 return m_compilationUnit->bindingValueAsString(binding);
666}
667
668QString ExecutableCompilationUnit::translateFrom(TranslationDataIndex index) const
669{
670#if !QT_CONFIG(translation)
671 return QString();
672#else
673 const CompiledData::TranslationData &translation = unitData()->translations()[index.index];
674
675 if (index.byId) {
676 QByteArray id = stringAt(index: translation.stringIndex).toUtf8();
677 return qtTrId(id: id.constData(), n: translation.number);
678 }
679
680 const auto fileContext = [this]() {
681 // This code must match that in the qsTr() implementation
682 const QString &path = fileName();
683 int lastSlash = path.lastIndexOf(c: QLatin1Char('/'));
684
685 QStringView context = (lastSlash > -1)
686 ? QStringView{ path }.mid(pos: lastSlash + 1, n: path.size() - lastSlash - 5)
687 : QStringView();
688 return context.toUtf8();
689 };
690
691 const bool hasContext
692 = translation.contextIndex != QV4::CompiledData::TranslationData::NoContextIndex;
693 QByteArray context;
694 if (hasContext) {
695 context = stringAt(index: translation.contextIndex).toUtf8();
696 } else {
697 auto pragmaTranslationContext = unitData()->translationContextIndex();
698 context = stringAt(index: *pragmaTranslationContext).toUtf8();
699 context = context.isEmpty() ? fileContext() : context;
700 }
701
702 QByteArray comment = stringAt(index: translation.commentIndex).toUtf8();
703 QByteArray text = stringAt(index: translation.stringIndex).toUtf8();
704 return QCoreApplication::translate(context, key: text, disambiguation: comment, n: translation.number);
705#endif
706}
707
708Heap::Module *ExecutableCompilationUnit::module() const
709{
710 if (const Module *m = m_valueOrModule.as<QV4::Module>())
711 return m->d();
712 return nullptr;
713}
714
715void ExecutableCompilationUnit::setModule(Heap::Module *module)
716{
717 m_valueOrModule = module;
718}
719
720} // namespace QV4
721
722QT_END_NAMESPACE
723

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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