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
qv4compiler.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2018 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant
5
6#include <qv4compiler_p.h>
7#include <qv4codegen_p.h>
8#include <private/qv4compileddata_p.h>
9#include <private/qv4staticvalue_p.h>
10#include <private/qv4alloca_p.h>
11#include <private/qqmljslexer_p.h>
12#include <private/qqmljsast_p.h>
13#include <private/qqmlirbuilder_p.h>
14#include <QCryptographicHash>
15#include <QtEndian>
16
17// Efficient implementation that takes advantage of powers of two.
18
20namespace QtPrivate { // Disambiguate from WTF::roundUpToMultipleOf
21static inline size_t roundUpToMultipleOf(size_t divisor, size_t x)
22{
23 Q_ASSERT(divisor && !(divisor & (divisor - 1)));
24 const size_t remainderMask = divisor - 1;
25 return (x + remainderMask) & ~remainderMask;
26}
27}
28QT_END_NAMESPACE
29
30QV4::Compiler::StringTableGenerator::StringTableGenerator()
31{
32 clear();
33}
34
35int QV4::Compiler::StringTableGenerator::registerString(const QString &str)
36{
37 Q_ASSERT(!frozen);
38 QHash<QString, int>::ConstIterator it = stringToId.constFind(str);
39 if (it != stringToId.cend())
40 return *it;
41 stringToId.insert(str, strings.size());
42 strings.append(str);
43 stringDataSize += QV4::CompiledData::String::calculateSize(str);
44 return strings.size() - 1;
45}
46
47int QV4::Compiler::StringTableGenerator::getStringId(const QString &string) const
48{
49 Q_ASSERT(stringToId.contains(string));
50 return stringToId.value(string);
51}
52
53void QV4::Compiler::StringTableGenerator::clear()
54{
55 strings.clear();
56 stringToId.clear();
57 stringDataSize = 0;
58 frozen = false;
59}
60
61void QV4::Compiler::StringTableGenerator::initializeFromBackingUnit(const QV4::CompiledData::Unit *unit)
62{
63 clear();
64 for (uint i = 0; i < unit->stringTableSize; ++i)
65 registerString(unit->stringAtInternal(i));
66 backingUnitTableSize = unit->stringTableSize;
67 stringDataSize = 0;
68}
69
70void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit)
71{
72 char *dataStart = reinterpret_cast<char *>(unit);
73 quint32_le *stringTable = reinterpret_cast<quint32_le *>(dataStart + unit->offsetToStringTable);
74 char *stringData = reinterpret_cast<char *>(stringTable)
75 + QtPrivate::roundUpToMultipleOf(8, unit->stringTableSize * sizeof(uint));
76 for (int i = backingUnitTableSize ; i < strings.size(); ++i) {
77 const int index = i - backingUnitTableSize;
78 stringTable[index] = stringData - dataStart;
79 const QString &qstr = strings.at(i);
80
81 QV4::CompiledData::String *s = reinterpret_cast<QV4::CompiledData::String *>(stringData);
82 Q_ASSERT(reinterpret_cast<uintptr_t>(s) % alignof(QV4::CompiledData::String) == 0);
83 Q_ASSERT(qstr.size() >= 0);
84 s->size = qstr.size();
85
86 ushort *uc = reinterpret_cast<ushort *>(reinterpret_cast<char *>(s) + sizeof(*s));
87 qToLittleEndian<ushort>(qstr.constData(), s->size, uc);
88 uc[s->size] = 0;
89
90 stringData += QV4::CompiledData::String::calculateSize(qstr);
91 }
92}
93
94void QV4::Compiler::JSUnitGenerator::generateUnitChecksum(QV4::CompiledData::Unit *unit)
95{
96#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
97 QCryptographicHash hash(QCryptographicHash::Md5);
98
99 const int checksummableDataOffset
100 = offsetof(QV4::CompiledData::Unit, md5Checksum) + sizeof(unit->md5Checksum);
101
102 const char *dataPtr = reinterpret_cast<const char *>(unit) + checksummableDataOffset;
103 hash.addData({dataPtr, qsizetype(unit->unitSize - checksummableDataOffset)});
104
105 QByteArray checksum = hash.result();
106 Q_ASSERT(checksum.size() == sizeof(unit->md5Checksum));
107 memcpy(unit->md5Checksum, checksum.constData(), sizeof(unit->md5Checksum));
108#else
109 memset(unit->md5Checksum, 0, sizeof(unit->md5Checksum));
110#endif
111}
112
113QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QV4::Compiler::Module *module)
114 : module(module)
115{
116 // Make sure the empty string always gets index 0
117 registerString(QString());
118}
119
120int QV4::Compiler::JSUnitGenerator::registerGetterLookup(const QString &name, LookupMode mode)
121{
122 return registerGetterLookup(registerString(name), mode);
123}
124
125static QV4::CompiledData::Lookup::Mode lookupMode(QV4::Compiler::JSUnitGenerator::LookupMode mode)
126{
127 return mode == QV4::Compiler::JSUnitGenerator::LookupForCall
128 ? QV4::CompiledData::Lookup::Mode_ForCall
129 : QV4::CompiledData::Lookup::Mode_ForStorage;
130}
131
132int QV4::Compiler::JSUnitGenerator::registerGetterLookup(int nameIndex, LookupMode mode)
133{
134 lookups << CompiledData::Lookup(
135 CompiledData::Lookup::Type_Getter, lookupMode(mode), nameIndex);
136 return lookups.size() - 1;
137}
138
139int QV4::Compiler::JSUnitGenerator::registerSetterLookup(const QString &name)
140{
141 return registerSetterLookup(registerString(name));
142}
143
144int QV4::Compiler::JSUnitGenerator::registerSetterLookup(int nameIndex)
145{
146 lookups << CompiledData::Lookup(
147 CompiledData::Lookup::Type_Setter,
148 CompiledData::Lookup::Mode_ForStorage, nameIndex);
149 return lookups.size() - 1;
150}
151
152int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex, LookupMode mode)
153{
154 lookups << CompiledData::Lookup(
155 CompiledData::Lookup::Type_GlobalGetter, lookupMode(mode), nameIndex);
156 return lookups.size() - 1;
157}
158
159int QV4::Compiler::JSUnitGenerator::registerQmlContextPropertyGetterLookup(
160 int nameIndex, LookupMode mode)
161{
162 lookups << CompiledData::Lookup(
163 CompiledData::Lookup::Type_QmlContextPropertyGetter, lookupMode(mode),
164 nameIndex);
165 return lookups.size() - 1;
166}
167
168int QV4::Compiler::JSUnitGenerator::registerRegExp(QQmlJS::AST::RegExpLiteral *regexp)
169{
170 quint32 flags = 0;
171 if (regexp->flags & QQmlJS::Lexer::RegExp_Global)
172 flags |= CompiledData::RegExp::RegExp_Global;
173 if (regexp->flags & QQmlJS::Lexer::RegExp_IgnoreCase)
174 flags |= CompiledData::RegExp::RegExp_IgnoreCase;
175 if (regexp->flags & QQmlJS::Lexer::RegExp_Multiline)
176 flags |= CompiledData::RegExp::RegExp_Multiline;
177 if (regexp->flags & QQmlJS::Lexer::RegExp_Unicode)
178 flags |= CompiledData::RegExp::RegExp_Unicode;
179 if (regexp->flags & QQmlJS::Lexer::RegExp_Sticky)
180 flags |= CompiledData::RegExp::RegExp_Sticky;
181
182 regexps.append(CompiledData::RegExp(flags, registerString(regexp->pattern.toString())));
183 return regexps.size() - 1;
184}
185
186int QV4::Compiler::JSUnitGenerator::registerConstant(QV4::ReturnedValue v)
187{
188 int idx = constants.indexOf(v);
189 if (idx >= 0)
190 return idx;
191 constants.append(v);
192 return constants.size() - 1;
193}
194
195QV4::ReturnedValue QV4::Compiler::JSUnitGenerator::constant(int idx) const
196{
197 return constants.at(idx);
198}
199
200// The JSClass object and its members are stored contiguously in the jsClassData.
201// In order to get to the members you have to skip over the JSClass, therefore +1.
202static constexpr qsizetype jsClassMembersOffset = 1;
203
204int QV4::Compiler::JSUnitGenerator::registerJSClass(const QStringList &members)
205{
206 // ### re-use existing class definitions.
207
208 const int size = CompiledData::JSClass::calculateSize(members.size());
209 jsClassOffsets.append(jsClassData.size());
210 const int oldSize = jsClassData.size();
211 jsClassData.resize(jsClassData.size() + size);
212 memset(jsClassData.data() + oldSize, 0, size);
213
214 CompiledData::JSClass *jsClass = reinterpret_cast<CompiledData::JSClass*>(jsClassData.data() + oldSize);
215 jsClass->nMembers = members.size();
216 CompiledData::JSClassMember *member
217 = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + jsClassMembersOffset);
218
219 for (const auto &name : members) {
220 member->set(registerString(name), false);
221 ++member;
222 }
223
224 return jsClassOffsets.size() - 1;
225}
226
227int QV4::Compiler::JSUnitGenerator::jsClassSize(int jsClassId) const
228{
229 const CompiledData::JSClass *jsClass
230 = reinterpret_cast<const CompiledData::JSClass*>(
231 jsClassData.data() + jsClassOffsets[jsClassId]);
232 return jsClass->nMembers;
233}
234
235QString QV4::Compiler::JSUnitGenerator::jsClassMember(int jsClassId, int member) const
236{
237 const CompiledData::JSClass *jsClass = reinterpret_cast<const CompiledData::JSClass*>(
238 jsClassData.data() + jsClassOffsets[jsClassId]);
239 Q_ASSERT(member >= 0);
240 Q_ASSERT(uint(member) < jsClass->nMembers);
241 const CompiledData::JSClassMember *members
242 = reinterpret_cast<const CompiledData::JSClassMember*>(jsClass + jsClassMembersOffset);
243 return stringForIndex(members[member].nameOffset());
244}
245
246int QV4::Compiler::JSUnitGenerator::registerTranslation(const QV4::CompiledData::TranslationData &translation)
247{
248 translations.append(translation);
249 return translations.size() - 1;
250}
251
252QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorOption option)
253{
254 Q_ALLOCA_INIT();
255 const auto registerTypeStrings = [this](QQmlJS::AST::Type *type) {
256 if (!type)
257 return;
258
259 if (type->typeArgument) {
260 registerString(type->typeArgument->toString());
261 registerString(type->typeId->toString());
262 }
263 registerString(type->toString());
264 };
265
266 registerString(module->fileName);
267 registerString(module->finalUrl);
268 for (Context *f : std::as_const(module->functions)) {
269 registerString(f->name);
270 registerTypeStrings(f->returnType);
271 for (int i = 0; i < f->arguments.size(); ++i) {
272 registerString(f->arguments.at(i).id);
273 if (const QQmlJS::AST::TypeAnnotation *annotation
274 = f->arguments.at(i).typeAnnotation.data()) {
275 registerTypeStrings(annotation->type);
276 }
277 }
278 for (int i = 0; i < f->locals.size(); ++i)
279 registerString(f->locals.at(i));
280 }
281 for (Context *c : std::as_const(module->blocks)) {
282 for (int i = 0; i < c->locals.size(); ++i)
283 registerString(c->locals.at(i));
284 }
285 {
286 const auto registerExportEntry = [this](const Compiler::ExportEntry &entry) {
287 registerString(entry.exportName);
288 registerString(entry.moduleRequest);
289 registerString(entry.importName);
290 registerString(entry.localName);
291 };
292 std::for_each(module->localExportEntries.constBegin(), module->localExportEntries.constEnd(), registerExportEntry);
293 std::for_each(module->indirectExportEntries.constBegin(), module->indirectExportEntries.constEnd(), registerExportEntry);
294 std::for_each(module->starExportEntries.constBegin(), module->starExportEntries.constEnd(), registerExportEntry);
295 }
296 {
297 for (const auto &entry: std::as_const(module->importEntries)) {
298 registerString(entry.moduleRequest);
299 registerString(entry.importName);
300 registerString(entry.localName);
301 }
302
303 for (const QString &request: std::as_const(module->moduleRequests))
304 registerString(request);
305 }
306
307 Q_ALLOCA_VAR(quint32_le, blockClassAndFunctionOffsets, (module->functions.size() + module->classes.size() + module->templateObjects.size() + module->blocks.size()) * sizeof(quint32_le));
308 uint jsClassDataOffset = 0;
309
310 char *dataPtr;
311 CompiledData::Unit *unit;
312 {
313 QV4::CompiledData::Unit tempHeader = generateHeader(option, blockClassAndFunctionOffsets, &jsClassDataOffset);
314 dataPtr = reinterpret_cast<char *>(malloc(tempHeader.unitSize));
315 memset(dataPtr, 0, tempHeader.unitSize);
316 memcpy(&unit, &dataPtr, sizeof(CompiledData::Unit*));
317 memcpy(unit, &tempHeader, sizeof(tempHeader));
318 }
319
320 memcpy(dataPtr + unit->offsetToFunctionTable, blockClassAndFunctionOffsets, unit->functionTableSize * sizeof(quint32_le));
321 memcpy(dataPtr + unit->offsetToClassTable, blockClassAndFunctionOffsets + unit->functionTableSize, unit->classTableSize * sizeof(quint32_le));
322 memcpy(dataPtr + unit->offsetToTemplateObjectTable, blockClassAndFunctionOffsets + unit->functionTableSize + unit->classTableSize, unit->templateObjectTableSize * sizeof(quint32_le));
323 memcpy(dataPtr + unit->offsetToBlockTable, blockClassAndFunctionOffsets + unit->functionTableSize + unit->classTableSize + unit->templateObjectTableSize, unit->blockTableSize * sizeof(quint32_le));
324
325 for (int i = 0; i < module->functions.size(); ++i) {
326 Context *function = module->functions.at(i);
327 if (function == module->rootContext)
328 unit->indexOfRootFunction = i;
329
330 writeFunction(dataPtr + blockClassAndFunctionOffsets[i], function);
331 }
332
333 for (int i = 0; i < module->classes.size(); ++i) {
334 const Class &c = module->classes.at(i);
335
336 writeClass(dataPtr + blockClassAndFunctionOffsets[i + module->functions.size()], c);
337 }
338
339 for (int i = 0; i < module->templateObjects.size(); ++i) {
340 const TemplateObject &t = module->templateObjects.at(i);
341
342 writeTemplateObject(dataPtr + blockClassAndFunctionOffsets[i + module->functions.size() + module->classes.size()], t);
343 }
344
345 for (int i = 0; i < module->blocks.size(); ++i) {
346 Context *block = module->blocks.at(i);
347
348 writeBlock(dataPtr + blockClassAndFunctionOffsets[i + module->classes.size() + module->templateObjects.size() + module->functions.size()], block);
349 }
350
351 CompiledData::Lookup *lookupsToWrite = reinterpret_cast<CompiledData::Lookup*>(dataPtr + unit->offsetToLookupTable);
352 for (const CompiledData::Lookup &l : std::as_const(lookups))
353 *lookupsToWrite++ = l;
354
355 CompiledData::RegExp *regexpTable = reinterpret_cast<CompiledData::RegExp *>(dataPtr + unit->offsetToRegexpTable);
356 if (regexps.size())
357 memcpy(regexpTable, regexps.constData(), regexps.size() * sizeof(*regexpTable));
358
359#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
360 ReturnedValue *constantTable = reinterpret_cast<ReturnedValue *>(dataPtr + unit->offsetToConstantTable);
361 if (constants.size())
362 memcpy(constantTable, constants.constData(), constants.size() * sizeof(ReturnedValue));
363#else
364 quint64_le *constantTable = reinterpret_cast<quint64_le *>(dataPtr + unit->offsetToConstantTable);
365 for (int i = 0; i < constants.count(); ++i)
366 constantTable[i] = constants.at(i);
367#endif
368
369 {
370 if (jsClassData.size())
371 memcpy(dataPtr + jsClassDataOffset, jsClassData.constData(), jsClassData.size());
372
373 // write js classes and js class lookup table
374 quint32_le *jsClassOffsetTable = reinterpret_cast<quint32_le *>(dataPtr + unit->offsetToJSClassTable);
375 for (int i = 0; i < jsClassOffsets.size(); ++i)
376 jsClassOffsetTable[i] = jsClassDataOffset + jsClassOffsets.at(i);
377 }
378
379 if (translations.size()) {
380 memcpy(dataPtr + unit->offsetToTranslationTable, translations.constData(), translations.size() * sizeof(CompiledData::TranslationData));
381 }
382
383 {
384 const auto populateExportEntryTable = [this, dataPtr](const QList<Compiler::ExportEntry> &table, quint32_le offset) {
385 CompiledData::ExportEntry *entryToWrite = reinterpret_cast<CompiledData::ExportEntry *>(dataPtr + offset);
386 for (const Compiler::ExportEntry &entry: table) {
387 entryToWrite->exportName = getStringId(entry.exportName);
388 entryToWrite->moduleRequest = getStringId(entry.moduleRequest);
389 entryToWrite->importName = getStringId(entry.importName);
390 entryToWrite->localName = getStringId(entry.localName);
391 entryToWrite->location = entry.location;
392 entryToWrite++;
393 }
394 };
395 populateExportEntryTable(module->localExportEntries, unit->offsetToLocalExportEntryTable);
396 populateExportEntryTable(module->indirectExportEntries, unit->offsetToIndirectExportEntryTable);
397 populateExportEntryTable(module->starExportEntries, unit->offsetToStarExportEntryTable);
398 }
399
400 {
401 CompiledData::ImportEntry *entryToWrite = reinterpret_cast<CompiledData::ImportEntry *>(dataPtr + unit->offsetToImportEntryTable);
402 for (const Compiler::ImportEntry &entry: std::as_const(module->importEntries)) {
403 entryToWrite->moduleRequest = getStringId(entry.moduleRequest);
404 entryToWrite->importName = getStringId(entry.importName);
405 entryToWrite->localName = getStringId(entry.localName);
406 entryToWrite->location = entry.location;
407 entryToWrite++;
408 }
409 }
410
411 {
412 quint32_le *moduleRequestEntryToWrite = reinterpret_cast<quint32_le *>(dataPtr + unit->offsetToModuleRequestTable);
413 for (const QString &moduleRequest: std::as_const(module->moduleRequests)) {
414 *moduleRequestEntryToWrite = getStringId(moduleRequest);
415 moduleRequestEntryToWrite++;
416 }
417 }
418
419 // write strings and string table
420 if (option == GenerateWithStringTable)
421 stringTable.serialize(unit);
422
423 generateUnitChecksum(unit);
424
425 return unit;
426}
427
428void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Context *irFunction) const
429{
430 QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f;
431
432 quint32 currentOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, sizeof(*function)));
433
434 function->nameIndex = getStringId(irFunction->name);
435 function->flags = 0;
436 if (irFunction->isStrict)
437 function->flags |= CompiledData::Function::IsStrict;
438 if (irFunction->isArrowFunction)
439 function->flags |= CompiledData::Function::IsArrowFunction;
440 if (irFunction->isGenerator)
441 function->flags |= CompiledData::Function::IsGenerator;
442 if (irFunction->returnsClosure)
443 function->flags |= CompiledData::Function::IsClosureWrapper;
444
445 if (!irFunction->returnsClosure
446 || (irFunction->usesArgumentsObject == Context::UsesArgumentsObject::Used)
447 || irFunction->innerFunctionAccessesThis
448 || irFunction->innerFunctionAccessesNewTarget) {
449 // If the inner function does things with this and new.target we need to do some work in
450 // the outer function. Then we shouldn't directly access the nested function.
451 function->nestedFunctionIndex = std::numeric_limits<uint32_t>::max();
452 } else {
453 // Otherwise we can directly use the nested function.
454 function->nestedFunctionIndex
455 = quint32(module->functions.indexOf(irFunction->nestedContexts.first()));
456 }
457
458 function->length = irFunction->formals ? irFunction->formals->length() : 0;
459 function->nFormals = irFunction->arguments.size();
460 function->formalsOffset = currentOffset;
461 currentOffset += function->nFormals * sizeof(CompiledData::Parameter);
462
463 const auto idGenerator = [this](const QString &str) { return getStringId(str); };
464
465 QmlIR::Parameter::initType(&function->returnType, idGenerator, irFunction->returnType);
466
467 function->sizeOfLocalTemporalDeadZone = irFunction->sizeOfLocalTemporalDeadZone;
468 function->sizeOfRegisterTemporalDeadZone = irFunction->sizeOfRegisterTemporalDeadZone;
469 function->firstTemporalDeadZoneRegister = irFunction->firstTemporalDeadZoneRegister;
470
471 function->nLocals = irFunction->locals.size();
472 function->localsOffset = currentOffset;
473 currentOffset += function->nLocals * sizeof(quint32);
474
475 function->nLineAndStatementNumbers
476 = irFunction->lineAndStatementNumberMapping.size();
477 Q_ASSERT(function->lineAndStatementNumberOffset() == currentOffset);
478 currentOffset += function->nLineAndStatementNumbers
479 * sizeof(CompiledData::CodeOffsetToLineAndStatement);
480
481 function->nRegisters = irFunction->registerCountInFunction;
482
483 if (!irFunction->labelInfo.empty()) {
484 function->nLabelInfos = quint32(irFunction->labelInfo.size());
485 Q_ASSERT(function->labelInfosOffset() == currentOffset);
486 currentOffset += function->nLabelInfos * sizeof(quint32);
487 }
488
489 function->location.set(irFunction->line, irFunction->column);
490
491 function->codeOffset = currentOffset;
492 function->codeSize = irFunction->code.size();
493
494 // write formals
495 CompiledData::Parameter *formals = (CompiledData::Parameter *)(f + function->formalsOffset);
496 for (int i = 0; i < irFunction->arguments.size(); ++i) {
497 auto *formal = &formals[i];
498 formal->nameIndex = getStringId(irFunction->arguments.at(i).id);
499 if (QQmlJS::AST::TypeAnnotation *annotation = irFunction->arguments.at(i).typeAnnotation.data())
500 QmlIR::Parameter::initType(&formal->type, idGenerator, annotation->type);
501 }
502
503 // write locals
504 quint32_le *locals = (quint32_le *)(f + function->localsOffset);
505 for (int i = 0; i < irFunction->locals.size(); ++i)
506 locals[i] = getStringId(irFunction->locals.at(i));
507
508 // write line and statement numbers
509 memcpy(f + function->lineAndStatementNumberOffset(),
510 irFunction->lineAndStatementNumberMapping.constData(),
511 irFunction->lineAndStatementNumberMapping.size()
512 * sizeof(CompiledData::CodeOffsetToLineAndStatement));
513
514 quint32_le *labels = (quint32_le *)(f + function->labelInfosOffset());
515 for (unsigned u : irFunction->labelInfo) {
516 *labels++ = u;
517 }
518
519 // write byte code
520 memcpy(f + function->codeOffset, irFunction->code.constData(), irFunction->code.size());
521}
522
523static_assert(int(QV4::Compiler::Class::Method::Regular) == int(QV4::CompiledData::Method::Regular), "Incompatible layout");
524static_assert(int(QV4::Compiler::Class::Method::Getter) == int(QV4::CompiledData::Method::Getter), "Incompatible layout");
525static_assert(int(QV4::Compiler::Class::Method::Setter) == int(QV4::CompiledData::Method::Setter), "Incompatible layout");
526
527void QV4::Compiler::JSUnitGenerator::writeClass(char *b, const QV4::Compiler::Class &c)
528{
529 QV4::CompiledData::Class *cls = reinterpret_cast<QV4::CompiledData::Class *>(b);
530
531 quint32 currentOffset = sizeof(QV4::CompiledData::Class);
532
533 QList<Class::Method> allMethods = c.staticMethods;
534 allMethods += c.methods;
535
536 cls->constructorFunction = c.constructorIndex;
537 cls->nameIndex = c.nameIndex;
538 cls->nMethods = c.methods.size();
539 cls->nStaticMethods = c.staticMethods.size();
540 cls->methodTableOffset = currentOffset;
541 CompiledData::Method *method = reinterpret_cast<CompiledData::Method *>(b + currentOffset);
542
543 // write methods
544 for (int i = 0; i < allMethods.size(); ++i) {
545 method->name = allMethods.at(i).nameIndex;
546 method->type = allMethods.at(i).type;
547 method->function = allMethods.at(i).functionIndex;
548 ++method;
549 }
550
551 static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
552 if (showCode) {
553 qDebug() << "=== Class" << stringForIndex(cls->nameIndex) << "static methods"
554 << cls->nStaticMethods << "methods" << cls->nMethods;
555 qDebug() << " constructor:" << cls->constructorFunction;
556 for (uint i = 0; i < cls->nStaticMethods + cls->nMethods; ++i) {
557 QDebug output = qDebug().nospace();
558 output << " " << i << ": ";
559 if (i < cls->nStaticMethods)
560 output << "static ";
561 switch (cls->methodTable()[i].type) {
562 case CompiledData::Method::Getter:
563 output << "get "; break;
564 case CompiledData::Method::Setter:
565 output << "set "; break;
566 default:
567 break;
568 }
569 output << stringForIndex(cls->methodTable()[i].name) << " "
570 << cls->methodTable()[i].function;
571 }
572 qDebug().space();
573 }
574}
575
576void QV4::Compiler::JSUnitGenerator::writeTemplateObject(char *b, const QV4::Compiler::TemplateObject &t)
577{
578 QV4::CompiledData::TemplateObject *tmpl = reinterpret_cast<QV4::CompiledData::TemplateObject *>(b);
579 tmpl->size = t.strings.size();
580
581 quint32 currentOffset = sizeof(QV4::CompiledData::TemplateObject);
582
583 quint32_le *strings = reinterpret_cast<quint32_le *>(b + currentOffset);
584
585 // write methods
586 for (int i = 0; i < t.strings.size(); ++i)
587 strings[i] = t.strings.at(i);
588 strings += t.strings.size();
589
590 for (int i = 0; i < t.rawStrings.size(); ++i)
591 strings[i] = t.rawStrings.at(i);
592
593 static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
594 if (showCode) {
595 qDebug() << "=== TemplateObject size" << tmpl->size;
596 for (uint i = 0; i < tmpl->size; ++i) {
597 qDebug() << " " << i << stringForIndex(tmpl->stringIndexAt(i));
598 qDebug() << " raw: " << stringForIndex(tmpl->rawStringIndexAt(i));
599 }
600 qDebug();
601 }
602}
603
604void QV4::Compiler::JSUnitGenerator::writeBlock(char *b, QV4::Compiler::Context *irBlock) const
605{
606 QV4::CompiledData::Block *block = reinterpret_cast<QV4::CompiledData::Block *>(b);
607
608 quint32 currentOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, sizeof(*block)));
609
610 block->sizeOfLocalTemporalDeadZone = irBlock->sizeOfLocalTemporalDeadZone;
611 block->nLocals = irBlock->locals.size();
612 block->localsOffset = currentOffset;
613 currentOffset += block->nLocals * sizeof(quint32);
614
615 // write locals
616 quint32_le *locals = (quint32_le *)(b + block->localsOffset);
617 for (int i = 0; i < irBlock->locals.size(); ++i)
618 locals[i] = getStringId(irBlock->locals.at(i));
619
620 static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
621 if (showCode) {
622 qDebug() << "=== Variables for block" << irBlock->blockIndex;
623 for (int i = 0; i < irBlock->locals.size(); ++i)
624 qDebug() << " " << i << ":" << locals[i];
625 qDebug();
626 }
627}
628
629QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, quint32_le *blockAndFunctionOffsets, uint *jsClassDataOffset)
630{
631 CompiledData::Unit unit;
632 memset(&unit, 0, sizeof(unit));
633 memcpy(unit.magic, CompiledData::magic_str, sizeof(unit.magic));
634 unit.flags = QV4::CompiledData::Unit::IsJavascript;
635 unit.flags |= module->unitFlags;
636 unit.version = QV4_DATA_STRUCTURE_VERSION;
637 memset(unit.md5Checksum, 0, sizeof(unit.md5Checksum));
638 memset(unit.dependencyMD5Checksum, 0, sizeof(unit.dependencyMD5Checksum));
639
640 quint32 nextOffset = sizeof(CompiledData::Unit);
641
642 unit.functionTableSize = module->functions.size();
643 unit.offsetToFunctionTable = nextOffset;
644 nextOffset += unit.functionTableSize * sizeof(uint);
645
646 unit.classTableSize = module->classes.size();
647 unit.offsetToClassTable = nextOffset;
648 nextOffset += unit.classTableSize * sizeof(uint);
649
650 unit.templateObjectTableSize = module->templateObjects.size();
651 unit.offsetToTemplateObjectTable = nextOffset;
652 nextOffset += unit.templateObjectTableSize * sizeof(uint);
653
654 unit.blockTableSize = module->blocks.size();
655 unit.offsetToBlockTable = nextOffset;
656 nextOffset += unit.blockTableSize * sizeof(uint);
657
658 unit.lookupTableSize = lookups.size();
659 unit.offsetToLookupTable = nextOffset;
660 nextOffset += unit.lookupTableSize * sizeof(CompiledData::Lookup);
661
662 unit.regexpTableSize = regexps.size();
663 unit.offsetToRegexpTable = nextOffset;
664 nextOffset += unit.regexpTableSize * sizeof(CompiledData::RegExp);
665
666 unit.constantTableSize = constants.size();
667
668 // Ensure we load constants from well-aligned addresses into for example SSE registers.
669 nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(16, nextOffset));
670 unit.offsetToConstantTable = nextOffset;
671 nextOffset += unit.constantTableSize * sizeof(ReturnedValue);
672
673 unit.jsClassTableSize = jsClassOffsets.size();
674 unit.offsetToJSClassTable = nextOffset;
675 nextOffset += unit.jsClassTableSize * sizeof(uint);
676
677 *jsClassDataOffset = nextOffset;
678 nextOffset += jsClassData.size();
679
680 nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, nextOffset));
681
682 unit.translationTableSize = translations.size();
683 unit.offsetToTranslationTable = nextOffset;
684 nextOffset += unit.translationTableSize * sizeof(CompiledData::TranslationData);
685 if (unit.translationTableSize != 0) {
686 constexpr auto spaceForTranslationContextId = sizeof(quint32_le);
687 nextOffset += spaceForTranslationContextId;
688 }
689
690 nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, nextOffset));
691
692 const auto reserveExportTable = [&nextOffset](int count, quint32_le *tableSizePtr, quint32_le *offsetPtr) {
693 *tableSizePtr = count;
694 *offsetPtr = nextOffset;
695 nextOffset += count * sizeof(CompiledData::ExportEntry);
696 nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, nextOffset));
697 };
698
699 reserveExportTable(module->localExportEntries.size(), &unit.localExportEntryTableSize, &unit.offsetToLocalExportEntryTable);
700 reserveExportTable(module->indirectExportEntries.size(), &unit.indirectExportEntryTableSize, &unit.offsetToIndirectExportEntryTable);
701 reserveExportTable(module->starExportEntries.size(), &unit.starExportEntryTableSize, &unit.offsetToStarExportEntryTable);
702
703 unit.importEntryTableSize = module->importEntries.size();
704 unit.offsetToImportEntryTable = nextOffset;
705 nextOffset += unit.importEntryTableSize * sizeof(CompiledData::ImportEntry);
706 nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, nextOffset));
707
708 unit.moduleRequestTableSize = module->moduleRequests.size();
709 unit.offsetToModuleRequestTable = nextOffset;
710 nextOffset += unit.moduleRequestTableSize * sizeof(uint);
711 nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, nextOffset));
712
713 quint32 functionSize = 0;
714 for (int i = 0; i < module->functions.size(); ++i) {
715 Context *f = module->functions.at(i);
716 blockAndFunctionOffsets[i] = nextOffset;
717
718 quint32 size = QV4::CompiledData::Function::calculateSize(
719 f->arguments.size(), f->locals.size(), f->lineAndStatementNumberMapping.size(),
720 int(f->labelInfo.size()), f->code.size());
721 functionSize += size - f->code.size();
722 nextOffset += size;
723 }
724
725 blockAndFunctionOffsets += module->functions.size();
726
727 for (int i = 0; i < module->classes.size(); ++i) {
728 const Class &c = module->classes.at(i);
729 blockAndFunctionOffsets[i] = nextOffset;
730
731 nextOffset += QV4::CompiledData::Class::calculateSize(c.staticMethods.size(), c.methods.size());
732 }
733 blockAndFunctionOffsets += module->classes.size();
734
735 for (int i = 0; i < module->templateObjects.size(); ++i) {
736 const TemplateObject &t = module->templateObjects.at(i);
737 blockAndFunctionOffsets[i] = nextOffset;
738
739 nextOffset += QV4::CompiledData::TemplateObject::calculateSize(t.strings.size());
740 }
741 blockAndFunctionOffsets += module->templateObjects.size();
742
743 for (int i = 0; i < module->blocks.size(); ++i) {
744 Context *c = module->blocks.at(i);
745 blockAndFunctionOffsets[i] = nextOffset;
746
747 nextOffset += QV4::CompiledData::Block::calculateSize(c->locals.size());
748 }
749
750 if (option == GenerateWithStringTable) {
751 unit.stringTableSize = stringTable.stringCount();
752 nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, nextOffset));
753 unit.offsetToStringTable = nextOffset;
754 nextOffset += stringTable.sizeOfTableAndData();
755 } else {
756 unit.stringTableSize = 0;
757 unit.offsetToStringTable = 0;
758 }
759 unit.indexOfRootFunction = -1;
760 unit.sourceFileIndex = getStringId(module->fileName);
761 unit.finalUrlIndex = getStringId(module->finalUrl);
762 unit.sourceTimeStamp = module->sourceTimeStamp.isValid() ? module->sourceTimeStamp.toMSecsSinceEpoch() : 0;
763 unit.offsetToQmlUnit = 0;
764
765 unit.unitSize = nextOffset;
766
767 static const bool showStats = qEnvironmentVariableIsSet("QML_SHOW_UNIT_STATS");
768 if (showStats) {
769 qDebug() << "Generated JS unit that is" << unit.unitSize << "bytes contains:";
770 qDebug() << " " << functionSize << "bytes for non-code function data for" << unit.functionTableSize << "functions";
771 qDebug() << " " << translations.size() * sizeof(CompiledData::TranslationData) << "bytes for" << translations.size() << "translations";
772 }
773
774 return unit;
775}
Combined button and popup list for selecting options.
static size_t roundUpToMultipleOf(size_t divisor, size_t x)
static constexpr qsizetype jsClassMembersOffset
static QV4::CompiledData::Lookup::Mode lookupMode(QV4::Compiler::JSUnitGenerator::LookupMode mode)