New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
qt5: Replace deprecated QtScript with QJSEngine #8986
Comments
Commented by: Be-ing It seems there isn't a way to get the current "this" object from a C++ function that is exposed to the JS environment, which will break engine.makeConnection and engine.beginTimer. However, QJSEngine supports ECMAScript 5, which introduced Function.prototype.bind. This will require some careful planning... |
Commented by: Be-ing I am assigning this to the 2.3 milestone for now, but I'm not sure we'll get to it by then. Let's reconsider when the 2.2 beta branch is cut. |
Commented by: Be-ing It may make sense to implement this concurrently with Ctlra support. |
Commented by: Pegasus-RPG In the worst case, can we do something in C++ that works around the "this" object problems? This also may be a good time to plan how we want to expose some C++ objects (e.g. ControlObjects) directly to scripts. (I'm not even sure if QJSEngine can do that like QtScript can.) |
Commented by: ferranpujolcamins
The current implementation is actually wrong. We should not artificially bind to "this" in makeConnection and beginTimer. It can cause unexpected behaviour (see attached example). As a javascript programmer, you must know that sometimes "this" won't be bind to what you expect at first glance. The current implementation makes the thing easier in some cases, but introduces unexpected behaviour in other cases. I prefer users to have the need to always bind "this" themselves over them to figure out why something that should not happen is actually happening. |
Commented by: ferranpujolcamins By the way, are you actively working on this / have you made significant progress? I'd like to take it otherwise. |
Commented by: Be-ing
You are correct, this imposes some unexpected limitations. That actually just caused me trouble working on the Xone K2 mapping and I had to modify the Components library to work around it: 987520e#diff-9bedde251ff7d14d89b40d05168275e4R617 I have not started working on this. I have only very briefly glanced at the QJSEngine documentation. I'd be happy to work together with you on this after the 2.1 release. |
Commented by: ferranpujolcamins Nice, I'm working on it meanwhile. |
Commented by: ferranpujolcamins I think the solution is to add support for ES6 arrow functions. Users that are new to JS will just learn to use arrow functions in makeConnection and beginTimer because that's what we'll document in the wiki. On the other hand, seasoned JS developers will already know what a arrow function does, so they will know what's going on, because Mixxx will not bind "this" unexpectedly anymore. How can we bring support for arrow functions? 1 - add a second QJSEngine in ControllerEngine, let's call it preprocessorEngine. I'd like to write tests for each JS extension we add, so I think it's not a good idea to bundle a full-blown babel build to write ES6 code. We can add specific JS extensions you fancy. I'd like to add just specific plugins for specific and justified purposes. How can we keep compatibility for old scripts? I think I'll add a ControllerEngine interface and then make a class for the current implementation and a class for the new implementation. Then add a engine version field in the xml file to choose which version to use (if no version is specified, use the new version). Then, specify old engine version for mappings that are not tested with the new engine when we are about to release Mixxx. What do you think of these ideas? |
Commented by: ferranpujolcamins I've just discovered this: https://github.com/quickly/quickly |
Commented by: ferranpujolcamins Although, the difference is that quickly runs babel on node, and my plan is to run it on QJSEngine. So better stick with the original plan and only add the arrow function pluguin. |
Commented by: Be-ing Yes, the current way of getting "this" for functions passed to engine.makeConnection is basically the same as arrow functions. However, I don't think it's a great idea to bring in external dependencies to hack around QJSEngine's limitations. I think that effort would be better spent contributing the desired functionality upstream to Qt. I recognize that is likely harder, but I think it would be more sustainable. I also don't think it's a good idea to recommend developers to rely on external JavaScript tooling to write mappings. We can mention that it's a possibility, but I am afraid that setting that up would be such a pain that it would deter people from contributing mappings. IIRC the ancient QtScript JS interpeter does not support Function.prototype.bind, but I think QJSEngine does. Requiring a call to Function.prototype.bind for every function passed to engine.makeConnection would be annoying, but in general I don't think controller mapping developers should be using engine.makeConnection directly. I think Component.prototype.connect should abstract those details away unless the mapping needs something fancy for a Component with a custom "connect" function implemented. I am quite confused why Qt hasn't implemented full ES6 support yet considering it uses JavaScript so heavily now. There is an issue on Qt's tracker for this: |
Commented by: ferranpujolcamins Developers would not need to rely on external Javascript functionality. |
Commented by: ferranpujolcamins Other idea: Let's make engine.makeConnection take an additional parameter that is the object that the callback will be bond to. Then, on engine init, eval the following code:
So if you call makeConnection on an object instead of on engine, "this" is bond to the object. The same can be done with beginTimer. I think is a good enough compromise solution until we can get "this" for a function in QJSEngine. Does this better appeal to you? |
Commented by: ferranpujolcamins Another way to do this is to make engine.makeConnection take no additional parameter and make it execute its callback as is, without binding it to any object. Then bind the function to "this" in Object.prototype.makeConnection. |
Commented by: Be-ing That is quite similar to what I was alluding to about above. I think adding nonstandard functions to the prototype of language builtins like Object will be odd for developers who know JS well. But we can accomplish essentially the same thing with Components by changing the line in Component.prototype.connect:
to
|
Commented by: ferranpujolcamins The 2018-06-30 date is too tight for me to finish this. Unless feature freeze for 2.2 is postponed a week, I have to unassign this from me. |
Commented by: Be-ing This was already assigned to 2.3 anyway. I don't think there's any need to rush it for 2.2. |
Commented by: ferranpujolcamins Then I'm retaking it :) |
Commented by: Be-ing This post has some background on why QtScript has been deprecated: According to https://forum.qt.io/topic/52306/qt-5-5-qt-script-deprecated-what-is-replacement/2 QtScript will continue to be a part of Qt until at least Qt 6. In May 2017 it was announced that planning for Qt 6 would begin after Qt 5.11, which is the current stable version. However, I cannot find any information about Qt 6 more recent than 2017. So I think targeting this migration to QJSEngine for Mixxx 2.3 is a good idea to avoid the rug being pulled out from under us. |
Commented by: Be-ing Good news! Work is in progress to upgrade QJSEngine to support ECMAScript 2017: https://bugreports.qt.io/browse/QTBUG-47735?focusedCommentId=398077&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-398077 |
Commented by: Be-ing ECMAScript 2017 support including arrow functions are targeted for Qt 5.12, which will be released in November 2018. So I anticipate distributions will be packaging it in spring 2019 (for Fedora, this would be Fedora 30). I propose we start working on migrating to QJSEngine now and try using Function.prototype.bind(this) in place of arrow functions. However, it may be a good idea to hold off releasing a new XML-less mapping system and rewriting Components for that until distributions are shipping Qt 5.12. |
Issue closed with status Fix Committed. |
Reported by: Pegasus-RPG
Date: 2017-11-21T18:00:37Z
Status: Fix Committed
Importance: Low
Launchpad Issue: lp1733666
Tags: controllers, qt5
Attachments: sample.js
As of Qt5.5, QtScript for Applications has been deprecated: http://wiki.qt.io/New_Features_in_Qt_5.5#Deprecated_Functionality
Its replacement is QJSEngine: http://doc.qt.io/qt-5/qjsengine.html
While there is currently no mention of QtScript being removed, it's not being improved so we should consider migrating.
The text was updated successfully, but these errors were encountered: