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>(QmlIR::IRBuilder::ContinueAfterFailure)
178 : std::make_unique<QmlIR::IRBuilder>();
179 if (!irBuilder->generateFromQml(sourceCode, inputFileName, &irDocument, wInterface)) {
180 error->appendDiagnostics(inputFileName, irBuilder->errors);
185 QQmlJSAotFunctionMap aotFunctionsByIndex;
188 QmlIR::JSCodeGen v4CodeGen(&irDocument, wInterface, storeSourceLocation);
191 aotCompiler->setDocument(&v4CodeGen, &irDocument);
193 QHash<QmlIR::Object *, QmlIR::Object *> effectiveScopes;
194 for (QmlIR::Object *object: std::as_const(irDocument.objects)) {
195 if (object->functionsAndExpressions->count == 0 && object->bindingCount() == 0)
198 if (!v4CodeGen.generateRuntimeFunctions(object)) {
199 Q_ASSERT(v4CodeGen.hasError());
200 error->appendDiagnostic(inputFileName, v4CodeGen.error());
207 QmlIR::Object *scope = object;
208 for (
auto it = effectiveScopes.constFind(scope), end = effectiveScopes.constEnd();
209 it != end; it = effectiveScopes.constFind(scope)) {
213 aotCompiler->setScope(object, scope);
214 aotFunctionsByIndex[FileScopeCodeIndex] = aotCompiler->globalCode();
216 std::vector<BindingOrFunction> bindingsAndFunctions;
217 bindingsAndFunctions.reserve(object->bindingCount() + object->functionCount());
219 std::copy(object->bindingsBegin(), object->bindingsEnd(),
220 std::back_inserter(bindingsAndFunctions));
221 std::copy(object->functionsBegin(), object->functionsEnd(),
222 std::back_inserter(bindingsAndFunctions));
224 QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile;
225 for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first;
226 foe; foe = foe->next) {
227 functionsToCompile << *foe;
232 auto contextMap = v4CodeGen.module()->contextMap;
233 std::sort(bindingsAndFunctions.begin(), bindingsAndFunctions.end());
234 std::for_each(bindingsAndFunctions.begin(), bindingsAndFunctions.end(),
235 [&](
const BindingOrFunction &bindingOrFunction) {
236 using Result = std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>>;
237 const auto processResult = [&](
const Result &result) {
238 if (
auto *errors = std::get_if<QList<QQmlJS::DiagnosticMessage>>(&result)) {
239 for (
const auto &error : *errors) {
240 qCDebug(lcAotCompiler) <<
"Compilation failed:"
241 << diagnosticErrorMessage(inputFileName, error);
243 }
else if (
auto *func = std::get_if<QQmlJSAotFunction>(&result)) {
244 if (func->skipReason.has_value()) {
245 qCDebug(lcAotCompiler) <<
"Compilation skipped:"
246 << func->skipReason.value();
248 qCDebug(lcAotCompiler) <<
"Generated code:" << func->code;
249 auto index = object->runtimeFunctionIndices[bindingOrFunction.index()];
250 aotFunctionsByIndex[index] = *func;
255 if (
const auto *binding = bindingOrFunction.binding()) {
256 switch (binding->type()) {
257 case QmlIR::Binding::Type_AttachedProperty:
258 case QmlIR::Binding::Type_GroupedProperty:
259 effectiveScopes.insert(
260 irDocument.objects.at(binding->value.objectIndex), scope);
262 case QmlIR::Binding::Type_Boolean:
263 case QmlIR::Binding::Type_Number:
264 case QmlIR::Binding::Type_String:
265 case QmlIR::Binding::Type_Null:
266 case QmlIR::Binding::Type_Object:
267 case QmlIR::Binding::Type_Translation:
268 case QmlIR::Binding::Type_TranslationById:
274 Q_ASSERT(quint32(functionsToCompile.size()) > binding->value.compiledScriptIndex);
275 const auto &functionToCompile
276 = functionsToCompile[binding->value.compiledScriptIndex];
277 auto *parentNode = functionToCompile.parentNode;
278 Q_ASSERT(parentNode);
279 Q_ASSERT(contextMap.contains(parentNode));
280 QV4::Compiler::Context *context = contextMap.take(parentNode);
283 auto *node = functionToCompile.node;
286 if (context->returnsClosure) {
287 QQmlJS::AST::Node *inner
288 = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(
291 QV4::Compiler::Context *innerContext = contextMap.take(inner);
292 Q_ASSERT(innerContext);
293 qCDebug(lcAotCompiler) <<
"Compiling signal handler for"
294 << irDocument.stringAt(binding->propertyNameIndex);
295 std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>> innerResult
296 = aotCompiler->compileBinding(innerContext, *binding, inner);
297 if (
auto *errors = std::get_if<QList<QQmlJS::DiagnosticMessage>>(&innerResult)) {
298 for (
const auto &error : std::as_const(*errors)) {
299 qCDebug(lcAotCompiler) <<
"Compilation failed:"
300 << diagnosticErrorMessage(inputFileName, error);
302 }
else if (
auto *func = std::get_if<QQmlJSAotFunction>(&innerResult)) {
303 qCDebug(lcAotCompiler) <<
"Generated code:" << func->code;
304 aotFunctionsByIndex[innerContext->functionIndex] = *func;
308 qCDebug(lcAotCompiler) <<
"Compiling binding for property"
309 << irDocument.stringAt(binding->propertyNameIndex);
310 processResult(aotCompiler->compileBinding(context, *binding, node));
311 }
else if (
const auto *function = bindingOrFunction.function()) {
312 if (!aotCompiler->isLintCompiler() && !function->isQmlFunction)
315 Q_ASSERT(quint32(functionsToCompile.size()) > function->index);
316 auto *node = functionsToCompile[function->index].node;
318 Q_ASSERT(contextMap.contains(node));
319 QV4::Compiler::Context *context = contextMap.take(node);
322 const QString functionName = irDocument.stringAt(function->nameIndex);
323 qCDebug(lcAotCompiler) <<
"Compiling function" << functionName;
324 processResult(aotCompiler->compileFunction(context, functionName, node));
331 if (!checkArgumentsObjectUseInSignalHandlers(irDocument, error)) {
332 *error = error->augment(inputFileName);
336 QmlIR::QmlUnitGenerator generator;
337 irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(
false);
338 generator.generate(irDocument);
340 const quint32 saveFlags
341 = QV4::CompiledData::Unit::StaticData
342 | QV4::CompiledData::Unit::PendingTypeCompilation;
343 QV4::CompiledData::SaveableUnitPointer saveable(
344 irDocument.javaScriptCompilationUnit->unitData(), saveFlags);
345 LookupSignatures sigs = aotCompiler ? aotCompiler->lookupSignatures() : LookupSignatures();
346 if (!saveFunction(saveable, aotFunctionsByIndex, sigs, &error->message))
353 const QString &inputFileName,
const QString &inputFileUrl,
356 Q_UNUSED(inputFileUrl);
358 QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit;
362 QFile f(inputFileName);
363 if (!f.open(QIODevice::ReadOnly)) {
364 error->message = QLatin1String(
"Error opening ") + inputFileName + QLatin1Char(
':') + f.errorString();
367 sourceCode = QString::fromUtf8(f.readAll());
368 if (f.error() != QFileDevice::NoError) {
369 error->message = QLatin1String(
"Error reading from ") + inputFileName + QLatin1Char(
':') + f.errorString();
374 const bool isModule = inputFileName.endsWith(QLatin1String(
".mjs"));
376 QList<QQmlJS::DiagnosticMessage> diagnostics;
379 unit = QV4::Compiler::Codegen::compileModule(
false, url, sourceCode,
380 QDateTime(), &diagnostics);
381 error->appendDiagnostics(inputFileName, diagnostics);
382 if (!unit || !unit->unitData())
385 QmlIR::Document irDocument(QString(), QString(),
false);
387 QQmlJS::Engine *engine = &irDocument.jsParserEngine;
388 QmlIR::ScriptDirectivesCollector directivesCollector(&irDocument);
389 QQmlJS::Directives *oldDirs = engine->directives();
390 engine->setDirectives(&directivesCollector);
391 auto directivesGuard = qScopeGuard([engine, oldDirs]{
392 engine->setDirectives(oldDirs);
395 QQmlJS::AST::Program *program =
nullptr;
398 QQmlJS::Lexer lexer(engine);
399 lexer.setCode(sourceCode, 1,
false);
400 QQmlJS::Parser parser(engine);
402 bool parsed = parser.parseProgram();
404 error->appendDiagnostics(inputFileName, parser.diagnosticMessages());
409 program = QQmlJS::AST::cast<QQmlJS::AST::Program*>(parser.rootNode());
411 lexer.setCode(QStringLiteral(
"undefined;"), 1,
false);
412 parsed = parser.parseProgram();
414 program = QQmlJS::AST::cast<QQmlJS::AST::Program*>(parser.rootNode());
420 QmlIR::JSCodeGen v4CodeGen(&irDocument);
421 v4CodeGen.generateFromProgram(
422 sourceCode, program, &irDocument.jsModule,
423 QV4::Compiler::ContextType::ScriptImportedByQML);
424 if (v4CodeGen.hasError()) {
425 error->appendDiagnostic(inputFileName, v4CodeGen.error());
430 Q_ASSERT(irDocument.jsModule.fileName.isEmpty());
431 Q_ASSERT(irDocument.jsModule.finalUrl.isEmpty());
433 irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(
false);
434 QmlIR::QmlUnitGenerator generator;
435 generator.generate(irDocument);
436 unit =
std::move(irDocument.javaScriptCompilationUnit);
440 QQmlJSAotFunctionMap funcs;
441 LookupSignatures sigs;
443 QV4::CompiledData::SaveableUnitPointer(unit->unitData()), funcs, sigs, &error->message);
447 [](const QQmlPrivate::AOTCompiledContext *aotContext, void **argv) {
448Q_UNUSED(aotContext)
449Q_UNUSED(argv)
450)";
454 return "u\"%1\"_s"_L1.arg(s);
459 bool isComposite = type.isComposite == QQmlPrivate::AOTLookupValidation::IsComposite::Yes;
460 bool isIC = type.isInlineComponent == QQmlPrivate::AOTLookupValidation::IsIC::Yes;
461 QString version =
"QTypeRevision()"_L1;
462 if (type.version.hasMinorVersion()) {
463 version =
"QTypeRevision::fromVersion(%1, %2)"_L1.arg(
464 QString::number(type.version.majorVersion()),
465 QString::number(type.version.minorVersion()));
466 }
else if (type.version.hasMajorVersion()) {
467 version =
"QTypeRevision::fromMajorVersion(%1)"_L1.arg(
468 QString::number(type.version.majorVersion()));
471 return u"Type{ %1, %2, %3, %4, %5, %6 }"_s.arg(wrapString(type.module), wrapString(type.name),
472 wrapString(type.icNameOrExtensionTypeName),
474 isComposite ?
"IsComposite::Yes"_L1
475 :
"IsComposite::No"_L1,
476 isIC ?
"IsIC::Yes"_L1 :
"IsIC::No"_L1);
481 return "Lookup{ %1, %2, %3 }"_L1.arg(typeToString(lookup.base), wrapString(lookup.member),
482 wrapString(lookup.enumName));
487 if (
const auto *p = std::get_if<QQmlPrivate::AOTLookupValidation::PropertySignature>(&signature)) {
488 return u"PropertySignature{ %1, %2 }"_s
489 .arg(typeToString(p->type), QString::number(p->relativeIndex));
490 }
else if (
const auto *e = std::get_if<QQmlPrivate::AOTLookupValidation::EnumKeySignature>(&signature)) {
491 bool isFlag = e->isFlag == QQmlPrivate::AOTLookupValidation::IsFlag::Yes;
492 return u"EnumKeySignature{ %1, %2 }"_s
493 .arg(QString::number(e->value), isFlag ?
"IsFlag::Yes"_L1 :
"IsFlag::No"_L1);
494 }
else if (
const auto *m = std::get_if<QQmlPrivate::AOTLookupValidation::MethodSignature>(&signature)) {
495 QString paramNames =
"{ "_L1;
496 for (
const auto ¶mName : m->paramNames)
497 paramNames += wrapString(paramName) +
", "_L1;
500 QString types =
"{ "_L1;
501 for (
const auto ¶mType : m->types)
502 types += typeToString(paramType) +
", "_L1;
505 bool isSignal = m->isSignal == QQmlPrivate::AOTLookupValidation::IsSignal::Yes;
506 return u"MethodSignature{ %1, %2, %3, %4 }"_s
507 .arg(paramNames, types, QString::number(m->relativeIndex),
508 isSignal ?
"IsSignal::Yes"_L1 :
"IsSignal::No"_L1);
510 Q_UNREACHABLE_RETURN(QString());
514bool validateLookupSignatures(QQmlEngine *engine, QV4::CompiledData::CompilationUnit *cu)
515{
516 // AOT validation code not generated (NO_GENERATE_AOT_VALIDATION)
517 Q_UNUSED(engine);
518 Q_UNUSED(cu);
519 return true;
520}
521
522)";
524template <
typename WriteStr>
526 const WriteStr &writeStr,
const LookupSignatures &lookupSignatures,
bool noAotValidation)
528 if (noAotValidation) {
534 using namespace QQmlPrivate::AOTLookupValidation;
535 if (!writeStr(
"QQmlPrivate::AOTLookupValidation::LookupSignatures expectedLookupSignatures()\n"
537 " using namespace Qt::StringLiterals;\n"
538 " using namespace QQmlPrivate::AOTLookupValidation;\n"
543 const auto &signatures = lookupSignatures.asKeyValueRange();
544 QList<std::pair<Lookup, Signature>> sorted{ signatures.begin(), signatures.end() };
545 std::stable_sort(sorted.begin(), sorted.end(), [&](
const auto &lhs,
const auto &rhs) {
546 const auto &lTypeString = typeToString(lhs.first.base);
547 const auto &rTypeString = typeToString(rhs.first.base);
548 if (lTypeString == rTypeString)
549 return lhs.first.member < rhs.first.member;
550 return lTypeString < rTypeString;
553 for (
const auto &[memberlookup, signature] : sorted) {
554 if (!writeStr(
" { %1, %2 },\n"_L1
555 .arg(lookupToString(memberlookup), signatureToString(signature))
561 if (!writeStr(
" };\n}\n"))
564 const QString validateLookupSignatures = uR"(
565bool validateLookupSignatures(QQmlEngine *engine, QV4::CompiledData::CompilationUnit *cu)
566{
567 enum ValidationState { Pending, Failed, Succeeded };
568 static ValidationState state = Pending;
569 if (state == Failed)
570 return false;
571 if (state == Succeeded)
573 const auto &expectedSignatures = expectedLookupSignatures();
574 for (const auto &[lookup, expectedSignature] : expectedSignatures.asKeyValueRange()) {
575 if (!QQmlPrivate::AOTLookupValidation::validateLookupSignature(engine, cu, lookup, expectedSignature)) {
576 state = Failed;
577 return false;
578 }
579 }
580 state = Succeeded;
581 return true;
582}
583
584)"_s;
586 if (!writeStr(validateLookupSignatures.toUtf8()))
592bool qSaveQmlJSUnitAsCpp(
const QString &inputFileName,
const QString &outputFileName,
593 const QV4::CompiledData::SaveableUnitPointer &unit,
594 const QQmlJSAotFunctionMap &aotFunctions,
595 const LookupSignatures &lookupSignatures,
bool noAotValidation,
596 QString *errorString)
598#if QT_CONFIG(temporaryfile)
599 QSaveFile f(outputFileName);
601 QFile f(outputFileName);
603 if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
604 *errorString = f.errorString();
608 auto writeStr = [&f, errorString](
const QByteArray &data) {
609 if (f.write(data) != data.size()) {
610 *errorString = f.errorString();
616 if (!writeStr(
"// "))
619 if (!writeStr(inputFileName.toUtf8()))
625 if (!writeStr(
"#include <QtQml/qqmlprivate.h>\n"))
628 if (!noAotValidation) {
629 if (!writeStr(
"#include <QtCore/qhash.h>\n"))
633 if (!aotFunctions.isEmpty()) {
634 QStringList includes;
636 for (
const auto &function : aotFunctions)
637 includes.append(function.includes);
639 std::sort(includes.begin(), includes.end());
640 const auto end = std::unique(includes.begin(), includes.end());
641 for (
auto it = includes.begin(); it != end; ++it) {
642 if (!writeStr(QStringLiteral(
"#include <%1>\n").arg(*it).toUtf8()))
647 if (!writeStr(QByteArrayLiteral(
"\nnamespace QmlCacheGeneratedCode {\nnamespace ")))
650 if (!writeStr(qQmlJSSymbolNamespaceForPath(inputFileName).toUtf8()))
653 if (!writeStr(
" {\n"))
656 if (!generateAotValidationCode(writeStr, lookupSignatures, noAotValidation))
659 if (!writeStr(QByteArrayLiteral(
"extern const unsigned char qmlData alignas(16) [];\n"
660 "extern const unsigned char qmlData alignas(16) [] = {\n")))
663 unit.saveToDisk<uchar>([&writeStr](
const uchar *begin, quint32 size) {
664 QByteArray hexifiedData;
666 QTextStream stream(&hexifiedData);
667 const uchar *end = begin + size;
670 for (
const uchar *data = begin; data < end; ++data, ++col) {
677 stream <<
"0x" << *data;
681 return writeStr(hexifiedData);
686 if (!writeStr(
"};\n"))
690 if (aotFunctions.size() <= 1) {
692 writeStr(
"extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
693 "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = { { 0, 0, nullptr, nullptr } };\n");
695 writeStr(
"extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
696 "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = {\n");
698 QString footer = QStringLiteral(
"}\n");
700 for (QQmlJSAotFunctionMap::ConstIterator func = aotFunctions.constBegin(),
701 end = aotFunctions.constEnd();
702 func != end; ++func) {
704 if (func.key() == FileScopeCodeIndex)
707 const QString function = QString::fromUtf8(funcHeaderCode) + func.value().code + footer;
709 writeStr(QStringLiteral(
"{ %1, %2, [](QV4::ExecutableCompilationUnit *contextUnit, "
710 "QMetaType *argTypes) {\n%3}, %4 },")
712 .arg(func->numArguments)
713 .arg(func->signature.isEmpty() ? u" Q_UNUSED(contextUnit);\n Q_UNUSED(argTypes);\n"_s : func->signature, function)
714 .toUtf8().constData());
718 writeStr(
"{ 0, 0, nullptr, nullptr }");
722 if (!writeStr(
"}\n}\n"))
725#if QT_CONFIG(temporaryfile)
727 *errorString = f.errorString();
735QQmlJSAotCompiler::QQmlJSAotCompiler(
736 QQmlJSImporter *importer,
const QString &resourcePath,
const QStringList &qmldirFiles,
737 QQmlJSLogger *logger)
738 : m_typeResolver(importer)
739 , m_resourcePath(resourcePath)
740 , m_qmldirFiles(qmldirFiles)
741 , m_importer(importer)
746void QQmlJSAotCompiler::setDocument(
747 const QmlIR::JSCodeGen *codegen,
const QmlIR::Document *irDocument)
750 m_document = irDocument;
751 const QFileInfo resourcePathInfo(m_resourcePath);
752 if (m_logger->filePath().isEmpty())
753 m_logger->setFilePath(resourcePathInfo.fileName());
754 m_logger->setCode(irDocument->code);
755 m_unitGenerator = &irDocument->jsGenerator;
756 QQmlJSImportVisitor visitor(m_importer, m_logger,
757 resourcePathInfo.canonicalPath() + u'/',
759 m_typeResolver.init(&visitor, irDocument->program);
762void QQmlJSAotCompiler::setScope(
const QmlIR::Object *object,
const QmlIR::Object *scope)
764 m_currentObject = object;
765 m_currentScope = scope;
768static bool isStrict(
const QmlIR::Document *doc)
770 for (
const QmlIR::Pragma *pragma : doc->pragmas) {
771 if (pragma->type == QmlIR::Pragma::Strict)
777QQmlJS::DiagnosticMessage QQmlJSAotCompiler::diagnose(
778 const QString &message, QtMsgType type,
const QQmlJS::SourceLocation &location)
const
780 if (isStrict(m_document)
781 && (type == QtWarningMsg || type == QtCriticalMsg || type == QtFatalMsg)
782 && m_logger->categorySeverity(qmlCompiler) == QQmlSA::WarningSeverity::Error) {
783 qFatal(
"%s:%d: (strict mode) %s",
784 qPrintable(QFileInfo(m_resourcePath).fileName()),
785 location.startLine, qPrintable(message));
788 return QQmlJS::DiagnosticMessage {
795std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>> QQmlJSAotCompiler::compileBinding(
796 const QV4::Compiler::Context *context,
const QmlIR::Binding &irBinding,
797 QQmlJS::AST::Node *astNode)
799 QQmlJSFunctionInitializer initializer(
800 &m_typeResolver, m_currentObject->location, m_currentScope->location, m_logger);
802 const QString name = m_document->stringAt(irBinding.propertyNameIndex);
803 QQmlJSCompilePass::Function function = initializer.run( context, name, astNode, irBinding);
805 const QQmlJSAotFunction aotFunction = doCompileAndRecordAotStats(
806 context, &function, name, astNode->firstSourceLocation());
808 if (
const auto errors = finalizeBindingOrFunction())
811 qCDebug(lcAotCompiler()) <<
"includes:" << aotFunction.includes;
812 qCDebug(lcAotCompiler()) <<
"binding code:" << aotFunction.code;
816std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>> QQmlJSAotCompiler::compileFunction(
817 const QV4::Compiler::Context *context,
const QString &name, QQmlJS::AST::Node *astNode)
819 QQmlJSFunctionInitializer initializer(
820 &m_typeResolver, m_currentObject->location, m_currentScope->location, m_logger);
821 QQmlJSCompilePass::Function function = initializer.run(context, name, astNode);
823 const QQmlJSAotFunction aotFunction = doCompileAndRecordAotStats(
824 context, &function, name, astNode->firstSourceLocation());
826 if (
const auto errors = finalizeBindingOrFunction())
829 qCDebug(lcAotCompiler()) <<
"includes:" << aotFunction.includes;
830 qCDebug(lcAotCompiler()) <<
"binding code:" << aotFunction.code;
834QQmlJSAotFunction QQmlJSAotCompiler::globalCode()
const
836 QQmlJSAotFunction global;
838 u"QtQml/qjsengine.h"_s,
839 u"QtQml/qjsprimitivevalue.h"_s,
840 u"QtQml/qjsvalue.h"_s,
841 u"QtQml/qqmlcomponent.h"_s,
842 u"QtQml/qqmlcontext.h"_s,
843 u"QtQml/qqmlengine.h"_s,
844 u"QtQml/qqmllist.h"_s,
846 u"QtCore/qdatetime.h"_s,
847 u"QtCore/qtimezone.h"_s,
848 u"QtCore/qobject.h"_s,
849 u"QtCore/qstring.h"_s,
850 u"QtCore/qstringlist.h"_s,
852 u"QtCore/qvariant.h"_s,
859std::optional<QList<QQmlJS::DiagnosticMessage>> QQmlJSAotCompiler::finalizeBindingOrFunction()
861 const auto archiveMessages = qScopeGuard([
this]() { m_logger->finalizeFunction(); });
863 if (!m_logger->currentFunctionHasCompileError())
866 QList<QQmlJS::DiagnosticMessage> errors;
867 m_logger->iterateCurrentFunctionMessages([&](
const Message &msg) {
868 if (msg.compilationStatus == Message::CompilationStatus::Error)
869 errors.append(diagnose(msg.message, msg.msgType(), msg.loc));
874QQmlJSAotFunction QQmlJSAotCompiler::doCompile(
875 const QV4::Compiler::Context *context,
const QQmlJSCompilePass::Function *function)
877 if (m_logger->currentFunctionHasErrorOrSkip())
878 return QQmlJSAotFunction();
880 bool basicBlocksValidationFailed =
false;
881 QQmlJSBasicBlocks basicBlocks(context, m_unitGenerator, &m_typeResolver, m_logger);
882 auto passResult = basicBlocks.run(function, m_flags, basicBlocksValidationFailed);
883 auto &[blocks, annotations] = passResult;
885 QQmlJSTypePropagator propagator(
886 m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
887 passResult = propagator.run(function);
888 if (m_logger->currentFunctionHasErrorOrSkip())
889 return QQmlJSAotFunction();
891 QQmlJSShadowCheck shadowCheck(
892 m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
893 passResult = shadowCheck.run(function);
894 if (m_logger->currentFunctionHasErrorOrSkip())
895 return QQmlJSAotFunction();
897 QQmlJSOptimizations optimizer(
898 m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations,
899 basicBlocks.objectAndArrayDefinitions());
900 passResult = optimizer.run(function);
901 if (m_logger->currentFunctionHasErrorOrSkip())
902 return QQmlJSAotFunction();
904 QQmlJSStorageInitializer initializer(
905 m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
906 passResult = initializer.run(function);
909 QQmlJSStorageGeneralizer generalizer(
910 m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
911 passResult = generalizer.run(function);
912 if (m_logger->currentFunctionHasErrorOrSkip())
913 return QQmlJSAotFunction();
915 QQmlJSCodeGenerator codegen(context, m_unitGenerator, &m_typeResolver, m_logger, blocks,
916 annotations, noAotValidation());
917 QQmlJSAotFunction result = codegen.run(function, basicBlocksValidationFailed);
918 if (m_logger->currentFunctionHasErrorOrSkip())
919 return QQmlJSAotFunction();
921 m_lookupSignatures.insert(codegen.lookupSignatures());
925QQmlJSAotFunction QQmlJSAotCompiler::doCompileAndRecordAotStats(
926 const QV4::Compiler::Context *context,
const QQmlJSCompilePass::Function *function,
927 const QString &name, QQmlJS::SourceLocation location)
929 QElapsedTimer timer {};
931 QQmlJSAotFunction result;
932 if (!m_logger->currentFunctionHasCompileError())
933 result = doCompile(context, function);
934 auto elapsed = std::chrono::milliseconds { timer.elapsed() };
936 if (QQmlJS::QQmlJSAotCompilerStats::recordAotStats()) {
937 QQmlJS::AotStatsEntry entry;
938 entry.codegenDuration = elapsed;
939 entry.functionName = name;
940 entry.message = m_logger->currentFunctionWasSkipped()
941 ? m_logger->currentFunctionCompileSkipMessage()
942 : m_logger->currentFunctionCompileErrorMessage();
943 entry.line = location.startLine;
944 entry.column = location.startColumn;
945 if (m_logger->currentFunctionWasSkipped())
946 entry.codegenResult = QQmlJS::CodegenResult::Skip;
947 else if (m_logger->currentFunctionHasCompileError())
948 entry.codegenResult = QQmlJS::CodegenResult::Failure;
950 entry.codegenResult = QQmlJS::CodegenResult::Success;
951 QQmlJS::QQmlJSAotCompilerStats::addEntry(
952 function->qmlScope.containedType()->filePath(), entry);
955 if (m_logger->currentFunctionWasSkipped())
956 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