aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime
Commit message (Collapse)AuthorAgeFilesLines
...
* QtQml: Disable AOT compiled code when QML-previewingUlf Hermann2024-09-272-2/+13
| | | | | | | | | | | We cannot replace AOT-compiled compilation units while an object is still holding on to them and we cannot delete all objects holding on to a CU because they might not belong to the preview to begin with. Pick-to: 6.8 6.5 Fixes: QTBUG-129329 Change-Id: Icbcb7822be770a440f3216955c0ae51151390e17 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QtQml: Decouple JavaScript library CUs from enginesUlf Hermann2024-09-204-6/+29
| | | | | | | | | | | | | | | | | | | | | | | | In order to re-use the compilation units for JavaScript libraries, we need to eliminate the "m_value" member they are carrying, indirectly. The values are tied to specific engines and invalid in others. Luckily, we already have two suitable places to store such values: 1. In case of a "native" module without a compilation unit we have the nativeModules hash in ExecutionEngine. 2. In case of a module or library backed by a CU we have the "module" member of ExecutableCompilationUnit. This can currently only hold modules but there is no reason why it wouldn't hold JavaScript libraries equally well. By using the "empty" V4 value we can also get rid of the m_loaded bool. As a drive by, correct the QQmlScriptBlob::isNative() misnomer. We don't want to know whether the script is native (for any value of that), but rather whether it has a value. Pick-to: 6.8 Fixes: QTBUG-129052 Change-Id: I284823f4aa26e46dec8328f88f65877f59e40f79 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QtQml: Never clear all executable CUs from the QQmlEngineUlf Hermann2024-09-101-1/+0
| | | | | | | | | | | | | | | | | Since the executable CUs are used as entry points for marking by the GC, dropping them from QQmlEngine means their strings, lookups, regexes etc won't get marked anymore. If, however, the CUs are still referenced elsewhere, their inner objects will still get used, despite having potentially been swept by the GC. Also fix the documentation of clearComponentCache() to clarify that it does in fact not clear all components. Pick-to: 6.8 Fixes: QTBUG-128638 Fixes: QTBUG-128782 Change-Id: I96480914733c399c18778202ae8dca7e332c3a85 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QtQml: Explicitly ignore return value of checkStackLimits()Ulf Hermann2024-09-031-1/+2
| | | | | | | | | | | We usually have to do some handling in this case, but here we return anyway. Coverity-Id: 468063 Change-Id: Ie2ced36322a28d0c68c6c8618eb4f7890ae0f7ab Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io> Reviewed-by: Sami Shalayel <sami.shalayel@qt.io> Reviewed-by: Luca Di Sera <luca.disera@qt.io>
* Silence warning C4146Luca Di Sera2024-09-021-1/+2
| | | | | | | | | | | | Warning C4146 is issued when the unary minus operator is used on an unsigned type. Silence the warning by substituting `-1u` with the equivalent and clearer `numeric_limits<uint>::max`. Pick-to: 6.8 Change-Id: Ie73c48467abf4af3059639d8bbd436e6cdc717e0 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* Fix codechecker `inaccurate-erase` warningLuca Di Sera2024-09-021-1/+3
| | | | | | | | Use `q20::erase` instad of the one-argument overload of erase and the erase-remove idiom. Change-Id: I880cdbdf38df1873fb87522975bcd9e9aaf9c9d4 Reviewed-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
* QJSEngine: Do not skip values in Map/Set.prototype.forEachLuca Di Sera2024-08-304-5/+62
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The backing storage implementation for Maps/Sets, `ESTable`, currently uses a pair of arrays for its storage of keys and values, respectively. Additional elements that should be added are appended to the arrays. When an element is removed, all elements past it are shifted to the left, to avoid having empty spaces. The arrays naturally preserve insertion order, which is required to be the iteration order for `forEach` based on the spec. `Map/Set.prototype.forEach` implementations make use of this fact by iterating per-index so that the iteration follows the required order. As per the spec, during the execution of a `Map/Set.prototype.forEach` call, a call to `callbackFn` might modify the iterated upon collection itself. Depending on the specific changes that are performed, this might break the iteration cycle in the index-based iteration that the `forEach` implementation performs. For example, given: ``` let set = new Set([1,2,3]); set.forEach((v) => { console.log(v) set.delete(v) }) ``` The index based implementation would perform the following iterations: - Set = [1, 2, 3]; index = 0; - visit 1 - 1 is deleted from the Set - ... - Set = [2, 3]; index = 1; - visit 3 ... Skipping the required visit of 2 due to the index not being re-adjusted between iterations. To avoid the issue, the way that `forEach` implementations iterate over an `ESTable` was slightly modified. `ESTable` now exposes a simple structure that can be used to observe changes in position in relation to a an indexed entry. An instance of the structure can be registered with the table to observe the internal changes that the table performs, allowing the re-adjustment of an index during an index-based iteration. A small test was added to ensure that the case reported in attached bug report is solved. `tst_ecmascripttests` was modified to enable some `forEach` related tests that were previously failing and will now work due to the iteration changes. Fixes: QTBUG-123377 Pick-to: 6.8 Change-Id: I8fc72b747eb295f009c2e2c2933f05b26f1ce717 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* QJSEngine: Check for stack overflows on generatorsLuca Di Sera2024-08-262-10/+21
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Generally, the engine checks for stack overflows each time a function is called. In the common case, the entry point for the execution of a function is `QV4::Moth::VME::exec`, which will perform the check as part of its execution. `exec` will then dispatch to the execution of the function, either by using the JIT when enabled and available or by calling `QV4::Moth::VME::interpret` when this is not the case. Generators work slightly differently than a normal call, both when a Generator Function or Method is executed and when a generator is resumed. In particular, the execution of a Generator Function or Method and the execution of a resumed generator do not pass by `exec`, instead directly calling `interpret`on the relevant code. As `interpret` does not perform any check for stack overflows, a call to a Generator Function or Method or the processing of a resume operation on a Generator will not identify those overflows, such that it will fail to throw a `RangeError`, as it would be normal for a normal call, instead ending up segfaulting down the call chain. To avoid the issue, ensure that a check for stack overflows is performed before a call to `interpret` in both `GeneratorFunction::virtualCall` and `GeneratorObject::resume`, to ensure that an exception is thrown when required. The ordering of operations for `resume` was modified to allow for the abrupt return due to the, now possible, stack overflow exception. Both `return` and `throw` can produce a resume of the generator and both of them throw an exception in doing so. `return` throws an exception as an implementation detail. An `emptyValue` is thrown, which will later be recognized by the execution of a `Resume` instruction and cleaned away. Failure to do the cleaning will produce an issue as an `emptyValue` should not propagate through the system. `throw` throws as part of its implementation as expected by the spec. As both throws are performed before resume is called, the thrown value would hide the `RangeError` exception that can be thrown due to a stack overflow. To allow for the stack overflow to be correctly recognized and its thrown error not to be hidden, `resume` was modified to accept a third optional parameter, representing a value to be thrown just before calling `interpret` and after checking for a stack overflow. Its call site in `method_next`, `method_return` and `method_throw` were modified accordingly. Some test-cases were added to `tst_qjsengine` to ensure that the correct kind of exception is thrown when a stack overflow happens on the execution of a Generator Function or Method or when resuming a Generator. Pick-to: 6.7 6.8 Fixes: QTBUG-127049 Change-Id: I3656957097df3ccd5c6d70f86335bc40e229bb20 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QmlEngine: Add support for sequences to CallArgument::fromValueOlivier De Cannière2024-08-081-0/+20
| | | | | | | Fixes: QTBUG-127704 Pick-to: 6.7 6.8 Change-Id: Ifb6aad0feb26fadee2838b74a3a7c29949be05a6 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* Bytecode: Fix macro and remove unused definesOlivier De Cannière2024-08-011-1/+1
| | | | | | | | | | | | 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: Improve overload resolution for value type ctorsUlf Hermann2024-07-302-16/+21
| | | | | | | | | | | | | Prefer exact matches where they exist, try all ctors for derived types then, and only if that doesn't help, try conversion. In order to properly compare the types, always retrieve variants from V4 values as their "natural" type rather than pre-coercing them. Pick-to: 6.8 Task-number: QTBUG-125341 Change-Id: Ib3bc45edd6983ca662c556ca8d8c6ed5d2bbaf46 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QJSEngine: Disable tail calls for GeneratorFunction/MethodLuca Di Sera2024-07-171-0/+4
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | When a generator function or method is called, the engine needs to perform some setup for the call to be performed. For example, it needs to setup an actual generator object, provide it with a frame for re-entering, actually execution the body of the generator callable and then return the generator object as the result. This necessary processing is generally handled by `GeneratorFunction::virtualCall`. The engine implements Proper tail calls as introduced by the ES6 specification. For example in: ``` function foo() { return 0; } function bar() { return foo(); } ``` The call to `foo()` in `bar()` in in a tail position and can be optimized accordingly. In the engine, tail calls will be optimized and implemented as jumps, eliding the actual call indirection. While this is generally fine, it produces incorrect results when the elided call is to a generator function/method, as the required setup for the generator call is never executed. For example in: ``` function* foo() { yield 0; } function bar() { return foo(); } ``` `foo` is a generator function that is called in tail position in `bar`. When `bar` is called, the call to `foo` will be optimized out and its body will be executed. This will correctly execute the body but produce an incorrect result. The YIELD instruction will mark the incorrect frame as a yielding frame and no generator object will be constructed or returned, instead producing an undefined result. To avoid the issue, disable TCE for `GeneratorFunction`/`GeneratorMethod` to ensure that tail calls to a generator function or method will not elide the call to `GeneratorFunction::virtualCall`, so that they can perform the required setup for the execution to succeed. Some test cases were added to `tst_QJSEngine` to test that tails calls to a generator function or method do not produce an undefined value. Fixes: QTBUG-109880 Pick-to: 6.5 6.7 6.8 Change-Id: I712ab5c8fdcfde2591b83c4e951dd4723b8bfc1d Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io> Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
* QML: Allow conversion between different list typesUlf Hermann2024-07-033-92/+136
| | | | | | | | | | | | | We universally allow this pretty much everywhere else. We should also allow it when evaluating bindings. To facilitate this, generalize the SequencePrototype::toVariant() method so that it works with any array-like and faithfully coerces the elements by the type coercion rules. Pick-to: 6.8 Fixes: QTBUG-126398 Change-Id: I520cd40e5f74bee5ac4b418aa86dc043774efcbe Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* Fix typo in commentLuca Di Sera2024-06-281-1/+1
| | | | | | | scopde -> scoped Change-Id: I34d68aa09a9d3fb84cc558055ed0388725f41fbb Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QJSEngine: Treat empty string literals as non-null, empty QStringsLuca Di Sera2024-06-281-0/+5
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | When evaluating a script with `QJSEngine` an empty string literal will be treated as a null string. For example evaluating `""` will produce a string-holding `QJSValue` that produces a null `QString` when the string value is extracted. This is in contrast with the general behavior of `QJSValue`, where directly constructing a `QJSValue` from a null or empty `QString` will produce a `QString` value that is empty but not null. When the engine reads the literal from the string table, it specifically returns a null `QString` for zero-sized strings. This null `QString` will then propagate its null-ness when used to allocate a string, which will in turn propagate all the way up to the returned `QJSValue` that holds the produced string. To align the behavior of a string-holding `QJSValue` produced from the engine to the behavior of a `QJSValue` directly built from a `QString`, the specialized code that produced the original null `QString` was removed, so that an empty string is constructed instead. This partially amends ff0857541d5d391c7c03cce5893b41dd9b35e7fa, which introduced the specialized construction of the null string in relation to another issue. The change was already partially amended in 86379e265e19a078545306d93c59b0d92c04920a, where some of the additional behavior introduced by the original patch was rendered unnecessary by changes in Qt itself. The null string behavior for empty literals that was added as part of the original patch was partly tied to the code removed in the first amendment and is not expected to break the original case anymore due to the changes in the context around it. A test case was added to `tst_QJSEngine` to test the behavior. The test case that was originally added in ff0857541d5d391c7c03cce5893b41dd9b35e7fa, which tested that empty string literals were treated as nulls was modified to align to the new behavior of non-null, empty strings. A slight change was made to the implementation for the String prototype `startsWith` method to adapt it to the changes which exposed a previously existing bug. When `startsWith` was given a position as part of its second argument, this would have been retrieved as a double and then directly passed to `QStringView::mid`, producing an implicit conversion to `qsizetype`. For values that `qsizetype` cannot hold, the result can be different between platforms. In particular, this showed on a ecmascript compliancy test that would now fail on ARM mac platforms. The test would pass infinity as the second argument and an empty literal string as the first in a call to `startsWith`, which, by the spec description should return true. In general, on an ARM mac platform, the conversion would saturate to the nearest integer, a positive value. On such a parameter the call to `mid` would return a null string. Due to the way the `startsWith` implementation for `QStringView` works, this would require the searched for string to be null for the search to return true. Previously, due to literal empty strings being read as null, this would silently pass. On the contrary, on a platform such as x86_64, the implicit cast would generally produce the indefinite integer value, which appears as a negative integer, producing the whole original string on a call to mid, which then would have the correct behavior on a call to `QStringView::startsWith` with regards to the test in question, so that it would generally pass. To avoid the platform specific behavior, the position in double form is now clamped between zero and the length of the string that should be searched, which should generally avoid the unexpected behavior and be relatively consistent between platforms. [ChangeLog][QtQml] Assigning an empty JavaScript string to a property of type QString now produces only an empty QString, not a null QString. Fixes: QTBUG-125611 Pick-to: 6.8 Change-Id: Id6850fd98082f33db93d2a7d0bc4f7b5fdcad45b Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* JIT: storeLocal needs to go through WriteBarrierFabian Kosmale2024-06-182-0/+23
| | | | | | | | | | | | | | The interpreter already has the necessary setup, but the JIT did simply write the value without marking so far. We fix this by adding a new runtime function call, which simply uses QV4::WriteBarrier::markCustom to mark the given value. Both the StoreLocal and StoreScopedLocal bytecode instructions are handled by adding the code to BaselineAssembler::storeLocal. Pick-to: 6.8 Change-Id: I4b9226848bff029a076c0cfa6daf899ca9b84622 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* Fix Proxy / QV4::Lookup interactionFabian Kosmale2024-06-171-0/+6
| | | | | | | | | | | | | If an object is proxied, we can't use the optimized getters for the lookup, as those would bypass the custom proxy logic. Fix this by always using the fallback code path when encountering a Proxy object. Pick-to: 6.8 6.7 6.5 Fixes: QTBUG-112320 Change-Id: Idd8d4f2cd0134ada010448d470cfc68e765ef125 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* gc: Do not attempt to mark primitive keys of Map/SetFabian Kosmale2024-06-172-2/+5
| | | | | | | | | In contrast to a WeakMap/WeakSet, Map and Set also accept primitive keys, from which we obviously won't be able to obtain a heapObject. Pick-to: 6.8 Change-Id: I2ceeb54339ff3a3a14424c22b2f49d098cd635cd Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* QtQml: Straighten out some logging categoriesUlf Hermann2024-06-175-7/+9
| | | | | | | | | | | 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>
* Rework the sort implementation for SequencesLuca Di Sera2024-06-064-100/+56
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | When QML reads a property with a C++ provenance it sometimes apply certain transformations to work with the property in a JS environment. For example, certain containers, such as `QJsonArray` or `QVariantList`, are converted to a `Sequence`, an array-like object that knows how to modify the container and takes care of reflecting mutations back to the property. `Sequence` provides a specialized implementation for the built-in sort method. Generally, the default sort implementation for an array in JS converts the elements to a string and compares the stringified representation. In the case of `Sequence`, the sort implementation will treats the elements as `QVariant`s and use `QVariant::toString` to perform this part of the sorting algorithm. Due to the way `QVariant::toString` works, this can fail for certain elements. For example, `QVariant::toString` is unaware of how to produce a string from a `QJsonValue`, the type of the elements that compose a `QJsonArray`, thus failing to correctly sort a container with such elements. Other than the `Sequence` implementation, the JS runtime provides, as per specification, a sort method for the Array prototype. Contrary to other methods that are implemented for the prototype, the `sort` method is implemented so that it can only work on values that have a populated `ArrayData`, an optimized storage for certain array and array-like objects. As `Sequences` do not use an `ArrayData` storage for their elements, the method is unable to work on a `Sequence`. To broaden the ability of the sort method implementation for `Sequence` to work more generically, the default sort implementation for the Array prototype sort method was modified to work more generically on objects that do not present an `ArrayData` storage, with an implementation based on the latest draft of the JS specification. The specialized `Sequence` implementation was removed, in favor of `Sequence` delegating to the Array prototype implementation which would now support working with `Sequence`s. While this should be generally slower than the specialized implementation, foregoing some performance, it should allow a more generic foundation for the sort method for `Sequences` or other elements that act like an array but do not use the specialized `ArrayData` representation. Some specialization could later be reapplied to `Sequence` to improve the performances of the implementation. Previously, the Array prototype implementation would directly delegate to `ArrayData::sort`, the sort implementation for the specialized `ArrayData` storage. This was modified to dispatch to an implementation based on generic methods when no `ArrayData` is populated on the object of the sort. The code related to the specialized `Sequence` implementation for sort was removed and the sequence prototype was modified to not present a specialized `sort` property, so as to fallback on the Array prototype one. The `ArrayData::sort` implementation was slightly modified. `ArrayData::sort` would perform a check on the presence of a defined callback for the sorting and throw a type error if the non-undefined element is not callable, as per specification. This check was moved to the Array prototype implementation, to be shared between the specialized `ArrayData::sort` implementation and the generic implementation. As per the spec, the check should be performed as soon as the method is entered and before the receiver of the method is converted to an object. With the check moved to the start of the Array prototype sort implementation this ordering of operations is now fulfilled. The compliance test that checks for this behavior, `comparefn-nonfunction-call-throws`, that was previously failing, will now pass and was thus removed from the list of expected failures for the `ecmascript` tests. A `QEXPECT_FAIL` related to testing the default sort of a `QJsonArray` property was removed from `tst_qqmllanguage`, as the sort is now expected to work correctly. Fixes: QTBUG-125400 Change-Id: I158a9a160b8bdde2b8a06bb349a76469fc25c5a1 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* gc: fix marking for (Weak)(Map|Set)Fabian Kosmale2024-06-054-4/+32
| | | | | | | | | | | | Map and Set must hold on to their keys (and values in case of Map), which did not happen if we inserted values into an already marked Map or Set. A similar issue holds for weak maps and sets, except that for those we only want to mark them once we are past the FreeWeak(Maps|Sets) phase. Pick-to: 6.8 Change-Id: Ibc05be4a85172a9b726beebf0d008acde4479edf Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* Restructure builtins and QtQml.BaseUlf Hermann2024-06-031-4/+4
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The QtQml library should hold the builtins and provide no plugin. Move the types currently exposed in QtQml.Base to QtQml where it makes sense. Anonymous object types as well as sequence types and value types can well be exposed as builtins. This makes everybody's life easier since you now can universally depend on their availability. The Qt object, despite being a named object type, also becomes a builtin because you always have the "Qt" member of the JavaScript global object which holds the same thing. So, formally exposing "Qt" as builtin doesn't really add anything new. QQmlLoggingCategory is split up into two classes, not only because we need the base class when printing to the console from QtQml, but also because this paves the way for compile time identification of logging categories as first argument to the console methods. For QQmlLocale we have to refer to a different trick. The value type and the QQmlLocale "namespace" have to be builtins, but the "Locale" name, due to being uppercase and versioned, has to be part of QtQml. We transform QQmlLocale into a struct so that we can inherit the enums from QLocale and extend a namespace with it in QtQml. Pick-to: 6.8 Fixes: QTBUG-119473 Fixes: QTBUG-125765 Change-Id: Ica59390e54c473e33b4315f4384b724c870c1062 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* Clean up some includesUlf Hermann2024-05-303-49/+36
| | | | | | | | | | In particular, don't include qqmllocale_p.h, qqmlbind_p.h and qv4sequenceobject_p.h where we don't need them. Also, don't qualify private includes by their module name so that we can move them to other modules more easily. Change-Id: Ic13592cef574424498ce9e7708d6fbaa681b81f3 Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
* QtQml: Optimize QV4::Sequence's shift() methodUlf Hermann2024-05-282-0/+62
| | | | | | | | Most of the time we have a native shift() operation. Fixes: QTBUG-123882 Change-Id: I1bc50f98f29918a56b5fc70d1644eb99542a3073 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QtQml: Document and uphold precondition of metaTypeFromJS()Ulf Hermann2024-05-281-6/+10
| | | | | | | | | | | | | The value needs to be a default-constructed instance. Otherwise a number of branches in this method produce unwanted effects, such as appending to an already existing array rather than creating a new one. Amends commit 1b89c1edcae68351632c2755e5408410c2ff98e3. Pick-to: 6.7 6.5 Fixes: QTBUG-125429 Change-Id: If175a02b3a794573abc03df206fbddd41f2855b4 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QtQml: Restore as-casting of incompatible type to value typeUlf Hermann2024-05-281-1/+18
| | | | | | | | | | This used to (mistakenly) return null, which is much better than constructing the value type. Amends commit 71e259837967f1eee50c057229094c2a971a1a61. Change-Id: I3da9d0a765d118dc8d85d7c5dde91936855bbf67 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* TypedArray: Preserve exact NaN bit pattern on fill()Ulf Hermann2024-05-251-2/+8
| | | | | | | | ... as required by ECMAScript. Pick-to: 6.7 6.5 Change-Id: I31bc7e6a87e404a8e6d314c99f163f82208e13a1 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QML: Deprecate coercion on type assertionsUlf Hermann2024-05-242-3/+20
| | | | | | | | | | | | | | | | | | | 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>
* QtQml: Let QQmlTypeWrapper act as a constructor for its typeUlf Hermann2024-05-235-54/+100
| | | | | | | | | | | | | | This calls any invokable ctors and only invokable ctors. Any type that doesn't have an invokable ctor won't even expose a function, since functions are determined by the presence of call methods. QMetaObjectWrapper gains the same functionality since the code is shared. It can now not only create object types but also value types. Task-number: QTBUG-124662 Change-Id: Ib30098666f67aef7a1f464f52d9b0bbd70d896d1 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* Load a QJsonArray property as a SequenceLuca Di Sera2024-05-221-5/+2
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | When a property coming from C++ is used in a QML file, the engine might perform some internal conversion to handle the type. When a `QJsonArray` property is loaded, currently, it will be converted to an internal `JsonObject` representation containing an engine-owned array. When the conversion is done, the data from the `QJsonArray` is copied as a part of this process. The performed copy is expected to act like a Javascript array. Nonetheless, due to the way the conversion is implemented, using the property will perform the requested work on the copy without writing back to the original property. This in turn prevents certain operation from working as expected, in particular the mutable methods on the array prototype. For example, if `jsonArray` is a `QJsonArray` property with a C++ provenance, the following will not register the push on the original property, so that it will be lost the following time the property is accessed: ``` ... jsonArray.push(4) ... ``` As a `QJsonArray` property should behave as a Javascript array when used from QML, a `QJsonArray` property will now be converted to a `Sequence`, an internal representation that behaves like a Javascript array but acts as a reference object with property write-backs, to allow in-place mutations to be correctly reflected on the original property. Currently, a `QJsonArray` `Sequence` call to the sort method will not correctly work due to the way the sort method is implemented. This is currently left as non-working and will be fixed separately. A non-exhaustive test-case was added to check that a C++ loaded `QJsonProperty` acts as a Javascript array with regards to the Array prototype methods, taking into consideration the inability of the sort method to currently work. Task-number: QTBUG-111015 Change-Id: Iec48fe4cba9adb9b794e5a568985b86b8da4556c Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* QtQml: Add some asserts to help the code checkerUlf Hermann2024-05-161-0/+2
| | | | | | | | | The name of a bound function cannot be null. Amends commit 8b6a9403bf2e04d34b9b07d2780186029fab99d0 Change-Id: I8a32d4c2cc8170f1b5d722cd8c5b823aa2211975 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* V4: Move FunctionObject flags into VTableUlf Hermann2024-05-148-69/+83
| | | | | | | | | | | | | | | | | | | These are really rather generic type traits that shouldn't be stored in individual objects. Moving them away slims down FunctionObject even more. FunctionObject doesn't add any extra overhead on top of Object anymore. You also cannot easily cast an object that doesn't implement any call methods to FunctionObject anymore. Therefore, we can derive from FunctionObject even if we only need to implement call methods in a further derived class. The fact that ProxyObject is not a FunctionObject but its derivatives are is already tested as part of the ecmascript test suite. Task-number: QTBUG-124662 Change-Id: I5632de8c54ac1d6a4b15c4926c655b87b475db49 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* V4: Slim down FunctionObjectUlf Hermann2024-05-1448-369/+458
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | Most FunctionObjects do not actually need their custom jsCall members. They will only call the functions from the vtable anyway. FunctionObject can therefore be split into a static and a dynamic variant. Only the dyanmic variant needs to carry (and invoke) the extra pointer. The jsCallWithMetaTypes pointer is completely pointless because none of the dynamic functions actually implement it. Furthermore, the QV4::Function and QV4::ExecutionContext pointers in FunctionObject are only needed by actual JavaScript functions. The builtins that like to be dynamic functions never need them. Therefore, split out another class for this. In the generic FunctionObject, we need the capability to decide at run time whether the function shall be a constructor or not. Add a flag to replace the check for jsCallAsConstructor. Also, where we can, avoid the pessimization of checking whether a function is a constructor before trying to call it as constructor. Rather have the default implementation throw the exception. As a side effect, for most functions we don't need an ExecutionContext anymore. The engine is enough. Task-number: QTBUG-124662 Change-Id: Iac657fa71288dd6ec230a33de2986ba3bcf4628c Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QtQml: Move QMetaObjectWrapper into separate header/impl filesUlf Hermann2024-04-285-140/+214
| | | | | | | | | We want to use it from QQmlTypeWrapper and avoid circular includes. Task-number: QTBUG-124662 Change-Id: I4c78a17eb262a303b7239bbdd853ec02d609c330 Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QtQml: Properly enforce signatures of AOT-compiled functionsUlf Hermann2024-04-266-64/+73
| | | | | | | | | | | | | Pass the metatypes of the contained types rather than the stored types. [ChangeLog][QtQml][Important Behavior Changes] The AOT compiled code for type-annotated JavaScript functions does not let you pass or return values of the wrong type anymore. Fixes: QTBUG-119885 Change-Id: I685d398c0745d32a999a3abd76c622a2c0d6651f Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* gc: Fix stale pointers in WeakValuesFabian Kosmale2024-04-252-18/+53
| | | | | | | | | | | | | | | | | WeakValue::set shold normally not mark objects, given that a weak value is not supposed to keep an object alive. However, if we are past GCState::HandleQObjectWrappers, nothing will reset weak values referencing unmarked values, even if sweep collects the referenced value. That leads to stale pointers, and then most likely to crashes. To avoid this, we mark the objects under this special condition. The test is written in a way that would also allow for resetting the new weak values instead, but the current implementation treats memory usage for throughput and doesn't revisit weak values to reset them. Change-Id: I789f63c1d8609957711c2253d2e76b4bd3f9810a Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* QmlCompiler: Perform return value assignment inside generated codeUlf Hermann2024-04-241-1/+1
| | | | | | | | | | | | This is in preparation for using exact types and actually enforcing them. We shouldn't wrap the return value into a QVariant in order to then painstakingly unwrap it again. The generated code can already do the right thing. Task-number: QTBUG-119885 Change-Id: I13e517967ee982be717024a9abb74d5e02a185d6 Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QtQml: Fix some type conversion edge casesUlf Hermann2024-04-232-15/+24
| | | | | | | | | | | | If the type conversion code fails to convert an argument, we still need to make sure the argument has a definite value. Otherwise we may trigger undefined behavior somewhere down the line. Furthermore, we want to look at the precise type when converting list properties. Otherwise we get a list property without any methods back when converting. Pick-to: 6.7 6.5 6.2 Change-Id: I012c0360ef1578c768362d5a4648252d3e6803d8 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* QtQml: Use QHash/QMap's constFind() to avoid unnecessary detachesVladimir Belyavsky2024-04-222-8/+8
| | | | | | | | Use QHash/QMap's constFind() instead of non-const find() where applicable to avoid unnecessary detaches. Change-Id: I6b31af1d163d11deb229681ff7e2f6c9f8335d8c Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* QtQml: do not obtain the stack trace twice in CallPrecise()Vladimir Belyavsky2024-04-221-1/+1
| | | | | | Change-Id: Ieb108a84f5c1fefe813ac0af6c2ca9332fdbefe8 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* V4: Handle all array-like containers when converting to QJsonArrayOlivier De Cannière2024-04-223-11/+14
| | | | | | | | | | | | | | | | Commit b9bfdea0e2c6721d2306af0ecc44f88da9988957 removed specialized code for QVariantList conversions by relying on sequences instead. Some checks for sequences and other array-like containers were missed. Add those and perform all calls to QJsonObject::toJsonArray through a common QV4::Object interface. Amends b9bfdea0e2c6721d2306af0ecc44f88da9988957 Pick-to: 6.7 Fixes: QTBUG-123993 Change-Id: Ia671d556af4f2b4d44f652fa93182977d88621f2 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* UrlObject: Avoid potential extra QList detachesVladimir Belyavsky2024-04-221-6/+1
| | | | | | | | | | There might be potential extra QList detach due to the use of non-const QList iterators in UrlSearchParamsPrototype::method_delete(). To avoid this, we can use QList::removeIf() which doesn't detach if there is nothing to remove and also makes the code a bit clearer. Change-Id: Ia4d2d2d0ac2d1dc4b08ed0b34b701bab7bca6250 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* QtQml: Add a wrapper builtin for QQmlV4Function*Ulf Hermann2024-04-122-2/+2
| | | | | | | | | This way qmltyperegistrar can recognize it and refrain from warning about it. Task-number: QTBUG-101143 Change-Id: I598140e7e90dbd3e27a78c26eff3d46f0fd3e989 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* V4: Don't call methods on nullptrUlf Hermann2024-04-051-18/+26
| | | | | | | | | | The root node of a sparse array can be null. Pick-to: 6.7 6.5 6.2 5.15 Fixes: QTBUG-123596 Change-Id: I5ea7fd73aeec460082d0cf19c7fc8a01993ed1f9 Reviewed-by: Semih Yavuz <semih.yavuz@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* Fix heap-buffer-overflow in ESTable::removeOliver Dawes2024-04-042-17/+17
| | | | | | | | | | | Fixes a heap-buffer-overflow issue in ESTable::remove due to an off by one error in the count provided to memmove calls. Task-number: QTBUG-123999 Pick-to: 6.7 6.5 6.2 5.15 Change-Id: I4ee0fbc16ba8936ea921e5f1d1bb267dae0b1d5f Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
* ArrayData/MemberData: Prove Fixed gc interactionFabian Kosmale2024-03-051-1/+1
| | | | | | | | | | | | | | | | | | When setting values on a newly allocated Array- or MemberData, d7aa952e143accc18d54707d956d019272197078 made the assumption that it is safe to skip the write-barrier, as the new values would be marked when the "values" member would be written to (pushing the new data on the mark stack, and then later marking all objects stored in values when it gets popped and markObjects runs). Now that we no longer do black allocations, this actually holds true. Add a unit test to verify it. Task-number: QTBUG-119274 Task-number: QTBUG-121910 Change-Id: Ia1ceeaffeaf30dc1fb2b9e1992dd0b599050294c Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> Reviewed-by: Semih Yavuz <semih.yavuz@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
* qv4mm: Handle running out of native heap memoryFabian Kosmale2024-03-052-8/+2
| | | | | | | | | | | | | | | | | With the current setup, we would have risked running out of native heap space if the incremental garbage collection takes too long. Remedy this by forcing the current gc run to complete immediately once we overshoot the limit by a factor of 3/2. We still need to respect that the gc must not run in a critical section: Handle this by rechecking the condition when leaving the critical section, and only forcing a gc run at that point if necessary. Note that we currently update the gc limits before the gc cycle finishes, so that the effective limit might actually not be 3/2 in that case. This will be remedied in an upcoming patch. Change-Id: I19c367bd1ff6ffc129293fc79e39b101f0728135 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* Prepare for white allocations during gc (9/9): ExecutableCompilationUnitFabian Kosmale2024-03-052-6/+18
| | | | | | | | | | | | | | | | | | | | | | | ExecutableCompilationUnits are meant to be part of the roots as far as the gc is concerned. However, they can be created at any point in time, notably also while the GC is running. This was safe so far because all allocations are black, and the compilation unit will only reference freshly allocated heap items. As we want to move away from that pattern, we have to change this: We could use the typical combination of QV4::Scope and usage of the WriteBarrier, however that would add overhead for a very uncommon case (except when using QV4_MM_AGGRESSIVE_GC). Instead, we temporarily block the garbage collection, reset the state of an ongoing garbage collection and at the end of the setup of the ExecutableCompilationUnit, we mark the ExecutableCompilationUnit if the GC was already running. Introduce a new blocked state (CriticalSection) to distinguish between the normal blocked state (gc is running) and the new state where we must not even run an incremental gc to completion until the section has finished. Task-number: QTBUG-121910 Change-Id: I1dba3cc8a4f8d2b741a1a5e84fdfe7736b08e166 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* Prepare for white allocations (7/9): QQuick(Window|View)Fabian Kosmale2024-03-052-1/+26
| | | | | | | | | | | | | Prevent QObjectWrapper from being gced - if we use white alloctations, and the weak values have already been marked, then nothing will mark the newly created QObjectWrapper. Use a helper function which takes care of the marking, and call it in the appropriate places. Also mark the normal wrap and wrapConst functions as [[nodiscard]] to avoid this issue from resurfacing in the future, and adjust a few call-sites to also call ensureWrapper. Change-Id: I8d4c73ae62b61d21b1456a3b096fc6a42f302160 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
* Prepare for white allocations during gc(6/9): Engine setupFabian Kosmale2024-03-051-0/+3
| | | | | | | | | | | | | | | | We temporarily forbid the GC from running before global object is setup, as our root set marking would otherwise run into issues in incremental mode (we don't revisit roots, and roots don't generally employ write barriers, so we might miss internal classes referenced by the global object). The GC would normally never run while the engine's constructor has not completed, however this does not hold true when QV4_MM_AGGRESSIVE_GC is enabled. Task-number: QTBUG-121910 Change-Id: I08360005f66bb6e6a36da2e16704093398f0d154 Reviewed-by: Sami Shalayel <sami.shalayel@qt.io> Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io> Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>