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 | |
29 | QT_BEGIN_NAMESPACE |
30 | |
31 | namespace QV4 { |
32 | |
33 | ExecutableCompilationUnit::ExecutableCompilationUnit() = default; |
34 | |
35 | ExecutableCompilationUnit::ExecutableCompilationUnit( |
36 | QQmlRefPointer<CompiledData::CompilationUnit> &&compilationUnit) |
37 | : m_compilationUnit(std::move(compilationUnit)) |
38 | { |
39 | constants = m_compilationUnit->constants; |
40 | } |
41 | |
42 | ExecutableCompilationUnit::~ExecutableCompilationUnit() |
43 | { |
44 | if (engine) |
45 | clear(); |
46 | } |
47 | |
48 | static 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 | |
63 | static 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 | |
73 | void 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 | |
208 | Heap::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 | |
242 | void 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 | |
268 | void 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 | |
306 | IdentifierHash 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 | |
319 | QQmlRefPointer<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 | |
329 | Heap::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) |
356 | dependentModuleUnit->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 | if (const auto module = engine->loadModule(url: url, referrer: this)) { |
372 | const Value *valuePtr = module->resolveExport(exportName: importName); |
373 | if (!valuePtr) { |
374 | QString referenceErrorMessage = QStringLiteral("Unable to resolve import reference " ); |
375 | referenceErrorMessage += importName->toQString(); |
376 | QV4::ScopedValue compiledValue(scope, module->value()); |
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 | } |
384 | } |
385 | |
386 | const auto throwReferenceError = [&](const CompiledData::ExportEntry &entry, const QString &importName) { |
387 | QString referenceErrorMessage = QStringLiteral("Unable to resolve re-export reference " ); |
388 | referenceErrorMessage += importName; |
389 | engine->throwReferenceError( |
390 | value: referenceErrorMessage, fileName: fileName(), |
391 | lineNumber: entry.location.line(), column: entry.location.column()); |
392 | }; |
393 | |
394 | for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) { |
395 | const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i]; |
396 | if (auto dependentModule = engine->loadModule(url: urlAt(index: entry.moduleRequest), referrer: this)) { |
397 | ScopedString importName(scope, runtimeStrings[entry.importName]); |
398 | if (!dependentModule->resolveExport(exportName: importName)) { |
399 | throwReferenceError(entry, importName->toQString()); |
400 | return nullptr; |
401 | } |
402 | } |
403 | } |
404 | |
405 | return module->d(); |
406 | } |
407 | |
408 | const Value *ExecutableCompilationUnit::resolveExportRecursively( |
409 | QV4::String *exportName, QVector<ResolveSetEntry> *resolveSet) |
410 | { |
411 | if (!module()) |
412 | return nullptr; |
413 | |
414 | for (const auto &entry: *resolveSet) |
415 | if (entry.module == this && entry.exportName->isEqualTo(other: exportName)) |
416 | return nullptr; |
417 | |
418 | (*resolveSet) << ResolveSetEntry(this, exportName); |
419 | |
420 | if (exportName->toQString() == QLatin1String("*" )) |
421 | return &module()->self; |
422 | |
423 | const CompiledData::Unit *data = m_compilationUnit->data; |
424 | |
425 | Q_ASSERT(data); |
426 | Q_ASSERT(engine); |
427 | |
428 | Scope scope(engine); |
429 | |
430 | if (auto localExport = lookupNameInExportTable( |
431 | firstExportEntry: data->localExportEntryTable(), tableSize: data->localExportEntryTableSize, name: exportName)) { |
432 | ScopedString localName(scope, runtimeStrings[localExport->localName]); |
433 | uint index = module()->scope->internalClass->indexOfValueOrGetter(id: localName->toPropertyKey()); |
434 | if (index == UINT_MAX) |
435 | return nullptr; |
436 | if (index >= module()->scope->locals.size) |
437 | return &(imports[index - module()->scope->locals.size]->asValue<Value>()); |
438 | return &module()->scope->locals[index]; |
439 | } |
440 | |
441 | if (auto indirectExport = lookupNameInExportTable( |
442 | firstExportEntry: data->indirectExportEntryTable(), tableSize: data->indirectExportEntryTableSize, name: exportName)) { |
443 | QUrl request = urlAt(index: indirectExport->moduleRequest); |
444 | if (auto dependentModule = engine->loadModule(url: request, referrer: this)) { |
445 | ScopedString importName(scope, runtimeStrings[indirectExport->importName]); |
446 | return dependentModule->resolveExportRecursively(exportName: importName, resolveSet); |
447 | } |
448 | return nullptr; |
449 | } |
450 | |
451 | if (exportName->toQString() == QLatin1String("default" )) |
452 | return nullptr; |
453 | |
454 | const Value *starResolution = nullptr; |
455 | |
456 | for (uint i = 0; i < data->starExportEntryTableSize; ++i) { |
457 | const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i]; |
458 | QUrl request = urlAt(index: entry.moduleRequest); |
459 | const Value *resolution = nullptr; |
460 | if (auto dependentModule = engine->loadModule(url: request, referrer: this)) |
461 | resolution = dependentModule->resolveExportRecursively(exportName, resolveSet); |
462 | |
463 | // ### handle ambiguous |
464 | if (resolution) { |
465 | if (!starResolution) { |
466 | starResolution = resolution; |
467 | continue; |
468 | } |
469 | if (resolution != starResolution) |
470 | return nullptr; |
471 | } |
472 | } |
473 | |
474 | return starResolution; |
475 | } |
476 | |
477 | const CompiledData::ExportEntry *ExecutableCompilationUnit::lookupNameInExportTable( |
478 | const CompiledData::ExportEntry *firstExportEntry, int tableSize, QV4::String *name) const |
479 | { |
480 | const CompiledData::ExportEntry *lastExportEntry = firstExportEntry + tableSize; |
481 | auto matchingExport = std::lower_bound(first: firstExportEntry, last: lastExportEntry, val: name, comp: [this](const CompiledData::ExportEntry &lhs, QV4::String *name) { |
482 | return stringAt(index: lhs.exportName) < name->toQString(); |
483 | }); |
484 | if (matchingExport == lastExportEntry || stringAt(index: matchingExport->exportName) != name->toQString()) |
485 | return nullptr; |
486 | return matchingExport; |
487 | } |
488 | |
489 | void ExecutableCompilationUnit::getExportedNamesRecursively( |
490 | QStringList *names, QVector<const ExecutableCompilationUnit*> *exportNameSet, |
491 | bool includeDefaultExport) const |
492 | { |
493 | if (exportNameSet->contains(t: this)) |
494 | return; |
495 | exportNameSet->append(t: this); |
496 | |
497 | const auto append = [names, includeDefaultExport](const QString &name) { |
498 | if (!includeDefaultExport && name == QLatin1String("default" )) |
499 | return; |
500 | names->append(t: name); |
501 | }; |
502 | |
503 | const CompiledData::Unit *data = m_compilationUnit->data; |
504 | |
505 | Q_ASSERT(data); |
506 | Q_ASSERT(engine); |
507 | |
508 | for (uint i = 0; i < data->localExportEntryTableSize; ++i) { |
509 | const CompiledData::ExportEntry &entry = data->localExportEntryTable()[i]; |
510 | append(stringAt(index: entry.exportName)); |
511 | } |
512 | |
513 | for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) { |
514 | const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i]; |
515 | append(stringAt(index: entry.exportName)); |
516 | } |
517 | |
518 | for (uint i = 0; i < data->starExportEntryTableSize; ++i) { |
519 | const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i]; |
520 | if (auto dependentModule = engine->loadModule(url: urlAt(index: entry.moduleRequest), referrer: this)) { |
521 | dependentModule->getExportedNamesRecursively( |
522 | names, exportNameSet, /*includeDefaultExport*/false); |
523 | } |
524 | } |
525 | } |
526 | |
527 | void ExecutableCompilationUnit::evaluate() |
528 | { |
529 | Q_ASSERT(engine); |
530 | |
531 | QV4::Scope scope(engine); |
532 | QV4::Scoped<Module> mod(scope, module()); |
533 | mod->evaluate(); |
534 | } |
535 | |
536 | void ExecutableCompilationUnit::evaluateModuleRequests() |
537 | { |
538 | Q_ASSERT(engine); |
539 | |
540 | const QStringList moduleRequests = m_compilationUnit->moduleRequests(); |
541 | for (const QString &request: moduleRequests) { |
542 | auto dependentModule = engine->loadModule(url: QUrl(request), referrer: this); |
543 | |
544 | if (engine->hasException) |
545 | return; |
546 | |
547 | Q_ASSERT(dependentModule); |
548 | dependentModule->evaluate(); |
549 | if (engine->hasException) |
550 | return; |
551 | } |
552 | } |
553 | |
554 | QString ExecutableCompilationUnit::bindingValueAsString(const CompiledData::Binding *binding) const |
555 | { |
556 | #if QT_CONFIG(translation) |
557 | using namespace CompiledData; |
558 | bool byId = false; |
559 | switch (binding->type()) { |
560 | case Binding::Type_TranslationById: |
561 | byId = true; |
562 | Q_FALLTHROUGH(); |
563 | case Binding::Type_Translation: { |
564 | return translateFrom(index: { .index: binding->value.translationDataIndex, .byId: byId }); |
565 | } |
566 | default: |
567 | break; |
568 | } |
569 | #endif |
570 | return m_compilationUnit->bindingValueAsString(binding); |
571 | } |
572 | |
573 | QString ExecutableCompilationUnit::translateFrom(TranslationDataIndex index) const |
574 | { |
575 | #if !QT_CONFIG(translation) |
576 | return QString(); |
577 | #else |
578 | const CompiledData::TranslationData &translation = unitData()->translations()[index.index]; |
579 | |
580 | if (index.byId) { |
581 | QByteArray id = stringAt(index: translation.stringIndex).toUtf8(); |
582 | return qtTrId(id: id.constData(), n: translation.number); |
583 | } |
584 | |
585 | const auto fileContext = [this]() { |
586 | // This code must match that in the qsTr() implementation |
587 | const QString &path = fileName(); |
588 | int lastSlash = path.lastIndexOf(c: QLatin1Char('/')); |
589 | |
590 | QStringView context = (lastSlash > -1) |
591 | ? QStringView{ path }.mid(pos: lastSlash + 1, n: path.size() - lastSlash - 5) |
592 | : QStringView(); |
593 | return context.toUtf8(); |
594 | }; |
595 | |
596 | const bool hasContext |
597 | = translation.contextIndex != QV4::CompiledData::TranslationData::NoContextIndex; |
598 | QByteArray context; |
599 | if (hasContext) { |
600 | context = stringAt(index: translation.contextIndex).toUtf8(); |
601 | } else { |
602 | auto pragmaTranslationContext = unitData()->translationContextIndex(); |
603 | context = stringAt(index: *pragmaTranslationContext).toUtf8(); |
604 | context = context.isEmpty() ? fileContext() : context; |
605 | } |
606 | |
607 | QByteArray = stringAt(index: translation.commentIndex).toUtf8(); |
608 | QByteArray text = stringAt(index: translation.stringIndex).toUtf8(); |
609 | return QCoreApplication::translate(context, key: text, disambiguation: comment, n: translation.number); |
610 | #endif |
611 | } |
612 | |
613 | Heap::Module *ExecutableCompilationUnit::module() const |
614 | { |
615 | if (const Module *m = m_valueOrModule.as<QV4::Module>()) |
616 | return m->d(); |
617 | return nullptr; |
618 | } |
619 | |
620 | void ExecutableCompilationUnit::setModule(Heap::Module *module) |
621 | { |
622 | m_valueOrModule = module; |
623 | } |
624 | |
625 | } // namespace QV4 |
626 | |
627 | QT_END_NAMESPACE |
628 | |