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