Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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/qqmljsoptimizations_p.h>
14#include <private/qqmljsparser_p.h>
15#include <private/qqmljsshadowcheck_p.h>
16#include <private/qqmljsstoragegeneralizer_p.h>
17#include <private/qqmljstypepropagator_p.h>
18
19#include <QtCore/qfile.h>
20#include <QtCore/qfileinfo.h>
21#include <QtCore/qloggingcategory.h>
22
23#include <QtQml/private/qqmlsignalnames_p.h>
24
25#include <limits>
26
28
29using namespace Qt::StringLiterals;
30
31Q_LOGGING_CATEGORY(lcAotCompiler, "qt.qml.compiler.aot", QtFatalMsg);
32
33static const int FileScopeCodeIndex = -1;
34
35static QSet<QString> getIllegalNames()
36{
37 QSet<QString> illegalNames;
38 for (const char **g = QV4::Compiler::Codegen::s_globalNames; *g != nullptr; ++g)
39 illegalNames.insert(QString::fromLatin1(*g));
40 return illegalNames;
41}
42
43Q_GLOBAL_STATIC_WITH_ARGS(QSet<QString>, illegalNames, (getIllegalNames()));
44
45
47{
48 fprintf(stderr, "%s\n", qPrintable(message));
49}
50
52{
53 QQmlJSCompileError augmented;
54 augmented.message = contextErrorMessage + message;
55 return augmented;
56}
57
59{
61 message = fileName + QLatin1Char(':') + QString::number(m.loc.startLine) + QLatin1Char(':');
62 if (m.loc.startColumn > 0)
63 message += QString::number(m.loc.startColumn) + QLatin1Char(':');
64
65 if (m.isError())
66 message += QLatin1String(" error: ");
67 else
68 message += QLatin1String(" warning: ");
69 message += m.message;
70 return message;
71}
72
74 const QQmlJS::DiagnosticMessage &diagnostic)
75{
76 if (!message.isEmpty())
77 message += QLatin1Char('\n');
78 message += diagnosticErrorMessage(inputFileName, diagnostic);
79}
80
82 const QList<QQmlJS::DiagnosticMessage> &diagnostics)
83{
84 for (const QQmlJS::DiagnosticMessage &diagnostic: diagnostics)
85 appendDiagnostic(inputFileName, diagnostic);
86}
87
88// Ensure that ListElement objects keep all property assignments in their string form
90{
91 QStringList listElementNames;
92
93 for (const QV4::CompiledData::Import *import : std::as_const(document->imports)) {
94 const QString uri = document->stringAt(import->uriIndex);
95 if (uri != QStringLiteral("QtQml.Models") && uri != QStringLiteral("QtQuick"))
96 continue;
97
98 QString listElementName = QStringLiteral("ListElement");
99 const QString qualifier = document->stringAt(import->qualifierIndex);
100 if (!qualifier.isEmpty()) {
101 listElementName.prepend(QLatin1Char('.'));
102 listElementName.prepend(qualifier);
103 }
104 listElementNames.append(listElementName);
105 }
106
107 if (listElementNames.isEmpty())
108 return;
109
110 for (QmlIR::Object *object : std::as_const(document->objects)) {
111 if (!listElementNames.contains(document->stringAt(object->inheritedTypeNameIndex)))
112 continue;
113 for (QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) {
114 if (binding->type() != QV4::CompiledData::Binding::Type_Script)
115 continue;
116 binding->stringIndex = document->registerString(object->bindingAsString(document, binding->value.compiledScriptIndex));
117 }
118 }
119}
120
123{
124 for (QmlIR::Object *object: std::as_const(doc.objects)) {
125 for (auto binding = object->bindingsBegin(); binding != object->bindingsEnd(); ++binding) {
126 if (binding->type() != QV4::CompiledData::Binding::Type_Script)
127 continue;
128 const QString propName = doc.stringAt(binding->propertyNameIndex);
129 if (!QQmlSignalNames::isHandlerName(propName))
130 continue;
131 auto compiledFunction = doc.jsModule.functions.value(object->runtimeFunctionIndices.at(binding->value.compiledScriptIndex));
132 if (!compiledFunction)
133 continue;
134 if (compiledFunction->usesArgumentsObject == QV4::Compiler::Context::ArgumentsObjectUsed) {
135 error->message = QLatin1Char(':') + QString::number(compiledFunction->line) + QLatin1Char(':');
136 if (compiledFunction->column > 0)
137 error->message += QString::number(compiledFunction->column) + QLatin1Char(':');
138
139 error->message += QLatin1String(" error: The use of eval() or the use of the arguments object in signal handlers is\n"
140 "not supported when compiling qml files ahead of time. That is because it's ambiguous if \n"
141 "any signal parameter is called \"arguments\". Similarly the string passed to eval might use\n"
142 "\"arguments\". Unfortunately we cannot distinguish between it being a parameter or the\n"
143 "JavaScript arguments object at this point.\n"
144 "Consider renaming the parameter of the signal if applicable or moving the code into a\n"
145 "helper function.");
146 return false;
147 }
148 }
149 }
150 return true;
151}
152
154{
155public:
156 BindingOrFunction(const QmlIR::Binding &b) : m_binding(&b) {}
157 BindingOrFunction(const QmlIR::Function &f) : m_function(&f) {}
158
159 friend bool operator<(const BindingOrFunction &lhs, const BindingOrFunction &rhs)
160 {
161 return lhs.index() < rhs.index();
162 }
163
164 const QmlIR::Binding *binding() const { return m_binding; }
165 const QmlIR::Function *function() const { return m_function; }
166
168 {
169 return m_binding
170 ? m_binding->value.compiledScriptIndex
171 : (m_function
172 ? m_function->index
173 : std::numeric_limits<quint32>::max());
174 }
175
176private:
177 const QmlIR::Binding *m_binding = nullptr;
178 const QmlIR::Function *m_function = nullptr;
179};
180
181bool qCompileQmlFile(const QString &inputFileName, QQmlJSSaveFunction saveFunction,
183 bool storeSourceLocation, QV4::Compiler::CodegenWarningInterface *interface,
184 const QString *fileContents)
185{
186 QmlIR::Document irDocument(/*debugMode*/false);
187 return qCompileQmlFile(irDocument, inputFileName, saveFunction, aotCompiler, error,
188 storeSourceLocation, interface, fileContents);
189}
190
191bool qCompileQmlFile(QmlIR::Document &irDocument, const QString &inputFileName,
192 QQmlJSSaveFunction saveFunction, QQmlJSAotCompiler *aotCompiler,
193 QQmlJSCompileError *error, bool storeSourceLocation,
195{
196 QString sourceCode;
197
198 if (fileContents != nullptr) {
199 sourceCode = *fileContents;
200 } else {
201 QFile f(inputFileName);
202 if (!f.open(QIODevice::ReadOnly)) {
203 error->message = QLatin1String("Error opening ") + inputFileName + QLatin1Char(':') + f.errorString();
204 return false;
205 }
206 sourceCode = QString::fromUtf8(f.readAll());
207 if (f.error() != QFileDevice::NoError) {
208 error->message = QLatin1String("Error reading from ") + inputFileName + QLatin1Char(':') + f.errorString();
209 return false;
210 }
211 }
212
213 {
214 QmlIR::IRBuilder irBuilder(*illegalNames());
215 if (!irBuilder.generateFromQml(sourceCode, inputFileName, &irDocument)) {
216 error->appendDiagnostics(inputFileName, irBuilder.errors);
217 return false;
218 }
219 }
220
221 annotateListElements(&irDocument);
222 QQmlJSAotFunctionMap aotFunctionsByIndex;
223
224 {
225 QmlIR::JSCodeGen v4CodeGen(&irDocument, *illegalNames(), interface, storeSourceLocation);
226
227 if (aotCompiler)
228 aotCompiler->setDocument(&v4CodeGen, &irDocument);
229
230 QHash<QmlIR::Object *, QmlIR::Object *> effectiveScopes;
231 for (QmlIR::Object *object: std::as_const(irDocument.objects)) {
232 if (object->functionsAndExpressions->count == 0 && object->bindingCount() == 0)
233 continue;
234
235 if (!v4CodeGen.generateRuntimeFunctions(object)) {
236 Q_ASSERT(v4CodeGen.hasError());
237 error->appendDiagnostic(inputFileName, v4CodeGen.error());
238 return false;
239 }
240
241 if (!aotCompiler)
242 continue;
243
244 QmlIR::Object *scope = object;
245 for (auto it = effectiveScopes.constFind(scope), end = effectiveScopes.constEnd();
246 it != end; it = effectiveScopes.constFind(scope)) {
247 scope = *it;
248 }
249
250 aotCompiler->setScope(object, scope);
251 aotFunctionsByIndex[FileScopeCodeIndex] = aotCompiler->globalCode();
252
253 std::vector<BindingOrFunction> bindingsAndFunctions;
254 bindingsAndFunctions.reserve(object->bindingCount() + object->functionCount());
255
256 std::copy(object->bindingsBegin(), object->bindingsEnd(),
257 std::back_inserter(bindingsAndFunctions));
258 std::copy(object->functionsBegin(), object->functionsEnd(),
259 std::back_inserter(bindingsAndFunctions));
260
261 QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile;
262 for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first;
263 foe; foe = foe->next) {
264 functionsToCompile << *foe;
265 }
266
267 // AOT-compile bindings and functions in the same order as above so that the runtime
268 // class indices match
269 auto contextMap = v4CodeGen.module()->contextMap;
270 std::sort(bindingsAndFunctions.begin(), bindingsAndFunctions.end());
271 std::for_each(bindingsAndFunctions.begin(), bindingsAndFunctions.end(),
272 [&](const BindingOrFunction &bindingOrFunction) {
273 std::variant<QQmlJSAotFunction, QQmlJS::DiagnosticMessage> result;
274 if (const auto *binding = bindingOrFunction.binding()) {
275 switch (binding->type()) {
276 case QmlIR::Binding::Type_AttachedProperty:
277 case QmlIR::Binding::Type_GroupProperty:
278 effectiveScopes.insert(
279 irDocument.objects.at(binding->value.objectIndex), scope);
280 return;
281 case QmlIR::Binding::Type_Boolean:
282 case QmlIR::Binding::Type_Number:
283 case QmlIR::Binding::Type_String:
284 case QmlIR::Binding::Type_Null:
285 case QmlIR::Binding::Type_Object:
286 case QmlIR::Binding::Type_Translation:
287 case QmlIR::Binding::Type_TranslationById:
288 return;
289 default:
290 break;
291 }
292
293 Q_ASSERT(quint32(functionsToCompile.size()) > binding->value.compiledScriptIndex);
294 const auto &functionToCompile
295 = functionsToCompile[binding->value.compiledScriptIndex];
296 auto *parentNode = functionToCompile.parentNode;
297 Q_ASSERT(parentNode);
298 Q_ASSERT(contextMap.contains(parentNode));
299 QV4::Compiler::Context *context = contextMap.take(parentNode);
300 Q_ASSERT(context);
301
302 auto *node = functionToCompile.node;
303 Q_ASSERT(node);
304
305 if (context->returnsClosure) {
306 QQmlJS::AST::Node *inner
307 = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(
308 node)->expression;
309 Q_ASSERT(inner);
310 QV4::Compiler::Context *innerContext = contextMap.take(inner);
311 Q_ASSERT(innerContext);
312 qCDebug(lcAotCompiler) << "Compiling signal handler for"
313 << irDocument.stringAt(binding->propertyNameIndex);
314 std::variant<QQmlJSAotFunction, QQmlJS::DiagnosticMessage> innerResult
315 = aotCompiler->compileBinding(innerContext, *binding, inner);
316
317 if (auto *error = std::get_if<QQmlJS::DiagnosticMessage>(&innerResult)) {
318 qCDebug(lcAotCompiler) << "Compilation failed:"
319 << diagnosticErrorMessage(inputFileName, *error);
320 } else if (auto *func = std::get_if<QQmlJSAotFunction>(&innerResult)) {
321 qCDebug(lcAotCompiler) << "Generated code:" << func->code;
322 aotFunctionsByIndex[innerContext->functionIndex] = *func;
323 }
324 }
325
326 qCDebug(lcAotCompiler) << "Compiling binding for property"
327 << irDocument.stringAt(binding->propertyNameIndex);
328 result = aotCompiler->compileBinding(context, *binding, node);
329 } else if (const auto *function = bindingOrFunction.function()) {
330 Q_ASSERT(quint32(functionsToCompile.size()) > function->index);
331 auto *node = functionsToCompile[function->index].node;
332 Q_ASSERT(node);
333 Q_ASSERT(contextMap.contains(node));
334 QV4::Compiler::Context *context = contextMap.take(node);
335 Q_ASSERT(context);
336
337 const QString functionName = irDocument.stringAt(function->nameIndex);
338 qCDebug(lcAotCompiler) << "Compiling function" << functionName;
339 result = aotCompiler->compileFunction(context, functionName, node);
340 } else {
341 Q_UNREACHABLE();
342 }
343
344 if (auto *error = std::get_if<QQmlJS::DiagnosticMessage>(&result)) {
345 qCDebug(lcAotCompiler) << "Compilation failed:"
346 << diagnosticErrorMessage(inputFileName, *error);
347 } else if (auto *func = std::get_if<QQmlJSAotFunction>(&result)) {
348 qCDebug(lcAotCompiler) << "Generated code:" << func->code;
349 aotFunctionsByIndex[object->runtimeFunctionIndices[bindingOrFunction.index()]] =
350 *func;
351 }
352 });
353 }
354
356 *error = error->augment(inputFileName);
357 return false;
358 }
359
361 irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(/*generate unit*/false);
362 generator.generate(irDocument);
363
364 const quint32 saveFlags
368 irDocument.javaScriptCompilationUnit->unitData(), saveFlags);
369 if (!saveFunction(saveable, aotFunctionsByIndex, &error->message))
370 return false;
371 }
372 return true;
373}
374
375bool qCompileJSFile(const QString &inputFileName, const QString &inputFileUrl, QQmlJSSaveFunction saveFunction, QQmlJSCompileError *error)
376{
377 QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit;
378
379 QString sourceCode;
380 {
381 QFile f(inputFileName);
382 if (!f.open(QIODevice::ReadOnly)) {
383 error->message = QLatin1String("Error opening ") + inputFileName + QLatin1Char(':') + f.errorString();
384 return false;
385 }
386 sourceCode = QString::fromUtf8(f.readAll());
387 if (f.error() != QFileDevice::NoError) {
388 error->message = QLatin1String("Error reading from ") + inputFileName + QLatin1Char(':') + f.errorString();
389 return false;
390 }
391 }
392
393 const bool isModule = inputFileName.endsWith(QLatin1String(".mjs"));
394 if (isModule) {
395 QList<QQmlJS::DiagnosticMessage> diagnostics;
396 // Precompiled files are relocatable and the final location will be set when loading.
397 QString url;
398 unit = QV4::Compiler::Codegen::compileModule(/*debugMode*/false, url, sourceCode,
399 QDateTime(), &diagnostics);
400 error->appendDiagnostics(inputFileName, diagnostics);
401 if (!unit || !unit->unitData())
402 return false;
403 } else {
404 QmlIR::Document irDocument(/*debugMode*/false);
405
406 QQmlJS::Engine *engine = &irDocument.jsParserEngine;
407 QmlIR::ScriptDirectivesCollector directivesCollector(&irDocument);
408 QQmlJS::Directives *oldDirs = engine->directives();
409 engine->setDirectives(&directivesCollector);
410 auto directivesGuard = qScopeGuard([engine, oldDirs]{
411 engine->setDirectives(oldDirs);
412 });
413
414 QQmlJS::AST::Program *program = nullptr;
415
416 {
417 QQmlJS::Lexer lexer(engine);
418 lexer.setCode(sourceCode, /*line*/1, /*parseAsBinding*/false);
419 QQmlJS::Parser parser(engine);
420
421 bool parsed = parser.parseProgram();
422
423 error->appendDiagnostics(inputFileName, parser.diagnosticMessages());
424
425 if (!parsed)
426 return false;
427
428 program = QQmlJS::AST::cast<QQmlJS::AST::Program*>(parser.rootNode());
429 if (!program) {
430 lexer.setCode(QStringLiteral("undefined;"), 1, false);
431 parsed = parser.parseProgram();
432 Q_ASSERT(parsed);
433 program = QQmlJS::AST::cast<QQmlJS::AST::Program*>(parser.rootNode());
435 }
436 }
437
438 {
439 QmlIR::JSCodeGen v4CodeGen(&irDocument, *illegalNames());
440 v4CodeGen.generateFromProgram(inputFileName, inputFileUrl, sourceCode, program,
442 if (v4CodeGen.hasError()) {
443 error->appendDiagnostic(inputFileName, v4CodeGen.error());
444 return false;
445 }
446
447 // Precompiled files are relocatable and the final location will be set when loading.
448 irDocument.jsModule.fileName.clear();
449 irDocument.jsModule.finalUrl.clear();
450
451 irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(/*generate unit*/false);
453 generator.generate(irDocument);
454 unit = std::move(irDocument.javaScriptCompilationUnit);
455 }
456 }
457
459 return saveFunction(
460 QV4::CompiledData::SaveableUnitPointer(unit->unitData()), empty, &error->message);
461}
462
463static const char *funcHeaderCode = R"(
464 [](const QQmlPrivate::AOTCompiledContext *aotContext, void **argv) {
465Q_UNUSED(aotContext)
466Q_UNUSED(argv)
467)";
468
469bool qSaveQmlJSUnitAsCpp(const QString &inputFileName, const QString &outputFileName, const QV4::CompiledData::SaveableUnitPointer &unit, const QQmlJSAotFunctionMap &aotFunctions, QString *errorString)
470{
471#if QT_CONFIG(temporaryfile)
472 QSaveFile f(outputFileName);
473#else
474 QFile f(outputFileName);
475#endif
477 *errorString = f.errorString();
478 return false;
479 }
480
481 auto writeStr = [&f, errorString](const QByteArray &data) {
482 if (f.write(data) != data.size()) {
483 *errorString = f.errorString();
484 return false;
485 }
486 return true;
487 };
488
489 if (!writeStr("// "))
490 return false;
491
492 if (!writeStr(inputFileName.toUtf8()))
493 return false;
494
495 if (!writeStr("\n"))
496 return false;
497
498 if (!writeStr("#include <QtQml/qqmlprivate.h>\n"))
499 return false;
500
501 if (!aotFunctions.isEmpty()) {
502 QStringList includes;
503
504 for (const auto &function : aotFunctions)
505 includes.append(function.includes);
506
507 std::sort(includes.begin(), includes.end());
508 const auto end = std::unique(includes.begin(), includes.end());
509 for (auto it = includes.begin(); it != end; ++it) {
510 if (!writeStr(QStringLiteral("#include <%1>\n").arg(*it).toUtf8()))
511 return false;
512 }
513 }
514
515 if (!writeStr(QByteArrayLiteral("namespace QmlCacheGeneratedCode {\nnamespace ")))
516 return false;
517
518 if (!writeStr(qQmlJSSymbolNamespaceForPath(inputFileName).toUtf8()))
519 return false;
520
521 if (!writeStr(QByteArrayLiteral(" {\nextern const unsigned char qmlData alignas(16) [];\n"
522 "extern const unsigned char qmlData alignas(16) [] = {\n")))
523 return false;
524
525 unit.saveToDisk<uchar>([&writeStr](const uchar *begin, quint32 size) {
526 QByteArray hexifiedData;
527 {
528 QTextStream stream(&hexifiedData);
529 const uchar *end = begin + size;
530 stream << Qt::hex;
531 int col = 0;
532 for (const uchar *data = begin; data < end; ++data, ++col) {
533 if (data > begin)
534 stream << ',';
535 if (col % 8 == 0) {
536 stream << '\n';
537 col = 0;
538 }
539 stream << "0x" << *data;
540 }
541 stream << '\n';
542 }
543 return writeStr(hexifiedData);
544 });
545
546
547
548 if (!writeStr("};\n"))
549 return false;
550
551 // Suppress the following warning generated by MSVC 2019:
552 // "the usage of 'QJSNumberCoercion::toInteger' requires the compiler to capture 'this'
553 // but the current default capture mode does not allow it"
554 // You clearly don't have to capture 'this' in order to call 'QJSNumberCoercion::toInteger'.
555 // TODO: Remove when we don't have to support MSVC 2019 anymore. Mind the QT_WARNING_POP below.
556 if (!writeStr("QT_WARNING_PUSH\nQT_WARNING_DISABLE_MSVC(4573)\n"))
557 return false;
558
559 writeStr(aotFunctions[FileScopeCodeIndex].code.toUtf8().constData());
560 if (aotFunctions.size() <= 1) {
561 // FileScopeCodeIndex is always there, but it may be the only one.
562 writeStr("extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
563 "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = { { 0, 0, nullptr, nullptr } };\n");
564 } else {
565 writeStr("extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
566 "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = {\n");
567
568 QString footer = QStringLiteral("}\n");
569
570 for (QQmlJSAotFunctionMap::ConstIterator func = aotFunctions.constBegin(),
571 end = aotFunctions.constEnd();
572 func != end; ++func) {
573
574 if (func.key() == FileScopeCodeIndex)
575 continue;
576
577 const QString function = QString::fromUtf8(funcHeaderCode) + func.value().code + footer;
578
579 writeStr(QStringLiteral("{ %1, %2, [](QV4::ExecutableCompilationUnit *unit, "
580 "QMetaType *argTypes) {\n%3}, %4 },")
581 .arg(func.key())
582 .arg(func->numArguments)
583 .arg(func->signature, function)
584 .toUtf8().constData());
585 }
586
587 // Conclude the list with a nullptr
588 writeStr("{ 0, 0, nullptr, nullptr }");
589 writeStr("};\n");
590 }
591
592 if (!writeStr("QT_WARNING_POP\n"))
593 return false;
594
595 if (!writeStr("}\n}\n"))
596 return false;
597
598#if QT_CONFIG(temporaryfile)
599 if (!f.commit()) {
600 *errorString = f.errorString();
601 return false;
602 }
603#endif
604
605 return true;
606}
607
609 QQmlJSImporter *importer, const QString &resourcePath, const QStringList &qmldirFiles,
610 QQmlJSLogger *logger)
611 : m_typeResolver(importer)
612 , m_resourcePath(resourcePath)
613 , m_qmldirFiles(qmldirFiles)
614 , m_importer(importer)
615 , m_logger(logger)
616{
617}
618
620 const QmlIR::JSCodeGen *codegen, const QmlIR::Document *irDocument)
621{
622 Q_UNUSED(codegen);
623 m_document = irDocument;
624 const QFileInfo resourcePathInfo(m_resourcePath);
625 m_logger->setFileName(resourcePathInfo.fileName());
626 m_logger->setCode(irDocument->code);
627 m_unitGenerator = &irDocument->jsGenerator;
630 resourcePathInfo.canonicalPath() + u'/',
632 m_typeResolver.init(&visitor, irDocument->program);
633}
634
636{
638 m_currentScope = scope;
639}
640
641static bool isStrict(const QmlIR::Document *doc)
642{
643 for (const QmlIR::Pragma *pragma : doc->pragmas) {
644 if (pragma->type == QmlIR::Pragma::Strict)
645 return true;
646 }
647 return false;
648}
649
652{
656 qFatal("%s:%d: (strict mode) %s",
658 location.startLine, qPrintable(message));
659 }
660
661 // TODO: this is a special place that explicitly sets the severity through
662 // logger's private function
664
666 message,
667 type,
669 };
670}
671
672std::variant<QQmlJSAotFunction, QQmlJS::DiagnosticMessage> QQmlJSAotCompiler::compileBinding(
673 const QV4::Compiler::Context *context, const QmlIR::Binding &irBinding,
674 QQmlJS::AST::Node *astNode)
675{
676 QQmlJSFunctionInitializer initializer(
679 const QString name = m_document->stringAt(irBinding.propertyNameIndex);
680 QQmlJSCompilePass::Function function = initializer.run(
681 context, name, astNode, irBinding, &error);
682 const QQmlJSAotFunction aotFunction = doCompile(context, &function, &error);
683
684 if (error.isValid()) {
685 // If it's a signal and the function just returns a closure, it's harmless.
686 // Otherwise promote the message to warning level.
687 return diagnose(error.message,
688 (function.isSignalHandler && error.type == QtDebugMsg)
689 ? QtDebugMsg
690 : QtWarningMsg,
691 error.loc);
692 }
693
694 qCDebug(lcAotCompiler()) << "includes:" << aotFunction.includes;
695 qCDebug(lcAotCompiler()) << "binding code:" << aotFunction.code;
696 return aotFunction;
697}
698
699std::variant<QQmlJSAotFunction, QQmlJS::DiagnosticMessage> QQmlJSAotCompiler::compileFunction(
701{
702 QQmlJSFunctionInitializer initializer(
705 QQmlJSCompilePass::Function function = initializer.run(context, name, astNode, &error);
706 const QQmlJSAotFunction aotFunction = doCompile(context, &function, &error);
707
708 if (error.isValid())
709 return diagnose(error.message, QtWarningMsg, error.loc);
710
711 qCDebug(lcAotCompiler()) << "includes:" << aotFunction.includes;
712 qCDebug(lcAotCompiler()) << "binding code:" << aotFunction.code;
713 return aotFunction;
714}
715
717{
719 global.includes = {
720 u"QtQml/qjsengine.h"_s,
721 u"QtQml/qjsprimitivevalue.h"_s,
722 u"QtQml/qjsvalue.h"_s,
723 u"QtQml/qqmlcomponent.h"_s,
724 u"QtQml/qqmlcontext.h"_s,
725 u"QtQml/qqmlengine.h"_s,
726 u"QtQml/qqmllist.h"_s,
727
728 u"QtCore/qdatetime.h"_s,
729 u"QtCore/qtimezone.h"_s,
730 u"QtCore/qobject.h"_s,
731 u"QtCore/qstring.h"_s,
732 u"QtCore/qstringlist.h"_s,
733 u"QtCore/qurl.h"_s,
734 u"QtCore/qvariant.h"_s,
735
736 u"type_traits"_s
737 };
738 return global;
739}
740
741QQmlJSAotFunction QQmlJSAotCompiler::doCompile(
744{
745 const auto compileError = [&]() {
746 Q_ASSERT(error->isValid());
747 error->type = context->returnsClosure ? QtDebugMsg : QtWarningMsg;
748 return QQmlJSAotFunction();
749 };
750
751 if (error->isValid())
752 return compileError();
753
754 bool basicBlocksValidationFailed = false;
756 auto passResult = basicBlocks.run(function, m_flags, basicBlocksValidationFailed);
757 auto &[blocks, annotations] = passResult;
758
759 QQmlJSTypePropagator propagator(m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
760 passResult = propagator.run(function, error);
761 if (error->isValid())
762 return compileError();
763
764 QQmlJSShadowCheck shadowCheck(m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
765 passResult = shadowCheck.run(function, error);
766 if (error->isValid())
767 return compileError();
768
769 QQmlJSOptimizations optimizer(m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations,
770 basicBlocks.objectAndArrayDefinitions());
771 passResult = optimizer.run(function, error);
772 if (error->isValid())
773 return compileError();
774
775 // Generalize all arguments, registers, and the return type.
776 QQmlJSStorageGeneralizer generalizer(m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
777 passResult = generalizer.run(function, error);
778 if (error->isValid())
779 return compileError();
780
781 QQmlJSCodeGenerator codegen(context, m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
782 QQmlJSAotFunction result = codegen.run(function, error, basicBlocksValidationFailed);
783 return error->isValid() ? compileError() : result;
784}
785
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:124
\inmodule QtCore\reentrant
Definition qdatetime.h:283
\inmodule QtCore
Definition qfile.h:93
Definition qmap.h:187
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)
static bool isHandlerName(QStringView signalName)
quint32 generate()
Generates a 32-bit random quantity and returns it.
Definition qrandom.h:48
const_iterator constFind(const T &value) const
Definition qset.h:161
void reserve(qsizetype size)
Definition qset.h:226
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
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:5506
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:8084
QByteArray toUtf8() const &
Definition qstring.h:634
\inmodule QtCore
bool saveToDisk(const std::function< bool(const Char *, quint32)> &writer) const
static const char * s_globalNames[]
static QQmlRefPointer< QV4::CompiledData::CompilationUnit > compileModule(bool debugMode, const QString &url, const QString &sourceCode, const QDateTime &sourceTimeStamp, QList< QQmlJS::DiagnosticMessage > *diagnostics)
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 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:168
#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]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLenum type
GLenum target
GLuint program
GLuint GLsizei const GLchar * message
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)
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 QQmlSA::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:1531
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:50
unsigned char uchar
Definition qtypes.h:32
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:18
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::@545 value
QList< Context * > functions
QVector< Object * > objects
QString stringAt(int index) const
QV4::Compiler::Module jsModule
QList< Pragma * > pragmas
QV4::CompiledData::Location location