Porting from QtScript to QJSEngine, setScriptClass, and toScriptvalue and fromScriptvalue functions.
-
Hello everyone,
I am porting an application from Qt5 to Qt6, and as such I need to port the internal scripting systems from QtScript to QJSEngine. We use the scripting system for a number of things in the application and as such the old implementation was quite complex and full featured, providing a complete API to the scripting environment,
and offering near complete control of the application via scripting, as well as a bunch of internal functions that we use for testing. One of the more complex elements of the scripting system is that we can use functions provided by the script system to self document various objects and object state within the application. This enables us to
present a representation of an object which is dynamically generated and is able to account for inherited properties and relationships.Not only am I having some problems finding a working solution, I am also unsure if the bits that I have got working are the best usage of the new new QJSengine environment. Obviously backwards compatibility with the previous system is the #1 goal, but given the lack of documentation and example code around creating larger more complex scripting environments with QJSengine, I'm unsure as to if I have
implemented the best possible solution for the problem at hand.This is where I am running into some problems.
I will try and outline my main concerns, and hopefully someone can guide me in the right direction.Q1.
What is the replacement for the toScriptValue and fromScriptValue functions?
Is supplying a conversion function via QMetaType::registerConverter supposed to be a
like for like replacement for the toScriptValue and fromScriptValue fucntionality?
You can see my Impl of these below....Additional detail:
In the QtScript based Impl I'm using qScriptRegisterMetaType<MyBaseObject*>(engine, toScriptValue, fromScriptValue);
To register the MyBaseObject* metatype and the to* and from* script functions, accordingly. Usng the old QtScript Impl the to and from functions ends up getting registered as the MarshalFunction and DemarshalFunction functions, this allows the script engine to execute the specified to* and from* functions when dealing with any Object derived from a MyBaseObject* and created via the toScriptvalue call.
Can I use the QMetaType::registerConverter<QJSValue, MyBaseObject*> and QMetaType::registerConverter<MyBaseObject*, QJSValue> functions as like for like replacements for the toScriptValue and fromScriptValue functions, after regsitering my metatype with qRegisterMetaType<MyBaseObject*>()
I have tried registering conversion functions for both of the required conversions, but the engine is not triggering them when I would expect.Q2.
What is the QJSengine replacement for the QtSCriptEngine::setScriptClass() mechanism?Additional detail:
In the old QtScript based Impl, the code often creates a QScriptValue from a c++ Object, and then craft a special QtScriptValue to return to the scripting environment, as a part of this process I use the setScriptclass function to tell the engine to treat this object as a specific type, rather than just a QtScriptValue. You can see how I do this in the Impl of the toScriptValue and fromScriptValue functions...QScriptValue MyBaseObjectScriptPrototype::toScriptValue( QScriptEngine* engine, MyBaseObject* const& object ) { if(!object) return engine->nullValue(); ScriptableScope scope(engine); MyBaseObjectScriptPrototype* instance = engine->findChild<MyBaseObjectScriptPrototype*>(); Q_ASSERT(instance); const MyBaseObjectDataSet* localDataSet = scope.scriptSystem()->dataSet(); const MyBaseObjectDataSet* remoteDataSet = object->dataSet(); QSet<MyBaseObjectPointer*>* tracker; if(localDataSet == remoteDataSet) tracker = 0; else tracker = &(instance->mTrackedManagedPtrs[remoteDataSet]); QScriptValue data = engine->newVariant(TrackedSmartPtr<MyBaseObjectPointer>::newQVariant(object, tracker)); QScriptValue result = engine->newObject(); result.setScriptClass(instance); result.setData(data); result.setPrototype(instance->mExtentionPrototypes.value(object->metaObject())); return result; } void MyBaseObjectScriptPrototype::fromScriptValue( const QScriptValue& object, MyBaseObject*& managedObject ) { managedObject = qvariant_cast<MyBaseObject*>(object.data().toVariant()); }
Obviously there is no .data() field in the QJSValue type, but I am planning to simply use a property called "data" enable the same behavior.
But by using setScriptClass(instance), ie. a MyBaseObjectScriptPrototype*, I can use functions within the MyBaseObjectScriptPrototype class to manage how I handle the QScriptValue, which in this case is essentially a wrapper for a MyBaseObject*, including supplying / injecting properties based on the actual object that is inherited from the MyBaseObject*.
But I am unsure of the Best way to enable this behavior in the QJSengine scripting environment?Sorry for such a long ( and slightly rambling post ) but I think this is a complex problem that requires a lot of context to accurately illustrate the issues I am running into.
Thanks,
James -
Well after much investigation i have discovered that it is possible to solve my issue. I believe it would be possible by using the proxy mechanism within the javascript itself to facilitate the functionality i required.
However given the complexity of that solution we have decided to simply not use the QJS functionality at all, and simply replace the entire QtScriptEngine systems with one based on the QuickJS library.
QuickJS is a much better fit for our needs, and a closer fit for a lot of the functionality that previously existed in the old QtScriptEngine stuff.
-
-
@Jammin44fm May I ask how you were able to port from QtScript to QuickJS? Are you still able to expose complete control of your application via scripting?