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