Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qqmljscompiler.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "qqmljscompiler_p.h"
5
6#include <private/qqmlirbuilder_p.h>
7#include <private/qqmljsbasicblocks_p.h>
8#include <private/qqmljscodegenerator_p.h>
9#include <private/qqmljsfunctioninitializer_p.h>
10#include <private/qqmljsimportvisitor_p.h>
11#include <private/qqmljslexer_p.h>
12#include <private/qqmljsloadergenerator_p.h>
13#include <private/qqmljsparser_p.h>
14#include <private/qqmljsshadowcheck_p.h>
15#include <private/qqmljsstoragegeneralizer_p.h>
16#include <private/qqmljstypepropagator_p.h>
17
18#include <QtCore/qfile.h>
19#include <QtCore/qfileinfo.h>
20#include <QtCore/qloggingcategory.h>
21
22#include <limits>
23
25
26using namespace Qt::StringLiterals;
27
28Q_LOGGING_CATEGORY(lcAotCompiler, "qt.qml.compiler.aot", QtFatalMsg);
29
30static const int FileScopeCodeIndex = -1;
31
33{
34 QSet<QString> illegalNames;
35 for (const char **g = QV4::Compiler::Codegen::s_globalNames; *g != nullptr; ++g)
36 illegalNames.insert(QString::fromLatin1(*g));
37 return illegalNames;
38}
39
41
42
44{
45 fprintf(stderr, "%s\n", qPrintable(message));
46}
47
49{
50 QQmlJSCompileError augmented;
51 augmented.message = contextErrorMessage + message;
52 return augmented;
53}
54
56{
58 message = fileName + QLatin1Char(':') + QString::number(m.loc.startLine) + QLatin1Char(':');
59 if (m.loc.startColumn > 0)
60 message += QString::number(m.loc.startColumn) + QLatin1Char(':');
61
62 if (m.isError())
63 message += QLatin1String(" error: ");
64 else
65 message += QLatin1String(" warning: ");
66 message += m.message;
67 return message;
68}
69
71 const QQmlJS::DiagnosticMessage &diagnostic)
72{
73 if (!message.isEmpty())
74 message += QLatin1Char('\n');
75 message += diagnosticErrorMessage(inputFileName, diagnostic);
76}
77
79 const QList<QQmlJS::DiagnosticMessage> &diagnostics)
80{
81 for (const QQmlJS::DiagnosticMessage &diagnostic: diagnostics)
82 appendDiagnostic(inputFileName, diagnostic);
83}
84
85// Ensure that ListElement objects keep all property assignments in their string form
87{
88 QStringList listElementNames;
89
90 for (const QV4::CompiledData::Import *import : std::as_const(document->imports)) {
91 const QString uri = document->stringAt(import->uriIndex);
92 if (uri != QStringLiteral("QtQml.Models") && uri != QStringLiteral("QtQuick"))
93 continue;
94
95 QString listElementName = QStringLiteral("ListElement");
96 const QString qualifier = document->stringAt(import->qualifierIndex);
97 if (!qualifier.isEmpty()) {
98 listElementName.prepend(QLatin1Char('.'));
99 listElementName.prepend(qualifier);
100 }
101 listElementNames.append(listElementName);
102 }
103
104 if (listElementNames.isEmpty())
105 return;
106
107 for (QmlIR::Object *object : std::as_const(document->objects)) {
108 if (!listElementNames.contains(document->stringAt(object->inheritedTypeNameIndex)))
109 continue;
110 for (QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) {
111 if (binding->type() != QV4::CompiledData::Binding::Type_Script)
112 continue;
113 binding->stringIndex = document->registerString(object->bindingAsString(document, binding->value.compiledScriptIndex));
114 }
115 }
116}
117
120{
121 for (QmlIR::Object *object: std::as_const(doc.objects)) {
122 for (auto binding = object->bindingsBegin(); binding != object->bindingsEnd(); ++binding) {
123 if (binding->type() != QV4::CompiledData::Binding::Type_Script)
124 continue;
125 const QString propName = doc.stringAt(binding->propertyNameIndex);
126 if (!propName.startsWith(QLatin1String("on"))
127 || propName.size() < 3
128 || !propName.at(2).isUpper())
129 continue;
130 auto compiledFunction = doc.jsModule.functions.value(object->runtimeFunctionIndices.at(binding->value.compiledScriptIndex));
131 if (!compiledFunction)
132 continue;
133 if (compiledFunction->usesArgumentsObject == QV4::Compiler::Context::ArgumentsObjectUsed) {
134 error->message = QLatin1Char(':') + QString::number(compiledFunction->line) + QLatin1Char(':');
135 if (compiledFunction->column > 0)
136 error->message += QString::number(compiledFunction->column) + QLatin1Char(':');
137
138 error->message += QLatin1String(" error: The use of eval() or the use of the arguments object in signal handlers is\n"
139 "not supported when compiling qml files ahead of time. That is because it's ambiguous if \n"
140 "any signal parameter is called \"arguments\". Similarly the string passed to eval might use\n"
141 "\"arguments\". Unfortunately we cannot distinguish between it being a parameter or the\n"
142 "JavaScript arguments object at this point.\n"
143 "Consider renaming the parameter of the signal if applicable or moving the code into a\n"
144 "helper function.");
145 return false;
146 }
147 }
148 }
149 return true;
150}
151
153{
154public:
155 BindingOrFunction(const QmlIR::Binding &b) : m_binding(&b) {}
156 BindingOrFunction(const QmlIR::Function &f) : m_function(&f) {}
157
158 friend bool operator<(const BindingOrFunction &lhs, const BindingOrFunction &rhs)
159 {
160 return lhs.index() < rhs.index();
161 }
162
163 const QmlIR::Binding *binding() const { return m_binding; }
164 const QmlIR::Function *function() const { return m_function; }
165
167 {
168 return m_binding
169 ? m_binding->value.compiledScriptIndex
170 : (m_function
171 ? m_function->index
172 : std::numeric_limits<quint32>::max());
173 }
174
175private:
176 const QmlIR::Binding *m_binding = nullptr;
177 const QmlIR::Function *m_function = nullptr;
178};
179
180bool qCompileQmlFile(const QString &inputFileName, QQmlJSSaveFunction saveFunction,
182 bool storeSourceLocation, QV4::Compiler::CodegenWarningInterface *interface,
183 const QString *fileContents)
184{
185 QmlIR::Document irDocument(/*debugMode*/false);
186 return qCompileQmlFile(irDocument, inputFileName, saveFunction, aotCompiler, error,
187 storeSourceLocation, interface, fileContents);
188}
189
190bool qCompileQmlFile(QmlIR::Document &irDocument, const QString &inputFileName,
191 QQmlJSSaveFunction saveFunction, QQmlJSAotCompiler *aotCompiler,
192 QQmlJSCompileError *error, bool storeSourceLocation,
194{
195 QString sourceCode;
196
197 if (fileContents != nullptr) {
198 sourceCode = *fileContents;
199 } else {
200 QFile f(inputFileName);
201 if (!f.open(QIODevice::ReadOnly)) {
202 error->message = QLatin1String("Error opening ") + inputFileName + QLatin1Char(':') + f.errorString();
203 return false;
204 }
205 sourceCode = QString::fromUtf8(f.readAll());
206 if (f.error() != QFileDevice::NoError) {
207 error->message = QLatin1String("Error reading from ") + inputFileName + QLatin1Char(':') + f.errorString();
208 return false;
209 }
210 }
211
212 {
213 QmlIR::IRBuilder irBuilder(*illegalNames());
214 if (!irBuilder.generateFromQml(sourceCode, inputFileName, &irDocument)) {
215 error->appendDiagnostics(inputFileName, irBuilder.errors);
216 return false;
217 }
218 }
219
220 annotateListElements(&irDocument);
221 QQmlJSAotFunctionMap aotFunctionsByIndex;
222
223 {
224 QmlIR::JSCodeGen v4CodeGen(&irDocument, *illegalNames(), interface, storeSourceLocation);
225
226 if (aotCompiler)
227 aotCompiler->setDocument(&v4CodeGen, &irDocument);
228
230 for (QmlIR::Object *object: std::as_const(irDocument.objects)) {
231 if (object->functionsAndExpressions->count == 0 && object->bindingCount() == 0)
232 continue;
233
234 if (!v4CodeGen.generateRuntimeFunctions(object)) {
235 Q_ASSERT(v4CodeGen.hasError());
236 error->appendDiagnostic(inputFileName, v4CodeGen.error());
237 return false;
238 }
239
240 if (!aotCompiler)
241 continue;
242
243 QmlIR::Object *scope = object;
244 for (auto it = effectiveScopes.constFind(scope), end = effectiveScopes.constEnd();
245 it != end; it = effectiveScopes.constFind(scope)) {
246 scope = *it;
247 }
248
249 aotCompiler->setScope(object, scope);
250 aotFunctionsByIndex[FileScopeCodeIndex] = aotCompiler->globalCode();
251
252 std::vector<BindingOrFunction> bindingsAndFunctions;
253 bindingsAndFunctions.reserve(object->bindingCount() + object->functionCount());
254
255 std::copy(object->bindingsBegin(), object->bindingsEnd(),
256 std::back_inserter(bindingsAndFunctions));
257 std::copy(object->functionsBegin(), object->functionsEnd(),
258 std::back_inserter(bindingsAndFunctions));
259
261 for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first;
262 foe; foe = foe->next) {
263 functionsToCompile << *foe;
264 }
265
266 // AOT-compile bindings and functions in the same order as above so that the runtime
267 // class indices match
268 auto contextMap = v4CodeGen.module()->contextMap;
269 std::sort(bindingsAndFunctions.begin(), bindingsAndFunctions.end());
270 std::for_each(bindingsAndFunctions.begin(), bindingsAndFunctions.end(),
271 [&](const BindingOrFunction &bindingOrFunction) {
272 std::variant<QQmlJSAotFunction, QQmlJS::DiagnosticMessage> result;
273 if (const auto *binding = bindingOrFunction.binding()) {
274 switch (binding->type()) {
275 case QmlIR::Binding::Type_AttachedProperty:
276 case QmlIR::Binding::Type_GroupProperty:
277 effectiveScopes.insert(
278 irDocument.objects.at(binding->value.objectIndex), scope);
279 return;
280 case QmlIR::Binding::Type_Boolean:
281 case QmlIR::Binding::Type_Number:
282 case QmlIR::Binding::Type_String:
283 case QmlIR::Binding::Type_Null:
284 case QmlIR::Binding::Type_Object:
285 case QmlIR::Binding::Type_Translation:
286 case QmlIR::Binding::Type_TranslationById:
287 return;
288 default:
289 break;
290 }
291
292 Q_ASSERT(quint32(functionsToCompile.size()) > binding->value.compiledScriptIndex);
293 const auto &functionToCompile
294 = functionsToCompile[binding->value.compiledScriptIndex];
295 auto *parentNode = functionToCompile.parentNode;
296 Q_ASSERT(parentNode);
297 Q_ASSERT(contextMap.contains(parentNode));
298 QV4::Compiler::Context *context = contextMap.take(parentNode);
299 Q_ASSERT(context);
300
301 auto *node = functionToCompile.node;
302 Q_ASSERT(node);
303
304 if (context->returnsClosure) {
305 QQmlJS::AST::Node *inner
306 = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(
307 node)->expression;
308 Q_ASSERT(inner);
309 QV4::Compiler::Context *innerContext = contextMap.take(inner);
310 Q_ASSERT(innerContext);
311 qCDebug(lcAotCompiler) << "Compiling signal handler for"
312 << irDocument.stringAt(binding->propertyNameIndex);
313 std::variant<QQmlJSAotFunction, QQmlJS::DiagnosticMessage> innerResult
314 = aotCompiler->compileBinding(innerContext, *binding, inner);
315
316 if (auto *error = std::get_if<QQmlJS::DiagnosticMessage>(&innerResult)) {
317 qCDebug(lcAotCompiler) << "Compilation failed:"
318 << diagnosticErrorMessage(inputFileName, *error);
319 } else if (auto *func = std::get_if<QQmlJSAotFunction>(&innerResult)) {
320 qCDebug(lcAotCompiler) << "Generated code:" << func->code;
321 aotFunctionsByIndex[innerContext->functionIndex] = *func;
322 }
323 }
324
325 qCDebug(lcAotCompiler) << "Compiling binding for property"
326 << irDocument.stringAt(binding->propertyNameIndex);
327 result = aotCompiler->compileBinding(context, *binding, node);
328 } else if (const auto *function = bindingOrFunction.function()) {
329 Q_ASSERT(quint32(functionsToCompile.size()) > function->index);
330 auto *node = functionsToCompile[function->index].node;
331 Q_ASSERT(node);
332 Q_ASSERT(contextMap.contains(node));
333 QV4::Compiler::Context *context = contextMap.take(node);
334 Q_ASSERT(context);
335
336 const QString functionName = irDocument.stringAt(function->nameIndex);
337 qCDebug(lcAotCompiler) << "Compiling function" << functionName;
338 result = aotCompiler->compileFunction(context, functionName, node);
339 } else {
340 Q_UNREACHABLE();
341 }
342
343 if (auto *error = std::get_if<QQmlJS::DiagnosticMessage>(&result)) {
344 qCDebug(lcAotCompiler) << "Compilation failed:"
345 << diagnosticErrorMessage(inputFileName, *error);
346 } else if (auto *func = std::get_if<QQmlJSAotFunction>(&result)) {
347 qCDebug(lcAotCompiler) << "Generated code:" << func->code;
348 aotFunctionsByIndex[object->runtimeFunctionIndices[bindingOrFunction.index()]] =
349 *func;
350 }
351 });
352 }
353
355 *error = error->augment(inputFileName);
356 return false;
357 }
358
360 irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(/*generate unit*/false);
361 generator.generate(irDocument);
362
363 const quint32 saveFlags
367 saveFlags);
368 if (!saveFunction(saveable, aotFunctionsByIndex, &error->message))
369 return false;
370 }
371 return true;
372}
373
374bool qCompileJSFile(const QString &inputFileName, const QString &inputFileUrl, QQmlJSSaveFunction saveFunction, QQmlJSCompileError *error)
375{
377
378 QString sourceCode;
379 {
380 QFile f(inputFileName);
381 if (!f.open(QIODevice::ReadOnly)) {
382 error->message = QLatin1String("Error opening ") + inputFileName + QLatin1Char(':') + f.errorString();
383 return false;
384 }
385 sourceCode = QString::fromUtf8(f.readAll());
386 if (f.error() != QFileDevice::NoError) {
387 error->message = QLatin1String("Error reading from ") + inputFileName + QLatin1Char(':') + f.errorString();
388 return false;
389 }
390 }
391
392 const bool isModule = inputFileName.endsWith(QLatin1String(".mjs"));
393 if (isModule) {
395 // Precompiled files are relocatable and the final location will be set when loading.
396 QString url;
397 unit = QV4::Compiler::Codegen::compileModule(/*debugMode*/false, url, sourceCode,
398 QDateTime(), &diagnostics);
399 error->appendDiagnostics(inputFileName, diagnostics);
400 if (!unit.unitData())
401 return false;
402 } else {
403 QmlIR::Document irDocument(/*debugMode*/false);
404
405 QQmlJS::Engine *engine = &irDocument.jsParserEngine;
406 QmlIR::ScriptDirectivesCollector directivesCollector(&irDocument);
407 QQmlJS::Directives *oldDirs = engine->directives();
408 engine->setDirectives(&directivesCollector);
409 auto directivesGuard = qScopeGuard([engine, oldDirs]{
410 engine->setDirectives(oldDirs);
411 });
412
413 QQmlJS::AST::Program *program = nullptr;
414
415 {
416 QQmlJS::Lexer lexer(engine);
417 lexer.setCode(sourceCode, /*line*/1, /*parseAsBinding*/false);
418 QQmlJS::Parser parser(engine);
419
420 bool parsed = parser.parseProgram();
421
422 error->appendDiagnostics(inputFileName, parser.diagnosticMessages());
423
424 if (!parsed)
425 return false;
426
427 program = QQmlJS::AST::cast<QQmlJS::AST::Program*>(parser.rootNode());
428 if (!program) {
429 lexer.setCode(QStringLiteral("undefined;"), 1, false);
430 parsed = parser.parseProgram();
431 Q_ASSERT(parsed);
432 program = QQmlJS::AST::cast<QQmlJS::AST::Program*>(parser.rootNode());
434 }
435 }
436
437 {
438 QmlIR::JSCodeGen v4CodeGen(&irDocument, *illegalNames());
439 v4CodeGen.generateFromProgram(inputFileName, inputFileUrl, sourceCode, program,
441 if (v4CodeGen.hasError()) {
442 error->appendDiagnostic(inputFileName, v4CodeGen.error());
443 return false;
444 }
445
446 // Precompiled files are relocatable and the final location will be set when loading.
447 irDocument.jsModule.fileName.clear();
448 irDocument.jsModule.finalUrl.clear();
449
450 irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(/*generate unit*/false);
452 generator.generate(irDocument);
453 unit = std::move(irDocument.javaScriptCompilationUnit);
454 }
455 }
456
458 return saveFunction(QV4::CompiledData::SaveableUnitPointer(unit.data), empty, &error->message);
459}
460
461static const char *wrapCallCode = R"(
462template <typename Binding>
463void wrapCall(const QQmlPrivate::AOTCompiledContext *aotContext, void *dataPtr, void **argumentsPtr, Binding &&binding)
464{
465 using return_type = std::invoke_result_t<Binding, const QQmlPrivate::AOTCompiledContext *, void **>;
466 if constexpr (std::is_same_v<return_type, void>) {
467 Q_UNUSED(dataPtr)
468 binding(aotContext, argumentsPtr);
469 } else {
470 if (dataPtr) {
471 new (dataPtr) return_type(binding(aotContext, argumentsPtr));
472 } else {
473 binding(aotContext, argumentsPtr);
474 }
475 }
476}
477)";
478
479static const char *funcHeaderCode = R"(
480 [](const QQmlPrivate::AOTCompiledContext *context, void *data, void **argv) {
481 wrapCall(context, data, argv, [](const QQmlPrivate::AOTCompiledContext *aotContext, void **argumentsPtr) {
482Q_UNUSED(aotContext)
483Q_UNUSED(argumentsPtr)
484)";
485
486bool qSaveQmlJSUnitAsCpp(const QString &inputFileName, const QString &outputFileName, const QV4::CompiledData::SaveableUnitPointer &unit, const QQmlJSAotFunctionMap &aotFunctions, QString *errorString)
487{
488#if QT_CONFIG(temporaryfile)
489 QSaveFile f(outputFileName);
490#else
491 QFile f(outputFileName);
492#endif
494 *errorString = f.errorString();
495 return false;
496 }
497
498 auto writeStr = [&f, errorString](const QByteArray &data) {
499 if (f.write(data) != data.size()) {
500 *errorString = f.errorString();
501 return false;
502 }
503 return true;
504 };
505
506 if (!writeStr("// "))
507 return false;
508
509 if (!writeStr(inputFileName.toUtf8()))
510 return false;
511
512 if (!writeStr("\n"))
513 return false;
514
515 if (!writeStr("#include <QtQml/qqmlprivate.h>\n"))
516 return false;
517
518 if (!aotFunctions.isEmpty()) {
519 QStringList includes;
520
521 for (const auto &function : aotFunctions)
522 includes.append(function.includes);
523
524 std::sort(includes.begin(), includes.end());
525 const auto end = std::unique(includes.begin(), includes.end());
526 for (auto it = includes.begin(); it != end; ++it) {
527 if (!writeStr(QStringLiteral("#include <%1>\n").arg(*it).toUtf8()))
528 return false;
529 }
530 }
531
532 if (!writeStr(QByteArrayLiteral("namespace QmlCacheGeneratedCode {\nnamespace ")))
533 return false;
534
535 if (!writeStr(qQmlJSSymbolNamespaceForPath(inputFileName).toUtf8()))
536 return false;
537
538 if (!writeStr(QByteArrayLiteral(" {\nextern const unsigned char qmlData alignas(16) [];\n"
539 "extern const unsigned char qmlData alignas(16) [] = {\n")))
540 return false;
541
542 unit.saveToDisk<uchar>([&writeStr](const uchar *begin, quint32 size) {
543 QByteArray hexifiedData;
544 {
545 QTextStream stream(&hexifiedData);
546 const uchar *end = begin + size;
547 stream << Qt::hex;
548 int col = 0;
549 for (const uchar *data = begin; data < end; ++data, ++col) {
550 if (data > begin)
551 stream << ',';
552 if (col % 8 == 0) {
553 stream << '\n';
554 col = 0;
555 }
556 stream << "0x" << *data;
557 }
558 stream << '\n';
559 }
560 return writeStr(hexifiedData);
561 });
562
563
564
565 if (!writeStr("};\n"))
566 return false;
567
568 // Suppress the following warning generated by MSVC 2019:
569 // "the usage of 'QJSNumberCoercion::toInteger' requires the compiler to capture 'this'
570 // but the current default capture mode does not allow it"
571 // You clearly don't have to capture 'this' in order to call 'QJSNumberCoercion::toInteger'.
572 // TODO: Remove when we don't have to support MSVC 2019 anymore. Mind the QT_WARNING_POP below.
573 if (!writeStr("QT_WARNING_PUSH\nQT_WARNING_DISABLE_MSVC(4573)\n"))
574 return false;
575
576 writeStr(aotFunctions[FileScopeCodeIndex].code.toUtf8().constData());
577 if (aotFunctions.size() <= 1) {
578 // FileScopeCodeIndex is always there, but it may be the only one.
579 writeStr("extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
580 "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = { { 0, QMetaType::fromType<void>(), {}, nullptr } };");
581 } else {
583 writeStr("extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
584 "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = {\n");
585
586 QString footer = QStringLiteral("});}\n");
587
589 end = aotFunctions.constEnd();
590 func != end; ++func) {
591
592 if (func.key() == FileScopeCodeIndex)
593 continue;
594
595 QString function = QString::fromUtf8(funcHeaderCode) + func.value().code + footer;
596
597 QString argumentTypes = func.value().argumentTypes.join(
598 QStringLiteral(">(), QMetaType::fromType<"));
599 if (!argumentTypes.isEmpty()) {
600 argumentTypes = QStringLiteral("QMetaType::fromType<")
601 + argumentTypes + QStringLiteral(">()");
602 }
603
604 writeStr(QStringLiteral("{ %1, QMetaType::fromType<%2>(), { %3 }, %4 },")
605 .arg(func.key())
606 .arg(func.value().returnType)
607 .arg(argumentTypes)
608 .arg(function)
609 .toUtf8().constData());
610 }
611
612 // Conclude the list with a nullptr
613 writeStr("{ 0, QMetaType::fromType<void>(), {}, nullptr }");
614 writeStr("};\n");
615 }
616
617 if (!writeStr("QT_WARNING_POP\n"))
618 return false;
619
620 if (!writeStr("}\n}\n"))
621 return false;
622
623#if QT_CONFIG(temporaryfile)
624 if (!f.commit()) {
625 *errorString = f.errorString();
626 return false;
627 }
628#endif
629
630 return true;
631}
632
634 QQmlJSImporter *importer, const QString &resourcePath, const QStringList &qmldirFiles,
635 QQmlJSLogger *logger)
636 : m_typeResolver(importer)
637 , m_resourcePath(resourcePath)
638 , m_qmldirFiles(qmldirFiles)
639 , m_importer(importer)
640 , m_logger(logger)
641{
642}
643
645 const QmlIR::JSCodeGen *codegen, const QmlIR::Document *irDocument)
646{
647 Q_UNUSED(codegen);
648 m_document = irDocument;
649 const QFileInfo resourcePathInfo(m_resourcePath);
650 m_logger->setFileName(resourcePathInfo.fileName());
651 m_logger->setCode(irDocument->code);
652 m_unitGenerator = &irDocument->jsGenerator;
655 resourcePathInfo.canonicalPath() + u'/',
657 m_typeResolver.init(&visitor, irDocument->program);
658}
659
661{
663 m_currentScope = scope;
664}
665
666static bool isStrict(const QmlIR::Document *doc)
667{
668 for (const QmlIR::Pragma *pragma : doc->pragmas) {
669 if (pragma->type == QmlIR::Pragma::Strict)
670 return true;
671 }
672 return false;
673}
674
677{
681 qFatal("%s:%d: (strict mode) %s",
683 location.startLine, qPrintable(message));
684 }
685
686 // TODO: this is a special place that explicitly sets the severity through
687 // logger's private function
689
691 message,
692 type,
694 };
695}
696
697std::variant<QQmlJSAotFunction, QQmlJS::DiagnosticMessage> QQmlJSAotCompiler::compileBinding(
698 const QV4::Compiler::Context *context, const QmlIR::Binding &irBinding,
699 QQmlJS::AST::Node *astNode)
700{
701 QQmlJSFunctionInitializer initializer(
706 context, name, astNode, irBinding, &error);
707 const QQmlJSAotFunction aotFunction = doCompile(context, &function, &error);
708
709 if (error.isValid()) {
710 // If it's a signal and the function just returns a closure, it's harmless.
711 // Otherwise promote the message to warning level.
712 return diagnose(error.message,
713 (function.isSignalHandler && error.type == QtDebugMsg)
714 ? QtDebugMsg
715 : QtWarningMsg,
716 error.loc);
717 }
718
719 qCDebug(lcAotCompiler()) << "includes:" << aotFunction.includes;
720 qCDebug(lcAotCompiler()) << "binding code:" << aotFunction.code;
721 return aotFunction;
722}
723
724std::variant<QQmlJSAotFunction, QQmlJS::DiagnosticMessage> QQmlJSAotCompiler::compileFunction(
726{
727 QQmlJSFunctionInitializer initializer(
730 QQmlJSCompilePass::Function function = initializer.run(context, name, astNode, &error);
731 const QQmlJSAotFunction aotFunction = doCompile(context, &function, &error);
732
733 if (error.isValid())
734 return diagnose(error.message, QtWarningMsg, error.loc);
735
736 qCDebug(lcAotCompiler()) << "includes:" << aotFunction.includes;
737 qCDebug(lcAotCompiler()) << "binding code:" << aotFunction.code;
738 return aotFunction;
739}
740
742{
744 global.includes = {
745 u"QtQml/qjsengine.h"_s,
746 u"QtQml/qjsprimitivevalue.h"_s,
747 u"QtQml/qjsvalue.h"_s,
748 u"QtQml/qqmlcomponent.h"_s,
749 u"QtQml/qqmlcontext.h"_s,
750 u"QtQml/qqmlengine.h"_s,
751 u"QtQml/qqmllist.h"_s,
752
753 u"QtCore/qdatetime.h"_s,
754 u"QtCore/qtimezone.h"_s,
755 u"QtCore/qobject.h"_s,
756 u"QtCore/qstring.h"_s,
757 u"QtCore/qstringlist.h"_s,
758 u"QtCore/qurl.h"_s,
759 u"QtCore/qvariant.h"_s,
760
761 u"type_traits"_s
762 };
763 return global;
764}
765
766
767QQmlJSAotFunction QQmlJSAotCompiler::doCompile(
770{
771 const auto compileError = [&]() {
772 Q_ASSERT(error->isValid());
773 error->type = context->returnsClosure ? QtDebugMsg : QtWarningMsg;
774 return QQmlJSAotFunction();
775 };
776
778 auto typePropagationResult = propagator.run(function, error);
779 if (error->isValid())
780 return compileError();
781
783 shadowCheck.run(&typePropagationResult, function, error);
784 if (error->isValid())
785 return compileError();
786
788 typePropagationResult = basicBlocks.run(function, typePropagationResult, error);
789 if (error->isValid())
790 return compileError();
791
792 // Generalize all arguments, registers, and the return type.
793 QQmlJSStorageGeneralizer generalizer(
795 typePropagationResult = generalizer.run(typePropagationResult, function, error);
796 if (error->isValid())
797 return compileError();
798
799 QQmlJSCodeGenerator codegen(
801 QQmlJSAotFunction result = codegen.run(function, &typePropagationResult, error);
802 return error->isValid() ? compileError() : result;
803}
804
const QmlIR::Function * function() const
const QmlIR::Binding * binding() const
friend bool operator<(const BindingOrFunction &lhs, const BindingOrFunction &rhs)
BindingOrFunction(const QmlIR::Function &f)
BindingOrFunction(const QmlIR::Binding &b)
quint32 index() const
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
constexpr bool isUpper() const noexcept
Returns true if the character is an uppercase letter, for example category() is Letter_Uppercase.
Definition qchar.h:475
\inmodule QtCore\reentrant
Definition qdatetime.h:257
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
QString fileName() const
Returns the name of the file, excluding the path.
QString canonicalPath() const
Returns the file's path canonical path (excluding the file name), i.e.
\inmodule QtCore
Definition qfile.h:93
\inmodule QtCore
Definition qhash.h:818
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1279
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
Definition qhash.h:1209
Definition qlist.h:74
Definition qmap.h:186
bool isEmpty() const
Definition qmap.h:268
const_iterator constBegin() const
Definition qmap.h:599
size_type size() const
Definition qmap.h:266
const_iterator constEnd() const
Definition qmap.h:603
const QV4::Compiler::JSUnitGenerator * m_unitGenerator
QQmlJSLogger * m_logger
virtual void setDocument(const QmlIR::JSCodeGen *codegen, const QmlIR::Document *document)
virtual QQmlJSAotFunction globalCode() const
QQmlJSTypeResolver m_typeResolver
QQmlJSAotCompiler(QQmlJSImporter *importer, const QString &resourcePath, const QStringList &qmldirFiles, QQmlJSLogger *logger)
const QString m_resourcePath
const QStringList m_qmldirFiles
virtual QQmlJS::DiagnosticMessage diagnose(const QString &message, QtMsgType type, const QQmlJS::SourceLocation &location) const
const QmlIR::Document * m_document
virtual void setScope(const QmlIR::Object *object, const QmlIR::Object *scope)
QQmlJSImporter * m_importer
virtual std::variant< QQmlJSAotFunction, QQmlJS::DiagnosticMessage > compileFunction(const QV4::Compiler::Context *context, const QString &name, QQmlJS::AST::Node *astNode)
const QmlIR::Object * m_currentScope
const QmlIR::Object * m_currentObject
virtual std::variant< QQmlJSAotFunction, QQmlJS::DiagnosticMessage > compileBinding(const QV4::Compiler::Context *context, const QmlIR::Binding &irBinding, QQmlJS::AST::Node *astNode)
QQmlJSCompilePass::Function run(const QV4::Compiler::Context *context, const QString &propertyName, QQmlJS::AST::Node *astNode, const QmlIR::Binding &irBinding, QQmlJS::DiagnosticMessage *error)
void setCode(const QString &code)
void setFileName(const QString &fileName)
bool isCategoryFatal(QQmlJS::LoggerWarningId id) const
void log(const QString &message, QQmlJS::LoggerWarningId id, const QQmlJS::SourceLocation &srcLocation, bool showContext=true, bool showFileName=true, const std::optional< QQmlJSFixSuggestion > &suggestion={}, const QString overrideFileName=QString())
static QQmlJSScope::Ptr create()
void init(QQmlJSImportVisitor *visitor, QQmlJS::AST::Node *program)
void setCode(const QString &code, int lineno, bool qmlMode=true, CodeContinuation codeContinuation=CodeContinuation::Reset)
quint32 generate()
Generates a 32-bit random quantity and returns it.
Definition qrandom.h:48
\inmodule QtCore
Definition qsavefile.h:24
Definition qset.h:18
iterator insert(const T &value)
Definition qset.h:155
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1107
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1079
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5350
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:7822
QByteArray toUtf8() const &
Definition qstring.h:563
QString & prepend(QChar c)
Definition qstring.h:411
\inmodule QtCore
bool saveToDisk(const std::function< bool(const Char *, quint32)> &writer) const
void generateFromProgram(const QString &fileName, const QString &finalUrl, const QString &sourceCode, QQmlJS::AST::Program *ast, Module *module, ContextType contextType=ContextType::Global)
QQmlJS::DiagnosticMessage error() const
static const char * s_globalNames[]
static QV4::CompiledData::CompilationUnit compileModule(bool debugMode, const QString &url, const QString &sourceCode, const QDateTime &sourceTimeStamp, QList< QQmlJS::DiagnosticMessage > *diagnostics)
QV4::CompiledData::CompilationUnit generateCompilationUnit(bool generateUnitData=true)
Module * module() const
QSet< QString >::iterator it
Combined button and popup list for selecting options.
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
static void * context
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char * interface
DBusConnection const char DBusError * error
EGLStreamKHR stream
#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS)
QtMsgType
Definition qlogging.h:29
@ QtCriticalMsg
Definition qlogging.h:32
@ QtWarningMsg
Definition qlogging.h:31
@ QtFatalMsg
Definition qlogging.h:33
@ QtDebugMsg
Definition qlogging.h:30
#define qFatal
Definition qlogging.h:164
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
GLint location
GLboolean GLboolean GLboolean b
const GLfloat * m
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint end
GLuint object
[3]
GLfloat GLfloat f
GLenum type
GLenum target
GLuint program
GLuint GLsizei const GLchar * message
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLboolean GLboolean g
GLuint name
GLenum func
Definition qopenglext.h:663
GLuint64EXT * result
[6]
static void writeStr(uchar **p, const QByteArray &str)
static const int FileScopeCodeIndex
static bool isStrict(const QmlIR::Document *doc)
static const char * funcHeaderCode
static QSet< QString > getIllegalNames()
static bool checkArgumentsObjectUseInSignalHandlers(const QmlIR::Document &doc, QQmlJSCompileError *error)
static void annotateListElements(QmlIR::Document *document)
bool qCompileQmlFile(const QString &inputFileName, QQmlJSSaveFunction saveFunction, QQmlJSAotCompiler *aotCompiler, QQmlJSCompileError *error, bool storeSourceLocation, QV4::Compiler::CodegenWarningInterface *interface, const QString *fileContents)
static QString diagnosticErrorMessage(const QString &fileName, const QQmlJS::DiagnosticMessage &m)
static const char * wrapCallCode
bool qSaveQmlJSUnitAsCpp(const QString &inputFileName, const QString &outputFileName, const QV4::CompiledData::SaveableUnitPointer &unit, const QQmlJSAotFunctionMap &aotFunctions, QString *errorString)
bool qCompileJSFile(const QString &inputFileName, const QString &inputFileUrl, QQmlJSSaveFunction saveFunction, QQmlJSCompileError *error)
std::function< bool(const QV4::CompiledData::SaveableUnitPointer &, const QQmlJSAotFunctionMap &, QString *)> QQmlJSSaveFunction
QString qQmlJSSymbolNamespaceForPath(const QString &relativePath)
const QQmlJS::LoggerWarningId qmlCompiler
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
SSL_CTX int(*) void arg)
#define qPrintable(string)
Definition qstring.h:1391
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:45
unsigned char uchar
Definition qtypes.h:27
if(qFloatDistance(a, b)<(1<< 7))
[0]
QRandomGenerator generator(sseq)
QUrl url("example.com")
[constructor-url-reference]
QJSValue global
QJSEngine engine
[0]
\inmodule QtCore \reentrant
Definition qchar.h:17
QQmlJSCompileError augment(const QString &contextErrorMessage) const
void appendDiagnostic(const QString &inputFileName, const QQmlJS::DiagnosticMessage &diagnostic)
void appendDiagnostics(const QString &inputFileName, const QList< QQmlJS::DiagnosticMessage > &diagnostics)
union QV4::CompiledData::Binding::@543 value
QHash< QQmlJS::AST::Node *, Context * > contextMap
QList< Context * > functions
QQmlJS::AST::UiProgram * program
QVector< Object * > objects
QString stringAt(int index) const
QV4::Compiler::Module jsModule
QV4::CompiledData::CompilationUnit javaScriptCompilationUnit
QV4::Compiler::JSUnitGenerator jsGenerator
QList< Pragma * > pragmas
QQmlJS::Engine jsParserEngine
bool generateFromQml(const QString &code, const QString &url, Document *output)
QList< QQmlJS::DiagnosticMessage > errors
bool generateRuntimeFunctions(QmlIR::Object *object)
QV4::CompiledData::Location location