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/qml_compile_hash_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
19QT_BEGIN_NAMESPACE
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(key: str);
39 if (it != stringToId.cend())
40 return *it;
41 stringToId.insert(key: str, value: strings.size());
42 strings.append(t: 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(key: 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(str: unit->stringAtInternal(idx: 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(divisor: 8, x: 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>(source: qstr.constData(), count: s->size, dest: uc);
88 uc[s->size] = 0;
89
90 stringData += QV4::CompiledData::String::calculateSize(str: 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(data: {dataPtr, qsizetype(unit->unitSize - checksummableDataOffset)});
104
105 QByteArray checksum = hash.result();
106 Q_ASSERT(checksum.size() == sizeof(unit->md5Checksum));
107 memcpy(dest: unit->md5Checksum, src: checksum.constData(), n: 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(str: QString());
118}
119
120int QV4::Compiler::JSUnitGenerator::registerGetterLookup(const QString &name, LookupMode mode)
121{
122 return registerGetterLookup(nameIndex: registerString(str: 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(nameIndex: registerString(str: 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(t: CompiledData::RegExp(flags, registerString(str: regexp->pattern.toString())));
183 return regexps.size() - 1;
184}
185
186int QV4::Compiler::JSUnitGenerator::registerConstant(QV4::ReturnedValue v)
187{
188 int idx = constants.indexOf(t: v);
189 if (idx >= 0)
190 return idx;
191 constants.append(t: v);
192 return constants.size() - 1;
193}
194
195QV4::ReturnedValue QV4::Compiler::JSUnitGenerator::constant(int idx) const
196{
197 return constants.at(i: idx);
198}
199
200int QV4::Compiler::JSUnitGenerator::registerJSClass(const QStringList &members)
201{
202 // ### re-use existing class definitions.
203
204 const int size = CompiledData::JSClass::calculateSize(nMembers: members.size());
205 jsClassOffsets.append(t: jsClassData.size());
206 const int oldSize = jsClassData.size();
207 jsClassData.resize(size: jsClassData.size() + size);
208 memset(s: jsClassData.data() + oldSize, c: 0, n: size);
209
210 CompiledData::JSClass *jsClass = reinterpret_cast<CompiledData::JSClass*>(jsClassData.data() + oldSize);
211 jsClass->nMembers = members.size();
212 CompiledData::JSClassMember *member = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1);
213
214 for (const auto &name : members) {
215 member->set(nameOffset: registerString(str: name), isAccessor: false);
216 ++member;
217 }
218
219 return jsClassOffsets.size() - 1;
220}
221
222int QV4::Compiler::JSUnitGenerator::registerTranslation(const QV4::CompiledData::TranslationData &translation)
223{
224 translations.append(t: translation);
225 return translations.size() - 1;
226}
227
228QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorOption option)
229{
230 const auto registerTypeStrings = [this](QQmlJS::AST::Type *type) {
231 if (!type)
232 return;
233
234 if (type->typeArgument) {
235 registerString(str: type->typeArgument->toString());
236 registerString(str: type->typeId->toString());
237 }
238 registerString(str: type->toString());
239 };
240
241 registerString(str: module->fileName);
242 registerString(str: module->finalUrl);
243 for (Context *f : std::as_const(t&: module->functions)) {
244 registerString(str: f->name);
245 registerTypeStrings(f->returnType);
246 for (int i = 0; i < f->arguments.size(); ++i) {
247 registerString(str: f->arguments.at(i).id);
248 if (const QQmlJS::AST::TypeAnnotation *annotation
249 = f->arguments.at(i).typeAnnotation.data()) {
250 registerTypeStrings(annotation->type);
251 }
252 }
253 for (int i = 0; i < f->locals.size(); ++i)
254 registerString(str: f->locals.at(i));
255 }
256 for (Context *c : std::as_const(t&: module->blocks)) {
257 for (int i = 0; i < c->locals.size(); ++i)
258 registerString(str: c->locals.at(i));
259 }
260 {
261 const auto registerExportEntry = [this](const Compiler::ExportEntry &entry) {
262 registerString(str: entry.exportName);
263 registerString(str: entry.moduleRequest);
264 registerString(str: entry.importName);
265 registerString(str: entry.localName);
266 };
267 std::for_each(first: module->localExportEntries.constBegin(), last: module->localExportEntries.constEnd(), f: registerExportEntry);
268 std::for_each(first: module->indirectExportEntries.constBegin(), last: module->indirectExportEntries.constEnd(), f: registerExportEntry);
269 std::for_each(first: module->starExportEntries.constBegin(), last: module->starExportEntries.constEnd(), f: registerExportEntry);
270 }
271 {
272 for (const auto &entry: module->importEntries) {
273 registerString(str: entry.moduleRequest);
274 registerString(str: entry.importName);
275 registerString(str: entry.localName);
276 }
277
278 for (const QString &request: module->moduleRequests)
279 registerString(str: request);
280 }
281
282 Q_ALLOCA_VAR(quint32_le, blockClassAndFunctionOffsets, (module->functions.size() + module->classes.size() + module->templateObjects.size() + module->blocks.size()) * sizeof(quint32_le));
283 uint jsClassDataOffset = 0;
284
285 char *dataPtr;
286 CompiledData::Unit *unit;
287 {
288 QV4::CompiledData::Unit tempHeader = generateHeader(option, functionOffsets: blockClassAndFunctionOffsets, jsClassDataOffset: &jsClassDataOffset);
289 dataPtr = reinterpret_cast<char *>(malloc(size: tempHeader.unitSize));
290 memset(s: dataPtr, c: 0, n: tempHeader.unitSize);
291 memcpy(dest: &unit, src: &dataPtr, n: sizeof(CompiledData::Unit*));
292 memcpy(dest: unit, src: &tempHeader, n: sizeof(tempHeader));
293 }
294
295 memcpy(dest: dataPtr + unit->offsetToFunctionTable, src: blockClassAndFunctionOffsets, n: unit->functionTableSize * sizeof(quint32_le));
296 memcpy(dest: dataPtr + unit->offsetToClassTable, src: blockClassAndFunctionOffsets + unit->functionTableSize, n: unit->classTableSize * sizeof(quint32_le));
297 memcpy(dest: dataPtr + unit->offsetToTemplateObjectTable, src: blockClassAndFunctionOffsets + unit->functionTableSize + unit->classTableSize, n: unit->templateObjectTableSize * sizeof(quint32_le));
298 memcpy(dest: dataPtr + unit->offsetToBlockTable, src: blockClassAndFunctionOffsets + unit->functionTableSize + unit->classTableSize + unit->templateObjectTableSize, n: unit->blockTableSize * sizeof(quint32_le));
299
300 for (int i = 0; i < module->functions.size(); ++i) {
301 Context *function = module->functions.at(i);
302 if (function == module->rootContext)
303 unit->indexOfRootFunction = i;
304
305 writeFunction(f: dataPtr + blockClassAndFunctionOffsets[i], irFunction: function);
306 }
307
308 for (int i = 0; i < module->classes.size(); ++i) {
309 const Class &c = module->classes.at(i);
310
311 writeClass(f: dataPtr + blockClassAndFunctionOffsets[i + module->functions.size()], c);
312 }
313
314 for (int i = 0; i < module->templateObjects.size(); ++i) {
315 const TemplateObject &t = module->templateObjects.at(i);
316
317 writeTemplateObject(f: dataPtr + blockClassAndFunctionOffsets[i + module->functions.size() + module->classes.size()], o: t);
318 }
319
320 for (int i = 0; i < module->blocks.size(); ++i) {
321 Context *block = module->blocks.at(i);
322
323 writeBlock(f: dataPtr + blockClassAndFunctionOffsets[i + module->classes.size() + module->templateObjects.size() + module->functions.size()], irBlock: block);
324 }
325
326 CompiledData::Lookup *lookupsToWrite = reinterpret_cast<CompiledData::Lookup*>(dataPtr + unit->offsetToLookupTable);
327 for (const CompiledData::Lookup &l : std::as_const(t&: lookups))
328 *lookupsToWrite++ = l;
329
330 CompiledData::RegExp *regexpTable = reinterpret_cast<CompiledData::RegExp *>(dataPtr + unit->offsetToRegexpTable);
331 if (regexps.size())
332 memcpy(dest: regexpTable, src: regexps.constData(), n: regexps.size() * sizeof(*regexpTable));
333
334#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
335 ReturnedValue *constantTable = reinterpret_cast<ReturnedValue *>(dataPtr + unit->offsetToConstantTable);
336 if (constants.size())
337 memcpy(dest: constantTable, src: constants.constData(), n: constants.size() * sizeof(ReturnedValue));
338#else
339 quint64_le *constantTable = reinterpret_cast<quint64_le *>(dataPtr + unit->offsetToConstantTable);
340 for (int i = 0; i < constants.count(); ++i)
341 constantTable[i] = constants.at(i);
342#endif
343
344 {
345 if (jsClassData.size())
346 memcpy(dest: dataPtr + jsClassDataOffset, src: jsClassData.constData(), n: jsClassData.size());
347
348 // write js classes and js class lookup table
349 quint32_le *jsClassOffsetTable = reinterpret_cast<quint32_le *>(dataPtr + unit->offsetToJSClassTable);
350 for (int i = 0; i < jsClassOffsets.size(); ++i)
351 jsClassOffsetTable[i] = jsClassDataOffset + jsClassOffsets.at(i);
352 }
353
354 if (translations.size()) {
355 memcpy(dest: dataPtr + unit->offsetToTranslationTable, src: translations.constData(), n: translations.size() * sizeof(CompiledData::TranslationData));
356 }
357
358 {
359 const auto populateExportEntryTable = [this, dataPtr](const QVector<Compiler::ExportEntry> &table, quint32_le offset) {
360 CompiledData::ExportEntry *entryToWrite = reinterpret_cast<CompiledData::ExportEntry *>(dataPtr + offset);
361 for (const Compiler::ExportEntry &entry: table) {
362 entryToWrite->exportName = getStringId(string: entry.exportName);
363 entryToWrite->moduleRequest = getStringId(string: entry.moduleRequest);
364 entryToWrite->importName = getStringId(string: entry.importName);
365 entryToWrite->localName = getStringId(string: entry.localName);
366 entryToWrite->location = entry.location;
367 entryToWrite++;
368 }
369 };
370 populateExportEntryTable(module->localExportEntries, unit->offsetToLocalExportEntryTable);
371 populateExportEntryTable(module->indirectExportEntries, unit->offsetToIndirectExportEntryTable);
372 populateExportEntryTable(module->starExportEntries, unit->offsetToStarExportEntryTable);
373 }
374
375 {
376 CompiledData::ImportEntry *entryToWrite = reinterpret_cast<CompiledData::ImportEntry *>(dataPtr + unit->offsetToImportEntryTable);
377 for (const Compiler::ImportEntry &entry: module->importEntries) {
378 entryToWrite->moduleRequest = getStringId(string: entry.moduleRequest);
379 entryToWrite->importName = getStringId(string: entry.importName);
380 entryToWrite->localName = getStringId(string: entry.localName);
381 entryToWrite->location = entry.location;
382 entryToWrite++;
383 }
384 }
385
386 {
387 quint32_le *moduleRequestEntryToWrite = reinterpret_cast<quint32_le *>(dataPtr + unit->offsetToModuleRequestTable);
388 for (const QString &moduleRequest: module->moduleRequests) {
389 *moduleRequestEntryToWrite = getStringId(string: moduleRequest);
390 moduleRequestEntryToWrite++;
391 }
392 }
393
394 // write strings and string table
395 if (option == GenerateWithStringTable)
396 stringTable.serialize(unit);
397
398 generateUnitChecksum(unit);
399
400 return unit;
401}
402
403void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Context *irFunction) const
404{
405 QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f;
406
407 quint32 currentOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(divisor: 8, x: sizeof(*function)));
408
409 function->nameIndex = getStringId(string: irFunction->name);
410 function->flags = 0;
411 if (irFunction->isStrict)
412 function->flags |= CompiledData::Function::IsStrict;
413 if (irFunction->isArrowFunction)
414 function->flags |= CompiledData::Function::IsArrowFunction;
415 if (irFunction->isGenerator)
416 function->flags |= CompiledData::Function::IsGenerator;
417 if (irFunction->returnsClosure)
418 function->flags |= CompiledData::Function::IsClosureWrapper;
419
420 if (!irFunction->returnsClosure
421 || irFunction->innerFunctionAccessesThis
422 || irFunction->innerFunctionAccessesNewTarget) {
423 // If the inner function does things with this and new.target we need to do some work in
424 // the outer function. Then we shouldn't directly access the nested function.
425 function->nestedFunctionIndex = std::numeric_limits<uint32_t>::max();
426 } else {
427 // Otherwise we can directly use the nested function.
428 function->nestedFunctionIndex
429 = quint32(module->functions.indexOf(t: irFunction->nestedContexts.first()));
430 }
431
432 function->length = irFunction->formals ? irFunction->formals->length() : 0;
433 function->nFormals = irFunction->arguments.size();
434 function->formalsOffset = currentOffset;
435 currentOffset += function->nFormals * sizeof(CompiledData::Parameter);
436
437 const auto idGenerator = [this](const QString &str) { return getStringId(string: str); };
438
439 QmlIR::Parameter::initType(type: &function->returnType, idGenerator, annotation: irFunction->returnType);
440
441 function->sizeOfLocalTemporalDeadZone = irFunction->sizeOfLocalTemporalDeadZone;
442 function->sizeOfRegisterTemporalDeadZone = irFunction->sizeOfRegisterTemporalDeadZone;
443 function->firstTemporalDeadZoneRegister = irFunction->firstTemporalDeadZoneRegister;
444
445 function->nLocals = irFunction->locals.size();
446 function->localsOffset = currentOffset;
447 currentOffset += function->nLocals * sizeof(quint32);
448
449 function->nLineAndStatementNumbers
450 = irFunction->lineAndStatementNumberMapping.size();
451 Q_ASSERT(function->lineAndStatementNumberOffset() == currentOffset);
452 currentOffset += function->nLineAndStatementNumbers
453 * sizeof(CompiledData::CodeOffsetToLineAndStatement);
454
455 function->nRegisters = irFunction->registerCountInFunction;
456
457 if (!irFunction->labelInfo.empty()) {
458 function->nLabelInfos = quint32(irFunction->labelInfo.size());
459 Q_ASSERT(function->labelInfosOffset() == currentOffset);
460 currentOffset += function->nLabelInfos * sizeof(quint32);
461 }
462
463 function->location.set(line: irFunction->line, column: irFunction->column);
464
465 function->codeOffset = currentOffset;
466 function->codeSize = irFunction->code.size();
467
468 // write formals
469 CompiledData::Parameter *formals = (CompiledData::Parameter *)(f + function->formalsOffset);
470 for (int i = 0; i < irFunction->arguments.size(); ++i) {
471 auto *formal = &formals[i];
472 formal->nameIndex = getStringId(string: irFunction->arguments.at(i).id);
473 if (QQmlJS::AST::TypeAnnotation *annotation = irFunction->arguments.at(i).typeAnnotation.data())
474 QmlIR::Parameter::initType(type: &formal->type, idGenerator, annotation: annotation->type);
475 }
476
477 // write locals
478 quint32_le *locals = (quint32_le *)(f + function->localsOffset);
479 for (int i = 0; i < irFunction->locals.size(); ++i)
480 locals[i] = getStringId(string: irFunction->locals.at(i));
481
482 // write line and statement numbers
483 memcpy(dest: f + function->lineAndStatementNumberOffset(),
484 src: irFunction->lineAndStatementNumberMapping.constData(),
485 n: irFunction->lineAndStatementNumberMapping.size()
486 * sizeof(CompiledData::CodeOffsetToLineAndStatement));
487
488 quint32_le *labels = (quint32_le *)(f + function->labelInfosOffset());
489 for (unsigned u : irFunction->labelInfo) {
490 *labels++ = u;
491 }
492
493 // write byte code
494 memcpy(dest: f + function->codeOffset, src: irFunction->code.constData(), n: irFunction->code.size());
495}
496
497static_assert(int(QV4::Compiler::Class::Method::Regular) == int(QV4::CompiledData::Method::Regular), "Incompatible layout");
498static_assert(int(QV4::Compiler::Class::Method::Getter) == int(QV4::CompiledData::Method::Getter), "Incompatible layout");
499static_assert(int(QV4::Compiler::Class::Method::Setter) == int(QV4::CompiledData::Method::Setter), "Incompatible layout");
500
501void QV4::Compiler::JSUnitGenerator::writeClass(char *b, const QV4::Compiler::Class &c)
502{
503 QV4::CompiledData::Class *cls = reinterpret_cast<QV4::CompiledData::Class *>(b);
504
505 quint32 currentOffset = sizeof(QV4::CompiledData::Class);
506
507 QVector<Class::Method> allMethods = c.staticMethods;
508 allMethods += c.methods;
509
510 cls->constructorFunction = c.constructorIndex;
511 cls->nameIndex = c.nameIndex;
512 cls->nMethods = c.methods.size();
513 cls->nStaticMethods = c.staticMethods.size();
514 cls->methodTableOffset = currentOffset;
515 CompiledData::Method *method = reinterpret_cast<CompiledData::Method *>(b + currentOffset);
516
517 // write methods
518 for (int i = 0; i < allMethods.size(); ++i) {
519 method->name = allMethods.at(i).nameIndex;
520 method->type = allMethods.at(i).type;
521 method->function = allMethods.at(i).functionIndex;
522 ++method;
523 }
524
525 static const bool showCode = qEnvironmentVariableIsSet(varName: "QV4_SHOW_BYTECODE");
526 if (showCode) {
527 qDebug() << "=== Class" << stringForIndex(index: cls->nameIndex) << "static methods"
528 << cls->nStaticMethods << "methods" << cls->nMethods;
529 qDebug() << " constructor:" << cls->constructorFunction;
530 for (uint i = 0; i < cls->nStaticMethods + cls->nMethods; ++i) {
531 QDebug output = qDebug().nospace();
532 output << " " << i << ": ";
533 if (i < cls->nStaticMethods)
534 output << "static ";
535 switch (cls->methodTable()[i].type) {
536 case CompiledData::Method::Getter:
537 output << "get "; break;
538 case CompiledData::Method::Setter:
539 output << "set "; break;
540 default:
541 break;
542 }
543 output << stringForIndex(index: cls->methodTable()[i].name) << " "
544 << cls->methodTable()[i].function;
545 }
546 qDebug().space();
547 }
548}
549
550void QV4::Compiler::JSUnitGenerator::writeTemplateObject(char *b, const QV4::Compiler::TemplateObject &t)
551{
552 QV4::CompiledData::TemplateObject *tmpl = reinterpret_cast<QV4::CompiledData::TemplateObject *>(b);
553 tmpl->size = t.strings.size();
554
555 quint32 currentOffset = sizeof(QV4::CompiledData::TemplateObject);
556
557 quint32_le *strings = reinterpret_cast<quint32_le *>(b + currentOffset);
558
559 // write methods
560 for (int i = 0; i < t.strings.size(); ++i)
561 strings[i] = t.strings.at(i);
562 strings += t.strings.size();
563
564 for (int i = 0; i < t.rawStrings.size(); ++i)
565 strings[i] = t.rawStrings.at(i);
566
567 static const bool showCode = qEnvironmentVariableIsSet(varName: "QV4_SHOW_BYTECODE");
568 if (showCode) {
569 qDebug() << "=== TemplateObject size" << tmpl->size;
570 for (uint i = 0; i < tmpl->size; ++i) {
571 qDebug() << " " << i << stringForIndex(index: tmpl->stringIndexAt(i));
572 qDebug() << " raw: " << stringForIndex(index: tmpl->rawStringIndexAt(i));
573 }
574 qDebug();
575 }
576}
577
578void QV4::Compiler::JSUnitGenerator::writeBlock(char *b, QV4::Compiler::Context *irBlock) const
579{
580 QV4::CompiledData::Block *block = reinterpret_cast<QV4::CompiledData::Block *>(b);
581
582 quint32 currentOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(divisor: 8, x: sizeof(*block)));
583
584 block->sizeOfLocalTemporalDeadZone = irBlock->sizeOfLocalTemporalDeadZone;
585 block->nLocals = irBlock->locals.size();
586 block->localsOffset = currentOffset;
587 currentOffset += block->nLocals * sizeof(quint32);
588
589 // write locals
590 quint32_le *locals = (quint32_le *)(b + block->localsOffset);
591 for (int i = 0; i < irBlock->locals.size(); ++i)
592 locals[i] = getStringId(string: irBlock->locals.at(i));
593
594 static const bool showCode = qEnvironmentVariableIsSet(varName: "QV4_SHOW_BYTECODE");
595 if (showCode) {
596 qDebug() << "=== Variables for block" << irBlock->blockIndex;
597 for (int i = 0; i < irBlock->locals.size(); ++i)
598 qDebug() << " " << i << ":" << locals[i];
599 qDebug();
600 }
601}
602
603QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, quint32_le *blockAndFunctionOffsets, uint *jsClassDataOffset)
604{
605 CompiledData::Unit unit;
606 memset(s: &unit, c: 0, n: sizeof(unit));
607 memcpy(dest: unit.magic, src: CompiledData::magic_str, n: sizeof(unit.magic));
608 unit.flags = QV4::CompiledData::Unit::IsJavascript;
609 unit.flags |= module->unitFlags;
610 unit.version = QV4_DATA_STRUCTURE_VERSION;
611 unit.qtVersion = QT_VERSION;
612 qstrcpy(dst: unit.libraryVersionHash, QML_COMPILE_HASH);
613 memset(s: unit.md5Checksum, c: 0, n: sizeof(unit.md5Checksum));
614 memset(s: unit.dependencyMD5Checksum, c: 0, n: sizeof(unit.dependencyMD5Checksum));
615
616 quint32 nextOffset = sizeof(CompiledData::Unit);
617
618 unit.functionTableSize = module->functions.size();
619 unit.offsetToFunctionTable = nextOffset;
620 nextOffset += unit.functionTableSize * sizeof(uint);
621
622 unit.classTableSize = module->classes.size();
623 unit.offsetToClassTable = nextOffset;
624 nextOffset += unit.classTableSize * sizeof(uint);
625
626 unit.templateObjectTableSize = module->templateObjects.size();
627 unit.offsetToTemplateObjectTable = nextOffset;
628 nextOffset += unit.templateObjectTableSize * sizeof(uint);
629
630 unit.blockTableSize = module->blocks.size();
631 unit.offsetToBlockTable = nextOffset;
632 nextOffset += unit.blockTableSize * sizeof(uint);
633
634 unit.lookupTableSize = lookups.size();
635 unit.offsetToLookupTable = nextOffset;
636 nextOffset += unit.lookupTableSize * sizeof(CompiledData::Lookup);
637
638 unit.regexpTableSize = regexps.size();
639 unit.offsetToRegexpTable = nextOffset;
640 nextOffset += unit.regexpTableSize * sizeof(CompiledData::RegExp);
641
642 unit.constantTableSize = constants.size();
643
644 // Ensure we load constants from well-aligned addresses into for example SSE registers.
645 nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(divisor: 16, x: nextOffset));
646 unit.offsetToConstantTable = nextOffset;
647 nextOffset += unit.constantTableSize * sizeof(ReturnedValue);
648
649 unit.jsClassTableSize = jsClassOffsets.size();
650 unit.offsetToJSClassTable = nextOffset;
651 nextOffset += unit.jsClassTableSize * sizeof(uint);
652
653 *jsClassDataOffset = nextOffset;
654 nextOffset += jsClassData.size();
655
656 nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(divisor: 8, x: nextOffset));
657
658 unit.translationTableSize = translations.size();
659 unit.offsetToTranslationTable = nextOffset;
660 nextOffset += unit.translationTableSize * sizeof(CompiledData::TranslationData);
661
662 nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(divisor: 8, x: nextOffset));
663
664 const auto reserveExportTable = [&nextOffset](int count, quint32_le *tableSizePtr, quint32_le *offsetPtr) {
665 *tableSizePtr = count;
666 *offsetPtr = nextOffset;
667 nextOffset += count * sizeof(CompiledData::ExportEntry);
668 nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(divisor: 8, x: nextOffset));
669 };
670
671 reserveExportTable(module->localExportEntries.size(), &unit.localExportEntryTableSize, &unit.offsetToLocalExportEntryTable);
672 reserveExportTable(module->indirectExportEntries.size(), &unit.indirectExportEntryTableSize, &unit.offsetToIndirectExportEntryTable);
673 reserveExportTable(module->starExportEntries.size(), &unit.starExportEntryTableSize, &unit.offsetToStarExportEntryTable);
674
675 unit.importEntryTableSize = module->importEntries.size();
676 unit.offsetToImportEntryTable = nextOffset;
677 nextOffset += unit.importEntryTableSize * sizeof(CompiledData::ImportEntry);
678 nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(divisor: 8, x: nextOffset));
679
680 unit.moduleRequestTableSize = module->moduleRequests.size();
681 unit.offsetToModuleRequestTable = nextOffset;
682 nextOffset += unit.moduleRequestTableSize * sizeof(uint);
683 nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(divisor: 8, x: nextOffset));
684
685 quint32 functionSize = 0;
686 for (int i = 0; i < module->functions.size(); ++i) {
687 Context *f = module->functions.at(i);
688 blockAndFunctionOffsets[i] = nextOffset;
689
690 quint32 size = QV4::CompiledData::Function::calculateSize(
691 nFormals: f->arguments.size(), nLocals: f->locals.size(), nLinesAndStatements: f->lineAndStatementNumberMapping.size(),
692 nInnerfunctions: f->nestedContexts.size(), labelInfoSize: int(f->labelInfo.size()), codeSize: f->code.size());
693 functionSize += size - f->code.size();
694 nextOffset += size;
695 }
696
697 blockAndFunctionOffsets += module->functions.size();
698
699 for (int i = 0; i < module->classes.size(); ++i) {
700 const Class &c = module->classes.at(i);
701 blockAndFunctionOffsets[i] = nextOffset;
702
703 nextOffset += QV4::CompiledData::Class::calculateSize(nStaticMethods: c.staticMethods.size(), nMethods: c.methods.size());
704 }
705 blockAndFunctionOffsets += module->classes.size();
706
707 for (int i = 0; i < module->templateObjects.size(); ++i) {
708 const TemplateObject &t = module->templateObjects.at(i);
709 blockAndFunctionOffsets[i] = nextOffset;
710
711 nextOffset += QV4::CompiledData::TemplateObject::calculateSize(size: t.strings.size());
712 }
713 blockAndFunctionOffsets += module->templateObjects.size();
714
715 for (int i = 0; i < module->blocks.size(); ++i) {
716 Context *c = module->blocks.at(i);
717 blockAndFunctionOffsets[i] = nextOffset;
718
719 nextOffset += QV4::CompiledData::Block::calculateSize(nLocals: c->locals.size());
720 }
721
722 if (option == GenerateWithStringTable) {
723 unit.stringTableSize = stringTable.stringCount();
724 nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(divisor: 8, x: nextOffset));
725 unit.offsetToStringTable = nextOffset;
726 nextOffset += stringTable.sizeOfTableAndData();
727 } else {
728 unit.stringTableSize = 0;
729 unit.offsetToStringTable = 0;
730 }
731 unit.indexOfRootFunction = -1;
732 unit.sourceFileIndex = getStringId(string: module->fileName);
733 unit.finalUrlIndex = getStringId(string: module->finalUrl);
734 unit.sourceTimeStamp = module->sourceTimeStamp.isValid() ? module->sourceTimeStamp.toMSecsSinceEpoch() : 0;
735 unit.offsetToQmlUnit = 0;
736
737 unit.unitSize = nextOffset;
738
739 static const bool showStats = qEnvironmentVariableIsSet(varName: "QML_SHOW_UNIT_STATS");
740 if (showStats) {
741 qDebug() << "Generated JS unit that is" << unit.unitSize << "bytes contains:";
742 qDebug() << " " << functionSize << "bytes for non-code function data for" << unit.functionTableSize << "functions";
743 qDebug() << " " << translations.size() * sizeof(CompiledData::TranslationData) << "bytes for" << translations.size() << "translations";
744 }
745
746 return unit;
747}
748

source code of qtdeclarative/src/qml/compiler/qv4compiler.cpp