187 QQmlJSCompileError *error,
bool storeSourceLocation,
188 QV4::Compiler::CodegenWarningInterface *wInterface,
const QString *fileContents)
192 if (fileContents !=
nullptr) {
193 sourceCode = *fileContents;
195 QFile f(inputFileName);
196 if (!f.open(QIODevice::ReadOnly)) {
197 error->message = QLatin1String(
"Error opening ") + inputFileName + QLatin1Char(
':') + f.errorString();
200 sourceCode = QString::fromUtf8(f.readAll());
201 if (f.error() != QFileDevice::NoError) {
202 error->message = QLatin1String(
"Error reading from ") + inputFileName + QLatin1Char(
':') + f.errorString();
209 std::unique_ptr<QmlIR::IRBuilder> irBuilder = aotCompiler && aotCompiler->isLintCompiler()
210 ? std::make_unique<QQmlJSAOTIRBuilder>() : std::make_unique<QmlIR::IRBuilder>();
211 if (!irBuilder->generateFromQml(sourceCode, inputFileName, &irDocument)) {
212 error->appendDiagnostics(inputFileName, irBuilder->errors);
217 annotateListElements(&irDocument);
218 QQmlJSAotFunctionMap aotFunctionsByIndex;
221 QmlIR::JSCodeGen v4CodeGen(&irDocument, wInterface, storeSourceLocation);
224 aotCompiler->setDocument(&v4CodeGen, &irDocument);
226 QHash<QmlIR::Object *, QmlIR::Object *> effectiveScopes;
227 for (QmlIR::Object *object: std::as_const(irDocument.objects)) {
228 if (object->functionsAndExpressions->count == 0 && object->bindingCount() == 0)
231 if (!v4CodeGen.generateRuntimeFunctions(object)) {
232 Q_ASSERT(v4CodeGen.hasError());
233 error->appendDiagnostic(inputFileName, v4CodeGen.error());
240 QmlIR::Object *scope = object;
241 for (
auto it = effectiveScopes.constFind(scope), end = effectiveScopes.constEnd();
242 it != end; it = effectiveScopes.constFind(scope)) {
246 aotCompiler->setScope(object, scope);
247 aotFunctionsByIndex[FileScopeCodeIndex] = aotCompiler->globalCode();
249 std::vector<BindingOrFunction> bindingsAndFunctions;
250 bindingsAndFunctions.reserve(object->bindingCount() + object->functionCount());
252 std::copy(object->bindingsBegin(), object->bindingsEnd(),
253 std::back_inserter(bindingsAndFunctions));
254 std::copy(object->functionsBegin(), object->functionsEnd(),
255 std::back_inserter(bindingsAndFunctions));
257 QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile;
258 for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first;
259 foe; foe = foe->next) {
260 functionsToCompile << *foe;
265 auto contextMap = v4CodeGen.module()->contextMap;
266 std::sort(bindingsAndFunctions.begin(), bindingsAndFunctions.end());
267 std::for_each(bindingsAndFunctions.begin(), bindingsAndFunctions.end(),
268 [&](
const BindingOrFunction &bindingOrFunction) {
269 std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>> result;
270 if (
const auto *binding = bindingOrFunction.binding()) {
271 switch (binding->type()) {
272 case QmlIR::Binding::Type_AttachedProperty:
273 case QmlIR::Binding::Type_GroupProperty:
274 effectiveScopes.insert(
275 irDocument.objects.at(binding->value.objectIndex), scope);
277 case QmlIR::Binding::Type_Boolean:
278 case QmlIR::Binding::Type_Number:
279 case QmlIR::Binding::Type_String:
280 case QmlIR::Binding::Type_Null:
281 case QmlIR::Binding::Type_Object:
282 case QmlIR::Binding::Type_Translation:
283 case QmlIR::Binding::Type_TranslationById:
289 Q_ASSERT(quint32(functionsToCompile.size()) > binding->value.compiledScriptIndex);
290 const auto &functionToCompile
291 = functionsToCompile[binding->value.compiledScriptIndex];
292 auto *parentNode = functionToCompile.parentNode;
293 Q_ASSERT(parentNode);
294 Q_ASSERT(contextMap.contains(parentNode));
295 QV4::Compiler::Context *context = contextMap.take(parentNode);
298 auto *node = functionToCompile.node;
301 if (context->returnsClosure) {
302 QQmlJS::AST::Node *inner
303 = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(
306 QV4::Compiler::Context *innerContext = contextMap.take(inner);
307 Q_ASSERT(innerContext);
308 qCDebug(lcAotCompiler) <<
"Compiling signal handler for"
309 << irDocument.stringAt(binding->propertyNameIndex);
310 std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>> innerResult
311 = aotCompiler->compileBinding(innerContext, *binding, inner);
312 if (
auto *errors = std::get_if<QList<QQmlJS::DiagnosticMessage>>(&innerResult)) {
313 for (
const auto &error : std::as_const(*errors)) {
314 qCDebug(lcAotCompiler) <<
"Compilation failed:"
315 << diagnosticErrorMessage(inputFileName, error);
317 }
else if (
auto *func = std::get_if<QQmlJSAotFunction>(&innerResult)) {
318 qCDebug(lcAotCompiler) <<
"Generated code:" << func->code;
319 aotFunctionsByIndex[innerContext->functionIndex] = *func;
323 qCDebug(lcAotCompiler) <<
"Compiling binding for property"
324 << irDocument.stringAt(binding->propertyNameIndex);
325 result = aotCompiler->compileBinding(context, *binding, node);
326 }
else if (
const auto *function = bindingOrFunction.function()) {
327 if (!aotCompiler->isLintCompiler() && !function->isQmlFunction)
330 Q_ASSERT(quint32(functionsToCompile.size()) > function->index);
331 auto *node = functionsToCompile[function->index].node;
333 Q_ASSERT(contextMap.contains(node));
334 QV4::Compiler::Context *context = contextMap.take(node);
337 const QString functionName = irDocument.stringAt(function->nameIndex);
338 qCDebug(lcAotCompiler) <<
"Compiling function" << functionName;
339 result = aotCompiler->compileFunction(context, functionName, node);
344 if (
auto *errors = std::get_if<QList<QQmlJS::DiagnosticMessage>>(&result)) {
345 for (
const auto &error : std::as_const(*errors)) {
346 qCDebug(lcAotCompiler) <<
"Compilation failed:"
347 << diagnosticErrorMessage(inputFileName, error);
349 }
else if (
auto *func = std::get_if<QQmlJSAotFunction>(&result)) {
350 if (func->skipReason.has_value()) {
351 qCDebug(lcAotCompiler) <<
"Compilation skipped:" << func->skipReason.value();
353 qCDebug(lcAotCompiler) <<
"Generated code:" << func->code;
354 auto index = object->runtimeFunctionIndices[bindingOrFunction.index()];
355 aotFunctionsByIndex[index] = *func;
361 if (!checkArgumentsObjectUseInSignalHandlers(irDocument, error)) {
362 *error = error->augment(inputFileName);
366 QmlIR::QmlUnitGenerator generator;
367 irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(
false);
368 generator.generate(irDocument);
370 const quint32 saveFlags
371 = QV4::CompiledData::Unit::StaticData
372 | QV4::CompiledData::Unit::PendingTypeCompilation;
373 QV4::CompiledData::SaveableUnitPointer saveable(
374 irDocument.javaScriptCompilationUnit->unitData(), saveFlags);
375 if (!saveFunction(saveable, aotFunctionsByIndex, &error->message))
382 const QString &inputFileName,
const QString &inputFileUrl,
385 Q_UNUSED(inputFileUrl);
387 QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit;
391 QFile f(inputFileName);
392 if (!f.open(QIODevice::ReadOnly)) {
393 error->message = QLatin1String(
"Error opening ") + inputFileName + QLatin1Char(
':') + f.errorString();
396 sourceCode = QString::fromUtf8(f.readAll());
397 if (f.error() != QFileDevice::NoError) {
398 error->message = QLatin1String(
"Error reading from ") + inputFileName + QLatin1Char(
':') + f.errorString();
403 const bool isModule = inputFileName.endsWith(QLatin1String(
".mjs"));
405 QList<QQmlJS::DiagnosticMessage> diagnostics;
408 unit = QV4::Compiler::Codegen::compileModule(
false, url, sourceCode,
409 QDateTime(), &diagnostics);
410 error->appendDiagnostics(inputFileName, diagnostics);
411 if (!unit || !unit->unitData())
414 QmlIR::Document irDocument(QString(), QString(),
false);
416 QQmlJS::Engine *engine = &irDocument.jsParserEngine;
417 QmlIR::ScriptDirectivesCollector directivesCollector(&irDocument);
418 QQmlJS::Directives *oldDirs = engine->directives();
419 engine->setDirectives(&directivesCollector);
420 auto directivesGuard = qScopeGuard([engine, oldDirs]{
421 engine->setDirectives(oldDirs);
424 QQmlJS::AST::Program *program =
nullptr;
427 QQmlJS::Lexer lexer(engine);
428 lexer.setCode(sourceCode, 1,
false);
429 QQmlJS::Parser parser(engine);
431 bool parsed = parser.parseProgram();
433 error->appendDiagnostics(inputFileName, parser.diagnosticMessages());
438 program = QQmlJS::AST::cast<QQmlJS::AST::Program*>(parser.rootNode());
440 lexer.setCode(QStringLiteral(
"undefined;"), 1,
false);
441 parsed = parser.parseProgram();
443 program = QQmlJS::AST::cast<QQmlJS::AST::Program*>(parser.rootNode());
449 QmlIR::JSCodeGen v4CodeGen(&irDocument);
450 v4CodeGen.generateFromProgram(
451 sourceCode, program, &irDocument.jsModule,
452 QV4::Compiler::ContextType::ScriptImportedByQML);
453 if (v4CodeGen.hasError()) {
454 error->appendDiagnostic(inputFileName, v4CodeGen.error());
459 Q_ASSERT(irDocument.jsModule.fileName.isEmpty());
460 Q_ASSERT(irDocument.jsModule.finalUrl.isEmpty());
462 irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(
false);
463 QmlIR::QmlUnitGenerator generator;
464 generator.generate(irDocument);
465 unit =
std::move(irDocument.javaScriptCompilationUnit);
469 QQmlJSAotFunctionMap empty;
471 QV4::CompiledData::SaveableUnitPointer(unit->unitData()), empty, &error->message);
480bool qSaveQmlJSUnitAsCpp(
const QString &inputFileName,
const QString &outputFileName,
const QV4::CompiledData::SaveableUnitPointer &unit,
const QQmlJSAotFunctionMap &aotFunctions, QString *errorString)
482#if QT_CONFIG(temporaryfile)
483 QSaveFile f(outputFileName);
485 QFile f(outputFileName);
487 if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
488 *errorString = f.errorString();
492 auto writeStr = [&f, errorString](
const QByteArray &data) {
493 if (f.write(data) != data.size()) {
494 *errorString = f.errorString();
500 if (!writeStr(
"// "))
503 if (!writeStr(inputFileName.toUtf8()))
509 if (!writeStr(
"#include <QtQml/qqmlprivate.h>\n"))
512 if (!aotFunctions.isEmpty()) {
513 QStringList includes;
515 for (
const auto &function : aotFunctions)
516 includes.append(function.includes);
518 std::sort(includes.begin(), includes.end());
519 const auto end = std::unique(includes.begin(), includes.end());
520 for (
auto it = includes.begin(); it != end; ++it) {
521 if (!writeStr(QStringLiteral(
"#include <%1>\n").arg(*it).toUtf8()))
526 if (!writeStr(QByteArrayLiteral(
"namespace QmlCacheGeneratedCode {\nnamespace ")))
529 if (!writeStr(qQmlJSSymbolNamespaceForPath(inputFileName).toUtf8()))
532 if (!writeStr(QByteArrayLiteral(
" {\nextern const unsigned char qmlData alignas(16) [];\n"
533 "extern const unsigned char qmlData alignas(16) [] = {\n")))
536 unit.saveToDisk<uchar>([&writeStr](
const uchar *begin, quint32 size) {
537 QByteArray hexifiedData;
539 QTextStream stream(&hexifiedData);
540 const uchar *end = begin + size;
543 for (
const uchar *data = begin; data < end; ++data, ++col) {
550 stream <<
"0x" << *data;
554 return writeStr(hexifiedData);
559 if (!writeStr(
"};\n"))
563 if (aotFunctions.size() <= 1) {
565 writeStr(
"extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
566 "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = { { 0, 0, nullptr, nullptr } };\n");
568 writeStr(
"extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
569 "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = {\n");
571 QString footer = QStringLiteral(
"}\n");
573 for (QQmlJSAotFunctionMap::ConstIterator func = aotFunctions.constBegin(),
574 end = aotFunctions.constEnd();
575 func != end; ++func) {
577 if (func.key() == FileScopeCodeIndex)
580 const QString function = QString::fromUtf8(funcHeaderCode) + func.value().code + footer;
582 writeStr(QStringLiteral(
"{ %1, %2, [](QV4::ExecutableCompilationUnit *contextUnit, "
583 "QMetaType *argTypes) {\n%3}, %4 },")
585 .arg(func->numArguments)
586 .arg(func->signature, function)
587 .toUtf8().constData());
591 writeStr(
"{ 0, 0, nullptr, nullptr }");
595 if (!writeStr(
"}\n}\n"))
598#if QT_CONFIG(temporaryfile)
600 *errorString = f.errorString();