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);
83 QQmlJSCompileError *error)
85 for (QmlIR::Object *object: std::as_const(doc.objects)) {
86 for (
auto binding = object->bindingsBegin(); binding != object->bindingsEnd(); ++binding) {
87 if (binding->type() != QV4::CompiledData::Binding::Type_Script)
89 const QString propName = doc.stringAt(binding->propertyNameIndex);
90 if (!QQmlSignalNames::isHandlerName(propName))
92 auto compiledFunction = doc.jsModule.functions.value(object->runtimeFunctionIndices.at(binding->value.compiledScriptIndex));
93 if (!compiledFunction)
95 if (compiledFunction->usesArgumentsObject == QV4::Compiler::Context::UsesArgumentsObject::Used) {
96 error->message = QLatin1Char(
':') + QString::number(compiledFunction->line) + QLatin1Char(
':');
97 if (compiledFunction->column > 0)
98 error->message += QString::number(compiledFunction->column) + QLatin1Char(
':');
100 error->message += QLatin1String(
" error: The use of eval() or the use of the arguments object in signal handlers is\n"
101 "not supported when compiling qml files ahead of time. That is because it's ambiguous if \n"
102 "any signal parameter is called \"arguments\". Similarly the string passed to eval might use\n"
103 "\"arguments\". Unfortunately we cannot distinguish between it being a parameter or the\n"
104 "JavaScript arguments object at this point.\n"
105 "Consider renaming the parameter of the signal if applicable or moving the code into a\n"
122 return lhs.index() < rhs.index();
131 ? m_binding->value.compiledScriptIndex
134 : std::numeric_limits<quint32>::max());
138 const QmlIR::Binding *m_binding =
nullptr;
139 const QmlIR::Function *m_function =
nullptr;
143 QQmlJSAotCompiler *aotCompiler, QQmlJSCompileError *error,
144 bool storeSourceLocation, QV4::Compiler::CodegenWarningInterface *wInterface,
145 const QString *fileContents)
147 QmlIR::Document irDocument(QString(), QString(),
false);
148 return qCompileQmlFile(irDocument, inputFileName, saveFunction, aotCompiler, error,
149 storeSourceLocation, wInterface, fileContents);
154 QQmlJSCompileError *error,
bool storeSourceLocation,
155 QV4::Compiler::CodegenWarningInterface *wInterface,
const QString *fileContents)
159 if (fileContents !=
nullptr) {
160 sourceCode = *fileContents;
162 QFile f(inputFileName);
163 if (!f.open(QIODevice::ReadOnly)) {
164 error->message = QLatin1String(
"Error opening ") + inputFileName + QLatin1Char(
':') + f.errorString();
167 sourceCode = QString::fromUtf8(f.readAll());
168 if (f.error() != QFileDevice::NoError) {
169 error->message = QLatin1String(
"Error reading from ") + inputFileName + QLatin1Char(
':') + f.errorString();
176 std::unique_ptr<QmlIR::IRBuilder> irBuilder = aotCompiler && aotCompiler->isLintCompiler()
177 ? std::make_unique<QQmlJSAOTIRBuilder>() : std::make_unique<QmlIR::IRBuilder>();
178 if (!irBuilder->generateFromQml(sourceCode, inputFileName, &irDocument, wInterface)) {
179 error->appendDiagnostics(inputFileName, irBuilder->errors);
184 QQmlJSAotFunctionMap aotFunctionsByIndex;
187 QmlIR::JSCodeGen v4CodeGen(&irDocument, wInterface, storeSourceLocation);
190 aotCompiler->setDocument(&v4CodeGen, &irDocument);
192 QHash<QmlIR::Object *, QmlIR::Object *> effectiveScopes;
193 for (QmlIR::Object *object: std::as_const(irDocument.objects)) {
194 if (object->functionsAndExpressions->count == 0 && object->bindingCount() == 0)
197 if (!v4CodeGen.generateRuntimeFunctions(object)) {
198 Q_ASSERT(v4CodeGen.hasError());
199 error->appendDiagnostic(inputFileName, v4CodeGen.error());
206 QmlIR::Object *scope = object;
207 for (
auto it = effectiveScopes.constFind(scope), end = effectiveScopes.constEnd();
208 it != end; it = effectiveScopes.constFind(scope)) {
212 aotCompiler->setScope(object, scope);
213 aotFunctionsByIndex[FileScopeCodeIndex] = aotCompiler->globalCode();
215 std::vector<BindingOrFunction> bindingsAndFunctions;
216 bindingsAndFunctions.reserve(object->bindingCount() + object->functionCount());
218 std::copy(object->bindingsBegin(), object->bindingsEnd(),
219 std::back_inserter(bindingsAndFunctions));
220 std::copy(object->functionsBegin(), object->functionsEnd(),
221 std::back_inserter(bindingsAndFunctions));
223 QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile;
224 for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first;
225 foe; foe = foe->next) {
226 functionsToCompile << *foe;
231 auto contextMap = v4CodeGen.module()->contextMap;
232 std::sort(bindingsAndFunctions.begin(), bindingsAndFunctions.end());
233 std::for_each(bindingsAndFunctions.begin(), bindingsAndFunctions.end(),
234 [&](
const BindingOrFunction &bindingOrFunction) {
235 using Result = std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>>;
236 const auto processResult = [&](
const Result &result) {
237 if (
auto *errors = std::get_if<QList<QQmlJS::DiagnosticMessage>>(&result)) {
238 for (
const auto &error : *errors) {
239 qCDebug(lcAotCompiler) <<
"Compilation failed:"
240 << diagnosticErrorMessage(inputFileName, error);
242 }
else if (
auto *func = std::get_if<QQmlJSAotFunction>(&result)) {
243 if (func->skipReason.has_value()) {
244 qCDebug(lcAotCompiler) <<
"Compilation skipped:"
245 << func->skipReason.value();
247 qCDebug(lcAotCompiler) <<
"Generated code:" << func->code;
248 auto index = object->runtimeFunctionIndices[bindingOrFunction.index()];
249 aotFunctionsByIndex[index] = *func;
254 if (
const auto *binding = bindingOrFunction.binding()) {
255 switch (binding->type()) {
256 case QmlIR::Binding::Type_AttachedProperty:
257 case QmlIR::Binding::Type_GroupedProperty:
258 effectiveScopes.insert(
259 irDocument.objects.at(binding->value.objectIndex), scope);
261 case QmlIR::Binding::Type_Boolean:
262 case QmlIR::Binding::Type_Number:
263 case QmlIR::Binding::Type_String:
264 case QmlIR::Binding::Type_Null:
265 case QmlIR::Binding::Type_Object:
266 case QmlIR::Binding::Type_Translation:
267 case QmlIR::Binding::Type_TranslationById:
273 Q_ASSERT(quint32(functionsToCompile.size()) > binding->value.compiledScriptIndex);
274 const auto &functionToCompile
275 = functionsToCompile[binding->value.compiledScriptIndex];
276 auto *parentNode = functionToCompile.parentNode;
277 Q_ASSERT(parentNode);
278 Q_ASSERT(contextMap.contains(parentNode));
279 QV4::Compiler::Context *context = contextMap.take(parentNode);
282 auto *node = functionToCompile.node;
285 if (context->returnsClosure) {
286 QQmlJS::AST::Node *inner
287 = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(
290 QV4::Compiler::Context *innerContext = contextMap.take(inner);
291 Q_ASSERT(innerContext);
292 qCDebug(lcAotCompiler) <<
"Compiling signal handler for"
293 << irDocument.stringAt(binding->propertyNameIndex);
294 std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>> innerResult
295 = aotCompiler->compileBinding(innerContext, *binding, inner);
296 if (
auto *errors = std::get_if<QList<QQmlJS::DiagnosticMessage>>(&innerResult)) {
297 for (
const auto &error : std::as_const(*errors)) {
298 qCDebug(lcAotCompiler) <<
"Compilation failed:"
299 << diagnosticErrorMessage(inputFileName, error);
301 }
else if (
auto *func = std::get_if<QQmlJSAotFunction>(&innerResult)) {
302 qCDebug(lcAotCompiler) <<
"Generated code:" << func->code;
303 aotFunctionsByIndex[innerContext->functionIndex] = *func;
307 qCDebug(lcAotCompiler) <<
"Compiling binding for property"
308 << irDocument.stringAt(binding->propertyNameIndex);
309 processResult(aotCompiler->compileBinding(context, *binding, node));
310 }
else if (
const auto *function = bindingOrFunction.function()) {
311 if (!aotCompiler->isLintCompiler() && !function->isQmlFunction)
314 Q_ASSERT(quint32(functionsToCompile.size()) > function->index);
315 auto *node = functionsToCompile[function->index].node;
317 Q_ASSERT(contextMap.contains(node));
318 QV4::Compiler::Context *context = contextMap.take(node);
321 const QString functionName = irDocument.stringAt(function->nameIndex);
322 qCDebug(lcAotCompiler) <<
"Compiling function" << functionName;
323 processResult(aotCompiler->compileFunction(context, functionName, node));
330 if (!checkArgumentsObjectUseInSignalHandlers(irDocument, error)) {
331 *error = error->augment(inputFileName);
335 QmlIR::QmlUnitGenerator generator;
336 irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(
false);
337 generator.generate(irDocument);
339 const quint32 saveFlags
340 = QV4::CompiledData::Unit::StaticData
341 | QV4::CompiledData::Unit::PendingTypeCompilation;
342 QV4::CompiledData::SaveableUnitPointer saveable(
343 irDocument.javaScriptCompilationUnit->unitData(), saveFlags);
344 LookupSignatures sigs = aotCompiler ? aotCompiler->lookupSignatures() : LookupSignatures();
345 if (!saveFunction(saveable, aotFunctionsByIndex, sigs, &error->message))
352 const QString &inputFileName,
const QString &inputFileUrl,
355 Q_UNUSED(inputFileUrl);
357 QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit;
361 QFile f(inputFileName);
362 if (!f.open(QIODevice::ReadOnly)) {
363 error->message = QLatin1String(
"Error opening ") + inputFileName + QLatin1Char(
':') + f.errorString();
366 sourceCode = QString::fromUtf8(f.readAll());
367 if (f.error() != QFileDevice::NoError) {
368 error->message = QLatin1String(
"Error reading from ") + inputFileName + QLatin1Char(
':') + f.errorString();
373 const bool isModule = inputFileName.endsWith(QLatin1String(
".mjs"));
375 QList<QQmlJS::DiagnosticMessage> diagnostics;
378 unit = QV4::Compiler::Codegen::compileModule(
false, url, sourceCode,
379 QDateTime(), &diagnostics);
380 error->appendDiagnostics(inputFileName, diagnostics);
381 if (!unit || !unit->unitData())
384 QmlIR::Document irDocument(QString(), QString(),
false);
386 QQmlJS::Engine *engine = &irDocument.jsParserEngine;
387 QmlIR::ScriptDirectivesCollector directivesCollector(&irDocument);
388 QQmlJS::Directives *oldDirs = engine->directives();
389 engine->setDirectives(&directivesCollector);
390 auto directivesGuard = qScopeGuard([engine, oldDirs]{
391 engine->setDirectives(oldDirs);
394 QQmlJS::AST::Program *program =
nullptr;
397 QQmlJS::Lexer lexer(engine);
398 lexer.setCode(sourceCode, 1,
false);
399 QQmlJS::Parser parser(engine);
401 bool parsed = parser.parseProgram();
403 error->appendDiagnostics(inputFileName, parser.diagnosticMessages());
408 program = QQmlJS::AST::cast<QQmlJS::AST::Program*>(parser.rootNode());
410 lexer.setCode(QStringLiteral(
"undefined;"), 1,
false);
411 parsed = parser.parseProgram();
413 program = QQmlJS::AST::cast<QQmlJS::AST::Program*>(parser.rootNode());
419 QmlIR::JSCodeGen v4CodeGen(&irDocument);
420 v4CodeGen.generateFromProgram(
421 sourceCode, program, &irDocument.jsModule,
422 QV4::Compiler::ContextType::ScriptImportedByQML);
423 if (v4CodeGen.hasError()) {
424 error->appendDiagnostic(inputFileName, v4CodeGen.error());
429 Q_ASSERT(irDocument.jsModule.fileName.isEmpty());
430 Q_ASSERT(irDocument.jsModule.finalUrl.isEmpty());
432 irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(
false);
433 QmlIR::QmlUnitGenerator generator;
434 generator.generate(irDocument);
435 unit =
std::move(irDocument.javaScriptCompilationUnit);
439 QQmlJSAotFunctionMap funcs;
440 LookupSignatures sigs;
442 QV4::CompiledData::SaveableUnitPointer(unit->unitData()), funcs, sigs, &error->message);
446 [](const QQmlPrivate::AOTCompiledContext *aotContext, void **argv) {
447Q_UNUSED(aotContext)
448Q_UNUSED(argv)
449)";
453 return "u\"%1\"_s"_L1.arg(s);
458 bool isComposite = type.isComposite == QQmlPrivate::AOTLookupValidation::IsComposite::Yes;
459 bool isIC = type.isInlineComponent == QQmlPrivate::AOTLookupValidation::IsIC::Yes;
460 QString version =
"QTypeRevision()"_L1;
461 if (type.version.hasMinorVersion()) {
462 version =
"QTypeRevision::fromVersion(%1, %2)"_L1.arg(
463 QString::number(type.version.majorVersion()),
464 QString::number(type.version.minorVersion()));
465 }
else if (type.version.hasMajorVersion()) {
466 version =
"QTypeRevision::fromMajorVersion(%1)"_L1.arg(
467 QString::number(type.version.majorVersion()));
470 return u"Type{ %1, %2, %3, %4, %5, %6 }"_s.arg(wrapString(type.module), wrapString(type.name),
471 wrapString(type.icNameOrExtensionTypeName),
473 isComposite ?
"IsComposite::Yes"_L1
474 :
"IsComposite::No"_L1,
475 isIC ?
"IsIC::Yes"_L1 :
"IsIC::No"_L1);
480 return "Lookup{ %1, %2, %3 }"_L1.arg(typeToString(lookup.base), wrapString(lookup.member),
481 wrapString(lookup.enumName));
486 if (
const auto *p = std::get_if<QQmlPrivate::AOTLookupValidation::PropertySignature>(&signature)) {
487 return u"PropertySignature{ %1, %2 }"_s
488 .arg(typeToString(p->type), QString::number(p->relativeIndex));
489 }
else if (
const auto *e = std::get_if<QQmlPrivate::AOTLookupValidation::EnumKeySignature>(&signature)) {
490 bool isFlag = e->isFlag == QQmlPrivate::AOTLookupValidation::IsFlag::Yes;
491 return u"EnumKeySignature{ %1, %2 }"_s
492 .arg(QString::number(e->value), isFlag ?
"IsFlag::Yes"_L1 :
"IsFlag::No"_L1);
493 }
else if (
const auto *m = std::get_if<QQmlPrivate::AOTLookupValidation::MethodSignature>(&signature)) {
494 QString paramNames =
"{ "_L1;
495 for (
const auto ¶mName : m->paramNames)
496 paramNames += wrapString(paramName) +
", "_L1;
499 QString types =
"{ "_L1;
500 for (
const auto ¶mType : m->types)
501 types += typeToString(paramType) +
", "_L1;
504 bool isSignal = m->isSignal == QQmlPrivate::AOTLookupValidation::IsSignal::Yes;
505 return u"MethodSignature{ %1, %2, %3, %4 }"_s
506 .arg(paramNames, types, QString::number(m->relativeIndex),
507 isSignal ?
"IsSignal::Yes"_L1 :
"IsSignal::No"_L1);
509 Q_UNREACHABLE_RETURN(QString());
513bool validateLookupSignatures(QQmlEngine *engine, QV4::CompiledData::CompilationUnit *cu)
514{
515 // AOT validation code not generated (NO_GENERATE_AOT_VALIDATION)
516 Q_UNUSED(engine);
517 Q_UNUSED(cu);
518 return true;
519}
520
521)";
523template <
typename WriteStr>
525 const WriteStr &writeStr,
const LookupSignatures &lookupSignatures,
bool noAotValidation)
527 if (noAotValidation) {
533 using namespace QQmlPrivate::AOTLookupValidation;
534 if (!writeStr(
"QQmlPrivate::AOTLookupValidation::LookupSignatures expectedLookupSignatures()\n"
536 " using namespace Qt::StringLiterals;\n"
537 " using namespace QQmlPrivate::AOTLookupValidation;\n"
542 const auto &signatures = lookupSignatures.asKeyValueRange();
543 QList<std::pair<Lookup, Signature>> sorted{ signatures.begin(), signatures.end() };
544 std::stable_sort(sorted.begin(), sorted.end(), [&](
const auto &lhs,
const auto &rhs) {
545 const auto &lTypeString = typeToString(lhs.first.base);
546 const auto &rTypeString = typeToString(rhs.first.base);
547 if (lTypeString == rTypeString)
548 return lhs.first.member < rhs.first.member;
549 return lTypeString < rTypeString;
552 for (
const auto &[memberlookup, signature] : sorted) {
553 if (!writeStr(
" { %1, %2 },\n"_L1
554 .arg(lookupToString(memberlookup), signatureToString(signature))
560 if (!writeStr(
" };\n}\n"))
563 const QString validateLookupSignatures = uR"(
564bool validateLookupSignatures(QQmlEngine *engine, QV4::CompiledData::CompilationUnit *cu)
565{
566 enum ValidationState { Pending, Failed, Succeeded };
567 static ValidationState state = Pending;
568 if (state == Failed)
569 return false;
570 if (state == Succeeded)
572 const auto &expectedSignatures = expectedLookupSignatures();
573 for (const auto &[lookup, expectedSignature] : expectedSignatures.asKeyValueRange()) {
574 if (!QQmlPrivate::AOTLookupValidation::validateLookupSignature(engine, cu, lookup, expectedSignature)) {
575 state = Failed;
576 return false;
577 }
578 }
579 state = Succeeded;
580 return true;
581}
582
583)"_s;
585 if (!writeStr(validateLookupSignatures.toUtf8()))
591bool qSaveQmlJSUnitAsCpp(
const QString &inputFileName,
const QString &outputFileName,
592 const QV4::CompiledData::SaveableUnitPointer &unit,
593 const QQmlJSAotFunctionMap &aotFunctions,
594 const LookupSignatures &lookupSignatures,
bool noAotValidation,
595 QString *errorString)
597#if QT_CONFIG(temporaryfile)
598 QSaveFile f(outputFileName);
600 QFile f(outputFileName);
602 if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
603 *errorString = f.errorString();
607 auto writeStr = [&f, errorString](
const QByteArray &data) {
608 if (f.write(data) != data.size()) {
609 *errorString = f.errorString();
615 if (!writeStr(
"// "))
618 if (!writeStr(inputFileName.toUtf8()))
624 if (!writeStr(
"#include <QtQml/qqmlprivate.h>\n"))
627 if (!noAotValidation) {
628 if (!writeStr(
"#include <QtCore/qhash.h>\n"))
632 if (!aotFunctions.isEmpty()) {
633 QStringList includes;
635 for (
const auto &function : aotFunctions)
636 includes.append(function.includes);
638 std::sort(includes.begin(), includes.end());
639 const auto end = std::unique(includes.begin(), includes.end());
640 for (
auto it = includes.begin(); it != end; ++it) {
641 if (!writeStr(QStringLiteral(
"#include <%1>\n").arg(*it).toUtf8()))
646 if (!writeStr(QByteArrayLiteral(
"\nnamespace QmlCacheGeneratedCode {\nnamespace ")))
649 if (!writeStr(qQmlJSSymbolNamespaceForPath(inputFileName).toUtf8()))
652 if (!writeStr(
" {\n"))
655 if (!generateAotValidationCode(writeStr, lookupSignatures, noAotValidation))
658 if (!writeStr(QByteArrayLiteral(
"extern const unsigned char qmlData alignas(16) [];\n"
659 "extern const unsigned char qmlData alignas(16) [] = {\n")))
662 unit.saveToDisk<uchar>([&writeStr](
const uchar *begin, quint32 size) {
663 QByteArray hexifiedData;
665 QTextStream stream(&hexifiedData);
666 const uchar *end = begin + size;
669 for (
const uchar *data = begin; data < end; ++data, ++col) {
676 stream <<
"0x" << *data;
680 return writeStr(hexifiedData);
685 if (!writeStr(
"};\n"))
689 if (aotFunctions.size() <= 1) {
691 writeStr(
"extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
692 "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = { { 0, 0, nullptr, nullptr } };\n");
694 writeStr(
"extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
695 "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = {\n");
697 QString footer = QStringLiteral(
"}\n");
699 for (QQmlJSAotFunctionMap::ConstIterator func = aotFunctions.constBegin(),
700 end = aotFunctions.constEnd();
701 func != end; ++func) {
703 if (func.key() == FileScopeCodeIndex)
706 const QString function = QString::fromUtf8(funcHeaderCode) + func.value().code + footer;
708 writeStr(QStringLiteral(
"{ %1, %2, [](QV4::ExecutableCompilationUnit *contextUnit, "
709 "QMetaType *argTypes) {\n%3}, %4 },")
711 .arg(func->numArguments)
712 .arg(func->signature.isEmpty() ? u" Q_UNUSED(contextUnit);\n Q_UNUSED(argTypes);\n"_s : func->signature, function)
713 .toUtf8().constData());
717 writeStr(
"{ 0, 0, nullptr, nullptr }");
721 if (!writeStr(
"}\n}\n"))
724#if QT_CONFIG(temporaryfile)
726 *errorString = f.errorString();
734QQmlJSAotCompiler::QQmlJSAotCompiler(
735 QQmlJSImporter *importer,
const QString &resourcePath,
const QStringList &qmldirFiles,
736 QQmlJSLogger *logger)
737 : m_typeResolver(importer)
738 , m_resourcePath(resourcePath)
739 , m_qmldirFiles(qmldirFiles)
740 , m_importer(importer)
745void QQmlJSAotCompiler::setDocument(
746 const QmlIR::JSCodeGen *codegen,
const QmlIR::Document *irDocument)
749 m_document = irDocument;
750 const QFileInfo resourcePathInfo(m_resourcePath);
751 if (m_logger->filePath().isEmpty())
752 m_logger->setFilePath(resourcePathInfo.fileName());
753 m_logger->setCode(irDocument->code);
754 m_unitGenerator = &irDocument->jsGenerator;
755 QQmlJSImportVisitor visitor(m_importer, m_logger,
756 resourcePathInfo.canonicalPath() + u'/',
758 m_typeResolver.init(&visitor, irDocument->program);
761void QQmlJSAotCompiler::setScope(
const QmlIR::Object *object,
const QmlIR::Object *scope)
763 m_currentObject = object;
764 m_currentScope = scope;
767static bool isStrict(
const QmlIR::Document *doc)
769 for (
const QmlIR::Pragma *pragma : doc->pragmas) {
770 if (pragma->type == QmlIR::Pragma::Strict)
776QQmlJS::DiagnosticMessage QQmlJSAotCompiler::diagnose(
777 const QString &message, QtMsgType type,
const QQmlJS::SourceLocation &location)
const
779 if (isStrict(m_document)
780 && (type == QtWarningMsg || type == QtCriticalMsg || type == QtFatalMsg)
781 && m_logger->categorySeverity(qmlCompiler) == QQmlSA::WarningSeverity::Error) {
782 qFatal(
"%s:%d: (strict mode) %s",
783 qPrintable(QFileInfo(m_resourcePath).fileName()),
784 location.startLine, qPrintable(message));
787 return QQmlJS::DiagnosticMessage {
794std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>> QQmlJSAotCompiler::compileBinding(
795 const QV4::Compiler::Context *context,
const QmlIR::Binding &irBinding,
796 QQmlJS::AST::Node *astNode)
798 QQmlJSFunctionInitializer initializer(
799 &m_typeResolver, m_currentObject->location, m_currentScope->location, m_logger);
801 const QString name = m_document->stringAt(irBinding.propertyNameIndex);
802 QQmlJSCompilePass::Function function = initializer.run( context, name, astNode, irBinding);
804 const QQmlJSAotFunction aotFunction = doCompileAndRecordAotStats(
805 context, &function, name, astNode->firstSourceLocation());
807 if (
const auto errors = finalizeBindingOrFunction())
810 qCDebug(lcAotCompiler()) <<
"includes:" << aotFunction.includes;
811 qCDebug(lcAotCompiler()) <<
"binding code:" << aotFunction.code;
815std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>> QQmlJSAotCompiler::compileFunction(
816 const QV4::Compiler::Context *context,
const QString &name, QQmlJS::AST::Node *astNode)
818 QQmlJSFunctionInitializer initializer(
819 &m_typeResolver, m_currentObject->location, m_currentScope->location, m_logger);
820 QQmlJSCompilePass::Function function = initializer.run(context, name, astNode);
822 const QQmlJSAotFunction aotFunction = doCompileAndRecordAotStats(
823 context, &function, name, astNode->firstSourceLocation());
825 if (
const auto errors = finalizeBindingOrFunction())
828 qCDebug(lcAotCompiler()) <<
"includes:" << aotFunction.includes;
829 qCDebug(lcAotCompiler()) <<
"binding code:" << aotFunction.code;
833QQmlJSAotFunction QQmlJSAotCompiler::globalCode()
const
835 QQmlJSAotFunction global;
837 u"QtQml/qjsengine.h"_s,
838 u"QtQml/qjsprimitivevalue.h"_s,
839 u"QtQml/qjsvalue.h"_s,
840 u"QtQml/qqmlcomponent.h"_s,
841 u"QtQml/qqmlcontext.h"_s,
842 u"QtQml/qqmlengine.h"_s,
843 u"QtQml/qqmllist.h"_s,
845 u"QtCore/qdatetime.h"_s,
846 u"QtCore/qtimezone.h"_s,
847 u"QtCore/qobject.h"_s,
848 u"QtCore/qstring.h"_s,
849 u"QtCore/qstringlist.h"_s,
851 u"QtCore/qvariant.h"_s,
858std::optional<QList<QQmlJS::DiagnosticMessage>> QQmlJSAotCompiler::finalizeBindingOrFunction()
860 const auto archiveMessages = qScopeGuard([
this]() { m_logger->finalizeFunction(); });
862 if (!m_logger->currentFunctionHasCompileError())
865 QList<QQmlJS::DiagnosticMessage> errors;
866 m_logger->iterateCurrentFunctionMessages([&](
const Message &msg) {
867 if (msg.compilationStatus == Message::CompilationStatus::Error)
868 errors.append(diagnose(msg.message, msg.type, msg.loc));
873QQmlJSAotFunction QQmlJSAotCompiler::doCompile(
874 const QV4::Compiler::Context *context,
const QQmlJSCompilePass::Function *function)
876 if (m_logger->currentFunctionHasErrorOrSkip())
877 return QQmlJSAotFunction();
879 bool basicBlocksValidationFailed =
false;
880 QQmlJSBasicBlocks basicBlocks(context, m_unitGenerator, &m_typeResolver, m_logger);
881 auto passResult = basicBlocks.run(function, m_flags, basicBlocksValidationFailed);
882 auto &[blocks, annotations] = passResult;
884 QQmlJSTypePropagator propagator(
885 m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
886 passResult = propagator.run(function);
887 if (m_logger->currentFunctionHasErrorOrSkip())
888 return QQmlJSAotFunction();
890 QQmlJSShadowCheck shadowCheck(
891 m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
892 passResult = shadowCheck.run(function);
893 if (m_logger->currentFunctionHasErrorOrSkip())
894 return QQmlJSAotFunction();
896 QQmlJSOptimizations optimizer(
897 m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations,
898 basicBlocks.objectAndArrayDefinitions());
899 passResult = optimizer.run(function);
900 if (m_logger->currentFunctionHasErrorOrSkip())
901 return QQmlJSAotFunction();
903 QQmlJSStorageInitializer initializer(
904 m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
905 passResult = initializer.run(function);
908 QQmlJSStorageGeneralizer generalizer(
909 m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
910 passResult = generalizer.run(function);
911 if (m_logger->currentFunctionHasErrorOrSkip())
912 return QQmlJSAotFunction();
914 QQmlJSCodeGenerator codegen(context, m_unitGenerator, &m_typeResolver, m_logger, blocks,
915 annotations, noAotValidation());
916 QQmlJSAotFunction result = codegen.run(function, basicBlocksValidationFailed);
917 if (m_logger->currentFunctionHasErrorOrSkip())
918 return QQmlJSAotFunction();
920 m_lookupSignatures.insert(codegen.lookupSignatures());
924QQmlJSAotFunction QQmlJSAotCompiler::doCompileAndRecordAotStats(
925 const QV4::Compiler::Context *context,
const QQmlJSCompilePass::Function *function,
926 const QString &name, QQmlJS::SourceLocation location)
928 QElapsedTimer timer {};
930 QQmlJSAotFunction result;
931 if (!m_logger->currentFunctionHasCompileError())
932 result = doCompile(context, function);
933 auto elapsed = std::chrono::milliseconds { timer.elapsed() };
935 if (QQmlJS::QQmlJSAotCompilerStats::recordAotStats()) {
936 QQmlJS::AotStatsEntry entry;
937 entry.codegenDuration = elapsed;
938 entry.functionName = name;
939 entry.message = m_logger->currentFunctionWasSkipped()
940 ? m_logger->currentFunctionCompileSkipMessage()
941 : m_logger->currentFunctionCompileErrorMessage();
942 entry.line = location.startLine;
943 entry.column = location.startColumn;
944 if (m_logger->currentFunctionWasSkipped())
945 entry.codegenResult = QQmlJS::CodegenResult::Skip;
946 else if (m_logger->currentFunctionHasCompileError())
947 entry.codegenResult = QQmlJS::CodegenResult::Failure;
949 entry.codegenResult = QQmlJS::CodegenResult::Success;
950 QQmlJS::QQmlJSAotCompilerStats::addEntry(
951 function->qmlScope.containedType()->filePath(), entry);
954 if (m_logger->currentFunctionWasSkipped())
955 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)
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