aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/compiler
Commit message (Collapse)AuthorAgeFilesLines
* qmllint: Also lint inner functionsOlivier De Cannière2025-11-052-42/+55
| | | | | | | | | | | | | | | | | | | We were only collecting the 'QML' functions that would be passed to the AOT compiler. Other function types were ignored. Also collect those and pass them to the compiler but only use them when linting. Defer invistigating whether it is a good idea to try to compile JS functions as well to a later point. Logic in a few places was adapted with this changing assumption. [ChangeLog][QML][qmllint] qmllint will now lint inner functions, defined in javascript, in addition to top-level functions and bindings. Fixes: QTBUG-138845 Pick-to: 6.10 6.8 6.5 Change-Id: If6f62aeace8739442b6a1f355fad95ce19c0643c Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* IRBuilder: Drop pointless visit overridesOlivier De Cannière2025-10-142-36/+0
| | | | | | | | Overriding the function only to then call the base implementation and nothing else is not very productive. Change-Id: Ib3cba0d38de652c94689b0bac5e878f629e35bd3 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* Improve error message for invalid import qualifierDheerendra Purohit2025-08-081-1/+1
| | | | | | | | | | | | Previously, the error message for an invalid import qualifier did not show the qualifier name or explain what was wrong. This change adds the qualifier name to the message and says it must start with an uppercase letter. Pick-to: 6.10 Fixes: QTBUG-133313 Change-Id: I0bd4bb143f7b4cdc3b26cae73ab42a4fb05977b1 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* Qml: Rename Expression to CommaExpression in the ASTOlivier De Cannière2025-05-162-2/+2
| | | | | | | | Expression is confusing. Pick-to: 6.9 6.8 6.5 Change-Id: I365ce06a266e24a506b14734fef8b977d6794a72 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* Revert "QtQml: Remove dependency hashing"Ulf Hermann2025-05-153-4/+11
| | | | | | | | | | | This reverts commit 18c421fe6159dc921643c72ae335cf189eb1cc3a. Removing the dependency hashing is not safe because there are various other bits covered by it, not only the alias target IDs. Task-number: QTBUG-136806 Change-Id: I4a8a57d810203a47945ce67916ee5b54ee7a603d Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QtQml: Remove dependency hashingUlf Hermann2025-05-133-11/+4
| | | | | | | | | Since we don't store any property indices in the compilation units anymore, we don't need to hash the dependencies anymore. Task-number: QTBUG-135286 Change-Id: I2ea05c920475749f2a2d6cf309d0956a74d6c688 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* qmllint: warn about unreachable codeSami Shalayel2025-04-252-5/+10
| | | | | | | | | | | | | | | | | | | | The compiler is very polite and does not tell the user about its useless code. Codegen::statementList(StatementList *ast) silently discards unreachable statements during byte code generation. Warn the user that their code is unreachable. Don't warn about function definitions because these ones are "hoisted" up, which means that their definition is supposed to be pushed up, so that they can be used even if they are behind a "return" or "throw" statement. Don't use the qqmljsbasicblock analysis for that, it reports too many "false positives" where the compiler generates dead code that can't be fixed by the user. Task-number: QTBUG-129307 Change-Id: Ia26e8af1adf4e63b26dcaa7fb10be73b7eb084d7 Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io>
* qmllint: Implement WarnFunctionUsedBeforeDeclarationSami Shalayel2025-04-245-3/+21
| | | | | | | | | | | | | | | | | | | | | Warn about functions used before their declaration. Its not technically an error like the "var used before declaration" because functions are "hoisted up" and therefore available even before their declaration, so create a new warning category for it instead of reusing the "var used before declaration" category. Disable the warning by default: Qt Creator used to have it as default, while other tools like eslint don't. For the same reason, don't warn about functions used before declaration during codegen, and add a method to warn about it in CodeGenWarningInterface. The code for "var used before declaration" can be reused by function declarations by adding a sourcelocation for function declarations in the "addLocalVar"-call, so make sure to differentiate between functions and vars by adding an extra member to Context::ResolvedName. Task-number: QTBUG-129307 Change-Id: I83a4f8cd00c120db23a0cec3365a00ed44de2836 Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io>
* qv4codegen: Track the correct location for assign instructionsOlivier De Cannière2025-04-141-0/+1
| | | | | | | | | | | | | | | | | This fixes a qmllint warning about unqualified access in the wrong place in the following unconventional but valid case: Warning: .../Main.qml:5:9: Unqualified access [unqualified] console.log(a = 1) ^^^^^^^ Warning: .../Main.qml:5:21: Unqualified access [unqualified] console.log(a = 1) ^ Pick-to: 6.9 6.8 6.5 Change-Id: I8eaa43f520c394b094917fe37dc6115ec2f7af74 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* Make UsesArgumentsObject a scoped enumerationLuca Di Sera2025-04-104-20/+20
| | | | | Change-Id: I766dc99b8daaeaa64da9075dcfde5dff507c27f2 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* Avoid incorrect access to arguments in signal bindings to arrow functionsLuca Di Sera2025-04-091-0/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Signal handler in QML can be bound to a Javascript expression. For example: ``` onHello: console.log(10) ``` Where "onHello" is a Signal Handler. When this is the case a certain amount of code will be generated for the Signal Handler, performing some setup routines and executing the provided expression, generally in a way that is somewhat equivalent to the same expression being executed as the body of a function call. It is possible to bind a Signal Handler to a anonymous function, say: ``` onHello: function () { ... } ``` Or: ``` onHello: () => { ... } ``` When this is the case, if the usual process was followed, executing the Signal Handler would simply produce an anonymous function, but would never actually call it. Instead, when such a literal function expression is bound to a Signal Handler, it is treated specially. In particular, while the code generation will generally behave similarly, on execution of the Signal Handler, the expression will be directly called while the "wrapping" function that was generated around the expression is ignored. While this works correctly in many cases, it can misbehave on certain occasions. For example, an arrow function doesn't generally set up its own context for a call, instead borrowing from the outer context at the time of creation. When making such a call to an arrow function, in a QML context, it is then possible to execute code in an environment that wasn't properly set up for it. In particular, if the body of the arrow function introduces a `LoadLocal` instruction, which assumes, when interpreted, an available `CallContext`, it is possible to try and access memory that was never correctly set up. It is, for example, possible to do so by usage of `arguments`, a special reference that allows access to a pack of arguments in all non-arrow functions, which will produce a `LoadLocal` instruction when accessed, for example: ``` onHello: () => { console.log(arguments) } ``` Internally, when generating code for a JavaScript program or expression, an analysis is performed to understand whether the special "arguments" reference is being used. When that is the case and the context in which the reference is used is under a context where the reference can exist, a local variable for "arguments" is injected and a "LoadLocal" instruction is later generated to access it. The analysis considers a binding scope as being able to provide the "arguments" reference, something that is generally true due to the "wrapping" that is performed when generating an expression for the binding, and which needs to be guaranteed in certain cases. While this breaks down in the face of directly calling the "inner" function in a Signal Handler binding, such that the analysis might itself not be thorough enough in those cases, at the time the analysis is performed we cannot currently know whether the binding we are dealing with is that of a Signal Handler. Furthermore, we don't always bypass the "wrapping" function in a Signal Handler, as there are other cases where this can create issues, for example the usage of "this" in an arrow function. When this is the case, instead of directly calling the "inner" function, the normal "wrapping" function is called to perform setup routines and obtain the function itself by executing the bound expression, subsequently calling the function obtained in this way. To avoid the issue with the usage of "arguments", we re-use the same methodology, ensuring that when the special "arguments" object is referenced we never bypass the setup provided by the binding expression. This ensures that the arrow function will be created in a context where `arguments` is present and where the necessary setup for the call is performed. The special object will be always be empty in that context, which aligns the behavior to that of non-signal bindings. To do so, an additional case was added to the code in `writeFunction` that sets up a binding expression to later skip to its inner function. A few test cases were added to inspect usages of the "arguments" special reference under binding contexts. Fixes: QTBUG-134215 Change-Id: Ib7fdfee91709358f2ee465b1926809ca4617d6f6 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* QtQml: Drop checks for compile hash and Qt version from the CUsUlf Hermann2025-03-181-3/+0
| | | | | | | | | | | | | | | The data structure version is supposed to encode any incompatible changes to our compilation unit format. Checking the compile hash and Qt version in addition is redundant and excessively restrictive. [ChangeLog][QtQml] You can now use QML code compiled with Qt Quick Compiler across Qt versions as long as the compilation unit format hasn't changed between those versions. You cannot rely on the compilation unit format to stay unchanged under any specific circumstances. However, we won't change it unnecessarily. Change-Id: I8c407b505ac7fa952f53fa25bb6d4e7caf0fba0c Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QML: Add final property attributeOlivier De Cannière2025-03-061-0/+1
| | | | | | | | | This works the same as the FINAL attribute to Q_PROPERTY. Task-number: QTBUG-98320 Change-Id: Icc2cf1afb5354fd711770f7147ded853b74cd1da Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
* QV4: Reserve a bit in CompiledData::PropertyOlivier De Cannière2025-03-061-6/+6
| | | | | | | | | | | | The bit is taken from nameIndex. It should have more than enough capacity with 31 bits. The reserved bit will be used as a flag in subsequent commits. A more typed and centrally enforced aproach to the indexes will be necessary in the future. Change-Id: Ia7c686affba6d5320e674dd3f32b7c59b6321e22 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* qv4compilerscanfunctions: Avoid QList detachFabian Kosmale2025-02-181-1/+1
| | | | | | Change-Id: Ie956854fd0e89fd7fde0509fb46ac861c9571d0f Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io>
* Codegen: Make defineFunction non-virtualFabian Kosmale2025-02-181-1/+1
| | | | | | | Nothing overrides it (anymore?). Change-Id: I8704c0f2a853c9ff3b102e11b7dab20e7e35cc94 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* QtQml: Fix assignment of fileName and URL during compilationUlf Hermann2025-02-045-51/+21
| | | | | | | | | | | We need to assign them right away when creating the module. If we do it later on, there are a lot of different code paths to cover and in fact we were missing some. Pick-to: 6.9 6.8 Task-number: QTBUG-133053 Change-Id: I57e381c787f504eb9bcd8c2041e41b4f1d1f8b53 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* ExceptionHandler: restore the RO5Giuseppe D'Angelo2025-01-201-0/+4
| | | | | | | | | The destructor seems to be declared to check another object's state (?), but the class itself does not seem to have any invariants, and has been used as such for a while, so just redeclare the copy/move operations. Change-Id: I78a6cf6bfde38c2d53ac31f398e123e4fcf6e247 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* IR Builder: Fix translation binding parsingFabian Kosmale2025-01-131-0/+17
| | | | | | | | | | | | | | | | | | QT_TR_NOOP and QT_TRANSLATE_NOOP bindings can have a disambiguation string. Adjust the parser to handle this properly, and amend tst_qqmllistmodel to handle this case. Moreover, adjust the test case to still trigger the expected error in "QT_TR_NOOP extra param": The second parameter would now be (correctly) parsed as disambiguation string, thus add a third one. [ChangeLog][QtQml][ListElement] ListElement now supports disambiguation strings when QT_TR_NOOP is used. Pick-to: 6.9 6.8 6.5 Fixes: QTBUG-132684 Change-Id: Ia133d3c1eece036f982cad8578cfdee89d9216b4 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* QtQml: Generalize the global/illegal namesUlf Hermann2024-12-174-107/+107
| | | | | | | | | Instead of passing them around everywhere, use the ones we statically know and only validate them when creating a new engine. Task-number: QTBUG-131721 Change-Id: I7fb93d15eb6e4194c46249727bcf7a48f5dce730 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QtQml: Update global namesUlf Hermann2024-12-131-0/+15
| | | | | | | | | These are the actual global names as present in the global object. We should use the same ones everywhere. Change-Id: I4fb42f609b656019db5169fd4ea2b243feab18d6 Reviewed-by: Sami Shalayel <sami.shalayel@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QtQml: Sort the global, "illegal" namesUlf Hermann2024-12-121-42/+42
| | | | | | | When adding to them, we want to be able to see what we're doing. Change-Id: I563999d8371ff7bd60ca5ad8e7663fd3fe2f4fca Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
* QtQml: Model native modules as compilation unitsUlf Hermann2024-12-062-9/+84
| | | | | | | | | | | | | | | | | | | | | | | QQmlTypeLoader::injectedScript() was unsafe and impossible to fix because it had to query the engine from the type loader thread in order to find out whether to load a script from an actual file. By removing the whole special-casing of native modules, we can make the script loading thread safe. A native module is now also a compilation unit, with a regular QV4::Module as value. This means we can remove a lot of code that deals with the native modules in the engine. The downside is that native modules are now a lot larger than before. However, given that they don't appear in any examples and hardly any bugs have been filed about native modules since their introduction, we can assume that they are not a very popular feature. The reduction in complexity and the removal of the native modules map in the engine is expected to outweigh the extra memory overhead for native modules. Task-number: QTBUG-131721 Pick-to: 6.8 Change-Id: Ia7388d7ba8d71637559a791d874257fba4646330 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QmlCompiler: Allow string lookup of composite metatypes againUlf Hermann2024-10-021-0/+2
| | | | | | | | | | | We're not guaranteed to get string IDs for those. Pick-to: 6.8 Change-Id: I5800a1e90589f3a6ae55ce8624fa56968f0f3ec3 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io> (cherry picked from commit b8c3cf7192fe5793fdaf56a1203cb2cf76d8c3a2) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
* QV4: Document bytecode instruction formatOlivier De Cannière2024-10-011-0/+72
| | | | | | | | This enables us to encode instructions efficiently in between 1 and 18 bytes. Change-Id: I2e6437ffb31c34069241cfe716f6c61e20388892 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* Bytecode: Fix macro and remove unused definesOlivier De Cannière2024-08-011-2/+0
| | | | | | | | | | | | The code that handles the GetException instruction uses GetException in the BEGIN macro and HasException in the END macro. Use GetException for both so that they match. Also remove two defines from the instruction generation macros which aren't used anywhere. Change-Id: If5c88e94de831cd3d60d6316026fbf7335fb89e0 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QtQml: Straighten out some logging categoriesUlf Hermann2024-06-171-2/+2
| | | | | | | | | | | Either make them static or declare them in a header. We want them to be static wherever possible, in order to reduce the number of visible symbols. If they can't be static, however, they should at least be declared in only one place. Task-number: QTBUG-67692 Change-Id: I91fa641b46510ea8902b478d31dfd60d34b5f580 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QML: Deprecate coercion on type assertionsUlf Hermann2024-05-242-0/+14
| | | | | | | | | | | | | | | | | | | By using an "as" cast you want to check the type of the value, not coerce it. Previously, however, if you did this with a value type, it would create the value type instead of just checking for it. Add an attribute "Assertable" to the ValueTypeBehavior pragma that prevents this and enables the correct behavior. Also print a warning when coercing as part of an as-cast. [ChangeLog][QtQml] A new attribute "Assertable" has been added to the "ValueTypeBehavior" pragma. You should always use it if you want to type-check value types using "as". If you don't use it, an instance of the type is created as result of the "as" if the type doesn't match. Task-number: QTBUG-124662 Change-Id: I1d5a6ca0a6f97d7d48440330bed1f9f6472198aa Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* qv4codegen: Store the location of binary expressionsOlivier De Cannière2024-04-222-5/+9
| | | | | | | | | | | | | | | | | | | | | | | | | | | | This location is then used later, for example, to improve the accuracy of warnings. OLD Warning: Main.qml:22:30: function without return type annotation returns double of double [compiler] function type() { return 1 + 1 } ^^^^^^ NEW Warning: Main.qml:22:30: function without return type annotation returns double of double [compiler] function type() { return 1 + 1 } ^^^^^ The location stored is the combined locations of the left operand, the operator, and the right operator. We should investigate if this is the right approach. Created QTBUG-124548. Task-number: QTBUG-124548 Task-number: QTBUG-124220 Pick-to: 6.7 Change-Id: Icac335d53349c05d0e9ee6e436bc6ab08ad970d2 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* Remove the use of Q_QML_PRIVATE_EXPORTAlexey Edelev2024-01-111-1/+1
| | | | | | Task-number: QTBUG-117983 Change-Id: I5790f01d614cd70c7fcc9bd817ec6ace3f3e3730 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* QtQml: Make base CU a member of ExecutableCompilationUnitUlf Hermann2024-01-104-15/+22
| | | | | | | | | | | | We want to re-use the base compilation unit across engines. For that to work it cannot be a slice of the engine-specific ExecutableCompilationUnit. Since CompiledData::CompilationUnit is refcounted on its own now, make it unmovable. Change-Id: I8418c9754d7a07e5210c1e7a7fc69355e1d57807 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* Remove the use of Q_QML_COMPILER_PRIVATE_EXPORTAlexey Edelev2024-01-094-13/+13
| | | | | | Task-number: QTBUG-117983 Change-Id: I717bf43032e72ec743f238ac48935a3019f1879d Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* qv4codegen: Do not call functions on null objects inside optional chainsOlivier De Cannière2024-01-081-1/+1
| | | | | | | | | Amends: 86c48761dc7ba5bcac7dc6740e94efbfb8678403 Fixes: QTBUG-120504 Pick-to: 6.7 Change-Id: Id77236a07d7c1a16e2f60238909eff245c5c354a Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* QtQml: Pass Codegen::Reference as referenceUlf Hermann2024-01-082-2/+2
| | | | | | | | | Despite its name, it's really large. Coverity-Id: 434159 Change-Id: I7ccdfbdf0582edd13011d89dbb50a17ea1455637 Reviewed-by: Sami Shalayel <sami.shalayel@qt.io> Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io>
* QtQml: Do optional chain finalization also on string-y element lookupUlf Hermann2023-12-201-0/+1
| | | | | | | | | | Amends commit 86c48761dc7ba5bcac7dc6740e94efbfb8678403. Pick-to: 6.7 Fixes: QTBUG-120168 Change-Id: I5848d8394498bafb1e897eca865d405224eaf997 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
* Engine: Group 'bad' case handling for optional chainsOlivier De Cannière2023-11-202-160/+165
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This patch changes the way optional chains are dealt with in the bytecode. Instead of dealing with the 'bad' case (where the base of the lookup is null or undefined) of each instruction separately, all optional operations point to the same piece of code at the end of the optional chain to deal with bad accesses. In practice, for the lookup `root?.foo.bar?.baz` the following bytecode instructions are generated. LoadQmlContextPropertyLookup // root GetOptionalLookup --v // ?.foo GetLookup | // .bar GetOptionalLookup --v // ?.baz Jump done ------------v undefined: <----< | LoadUndefined | done: <------< In this way, the 'bad' case is handled in one place at the undefined label. If, on the other hand, the chain evaluation reaches the bottom, one jump takes the resulting value to the rest of the program. In this way, the 'bad' case has a constant size relative to the length of the chain. If no optional operation is performed at all. The 'bad' case handler is not generated at all. For this to work, GetOptionalLookup now jumps to the undefined label when its base is null of undefined. Other operations such as function calls `f?.()`, array access `a?.[0]` and delete expressions `delete foo?.bar` have also been adapted to point to the undefined label. Change-Id: I07158efc8767d84a7588299cae9fb763b0f6e253 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* Add pragma syntax to support translation contextLucie Gérard2023-10-263-0/+15
| | | | | | | | | | | | | Translator pragma can be used to set the translation context instead of having the file name used [ChangeLog][qml][translation][Important Behavior Changes] The context for the translation can now be controled in a given file using pragma Translator. Task-number: QTBUG-114528 Change-Id: I6d9d7fb81ea969a90d8637d7277bdbe96c102088 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* Fix compile time qsTranslate with empty contextUlf Hermann2023-10-241-4/+12
| | | | | | | | | | | | | | | | | An empty context is to be passed as-is. We shall not replace it with the file context context. Since the TranslationData struct has a field for the context, we need to invent a "no context" value we use for the methods that don't allow you to set a context (e.g. qsTr, qsTrId). We cannot use 0 because that is the empty string which is a valid context now. Amends commit 9cfc19faf5d1ce2b9626914ab4528998b072385d. Pick-to: 6.6 6.5 Fixes: QTBUG-118469 Change-Id: I160c512f42aba4a8ae2fc8860cdf4e50c53d9d3e Reviewed-by: Sami Shalayel <sami.shalayel@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* bytecode: Dump instruction with src/dest arguments as: Inst Src DestOlivier De Cannière2023-09-151-3/+3
| | | | | | | | | | The source and destination arguments of the bytecode are always given as source first and destination second. The bytecode dumper flips this convention. Let's fix that. Change-Id: I0489a80f9b0494e303a5ad981ca16b68a1ad358e Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
* V4: Eliminate "done" from iteratorsUlf Hermann2023-09-085-51/+80
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Instead of dragging another stack value around to mark if the iterator was done, rather pass it an offset it should jump to if so. It can then jump over any IteratorClose instruction while the ExceptionHandler can still point to the IteratorClose instruction. For this to work, we also have to refrain from checking for exceptions as part of IteratorNext or IteratorClose. If IteratorNext generates an exception, it also jumps to the "done" label, after which we dispatch the exception. We don't want to jump to the exception handler for other instructions in between as that would close the iterator. The iterator should _not_ be closed if it has just thrown an exception, though. The same holds for IteratorClose: If it throws an exception, we don't want to jump back to the beginning of the loop's exception handler, since that would produce an infinite loop. We also don't want to reset the exception handler before IteratorClose because it needs to also be reset if the iterator does not need to be closed. This saves quite a few instructions and stack variables on actual iteration. For destructuring, we have to change the execution flow a bit. We need to first perform the iteration for non-rest parameters, saving the results in separate stack slots. This way we can apply our new "jump if done" behavior if the iterator runs out or produces an exception itself. We then save the "done" state in a separate stack slot, as before. During the assignment of the iteration results to the actual variables, we install an exception handler, so that we can still close the iterator if one of the initializers throws an exception. This produces a few more instructions than before: 1. We need to set and read the "needsClose" variable explicitly rather than having IteratorNext and IteratorDone do it implicitly. 2. We need an additional CheckException after the iteration. 3. We need an additional conditional Jump over the IteratorDone. Everything considered, the savings we get for regular iteration and the more consistent semantics of the instructions involved are well worth the few extra instructions on destructuring, especially since everything those extra instructions do was done implicitly by the iterator instructions before. For consistency, the IteratorNextForYieldStar instruction is refactored to work the same way as IteratorNext: In case of either an exception or "done" it jumps to an offset, and we refrain from individually exception-checking each IteratorNextForYieldStart instruction. Task-number: QTBUG-116725 Change-Id: I9e2ad4319495aecabafdbbd3dd0cbf3c6191f942 Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io> Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
* QtQml: Add more fine grained logging categories for qv4codegen.cppUlf Hermann2023-09-081-3/+4
| | | | | | | | | | | We want to control the warnings about variables used before declaration and injected signal parameters separately, and they are not necessarily part of the compiler warnings. Fixes: QTBUG-116764 Change-Id: If3f28f99b539a069cbdb7854d701c027debc77be Reviewed-by: Andreas Aardal Hanssen <andrhans@cisco.com> Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
* qv4codegen: Eliminate redundant SetUnwindHandler for try-catch-finallyOlivier De Cannière2023-08-302-3/+9
| | | | | | | | | | | | | | For try-catch-finally sequences with a catch block, the generator would start by setting the unwind handler to code from the finally block and immediately after that set the handler to code from the catch block, overwriting the last instruction. In cases with catch blocks, do not emit the first instruction setting the unwind handler. Change-Id: I588433c1ad2521b1750f77f2084315a707577a8a Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* qv4codegen: Remove redundant jump instruction for coalesce expressionsOlivier De Cannière2023-08-291-8/+2
| | | | | | | | | | | | | | | | | | | For binary expressions with the ?? operator, two cases must be handled in two code paths. When the lhs is defined, it is used as the result of the expression. When the lhs is undefined, the rhs expression is used as a fallback instead. Distinguishing between these two branches can be done with a test such as CmpEqNull and a conditional jump based on it.. Previously, on top of a conditional JumpTrue to jump to the correct location based on the test, a second JumpFalse was emitted to perform the same operation a second time. This is redundant. The second jump was removed and the logic was slightly reorganized. Change-Id: I65479752d9fac8c18ef2475b062dd2b5262c372e Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* QtQml: Don't generate an unwind handler for for..in loopsUlf Hermann2023-08-251-5/+6
| | | | | | | | We only need the unwind handler for for..of loops in order to close the iterator. Change-Id: Ieae67cf852fd858e0c1956889d4cf5b40a660b7e Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* Replace signal name manipulations with QQmlSignalNamesSami Shalayel2023-08-152-35/+0
| | | | | | | | | | | | | | | Remove custom implementations found in qqmljs* and use the static helper methods from qqmlsignalnames_p.h instead. This sometimes requires to move some code around to avoid bugs with property that do not have letters in their name. Add a warning in the JS implementation of the SignalSpy.qml that the used heuristic might fail on certain signal names. Add tests in in tst_qqmllanguage to see if the property change handlers work correctly for weird names. Change-Id: I4dc73c34df7f77f529511fa04ab5fcc5385b59fc Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* UndefinedBehavior: fix some things found with -sanitize undefinedOlivier De Cannière2023-08-011-1/+1
| | | | | | | | | | | | | | | | | | Here are the sorts of things that were found: - Uninitialized variables containing garbage. - Calling member function through nullptr (where this is not actually used inside the function because that would trigger a segfault). - static_cast'ing double to int where the double is either +/-infinity or is outside the range of min and max values for int. Additionally, the uses of QJSNumberCoercion::isInteger() in the code generator have been replaced by QJSNumberCoercion::isArrayIndex() and the former was deprecated as it is no longer being used. Pick-to: 6.5 6.6 Change-Id: I9318671ccbda37e5519f4fcb84a1537585c2103f Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* Compiler: Allow dumping the basic blocks for visualization and debuggingOlivier De Cannière2023-07-103-121/+142
| | | | | | | | | | | | | | | | | | | | If the QV4_DUMP_BASIC_BLOCKS environment variable is set, the compiler will output the details of the basic blocks of the compiled functions to the console. It will also generate a control flow graph containing the byte code in DOT format for easier visualization and debugging of the program execution and of the structure of the generated code. The value of QV4_DUMP_BASIC_BLOCKS will be used as the path to the folder in which to output the DOT files. If the path is any of ["-", "1", "true"] or if files can't be opened, it will be dumped to stdout instead. The logic in dumpByteCode has been adapted to use a QTextStream. This way it can continue to be used to dump the byte code of the whole program as before and also to construct the CFG. Change-Id: If398d795e4fc0950b5fa8ee1349e80b1ae262deb Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* QmlCompiler: Allow creation of structured value typesUlf Hermann2023-07-062-1/+28
| | | | | | | | | | | | | | | | With this change, qmlcachegen can populate structured value types from object literals. Also fix the construction of value types via Q_INVOKABLE ctors. We don't need to wrap the ctor argument in QVariant if we can store the original type, and we should always look at the base type for the creatable flag, not the extension. Task-number: QTBUG-107469 Task-number: QTBUG-112485 Change-Id: I9f3db13f00466dc9d87237bdf0b380d6eeb58a10 Reviewed-by: Sami Shalayel <sami.shalayel@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QML: Revert the default for enforcing function signaturesUlf Hermann2023-06-261-2/+1
| | | | | | | | | [ChangeLog][QtQml][Important Behavior Changes] Type annotations on function signatures are now enforced, no matter if the code in question is interpreted, JIT-compiled, or AOT-compiled. Previously, only AOT-compiled code enforced the signatures. Therefore you could produce divergent behavior by passing or returning values that violated the type annotations. Fixes: QTBUG-113527 Fixes: QTBUG-109221 Change-Id: Ie573b31f35813db37b75189e747c764d1b9bbe78 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QML: Use QQmlType as container for composite types (inline or not)Ulf Hermann2023-06-221-0/+6
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This gives us a unified interface to all kinds of QML types at run time and reduces the effort of finding corresponding type attributes. QQmlType is much larger than CompositeMetaTypeIds. Most composite types, however, are initially referenced by URL, and we call typeForUrl anyway. typeForUrl already creates a mostly functional QQmlType; we only need to add the dynamic metatypes. The same type can be retrieved later and associated with the actual CU using the compositeTypes hash. That way, we don't need any extra type. We do, however, incur the cost of creating the QMetaTypePrivate instances when first referencing a type. This could be optimized, like many things in this area, by using thread safe lazy initialization. Now some QQmlTypes implicitly depend on the CUs they were created for. This creates problems if the CUs are removed but the types still persist. Such a situation can arise if you create and delete engines. In order to avoid it, we: 1. Make the compositeTypes hold a strong reference to the CUs 2. When unlinking, avoid dropping the property caches (as those are used to hold the metaobjects) Now, however we got a cyclic reference between the CU and its QQmlType(s). To resolve this, we clear the QQmlTypes on unlinking. Finally, to avoid deletion recursion when clearing the last CUs on destruction of the QQmlMetaTypeData, we move the compilation units out of the way first. All of this still doesn't work if multiple CUs hold the same QQmlType, since compositeTypes can only hold one CU per type and that may be the one that gets removed first. Therefore, we cannot allow such a situation to happen and have to create a new QQmlType if it arises. It can only arise if you have multiple engines loading the same QML components, which should be fairly rare. For inline components, we apply a similar trick: You can either find an inline component by Url, and receive any type that matches, no matter what CU it belongs to. Or you can request an inline component type that belongs to a specific CU. It turns out we don't have to store the containing type of an IC at all. Also, we slightly change the naming of internal components' "class names" since we want to use the inline components' element names for them. Change-Id: I0ef89bd4b0a02cc927aed2525e72f6bff56df626 Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>