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)) {
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 std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>> result;
236 if (
const auto *binding = bindingOrFunction.binding()) {
237 switch (binding->type()) {
238 case QmlIR::Binding::Type_AttachedProperty:
239 case QmlIR::Binding::Type_GroupProperty:
240 effectiveScopes.insert(
241 irDocument.objects.at(binding->value.objectIndex), scope);
243 case QmlIR::Binding::Type_Boolean:
244 case QmlIR::Binding::Type_Number:
245 case QmlIR::Binding::Type_String:
246 case QmlIR::Binding::Type_Null:
247 case QmlIR::Binding::Type_Object:
248 case QmlIR::Binding::Type_Translation:
249 case QmlIR::Binding::Type_TranslationById:
255 Q_ASSERT(quint32(functionsToCompile.size()) > binding->value.compiledScriptIndex);
256 const auto &functionToCompile
257 = functionsToCompile[binding->value.compiledScriptIndex];
258 auto *parentNode = functionToCompile.parentNode;
259 Q_ASSERT(parentNode);
260 Q_ASSERT(contextMap.contains(parentNode));
261 QV4::Compiler::Context *context = contextMap.take(parentNode);
264 auto *node = functionToCompile.node;
267 if (context->returnsClosure) {
268 QQmlJS::AST::Node *inner
269 = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(
272 QV4::Compiler::Context *innerContext = contextMap.take(inner);
273 Q_ASSERT(innerContext);
274 qCDebug(lcAotCompiler) <<
"Compiling signal handler for"
275 << irDocument.stringAt(binding->propertyNameIndex);
276 std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>> innerResult
277 = aotCompiler->compileBinding(innerContext, *binding, inner);
278 if (
auto *errors = std::get_if<QList<QQmlJS::DiagnosticMessage>>(&innerResult)) {
279 for (
const auto &error : std::as_const(*errors)) {
280 qCDebug(lcAotCompiler) <<
"Compilation failed:"
281 << diagnosticErrorMessage(inputFileName, error);
283 }
else if (
auto *func = std::get_if<QQmlJSAotFunction>(&innerResult)) {
284 qCDebug(lcAotCompiler) <<
"Generated code:" << func->code;
285 aotFunctionsByIndex[innerContext->functionIndex] = *func;
289 qCDebug(lcAotCompiler) <<
"Compiling binding for property"
290 << irDocument.stringAt(binding->propertyNameIndex);
291 result = aotCompiler->compileBinding(context, *binding, node);
292 }
else if (
const auto *function = bindingOrFunction.function()) {
293 if (!aotCompiler->isLintCompiler() && !function->isQmlFunction)
296 Q_ASSERT(quint32(functionsToCompile.size()) > function->index);
297 auto *node = functionsToCompile[function->index].node;
299 Q_ASSERT(contextMap.contains(node));
300 QV4::Compiler::Context *context = contextMap.take(node);
303 const QString functionName = irDocument.stringAt(function->nameIndex);
304 qCDebug(lcAotCompiler) <<
"Compiling function" << functionName;
305 result = aotCompiler->compileFunction(context, functionName, node);
310 if (
auto *errors = std::get_if<QList<QQmlJS::DiagnosticMessage>>(&result)) {
311 for (
const auto &error : std::as_const(*errors)) {
312 qCDebug(lcAotCompiler) <<
"Compilation failed:"
313 << diagnosticErrorMessage(inputFileName, error);
315 }
else if (
auto *func = std::get_if<QQmlJSAotFunction>(&result)) {
316 if (func->skipReason.has_value()) {
317 qCDebug(lcAotCompiler) <<
"Compilation skipped:" << func->skipReason.value();
319 qCDebug(lcAotCompiler) <<
"Generated code:" << func->code;
320 auto index = object->runtimeFunctionIndices[bindingOrFunction.index()];
321 aotFunctionsByIndex[index] = *func;
327 if (!checkArgumentsObjectUseInSignalHandlers(irDocument, error)) {
328 *error = error->augment(inputFileName);
332 QmlIR::QmlUnitGenerator generator;
333 irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(
false);
334 generator.generate(irDocument);
336 const quint32 saveFlags
337 = QV4::CompiledData::Unit::StaticData
338 | QV4::CompiledData::Unit::PendingTypeCompilation;
339 QV4::CompiledData::SaveableUnitPointer saveable(
340 irDocument.javaScriptCompilationUnit->unitData(), saveFlags);
341 LookupSignatures sigs = aotCompiler ? aotCompiler->lookupSignatures() : LookupSignatures();
342 if (!saveFunction(saveable, aotFunctionsByIndex, sigs, &error->message))
349 const QString &inputFileName,
const QString &inputFileUrl,
352 Q_UNUSED(inputFileUrl);
354 QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit;
358 QFile f(inputFileName);
359 if (!f.open(QIODevice::ReadOnly)) {
360 error->message = QLatin1String(
"Error opening ") + inputFileName + QLatin1Char(
':') + f.errorString();
363 sourceCode = QString::fromUtf8(f.readAll());
364 if (f.error() != QFileDevice::NoError) {
365 error->message = QLatin1String(
"Error reading from ") + inputFileName + QLatin1Char(
':') + f.errorString();
370 const bool isModule = inputFileName.endsWith(QLatin1String(
".mjs"));
372 QList<QQmlJS::DiagnosticMessage> diagnostics;
375 unit = QV4::Compiler::Codegen::compileModule(
false, url, sourceCode,
376 QDateTime(), &diagnostics);
377 error->appendDiagnostics(inputFileName, diagnostics);
378 if (!unit || !unit->unitData())
381 QmlIR::Document irDocument(QString(), QString(),
false);
383 QQmlJS::Engine *engine = &irDocument.jsParserEngine;
384 QmlIR::ScriptDirectivesCollector directivesCollector(&irDocument);
385 QQmlJS::Directives *oldDirs = engine->directives();
386 engine->setDirectives(&directivesCollector);
387 auto directivesGuard = qScopeGuard([engine, oldDirs]{
388 engine->setDirectives(oldDirs);
391 QQmlJS::AST::Program *program =
nullptr;
394 QQmlJS::Lexer lexer(engine);
395 lexer.setCode(sourceCode, 1,
false);
396 QQmlJS::Parser parser(engine);
398 bool parsed = parser.parseProgram();
400 error->appendDiagnostics(inputFileName, parser.diagnosticMessages());
405 program = QQmlJS::AST::cast<QQmlJS::AST::Program*>(parser.rootNode());
407 lexer.setCode(QStringLiteral(
"undefined;"), 1,
false);
408 parsed = parser.parseProgram();
410 program = QQmlJS::AST::cast<QQmlJS::AST::Program*>(parser.rootNode());
416 QmlIR::JSCodeGen v4CodeGen(&irDocument);
417 v4CodeGen.generateFromProgram(
418 sourceCode, program, &irDocument.jsModule,
419 QV4::Compiler::ContextType::ScriptImportedByQML);
420 if (v4CodeGen.hasError()) {
421 error->appendDiagnostic(inputFileName, v4CodeGen.error());
426 Q_ASSERT(irDocument.jsModule.fileName.isEmpty());
427 Q_ASSERT(irDocument.jsModule.finalUrl.isEmpty());
429 irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(
false);
430 QmlIR::QmlUnitGenerator generator;
431 generator.generate(irDocument);
432 unit =
std::move(irDocument.javaScriptCompilationUnit);
436 QQmlJSAotFunctionMap funcs;
437 LookupSignatures sigs;
439 QV4::CompiledData::SaveableUnitPointer(unit->unitData()), funcs, sigs, &error->message);
443 [](const QQmlPrivate::AOTCompiledContext *aotContext, void **argv) {
444Q_UNUSED(aotContext)
445Q_UNUSED(argv)
446)";
450 return "u\"%1\"_s"_L1.arg(s);
455 bool isComposite = type.isComposite == QQmlPrivate::AOTLookupValidation::IsComposite::Yes;
456 bool isIC = type.isInlineComponent == QQmlPrivate::AOTLookupValidation::IsIC::Yes;
457 return u"Type{ %1, %2, %3, %4, %5 }"_s
458 .arg(wrapString(type.module), wrapString(type.name),
459 wrapString(type.icNameOrExtensionTypeName),
460 isComposite ?
"IsComposite::Yes"_L1 :
"IsComposite::No"_L1,
461 isIC ?
"IsIC::Yes"_L1 :
"IsIC::No"_L1);
466 return "Lookup{ %1, %2, %3 }"_L1.arg(typeToString(lookup.base), wrapString(lookup.member),
467 wrapString(lookup.enumName));
472 if (
const auto *p = std::get_if<QQmlPrivate::AOTLookupValidation::PropertySignature>(&signature)) {
473 return u"PropertySignature{ %1, %2 }"_s
474 .arg(typeToString(p->type), QString::number(p->relativeIndex));
475 }
else if (
const auto *e = std::get_if<QQmlPrivate::AOTLookupValidation::EnumKeySignature>(&signature)) {
476 bool isFlag = e->isFlag == QQmlPrivate::AOTLookupValidation::IsFlag::Yes;
477 return u"EnumKeySignature{ %1, %2 }"_s
478 .arg(QString::number(e->value), isFlag ?
"IsFlag::Yes"_L1 :
"IsFlag::No"_L1);
479 }
else if (
const auto *m = std::get_if<QQmlPrivate::AOTLookupValidation::MethodSignature>(&signature)) {
480 QString paramNames =
"{ "_L1;
481 for (
const auto ¶mName : m->paramNames)
482 paramNames += wrapString(paramName) +
", "_L1;
485 QString types =
"{ "_L1;
486 for (
const auto ¶mType : m->types)
487 types += typeToString(paramType) +
", "_L1;
490 bool isSignal = m->isSignal == QQmlPrivate::AOTLookupValidation::IsSignal::Yes;
491 return u"MethodSignature{ %1, %2, %3, %4 }"_s
492 .arg(paramNames, types, QString::number(m->relativeIndex),
493 isSignal ?
"IsSignal::Yes"_L1 :
"IsSignal::No"_L1);
495 Q_UNREACHABLE_RETURN(QString());
499bool validateLookupSignatures(QQmlEngine *engine, QV4::CompiledData::CompilationUnit *cu)
500{
501 // AOT validation code not generated (NO_GENERATE_AOT_VALIDATION)
502 Q_UNUSED(engine);
503 Q_UNUSED(cu);
504 return true;
505}
506
507)";
509template <
typename WriteStr>
511 const WriteStr &writeStr,
const LookupSignatures &lookupSignatures,
bool noAotValidation)
513 if (noAotValidation) {
519 using namespace QQmlPrivate::AOTLookupValidation;
520 if (!writeStr(
"QQmlPrivate::AOTLookupValidation::LookupSignatures expectedLookupSignatures()\n"
522 " using namespace Qt::StringLiterals;\n"
523 " using namespace QQmlPrivate::AOTLookupValidation;\n"
528 const auto &signatures = lookupSignatures.asKeyValueRange();
529 QList<std::pair<Lookup, Signature>> sorted{ signatures.begin(), signatures.end() };
530 std::stable_sort(sorted.begin(), sorted.end(), [&](
const auto &lhs,
const auto &rhs) {
531 const auto &lTypeString = typeToString(lhs.first.base);
532 const auto &rTypeString = typeToString(rhs.first.base);
533 if (lTypeString == rTypeString)
534 return lhs.first.member < rhs.first.member;
535 return lTypeString < rTypeString;
538 for (
const auto &[memberlookup, signature] : sorted) {
539 if (!writeStr(
" { %1, %2 },\n"_L1
540 .arg(lookupToString(memberlookup), signatureToString(signature))
546 if (!writeStr(
" };\n}\n"))
549 const QString validateLookupSignatures = uR"(
550bool validateLookupSignatures(QQmlEngine *engine, QV4::CompiledData::CompilationUnit *cu)
551{
552 enum ValidationState { Pending, Failed, Succeeded };
553 static ValidationState state = Pending;
554 if (state == Failed)
555 return false;
556 if (state == Succeeded)
558 const auto &expectedSignatures = expectedLookupSignatures();
559 for (const auto &[lookup, expectedSignature] : expectedSignatures.asKeyValueRange()) {
560 if (!QQmlPrivate::AOTLookupValidation::validateLookupSignature(engine, cu, lookup, expectedSignature)) {
561 state = Failed;
562 return false;
563 }
564 }
565 state = Succeeded;
566 return true;
567}
568
569)"_s;
571 if (!writeStr(validateLookupSignatures.toUtf8()))
577bool qSaveQmlJSUnitAsCpp(
const QString &inputFileName,
const QString &outputFileName,
578 const QV4::CompiledData::SaveableUnitPointer &unit,
579 const QQmlJSAotFunctionMap &aotFunctions,
580 const LookupSignatures &lookupSignatures,
bool noAotValidation,
581 QString *errorString)
583#if QT_CONFIG(temporaryfile)
584 QSaveFile f(outputFileName);
586 QFile f(outputFileName);
588 if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
589 *errorString = f.errorString();
593 auto writeStr = [&f, errorString](
const QByteArray &data) {
594 if (f.write(data) != data.size()) {
595 *errorString = f.errorString();
601 if (!writeStr(
"// "))
604 if (!writeStr(inputFileName.toUtf8()))
610 if (!writeStr(
"#include <QtQml/qqmlprivate.h>\n"))
613 if (!noAotValidation) {
614 if (!writeStr(
"#include <QtCore/qhash.h>\n"))
618 if (!aotFunctions.isEmpty()) {
619 QStringList includes;
621 for (
const auto &function : aotFunctions)
622 includes.append(function.includes);
624 std::sort(includes.begin(), includes.end());
625 const auto end = std::unique(includes.begin(), includes.end());
626 for (
auto it = includes.begin(); it != end; ++it) {
627 if (!writeStr(QStringLiteral(
"#include <%1>\n").arg(*it).toUtf8()))
632 if (!writeStr(QByteArrayLiteral(
"\nnamespace QmlCacheGeneratedCode {\nnamespace ")))
635 if (!writeStr(qQmlJSSymbolNamespaceForPath(inputFileName).toUtf8()))
638 if (!writeStr(
" {\n"))
641 if (!generateAotValidationCode(writeStr, lookupSignatures, noAotValidation))
644 if (!writeStr(QByteArrayLiteral(
"extern const unsigned char qmlData alignas(16) [];\n"
645 "extern const unsigned char qmlData alignas(16) [] = {\n")))
648 unit.saveToDisk<uchar>([&writeStr](
const uchar *begin, quint32 size) {
649 QByteArray hexifiedData;
651 QTextStream stream(&hexifiedData);
652 const uchar *end = begin + size;
655 for (
const uchar *data = begin; data < end; ++data, ++col) {
662 stream <<
"0x" << *data;
666 return writeStr(hexifiedData);
671 if (!writeStr(
"};\n"))
675 if (aotFunctions.size() <= 1) {
677 writeStr(
"extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
678 "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = { { 0, 0, nullptr, nullptr } };\n");
680 writeStr(
"extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
681 "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = {\n");
683 QString footer = QStringLiteral(
"}\n");
685 for (QQmlJSAotFunctionMap::ConstIterator func = aotFunctions.constBegin(),
686 end = aotFunctions.constEnd();
687 func != end; ++func) {
689 if (func.key() == FileScopeCodeIndex)
692 const QString function = QString::fromUtf8(funcHeaderCode) + func.value().code + footer;
694 writeStr(QStringLiteral(
"{ %1, %2, [](QV4::ExecutableCompilationUnit *contextUnit, "
695 "QMetaType *argTypes) {\n%3}, %4 },")
697 .arg(func->numArguments)
698 .arg(func->signature.isEmpty() ? u" Q_UNUSED(contextUnit);\n Q_UNUSED(argTypes);\n"_s : func->signature, function)
699 .toUtf8().constData());
703 writeStr(
"{ 0, 0, nullptr, nullptr }");
707 if (!writeStr(
"}\n}\n"))
710#if QT_CONFIG(temporaryfile)
712 *errorString = f.errorString();
720QQmlJSAotCompiler::QQmlJSAotCompiler(
721 QQmlJSImporter *importer,
const QString &resourcePath,
const QStringList &qmldirFiles,
722 QQmlJSLogger *logger)
723 : m_typeResolver(importer)
724 , m_resourcePath(resourcePath)
725 , m_qmldirFiles(qmldirFiles)
726 , m_importer(importer)
731void QQmlJSAotCompiler::setDocument(
732 const QmlIR::JSCodeGen *codegen,
const QmlIR::Document *irDocument)
735 m_document = irDocument;
736 const QFileInfo resourcePathInfo(m_resourcePath);
737 if (m_logger->filePath().isEmpty())
738 m_logger->setFilePath(resourcePathInfo.fileName());
739 m_logger->setCode(irDocument->code);
740 m_unitGenerator = &irDocument->jsGenerator;
741 QQmlJSImportVisitor visitor(m_importer, m_logger,
742 resourcePathInfo.canonicalPath() + u'/',
744 m_typeResolver.init(&visitor, irDocument->program);
747void QQmlJSAotCompiler::setScope(
const QmlIR::Object *object,
const QmlIR::Object *scope)
749 m_currentObject = object;
750 m_currentScope = scope;
753static bool isStrict(
const QmlIR::Document *doc)
755 for (
const QmlIR::Pragma *pragma : doc->pragmas) {
756 if (pragma->type == QmlIR::Pragma::Strict)
762QQmlJS::DiagnosticMessage QQmlJSAotCompiler::diagnose(
763 const QString &message, QtMsgType type,
const QQmlJS::SourceLocation &location)
const
765 if (isStrict(m_document)
766 && (type == QtWarningMsg || type == QtCriticalMsg || type == QtFatalMsg)
767 && m_logger->categorySeverity(qmlCompiler) == QQmlSA::WarningSeverity::Error) {
768 qFatal(
"%s:%d: (strict mode) %s",
769 qPrintable(QFileInfo(m_resourcePath).fileName()),
770 location.startLine, qPrintable(message));
773 return QQmlJS::DiagnosticMessage {
780std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>> QQmlJSAotCompiler::compileBinding(
781 const QV4::Compiler::Context *context,
const QmlIR::Binding &irBinding,
782 QQmlJS::AST::Node *astNode)
784 QQmlJSFunctionInitializer initializer(
785 &m_typeResolver, m_currentObject->location, m_currentScope->location, m_logger);
787 const QString name = m_document->stringAt(irBinding.propertyNameIndex);
788 QQmlJSCompilePass::Function function = initializer.run( context, name, astNode, irBinding);
790 const QQmlJSAotFunction aotFunction = doCompileAndRecordAotStats(
791 context, &function, name, astNode->firstSourceLocation());
793 if (
const auto errors = finalizeBindingOrFunction())
796 qCDebug(lcAotCompiler()) <<
"includes:" << aotFunction.includes;
797 qCDebug(lcAotCompiler()) <<
"binding code:" << aotFunction.code;
801std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>> QQmlJSAotCompiler::compileFunction(
802 const QV4::Compiler::Context *context,
const QString &name, QQmlJS::AST::Node *astNode)
804 QQmlJSFunctionInitializer initializer(
805 &m_typeResolver, m_currentObject->location, m_currentScope->location, m_logger);
806 QQmlJSCompilePass::Function function = initializer.run(context, name, astNode);
808 const QQmlJSAotFunction aotFunction = doCompileAndRecordAotStats(
809 context, &function, name, astNode->firstSourceLocation());
811 if (
const auto errors = finalizeBindingOrFunction())
814 qCDebug(lcAotCompiler()) <<
"includes:" << aotFunction.includes;
815 qCDebug(lcAotCompiler()) <<
"binding code:" << aotFunction.code;
819QQmlJSAotFunction QQmlJSAotCompiler::globalCode()
const
821 QQmlJSAotFunction global;
823 u"QtQml/qjsengine.h"_s,
824 u"QtQml/qjsprimitivevalue.h"_s,
825 u"QtQml/qjsvalue.h"_s,
826 u"QtQml/qqmlcomponent.h"_s,
827 u"QtQml/qqmlcontext.h"_s,
828 u"QtQml/qqmlengine.h"_s,
829 u"QtQml/qqmllist.h"_s,
831 u"QtCore/qdatetime.h"_s,
832 u"QtCore/qtimezone.h"_s,
833 u"QtCore/qobject.h"_s,
834 u"QtCore/qstring.h"_s,
835 u"QtCore/qstringlist.h"_s,
837 u"QtCore/qvariant.h"_s,
844std::optional<QList<QQmlJS::DiagnosticMessage>> QQmlJSAotCompiler::finalizeBindingOrFunction()
846 const auto archiveMessages = qScopeGuard([
this]() { m_logger->finalizeFunction(); });
848 if (!m_logger->currentFunctionHasCompileError())
851 QList<QQmlJS::DiagnosticMessage> errors;
852 m_logger->iterateCurrentFunctionMessages([&](
const Message &msg) {
853 if (msg.compilationStatus == Message::CompilationStatus::Error)
854 errors.append(diagnose(msg.message, msg.type, msg.loc));
859QQmlJSAotFunction QQmlJSAotCompiler::doCompile(
860 const QV4::Compiler::Context *context,
const QQmlJSCompilePass::Function *function)
862 if (m_logger->currentFunctionHasErrorOrSkip())
863 return QQmlJSAotFunction();
865 bool basicBlocksValidationFailed =
false;
866 QQmlJSBasicBlocks basicBlocks(context, m_unitGenerator, &m_typeResolver, m_logger);
867 auto passResult = basicBlocks.run(function, m_flags, basicBlocksValidationFailed);
868 auto &[blocks, annotations] = passResult;
870 QQmlJSTypePropagator propagator(
871 m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
872 passResult = propagator.run(function);
873 if (m_logger->currentFunctionHasErrorOrSkip())
874 return QQmlJSAotFunction();
876 QQmlJSShadowCheck shadowCheck(
877 m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
878 passResult = shadowCheck.run(function);
879 if (m_logger->currentFunctionHasErrorOrSkip())
880 return QQmlJSAotFunction();
882 QQmlJSOptimizations optimizer(
883 m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations,
884 basicBlocks.objectAndArrayDefinitions());
885 passResult = optimizer.run(function);
886 if (m_logger->currentFunctionHasErrorOrSkip())
887 return QQmlJSAotFunction();
889 QQmlJSStorageInitializer initializer(
890 m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
891 passResult = initializer.run(function);
894 QQmlJSStorageGeneralizer generalizer(
895 m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
896 passResult = generalizer.run(function);
897 if (m_logger->currentFunctionHasErrorOrSkip())
898 return QQmlJSAotFunction();
900 QQmlJSCodeGenerator codegen(context, m_unitGenerator, &m_typeResolver, m_logger, blocks,
901 annotations, noAotValidation());
902 QQmlJSAotFunction result = codegen.run(function, basicBlocksValidationFailed);
903 if (m_logger->currentFunctionHasErrorOrSkip())
904 return QQmlJSAotFunction();
906 m_lookupSignatures.insert(codegen.lookupSignatures());
910QQmlJSAotFunction QQmlJSAotCompiler::doCompileAndRecordAotStats(
911 const QV4::Compiler::Context *context,
const QQmlJSCompilePass::Function *function,
912 const QString &name, QQmlJS::SourceLocation location)
914 QElapsedTimer timer {};
916 QQmlJSAotFunction result;
917 if (!m_logger->currentFunctionHasCompileError())
918 result = doCompile(context, function);
919 auto elapsed = std::chrono::milliseconds { timer.elapsed() };
921 if (QQmlJS::QQmlJSAotCompilerStats::recordAotStats()) {
922 QQmlJS::AotStatsEntry entry;
923 entry.codegenDuration = elapsed;
924 entry.functionName = name;
925 entry.message = m_logger->currentFunctionWasSkipped()
926 ? m_logger->currentFunctionCompileSkipMessage()
927 : m_logger->currentFunctionCompileErrorMessage();
928 entry.line = location.startLine;
929 entry.column = location.startColumn;
930 if (m_logger->currentFunctionWasSkipped())
931 entry.codegenResult = QQmlJS::CodegenResult::Skip;
932 else if (m_logger->currentFunctionHasCompileError())
933 entry.codegenResult = QQmlJS::CodegenResult::Failure;
935 entry.codegenResult = QQmlJS::CodegenResult::Success;
936 QQmlJS::QQmlJSAotCompilerStats::addEntry(
937 function->qmlScope.containedType()->filePath(), entry);
940 if (m_logger->currentFunctionWasSkipped())
941 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