252QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorOption option)
254 const auto registerTypeStrings = [
this](QQmlJS::AST::Type *type) {
258 if (type->typeArgument) {
259 registerString(type->typeArgument->toString());
260 registerString(type->typeId->toString());
262 registerString(type->toString());
265 registerString(module->fileName);
266 registerString(module->finalUrl);
267 for (Context *f : std::as_const(module->functions)) {
268 registerString(f->name);
269 registerTypeStrings(f->returnType);
270 for (
int i = 0; i < f->arguments.size(); ++i) {
271 registerString(f->arguments.at(i).id);
272 if (
const QQmlJS::AST::TypeAnnotation *annotation
273 = f->arguments.at(i).typeAnnotation.data()) {
274 registerTypeStrings(annotation->type);
277 for (
int i = 0; i < f->locals.size(); ++i)
278 registerString(f->locals.at(i));
280 for (Context *c : std::as_const(module->blocks)) {
281 for (
int i = 0; i < c->locals.size(); ++i)
282 registerString(c->locals.at(i));
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);
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);
296 for (
const auto &entry: module->importEntries) {
297 registerString(entry.moduleRequest);
298 registerString(entry.importName);
299 registerString(entry.localName);
302 for (
const QString &request: module->moduleRequests)
303 registerString(request);
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;
310 CompiledData::Unit *unit;
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));
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));
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;
329 writeFunction(dataPtr + blockClassAndFunctionOffsets[i], function);
332 for (
int i = 0; i < module->classes.size(); ++i) {
333 const Class &c = module->classes.at(i);
335 writeClass(dataPtr + blockClassAndFunctionOffsets[i + module->functions.size()], c);
338 for (
int i = 0; i < module->templateObjects.size(); ++i) {
339 const TemplateObject &t = module->templateObjects.at(i);
341 writeTemplateObject(dataPtr + blockClassAndFunctionOffsets[i + module->functions.size() + module->classes.size()], t);
344 for (
int i = 0; i < module->blocks.size(); ++i) {
345 Context *block = module->blocks.at(i);
347 writeBlock(dataPtr + blockClassAndFunctionOffsets[i + module->classes.size() + module->templateObjects.size() + module->functions.size()], block);
350 CompiledData::Lookup *lookupsToWrite =
reinterpret_cast<CompiledData::Lookup*>(dataPtr + unit->offsetToLookupTable);
351 for (
const CompiledData::Lookup &l : std::as_const(lookups))
352 *lookupsToWrite++ = l;
354 CompiledData::RegExp *regexpTable =
reinterpret_cast<CompiledData::RegExp *>(dataPtr + unit->offsetToRegexpTable);
356 memcpy(regexpTable, regexps.constData(), regexps.size() *
sizeof(*regexpTable));
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));
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);
369 if (jsClassData.size())
370 memcpy(dataPtr + jsClassDataOffset, jsClassData.constData(), jsClassData.size());
373 quint32_le *jsClassOffsetTable =
reinterpret_cast<quint32_le *>(dataPtr + unit->offsetToJSClassTable);
374 for (
int i = 0; i < jsClassOffsets.size(); ++i)
375 jsClassOffsetTable[i] = jsClassDataOffset + jsClassOffsets.at(i);
378 if (translations.size()) {
379 memcpy(dataPtr + unit->offsetToTranslationTable, translations.constData(), translations.size() *
sizeof(CompiledData::TranslationData));
383 const auto populateExportEntryTable = [
this, dataPtr](
const QList<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;
394 populateExportEntryTable(module->localExportEntries, unit->offsetToLocalExportEntryTable);
395 populateExportEntryTable(module->indirectExportEntries, unit->offsetToIndirectExportEntryTable);
396 populateExportEntryTable(module->starExportEntries, unit->offsetToStarExportEntryTable);
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;
411 quint32_le *moduleRequestEntryToWrite =
reinterpret_cast<quint32_le *>(dataPtr + unit->offsetToModuleRequestTable);
412 for (
const QString &moduleRequest: module->moduleRequests) {
413 *moduleRequestEntryToWrite = getStringId(moduleRequest);
414 moduleRequestEntryToWrite++;
419 if (option == GenerateWithStringTable)
420 stringTable.serialize(unit);
422 generateUnitChecksum(unit);
427void QV4::Compiler::JSUnitGenerator::writeFunction(
char *f, QV4::Compiler::Context *irFunction)
const
429 QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f;
431 quint32 currentOffset =
static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8,
sizeof(*function)));
433 function->nameIndex = getStringId(irFunction->name);
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 if (irFunction->returnsClosure)
442 function->flags |= CompiledData::Function::IsClosureWrapper;
444 if (!irFunction->returnsClosure
445 || (irFunction->usesArgumentsObject == Context::UsesArgumentsObject::Used)
446 || irFunction->innerFunctionAccessesThis
447 || irFunction->innerFunctionAccessesNewTarget) {
450 function->nestedFunctionIndex = std::numeric_limits<uint32_t>::max();
453 function->nestedFunctionIndex
454 = quint32(module->functions.indexOf(irFunction->nestedContexts.first()));
457 function->length = irFunction->formals ? irFunction->formals->length() : 0;
458 function->nFormals = irFunction->arguments.size();
459 function->formalsOffset = currentOffset;
460 currentOffset += function->nFormals *
sizeof(CompiledData::Parameter);
462 const auto idGenerator = [
this](
const QString &str) {
return getStringId(str); };
464 QmlIR::Parameter::initType(&function->returnType, idGenerator, irFunction->returnType);
466 function->sizeOfLocalTemporalDeadZone = irFunction->sizeOfLocalTemporalDeadZone;
467 function->sizeOfRegisterTemporalDeadZone = irFunction->sizeOfRegisterTemporalDeadZone;
468 function->firstTemporalDeadZoneRegister = irFunction->firstTemporalDeadZoneRegister;
470 function->nLocals = irFunction->locals.size();
471 function->localsOffset = currentOffset;
472 currentOffset += function->nLocals *
sizeof(quint32);
474 function->nLineAndStatementNumbers
475 = irFunction->lineAndStatementNumberMapping.size();
476 Q_ASSERT(function->lineAndStatementNumberOffset() == currentOffset);
477 currentOffset += function->nLineAndStatementNumbers
478 *
sizeof(CompiledData::CodeOffsetToLineAndStatement);
480 function->nRegisters = irFunction->registerCountInFunction;
482 if (!irFunction->labelInfo.empty()) {
483 function->nLabelInfos = quint32(irFunction->labelInfo.size());
484 Q_ASSERT(function->labelInfosOffset() == currentOffset);
485 currentOffset += function->nLabelInfos *
sizeof(quint32);
488 function->location.set(irFunction->line, irFunction->column);
490 function->codeOffset = currentOffset;
491 function->codeSize = irFunction->code.size();
494 CompiledData::Parameter *formals = (CompiledData::Parameter *)(f + function->formalsOffset);
495 for (
int i = 0; i < irFunction->arguments.size(); ++i) {
496 auto *formal = &formals[i];
497 formal->nameIndex = getStringId(irFunction->arguments.at(i).id);
498 if (QQmlJS::AST::TypeAnnotation *annotation = irFunction->arguments.at(i).typeAnnotation.data())
499 QmlIR::Parameter::initType(&formal->type, idGenerator, annotation->type);
503 quint32_le *locals = (quint32_le *)(f + function->localsOffset);
504 for (
int i = 0; i < irFunction->locals.size(); ++i)
505 locals[i] = getStringId(irFunction->locals.at(i));
508 memcpy(f + function->lineAndStatementNumberOffset(),
509 irFunction->lineAndStatementNumberMapping.constData(),
510 irFunction->lineAndStatementNumberMapping.size()
511 *
sizeof(CompiledData::CodeOffsetToLineAndStatement));
513 quint32_le *labels = (quint32_le *)(f + function->labelInfosOffset());
514 for (
unsigned u : irFunction->labelInfo) {
519 memcpy(f + function->codeOffset, irFunction->code.constData(), irFunction->code.size());
526void QV4::Compiler::JSUnitGenerator::writeClass(
char *b,
const QV4::Compiler::Class &c)
528 QV4::CompiledData::Class *cls =
reinterpret_cast<QV4::CompiledData::Class *>(b);
530 quint32 currentOffset =
sizeof(QV4::CompiledData::Class);
532 QList<Class::Method> allMethods = c.staticMethods;
533 allMethods += c.methods;
535 cls->constructorFunction = c.constructorIndex;
536 cls->nameIndex = c.nameIndex;
537 cls->nMethods = c.methods.size();
538 cls->nStaticMethods = c.staticMethods.size();
539 cls->methodTableOffset = currentOffset;
540 CompiledData::Method *method =
reinterpret_cast<CompiledData::Method *>(b + currentOffset);
543 for (
int i = 0; i < allMethods.size(); ++i) {
544 method->name = allMethods.at(i).nameIndex;
545 method->type = allMethods.at(i).type;
546 method->function = allMethods.at(i).functionIndex;
550 static const bool showCode = qEnvironmentVariableIsSet(
"QV4_SHOW_BYTECODE");
552 qDebug() <<
"=== Class" << stringForIndex(cls->nameIndex) <<
"static methods"
553 << cls->nStaticMethods <<
"methods" << cls->nMethods;
554 qDebug() <<
" constructor:" << cls->constructorFunction;
555 for (uint i = 0; i < cls->nStaticMethods + cls->nMethods; ++i) {
556 QDebug output = qDebug().nospace();
557 output <<
" " << i <<
": ";
558 if (i < cls->nStaticMethods)
560 switch (cls->methodTable()[i].type) {
561 case CompiledData::Method::Getter:
562 output <<
"get ";
break;
563 case CompiledData::Method::Setter:
564 output <<
"set ";
break;
568 output << stringForIndex(cls->methodTable()[i].name) <<
" "
569 << cls->methodTable()[i].function;