Hi,

after some more experimentation, I've realized the problem is actually of larger dimension. Since function signatures are ignored when interpreting or running JIT-compiled QML code, you already get divergent behavior without all the value type write back problems. Take for example the following function:

function getLength(a: string) : int { return a.length }

Now this function is nicely compilable to C++. If it's caller is compiled to C++, we could enforce the signature at compile time. Suppose the caller is _not_ compiled to C++, though. The caller does getLength(b) where b is of a value type with a "length" property. When getLength is AOT-compiled, the this coerces b into a string (most likely "[object Object]", but you can have a custom toString() method) and gets the length of that. If it's interpreted, the type annotations is ignored, and getLength will get the "length" property of a.

You may argue that the coercion should fail in the case where getLength() is AOT-compiled, but since at the call site we're deep in JavaScript land, that would be fairly incosistent. When calling C++-defined methods, we can coerce everything to string.

(OK, the coercion rules when calling a QObject method are already different from what we do when calling a typed JS method ... but let's solve that another time.)

So, the clean solution would be to coerce all arguments into their declared types even when interpreting or running JIT-compiled code.

Now, the type coercion is not free. If you call from one AOT-compiled function into a different one, we don't have to do it because the AOT-compiled functions are already using the C++ types. However, when calling from an interpreted or JIT'ed function into an AOT-compiled one, there is some overhead. Most of the time it's well worth it because the actual function then runs much faster.

Considering this, I don't want to force the extra call overhead on calls to interpreted or JIT'ed functions, as that would actually be slower. It would discourage people from using the type annotations.

Furthermore, as shown above, enforcing the types would introduce subtle behavior changes into code that's not compiled to C++. As the compilation to C++ envolves a number of other changes to your application (such as using qt_add_qml_module), we can probably live with a slight difference in behavior between interpreted and AOT-compiled code. It would be somewhat worse to introduce deliberate behavior changes to code completely unaffected by any of this.

You should be _able_ to enforce the same behavior in either execution mode, though. Therefore, my current idea is to introduce a pragma FunctionSignatureBehavior that you can set to "Enforced" or "Ignored". If explicitly enforced, the engine would coerce the arguments even when calling an interpreted or JIT'ed function. If explicitly ignored, qmlcachegen and qmlsc would refuse to compile any functions and calls to QML-defined function in the component to C++. (they would still compile bindings and signal handlers that at most call C++-defined functions, though)

You can see my current work in progress at https://codereview.qt-project.org/c/qt/qtdeclarative/+/434663

regards,
ULf
_______________________________________________
Development mailing list
[email protected]
https://lists.qt-project.org/listinfo/development

Reply via email to