185 QQmlJSCompileError *error,
bool storeSourceLocation,
186 QV4::Compiler::CodegenWarningInterface *wInterface,
const QString *fileContents)
190 if (fileContents !=
nullptr) {
191 sourceCode = *fileContents;
193 QFile f(inputFileName);
194 if (!f.open(QIODevice::ReadOnly)) {
195 error->message = QLatin1String(
"Error opening ") + inputFileName + QLatin1Char(
':') + f.errorString();
198 sourceCode = QString::fromUtf8(f.readAll());
199 if (f.error() != QFileDevice::NoError) {
200 error->message = QLatin1String(
"Error reading from ") + inputFileName + QLatin1Char(
':') + f.errorString();
206 QmlIR::IRBuilder irBuilder;
207 if (!irBuilder.generateFromQml(sourceCode, inputFileName, &irDocument)) {
208 error->appendDiagnostics(inputFileName, irBuilder.errors);
213 annotateListElements(&irDocument);
214 QQmlJSAotFunctionMap aotFunctionsByIndex;
217 QmlIR::JSCodeGen v4CodeGen(&irDocument, wInterface, storeSourceLocation);
220 aotCompiler->setDocument(&v4CodeGen, &irDocument);
222 QHash<QmlIR::Object *, QmlIR::Object *> effectiveScopes;
223 for (QmlIR::Object *object: std::as_const(irDocument.objects)) {
224 if (object->functionsAndExpressions->count == 0 && object->bindingCount() == 0)
227 if (!v4CodeGen.generateRuntimeFunctions(object)) {
228 Q_ASSERT(v4CodeGen.hasError());
229 error->appendDiagnostic(inputFileName, v4CodeGen.error());
236 QmlIR::Object *scope = object;
237 for (
auto it = effectiveScopes.constFind(scope), end = effectiveScopes.constEnd();
238 it != end; it = effectiveScopes.constFind(scope)) {
242 aotCompiler->setScope(object, scope);
243 aotFunctionsByIndex[FileScopeCodeIndex] = aotCompiler->globalCode();
245 std::vector<BindingOrFunction> bindingsAndFunctions;
246 bindingsAndFunctions.reserve(object->bindingCount() + object->functionCount());
248 std::copy(object->bindingsBegin(), object->bindingsEnd(),
249 std::back_inserter(bindingsAndFunctions));
250 std::copy(object->functionsBegin(), object->functionsEnd(),
251 std::back_inserter(bindingsAndFunctions));
253 QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile;
254 for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first;
255 foe; foe = foe->next) {
256 functionsToCompile << *foe;
261 auto contextMap = v4CodeGen.module()->contextMap;
262 std::sort(bindingsAndFunctions.begin(), bindingsAndFunctions.end());
263 std::for_each(bindingsAndFunctions.begin(), bindingsAndFunctions.end(),
264 [&](
const BindingOrFunction &bindingOrFunction) {
265 std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>> result;
266 if (
const auto *binding = bindingOrFunction.binding()) {
267 switch (binding->type()) {
268 case QmlIR::Binding::Type_AttachedProperty:
269 case QmlIR::Binding::Type_GroupProperty:
270 effectiveScopes.insert(
271 irDocument.objects.at(binding->value.objectIndex), scope);
273 case QmlIR::Binding::Type_Boolean:
274 case QmlIR::Binding::Type_Number:
275 case QmlIR::Binding::Type_String:
276 case QmlIR::Binding::Type_Null:
277 case QmlIR::Binding::Type_Object:
278 case QmlIR::Binding::Type_Translation:
279 case QmlIR::Binding::Type_TranslationById:
285 Q_ASSERT(quint32(functionsToCompile.size()) > binding->value.compiledScriptIndex);
286 const auto &functionToCompile
287 = functionsToCompile[binding->value.compiledScriptIndex];
288 auto *parentNode = functionToCompile.parentNode;
289 Q_ASSERT(parentNode);
290 Q_ASSERT(contextMap.contains(parentNode));
291 QV4::Compiler::Context *context = contextMap.take(parentNode);
294 auto *node = functionToCompile.node;
297 if (context->returnsClosure) {
298 QQmlJS::AST::Node *inner
299 = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(
302 QV4::Compiler::Context *innerContext = contextMap.take(inner);
303 Q_ASSERT(innerContext);
304 qCDebug(lcAotCompiler) <<
"Compiling signal handler for"
305 << irDocument.stringAt(binding->propertyNameIndex);
306 std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>> innerResult
307 = aotCompiler->compileBinding(innerContext, *binding, inner);
308 if (
auto *errors = std::get_if<QList<QQmlJS::DiagnosticMessage>>(&innerResult)) {
309 for (
const auto &error : std::as_const(*errors)) {
310 qCDebug(lcAotCompiler) <<
"Compilation failed:"
311 << diagnosticErrorMessage(inputFileName, error);
313 }
else if (
auto *func = std::get_if<QQmlJSAotFunction>(&innerResult)) {
314 qCDebug(lcAotCompiler) <<
"Generated code:" << func->code;
315 aotFunctionsByIndex[innerContext->functionIndex] = *func;
319 qCDebug(lcAotCompiler) <<
"Compiling binding for property"
320 << irDocument.stringAt(binding->propertyNameIndex);
321 result = aotCompiler->compileBinding(context, *binding, node);
322 }
else if (
const auto *function = bindingOrFunction.function()) {
323 Q_ASSERT(quint32(functionsToCompile.size()) > function->index);
324 auto *node = functionsToCompile[function->index].node;
326 Q_ASSERT(contextMap.contains(node));
327 QV4::Compiler::Context *context = contextMap.take(node);
330 const QString functionName = irDocument.stringAt(function->nameIndex);
331 qCDebug(lcAotCompiler) <<
"Compiling function" << functionName;
332 result = aotCompiler->compileFunction(context, functionName, node);
337 if (
auto *errors = std::get_if<QList<QQmlJS::DiagnosticMessage>>(&result)) {
338 for (
const auto &error : std::as_const(*errors)) {
339 qCDebug(lcAotCompiler) <<
"Compilation failed:"
340 << diagnosticErrorMessage(inputFileName, error);
342 }
else if (
auto *func = std::get_if<QQmlJSAotFunction>(&result)) {
343 if (func->skipReason.has_value()) {
344 qCDebug(lcAotCompiler) <<
"Compilation skipped:" << func->skipReason.value();
346 qCDebug(lcAotCompiler) <<
"Generated code:" << func->code;
347 auto index = object->runtimeFunctionIndices[bindingOrFunction.index()];
348 aotFunctionsByIndex[index] = *func;
354 if (!checkArgumentsObjectUseInSignalHandlers(irDocument, error)) {
355 *error = error->augment(inputFileName);
359 QmlIR::QmlUnitGenerator generator;
360 irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(
false);
361 generator.generate(irDocument);
363 const quint32 saveFlags
364 = QV4::CompiledData::Unit::StaticData
365 | QV4::CompiledData::Unit::PendingTypeCompilation;
366 QV4::CompiledData::SaveableUnitPointer saveable(
367 irDocument.javaScriptCompilationUnit->unitData(), saveFlags);
368 if (!saveFunction(saveable, aotFunctionsByIndex, &error->message))
375 const QString &inputFileName,
const QString &inputFileUrl,
378 Q_UNUSED(inputFileUrl);
380 QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit;
384 QFile f(inputFileName);
385 if (!f.open(QIODevice::ReadOnly)) {
386 error->message = QLatin1String(
"Error opening ") + inputFileName + QLatin1Char(
':') + f.errorString();
389 sourceCode = QString::fromUtf8(f.readAll());
390 if (f.error() != QFileDevice::NoError) {
391 error->message = QLatin1String(
"Error reading from ") + inputFileName + QLatin1Char(
':') + f.errorString();
396 const bool isModule = inputFileName.endsWith(QLatin1String(
".mjs"));
398 QList<QQmlJS::DiagnosticMessage> diagnostics;
401 unit = QV4::Compiler::Codegen::compileModule(
false, url, sourceCode,
402 QDateTime(), &diagnostics);
403 error->appendDiagnostics(inputFileName, diagnostics);
404 if (!unit || !unit->unitData())
407 QmlIR::Document irDocument(QString(), QString(),
false);
409 QQmlJS::Engine *engine = &irDocument.jsParserEngine;
410 QmlIR::ScriptDirectivesCollector directivesCollector(&irDocument);
411 QQmlJS::Directives *oldDirs = engine->directives();
412 engine->setDirectives(&directivesCollector);
413 auto directivesGuard = qScopeGuard([engine, oldDirs]{
414 engine->setDirectives(oldDirs);
417 QQmlJS::AST::Program *program =
nullptr;
420 QQmlJS::Lexer lexer(engine);
421 lexer.setCode(sourceCode, 1,
false);
422 QQmlJS::Parser parser(engine);
424 bool parsed = parser.parseProgram();
426 error->appendDiagnostics(inputFileName, parser.diagnosticMessages());
431 program = QQmlJS::AST::cast<QQmlJS::AST::Program*>(parser.rootNode());
433 lexer.setCode(QStringLiteral(
"undefined;"), 1,
false);
434 parsed = parser.parseProgram();
436 program = QQmlJS::AST::cast<QQmlJS::AST::Program*>(parser.rootNode());
442 QmlIR::JSCodeGen v4CodeGen(&irDocument);
443 v4CodeGen.generateFromProgram(
444 sourceCode, program, &irDocument.jsModule,
445 QV4::Compiler::ContextType::ScriptImportedByQML);
446 if (v4CodeGen.hasError()) {
447 error->appendDiagnostic(inputFileName, v4CodeGen.error());
452 Q_ASSERT(irDocument.jsModule.fileName.isEmpty());
453 Q_ASSERT(irDocument.jsModule.finalUrl.isEmpty());
455 irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(
false);
456 QmlIR::QmlUnitGenerator generator;
457 generator.generate(irDocument);
458 unit = std::move(irDocument.javaScriptCompilationUnit);
462 QQmlJSAotFunctionMap empty;
464 QV4::CompiledData::SaveableUnitPointer(unit->unitData()), empty, &error->message);
473bool qSaveQmlJSUnitAsCpp(
const QString &inputFileName,
const QString &outputFileName,
const QV4::CompiledData::SaveableUnitPointer &unit,
const QQmlJSAotFunctionMap &aotFunctions, QString *errorString)
475#if QT_CONFIG(temporaryfile)
476 QSaveFile f(outputFileName);
478 QFile f(outputFileName);
480 if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
481 *errorString = f.errorString();
485 auto writeStr = [&f, errorString](
const QByteArray &data) {
486 if (f.write(data) != data.size()) {
487 *errorString = f.errorString();
493 if (!writeStr(
"// "))
496 if (!writeStr(inputFileName.toUtf8()))
502 if (!writeStr(
"#include <QtQml/qqmlprivate.h>\n"))
505 if (!aotFunctions.isEmpty()) {
506 QStringList includes;
508 for (
const auto &function : aotFunctions)
509 includes.append(function.includes);
511 std::sort(includes.begin(), includes.end());
512 const auto end = std::unique(includes.begin(), includes.end());
513 for (
auto it = includes.begin(); it != end; ++it) {
514 if (!writeStr(QStringLiteral(
"#include <%1>\n").arg(*it).toUtf8()))
519 if (!writeStr(QByteArrayLiteral(
"namespace QmlCacheGeneratedCode {\nnamespace ")))
522 if (!writeStr(qQmlJSSymbolNamespaceForPath(inputFileName).toUtf8()))
525 if (!writeStr(QByteArrayLiteral(
" {\nextern const unsigned char qmlData alignas(16) [];\n"
526 "extern const unsigned char qmlData alignas(16) [] = {\n")))
529 unit.saveToDisk<uchar>([&writeStr](
const uchar *begin, quint32 size) {
530 QByteArray hexifiedData;
532 QTextStream stream(&hexifiedData);
533 const uchar *end = begin + size;
536 for (
const uchar *data = begin; data < end; ++data, ++col) {
543 stream <<
"0x" << *data;
547 return writeStr(hexifiedData);
552 if (!writeStr(
"};\n"))
560 if (!writeStr(
"QT_WARNING_PUSH\nQT_WARNING_DISABLE_MSVC(4573)\n"))
564 if (aotFunctions.size() <= 1) {
566 writeStr(
"extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
567 "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = { { 0, 0, nullptr, nullptr } };\n");
569 writeStr(
"extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
570 "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = {\n");
572 QString footer = QStringLiteral(
"}\n");
574 for (QQmlJSAotFunctionMap::ConstIterator func = aotFunctions.constBegin(),
575 end = aotFunctions.constEnd();
576 func != end; ++func) {
578 if (func.key() == FileScopeCodeIndex)
581 const QString function = QString::fromUtf8(funcHeaderCode) + func.value().code + footer;
583 writeStr(QStringLiteral(
"{ %1, %2, [](QV4::ExecutableCompilationUnit *contextUnit, "
584 "QMetaType *argTypes) {\n%3}, %4 },")
586 .arg(func->numArguments)
587 .arg(func->signature, function)
588 .toUtf8().constData());
592 writeStr(
"{ 0, 0, nullptr, nullptr }");
596 if (!writeStr(
"QT_WARNING_POP\n"))
599 if (!writeStr(
"}\n}\n"))
602#if QT_CONFIG(temporaryfile)
604 *errorString = f.errorString();