7#include <private/qqmlirbuilder_p.h>
8#include <private/qqmljsaotirbuilder_p.h>
9#include <private/qqmljsbasicblocks_p.h>
10#include <private/qqmljscodegenerator_p.h>
11#include <private/qqmljscompilerstats_p.h>
12#include <private/qqmljsfunctioninitializer_p.h>
13#include <private/qqmljsimportvisitor_p.h>
14#include <private/qqmljslexer_p.h>
15#include <private/qqmljsloadergenerator_p.h>
16#include <private/qqmljsoptimizations_p.h>
17#include <private/qqmljsparser_p.h>
18#include <private/qqmljsshadowcheck_p.h>
19#include <private/qqmljsstoragegeneralizer_p.h>
20#include <private/qqmljsstorageinitializer_p.h>
21#include <private/qqmljstypepropagator_p.h>
23#include <QtCore/qfile.h>
24#include <QtCore/qfileinfo.h>
25#include <QtCore/qloggingcategory.h>
26#include <QtCore/qelapsedtimer.h>
28#include <QtQml/private/qqmlsignalnames_p.h>
34using namespace Qt::StringLiterals;
40void QQmlJSCompileError::print()
42 fprintf(stderr,
"%s\n", qPrintable(message));
45QQmlJSCompileError QQmlJSCompileError::augment(
const QString &contextErrorMessage)
const
47 QQmlJSCompileError augmented;
48 augmented.message = contextErrorMessage + message;
55 message = fileName + QLatin1Char(
':') + QString::number(m.loc.startLine) + QLatin1Char(
':');
56 if (m.loc.startColumn > 0)
57 message += QString::number(m.loc.startColumn) + QLatin1Char(
':');
60 message += QLatin1String(
" error: ");
62 message += QLatin1String(
" warning: ");
67void QQmlJSCompileError::appendDiagnostic(
const QString &inputFileName,
68 const QQmlJS::DiagnosticMessage &diagnostic)
70 if (!message.isEmpty())
71 message += QLatin1Char(
'\n');
72 message += diagnosticErrorMessage(inputFileName, diagnostic);
75void QQmlJSCompileError::appendDiagnostics(
const QString &inputFileName,
76 const QList<QQmlJS::DiagnosticMessage> &diagnostics)
78 for (
const QQmlJS::DiagnosticMessage &diagnostic: diagnostics)
79 appendDiagnostic(inputFileName, diagnostic);
85 QStringList listElementNames;
87 for (
const QV4::CompiledData::Import *import : std::as_const(document->imports)) {
88 const QString uri = document->stringAt(import->uriIndex);
89 if (uri != QStringLiteral(
"QtQml.Models") && uri != QStringLiteral(
"QtQuick"))
92 QString listElementName = QStringLiteral(
"ListElement");
93 const QString qualifier = document->stringAt(import->qualifierIndex);
94 if (!qualifier.isEmpty()) {
95 listElementName.prepend(QLatin1Char(
'.'));
96 listElementName.prepend(qualifier);
98 listElementNames.append(listElementName);
101 if (listElementNames.isEmpty())
104 for (QmlIR::Object *object : std::as_const(document->objects)) {
105 if (!listElementNames.contains(document->stringAt(object->inheritedTypeNameIndex)))
107 for (QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) {
108 if (binding->type() != QV4::CompiledData::Binding::Type_Script)
110 binding->stringIndex = document->registerString(object->bindingAsString(document, binding->value.compiledScriptIndex));
116 QQmlJSCompileError *error)
118 for (QmlIR::Object *object: std::as_const(doc.objects)) {
119 for (
auto binding = object->bindingsBegin(); binding != object->bindingsEnd(); ++binding) {
120 if (binding->type() != QV4::CompiledData::Binding::Type_Script)
122 const QString propName = doc.stringAt(binding->propertyNameIndex);
123 if (!QQmlSignalNames::isHandlerName(propName))
125 auto compiledFunction = doc.jsModule.functions.value(object->runtimeFunctionIndices.at(binding->value.compiledScriptIndex));
126 if (!compiledFunction)
128 if (compiledFunction->usesArgumentsObject == QV4::Compiler::Context::UsesArgumentsObject::Used) {
129 error->message = QLatin1Char(
':') + QString::number(compiledFunction->line) + QLatin1Char(
':');
130 if (compiledFunction->column > 0)
131 error->message += QString::number(compiledFunction->column) + QLatin1Char(
':');
133 error->message += QLatin1String(
" error: The use of eval() or the use of the arguments object in signal handlers is\n"
134 "not supported when compiling qml files ahead of time. That is because it's ambiguous if \n"
135 "any signal parameter is called \"arguments\". Similarly the string passed to eval might use\n"
136 "\"arguments\". Unfortunately we cannot distinguish between it being a parameter or the\n"
137 "JavaScript arguments object at this point.\n"
138 "Consider renaming the parameter of the signal if applicable or moving the code into a\n"
155 return lhs.index() < rhs.index();
164 ? m_binding->value.compiledScriptIndex
167 : std::numeric_limits<quint32>::max());
171 const QmlIR::Binding *m_binding =
nullptr;
172 const QmlIR::Function *m_function =
nullptr;
176 QQmlJSAotCompiler *aotCompiler, QQmlJSCompileError *error,
177 bool storeSourceLocation, QV4::Compiler::CodegenWarningInterface *wInterface,
178 const QString *fileContents)
180 QmlIR::Document irDocument(QString(), QString(),
false);
181 return qCompileQmlFile(irDocument, inputFileName, saveFunction, aotCompiler, error,
182 storeSourceLocation, wInterface, fileContents);
187 QQmlJSCompileError *error,
bool storeSourceLocation,
188 QV4::Compiler::CodegenWarningInterface *wInterface,
const QString *fileContents)
192 if (fileContents !=
nullptr) {
193 sourceCode = *fileContents;
195 QFile f(inputFileName);
196 if (!f.open(QIODevice::ReadOnly)) {
197 error->message = QLatin1String(
"Error opening ") + inputFileName + QLatin1Char(
':') + f.errorString();
200 sourceCode = QString::fromUtf8(f.readAll());
201 if (f.error() != QFileDevice::NoError) {
202 error->message = QLatin1String(
"Error reading from ") + inputFileName + QLatin1Char(
':') + f.errorString();
209 std::unique_ptr<QmlIR::IRBuilder> irBuilder = aotCompiler && aotCompiler->isLintCompiler()
210 ? std::make_unique<QQmlJSAOTIRBuilder>() : std::make_unique<QmlIR::IRBuilder>();
211 if (!irBuilder->generateFromQml(sourceCode, inputFileName, &irDocument)) {
212 error->appendDiagnostics(inputFileName, irBuilder->errors);
217 annotateListElements(&irDocument);
218 QQmlJSAotFunctionMap aotFunctionsByIndex;
221 QmlIR::JSCodeGen v4CodeGen(&irDocument, wInterface, storeSourceLocation);
224 aotCompiler->setDocument(&v4CodeGen, &irDocument);
226 QHash<QmlIR::Object *, QmlIR::Object *> effectiveScopes;
227 for (QmlIR::Object *object: std::as_const(irDocument.objects)) {
228 if (object->functionsAndExpressions->count == 0 && object->bindingCount() == 0)
231 if (!v4CodeGen.generateRuntimeFunctions(object)) {
232 Q_ASSERT(v4CodeGen.hasError());
233 error->appendDiagnostic(inputFileName, v4CodeGen.error());
240 QmlIR::Object *scope = object;
241 for (
auto it = effectiveScopes.constFind(scope), end = effectiveScopes.constEnd();
242 it != end; it = effectiveScopes.constFind(scope)) {
246 aotCompiler->setScope(object, scope);
247 aotFunctionsByIndex[FileScopeCodeIndex] = aotCompiler->globalCode();
249 std::vector<BindingOrFunction> bindingsAndFunctions;
250 bindingsAndFunctions.reserve(object->bindingCount() + object->functionCount());
252 std::copy(object->bindingsBegin(), object->bindingsEnd(),
253 std::back_inserter(bindingsAndFunctions));
254 std::copy(object->functionsBegin(), object->functionsEnd(),
255 std::back_inserter(bindingsAndFunctions));
257 QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile;
258 for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first;
259 foe; foe = foe->next) {
260 functionsToCompile << *foe;
265 auto contextMap = v4CodeGen.module()->contextMap;
266 std::sort(bindingsAndFunctions.begin(), bindingsAndFunctions.end());
267 std::for_each(bindingsAndFunctions.begin(), bindingsAndFunctions.end(),
268 [&](
const BindingOrFunction &bindingOrFunction) {
269 std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>> result;
270 if (
const auto *binding = bindingOrFunction.binding()) {
271 switch (binding->type()) {
272 case QmlIR::Binding::Type_AttachedProperty:
273 case QmlIR::Binding::Type_GroupProperty:
274 effectiveScopes.insert(
275 irDocument.objects.at(binding->value.objectIndex), scope);
277 case QmlIR::Binding::Type_Boolean:
278 case QmlIR::Binding::Type_Number:
279 case QmlIR::Binding::Type_String:
280 case QmlIR::Binding::Type_Null:
281 case QmlIR::Binding::Type_Object:
282 case QmlIR::Binding::Type_Translation:
283 case QmlIR::Binding::Type_TranslationById:
289 Q_ASSERT(quint32(functionsToCompile.size()) > binding->value.compiledScriptIndex);
290 const auto &functionToCompile
291 = functionsToCompile[binding->value.compiledScriptIndex];
292 auto *parentNode = functionToCompile.parentNode;
293 Q_ASSERT(parentNode);
294 Q_ASSERT(contextMap.contains(parentNode));
295 QV4::Compiler::Context *context = contextMap.take(parentNode);
298 auto *node = functionToCompile.node;
301 if (context->returnsClosure) {
302 QQmlJS::AST::Node *inner
303 = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(
306 QV4::Compiler::Context *innerContext = contextMap.take(inner);
307 Q_ASSERT(innerContext);
308 qCDebug(lcAotCompiler) <<
"Compiling signal handler for"
309 << irDocument.stringAt(binding->propertyNameIndex);
310 std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>> innerResult
311 = aotCompiler->compileBinding(innerContext, *binding, inner);
312 if (
auto *errors = std::get_if<QList<QQmlJS::DiagnosticMessage>>(&innerResult)) {
313 for (
const auto &error : std::as_const(*errors)) {
314 qCDebug(lcAotCompiler) <<
"Compilation failed:"
315 << diagnosticErrorMessage(inputFileName, error);
317 }
else if (
auto *func = std::get_if<QQmlJSAotFunction>(&innerResult)) {
318 qCDebug(lcAotCompiler) <<
"Generated code:" << func->code;
319 aotFunctionsByIndex[innerContext->functionIndex] = *func;
323 qCDebug(lcAotCompiler) <<
"Compiling binding for property"
324 << irDocument.stringAt(binding->propertyNameIndex);
325 result = aotCompiler->compileBinding(context, *binding, node);
326 }
else if (
const auto *function = bindingOrFunction.function()) {
327 if (!aotCompiler->isLintCompiler() && !function->isQmlFunction)
330 Q_ASSERT(quint32(functionsToCompile.size()) > function->index);
331 auto *node = functionsToCompile[function->index].node;
333 Q_ASSERT(contextMap.contains(node));
334 QV4::Compiler::Context *context = contextMap.take(node);
337 const QString functionName = irDocument.stringAt(function->nameIndex);
338 qCDebug(lcAotCompiler) <<
"Compiling function" << functionName;
339 result = aotCompiler->compileFunction(context, functionName, node);
344 if (
auto *errors = std::get_if<QList<QQmlJS::DiagnosticMessage>>(&result)) {
345 for (
const auto &error : std::as_const(*errors)) {
346 qCDebug(lcAotCompiler) <<
"Compilation failed:"
347 << diagnosticErrorMessage(inputFileName, error);
349 }
else if (
auto *func = std::get_if<QQmlJSAotFunction>(&result)) {
350 if (func->skipReason.has_value()) {
351 qCDebug(lcAotCompiler) <<
"Compilation skipped:" << func->skipReason.value();
353 qCDebug(lcAotCompiler) <<
"Generated code:" << func->code;
354 auto index = object->runtimeFunctionIndices[bindingOrFunction.index()];
355 aotFunctionsByIndex[index] = *func;
361 if (!checkArgumentsObjectUseInSignalHandlers(irDocument, error)) {
362 *error = error->augment(inputFileName);
366 QmlIR::QmlUnitGenerator generator;
367 irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(
false);
368 generator.generate(irDocument);
370 const quint32 saveFlags
371 = QV4::CompiledData::Unit::StaticData
372 | QV4::CompiledData::Unit::PendingTypeCompilation;
373 QV4::CompiledData::SaveableUnitPointer saveable(
374 irDocument.javaScriptCompilationUnit->unitData(), saveFlags);
375 LookupSignatures sigs = aotCompiler ? aotCompiler->lookupSignatures() : LookupSignatures();
376 if (!saveFunction(saveable, aotFunctionsByIndex, sigs, &error->message))
383 const QString &inputFileName,
const QString &inputFileUrl,
386 Q_UNUSED(inputFileUrl);
388 QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit;
392 QFile f(inputFileName);
393 if (!f.open(QIODevice::ReadOnly)) {
394 error->message = QLatin1String(
"Error opening ") + inputFileName + QLatin1Char(
':') + f.errorString();
397 sourceCode = QString::fromUtf8(f.readAll());
398 if (f.error() != QFileDevice::NoError) {
399 error->message = QLatin1String(
"Error reading from ") + inputFileName + QLatin1Char(
':') + f.errorString();
404 const bool isModule = inputFileName.endsWith(QLatin1String(
".mjs"));
406 QList<QQmlJS::DiagnosticMessage> diagnostics;
409 unit = QV4::Compiler::Codegen::compileModule(
false, url, sourceCode,
410 QDateTime(), &diagnostics);
411 error->appendDiagnostics(inputFileName, diagnostics);
412 if (!unit || !unit->unitData())
415 QmlIR::Document irDocument(QString(), QString(),
false);
417 QQmlJS::Engine *engine = &irDocument.jsParserEngine;
418 QmlIR::ScriptDirectivesCollector directivesCollector(&irDocument);
419 QQmlJS::Directives *oldDirs = engine->directives();
420 engine->setDirectives(&directivesCollector);
421 auto directivesGuard = qScopeGuard([engine, oldDirs]{
422 engine->setDirectives(oldDirs);
425 QQmlJS::AST::Program *program =
nullptr;
428 QQmlJS::Lexer lexer(engine);
429 lexer.setCode(sourceCode, 1,
false);
430 QQmlJS::Parser parser(engine);
432 bool parsed = parser.parseProgram();
434 error->appendDiagnostics(inputFileName, parser.diagnosticMessages());
439 program = QQmlJS::AST::cast<QQmlJS::AST::Program*>(parser.rootNode());
441 lexer.setCode(QStringLiteral(
"undefined;"), 1,
false);
442 parsed = parser.parseProgram();
444 program = QQmlJS::AST::cast<QQmlJS::AST::Program*>(parser.rootNode());
450 QmlIR::JSCodeGen v4CodeGen(&irDocument);
451 v4CodeGen.generateFromProgram(
452 sourceCode, program, &irDocument.jsModule,
453 QV4::Compiler::ContextType::ScriptImportedByQML);
454 if (v4CodeGen.hasError()) {
455 error->appendDiagnostic(inputFileName, v4CodeGen.error());
460 Q_ASSERT(irDocument.jsModule.fileName.isEmpty());
461 Q_ASSERT(irDocument.jsModule.finalUrl.isEmpty());
463 irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(
false);
464 QmlIR::QmlUnitGenerator generator;
465 generator.generate(irDocument);
466 unit =
std::move(irDocument.javaScriptCompilationUnit);
470 QQmlJSAotFunctionMap funcs;
471 LookupSignatures sigs;
473 QV4::CompiledData::SaveableUnitPointer(unit->unitData()), funcs, sigs, &error->message);
477 [](const QQmlPrivate::AOTCompiledContext *aotContext, void **argv) {
478Q_UNUSED(aotContext)
479Q_UNUSED(argv)
480)";
484 return "u\"%1\"_s"_L1.arg(s);
489 bool isComposite = type.isComposite == QQmlPrivate::AOTLookupValidation::IsComposite::Yes;
490 bool isIC = type.isInlineComponent == QQmlPrivate::AOTLookupValidation::IsIC::Yes;
491 return u"Type{ %1, %2, %3, %4, %5 }"_s
492 .arg(wrapString(type.module), wrapString(type.name),
493 wrapString(type.icNameOrExtensionTypeName),
494 isComposite ?
"IsComposite::Yes"_L1 :
"IsComposite::No"_L1,
495 isIC ?
"IsIC::Yes"_L1 :
"IsIC::No"_L1);
500 return "Lookup{ %1, %2, %3 }"_L1.arg(typeToString(lookup.base), wrapString(lookup.member),
501 wrapString(lookup.enumName));
506 if (
const auto *p = std::get_if<QQmlPrivate::AOTLookupValidation::PropertySignature>(&signature)) {
507 return u"PropertySignature{ %1, %2 }"_s
508 .arg(typeToString(p->type), QString::number(p->relativeIndex));
509 }
else if (
const auto *e = std::get_if<QQmlPrivate::AOTLookupValidation::EnumKeySignature>(&signature)) {
510 bool isFlag = e->isFlag == QQmlPrivate::AOTLookupValidation::IsFlag::Yes;
511 return u"EnumKeySignature{ %1, %2 }"_s
512 .arg(QString::number(e->value), isFlag ?
"IsFlag::Yes"_L1 :
"IsFlag::No"_L1);
513 }
else if (
const auto *m = std::get_if<QQmlPrivate::AOTLookupValidation::MethodSignature>(&signature)) {
514 QString paramNames =
"{ "_L1;
515 for (
const auto ¶mName : m->paramNames)
516 paramNames += wrapString(paramName) +
", "_L1;
519 QString types =
"{ "_L1;
520 for (
const auto ¶mType : m->types)
521 types += typeToString(paramType) +
", "_L1;
524 bool isSignal = m->isSignal == QQmlPrivate::AOTLookupValidation::IsSignal::Yes;
525 return u"MethodSignature{ %1, %2, %3, %4 }"_s
526 .arg(paramNames, types, QString::number(m->relativeIndex),
527 isSignal ?
"IsSignal::Yes"_L1 :
"IsSignal::No"_L1);
529 Q_UNREACHABLE_RETURN(QString());
533bool validateLookupSignatures(QQmlEngine *engine, QV4::CompiledData::CompilationUnit *cu)
534{
535 // AOT validation code not generated (NO_GENERATE_AOT_VALIDATION)
536 Q_UNUSED(engine);
537 Q_UNUSED(cu);
538 return true;
539}
540
541)";
543template <
typename WriteStr>
545 const WriteStr &writeStr,
const LookupSignatures &lookupSignatures,
bool noAotValidation)
547 if (noAotValidation) {
553 using namespace QQmlPrivate::AOTLookupValidation;
554 if (!writeStr(
"QQmlPrivate::AOTLookupValidation::LookupSignatures expectedLookupSignatures()\n"
556 " using namespace Qt::StringLiterals;\n"
557 " using namespace QQmlPrivate::AOTLookupValidation;\n"
562 const auto &signatures = lookupSignatures.asKeyValueRange();
563 QList<std::pair<Lookup, Signature>> sorted{ signatures.begin(), signatures.end() };
564 std::stable_sort(sorted.begin(), sorted.end(), [&](
const auto &lhs,
const auto &rhs) {
565 const auto &lTypeString = typeToString(lhs.first.base);
566 const auto &rTypeString = typeToString(rhs.first.base);
567 if (lTypeString == rTypeString)
568 return lhs.first.member < rhs.first.member;
569 return lTypeString < rTypeString;
572 for (
const auto &[memberlookup, signature] : sorted) {
573 if (!writeStr(
" { %1, %2 },\n"_L1
574 .arg(lookupToString(memberlookup), signatureToString(signature))
580 if (!writeStr(
" };\n}\n"))
583 const QString validateLookupSignatures = uR"(
584bool validateLookupSignatures(QQmlEngine *engine, QV4::CompiledData::CompilationUnit *cu)
585{
586 enum ValidationState { Pending, Failed, Succeeded };
587 static ValidationState state = Pending;
588 if (state == Failed)
589 return false;
590 if (state == Succeeded)
592 const auto &expectedSignatures = expectedLookupSignatures();
593 for (const auto &[lookup, expectedSignature] : expectedSignatures.asKeyValueRange()) {
594 if (!QQmlPrivate::AOTLookupValidation::validateLookupSignature(engine, cu, lookup, expectedSignature)) {
595 state = Failed;
596 return false;
597 }
598 }
599 state = Succeeded;
600 return true;
601}
602
603)"_s;
605 if (!writeStr(validateLookupSignatures.toUtf8()))
611bool qSaveQmlJSUnitAsCpp(
const QString &inputFileName,
const QString &outputFileName,
612 const QV4::CompiledData::SaveableUnitPointer &unit,
613 const QQmlJSAotFunctionMap &aotFunctions,
614 const LookupSignatures &lookupSignatures,
bool noAotValidation,
615 QString *errorString)
617#if QT_CONFIG(temporaryfile)
618 QSaveFile f(outputFileName);
620 QFile f(outputFileName);
622 if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
623 *errorString = f.errorString();
627 auto writeStr = [&f, errorString](
const QByteArray &data) {
628 if (f.write(data) != data.size()) {
629 *errorString = f.errorString();
635 if (!writeStr(
"// "))
638 if (!writeStr(inputFileName.toUtf8()))
644 if (!writeStr(
"#include <QtQml/qqmlprivate.h>\n"))
647 if (!noAotValidation) {
648 if (!writeStr(
"#include <QtCore/qhash.h>\n"))
652 if (!aotFunctions.isEmpty()) {
653 QStringList includes;
655 for (
const auto &function : aotFunctions)
656 includes.append(function.includes);
658 std::sort(includes.begin(), includes.end());
659 const auto end = std::unique(includes.begin(), includes.end());
660 for (
auto it = includes.begin(); it != end; ++it) {
661 if (!writeStr(QStringLiteral(
"#include <%1>\n").arg(*it).toUtf8()))
666 if (!writeStr(QByteArrayLiteral(
"\nnamespace QmlCacheGeneratedCode {\nnamespace ")))
669 if (!writeStr(qQmlJSSymbolNamespaceForPath(inputFileName).toUtf8()))
672 if (!writeStr(
" {\n"))
675 if (!generateAotValidationCode(writeStr, lookupSignatures, noAotValidation))
678 if (!writeStr(QByteArrayLiteral(
"extern const unsigned char qmlData alignas(16) [];\n"
679 "extern const unsigned char qmlData alignas(16) [] = {\n")))
682 unit.saveToDisk<uchar>([&writeStr](
const uchar *begin, quint32 size) {
683 QByteArray hexifiedData;
685 QTextStream stream(&hexifiedData);
686 const uchar *end = begin + size;
689 for (
const uchar *data = begin; data < end; ++data, ++col) {
696 stream <<
"0x" << *data;
700 return writeStr(hexifiedData);
705 if (!writeStr(
"};\n"))
709 if (aotFunctions.size() <= 1) {
711 writeStr(
"extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
712 "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = { { 0, 0, nullptr, nullptr } };\n");
714 writeStr(
"extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
715 "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = {\n");
717 QString footer = QStringLiteral(
"}\n");
719 for (QQmlJSAotFunctionMap::ConstIterator func = aotFunctions.constBegin(),
720 end = aotFunctions.constEnd();
721 func != end; ++func) {
723 if (func.key() == FileScopeCodeIndex)
726 const QString function = QString::fromUtf8(funcHeaderCode) + func.value().code + footer;
728 writeStr(QStringLiteral(
"{ %1, %2, [](QV4::ExecutableCompilationUnit *contextUnit, "
729 "QMetaType *argTypes) {\n%3}, %4 },")
731 .arg(func->numArguments)
732 .arg(func->signature.isEmpty() ? u" Q_UNUSED(contextUnit);\n Q_UNUSED(argTypes);\n"_s : func->signature, function)
733 .toUtf8().constData());
737 writeStr(
"{ 0, 0, nullptr, nullptr }");
741 if (!writeStr(
"}\n}\n"))
744#if QT_CONFIG(temporaryfile)
746 *errorString = f.errorString();
754QQmlJSAotCompiler::QQmlJSAotCompiler(
755 QQmlJSImporter *importer,
const QString &resourcePath,
const QStringList &qmldirFiles,
756 QQmlJSLogger *logger)
757 : m_typeResolver(importer)
758 , m_resourcePath(resourcePath)
759 , m_qmldirFiles(qmldirFiles)
760 , m_importer(importer)
765void QQmlJSAotCompiler::setDocument(
766 const QmlIR::JSCodeGen *codegen,
const QmlIR::Document *irDocument)
769 m_document = irDocument;
770 const QFileInfo resourcePathInfo(m_resourcePath);
771 if (m_logger->filePath().isEmpty())
772 m_logger->setFilePath(resourcePathInfo.fileName());
773 m_logger->setCode(irDocument->code);
774 m_unitGenerator = &irDocument->jsGenerator;
775 QQmlJSImportVisitor visitor(m_importer, m_logger,
776 resourcePathInfo.canonicalPath() + u'/',
778 m_typeResolver.init(&visitor, irDocument->program);
781void QQmlJSAotCompiler::setScope(
const QmlIR::Object *object,
const QmlIR::Object *scope)
783 m_currentObject = object;
784 m_currentScope = scope;
787static bool isStrict(
const QmlIR::Document *doc)
789 for (
const QmlIR::Pragma *pragma : doc->pragmas) {
790 if (pragma->type == QmlIR::Pragma::Strict)
796QQmlJS::DiagnosticMessage QQmlJSAotCompiler::diagnose(
797 const QString &message, QtMsgType type,
const QQmlJS::SourceLocation &location)
const
799 if (isStrict(m_document)
800 && (type == QtWarningMsg || type == QtCriticalMsg || type == QtFatalMsg)
801 && m_logger->categorySeverity(qmlCompiler) == QQmlSA::WarningSeverity::Error) {
802 qFatal(
"%s:%d: (strict mode) %s",
803 qPrintable(QFileInfo(m_resourcePath).fileName()),
804 location.startLine, qPrintable(message));
807 return QQmlJS::DiagnosticMessage {
814std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>> QQmlJSAotCompiler::compileBinding(
815 const QV4::Compiler::Context *context,
const QmlIR::Binding &irBinding,
816 QQmlJS::AST::Node *astNode)
818 QQmlJSFunctionInitializer initializer(
819 &m_typeResolver, m_currentObject->location, m_currentScope->location, m_logger);
821 const QString name = m_document->stringAt(irBinding.propertyNameIndex);
822 QQmlJSCompilePass::Function function = initializer.run( context, name, astNode, irBinding);
824 const QQmlJSAotFunction aotFunction = doCompileAndRecordAotStats(
825 context, &function, name, astNode->firstSourceLocation());
827 if (
const auto errors = finalizeBindingOrFunction())
830 qCDebug(lcAotCompiler()) <<
"includes:" << aotFunction.includes;
831 qCDebug(lcAotCompiler()) <<
"binding code:" << aotFunction.code;
835std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>> QQmlJSAotCompiler::compileFunction(
836 const QV4::Compiler::Context *context,
const QString &name, QQmlJS::AST::Node *astNode)
838 QQmlJSFunctionInitializer initializer(
839 &m_typeResolver, m_currentObject->location, m_currentScope->location, m_logger);
840 QQmlJSCompilePass::Function function = initializer.run(context, name, astNode);
842 const QQmlJSAotFunction aotFunction = doCompileAndRecordAotStats(
843 context, &function, name, astNode->firstSourceLocation());
845 if (
const auto errors = finalizeBindingOrFunction())
848 qCDebug(lcAotCompiler()) <<
"includes:" << aotFunction.includes;
849 qCDebug(lcAotCompiler()) <<
"binding code:" << aotFunction.code;
853QQmlJSAotFunction QQmlJSAotCompiler::globalCode()
const
855 QQmlJSAotFunction global;
857 u"QtQml/qjsengine.h"_s,
858 u"QtQml/qjsprimitivevalue.h"_s,
859 u"QtQml/qjsvalue.h"_s,
860 u"QtQml/qqmlcomponent.h"_s,
861 u"QtQml/qqmlcontext.h"_s,
862 u"QtQml/qqmlengine.h"_s,
863 u"QtQml/qqmllist.h"_s,
865 u"QtCore/qdatetime.h"_s,
866 u"QtCore/qtimezone.h"_s,
867 u"QtCore/qobject.h"_s,
868 u"QtCore/qstring.h"_s,
869 u"QtCore/qstringlist.h"_s,
871 u"QtCore/qvariant.h"_s,
878std::optional<QList<QQmlJS::DiagnosticMessage>> QQmlJSAotCompiler::finalizeBindingOrFunction()
880 const auto archiveMessages = qScopeGuard([
this]() { m_logger->finalizeFunction(); });
882 if (!m_logger->currentFunctionHasCompileError())
885 QList<QQmlJS::DiagnosticMessage> errors;
886 m_logger->iterateCurrentFunctionMessages([&](
const Message &msg) {
887 if (msg.compilationStatus == Message::CompilationStatus::Error)
888 errors.append(diagnose(msg.message, msg.type, msg.loc));
893QQmlJSAotFunction QQmlJSAotCompiler::doCompile(
894 const QV4::Compiler::Context *context,
const QQmlJSCompilePass::Function *function)
896 if (m_logger->currentFunctionHasErrorOrSkip())
897 return QQmlJSAotFunction();
899 bool basicBlocksValidationFailed =
false;
900 QQmlJSBasicBlocks basicBlocks(context, m_unitGenerator, &m_typeResolver, m_logger);
901 auto passResult = basicBlocks.run(function, m_flags, basicBlocksValidationFailed);
902 auto &[blocks, annotations] = passResult;
904 QQmlJSTypePropagator propagator(
905 m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
906 passResult = propagator.run(function);
907 if (m_logger->currentFunctionHasErrorOrSkip())
908 return QQmlJSAotFunction();
910 QQmlJSShadowCheck shadowCheck(
911 m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
912 passResult = shadowCheck.run(function);
913 if (m_logger->currentFunctionHasErrorOrSkip())
914 return QQmlJSAotFunction();
916 QQmlJSOptimizations optimizer(
917 m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations,
918 basicBlocks.objectAndArrayDefinitions());
919 passResult = optimizer.run(function);
920 if (m_logger->currentFunctionHasErrorOrSkip())
921 return QQmlJSAotFunction();
923 QQmlJSStorageInitializer initializer(
924 m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
925 passResult = initializer.run(function);
928 QQmlJSStorageGeneralizer generalizer(
929 m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
930 passResult = generalizer.run(function);
931 if (m_logger->currentFunctionHasErrorOrSkip())
932 return QQmlJSAotFunction();
934 QQmlJSCodeGenerator codegen(context, m_unitGenerator, &m_typeResolver, m_logger, blocks,
935 annotations, noAotValidation());
936 QQmlJSAotFunction result = codegen.run(function, basicBlocksValidationFailed);
937 if (m_logger->currentFunctionHasErrorOrSkip())
938 return QQmlJSAotFunction();
940 m_lookupSignatures.insert(codegen.lookupSignatures());
944QQmlJSAotFunction QQmlJSAotCompiler::doCompileAndRecordAotStats(
945 const QV4::Compiler::Context *context,
const QQmlJSCompilePass::Function *function,
946 const QString &name, QQmlJS::SourceLocation location)
948 QElapsedTimer timer {};
950 QQmlJSAotFunction result;
951 if (!m_logger->currentFunctionHasCompileError())
952 result = doCompile(context, function);
953 auto elapsed = std::chrono::milliseconds { timer.elapsed() };
955 if (QQmlJS::QQmlJSAotCompilerStats::recordAotStats()) {
956 QQmlJS::AotStatsEntry entry;
957 entry.codegenDuration = elapsed;
958 entry.functionName = name;
959 entry.message = m_logger->currentFunctionWasSkipped()
960 ? m_logger->currentFunctionCompileSkipMessage()
961 : m_logger->currentFunctionCompileErrorMessage();
962 entry.line = location.startLine;
963 entry.column = location.startColumn;
964 if (m_logger->currentFunctionWasSkipped())
965 entry.codegenResult = QQmlJS::CodegenResult::Skip;
966 else if (m_logger->currentFunctionHasCompileError())
967 entry.codegenResult = QQmlJS::CodegenResult::Failure;
969 entry.codegenResult = QQmlJS::CodegenResult::Success;
970 QQmlJS::QQmlJSAotCompilerStats::addEntry(
971 function->qmlScope.containedType()->filePath(), entry);
974 if (m_logger->currentFunctionWasSkipped())
975 result.skipReason = m_logger->currentFunctionCompileSkipMessage();
const QmlIR::Function * function() const
const QmlIR::Binding * binding() const
friend bool operator<(const BindingOrFunction &lhs, const BindingOrFunction &rhs)
BindingOrFunction(const QmlIR::Binding &b)
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
static const char * funcHeaderCode
static QString lookupToString(const QQmlPrivate::AOTLookupValidation::Lookup &lookup)
static QString wrapString(const QString &s)
static QString signatureToString(const QQmlPrivate::AOTLookupValidation::Signature &signature)
static bool generateAotValidationCode(const WriteStr &writeStr, const LookupSignatures &lookupSignatures, bool noAotValidation)
static bool checkArgumentsObjectUseInSignalHandlers(const QmlIR::Document &doc, QQmlJSCompileError *error)
bool qCompileQmlFile(QmlIR::Document &irDocument, const QString &inputFileName, const QQmlJSSaveFunction &saveFunction, QQmlJSAotCompiler *aotCompiler, QQmlJSCompileError *error, bool storeSourceLocation, QV4::Compiler::CodegenWarningInterface *wInterface, const QString *fileContents)
static void annotateListElements(QmlIR::Document *document)
bool qCompileQmlFile(const QString &inputFileName, const QQmlJSSaveFunction &saveFunction, QQmlJSAotCompiler *aotCompiler, QQmlJSCompileError *error, bool storeSourceLocation, QV4::Compiler::CodegenWarningInterface *wInterface, const QString *fileContents)
static QString diagnosticErrorMessage(const QString &fileName, const QQmlJS::DiagnosticMessage &m)
static QString typeToString(const QQmlPrivate::AOTLookupValidation::Type &type)
bool qCompileJSFile(const QString &inputFileName, const QString &inputFileUrl, const QQmlJSSaveFunction &saveFunction, QQmlJSCompileError *error)
static const int FileScopeCodeIndex
static const char * skippedValidationCode
std::function< bool(const QV4::CompiledData::SaveableUnitPointer &, const QQmlJSAotFunctionMap &, const LookupSignatures &, QString *)> QQmlJSSaveFunction