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.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 | |
455 | const 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 | |
559 | const 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 | |
571 | void 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 | |
620 | void 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 | |
629 | void 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 | |
649 | QString 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 | |
668 | QString 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 | |
708 | Heap::Module *ExecutableCompilationUnit::module() const |
709 | { |
710 | if (const Module *m = m_valueOrModule.as<QV4::Module>()) |
711 | return m->d(); |
712 | return nullptr; |
713 | } |
714 | |
715 | void ExecutableCompilationUnit::setModule(Heap::Module *module) |
716 | { |
717 | m_valueOrModule = module; |
718 | } |
719 | |
720 | } // namespace QV4 |
721 | |
722 | QT_END_NAMESPACE |
723 |
Definitions
- ExecutableCompilationUnit
- ExecutableCompilationUnit
- ~ExecutableCompilationUnit
- toString
- dumpConstantTable
- populate
- templateObjectAt
- clear
- markObjects
- createNamedObjectsPerComponent
- create
- instantiate
- resolveExportRecursively
- lookupNameInExportTable
- getExportedNamesRecursively
- evaluate
- evaluateModuleRequests
- bindingValueAsString
- translateFrom
- module
Start learning QML with our Intro Training
Find out more