186 QQmlJSCompileError *error,
bool storeSourceLocation,
187 QV4::Compiler::CodegenWarningInterface *wInterface,
const QString *fileContents)
191 if (fileContents !=
nullptr) {
192 sourceCode = *fileContents;
194 QFile f(inputFileName);
195 if (!f.open(QIODevice::ReadOnly)) {
196 error->message = QLatin1String(
"Error opening ") + inputFileName + QLatin1Char(
':') + f.errorString();
199 sourceCode = QString::fromUtf8(f.readAll());
200 if (f.error() != QFileDevice::NoError) {
201 error->message = QLatin1String(
"Error reading from ") + inputFileName + QLatin1Char(
':') + f.errorString();
208 std::unique_ptr<QmlIR::IRBuilder> irBuilder = aotCompiler && aotCompiler->isLintCompiler()
209 ? std::make_unique<QQmlJSAOTIRBuilder>() : std::make_unique<QmlIR::IRBuilder>();
210 if (!irBuilder->generateFromQml(sourceCode, inputFileName, &irDocument)) {
211 error->appendDiagnostics(inputFileName, irBuilder->errors);
216 annotateListElements(&irDocument);
217 QQmlJSAotFunctionMap aotFunctionsByIndex;
220 QmlIR::JSCodeGen v4CodeGen(&irDocument, wInterface, storeSourceLocation);
223 aotCompiler->setDocument(&v4CodeGen, &irDocument);
225 QHash<QmlIR::Object *, QmlIR::Object *> effectiveScopes;
226 for (QmlIR::Object *object: std::as_const(irDocument.objects)) {
227 if (object->functionsAndExpressions->count == 0 && object->bindingCount() == 0)
230 if (!v4CodeGen.generateRuntimeFunctions(object)) {
231 Q_ASSERT(v4CodeGen.hasError());
232 error->appendDiagnostic(inputFileName, v4CodeGen.error());
239 QmlIR::Object *scope = object;
240 for (
auto it = effectiveScopes.constFind(scope), end = effectiveScopes.constEnd();
241 it != end; it = effectiveScopes.constFind(scope)) {
245 aotCompiler->setScope(object, scope);
246 aotFunctionsByIndex[FileScopeCodeIndex] = aotCompiler->globalCode();
248 std::vector<BindingOrFunction> bindingsAndFunctions;
249 bindingsAndFunctions.reserve(object->bindingCount() + object->functionCount());
251 std::copy(object->bindingsBegin(), object->bindingsEnd(),
252 std::back_inserter(bindingsAndFunctions));
253 std::copy(object->functionsBegin(), object->functionsEnd(),
254 std::back_inserter(bindingsAndFunctions));
256 QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile;
257 for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first;
258 foe; foe = foe->next) {
259 functionsToCompile << *foe;
264 auto contextMap = v4CodeGen.module()->contextMap;
265 std::sort(bindingsAndFunctions.begin(), bindingsAndFunctions.end());
266 std::for_each(bindingsAndFunctions.begin(), bindingsAndFunctions.end(),
267 [&](
const BindingOrFunction &bindingOrFunction) {
268 std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>> result;
269 if (
const auto *binding = bindingOrFunction.binding()) {
270 switch (binding->type()) {
271 case QmlIR::Binding::Type_AttachedProperty:
272 case QmlIR::Binding::Type_GroupProperty:
273 effectiveScopes.insert(
274 irDocument.objects.at(binding->value.objectIndex), scope);
276 case QmlIR::Binding::Type_Boolean:
277 case QmlIR::Binding::Type_Number:
278 case QmlIR::Binding::Type_String:
279 case QmlIR::Binding::Type_Null:
280 case QmlIR::Binding::Type_Object:
281 case QmlIR::Binding::Type_Translation:
282 case QmlIR::Binding::Type_TranslationById:
288 Q_ASSERT(quint32(functionsToCompile.size()) > binding->value.compiledScriptIndex);
289 const auto &functionToCompile
290 = functionsToCompile[binding->value.compiledScriptIndex];
291 auto *parentNode = functionToCompile.parentNode;
292 Q_ASSERT(parentNode);
293 Q_ASSERT(contextMap.contains(parentNode));
294 QV4::Compiler::Context *context = contextMap.take(parentNode);
297 auto *node = functionToCompile.node;
300 if (context->returnsClosure) {
301 QQmlJS::AST::Node *inner
302 = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(
305 QV4::Compiler::Context *innerContext = contextMap.take(inner);
306 Q_ASSERT(innerContext);
307 qCDebug(lcAotCompiler) <<
"Compiling signal handler for"
308 << irDocument.stringAt(binding->propertyNameIndex);
309 std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>> innerResult
310 = aotCompiler->compileBinding(innerContext, *binding, inner);
311 if (
auto *errors = std::get_if<QList<QQmlJS::DiagnosticMessage>>(&innerResult)) {
312 for (
const auto &error : std::as_const(*errors)) {
313 qCDebug(lcAotCompiler) <<
"Compilation failed:"
314 << diagnosticErrorMessage(inputFileName, error);
316 }
else if (
auto *func = std::get_if<QQmlJSAotFunction>(&innerResult)) {
317 qCDebug(lcAotCompiler) <<
"Generated code:" << func->code;
318 aotFunctionsByIndex[innerContext->functionIndex] = *func;
322 qCDebug(lcAotCompiler) <<
"Compiling binding for property"
323 << irDocument.stringAt(binding->propertyNameIndex);
324 result = aotCompiler->compileBinding(context, *binding, node);
325 }
else if (
const auto *function = bindingOrFunction.function()) {
326 if (!aotCompiler->isLintCompiler() && !function->isQmlFunction)
329 Q_ASSERT(quint32(functionsToCompile.size()) > function->index);
330 auto *node = functionsToCompile[function->index].node;
332 Q_ASSERT(contextMap.contains(node));
333 QV4::Compiler::Context *context = contextMap.take(node);
336 const QString functionName = irDocument.stringAt(function->nameIndex);
337 qCDebug(lcAotCompiler) <<
"Compiling function" << functionName;
338 result = aotCompiler->compileFunction(context, functionName, node);
343 if (
auto *errors = std::get_if<QList<QQmlJS::DiagnosticMessage>>(&result)) {
344 for (
const auto &error : std::as_const(*errors)) {
345 qCDebug(lcAotCompiler) <<
"Compilation failed:"
346 << diagnosticErrorMessage(inputFileName, error);
348 }
else if (
auto *func = std::get_if<QQmlJSAotFunction>(&result)) {
349 if (func->skipReason.has_value()) {
350 qCDebug(lcAotCompiler) <<
"Compilation skipped:" << func->skipReason.value();
352 qCDebug(lcAotCompiler) <<
"Generated code:" << func->code;
353 auto index = object->runtimeFunctionIndices[bindingOrFunction.index()];
354 aotFunctionsByIndex[index] = *func;
360 if (!checkArgumentsObjectUseInSignalHandlers(irDocument, error)) {
361 *error = error->augment(inputFileName);
365 QmlIR::QmlUnitGenerator generator;
366 irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(
false);
367 generator.generate(irDocument);
369 const quint32 saveFlags
370 = QV4::CompiledData::Unit::StaticData
371 | QV4::CompiledData::Unit::PendingTypeCompilation;
372 QV4::CompiledData::SaveableUnitPointer saveable(
373 irDocument.javaScriptCompilationUnit->unitData(), saveFlags);
374 if (!saveFunction(saveable, aotFunctionsByIndex, &error->message))
381 const QString &inputFileName,
const QString &inputFileUrl,
384 Q_UNUSED(inputFileUrl);
386 QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit;
390 QFile f(inputFileName);
391 if (!f.open(QIODevice::ReadOnly)) {
392 error->message = QLatin1String(
"Error opening ") + inputFileName + QLatin1Char(
':') + f.errorString();
395 sourceCode = QString::fromUtf8(f.readAll());
396 if (f.error() != QFileDevice::NoError) {
397 error->message = QLatin1String(
"Error reading from ") + inputFileName + QLatin1Char(
':') + f.errorString();
402 const bool isModule = inputFileName.endsWith(QLatin1String(
".mjs"));
404 QList<QQmlJS::DiagnosticMessage> diagnostics;
407 unit = QV4::Compiler::Codegen::compileModule(
false, url, sourceCode,
408 QDateTime(), &diagnostics);
409 error->appendDiagnostics(inputFileName, diagnostics);
410 if (!unit || !unit->unitData())
413 QmlIR::Document irDocument(QString(), QString(),
false);
415 QQmlJS::Engine *engine = &irDocument.jsParserEngine;
416 QmlIR::ScriptDirectivesCollector directivesCollector(&irDocument);
417 QQmlJS::Directives *oldDirs = engine->directives();
418 engine->setDirectives(&directivesCollector);
419 auto directivesGuard = qScopeGuard([engine, oldDirs]{
420 engine->setDirectives(oldDirs);
423 QQmlJS::AST::Program *program =
nullptr;
426 QQmlJS::Lexer lexer(engine);
427 lexer.setCode(sourceCode, 1,
false);
428 QQmlJS::Parser parser(engine);
430 bool parsed = parser.parseProgram();
432 error->appendDiagnostics(inputFileName, parser.diagnosticMessages());
437 program = QQmlJS::AST::cast<QQmlJS::AST::Program*>(parser.rootNode());
439 lexer.setCode(QStringLiteral(
"undefined;"), 1,
false);
440 parsed = parser.parseProgram();
442 program = QQmlJS::AST::cast<QQmlJS::AST::Program*>(parser.rootNode());
448 QmlIR::JSCodeGen v4CodeGen(&irDocument);
449 v4CodeGen.generateFromProgram(
450 sourceCode, program, &irDocument.jsModule,
451 QV4::Compiler::ContextType::ScriptImportedByQML);
452 if (v4CodeGen.hasError()) {
453 error->appendDiagnostic(inputFileName, v4CodeGen.error());
458 Q_ASSERT(irDocument.jsModule.fileName.isEmpty());
459 Q_ASSERT(irDocument.jsModule.finalUrl.isEmpty());
461 irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(
false);
462 QmlIR::QmlUnitGenerator generator;
463 generator.generate(irDocument);
464 unit = std::move(irDocument.javaScriptCompilationUnit);
468 QQmlJSAotFunctionMap empty;
470 QV4::CompiledData::SaveableUnitPointer(unit->unitData()), empty, &error->message);
479bool qSaveQmlJSUnitAsCpp(
const QString &inputFileName,
const QString &outputFileName,
const QV4::CompiledData::SaveableUnitPointer &unit,
const QQmlJSAotFunctionMap &aotFunctions, QString *errorString)
481#if QT_CONFIG(temporaryfile)
482 QSaveFile f(outputFileName);
484 QFile f(outputFileName);
486 if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
487 *errorString = f.errorString();
491 auto writeStr = [&f, errorString](
const QByteArray &data) {
492 if (f.write(data) != data.size()) {
493 *errorString = f.errorString();
499 if (!writeStr(
"// "))
502 if (!writeStr(inputFileName.toUtf8()))
508 if (!writeStr(
"#include <QtQml/qqmlprivate.h>\n"))
511 if (!aotFunctions.isEmpty()) {
512 QStringList includes;
514 for (
const auto &function : aotFunctions)
515 includes.append(function.includes);
517 std::sort(includes.begin(), includes.end());
518 const auto end = std::unique(includes.begin(), includes.end());
519 for (
auto it = includes.begin(); it != end; ++it) {
520 if (!writeStr(QStringLiteral(
"#include <%1>\n").arg(*it).toUtf8()))
525 if (!writeStr(QByteArrayLiteral(
"namespace QmlCacheGeneratedCode {\nnamespace ")))
528 if (!writeStr(qQmlJSSymbolNamespaceForPath(inputFileName).toUtf8()))
531 if (!writeStr(QByteArrayLiteral(
" {\nextern const unsigned char qmlData alignas(16) [];\n"
532 "extern const unsigned char qmlData alignas(16) [] = {\n")))
535 unit.saveToDisk<uchar>([&writeStr](
const uchar *begin, quint32 size) {
536 QByteArray hexifiedData;
538 QTextStream stream(&hexifiedData);
539 const uchar *end = begin + size;
542 for (
const uchar *data = begin; data < end; ++data, ++col) {
549 stream <<
"0x" << *data;
553 return writeStr(hexifiedData);
558 if (!writeStr(
"};\n"))
566 if (!writeStr(
"QT_WARNING_PUSH\nQT_WARNING_DISABLE_MSVC(4573)\n"))
570 if (aotFunctions.size() <= 1) {
572 writeStr(
"extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
573 "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = { { 0, 0, nullptr, nullptr } };\n");
575 writeStr(
"extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
576 "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = {\n");
578 QString footer = QStringLiteral(
"}\n");
580 for (QQmlJSAotFunctionMap::ConstIterator func = aotFunctions.constBegin(),
581 end = aotFunctions.constEnd();
582 func != end; ++func) {
584 if (func.key() == FileScopeCodeIndex)
587 const QString function = QString::fromUtf8(funcHeaderCode) + func.value().code + footer;
589 writeStr(QStringLiteral(
"{ %1, %2, [](QV4::ExecutableCompilationUnit *contextUnit, "
590 "QMetaType *argTypes) {\n%3}, %4 },")
592 .arg(func->numArguments)
593 .arg(func->signature, function)
594 .toUtf8().constData());
598 writeStr(
"{ 0, 0, nullptr, nullptr }");
602 if (!writeStr(
"QT_WARNING_POP\n"))
605 if (!writeStr(
"}\n}\n"))
608#if QT_CONFIG(temporaryfile)
610 *errorString = f.errorString();