From cbe608aff89c2ac55d807377af1a2bbfa35e8b72 Mon Sep 17 00:00:00 2001 From: Heather Anderson Date: Sun, 9 May 2021 01:21:50 -0700 Subject: [PATCH] INCOMPLETE COMMIT - WILL BE FORCE-PUSHED attempt to isolate calls to QtScript to an interface we control --- assignment-client/CMakeLists.txt | 2 +- assignment-client/src/Agent.cpp | 44 +- assignment-client/src/Agent.h | 10 +- .../src/avatars/ScriptableAvatar.cpp | 10 +- .../src/avatars/ScriptableAvatar.h | 4 +- .../src/scripts/EntityScriptServer.cpp | 74 +- .../src/scripts/EntityScriptServer.h | 9 +- domain-server/CMakeLists.txt | 1 + interface/CMakeLists.txt | 4 +- interface/src/Application.cpp | 52 +- interface/src/Application.h | 7 +- interface/src/AvatarBookmarks.cpp | 10 +- interface/src/LODManager.cpp | 6 +- interface/src/LODManager.h | 8 +- interface/src/Menu.cpp | 1 + interface/src/avatar/AvatarManager.cpp | 12 +- interface/src/avatar/AvatarManager.h | 9 +- interface/src/avatar/MyAvatar.cpp | 38 +- interface/src/avatar/MyAvatar.h | 20 +- .../LaserPointerScriptingInterface.cpp | 6 +- .../raypick/LaserPointerScriptingInterface.h | 8 +- interface/src/raypick/PathPointer.cpp | 1 + .../src/raypick/PickScriptingInterface.cpp | 22 +- .../src/raypick/PickScriptingInterface.h | 8 +- .../src/raypick/PointerScriptingInterface.cpp | 6 +- .../src/raypick/PointerScriptingInterface.h | 9 +- .../src/raypick/RayPickScriptingInterface.cpp | 5 +- .../src/raypick/RayPickScriptingInterface.h | 8 +- .../AccountServicesScriptingInterface.cpp | 18 +- .../AccountServicesScriptingInterface.h | 12 +- .../AssetMappingsScriptingInterface.cpp | 2 +- .../AssetMappingsScriptingInterface.h | 2 - .../src/scripting/DesktopScriptingInterface.h | 1 - .../src/scripting/HMDScriptingInterface.cpp | 17 +- .../src/scripting/HMDScriptingInterface.h | 14 +- .../PlatformInfoScriptingInterface.h | 2 - .../scripting/SettingsScriptingInterface.cpp | 2 +- .../src/scripting/TestScriptingInterface.cpp | 4 +- .../src/scripting/TestScriptingInterface.h | 6 +- .../scripting/WindowScriptingInterface.cpp | 46 +- .../src/scripting/WindowScriptingInterface.h | 20 +- interface/src/ui/InteractiveWindow.cpp | 14 +- interface/src/ui/InteractiveWindow.h | 12 +- interface/src/ui/JSConsole.cpp | 58 +- interface/src/ui/JSConsole.h | 15 +- interface/src/ui/TestingDialog.cpp | 11 +- interface/src/ui/TestingDialog.h | 7 +- interface/src/ui/overlays/Overlays.cpp | 67 +- interface/src/ui/overlays/Overlays.h | 15 +- libraries/animation/CMakeLists.txt | 3 +- libraries/animation/src/AnimVariant.cpp | 82 +- libraries/animation/src/AnimVariant.h | 12 +- libraries/animation/src/AnimationCache.h | 2 - libraries/animation/src/AnimationObject.cpp | 17 +- libraries/animation/src/AnimationObject.h | 10 +- libraries/animation/src/Rig.cpp | 31 +- libraries/animation/src/Rig.h | 12 +- libraries/audio/CMakeLists.txt | 1 + libraries/audio/src/AudioEffectOptions.cpp | 12 +- libraries/audio/src/AudioEffectOptions.h | 12 +- libraries/audio/src/AudioInjectorOptions.cpp | 82 +- libraries/audio/src/AudioInjectorOptions.h | 11 +- libraries/audio/src/Sound.cpp | 10 +- libraries/audio/src/Sound.h | 9 +- libraries/avatars-renderer/CMakeLists.txt | 2 +- libraries/avatars/CMakeLists.txt | 3 +- libraries/avatars/src/AvatarData.cpp | 106 +- libraries/avatars/src/AvatarData.h | 20 +- libraries/baking/CMakeLists.txt | 1 + libraries/baking/src/MaterialBaker.cpp | 7 +- libraries/baking/src/MaterialBaker.h | 3 +- libraries/controllers/CMakeLists.txt | 3 +- .../controllers/src/controllers/Pose.cpp | 37 +- libraries/controllers/src/controllers/Pose.h | 10 +- .../src/controllers/ScriptingInterface.h | 1 - .../src/controllers/UserInputMapper.cpp | 120 +- .../src/controllers/UserInputMapper.h | 11 +- .../src/controllers/impl/Endpoint.h | 2 - .../src/controllers/impl/Filter.cpp | 1 - .../controllers/impl/MappingBuilderProxy.cpp | 7 +- .../controllers/impl/MappingBuilderProxy.h | 8 +- .../controllers/impl/RouteBuilderProxy.cpp | 7 +- .../src/controllers/impl/RouteBuilderProxy.h | 8 +- .../impl/conditionals/ScriptConditional.cpp | 4 +- .../impl/conditionals/ScriptConditional.h | 10 +- .../impl/endpoints/ScriptEndpoint.cpp | 38 +- .../impl/endpoints/ScriptEndpoint.h | 9 +- libraries/entities-renderer/CMakeLists.txt | 2 +- .../src/EntityTreeRenderer.cpp | 144 +-- .../src/EntityTreeRenderer.h | 13 +- libraries/entities/CMakeLists.txt | 4 +- .../src/AmbientLightPropertyGroup.cpp | 6 +- .../entities/src/AmbientLightPropertyGroup.h | 11 +- .../entities/src/AnimationPropertyGroup.cpp | 4 +- .../entities/src/AnimationPropertyGroup.h | 12 +- libraries/entities/src/BloomPropertyGroup.cpp | 4 +- libraries/entities/src/BloomPropertyGroup.h | 11 +- libraries/entities/src/EntityEditFilters.cpp | 126 +- libraries/entities/src/EntityEditFilters.h | 15 +- libraries/entities/src/EntityItemID.cpp | 16 +- libraries/entities/src/EntityItemID.h | 14 +- .../entities/src/EntityItemProperties.cpp | 137 ++- libraries/entities/src/EntityItemProperties.h | 38 +- .../entities/src/EntityItemPropertiesMacros.h | 225 ++-- .../entities/src/EntityScriptingInterface.cpp | 166 +-- .../entities/src/EntityScriptingInterface.h | 46 +- libraries/entities/src/EntityTree.cpp | 18 +- libraries/entities/src/GrabPropertyGroup.cpp | 6 +- libraries/entities/src/GrabPropertyGroup.h | 10 +- libraries/entities/src/HazePropertyGroup.cpp | 4 +- libraries/entities/src/HazePropertyGroup.h | 11 +- .../entities/src/KeyLightPropertyGroup.cpp | 6 +- .../entities/src/KeyLightPropertyGroup.h | 11 +- libraries/entities/src/PropertyGroup.h | 9 +- libraries/entities/src/PulsePropertyGroup.cpp | 6 +- libraries/entities/src/PulsePropertyGroup.h | 11 +- .../src/RecurseOctreeToJSONOperator.cpp | 8 +- .../src/RecurseOctreeToJSONOperator.h | 11 +- .../src/RecurseOctreeToMapOperator.cpp | 8 +- .../entities/src/RecurseOctreeToMapOperator.h | 6 +- .../entities/src/RingGizmoPropertyGroup.cpp | 6 +- .../entities/src/RingGizmoPropertyGroup.h | 11 +- .../entities/src/SkyboxPropertyGroup.cpp | 4 +- libraries/entities/src/SkyboxPropertyGroup.h | 11 +- .../src/graphics-scripting/Forward.h | 1 - .../GraphicsScriptingInterface.cpp | 177 +-- .../GraphicsScriptingInterface.h | 12 +- .../GraphicsScriptingUtil.cpp | 23 +- .../GraphicsScriptingUtil.h | 13 +- .../src/graphics-scripting/ScriptableMesh.cpp | 37 +- .../src/graphics-scripting/ScriptableMesh.h | 21 +- .../graphics-scripting/ScriptableMeshPart.cpp | 7 +- .../graphics-scripting/ScriptableMeshPart.h | 16 +- .../graphics-scripting/ScriptableModel.cpp | 4 +- .../src/graphics-scripting/ScriptableModel.h | 2 - .../src/model-networking/SimpleMeshProxy.h | 4 - libraries/networking/CMakeLists.txt | 1 + libraries/networking/src/AssetClient.cpp | 1 - .../src/BaseAssetScriptingInterface.h | 2 +- .../src/LocationScriptingInterface.cpp | 11 +- .../src/LocationScriptingInterface.h | 11 +- libraries/networking/src/ResourceCache.h | 10 +- libraries/physics/CMakeLists.txt | 1 + libraries/pointers/CMakeLists.txt | 2 +- libraries/recording/CMakeLists.txt | 2 +- libraries/render-utils/CMakeLists.txt | 2 +- libraries/render-utils/src/Model.h | 1 - .../script-engine-qtscript/CMakeLists.txt | 10 + .../src/ArrayBufferClass.h | 4 +- .../src/ArrayBufferPrototype.cpp | 4 +- .../src/ArrayBufferPrototype.h | 0 .../src/ArrayBufferViewClass.cpp | 5 +- .../src/ArrayBufferViewClass.h | 8 +- .../src/BaseScriptEngine.cpp | 0 .../src/BaseScriptEngine.h | 29 +- .../src/DataViewClass.cpp | 2 +- .../src/DataViewClass.h | 2 +- .../src/DataViewPrototype.cpp | 3 + .../src/DataViewPrototype.h | 0 .../src/ScriptEngineQtScript.cpp | 914 ++++++++++++++ .../src/ScriptEngineQtScript.h | 508 ++++++++ .../src/TypedArrayPrototype.cpp | 2 + .../src/TypedArrayPrototype.h | 4 +- .../src/TypedArrays.cpp | 27 +- .../src/TypedArrays.h | 20 +- libraries/script-engine/CMakeLists.txt | 22 +- .../src/AbstractScriptingServicesInterface.h | 7 +- .../script-engine/src/ArrayBufferClass.cpp | 180 --- .../src/AssetScriptingInterface.cpp | 136 +- .../src/AssetScriptingInterface.h | 51 +- .../src/AudioScriptingInterface.cpp | 7 +- .../src/AudioScriptingInterface.h | 3 +- .../src/ConsoleScriptingInterface.cpp | 114 +- .../src/ConsoleScriptingInterface.h | 35 +- libraries/script-engine/src/EventTypes.cpp | 17 +- libraries/script-engine/src/EventTypes.h | 4 +- libraries/script-engine/src/KeyEvent.cpp | 53 +- libraries/script-engine/src/KeyEvent.h | 10 +- libraries/script-engine/src/MIDIEvent.cpp | 31 +- libraries/script-engine/src/MIDIEvent.h | 14 +- libraries/script-engine/src/Mat4.cpp | 5 +- libraries/script-engine/src/Mat4.h | 4 +- .../script-engine/src/MenuItemProperties.cpp | 42 +- .../script-engine/src/MenuItemProperties.h | 13 +- .../src/ModelScriptingInterface.cpp | 37 +- .../src/ModelScriptingInterface.h | 18 +- libraries/script-engine/src/MouseEvent.cpp | 30 +- libraries/script-engine/src/MouseEvent.h | 12 +- .../src/PointerEvent.cpp | 118 +- .../src/PointerEvent.h | 12 +- libraries/script-engine/src/Quat.cpp | 5 +- libraries/script-engine/src/Quat.h | 5 +- .../src/RecordingScriptingInterface.cpp | 36 +- .../src/RecordingScriptingInterface.h | 12 +- .../src/SceneScriptingInterface.h | 1 - .../script-engine/src/ScriptAudioInjector.cpp | 12 +- .../script-engine/src/ScriptAudioInjector.h | 11 +- libraries/script-engine/src/ScriptContext.h | 30 + libraries/script-engine/src/ScriptEngine.h | 1092 ++--------------- .../script-engine/src/ScriptEngineCast.h | 99 ++ libraries/script-engine/src/ScriptEngines.cpp | 151 +-- libraries/script-engine/src/ScriptEngines.h | 38 +- .../{ScriptEngine.cpp => ScriptManager.cpp} | 886 ++++++------- libraries/script-engine/src/ScriptManager.h | 941 ++++++++++++++ libraries/script-engine/src/ScriptProgram.h | 24 + libraries/script-engine/src/ScriptUUID.cpp | 5 +- libraries/script-engine/src/ScriptUUID.h | 5 +- libraries/script-engine/src/ScriptValue.h | 185 +++ .../script-engine/src/ScriptValueIterator.h | 31 + .../script-engine/src/ScriptValueUtils.cpp | 888 ++++++++++++++ .../script-engine/src/ScriptValueUtils.h | 262 ++++ libraries/script-engine/src/Scriptable.h | 59 + libraries/script-engine/src/ScriptsModel.h | 7 +- libraries/script-engine/src/SpatialEvent.cpp | 17 +- libraries/script-engine/src/SpatialEvent.h | 9 +- libraries/script-engine/src/TouchEvent.cpp | 66 +- libraries/script-engine/src/TouchEvent.h | 10 +- .../src/VariantMapToScriptValue.cpp | 26 +- .../src/VariantMapToScriptValue.h | 18 + libraries/script-engine/src/Vec3.cpp | 5 +- libraries/script-engine/src/Vec3.h | 4 +- .../script-engine/src/WebSocketClass.cpp | 92 +- libraries/script-engine/src/WebSocketClass.h | 61 +- .../src/WebSocketServerClass.cpp | 24 +- .../script-engine/src/WebSocketServerClass.h | 13 +- libraries/script-engine/src/WheelEvent.cpp | 32 +- libraries/script-engine/src/WheelEvent.h | 10 +- .../script-engine/src/XMLHttpRequestClass.cpp | 55 +- .../script-engine/src/XMLHttpRequestClass.h | 62 +- libraries/shared/CMakeLists.txt | 2 +- libraries/shared/src/RegisteredMetaTypes.cpp | 831 ------------- libraries/shared/src/RegisteredMetaTypes.h | 100 -- libraries/shared/src/ScriptValueUtils.cpp | 34 - libraries/shared/src/ScriptValueUtils.h | 21 - .../shared/src/VariantMapToScriptValue.h | 18 - libraries/shared/src/shared/MiniPromises.cpp | 15 - libraries/shared/src/shared/MiniPromises.h | 1 - .../src/shared/ScriptInitializerMixin.h | 7 +- libraries/ui/CMakeLists.txt | 4 +- libraries/ui/src/QmlFragmentClass.cpp | 19 +- libraries/ui/src/QmlFragmentClass.h | 14 +- libraries/ui/src/QmlWebWindowClass.cpp | 8 +- libraries/ui/src/QmlWebWindowClass.h | 13 +- libraries/ui/src/QmlWindowClass.cpp | 29 +- libraries/ui/src/QmlWindowClass.h | 18 +- libraries/ui/src/ui/QmlWrapper.h | 15 +- .../ui/src/ui/TabletScriptingInterface.h | 4 - .../ui/src/ui/ToolbarScriptingInterface.cpp | 20 +- .../ui/src/ui/ToolbarScriptingInterface.h | 1 - plugins/JSAPIExample/CMakeLists.txt | 1 + plugins/JSAPIExample/src/JSAPIExample.cpp | 32 +- scripts/system/request-service.js | 2 +- tests-manual/controllers/CMakeLists.txt | 2 +- tests-manual/entities/CMakeLists.txt | 2 +- tests-manual/gpu-textures/CMakeLists.txt | 2 +- tests-manual/gpu/CMakeLists.txt | 2 +- tests/octree/CMakeLists.txt | 2 +- tests/physics/CMakeLists.txt | 2 +- tools/oven/CMakeLists.txt | 1 + 259 files changed, 7007 insertions(+), 4996 deletions(-) create mode 100644 libraries/script-engine-qtscript/CMakeLists.txt rename libraries/{script-engine => script-engine-qtscript}/src/ArrayBufferClass.h (95%) rename libraries/{script-engine => script-engine-qtscript}/src/ArrayBufferPrototype.cpp (98%) rename libraries/{script-engine => script-engine-qtscript}/src/ArrayBufferPrototype.h (100%) rename libraries/{script-engine => script-engine-qtscript}/src/ArrayBufferViewClass.cpp (92%) rename libraries/{script-engine => script-engine-qtscript}/src/ArrayBufferViewClass.h (88%) rename libraries/{shared => script-engine-qtscript}/src/BaseScriptEngine.cpp (100%) rename libraries/{shared => script-engine-qtscript}/src/BaseScriptEngine.h (74%) rename libraries/{script-engine => script-engine-qtscript}/src/DataViewClass.cpp (97%) rename libraries/{script-engine => script-engine-qtscript}/src/DataViewClass.h (93%) rename libraries/{script-engine => script-engine-qtscript}/src/DataViewPrototype.cpp (99%) rename libraries/{script-engine => script-engine-qtscript}/src/DataViewPrototype.h (100%) create mode 100644 libraries/script-engine-qtscript/src/ScriptEngineQtScript.cpp create mode 100644 libraries/script-engine-qtscript/src/ScriptEngineQtScript.h rename libraries/{script-engine => script-engine-qtscript}/src/TypedArrayPrototype.cpp (99%) rename libraries/{script-engine => script-engine-qtscript}/src/TypedArrayPrototype.h (89%) rename libraries/{script-engine => script-engine-qtscript}/src/TypedArrays.cpp (92%) rename libraries/{script-engine => script-engine-qtscript}/src/TypedArrays.h (89%) delete mode 100644 libraries/script-engine/src/ArrayBufferClass.cpp rename libraries/{shared => script-engine}/src/PointerEvent.cpp (72%) rename libraries/{shared => script-engine}/src/PointerEvent.h (89%) create mode 100644 libraries/script-engine/src/ScriptContext.h create mode 100644 libraries/script-engine/src/ScriptEngineCast.h rename libraries/script-engine/src/{ScriptEngine.cpp => ScriptManager.cpp} (73%) create mode 100644 libraries/script-engine/src/ScriptManager.h create mode 100644 libraries/script-engine/src/ScriptProgram.h create mode 100644 libraries/script-engine/src/ScriptValue.h create mode 100644 libraries/script-engine/src/ScriptValueIterator.h create mode 100644 libraries/script-engine/src/ScriptValueUtils.cpp create mode 100644 libraries/script-engine/src/ScriptValueUtils.h create mode 100644 libraries/script-engine/src/Scriptable.h rename libraries/{shared => script-engine}/src/VariantMapToScriptValue.cpp (61%) create mode 100644 libraries/script-engine/src/VariantMapToScriptValue.h delete mode 100644 libraries/shared/src/ScriptValueUtils.cpp delete mode 100644 libraries/shared/src/ScriptValueUtils.h delete mode 100644 libraries/shared/src/VariantMapToScriptValue.h diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index abb94f95e3c..345118a89b0 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME assignment-client) -setup_hifi_project(Core Gui Network Script Quick WebSockets) +setup_hifi_project(Core Gui Network Quick WebSockets) # Fix up the rpath so macdeployqt works if (APPLE) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index a8ce7e30e59..1512c27d365 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -34,7 +35,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -180,7 +183,7 @@ static const QString AGENT_LOGGING_NAME = "agent"; void Agent::run() { // Create ScriptEngines on threaded-assignment thread then move to main thread. - DependencyManager::set(ScriptEngine::AGENT_SCRIPT)->moveToThread(qApp->thread()); + DependencyManager::set(ScriptManager::AGENT_SCRIPT)->moveToThread(qApp->thread()); DependencyManager::set(); @@ -372,7 +375,7 @@ void Agent::executeScript() { // the following block is scoped so that any shared pointers we take here // are cleared before we call setFinished at the end of the function { - _scriptEngine = scriptEngineFactory(ScriptEngine::AGENT_SCRIPT, _scriptContents, _payload); + _scriptManager = scriptManagerFactory(ScriptManager::AGENT_SCRIPT, _scriptContents, _payload); // setup an Avatar for the script to use auto scriptedAvatar = DependencyManager::get(); @@ -386,10 +389,11 @@ void Agent::executeScript() { scriptedAvatar->getHeadOrientation(); // give this AvatarData object to the script engine - _scriptEngine->registerGlobalObject("Avatar", scriptedAvatar.data()); + auto scriptEngine = _scriptManager->engine(); + scriptEngine->registerGlobalObject("Avatar", scriptedAvatar.data()); // give scripts access to the Users object - _scriptEngine->registerGlobalObject("Users", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("Users", DependencyManager::get().data()); auto player = DependencyManager::get(); connect(player.data(), &recording::Deck::playbackStateChanged, [&player, &scriptedAvatar] { @@ -493,26 +497,26 @@ void Agent::executeScript() { }); auto avatarHashMap = DependencyManager::set(); - _scriptEngine->registerGlobalObject("AvatarList", avatarHashMap.data()); + scriptEngine->registerGlobalObject("AvatarList", avatarHashMap.data()); // register ourselves to the script engine - _scriptEngine->registerGlobalObject("Agent", new AgentScriptingInterface(this)); + scriptEngine->registerGlobalObject("Agent", new AgentScriptingInterface(this)); - _scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get().data()); - _scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get().data()); - QScriptValue webSocketServerConstructorValue = _scriptEngine->newFunction(WebSocketServerClass::constructor); - _scriptEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue); + ScriptValuePointer webSocketServerConstructorValue = scriptEngine->newFunction(WebSocketServerClass::constructor); + scriptEngine->globalObject()->setProperty("WebSocketServer", webSocketServerConstructorValue); auto entityScriptingInterface = DependencyManager::get(); - _scriptEngine->registerGlobalObject("EntityViewer", &_entityViewer); + scriptEngine->registerGlobalObject("EntityViewer", &_entityViewer); - _scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter, + scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter, LocationScriptingInterface::locationSetter); auto recordingInterface = DependencyManager::get(); - _scriptEngine->registerGlobalObject("Recording", recordingInterface.data()); + scriptEngine->registerGlobalObject("Recording", recordingInterface.data()); entityScriptingInterface->init(); @@ -522,8 +526,8 @@ void Agent::executeScript() { DependencyManager::set(_entityViewer.getTree()); - DependencyManager::get()->runScriptInitializers(_scriptEngine); - _scriptEngine->run(); + DependencyManager::get()->runScriptInitializers(_scriptManager); + _scriptManager->run(); Frame::clearFrameHandler(AUDIO_FRAME_TYPE); Frame::clearFrameHandler(AVATAR_FRAME_TYPE); @@ -602,7 +606,7 @@ void Agent::setIsAvatar(bool isAvatar) { // start the timer _avatarQueryTimer->start(AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS); - connect(_scriptEngine.data(), &ScriptEngine::update, + connect(_scriptManager.data(), &ScriptManager::update, scriptableAvatar.data(), &ScriptableAvatar::update, Qt::QueuedConnection); // tell the avatarAudioTimer to start ticking @@ -638,7 +642,7 @@ void Agent::setIsAvatar(bool isAvatar) { nodeList->sendPacket(std::move(packet), *node); }); - disconnect(_scriptEngine.data(), &ScriptEngine::update, + disconnect(_scriptManager.data(), &ScriptManager::update, scriptableAvatar.data(), &ScriptableAvatar::update); QMetaObject::invokeMethod(&_avatarAudioTimer, "stop"); @@ -875,7 +879,7 @@ void Agent::aboutToFinish() { // drop our shared pointer to the script engine, then ask ScriptEngines to shutdown scripting // this ensures that the ScriptEngine goes down before ScriptEngines - _scriptEngine.clear(); + _scriptManager.clear(); { DependencyManager::get()->shutdownScripting(); @@ -895,8 +899,8 @@ void Agent::aboutToFinish() { } void Agent::stop() { - if (_scriptEngine) { - _scriptEngine->stop(); + if (_scriptManager) { + _scriptManager->stop(); } else { setFinished(true); } diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index b8e7652ccac..d3ed93e3560 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -15,11 +15,11 @@ #include #include -#include #include #include #include #include +#include #include #include @@ -28,11 +28,17 @@ #include +#include #include "AudioGate.h" #include "MixedAudioStream.h" #include "entities/EntityTreeHeadlessViewer.h" #include "avatars/ScriptableAvatar.h" +class ScriptEngine; +class ScriptManager; +using ScriptEnginePointer = QSharedPointer; +using ScriptManagerPointer = QSharedPointer; + class Agent : public ThreadedAssignment { Q_OBJECT @@ -89,7 +95,7 @@ private slots: void encodeFrameOfZeros(QByteArray& encodedZeros); void computeLoudness(const QByteArray* decodedBuffer, QSharedPointer); - ScriptEnginePointer _scriptEngine; + ScriptManagerPointer _scriptManager; EntityEditPacketSender _entityEditSender; EntityTreeHeadlessViewer _entityViewer; diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 752eaf81d28..15c9fce788d 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -17,16 +17,18 @@ #include #include +#include #include #include #include #include #include #include +#include #include -ScriptableAvatar::ScriptableAvatar() { +ScriptableAvatar::ScriptableAvatar(): _scriptEngine(newScriptEngine()) { _clientTraitsHandler.reset(new ClientTraitsHandler(this)); } @@ -311,7 +313,7 @@ AvatarEntityMap ScriptableAvatar::getAvatarEntityDataInternal(bool allProperties EntityItemProperties properties = entity->getProperties(desiredProperties); QByteArray blob; - EntityItemProperties::propertiesToBlob(_scriptEngine, sessionID, properties, blob, allProperties); + EntityItemProperties::propertiesToBlob(*_scriptEngine, sessionID, properties, blob, allProperties); data[id] = blob; } }); @@ -335,7 +337,7 @@ void ScriptableAvatar::setAvatarEntityData(const AvatarEntityMap& avatarEntityDa while (dataItr != avatarEntityData.end()) { EntityItemProperties properties; const QByteArray& blob = dataItr.value(); - if (!blob.isNull() && EntityItemProperties::blobToProperties(_scriptEngine, blob, properties)) { + if (!blob.isNull() && EntityItemProperties::blobToProperties(*_scriptEngine, blob, properties)) { newProperties[dataItr.key()] = properties; } ++dataItr; @@ -415,7 +417,7 @@ void ScriptableAvatar::updateAvatarEntity(const QUuid& entityID, const QByteArra EntityItemPointer entity; EntityItemProperties properties; - if (!EntityItemProperties::blobToProperties(_scriptEngine, entityData, properties)) { + if (!EntityItemProperties::blobToProperties(*_scriptEngine, entityData, properties)) { // entityData is corrupt return; } diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index 1e6046ba7e7..bb5b91ab97b 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -18,6 +18,8 @@ #include #include +class ScriptEngine; + /**jsdoc * The Avatar API is used to manipulate scriptable avatars on the domain. This API is a subset of the * {@link MyAvatar} API. To enable this API, set {@link Agent|Agent.isAvatar} to true. @@ -220,7 +222,7 @@ public slots: QHash _fstJointIndices; ///< 1-based, since zero is returned for missing keys QStringList _fstJointNames; ///< in order of depth-first traversal QUrl _skeletonFBXURL; - mutable QScriptEngine _scriptEngine; + mutable ScriptEnginePointer _scriptEngine; std::map _entities; /// Loads the joint indices, names from the FST file (if any) diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index 16931e8c266..6b866cb7acf 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -14,7 +14,9 @@ #include #include +#include #include +#include #include #include #include @@ -27,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -130,7 +133,7 @@ void EntityScriptServer::handleEntityScriptGetStatusPacket(QSharedPointerwritePrimitive(messageID); EntityScriptDetails details; - if (_entitiesScriptEngine->getEntityScriptDetails(entityID, details)) { + if (_entitiesScriptManager->getEntityScriptDetails(entityID, details)) { replyPacketList->writePrimitive(true); replyPacketList->writePrimitive(details.status); replyPacketList->writeString(details.errorInfo); @@ -175,7 +178,7 @@ void EntityScriptServer::handleSettings() { } void EntityScriptServer::updateEntityPPS() { - int numRunningScripts = _entitiesScriptEngine->getNumRunningEntityScripts(); + int numRunningScripts = _entitiesScriptManager->getNumRunningEntityScripts(); int pps; if (std::numeric_limits::max() / _entityPPSPerScript < numRunningScripts) { qWarning() << QString("Integer multiplication would overflow, clamping to maxint: %1 * %2").arg(numRunningScripts).arg(_entityPPSPerScript); @@ -236,7 +239,7 @@ void EntityScriptServer::pushLogs() { void EntityScriptServer::handleEntityScriptCallMethodPacket(QSharedPointer receivedMessage, SharedNodePointer senderNode) { - if (_entitiesScriptEngine && _entityViewer.getTree() && !_shuttingDown) { + if (_entitiesScriptManager && _entityViewer.getTree() && !_shuttingDown) { auto entityID = QUuid::fromRfc4122(receivedMessage->read(NUM_BYTES_RFC4122_UUID)); auto method = receivedMessage->readString(); @@ -250,13 +253,13 @@ void EntityScriptServer::handleEntityScriptCallMethodPacket(QSharedPointercallEntityScriptMethod(entityID, method, params, senderNode->getUUID()); + _entitiesScriptManager->callEntityScriptMethod(entityID, method, params, senderNode->getUUID()); } } void EntityScriptServer::run() { - DependencyManager::set(ScriptEngine::ENTITY_SERVER_SCRIPT); + DependencyManager::set(ScriptManager::ENTITY_SERVER_SCRIPT); DependencyManager::set(); DependencyManager::set(); @@ -446,52 +449,53 @@ void EntityScriptServer::selectAudioFormat(const QString& selectedCodecName) { void EntityScriptServer::resetEntitiesScriptEngine() { auto engineName = QString("about:Entities %1").arg(++_entitiesScriptEngineCount); - auto newEngine = scriptEngineFactory(ScriptEngine::ENTITY_SERVER_SCRIPT, NO_SCRIPT, engineName); + auto newManager = scriptManagerFactory(ScriptManager::ENTITY_SERVER_SCRIPT, NO_SCRIPT, engineName); + auto newEngine = newManager->engine(); auto webSocketServerConstructorValue = newEngine->newFunction(WebSocketServerClass::constructor); - newEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue); + newEngine->globalObject()->setProperty("WebSocketServer", webSocketServerConstructorValue); newEngine->registerGlobalObject("SoundCache", DependencyManager::get().data()); newEngine->registerGlobalObject("AvatarList", DependencyManager::get().data()); // connect this script engines printedMessage signal to the global ScriptEngines these various messages auto scriptEngines = DependencyManager::get().data(); - connect(newEngine.data(), &ScriptEngine::printedMessage, scriptEngines, &ScriptEngines::onPrintedMessage); - connect(newEngine.data(), &ScriptEngine::errorMessage, scriptEngines, &ScriptEngines::onErrorMessage); - connect(newEngine.data(), &ScriptEngine::warningMessage, scriptEngines, &ScriptEngines::onWarningMessage); - connect(newEngine.data(), &ScriptEngine::infoMessage, scriptEngines, &ScriptEngines::onInfoMessage); + connect(newManager.data(), &ScriptManager::printedMessage, scriptEngines, &ScriptEngines::onPrintedMessage); + connect(newManager.data(), &ScriptManager::errorMessage, scriptEngines, &ScriptEngines::onErrorMessage); + connect(newManager.data(), &ScriptManager::warningMessage, scriptEngines, &ScriptEngines::onWarningMessage); + connect(newManager.data(), &ScriptManager::infoMessage, scriptEngines, &ScriptEngines::onInfoMessage); - connect(newEngine.data(), &ScriptEngine::update, this, [this] { + connect(newManager.data(), &ScriptManager::update, this, [this] { _entityViewer.queryOctree(); _entityViewer.getTree()->preUpdate(); _entityViewer.getTree()->update(); }); - scriptEngines->runScriptInitializers(newEngine); - newEngine->runInThread(); - auto newEngineSP = qSharedPointerCast(newEngine); + scriptEngines->runScriptInitializers(newManager); + newManager->runInThread(); + auto newEngineSP = qSharedPointerCast(newManager); // On the entity script server, these are the same DependencyManager::get()->setPersistentEntitiesScriptEngine(newEngineSP); DependencyManager::get()->setNonPersistentEntitiesScriptEngine(newEngineSP); - if (_entitiesScriptEngine) { - disconnect(_entitiesScriptEngine.data(), &ScriptEngine::entityScriptDetailsUpdated, + if (_entitiesScriptManager) { + disconnect(_entitiesScriptManager.data(), &ScriptManager::entityScriptDetailsUpdated, this, &EntityScriptServer::updateEntityPPS); } - _entitiesScriptEngine.swap(newEngine); - connect(_entitiesScriptEngine.data(), &ScriptEngine::entityScriptDetailsUpdated, + _entitiesScriptManager.swap(newManager); + connect(_entitiesScriptManager.data(), &ScriptManager::entityScriptDetailsUpdated, this, &EntityScriptServer::updateEntityPPS); } void EntityScriptServer::clear() { // unload and stop the engine - if (_entitiesScriptEngine) { + if (_entitiesScriptManager) { // do this here (instead of in deleter) to avoid marshalling unload signals back to this thread - _entitiesScriptEngine->unloadAllEntityScripts(); - _entitiesScriptEngine->stop(); - _entitiesScriptEngine->waitTillDoneRunning(); + _entitiesScriptManager->unloadAllEntityScripts(); + _entitiesScriptManager->stop(); + _entitiesScriptManager->waitTillDoneRunning(); } _entityViewer.clear(); @@ -503,8 +507,8 @@ void EntityScriptServer::clear() { } void EntityScriptServer::shutdownScriptEngine() { - if (_entitiesScriptEngine) { - _entitiesScriptEngine->disconnectNonEssentialSignals(); // disconnect all slots/signals from the script engine, except essential + if (_entitiesScriptManager) { + _entitiesScriptManager->disconnectNonEssentialSignals(); // disconnect all slots/signals from the script engine, except essential } _shuttingDown = true; @@ -513,7 +517,7 @@ void EntityScriptServer::shutdownScriptEngine() { auto scriptEngines = DependencyManager::get(); scriptEngines->shutdownScripting(); - _entitiesScriptEngine.clear(); + _entitiesScriptManager.clear(); auto entityScriptingInterface = DependencyManager::get(); // our entity tree is going to go away so tell that to the EntityScriptingInterface @@ -531,8 +535,8 @@ void EntityScriptServer::addingEntity(const EntityItemID& entityID) { } void EntityScriptServer::deletingEntity(const EntityItemID& entityID) { - if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptEngine) { - _entitiesScriptEngine->unloadEntityScript(entityID, true); + if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptManager) { + _entitiesScriptManager->unloadEntityScript(entityID, true); } } @@ -543,20 +547,20 @@ void EntityScriptServer::entityServerScriptChanging(const EntityItemID& entityID } void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, bool forceRedownload) { - if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptEngine) { + if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptManager) { EntityItemPointer entity = _entityViewer.getTree()->findEntityByEntityItemID(entityID); EntityScriptDetails details; - bool isRunning = _entitiesScriptEngine->getEntityScriptDetails(entityID, details); + bool isRunning = _entitiesScriptManager->getEntityScriptDetails(entityID, details); if (entity && (forceRedownload || !isRunning || details.scriptText != entity->getServerScripts())) { if (isRunning) { - _entitiesScriptEngine->unloadEntityScript(entityID, true); + _entitiesScriptManager->unloadEntityScript(entityID, true); } QString scriptUrl = entity->getServerScripts(); if (!scriptUrl.isEmpty()) { scriptUrl = DependencyManager::get()->normalizeURL(scriptUrl); - _entitiesScriptEngine->loadEntityScript(entityID, scriptUrl, forceRedownload); + _entitiesScriptManager->loadEntityScript(entityID, scriptUrl, forceRedownload); } } } @@ -573,9 +577,9 @@ void EntityScriptServer::sendStatsPacket() { QJsonObject scriptEngineStats; int numberRunningScripts = 0; - const auto scriptEngine = _entitiesScriptEngine; - if (scriptEngine) { - numberRunningScripts = scriptEngine->getNumRunningEntityScripts(); + const auto scriptManager = _entitiesScriptManager; + if (scriptManager) { + numberRunningScripts = scriptManager->getNumRunningEntityScripts(); } scriptEngineStats["number_running_scripts"] = numberRunningScripts; statsObject["script_engine_stats"] = scriptEngineStats; diff --git a/assignment-client/src/scripts/EntityScriptServer.h b/assignment-client/src/scripts/EntityScriptServer.h index b795339174e..4ebe41cd9a8 100644 --- a/assignment-client/src/scripts/EntityScriptServer.h +++ b/assignment-client/src/scripts/EntityScriptServer.h @@ -17,14 +17,19 @@ #include #include +#include #include #include -#include #include #include +#include + #include "../entities/EntityTreeHeadlessViewer.h" +class ScriptManager; +using ScriptManagerPointer = QSharedPointer; + class EntityScriptServer : public ThreadedAssignment { Q_OBJECT @@ -75,7 +80,7 @@ private slots: bool _shuttingDown { false }; static int _entitiesScriptEngineCount; - ScriptEnginePointer _entitiesScriptEngine; + ScriptManagerPointer _entitiesScriptManager; SimpleEntitySimulationPointer _entitySimulation; EntityEditPacketSender _entityEditSender; EntityTreeHeadlessViewer _entityViewer; diff --git a/domain-server/CMakeLists.txt b/domain-server/CMakeLists.txt index a3a85684b4d..6f123f23612 100644 --- a/domain-server/CMakeLists.txt +++ b/domain-server/CMakeLists.txt @@ -25,6 +25,7 @@ symlink_or_copy_directory_beside_target(${_SHOULD_SYMLINK_RESOURCES} "${CMAKE_CU # link the shared hifi libraries include_hifi_library_headers(gpu) include_hifi_library_headers(graphics) +include_hifi_library_headers(script-engine) link_hifi_libraries(embedded-webserver networking shared avatars octree) target_zlib() diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index c307c631425..9a0af9ef9ad 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -98,7 +98,7 @@ endif () find_package( Qt5 COMPONENTS - Gui Widgets Multimedia Network Qml Quick Script Svg + Gui Widgets Multimedia Network Qml Quick Svg ${PLATFORM_QT_COMPONENTS} WebChannel WebSockets ) @@ -304,7 +304,7 @@ endif () target_link_libraries( ${TARGET_NAME} Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::Widgets - Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg + Qt5::Qml Qt5::Quick Qt5::Svg Qt5::WebChannel ${PLATFORM_QT_LIBRARIES} ) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dad22f6b1ff..cab82621c69 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -130,6 +130,7 @@ #include #include #include +#include #include #include #include @@ -137,8 +138,11 @@ #include #include #include -#include #include +#include +#include +#include +#include #include #include #include @@ -165,6 +169,7 @@ #include #include #include "recording/ClipCache.h" +#include #include "AudioClient.h" #include "audio/AudioScope.h" @@ -863,7 +868,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { #endif DependencyManager::set(); DependencyManager::set(); - DependencyManager::set(ScriptEngine::CLIENT_SCRIPT, defaultScriptsOverrideOption); + DependencyManager::set(ScriptManager::CLIENT_SCRIPT, defaultScriptsOverrideOption); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -1448,8 +1453,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo { auto scriptEngines = DependencyManager::get().data(); - scriptEngines->registerScriptInitializer([this](ScriptEnginePointer engine) { - registerScriptEngineWithApplicationServices(engine); + scriptEngines->registerScriptInitializer([this](ScriptManagerPointer manager) { + registerScriptEngineWithApplicationServices(manager); }); connect(scriptEngines, &ScriptEngines::scriptCountChanged, this, [this] { @@ -5874,7 +5879,7 @@ void Application::loadAvatarScripts(const QVector& urls) { if (index < 0) { auto scriptEnginePointer = scriptEngines->loadScript(url, false); if (scriptEnginePointer) { - scriptEnginePointer->setType(ScriptEngine::Type::AVATAR); + scriptEnginePointer->setType(ScriptManager::Type::AVATAR); } } } @@ -5885,7 +5890,7 @@ void Application::unloadAvatarScripts() { auto urls = scriptEngines->getRunningScripts(); for (auto url : urls) { auto scriptEngine = scriptEngines->getScriptEngine(url); - if (scriptEngine->getType() == ScriptEngine::Type::AVATAR) { + if (scriptEngine->getType() == ScriptManager::Type::AVATAR) { scriptEngines->stopScript(url, false); } } @@ -7475,9 +7480,10 @@ void Application::addingEntityWithCertificate(const QString& certificateID, cons ledger->updateLocation(certificateID, placeName); } -void Application::registerScriptEngineWithApplicationServices(const ScriptEnginePointer& scriptEngine) { +void Application::registerScriptEngineWithApplicationServices(const ScriptManagerPointer& scriptManager) { - scriptEngine->setEmitScriptUpdatesFunction([this]() { + auto scriptEngine = scriptManager->engine(); + scriptManager->setEmitScriptUpdatesFunction([this]() { SharedNodePointer entityServerNode = DependencyManager::get()->soloNodeOfType(NodeType::EntityServer); return !entityServerNode || isPhysicsEnabled(); }); @@ -7509,13 +7515,13 @@ void Application::registerScriptEngineWithApplicationServices(const ScriptEngine ClipboardScriptingInterface* clipboardScriptable = new ClipboardScriptingInterface(); scriptEngine->registerGlobalObject("Clipboard", clipboardScriptable); - connect(scriptEngine.data(), &ScriptEngine::finished, clipboardScriptable, &ClipboardScriptingInterface::deleteLater); + connect(scriptManager.data(), &ScriptManager::finished, clipboardScriptable, &ClipboardScriptingInterface::deleteLater); scriptEngine->registerGlobalObject("Overlays", &_overlays); - qScriptRegisterMetaType(scriptEngine.data(), RayToOverlayIntersectionResultToScriptValue, + scriptRegisterMetaType(scriptEngine.data(), RayToOverlayIntersectionResultToScriptValue, RayToOverlayIntersectionResultFromScriptValue); - bool clientScript = scriptEngine->isClientScript(); + bool clientScript = scriptManager->isClientScript(); #if !defined(DISABLE_QML) scriptEngine->registerGlobalObject("OffscreenFlags", getOffscreenUI()->getFlags()); @@ -7530,13 +7536,13 @@ void Application::registerScriptEngineWithApplicationServices(const ScriptEngine } #endif - qScriptRegisterMetaType(scriptEngine.data(), wrapperToScriptValue, wrapperFromScriptValue); - qScriptRegisterMetaType(scriptEngine.data(), + scriptRegisterMetaType(scriptEngine.data(), wrapperToScriptValue, wrapperFromScriptValue); + scriptRegisterMetaType(scriptEngine.data(), wrapperToScriptValue, wrapperFromScriptValue); scriptEngine->registerGlobalObject("Toolbars", DependencyManager::get().data()); - qScriptRegisterMetaType(scriptEngine.data(), wrapperToScriptValue, wrapperFromScriptValue); - qScriptRegisterMetaType(scriptEngine.data(), + scriptRegisterMetaType(scriptEngine.data(), wrapperToScriptValue, wrapperFromScriptValue); + scriptRegisterMetaType(scriptEngine.data(), wrapperToScriptValue, wrapperFromScriptValue); scriptEngine->registerGlobalObject("Tablet", DependencyManager::get().data()); // FIXME remove these deprecated names for the tablet scripting interface @@ -7587,12 +7593,12 @@ void Application::registerScriptEngineWithApplicationServices(const ScriptEngine scriptEngine->registerGlobalObject("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED scriptEngine->registerGlobalObject("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED scriptEngine->registerGlobalObject("AccountServices", AccountServicesScriptingInterface::getInstance()); - qScriptRegisterMetaType(scriptEngine.data(), DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue); + scriptRegisterMetaType(scriptEngine.data(), DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue); scriptEngine->registerGlobalObject("AvatarManager", DependencyManager::get().data()); scriptEngine->registerGlobalObject("LODManager", DependencyManager::get().data()); - qScriptRegisterMetaType(scriptEngine.data(), worldDetailQualityToScriptValue, worldDetailQualityFromScriptValue); + scriptRegisterMetaType(scriptEngine.data(), worldDetailQualityToScriptValue, worldDetailQualityFromScriptValue); scriptEngine->registerGlobalObject("Keyboard", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Performance", new PerformanceScriptingInterface()); @@ -7619,7 +7625,7 @@ void Application::registerScriptEngineWithApplicationServices(const ScriptEngine scriptEngine->registerGlobalObject("GooglePoly", DependencyManager::get().data()); if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { - scriptEngine->registerGlobalObject("Steam", new SteamScriptingInterface(scriptEngine.data(), steamClient.get())); + scriptEngine->registerGlobalObject("Steam", new SteamScriptingInterface(scriptManager.data(), steamClient.get())); } auto scriptingInterface = DependencyManager::get(); scriptEngine->registerGlobalObject("Controller", scriptingInterface.data()); @@ -7646,11 +7652,11 @@ void Application::registerScriptEngineWithApplicationServices(const ScriptEngine // connect this script engines printedMessage signal to the global ScriptEngines these various messages auto scriptEngines = DependencyManager::get().data(); - connect(scriptEngine.data(), &ScriptEngine::printedMessage, scriptEngines, &ScriptEngines::onPrintedMessage); - connect(scriptEngine.data(), &ScriptEngine::errorMessage, scriptEngines, &ScriptEngines::onErrorMessage); - connect(scriptEngine.data(), &ScriptEngine::warningMessage, scriptEngines, &ScriptEngines::onWarningMessage); - connect(scriptEngine.data(), &ScriptEngine::infoMessage, scriptEngines, &ScriptEngines::onInfoMessage); - connect(scriptEngine.data(), &ScriptEngine::clearDebugWindow, scriptEngines, &ScriptEngines::onClearDebugWindow); + connect(scriptManager.data(), &ScriptManager::printedMessage, scriptEngines, &ScriptEngines::onPrintedMessage); + connect(scriptManager.data(), &ScriptManager::errorMessage, scriptEngines, &ScriptEngines::onErrorMessage); + connect(scriptManager.data(), &ScriptManager::warningMessage, scriptEngines, &ScriptEngines::onWarningMessage); + connect(scriptManager.data(), &ScriptManager::infoMessage, scriptEngines, &ScriptEngines::onInfoMessage); + connect(scriptManager.data(), &ScriptManager::clearDebugWindow, scriptEngines, &ScriptEngines::onClearDebugWindow); } diff --git a/interface/src/Application.h b/interface/src/Application.h index e771d7ea388..679f3509bb2 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -20,11 +20,13 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -39,7 +41,6 @@ #include #include #include -#include #include #include #include @@ -88,6 +89,8 @@ class MainWindow; class AssetUpload; class CompositorHelper; class AudioInjector; +class ScriptEngine; +using ScriptEnginePointer = QSharedPointer; namespace controller { class StateController; @@ -243,7 +246,7 @@ class Application : public QApplication, NodeToOctreeSceneStats* getOcteeSceneStats() { return &_octreeServerSceneStats; } virtual controller::ScriptingInterface* getControllerScriptingInterface() { return _controllerScriptingInterface; } - virtual void registerScriptEngineWithApplicationServices(const ScriptEnginePointer& scriptEngine) override; + virtual void registerScriptEngineWithApplicationServices(const ScriptManagerPointer& scriptManager) override; virtual void copyCurrentViewFrustum(ViewFrustum& viewOut) const override { copyDisplayViewFrustum(viewOut); } virtual QThread* getMainThread() override { return thread(); } diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index 9aa1d91a0fd..e0d001a4a00 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -49,12 +49,12 @@ void addAvatarEntities(const QVariantList& avatarEntities) { EntitySimulationPointer entitySimulation = entityTree->getSimulation(); PhysicalEntitySimulationPointer physicalEntitySimulation = std::static_pointer_cast(entitySimulation); EntityEditPacketSender* entityPacketSender = physicalEntitySimulation->getPacketSender(); - QScriptEngine scriptEngine; + ScriptEnginePointer scriptEngine = newScriptEngine(); for (int index = 0; index < avatarEntities.count(); index++) { const QVariantMap& avatarEntityProperties = avatarEntities.at(index).toMap(); QVariant variantProperties = avatarEntityProperties["properties"]; QVariantMap asMap = variantProperties.toMap(); - QScriptValue scriptProperties = variantMapToScriptValue(asMap, scriptEngine); + ScriptValuePointer scriptProperties = variantMapToScriptValue(asMap, *scriptEngine); EntityItemProperties entityProperties; EntityItemPropertiesFromScriptValueIgnoreReadOnly(scriptProperties, entityProperties); @@ -298,7 +298,7 @@ QVariantMap AvatarBookmarks::getAvatarDataToBookmark() { EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; if (entityTree) { - QScriptEngine scriptEngine; + ScriptEnginePointer scriptEngine = newScriptEngine(); auto avatarEntities = myAvatar->getAvatarEntityDataNonDefault(); for (auto entityID : avatarEntities.keys()) { auto entity = entityTree->findEntityByID(entityID); @@ -318,8 +318,8 @@ QVariantMap AvatarBookmarks::getAvatarDataToBookmark() { desiredProperties -= PROP_JOINT_TRANSLATIONS; EntityItemProperties entityProperties = entity->getProperties(desiredProperties); - QScriptValue scriptProperties = EntityItemPropertiesToScriptValue(&scriptEngine, entityProperties); - avatarEntityData["properties"] = scriptProperties.toVariant(); + ScriptValuePointer scriptProperties = EntityItemPropertiesToScriptValue(scriptEngine, entityProperties); + avatarEntityData["properties"] = scriptProperties->toVariant(); wearableEntities.append(QVariant(avatarEntityData)); } } diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index 1c6ef387f3a..4f8933ee3a6 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -426,13 +426,13 @@ WorldDetailQuality LODManager::getWorldDetailQuality() const { return qApp->isHMDMode() ? _hmdWorldDetailQuality : _desktopWorldDetailQuality; } -QScriptValue worldDetailQualityToScriptValue(QScriptEngine* engine, const WorldDetailQuality& worldDetailQuality) { +ScriptValuePointer worldDetailQualityToScriptValue(ScriptEngine* engine, const WorldDetailQuality& worldDetailQuality) { return worldDetailQuality; } -void worldDetailQualityFromScriptValue(const QScriptValue& object, WorldDetailQuality& worldDetailQuality) { +void worldDetailQualityFromScriptValue(const ScriptValuePointer& object, WorldDetailQuality& worldDetailQuality) { worldDetailQuality = - static_cast(std::min(std::max(object.toInt32(), (int)WORLD_DETAIL_LOW), (int)WORLD_DETAIL_HIGH)); + static_cast(std::min(std::max(object->toInt32(), (int)WORLD_DETAIL_LOW), (int)WORLD_DETAIL_HIGH)); } void LODManager::setLODQualityLevel(float quality) { diff --git a/interface/src/LODManager.h b/interface/src/LODManager.h index 35f80efcda9..e644379b01a 100644 --- a/interface/src/LODManager.h +++ b/interface/src/LODManager.h @@ -14,6 +14,7 @@ #define hifi_LODManager_h #include +#include #include #include @@ -23,6 +24,9 @@ #include #include +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; /**jsdoc *

The world detail quality rendered.

@@ -380,7 +384,7 @@ class LODManager : public QObject, public Dependency { glm::vec4 _pidOutputs{ 0.0f }; }; -QScriptValue worldDetailQualityToScriptValue(QScriptEngine* engine, const WorldDetailQuality& worldDetailQuality); -void worldDetailQualityFromScriptValue(const QScriptValue& object, WorldDetailQuality& worldDetailQuality); +ScriptValuePointer worldDetailQualityToScriptValue(ScriptEngine* engine, const WorldDetailQuality& worldDetailQuality); +void worldDetailQualityFromScriptValue(const ScriptValuePointer& object, WorldDetailQuality& worldDetailQuality); #endif // hifi_LODManager_h diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 3a320cc6287..a462e7fa96e 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index b119f21f17b..91e3b8ad3ed 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -13,7 +13,7 @@ #include -#include +#include #include "AvatarLogging.h" @@ -735,8 +735,8 @@ AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID) } RayToAvatarIntersectionResult AvatarManager::findRayIntersection(const PickRay& ray, - const QScriptValue& avatarIdsToInclude, - const QScriptValue& avatarIdsToDiscard, + const ScriptValuePointer& avatarIdsToInclude, + const ScriptValuePointer& avatarIdsToDiscard, bool pickAgainstMesh) { QVector avatarsToInclude = qVectorEntityItemIDFromScriptValue(avatarIdsToInclude); QVector avatarsToDiscard = qVectorEntityItemIDFromScriptValue(avatarIdsToDiscard); @@ -980,10 +980,10 @@ float AvatarManager::getAvatarSortCoefficient(const QString& name) { } // HACK -void AvatarManager::setAvatarSortCoefficient(const QString& name, const QScriptValue& value) { +void AvatarManager::setAvatarSortCoefficient(const QString& name, const ScriptValuePointer& value) { bool somethingChanged = false; - if (value.isNumber()) { - float numericalValue = (float)value.toNumber(); + if (value->isNumber()) { + float numericalValue = (float)value->toNumber(); if (name == "size") { AvatarData::_avatarSortCoefficientSize = numericalValue; somethingChanged = true; diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 30f68297b4c..780d6a55b57 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -33,6 +33,7 @@ #include "MyAvatar.h" #include "OtherAvatar.h" +class ScriptEngine; using SortedAvatar = std::pair>; @@ -95,7 +96,7 @@ class AvatarManager : public AvatarHashMap { */ /// Registers the script types associated with the avatar manager. - static void registerMetaTypes(QScriptEngine* engine); + static void registerMetaTypes(ScriptEngine* engine); virtual ~AvatarManager(); @@ -186,8 +187,8 @@ class AvatarManager : public AvatarHashMap { * } */ Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersection(const PickRay& ray, - const QScriptValue& avatarIdsToInclude = QScriptValue(), - const QScriptValue& avatarIdsToDiscard = QScriptValue(), + const ScriptValuePointer& avatarIdsToInclude = ScriptValuePointer(), + const ScriptValuePointer& avatarIdsToDiscard = ScriptValuePointer(), bool pickAgainstMesh = true); /**jsdoc * @function AvatarManager.findRayIntersectionVector @@ -230,7 +231,7 @@ class AvatarManager : public AvatarHashMap { * @param {number} value - Value. * @deprecated This function is deprecated and will be removed. */ - Q_INVOKABLE void setAvatarSortCoefficient(const QString& name, const QScriptValue& value); + Q_INVOKABLE void setAvatarSortCoefficient(const QString& name, const ScriptValuePointer& value); /**jsdoc * Gets PAL (People Access List) data for one or more avatars. Using this method is faster than iterating over each avatar diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e8c91e79735..cf3325c0b5c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -43,6 +43,8 @@ #include #include #include +#include +#include #include #include #include @@ -438,18 +440,18 @@ void MyAvatar::enableHandTouchForID(const QUuid& entityID) { } void MyAvatar::registerMetaTypes(ScriptEnginePointer engine) { - QScriptValue value = engine->newQObject(this, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); - engine->globalObject().setProperty("MyAvatar", value); + ScriptValuePointer value = engine->newQObject(this, ScriptEngine::QtOwnership, ScriptEngine::ExcludeDeleteLater | ScriptEngine::ExcludeChildObjects); + engine->globalObject()->setProperty("MyAvatar", value); - QScriptValue driveKeys = engine->newObject(); + ScriptValuePointer driveKeys = engine->newObject(); auto metaEnum = QMetaEnum::fromType(); for (int i = 0; i < MAX_DRIVE_KEYS; ++i) { - driveKeys.setProperty(metaEnum.key(i), metaEnum.value(i)); + driveKeys->setProperty(metaEnum.key(i), metaEnum.value(i)); } - engine->globalObject().setProperty("DriveKeys", driveKeys); + engine->globalObject()->setProperty("DriveKeys", driveKeys); - qScriptRegisterMetaType(engine.data(), audioListenModeToScriptValue, audioListenModeFromScriptValue); - qScriptRegisterMetaType(engine.data(), driveKeysToScriptValue, driveKeysFromScriptValue); + scriptRegisterMetaType(engine.data(), audioListenModeToScriptValue, audioListenModeFromScriptValue); + scriptRegisterMetaType(engine.data(), driveKeysToScriptValue, driveKeysFromScriptValue); } void MyAvatar::setOrientationVar(const QVariant& newOrientationVar) { @@ -2061,7 +2063,7 @@ void MyAvatar::avatarEntityDataToJson(QJsonObject& root) const { void MyAvatar::loadData() { if (!_scriptEngine) { - _scriptEngine = new QScriptEngine(); + _scriptEngine = newScriptEngine(); } getHead()->setBasePitch(_headPitchSetting.get()); @@ -2669,9 +2671,9 @@ QVariantList MyAvatar::getAvatarEntitiesVariant() { EntityItemProperties entityProperties = entity->getProperties(desiredProperties); { std::lock_guard guard(_scriptEngineLock); - QScriptValue scriptProperties; + ScriptValuePointer scriptProperties; scriptProperties = EntityItemPropertiesToScriptValue(_scriptEngine, entityProperties); - avatarEntityData["properties"] = scriptProperties.toVariant(); + avatarEntityData["properties"] = scriptProperties->toVariant(); } avatarEntitiesData.append(QVariant(avatarEntityData)); } @@ -5702,20 +5704,20 @@ void MyAvatar::setAudioListenerMode(AudioListenerMode audioListenerMode) { } } -QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode) { - return audioListenerMode; +ScriptValuePointer audioListenModeToScriptValue(ScriptEngine* engine, const AudioListenerMode& audioListenerMode) { + return engine->newValue(audioListenerMode); } -void audioListenModeFromScriptValue(const QScriptValue& object, AudioListenerMode& audioListenerMode) { - audioListenerMode = static_cast(object.toUInt16()); +void audioListenModeFromScriptValue(const ScriptValuePointer& object, AudioListenerMode& audioListenerMode) { + audioListenerMode = static_cast(object->toUInt16()); } -QScriptValue driveKeysToScriptValue(QScriptEngine* engine, const MyAvatar::DriveKeys& driveKeys) { - return driveKeys; +ScriptValuePointer driveKeysToScriptValue(ScriptEngine* engine, const MyAvatar::DriveKeys& driveKeys) { + return engine->newValue(driveKeys); } -void driveKeysFromScriptValue(const QScriptValue& object, MyAvatar::DriveKeys& driveKeys) { - driveKeys = static_cast(object.toUInt16()); +void driveKeysFromScriptValue(const ScriptValuePointer& object, MyAvatar::DriveKeys& driveKeys) { + driveKeys = static_cast(object->toUInt16()); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index b4153400a79..0316c4ed6c7 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -26,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -39,6 +39,10 @@ class AvatarActionHold; class ModelItemID; class MyHead; class DetailedMotionState; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; +using ScriptEnginePointer = QSharedPointer; /**jsdoc *

Locomotion control types.

@@ -867,7 +871,7 @@ class MyAvatar : public Avatar { * MyAvatar.removeAnimationStateHandler(handler); * }, 100); */ - Q_INVOKABLE QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { return _skeletonModel->getRig().addAnimationStateHandler(handler, propertiesList); } + Q_INVOKABLE ScriptValuePointer addAnimationStateHandler(ScriptValuePointer handler, ScriptValuePointer propertiesList) { return _skeletonModel->getRig().addAnimationStateHandler(handler, propertiesList); } /**jsdoc * Removes an animation state handler function. @@ -875,7 +879,7 @@ class MyAvatar : public Avatar { * @param {number} handler - The ID of the animation state handler function to remove. */ // Removes a handler previously added by addAnimationStateHandler. - Q_INVOKABLE void removeAnimationStateHandler(QScriptValue handler) { _skeletonModel->getRig().removeAnimationStateHandler(handler); } + Q_INVOKABLE void removeAnimationStateHandler(ScriptValuePointer handler) { _skeletonModel->getRig().removeAnimationStateHandler(handler); } /**jsdoc @@ -3096,7 +3100,7 @@ private slots: // // keep a ScriptEngine around so we don't have to instantiate on the fly (these are very slow to create/delete) mutable std::mutex _scriptEngineLock; - QScriptEngine* _scriptEngine { nullptr }; + ScriptEngine* _scriptEngine { nullptr }; bool _needToSaveAvatarEntitySettings { false }; bool _reactionTriggers[NUM_AVATAR_TRIGGER_REACTIONS] { false, false }; @@ -3113,11 +3117,11 @@ private slots: QTimer _addAvatarEntitiesToTreeTimer; }; -QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); -void audioListenModeFromScriptValue(const QScriptValue& object, AudioListenerMode& audioListenerMode); +ScriptValuePointer audioListenModeToScriptValue(ScriptEngine* engine, const AudioListenerMode& audioListenerMode); +void audioListenModeFromScriptValue(const ScriptValuePointer& object, AudioListenerMode& audioListenerMode); -QScriptValue driveKeysToScriptValue(QScriptEngine* engine, const MyAvatar::DriveKeys& driveKeys); -void driveKeysFromScriptValue(const QScriptValue& object, MyAvatar::DriveKeys& driveKeys); +ScriptValuePointer driveKeysToScriptValue(ScriptEngine* engine, const MyAvatar::DriveKeys& driveKeys); +void driveKeysFromScriptValue(const ScriptValuePointer& object, MyAvatar::DriveKeys& driveKeys); bool isWearableEntity(const EntityItemPointer& entity); diff --git a/interface/src/raypick/LaserPointerScriptingInterface.cpp b/interface/src/raypick/LaserPointerScriptingInterface.cpp index 16fe65a989f..5aacd724594 100644 --- a/interface/src/raypick/LaserPointerScriptingInterface.cpp +++ b/interface/src/raypick/LaserPointerScriptingInterface.cpp @@ -11,14 +11,14 @@ #include "LaserPointerScriptingInterface.h" -#include "RegisteredMetaTypes.h" #include "PointerScriptingInterface.h" +#include -void LaserPointerScriptingInterface::setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems) const { +void LaserPointerScriptingInterface::setIgnoreItems(unsigned int uid, const ScriptValuePointer& ignoreItems) const { DependencyManager::get()->setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems)); } -void LaserPointerScriptingInterface::setIncludeItems(unsigned int uid, const QScriptValue& includeItems) const { +void LaserPointerScriptingInterface::setIncludeItems(unsigned int uid, const ScriptValuePointer& includeItems) const { DependencyManager::get()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems)); } diff --git a/interface/src/raypick/LaserPointerScriptingInterface.h b/interface/src/raypick/LaserPointerScriptingInterface.h index 5745e29e695..48f4842bf83 100644 --- a/interface/src/raypick/LaserPointerScriptingInterface.h +++ b/interface/src/raypick/LaserPointerScriptingInterface.h @@ -12,10 +12,14 @@ #define hifi_LaserPointerScriptingInterface_h #include +#include #include "DependencyManager.h" #include +class ScriptValue; +using ScriptValuePointer = QSharedPointer; + class LaserPointerScriptingInterface : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY @@ -113,7 +117,7 @@ class LaserPointerScriptingInterface : public QObject, public Dependency { * @param {number} id - The ID of the pointer. * @param {Uuid[]} ignoreItems - A list of IDs to ignore. */ - Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities) const; + Q_INVOKABLE void setIgnoreItems(unsigned int uid, const ScriptValuePointer& ignoreEntities) const; /**jsdoc * Sets a list of entity and avatar IDs that a pointer should include during intersection, instead of intersecting with @@ -122,7 +126,7 @@ class LaserPointerScriptingInterface : public QObject, public Dependency { * @param {number} id - The ID of the pointer. * @param {Uuid[]} includeItems - A list of IDs to include. */ - Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities) const; + Q_INVOKABLE void setIncludeItems(unsigned int uid, const ScriptValuePointer& includeEntities) const; /**jsdoc diff --git a/interface/src/raypick/PathPointer.cpp b/interface/src/raypick/PathPointer.cpp index 8a1675cfe18..b0223888aa4 100644 --- a/interface/src/raypick/PathPointer.cpp +++ b/interface/src/raypick/PathPointer.cpp @@ -12,6 +12,7 @@ #include #include +#include #include "PickScriptingInterface.h" #include "RayPick.h" diff --git a/interface/src/raypick/PickScriptingInterface.cpp b/interface/src/raypick/PickScriptingInterface.cpp index 1f940f761d6..d2985a2fec1 100644 --- a/interface/src/raypick/PickScriptingInterface.cpp +++ b/interface/src/raypick/PickScriptingInterface.cpp @@ -29,6 +29,8 @@ #include "EntityTransformNode.h" #include +#include +#include static const float WEB_TOUCH_Y_OFFSET = 0.105f; // how far forward (or back with a negative number) to slide stylus in hand static const glm::vec3 TIP_OFFSET = glm::vec3(0.0f, StylusPick::WEB_STYLUS_LENGTH - WEB_TOUCH_Y_OFFSET, 0.0f); @@ -425,11 +427,11 @@ void PickScriptingInterface::setPrecisionPicking(unsigned int uid, bool precisio DependencyManager::get()->setPrecisionPicking(uid, precisionPicking); } -void PickScriptingInterface::setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems) { +void PickScriptingInterface::setIgnoreItems(unsigned int uid, const ScriptValuePointer& ignoreItems) { DependencyManager::get()->setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems)); } -void PickScriptingInterface::setIncludeItems(unsigned int uid, const QScriptValue& includeItems) { +void PickScriptingInterface::setIncludeItems(unsigned int uid, const ScriptValuePointer& includeItems) { DependencyManager::get()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems)); } @@ -445,23 +447,23 @@ bool PickScriptingInterface::isMouse(unsigned int uid) { return DependencyManager::get()->isMouse(uid); } -QScriptValue pickTypesToScriptValue(QScriptEngine* engine, const PickQuery::PickType& pickType) { +ScriptValuePointer pickTypesToScriptValue(ScriptEngine* engine, const PickQuery::PickType& pickType) { return pickType; } -void pickTypesFromScriptValue(const QScriptValue& object, PickQuery::PickType& pickType) { - pickType = static_cast(object.toUInt16()); +void pickTypesFromScriptValue(const ScriptValuePointer& object, PickQuery::PickType& pickType) { + pickType = static_cast(object->toUInt16()); } -void PickScriptingInterface::registerMetaTypes(QScriptEngine* engine) { - QScriptValue pickTypes = engine->newObject(); +void PickScriptingInterface::registerMetaTypes(ScriptEngine* engine) { + ScriptValuePointer pickTypes = engine->newObject(); auto metaEnum = QMetaEnum::fromType(); for (int i = 0; i < PickQuery::PickType::NUM_PICK_TYPES; ++i) { - pickTypes.setProperty(metaEnum.key(i), metaEnum.value(i)); + pickTypes->setProperty(metaEnum.key(i), metaEnum.value(i)); } - engine->globalObject().setProperty("PickType", pickTypes); + engine->globalObject()->setProperty("PickType", pickTypes); - qScriptRegisterMetaType(engine, pickTypesToScriptValue, pickTypesFromScriptValue); + scriptRegisterMetaType(engine, pickTypesToScriptValue, pickTypesFromScriptValue); } unsigned int PickScriptingInterface::getPerFrameTimeBudget() const { diff --git a/interface/src/raypick/PickScriptingInterface.h b/interface/src/raypick/PickScriptingInterface.h index 58ed3326ecc..4b8e1016b52 100644 --- a/interface/src/raypick/PickScriptingInterface.h +++ b/interface/src/raypick/PickScriptingInterface.h @@ -15,6 +15,8 @@ #include #include +class ScriptEngine; + /**jsdoc * The Picks API lets you create and manage objects for repeatedly calculating intersections. * @@ -103,7 +105,7 @@ class PickScriptingInterface : public QObject, public Dependency { SINGLETON_DEPENDENCY public: - void registerMetaTypes(QScriptEngine* engine); + void registerMetaTypes(ScriptEngine* engine); /**jsdoc * Creates a new pick. Different {@link PickType}s use different properties, and within one PickType the properties you @@ -245,7 +247,7 @@ class PickScriptingInterface : public QObject, public Dependency { * @param {number} id - The ID of the pick. * @param {Uuid[]} ignoreItems - The list of IDs to ignore. */ - Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems); + Q_INVOKABLE void setIgnoreItems(unsigned int uid, const ScriptValuePointer& ignoreItems); /**jsdoc * Sets a list of entity and avatar IDs that a pick should include during intersection, instead of intersecting with @@ -255,7 +257,7 @@ class PickScriptingInterface : public QObject, public Dependency { * @param {number} id - The ID of the pick. * @param {Uuid[]} includeItems - The list of IDs to include. */ - Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeItems); + Q_INVOKABLE void setIncludeItems(unsigned int uid, const ScriptValuePointer& includeItems); /**jsdoc * Checks if a pick is associated with the left hand: a ray or parabola pick with joint property set to diff --git a/interface/src/raypick/PointerScriptingInterface.cpp b/interface/src/raypick/PointerScriptingInterface.cpp index a3aeb314e56..c6370ffb354 100644 --- a/interface/src/raypick/PointerScriptingInterface.cpp +++ b/interface/src/raypick/PointerScriptingInterface.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include "Application.h" #include "PickManager.h" #include "LaserPointer.h" @@ -23,11 +25,11 @@ static const glm::quat X_ROT_NEG_90{ 0.70710678f, -0.70710678f, 0.0f, 0.0f }; static const glm::vec3 DEFAULT_POSITION_OFFSET{0.0f, 0.0f, -StylusPick::WEB_STYLUS_LENGTH / 2.0f}; static const glm::vec3 DEFAULT_MODEL_DIMENSIONS{0.01f, 0.01f, StylusPick::WEB_STYLUS_LENGTH}; -void PointerScriptingInterface::setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems) const { +void PointerScriptingInterface::setIgnoreItems(unsigned int uid, const ScriptValuePointer& ignoreItems) const { DependencyManager::get()->setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems)); } -void PointerScriptingInterface::setIncludeItems(unsigned int uid, const QScriptValue& includeItems) const { +void PointerScriptingInterface::setIncludeItems(unsigned int uid, const ScriptValuePointer& includeItems) const { DependencyManager::get()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems)); } diff --git a/interface/src/raypick/PointerScriptingInterface.h b/interface/src/raypick/PointerScriptingInterface.h index 555136e7a7c..efbe22b9a92 100644 --- a/interface/src/raypick/PointerScriptingInterface.h +++ b/interface/src/raypick/PointerScriptingInterface.h @@ -9,12 +9,17 @@ #define hifi_PointerScriptingInterface_h #include +#include #include "DependencyManager.h" #include "RegisteredMetaTypes.h" #include #include +class ScriptValue; +using ScriptValuePointer = QSharedPointer; + + /**jsdoc * The Pointers API lets you create, manage, and visually represent objects for repeatedly calculating * intersections with avatars, entities, and overlays. Pointers can also be configured to generate events on entities and @@ -365,7 +370,7 @@ class PointerScriptingInterface : public QObject, public Dependency { * @param {number} id - The ID of the pointer. * @param {Uuid[]} ignoreItems - A list of IDs to ignore. */ - Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities) const; + Q_INVOKABLE void setIgnoreItems(unsigned int uid, const ScriptValuePointer& ignoreEntities) const; /**jsdoc * Sets a list of entity and avatar IDs that a pointer should include during intersection, instead of intersecting with @@ -375,7 +380,7 @@ class PointerScriptingInterface : public QObject, public Dependency { * @param {number} id - The ID of the pointer. * @param {Uuid[]} includeItems - A list of IDs to include. */ - Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities) const; + Q_INVOKABLE void setIncludeItems(unsigned int uid, const ScriptValuePointer& includeEntities) const; /**jsdoc diff --git a/interface/src/raypick/RayPickScriptingInterface.cpp b/interface/src/raypick/RayPickScriptingInterface.cpp index a837121e6ad..1ddbb487665 100644 --- a/interface/src/raypick/RayPickScriptingInterface.cpp +++ b/interface/src/raypick/RayPickScriptingInterface.cpp @@ -15,6 +15,7 @@ #include "GLMHelpers.h" #include +#include unsigned int RayPickScriptingInterface::createRayPick(const QVariant& properties) { return DependencyManager::get()->createPick(PickQuery::PickType::Ray, properties); @@ -45,11 +46,11 @@ void RayPickScriptingInterface::setPrecisionPicking(unsigned int uid, bool preci DependencyManager::get()->setPrecisionPicking(uid, precisionPicking); } -void RayPickScriptingInterface::setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems) { +void RayPickScriptingInterface::setIgnoreItems(unsigned int uid, const ScriptValuePointer& ignoreItems) { DependencyManager::get()->setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems)); } -void RayPickScriptingInterface::setIncludeItems(unsigned int uid, const QScriptValue& includeItems) { +void RayPickScriptingInterface::setIncludeItems(unsigned int uid, const ScriptValuePointer& includeItems) { DependencyManager::get()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems)); } diff --git a/interface/src/raypick/RayPickScriptingInterface.h b/interface/src/raypick/RayPickScriptingInterface.h index 204407e92d7..33ce979da60 100644 --- a/interface/src/raypick/RayPickScriptingInterface.h +++ b/interface/src/raypick/RayPickScriptingInterface.h @@ -12,12 +12,16 @@ #define hifi_RayPickScriptingInterface_h #include +#include #include "RegisteredMetaTypes.h" #include #include "PickScriptingInterface.h" +class ScriptValue; +using ScriptValuePointer = QSharedPointer; + /**jsdoc * The RayPick API is a subset of the {@link Picks} API, as used for ray picks. * @@ -121,7 +125,7 @@ class RayPickScriptingInterface : public QObject, public Dependency { * @param {number} id - The ID of the ray pick. * @param {Uuid[]} ignoreItems - The list of IDs to ignore. */ - Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities); + Q_INVOKABLE void setIgnoreItems(unsigned int uid, const ScriptValuePointer& ignoreEntities); /**jsdoc * Sets a list of entity and avatar IDs that a ray pick should include during intersection, instead of intersecting with @@ -130,7 +134,7 @@ class RayPickScriptingInterface : public QObject, public Dependency { * @param {number} id - The ID of the ray pick. * @param {Uuid[]} includeItems - The list of IDs to include. */ - Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities); + Q_INVOKABLE void setIncludeItems(unsigned int uid, const ScriptValuePointer& includeEntities); /**jsdoc diff --git a/interface/src/scripting/AccountServicesScriptingInterface.cpp b/interface/src/scripting/AccountServicesScriptingInterface.cpp index 5f8fb065ffa..8e48e3b5ca1 100644 --- a/interface/src/scripting/AccountServicesScriptingInterface.cpp +++ b/interface/src/scripting/AccountServicesScriptingInterface.cpp @@ -118,27 +118,27 @@ DownloadInfoResult::DownloadInfoResult() : * @property {number[]} downloading - The download percentage remaining of each asset currently downloading. * @property {number} pending - The number of assets pending download. */ -QScriptValue DownloadInfoResultToScriptValue(QScriptEngine* engine, const DownloadInfoResult& result) { - QScriptValue object = engine->newObject(); +ScriptValuePointer DownloadInfoResultToScriptValue(ScriptEngine* engine, const DownloadInfoResult& result) { + ScriptValuePointer object = engine->newObject(); - QScriptValue array = engine->newArray(result.downloading.count()); + ScriptValuePointer array = engine->newArray(result.downloading.count()); for (int i = 0; i < result.downloading.count(); i += 1) { - array.setProperty(i, result.downloading[i]); + array->setProperty(i, result.downloading[i]); } - object.setProperty("downloading", array); - object.setProperty("pending", result.pending); + object->setProperty("downloading", array); + object->setProperty("pending", result.pending); return object; } -void DownloadInfoResultFromScriptValue(const QScriptValue& object, DownloadInfoResult& result) { - QList downloading = object.property("downloading").toVariant().toList(); +void DownloadInfoResultFromScriptValue(const ScriptValuePointer& object, DownloadInfoResult& result) { + QList downloading = object->property("downloading")->toVariant().toList(); result.downloading.clear(); for (int i = 0; i < downloading.count(); i += 1) { result.downloading.append(downloading[i].toFloat()); } - result.pending = object.property("pending").toVariant().toFloat(); + result.pending = object->property("pending")->toVariant().toFloat(); } DownloadInfoResult AccountServicesScriptingInterface::getDownloadInfo() { diff --git a/interface/src/scripting/AccountServicesScriptingInterface.h b/interface/src/scripting/AccountServicesScriptingInterface.h index 7336d3e5cc9..3a31217120b 100644 --- a/interface/src/scripting/AccountServicesScriptingInterface.h +++ b/interface/src/scripting/AccountServicesScriptingInterface.h @@ -13,15 +13,17 @@ #define hifi_AccountServicesScriptingInterface_h #include -#include -#include -#include #include #include +#include #include #include +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; + class DownloadInfoResult { public: DownloadInfoResult(); @@ -31,8 +33,8 @@ class DownloadInfoResult { Q_DECLARE_METATYPE(DownloadInfoResult) -QScriptValue DownloadInfoResultToScriptValue(QScriptEngine* engine, const DownloadInfoResult& result); -void DownloadInfoResultFromScriptValue(const QScriptValue& object, DownloadInfoResult& result); +ScriptValuePointer DownloadInfoResultToScriptValue(ScriptEngine* engine, const DownloadInfoResult& result); +void DownloadInfoResultFromScriptValue(const ScriptValuePointer& object, DownloadInfoResult& result); class AccountServicesScriptingInterface : public QObject { Q_OBJECT diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.cpp b/interface/src/scripting/AssetMappingsScriptingInterface.cpp index 5b90474d233..d7a3eac9f88 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.cpp +++ b/interface/src/scripting/AssetMappingsScriptingInterface.cpp @@ -11,7 +11,7 @@ #include "AssetMappingsScriptingInterface.h" -#include +#include #include #include diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.h b/interface/src/scripting/AssetMappingsScriptingInterface.h index b27a72fbd0f..bc0676b1bca 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.h +++ b/interface/src/scripting/AssetMappingsScriptingInterface.h @@ -15,14 +15,12 @@ #define hifi_AssetMappingsScriptingInterface_h #include -#include #include #include #include "DependencyManager.h" - class AssetMappingModel : public QStandardItemModel { Q_OBJECT Q_PROPERTY(bool autoRefreshEnabled READ isAutoRefreshEnabled WRITE setAutoRefreshEnabled) diff --git a/interface/src/scripting/DesktopScriptingInterface.h b/interface/src/scripting/DesktopScriptingInterface.h index e57d7a68052..0c8dc741994 100644 --- a/interface/src/scripting/DesktopScriptingInterface.h +++ b/interface/src/scripting/DesktopScriptingInterface.h @@ -13,7 +13,6 @@ #define hifi_DesktopScriptingInterface_h #include -#include #include diff --git a/interface/src/scripting/HMDScriptingInterface.cpp b/interface/src/scripting/HMDScriptingInterface.cpp index 79c0452a452..cca69548199 100644 --- a/interface/src/scripting/HMDScriptingInterface.cpp +++ b/interface/src/scripting/HMDScriptingInterface.cpp @@ -11,14 +11,15 @@ #include "HMDScriptingInterface.h" -#include - #include #include #include #include #include #include +#include +#include +#include #include #include "Application.h" @@ -151,23 +152,23 @@ bool HMDScriptingInterface::getAwayStateWhenFocusLostInVREnabled() { } -QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) { +ScriptValuePointer HMDScriptingInterface::getHUDLookAtPosition2D(ScriptContext* context, ScriptEngine* engine) { glm::vec3 hudIntersection; auto instance = DependencyManager::get(); if (instance->getHUDLookAtPosition3D(hudIntersection)) { glm::vec2 overlayPos = qApp->getApplicationCompositor().overlayFromSphereSurface(hudIntersection); - return qScriptValueFromValue(engine, overlayPos); + return scriptValueFromValue(engine, overlayPos); } - return QScriptValue::NullValue; + return engine->nullValue(); } -QScriptValue HMDScriptingInterface::getHUDLookAtPosition3D(QScriptContext* context, QScriptEngine* engine) { +ScriptValuePointer HMDScriptingInterface::getHUDLookAtPosition3D(ScriptContext* context, ScriptEngine* engine) { glm::vec3 result; auto instance = DependencyManager::get(); if (instance->getHUDLookAtPosition3D(result)) { - return qScriptValueFromValue(engine, result); + return scriptValueFromValue(engine, result); } - return QScriptValue::NullValue; + return engine->nullValue(); } bool HMDScriptingInterface::getHUDLookAtPosition3D(glm::vec3& result) const { diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index 30a1353d814..73d6dfb5974 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -14,15 +14,17 @@ #include -#include -class QScriptContext; -class QScriptEngine; - #include #include #include #include +#include + +class ScriptContext; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; /**jsdoc * The HMD API provides access to the HMD used in VR display mode. @@ -442,14 +444,14 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen * @function HMD.getHUDLookAtPosition2D * @returns {Vec2} The position on the HUD overlay that your HMD is looking at, in pixels. */ - static QScriptValue getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine); + static ScriptValuePointer getHUDLookAtPosition2D(ScriptContext* context, ScriptEngine* engine); /**jsdoc * Gets the position on the HUD overlay that your HMD is looking at, in world coordinates. * @function HMD.getHUDLookAtPosition3D * @returns {Vec3} The position on the HUD overlay the your HMD is looking at, in world coordinates. */ - static QScriptValue getHUDLookAtPosition3D(QScriptContext* context, QScriptEngine* engine); + static ScriptValuePointer getHUDLookAtPosition3D(ScriptContext* context, ScriptEngine* engine); bool isMounted() const override; diff --git a/interface/src/scripting/PlatformInfoScriptingInterface.h b/interface/src/scripting/PlatformInfoScriptingInterface.h index 25cdc99577d..09c695ae0e8 100644 --- a/interface/src/scripting/PlatformInfoScriptingInterface.h +++ b/interface/src/scripting/PlatformInfoScriptingInterface.h @@ -12,8 +12,6 @@ #include #include -class QScriptValue; - /**jsdoc * The PlatformInfo API provides information about the hardware platform being used. * diff --git a/interface/src/scripting/SettingsScriptingInterface.cpp b/interface/src/scripting/SettingsScriptingInterface.cpp index 13eddddb1f5..9ad2ef187c9 100644 --- a/interface/src/scripting/SettingsScriptingInterface.cpp +++ b/interface/src/scripting/SettingsScriptingInterface.cpp @@ -47,7 +47,7 @@ void SettingsScriptingInterface::setValue(const QString& setting, const QVariant } } // Make a deep-copy of the string. - // Dangling pointers can occur with QStrings that are implicitly shared from a QScriptEngine. + // Dangling pointers can occur with QStrings that are implicitly shared from a ScriptEngine. QString deepCopy = QString::fromUtf16(setting.utf16()); Setting::Handle(deepCopy).set(value); emit valueChanged(setting, value); diff --git a/interface/src/scripting/TestScriptingInterface.cpp b/interface/src/scripting/TestScriptingInterface.cpp index 53630b3eede..e54086d21d3 100644 --- a/interface/src/scripting/TestScriptingInterface.cpp +++ b/interface/src/scripting/TestScriptingInterface.cpp @@ -153,9 +153,9 @@ void TestScriptingInterface::savePhysicsSimulationStats(QString originalPath) { qApp->saveNextPhysicsStats(path); } -void TestScriptingInterface::profileRange(const QString& name, QScriptValue fn) { +void TestScriptingInterface::profileRange(const QString& name, ScriptValuePointer fn) { PROFILE_RANGE(script, name); - fn.call(); + fn->call(); } void TestScriptingInterface::clearCaches() { diff --git a/interface/src/scripting/TestScriptingInterface.h b/interface/src/scripting/TestScriptingInterface.h index dffda3ece0a..d7c501d705f 100644 --- a/interface/src/scripting/TestScriptingInterface.h +++ b/interface/src/scripting/TestScriptingInterface.h @@ -12,8 +12,10 @@ #include #include +#include -class QScriptValue; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; class TestScriptingInterface : public QObject { Q_OBJECT @@ -127,7 +129,7 @@ public slots: * @param {string} name - Name used to reference the function * @param {function} function - Function to profile */ - Q_INVOKABLE void profileRange(const QString& name, QScriptValue function); + Q_INVOKABLE void profileRange(const QString& name, ScriptValuePointer function); /**jsdoc * Clear all caches (menu command Reload Content) diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index c34d1ac0066..60fa2b04f49 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -76,7 +76,7 @@ WindowScriptingInterface::~WindowScriptingInterface() { _messageBoxes.clear(); } -QScriptValue WindowScriptingInterface::hasFocus() { +ScriptValuePointer WindowScriptingInterface::hasFocus() { return qApp->hasFocus(); } @@ -96,28 +96,28 @@ void WindowScriptingInterface::raise() { /// Display an alert box /// \param const QString& message message to display -/// \return QScriptValue::UndefinedValue +/// \return ScriptValuePointer::UndefinedValue void WindowScriptingInterface::alert(const QString& message) { OffscreenUi::asyncWarning("", message, QMessageBox::Ok, QMessageBox::Ok); } /// Display a confirmation box with the options 'Yes' and 'No' /// \param const QString& message message to display -/// \return QScriptValue `true` if 'Yes' was clicked, `false` otherwise -QScriptValue WindowScriptingInterface::confirm(const QString& message) { - return QScriptValue((QMessageBox::Yes == OffscreenUi::question("", message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes))); +/// \return ScriptValuePointer `true` if 'Yes' was clicked, `false` otherwise +ScriptValuePointer WindowScriptingInterface::confirm(const QString& message) { + return ScriptValuePointer((QMessageBox::Yes == OffscreenUi::question("", message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes))); } /// Display a prompt with a text box /// \param const QString& message message to display /// \param const QString& defaultText default text in the text box -/// \return QScriptValue string text value in text box if the dialog was accepted, `null` otherwise. -QScriptValue WindowScriptingInterface::prompt(const QString& message, const QString& defaultText) { +/// \return ScriptValuePointer string text value in text box if the dialog was accepted, `null` otherwise. +ScriptValuePointer WindowScriptingInterface::prompt(const QString& message, const QString& defaultText) { QString result = OffscreenUi::getText(nullptr, "", message, QLineEdit::Normal, defaultText); - if (QScriptValue(result).equals("")) { - return QScriptValue::NullValue; + if (ScriptValuePointer(result).equals("")) { + return ScriptValuePointer::NullValue; } - return QScriptValue(result); + return ScriptValuePointer(result); } /// Display a prompt with a text box @@ -217,8 +217,8 @@ void WindowScriptingInterface::ensureReticleVisible() const { /// working directory. /// \param const QString& title title of the window /// \param const QString& directory directory to start the directory browser at -/// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue` -QScriptValue WindowScriptingInterface::browseDir(const QString& title, const QString& directory) { +/// \return ScriptValuePointer file path as a string if one was selected, otherwise `ScriptValuePointer::NullValue` +ScriptValuePointer WindowScriptingInterface::browseDir(const QString& title, const QString& directory) { ensureReticleVisible(); QString path = directory; if (path.isEmpty()) { @@ -231,7 +231,7 @@ QScriptValue WindowScriptingInterface::browseDir(const QString& title, const QSt if (!result.isEmpty()) { setPreviousBrowseLocation(QFileInfo(result).absolutePath()); } - return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result); + return result.isEmpty() ? ScriptValuePointer::NullValue : ScriptValuePointer(result); } /// Display a "browse to directory" dialog. If `directory` is an invalid file or directory the browser will start at the current @@ -261,8 +261,8 @@ void WindowScriptingInterface::browseDirAsync(const QString& title, const QStrin /// \param const QString& title title of the window /// \param const QString& directory directory to start the file browser at /// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog` -/// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue` -QScriptValue WindowScriptingInterface::browse(const QString& title, const QString& directory, const QString& nameFilter) { +/// \return ScriptValuePointer file path as a string if one was selected, otherwise `ScriptValuePointer::NullValue` +ScriptValuePointer WindowScriptingInterface::browse(const QString& title, const QString& directory, const QString& nameFilter) { ensureReticleVisible(); QString path = directory; if (path.isEmpty()) { @@ -275,7 +275,7 @@ QScriptValue WindowScriptingInterface::browse(const QString& title, const QStrin if (!result.isEmpty()) { setPreviousBrowseLocation(QFileInfo(result).absolutePath()); } - return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result); + return result.isEmpty() ? ScriptValuePointer::NullValue : ScriptValuePointer(result); } /// Display an open file dialog. If `directory` is an invalid file or directory the browser will start at the current @@ -308,8 +308,8 @@ void WindowScriptingInterface::browseAsync(const QString& title, const QString& /// \param const QString& title title of the window /// \param const QString& directory directory to start the file browser at /// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog` -/// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue` -QScriptValue WindowScriptingInterface::save(const QString& title, const QString& directory, const QString& nameFilter) { +/// \return ScriptValuePointer file path as a string if one was selected, otherwise `ScriptValuePointer::NullValue` +ScriptValuePointer WindowScriptingInterface::save(const QString& title, const QString& directory, const QString& nameFilter) { ensureReticleVisible(); QString path = directory; if (path.isEmpty()) { @@ -322,7 +322,7 @@ QScriptValue WindowScriptingInterface::save(const QString& title, const QString& if (!result.isEmpty()) { setPreviousBrowseLocation(QFileInfo(result).absolutePath()); } - return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result); + return result.isEmpty() ? ScriptValuePointer::NullValue : ScriptValuePointer(result); } /// Display a save file dialog. If `directory` is an invalid file or directory the browser will start at the current @@ -355,8 +355,8 @@ void WindowScriptingInterface::saveAsync(const QString& title, const QString& di /// \param const QString& title title of the window /// \param const QString& directory directory to start the asset browser at /// \param const QString& nameFilter filter to filter asset names by - see `QFileDialog` -/// \return QScriptValue asset path as a string if one was selected, otherwise `QScriptValue::NullValue` -QScriptValue WindowScriptingInterface::browseAssets(const QString& title, const QString& directory, const QString& nameFilter) { +/// \return ScriptValuePointer asset path as a string if one was selected, otherwise `ScriptValuePointer::NullValue` +ScriptValuePointer WindowScriptingInterface::browseAssets(const QString& title, const QString& directory, const QString& nameFilter) { ensureReticleVisible(); QString path = directory; if (path.isEmpty()) { @@ -372,7 +372,7 @@ QScriptValue WindowScriptingInterface::browseAssets(const QString& title, const if (!result.isEmpty()) { setPreviousBrowseAssetLocation(QFileInfo(result).absolutePath()); } - return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result); + return result.isEmpty() ? ScriptValuePointer::NullValue : ScriptValuePointer(result); } /// Display a select asset dialog that lets the user select an asset from the Asset Server. If `directory` is an invalid diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 86d0f400eab..05353bbd613 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -17,11 +17,15 @@ #include #include #include -#include #include +#include #include + +class ScriptValue; +using ScriptValuePointer = QSharedPointer; + /**jsdoc * The Window API provides various facilities not covered elsewhere, including: window dimensions, window focus, * camera view, announcements, user connections, common dialog boxes, snapshots, file import, domain navigation, domain changes, @@ -69,7 +73,7 @@ public slots: * @function Window.hasFocus * @returns {boolean} true if the Interface window has focus, false if it doesn't. */ - QScriptValue hasFocus(); + ScriptValuePointer hasFocus(); /**jsdoc * Makes the Interface window have focus. On Windows, if Interface doesn't already have focus, the task bar icon flashes to @@ -104,7 +108,7 @@ public slots: * var answer = Window.confirm("Are you sure?"); * print(answer); // true or false */ - QScriptValue confirm(const QString& message = ""); + ScriptValuePointer confirm(const QString& message = ""); /**jsdoc * Prompts the user to enter some text. Displays a modal dialog with a message and a text box, plus "OK" and "Cancel" @@ -121,7 +125,7 @@ public slots: * print("User answer: " + answer); * } */ - QScriptValue prompt(const QString& message, const QString& defaultText); + ScriptValuePointer prompt(const QString& message, const QString& defaultText); /**jsdoc * Prompts the user to enter some text. Displays a non-modal dialog with a message and a text box, plus "OK" and "Cancel" @@ -151,7 +155,7 @@ public slots: * var directory = Window.browseDir("Select Directory", Paths.resources); * print("Directory: " + directory); */ - QScriptValue browseDir(const QString& title = "", const QString& directory = ""); + ScriptValuePointer browseDir(const QString& title = "", const QString& directory = ""); /**jsdoc * Prompts the user to choose a directory. Displays a non-modal dialog that navigates the directory tree. A @@ -183,7 +187,7 @@ public slots: * var filename = Window.browse("Select Image File", Paths.resources, "Images (*.png *.jpg *.svg)"); * print("File: " + filename); */ - QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); + ScriptValuePointer browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); /**jsdoc * Prompts the user to choose a file. Displays a non-modal dialog that navigates the directory tree. A @@ -219,7 +223,7 @@ public slots: * var filename = Window.save("Save to JSON file", Paths.resources, "*.json"); * print("File: " + filename); */ - QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); + ScriptValuePointer save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); /**jsdoc * Prompts the user to specify the path and name of a file to save to. Displays a non-modal dialog that navigates the @@ -254,7 +258,7 @@ public slots: * var asset = Window.browseAssets("Select FBX File", "/", "*.fbx"); * print("FBX file: " + asset); */ - QScriptValue browseAssets(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); + ScriptValuePointer browseAssets(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); /**jsdoc * Prompts the user to choose an Asset Server item. Displays a non-modal dialog that navigates the tree of assets on the diff --git a/interface/src/ui/InteractiveWindow.cpp b/interface/src/ui/InteractiveWindow.cpp index daf80acf000..898ee707721 100644 --- a/interface/src/ui/InteractiveWindow.cpp +++ b/interface/src/ui/InteractiveWindow.cpp @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include "OffscreenUi.h" #include "shared/QtHelpers.h" @@ -91,16 +93,16 @@ static void dockWidgetDeleter(DockWidget* dockWidget) { dockWidget->deleteLater(); } -void registerInteractiveWindowMetaType(QScriptEngine* engine) { - qScriptRegisterMetaType(engine, interactiveWindowPointerToScriptValue, interactiveWindowPointerFromScriptValue); +void registerInteractiveWindowMetaType(ScriptEngine* engine) { + scriptRegisterMetaType(engine, interactiveWindowPointerToScriptValue, interactiveWindowPointerFromScriptValue); } -QScriptValue interactiveWindowPointerToScriptValue(QScriptEngine* engine, const InteractiveWindowPointer& in) { - return engine->newQObject(in, QScriptEngine::ScriptOwnership); +ScriptValuePointer interactiveWindowPointerToScriptValue(ScriptEngine* engine, const InteractiveWindowPointer& in) { + return engine->newQObject(in, ScriptEngine::ScriptOwnership); } -void interactiveWindowPointerFromScriptValue(const QScriptValue& object, InteractiveWindowPointer& out) { - if (const auto interactiveWindow = qobject_cast(object.toQObject())) { +void interactiveWindowPointerFromScriptValue(const ScriptValuePointer& object, InteractiveWindowPointer& out) { + if (const auto interactiveWindow = qobject_cast(object->toQObject())) { out = interactiveWindow; } } diff --git a/interface/src/ui/InteractiveWindow.h b/interface/src/ui/InteractiveWindow.h index 7c8897059f6..744bb5042bb 100644 --- a/interface/src/ui/InteractiveWindow.h +++ b/interface/src/ui/InteractiveWindow.h @@ -15,14 +15,18 @@ #define hifi_InteractiveWindow_h #include +#include #include -#include #include #include #include #include +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; + class QmlWindowProxy : public QmlWrapper { Q_OBJECT @@ -408,10 +412,10 @@ protected slots: typedef InteractiveWindow* InteractiveWindowPointer; -QScriptValue interactiveWindowPointerToScriptValue(QScriptEngine* engine, const InteractiveWindowPointer& in); -void interactiveWindowPointerFromScriptValue(const QScriptValue& object, InteractiveWindowPointer& out); +ScriptValuePointer interactiveWindowPointerToScriptValue(ScriptEngine* engine, const InteractiveWindowPointer& in); +void interactiveWindowPointerFromScriptValue(const ScriptValuePointer& object, InteractiveWindowPointer& out); -void registerInteractiveWindowMetaType(QScriptEngine* engine); +void registerInteractiveWindowMetaType(ScriptEngine* engine); Q_DECLARE_METATYPE(InteractiveWindowPointer) diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index ed4ee97780d..8cf76917a47 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include @@ -133,7 +135,7 @@ QStandardItemModel* JSConsole::getAutoCompleteModel(const QString& memberOf) { return model; } -JSConsole::JSConsole(QWidget* parent, const ScriptEnginePointer& scriptEngine) : +JSConsole::JSConsole(QWidget* parent, const ScriptManagerPointer& scriptManager) : QWidget(parent), _ui(new Ui::Console), _currentCommandInHistory(NO_CURRENT_HISTORY_COMMAND), @@ -181,11 +183,11 @@ JSConsole::JSConsole(QWidget* parent, const ScriptEnginePointer& scriptEngine) : QObject::connect(_completer, static_cast(&QCompleter::highlighted), this, &JSConsole::highlightedCompletion); - setScriptEngine(scriptEngine); + setScriptManager(scriptManager); resizeTextInput(); - connect(&_executeWatcher, &QFutureWatcher::finished, this, &JSConsole::commandFinished); + connect(&_executeWatcher, &QFutureWatcher::finished, this, &JSConsole::commandFinished); } void JSConsole::insertCompletion(const QModelIndex& completion) { @@ -305,33 +307,33 @@ void JSConsole::highlightedCompletion(const QModelIndex& completion) { } JSConsole::~JSConsole() { - if (_scriptEngine) { - disconnect(_scriptEngine.data(), nullptr, this, nullptr); - _scriptEngine.reset(); + if (_scriptManager) { + disconnect(_scriptManager.data(), nullptr, this, nullptr); + _scriptManager.reset(); } delete _ui; } -void JSConsole::setScriptEngine(const ScriptEnginePointer& scriptEngine) { - if (_scriptEngine == scriptEngine && scriptEngine != nullptr) { +void JSConsole::setScriptManager(const ScriptManagerPointer& scriptManager) { + if (_scriptManager == scriptManager && scriptManager != nullptr) { return; } - if (_scriptEngine != nullptr) { - disconnect(_scriptEngine.data(), nullptr, this, nullptr); - _scriptEngine.reset(); + if (scriptManager != nullptr) { + disconnect(_scriptManager.data(), nullptr, this, nullptr); + _scriptManager.reset(); } // if scriptEngine is nullptr then create one and keep track of it using _ownScriptEngine - if (scriptEngine.isNull()) { - _scriptEngine = DependencyManager::get()->loadScript(_consoleFileName, false); + if (scriptManager.isNull()) { + _scriptManager = DependencyManager::get()->loadScript(_consoleFileName, false); } else { - _scriptEngine = scriptEngine; + _scriptManager = scriptManager; } - connect(_scriptEngine.data(), &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); - connect(_scriptEngine.data(), &ScriptEngine::infoMessage, this, &JSConsole::handleInfo); - connect(_scriptEngine.data(), &ScriptEngine::warningMessage, this, &JSConsole::handleWarning); - connect(_scriptEngine.data(), &ScriptEngine::errorMessage, this, &JSConsole::handleError); + connect(_scriptManager.data(), &ScriptManager::printedMessage, this, &JSConsole::handlePrint); + connect(_scriptManager.data(), &ScriptManager::infoMessage, this, &JSConsole::handleInfo); + connect(_scriptManager.data(), &ScriptManager::warningMessage, this, &JSConsole::handleWarning); + connect(_scriptManager.data(), &ScriptManager::errorMessage, this, &JSConsole::handleError); } void JSConsole::executeCommand(const QString& command) { @@ -347,14 +349,14 @@ void JSConsole::executeCommand(const QString& command) { appendMessage(">", "" + command.toHtmlEscaped() + ""); - QWeakPointer weakScriptEngine = _scriptEngine; + QWeakPointer weakScriptManager = _scriptManager; auto consoleFileName = _consoleFileName; - QFuture future = QtConcurrent::run([weakScriptEngine, consoleFileName, command]()->QScriptValue{ - QScriptValue result; - auto scriptEngine = weakScriptEngine.lock(); - if (scriptEngine) { - BLOCKING_INVOKE_METHOD(scriptEngine.data(), "evaluate", - Q_RETURN_ARG(QScriptValue, result), + QFuture future = QtConcurrent::run([weakScriptManager, consoleFileName, command]() -> ScriptValuePointer { + ScriptValuePointer result; + auto scriptManager = weakScriptManager.lock(); + if (scriptManager) { + BLOCKING_INVOKE_METHOD(scriptManager->engine().data(), "evaluate", + Q_RETURN_ARG(ScriptValuePointer, result), Q_ARG(const QString&, command), Q_ARG(const QString&, consoleFileName)); } @@ -364,7 +366,7 @@ void JSConsole::executeCommand(const QString& command) { } void JSConsole::commandFinished() { - QScriptValue result = _executeWatcher.result(); + ScriptValuePointer result = _executeWatcher.result(); _ui->promptTextEdit->setDisabled(false); @@ -373,10 +375,10 @@ void JSConsole::commandFinished() { _ui->promptTextEdit->setFocus(); } - bool error = (_scriptEngine->hasUncaughtException() || result.isError()); + bool error = (_scriptEngine->hasUncaughtException() || result->isError()); QString gutter = error ? GUTTER_ERROR : GUTTER_PREVIOUS_COMMAND; QString resultColor = error ? RESULT_ERROR_STYLE : RESULT_SUCCESS_STYLE; - QString resultStr = "" + result.toString().toHtmlEscaped() + ""; + QString resultStr = "" + result->toString().toHtmlEscaped() + ""; appendMessage(gutter, resultStr); resetCurrentCommandHistory(); diff --git a/interface/src/ui/JSConsole.h b/interface/src/ui/JSConsole.h index eeb36018866..3b9ee9121af 100644 --- a/interface/src/ui/JSConsole.h +++ b/interface/src/ui/JSConsole.h @@ -19,7 +19,12 @@ #include #include "ui_console.h" -#include "ScriptEngine.h" + +class QStandardItemModel; +class ScriptManager; +class ScriptValue; +using ScriptManagerPointer = QSharedPointer; +using ScriptValuePointer = QSharedPointer; const QString CONSOLE_TITLE = "Scripting Console"; const float CONSOLE_WINDOW_OPACITY = 0.95f; @@ -29,10 +34,10 @@ const int CONSOLE_HEIGHT = 200; class JSConsole : public QWidget { Q_OBJECT public: - JSConsole(QWidget* parent, const ScriptEnginePointer& scriptEngine = ScriptEnginePointer()); + JSConsole(QWidget* parent, const ScriptManagerPointer& scriptManager = ScriptManagerPointer()); ~JSConsole(); - void setScriptEngine(const ScriptEnginePointer& scriptEngine = ScriptEnginePointer()); + void setScriptManager(const ScriptManagerPointer& scriptManager = ScriptManagerPointer()); void clear(); public slots: @@ -67,13 +72,13 @@ private slots: QStandardItemModel* getAutoCompleteModel(const QString& memberOf = nullptr); - QFutureWatcher _executeWatcher; + QFutureWatcher _executeWatcher; Ui::Console* _ui; int _currentCommandInHistory; QString _savedHistoryFilename; QList _commandHistory; QString _rootCommand; - ScriptEnginePointer _scriptEngine; + ScriptManagerPointer _scriptManager; static const QString _consoleFileName; QJsonArray _apiDocs; QCompleter* _completer; diff --git a/interface/src/ui/TestingDialog.cpp b/interface/src/ui/TestingDialog.cpp index 5f0b20ca7e0..c357820cf35 100644 --- a/interface/src/ui/TestingDialog.cpp +++ b/interface/src/ui/TestingDialog.cpp @@ -13,6 +13,7 @@ #include "Application.h" #include "ScriptEngines.h" +#include TestingDialog::TestingDialog(QWidget* parent) : QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint), @@ -23,12 +24,12 @@ TestingDialog::TestingDialog(QWidget* parent) : _console->setFixedHeight(TESTING_CONSOLE_HEIGHT); - _engine = DependencyManager::get()->loadScript(qApp->applicationDirPath() + testRunnerRelativePath); - _console->setScriptEngine(_engine); - connect(_engine.data(), &ScriptEngine::finished, this, &TestingDialog::onTestingFinished); + _manager = DependencyManager::get()->loadScript(qApp->applicationDirPath() + testRunnerRelativePath); + _console->setScriptManager(_manager); + connect(_manager.data(), &ScriptManager::finished, this, &TestingDialog::onTestingFinished); } void TestingDialog::onTestingFinished(const QString& scriptPath) { - _engine.reset(); - _console->setScriptEngine(); + _manager.reset(); + _console->setScriptManager(); } diff --git a/interface/src/ui/TestingDialog.h b/interface/src/ui/TestingDialog.h index a7e909ca0e1..968188fbca4 100644 --- a/interface/src/ui/TestingDialog.h +++ b/interface/src/ui/TestingDialog.h @@ -13,9 +13,12 @@ #define hifi_TestingDialog_h #include -#include "ScriptEngine.h" +#include #include "JSConsole.h" +class ScriptManager; +using ScriptManagerPointer = QSharedPointer; + const QString windowLabel = "Client Script Tests"; const QString testRunnerRelativePath = "/scripts/developer/tests/unit_tests/testRunner.js"; const unsigned int TESTING_CONSOLE_HEIGHT = 400; @@ -29,7 +32,7 @@ class TestingDialog : public QDialog { private: std::unique_ptr _console; - ScriptEnginePointer _engine; + ScriptManagerPointer _manager; }; #endif diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index e9e310e68bc..9ed3132466d 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -12,8 +12,6 @@ #include -#include - #include #include #include @@ -30,6 +28,7 @@ #include #include #include +#include #include #include "VariantMapToScriptValue.h" @@ -42,7 +41,7 @@ Q_LOGGING_CATEGORY(trace_render_overlays, "trace.render.overlays") std::unordered_map Overlays::_entityToOverlayTypes; std::unordered_map Overlays::_overlayToEntityTypes; -Overlays::Overlays() { +Overlays::Overlays() : _scriptEngine(newScriptEngine()) { ADD_TYPE_MAP(Box, cube); ADD_TYPE_MAP(Sphere, sphere); _overlayToEntityTypes["rectangle3d"] = "Shape"; @@ -632,16 +631,16 @@ EntityItemProperties Overlays::convertOverlayToEntityProperties(QVariantMap& ove } } - QScriptEngine scriptEngine; - QScriptValue props = variantMapToScriptValue(overlayProps, scriptEngine); + ScriptEnginePointer scriptEngine = newScriptEngine(); + ScriptValuePointer props = variantMapToScriptValue(overlayProps, scriptEngine); EntityItemProperties toReturn; EntityItemPropertiesFromScriptValueHonorReadOnly(props, toReturn); return toReturn; } QVariantMap Overlays::convertEntityToOverlayProperties(const EntityItemProperties& properties) { - QScriptEngine scriptEngine; - QVariantMap overlayProps = EntityItemPropertiesToScriptValue(&scriptEngine, properties).toVariant().toMap(); + ScriptEnginePointer scriptEngine = newScriptEngine(); + QVariantMap overlayProps = EntityItemPropertiesToScriptValue(scriptEngine, properties)->toVariant().toMap(); QString type = overlayProps["type"].toString(); overlayProps["type"] = entityToOverlayType(type); @@ -740,7 +739,7 @@ QVariantMap Overlays::convertEntityToOverlayProperties(const EntityItemPropertie GROUP_ENTITY_TO_OVERLAY_PROP(ring, majorTickMarksColor, majorTickMarksColor); GROUP_ENTITY_TO_OVERLAY_PROP(ring, minorTickMarksColor, minorTickMarksColor); } else if (type == "PolyLine") { - QVector points = qVectorVec3FromScriptValue(scriptEngine.newVariant(overlayProps["linePoints"])); + QVector points = qVectorVec3FromScriptValue(scriptEngine->newVariant(overlayProps["linePoints"])); glm::vec3 position = vec3FromVariant(overlayProps["position"]); if (points.length() > 1) { overlayProps["p1"] = vec3toVariant(points[0] + position); @@ -755,7 +754,7 @@ QVariantMap Overlays::convertEntityToOverlayProperties(const EntityItemPropertie RENAME_PROP(p2, endPoint); RENAME_PROP(p2, end); - QVector widths = qVectorFloatFromScriptValue(scriptEngine.newVariant(overlayProps["strokeWidths"])); + QVector widths = qVectorFloatFromScriptValue(scriptEngine->newVariant(overlayProps["strokeWidths"])); if (widths.length() > 0) { overlayProps["lineWidth"] = widths[0]; } @@ -1041,8 +1040,8 @@ QVariantMap Overlays::getOverlaysProperties(const QVariant& propertiesById) { } RayToOverlayIntersectionResult Overlays::findRayIntersection(const PickRay& ray, bool precisionPicking, - const QScriptValue& overlayIDsToInclude, - const QScriptValue& overlayIDsToDiscard, + const ScriptValuePointer& overlayIDsToInclude, + const ScriptValuePointer& overlayIDsToDiscard, bool visibleOnly, bool collidableOnly) { const QVector include = qVectorEntityItemIDFromScriptValue(overlayIDsToInclude); const QVector discard = qVectorEntityItemIDFromScriptValue(overlayIDsToDiscard); @@ -1110,38 +1109,38 @@ ParabolaToOverlayIntersectionResult Overlays::findParabolaIntersectionVector(con return overlayResult; } -QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, const RayToOverlayIntersectionResult& value) { - QScriptValue obj = engine->newObject(); - obj.setProperty("intersects", value.intersects); - QScriptValue overlayIDValue = quuidToScriptValue(engine, value.overlayID); - obj.setProperty("overlayID", overlayIDValue); - obj.setProperty("distance", value.distance); - obj.setProperty("face", boxFaceToString(value.face)); - - QScriptValue intersection = vec3ToScriptValue(engine, value.intersection); - obj.setProperty("intersection", intersection); - QScriptValue surfaceNormal = vec3ToScriptValue(engine, value.surfaceNormal); - obj.setProperty("surfaceNormal", surfaceNormal); - obj.setProperty("extraInfo", engine->toScriptValue(value.extraInfo)); +ScriptValuePointer RayToOverlayIntersectionResultToScriptValue(ScriptEngine* engine, const RayToOverlayIntersectionResult& value) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty("intersects", value.intersects); + ScriptValuePointer overlayIDValue = quuidToScriptValue(engine, value.overlayID); + obj->setProperty("overlayID", overlayIDValue); + obj->setProperty("distance", value.distance); + obj->setProperty("face", boxFaceToString(value.face)); + + ScriptValuePointer intersection = vec3ToScriptValue(engine, value.intersection); + obj->setProperty("intersection", intersection); + ScriptValuePointer surfaceNormal = vec3ToScriptValue(engine, value.surfaceNormal); + obj->setProperty("surfaceNormal", surfaceNormal); + obj->setProperty("extraInfo", engine->toScriptValue(value.extraInfo)); return obj; } -void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, RayToOverlayIntersectionResult& value) { - value.intersects = object.property("intersects").toVariant().toBool(); - QScriptValue overlayIDValue = object.property("overlayID"); +void RayToOverlayIntersectionResultFromScriptValue(const ScriptValuePointer& object, RayToOverlayIntersectionResult& value) { + value.intersects = object->property("intersects")->toVariant().toBool(); + ScriptValuePointer overlayIDValue = object->property("overlayID"); quuidFromScriptValue(overlayIDValue, value.overlayID); - value.distance = object.property("distance").toVariant().toFloat(); - value.face = boxFaceFromString(object.property("face").toVariant().toString()); + value.distance = object->property("distance")->toVariant().toFloat(); + value.face = boxFaceFromString(object->property("face")->toVariant().toString()); - QScriptValue intersection = object.property("intersection"); - if (intersection.isValid()) { + ScriptValuePointer intersection = object->property("intersection"); + if (intersection->isValid()) { vec3FromScriptValue(intersection, value.intersection); } - QScriptValue surfaceNormal = object.property("surfaceNormal"); - if (surfaceNormal.isValid()) { + ScriptValuePointer surfaceNormal = object->property("surfaceNormal"); + if (surfaceNormal->isValid()) { vec3FromScriptValue(surfaceNormal, value.surfaceNormal); } - value.extraInfo = object.property("extraInfo").toVariant().toMap(); + value.extraInfo = object->property("extraInfo")->toVariant().toMap(); } bool Overlays::isLoaded(const QUuid& id) { diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 6e59cbc00e9..51ea5dc9e7a 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -20,15 +20,18 @@ #include #include -#include #include +#include #include "Overlay.h" #include class PickRay; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; /**jsdoc * The result of a {@link PickRay} search using {@link Overlays.findRayIntersection|findRayIntersection}. @@ -52,8 +55,8 @@ class RayToOverlayIntersectionResult { QVariantMap extraInfo; }; Q_DECLARE_METATYPE(RayToOverlayIntersectionResult); -QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, const RayToOverlayIntersectionResult& value); -void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, RayToOverlayIntersectionResult& value); +ScriptValuePointer RayToOverlayIntersectionResultToScriptValue(ScriptEngine* engine, const RayToOverlayIntersectionResult& value); +void RayToOverlayIntersectionResultFromScriptValue(const ScriptValuePointer& object, RayToOverlayIntersectionResult& value); class ParabolaToOverlayIntersectionResult { public: @@ -120,7 +123,7 @@ class Overlays : public QObject { void cleanupAllOverlays(); - mutable QScriptEngine _scriptEngine; + mutable ScriptEnginePointer _scriptEngine; public slots: /**jsdoc @@ -411,8 +414,8 @@ public slots: */ RayToOverlayIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false, - const QScriptValue& include = QScriptValue(), - const QScriptValue& discard = QScriptValue(), + const ScriptValuePointer& include = ScriptValuePointer(), + const ScriptValuePointer& discard = ScriptValuePointer(), bool visibleOnly = false, bool collidableOnly = false); diff --git a/libraries/animation/CMakeLists.txt b/libraries/animation/CMakeLists.txt index d962d9e2224..894f6b576b6 100644 --- a/libraries/animation/CMakeLists.txt +++ b/libraries/animation/CMakeLists.txt @@ -1,9 +1,10 @@ set(TARGET_NAME animation) -setup_hifi_library(Network Script) +setup_hifi_library(Network) link_hifi_libraries(shared graphics model-serializers) include_hifi_library_headers(networking) include_hifi_library_headers(gpu) include_hifi_library_headers(hfm) include_hifi_library_headers(image) +include_hifi_library_headers(script-engine) target_nsight() diff --git a/libraries/animation/src/AnimVariant.cpp b/libraries/animation/src/AnimVariant.cpp index 1519aa8109e..9b596b2356b 100644 --- a/libraries/animation/src/AnimVariant.cpp +++ b/libraries/animation/src/AnimVariant.cpp @@ -11,39 +11,39 @@ #include "AnimVariant.h" // which has AnimVariant/AnimVariantMap -#include -#include +#include #include -#include +#include +#include const AnimVariant AnimVariant::False = AnimVariant(); -QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine, const QStringList& names, bool useNames) const { +ScriptValuePointer AnimVariantMap::animVariantMapToScriptValue(ScriptEngine* engine, const QStringList& names, bool useNames) const { if (QThread::currentThread() != engine->thread()) { qCWarning(animation) << "Cannot create Javacript object from non-script thread" << QThread::currentThread(); Q_ASSERT(false); - return QScriptValue(); + return ScriptValuePointer(); } - QScriptValue target = engine->newObject(); + ScriptValuePointer target = engine->newObject(); auto setOne = [&] (const QString& name, const AnimVariant& value) { switch (value.getType()) { case AnimVariant::Type::Bool: - target.setProperty(name, value.getBool()); + target->setProperty(name, value.getBool()); break; case AnimVariant::Type::Int: - target.setProperty(name, value.getInt()); + target->setProperty(name, value.getInt()); break; case AnimVariant::Type::Float: - target.setProperty(name, value.getFloat()); + target->setProperty(name, value.getFloat()); break; case AnimVariant::Type::String: - target.setProperty(name, value.getString()); + target->setProperty(name, value.getString()); break; case AnimVariant::Type::Vec3: - target.setProperty(name, vec3ToScriptValue(engine, value.getVec3())); + target->setProperty(name, vec3ToScriptValue(engine, value.getVec3())); break; case AnimVariant::Type::Quat: - target.setProperty(name, quatToScriptValue(engine, value.getQuat())); + target->setProperty(name, quatToScriptValue(engine, value.getQuat())); break; default: // Unknown type @@ -56,7 +56,7 @@ QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine, if (search != _map.end()) { setOne(name, search->second); } else if (_triggers.count(name) == 1) { - target.setProperty(name, true); + target->setProperty(name, true); } // scripts are allowed to request names that do not exist } @@ -74,8 +74,8 @@ void AnimVariantMap::copyVariantsFrom(const AnimVariantMap& other) { } } -void AnimVariantMap::animVariantMapFromScriptValue(const QScriptValue& source) { - if (QThread::currentThread() != source.engine()->thread()) { +void AnimVariantMap::animVariantMapFromScriptValue(const ScriptValuePointer& source) { + if (QThread::currentThread() != source->engine()->thread()) { qCWarning(animation) << "Cannot examine Javacript object from non-script thread" << QThread::currentThread(); Q_ASSERT(false); return; @@ -84,43 +84,43 @@ void AnimVariantMap::animVariantMapFromScriptValue(const QScriptValue& source) { // Whenever we identify a new outbound type in animVariantMapToScriptValue above, or a new inbound type in the code that follows here, // we would enter it into the dictionary. Then switch on that type here, with the code that follow being executed only if // the type is not known. One problem with that is that there is no checking that two different script use the same name differently. - QScriptValueIterator property(source); - // Note: QScriptValueIterator iterates only over source's own properties. It does not follow the prototype chain. - while (property.hasNext()) { - property.next(); - QScriptValue value = property.value(); - if (value.isBool()) { - set(property.name(), value.toBool()); - } else if (value.isString()) { - set(property.name(), value.toString()); - } else if (value.isNumber()) { - int asInteger = value.toInt32(); - float asFloat = value.toNumber(); + ScriptValueIteratorPointer property(source->newIterator()); + // Note: ScriptValueIterator iterates only over source's own properties. It does not follow the prototype chain. + while (property->hasNext()) { + property->next(); + ScriptValuePointer value = property->value(); + if (value->isBool()) { + set(property->name(), value->toBool()); + } else if (value->isString()) { + set(property->name(), value->toString()); + } else if (value->isNumber()) { + int asInteger = value->toInt32(); + float asFloat = value->toNumber(); if (asInteger == asFloat) { - set(property.name(), asInteger); + set(property->name(), asInteger); } else { - set(property.name(), asFloat); + set(property->name(), asFloat); } } else { // Try to get x,y,z and possibly w - if (value.isObject()) { - QScriptValue x = value.property("x"); - if (x.isNumber()) { - QScriptValue y = value.property("y"); - if (y.isNumber()) { - QScriptValue z = value.property("z"); - if (z.isNumber()) { - QScriptValue w = value.property("w"); - if (w.isNumber()) { - set(property.name(), glm::quat(w.toNumber(), x.toNumber(), y.toNumber(), z.toNumber())); + if (value->isObject()) { + ScriptValuePointer x = value->property("x"); + if (x->isNumber()) { + ScriptValuePointer y = value->property("y"); + if (y->isNumber()) { + ScriptValuePointer z = value->property("z"); + if (z->isNumber()) { + ScriptValuePointer w = value->property("w"); + if (w->isNumber()) { + set(property->name(), glm::quat(w->toNumber(), x->toNumber(), y->toNumber(), z->toNumber())); } else { - set(property.name(), glm::vec3(x.toNumber(), y.toNumber(), z.toNumber())); + set(property->name(), glm::vec3(x->toNumber(), y->toNumber(), z->toNumber())); } continue; // we got either a vector or quaternion object, so don't fall through to warning } } } } - qCWarning(animation) << "Ignoring unrecognized data" << value.toString() << "for animation property" << property.name(); + qCWarning(animation) << "Ignoring unrecognized data" << value->toString() << "for animation property" << property->name(); Q_ASSERT(false); } } diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index d9e75d5844f..15a0b0d48d7 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -17,10 +17,14 @@ #include #include #include -#include #include #include #include "AnimationLogging.h" +#include + +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; class AnimVariant { public: @@ -229,9 +233,9 @@ class AnimVariantMap { } // Answer a Plain Old Javascript Object (for the given engine) all of our values set as properties. - QScriptValue animVariantMapToScriptValue(QScriptEngine* engine, const QStringList& names, bool useNames) const; + ScriptValuePointer animVariantMapToScriptValue(ScriptEngine* engine, const QStringList& names, bool useNames) const; // Side-effect us with the value of object's own properties. (No inherited properties.) - void animVariantMapFromScriptValue(const QScriptValue& object); + void animVariantMapFromScriptValue(const ScriptValuePointer& object); void copyVariantsFrom(const AnimVariantMap& other); // For stat debugging. @@ -274,7 +278,7 @@ class AnimVariantMap { glm::quat _rigToGeometryRot; }; -typedef std::function AnimVariantResultHandler; +typedef std::function AnimVariantResultHandler; Q_DECLARE_METATYPE(AnimVariantResultHandler); Q_DECLARE_METATYPE(AnimVariantMap) diff --git a/libraries/animation/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h index e8bfa75ecb6..a1ba75879d7 100644 --- a/libraries/animation/src/AnimationCache.h +++ b/libraries/animation/src/AnimationCache.h @@ -13,8 +13,6 @@ #define hifi_AnimationCache_h #include -#include -#include #include #include diff --git a/libraries/animation/src/AnimationObject.cpp b/libraries/animation/src/AnimationObject.cpp index 831f310beec..2eda4081f06 100644 --- a/libraries/animation/src/AnimationObject.cpp +++ b/libraries/animation/src/AnimationObject.cpp @@ -11,27 +11,28 @@ #include "AnimationObject.h" -#include +#include +#include #include "AnimationCache.h" QStringList AnimationObject::getJointNames() const { - return qscriptvalue_cast(thisObject())->getJointNames(); + return scriptvalue_cast(thisObject())->getJointNames(); } QVector AnimationObject::getFrames() const { - return qscriptvalue_cast(thisObject())->getFrames(); + return scriptvalue_cast(thisObject())->getFrames(); } QVector AnimationFrameObject::getRotations() const { - return qscriptvalue_cast(thisObject()).rotations; + return scriptvalue_cast(thisObject()).rotations; } -void registerAnimationTypes(QScriptEngine* engine) { - qScriptRegisterSequenceMetaType >(engine); +void registerAnimationTypes(ScriptEngine* engine) { + scriptRegisterSequenceMetaType >(engine); engine->setDefaultPrototype(qMetaTypeId(), engine->newQObject( - new AnimationFrameObject(), QScriptEngine::ScriptOwnership)); + new AnimationFrameObject(), ScriptEngine::ScriptOwnership)); engine->setDefaultPrototype(qMetaTypeId(), engine->newQObject( - new AnimationObject(), QScriptEngine::ScriptOwnership)); + new AnimationObject(), ScriptEngine::ScriptOwnership)); } diff --git a/libraries/animation/src/AnimationObject.h b/libraries/animation/src/AnimationObject.h index 9df58a20afb..5e09ce1e0bb 100644 --- a/libraries/animation/src/AnimationObject.h +++ b/libraries/animation/src/AnimationObject.h @@ -13,11 +13,11 @@ #define hifi_AnimationObject_h #include -#include #include +#include "Scriptable.h" -class QScriptEngine; +class ScriptEngine; /**jsdoc * Information about an animation resource, created by {@link AnimationCache.getAnimation}. @@ -35,7 +35,7 @@ class QScriptEngine; * @property {AnimationFrameObject[]} frames - The frames in the animation. Read-only. */ /// Scriptable wrapper for animation pointers. -class AnimationObject : public QObject, protected QScriptable { +class AnimationObject : public QObject, protected Scriptable { Q_OBJECT Q_PROPERTY(QStringList jointNames READ getJointNames) Q_PROPERTY(QVector frames READ getFrames) @@ -72,7 +72,7 @@ class AnimationObject : public QObject, protected QScriptable { * @property {Quat[]} rotations - Joint rotations. Read-only. */ /// Scriptable wrapper for animation frames. -class AnimationFrameObject : public QObject, protected QScriptable { +class AnimationFrameObject : public QObject, protected Scriptable { Q_OBJECT Q_PROPERTY(QVector rotations READ getRotations) @@ -86,6 +86,6 @@ class AnimationFrameObject : public QObject, protected QScriptable { Q_INVOKABLE QVector getRotations() const; }; -void registerAnimationTypes(QScriptEngine* engine); +void registerAnimationTypes(ScriptEngine* engine); #endif // hifi_AnimationObject_h diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index e0589fdba98..d79584ef2ce 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -13,7 +13,6 @@ #include #include -#include #include #include @@ -21,7 +20,9 @@ #include #include #include +#include #include +#include #include #include "AnimationLogging.h" @@ -1584,10 +1585,10 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos } // Allow script to add/remove handlers and report results, from within their thread. -QScriptValue Rig::addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { // called in script thread +ScriptValuePointer Rig::addAnimationStateHandler(ScriptValuePointer handler, ScriptValuePointer propertiesList) { // called in script thread // validate argument types - if (handler.isFunction() && (isListOfStrings(propertiesList) || propertiesList.isUndefined() || propertiesList.isNull())) { + if (handler->isFunction() && (isListOfStrings(propertiesList) || propertiesList->isUndefined() || propertiesList->isNull())) { QMutexLocker locker(&_stateMutex); // Find a safe id, even if there are lots of many scripts add and remove handlers repeatedly. while (!_nextStateHandlerId || _stateHandlers.contains(_nextStateHandlerId)) { // 0 is unused, and don't reuse existing after wrap. @@ -1595,28 +1596,28 @@ QScriptValue Rig::addAnimationStateHandler(QScriptValue handler, QScriptValue pr } StateHandler& data = _stateHandlers[_nextStateHandlerId]; data.function = handler; - data.useNames = propertiesList.isArray(); + data.useNames = propertiesList->isArray(); if (data.useNames) { - data.propertyNames = propertiesList.toVariant().toStringList(); + data.propertyNames = propertiesList->toVariant().toStringList(); } - return QScriptValue(_nextStateHandlerId); // suitable for giving to removeAnimationStateHandler + return handler->engine()->newValue(_nextStateHandlerId); // suitable for giving to removeAnimationStateHandler } else { qCWarning(animation) << "Rig::addAnimationStateHandler invalid arguments, expected (function, string[])"; - return QScriptValue(QScriptValue::UndefinedValue); + return handler ? handler->engine()->undefinedValue() : ScriptValuePointer(); } } -void Rig::removeAnimationStateHandler(QScriptValue identifier) { // called in script thread +void Rig::removeAnimationStateHandler(ScriptValuePointer identifier) { // called in script thread // validate arguments - if (identifier.isNumber()) { + if (identifier->isNumber()) { QMutexLocker locker(&_stateMutex); - _stateHandlers.remove(identifier.toInt32()); // silently continues if handler not present. 0 is unused + _stateHandlers.remove(identifier->toInt32()); // silently continues if handler not present. 0 is unused } else { qCWarning(animation) << "Rig::removeAnimationStateHandler invalid argument, expected a number"; } } -void Rig::animationStateHandlerResult(int identifier, QScriptValue result) { // called synchronously from script +void Rig::animationStateHandlerResult(int identifier, ScriptValuePointer result) { // called synchronously from script QMutexLocker locker(&_stateMutex); auto found = _stateHandlers.find(identifier); if (found == _stateHandlers.end()) { @@ -1635,9 +1636,9 @@ void Rig::updateAnimationStateHandlers() { // called on avatar update thread (wh // call out: int identifier = data.key(); StateHandler& value = data.value(); - QScriptValue& function = value.function; + ScriptValuePointer& function = value.function; int rigId = _rigId; - auto handleResult = [rigId, identifier](QScriptValue result) { // called in script thread to get the result back to us. + auto handleResult = [rigId, identifier](ScriptValuePointer result) { // called in script thread to get the result back to us. // Hold the rigRegistryMutex to ensure thread-safe access to the rigRegistry, but // also to prevent the rig from being deleted while this lambda is being executed. std::lock_guard guard(rigRegistryMutex); @@ -1652,8 +1653,8 @@ void Rig::updateAnimationStateHandlers() { // called on avatar update thread (wh }; // invokeMethod makes a copy of the args, and copies of AnimVariantMap do copy the underlying map, so this will correctly capture // the state of _animVars and allow continued changes to _animVars in this thread without conflict. - QMetaObject::invokeMethod(function.engine(), "callAnimationStateHandler", Qt::QueuedConnection, - Q_ARG(QScriptValue, function), + QMetaObject::invokeMethod(function->engine(), "callAnimationStateHandler", Qt::QueuedConnection, + Q_ARG(ScriptValuePointer, function), Q_ARG(AnimVariantMap, _animVars), Q_ARG(QStringList, value.propertyNames), Q_ARG(bool, value.useNames), diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 7ae48a90ee9..f2f672df35d 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -16,10 +16,10 @@ #include #include -#include #include #include #include +#include #include "AnimNode.h" #include "AnimNodeLoader.h" @@ -30,6 +30,8 @@ class Rig; class AnimInverseKinematics; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; // Rig instances are reentrant. // However only specific methods thread-safe. Noted below. @@ -40,7 +42,7 @@ class Rig : public QObject { struct StateHandler { AnimVariantMap results; QStringList propertyNames; - QScriptValue function; + ScriptValuePointer function; bool useNames; }; @@ -205,9 +207,9 @@ class Rig : public QObject { AnimNode::ConstPointer getAnimNode() const { return _animNode; } AnimNode::ConstPointer findAnimNodeByName(const QString& name) const; AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; } - QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList); - void removeAnimationStateHandler(QScriptValue handler); - void animationStateHandlerResult(int identifier, QScriptValue result); + ScriptValuePointer addAnimationStateHandler(ScriptValuePointer handler, ScriptValuePointer propertiesList); + void removeAnimationStateHandler(ScriptValuePointer handler); + void animationStateHandlerResult(int identifier, ScriptValuePointer result); // rig space bool getModelRegistrationPoint(glm::vec3& modelRegistrationPointOut) const; diff --git a/libraries/audio/CMakeLists.txt b/libraries/audio/CMakeLists.txt index a8a398c14ff..c7399492cb2 100644 --- a/libraries/audio/CMakeLists.txt +++ b/libraries/audio/CMakeLists.txt @@ -6,3 +6,4 @@ if (ANDROID) endif () link_hifi_libraries(networking shared plugins) +include_hifi_library_headers(script-engine) diff --git a/libraries/audio/src/AudioEffectOptions.cpp b/libraries/audio/src/AudioEffectOptions.cpp index 873ce7df877..7451912ffef 100644 --- a/libraries/audio/src/AudioEffectOptions.cpp +++ b/libraries/audio/src/AudioEffectOptions.cpp @@ -10,6 +10,10 @@ #include "AudioEffectOptions.h" +#include +#include +#include + static const QString BANDWIDTH_HANDLE = "bandwidth"; static const QString PRE_DELAY_HANDLE = "preDelay"; static const QString LATE_DELAY_HANDLE = "lateDelay"; @@ -54,8 +58,8 @@ static const float LATE_MIX_LEFT_DEFAULT = 90.0f; static const float LATE_MIX_RIGHT_DEFAULT = 90.0f; static const float WET_DRY_MIX_DEFAULT = 50.0f; -static void setOption(QScriptValue arguments, const QString name, float defaultValue, float& variable) { - variable = arguments.property(name).isNumber() ? (float)arguments.property(name).toNumber() : defaultValue; +static void setOption(ScriptValuePointer arguments, const QString name, float defaultValue, float& variable) { + variable = arguments->property(name)->isNumber() ? (float)arguments->property(name)->toNumber() : defaultValue; } /**jsdoc @@ -83,7 +87,7 @@ static void setOption(QScriptValue arguments, const QString name, float defaultV * @property {number} lateMixRight=90 - The apparent distance of the source (percent) in the reverb tail. * @property {number} wetDryMix=50 - Adjusts the wet/dry ratio, from completely dry (0%) to completely wet (100%). */ -AudioEffectOptions::AudioEffectOptions(QScriptValue arguments) { +AudioEffectOptions::AudioEffectOptions(ScriptValuePointer arguments) { setOption(arguments, BANDWIDTH_HANDLE, BANDWIDTH_DEFAULT, _bandwidth); setOption(arguments, PRE_DELAY_HANDLE, PRE_DELAY_DEFAULT, _preDelay); setOption(arguments, LATE_DELAY_HANDLE, LATE_DELAY_DEFAULT, _lateDelay); @@ -137,6 +141,6 @@ AudioEffectOptions& AudioEffectOptions::operator=(const AudioEffectOptions &othe return *this; } -QScriptValue AudioEffectOptions::constructor(QScriptContext* context, QScriptEngine* engine) { +ScriptValuePointer AudioEffectOptions::constructor(ScriptContext* context, ScriptEngine* engine) { return engine->newQObject(new AudioEffectOptions(context->argument(0))); } diff --git a/libraries/audio/src/AudioEffectOptions.h b/libraries/audio/src/AudioEffectOptions.h index 48fe44feb53..45026eb0c17 100644 --- a/libraries/audio/src/AudioEffectOptions.h +++ b/libraries/audio/src/AudioEffectOptions.h @@ -12,8 +12,12 @@ #define hifi_AudioEffectOptions_h #include -#include -#include +#include + +class ScriptContext; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; /**jsdoc * Audio effect options used by the {@link Audio} API. @@ -78,11 +82,11 @@ class AudioEffectOptions : public QObject { Q_PROPERTY(float wetDryMix READ getWetDryMix WRITE setWetDryMix) public: - AudioEffectOptions(QScriptValue arguments = QScriptValue()); + AudioEffectOptions(ScriptValuePointer arguments = ScriptValuePointer()); AudioEffectOptions(const AudioEffectOptions &other); AudioEffectOptions& operator=(const AudioEffectOptions &other); - static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); + static ScriptValuePointer constructor(ScriptContext* context, ScriptEngine* engine); float getBandwidth() const { return _bandwidth; } void setBandwidth(float bandwidth) { _bandwidth = bandwidth; } diff --git a/libraries/audio/src/AudioInjectorOptions.cpp b/libraries/audio/src/AudioInjectorOptions.cpp index 99a2886a77c..4c287e203bc 100644 --- a/libraries/audio/src/AudioInjectorOptions.cpp +++ b/libraries/audio/src/AudioInjectorOptions.cpp @@ -11,9 +11,9 @@ #include "AudioInjectorOptions.h" -#include - -#include +#include +#include +#include #include "AudioLogging.h" @@ -32,18 +32,18 @@ AudioInjectorOptions::AudioInjectorOptions() : { } -QScriptValue injectorOptionsToScriptValue(QScriptEngine* engine, const AudioInjectorOptions& injectorOptions) { - QScriptValue obj = engine->newObject(); +ScriptValuePointer injectorOptionsToScriptValue(ScriptEngine* engine, const AudioInjectorOptions& injectorOptions) { + ScriptValuePointer obj = engine->newObject(); if (injectorOptions.positionSet) { - obj.setProperty("position", vec3ToScriptValue(engine, injectorOptions.position)); + obj->setProperty("position", vec3ToScriptValue(engine, injectorOptions.position)); } - obj.setProperty("volume", injectorOptions.volume); - obj.setProperty("loop", injectorOptions.loop); - obj.setProperty("orientation", quatToScriptValue(engine, injectorOptions.orientation)); - obj.setProperty("ignorePenumbra", injectorOptions.ignorePenumbra); - obj.setProperty("localOnly", injectorOptions.localOnly); - obj.setProperty("secondOffset", injectorOptions.secondOffset); - obj.setProperty("pitch", injectorOptions.pitch); + obj->setProperty("volume", injectorOptions.volume); + obj->setProperty("loop", injectorOptions.loop); + obj->setProperty("orientation", quatToScriptValue(engine, injectorOptions.orientation)); + obj->setProperty("ignorePenumbra", injectorOptions.ignorePenumbra); + obj->setProperty("localOnly", injectorOptions.localOnly); + obj->setProperty("secondOffset", injectorOptions.secondOffset); + obj->setProperty("pitch", injectorOptions.pitch); return obj; } @@ -66,8 +66,8 @@ QScriptValue injectorOptionsToScriptValue(QScriptEngine* engine, const AudioInje * @property {boolean} ignorePenumbra=false -

Deprecated: This property is deprecated and will be * removed.

*/ -void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOptions& injectorOptions) { - if (!object.isObject()) { +void injectorOptionsFromScriptValue(const ScriptValuePointer& object, AudioInjectorOptions& injectorOptions) { + if (!object->isObject()) { qWarning() << "Audio injector options is not an object."; return; } @@ -77,53 +77,53 @@ void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOpt } injectorOptions.positionSet = false; - QScriptValueIterator it(object); - while (it.hasNext()) { - it.next(); + ScriptValueIteratorPointer it(object->newIterator()); + while (it->hasNext()) { + it->next(); - if (it.name() == "position") { - vec3FromScriptValue(object.property("position"), injectorOptions.position); + if (it->name() == "position") { + vec3FromScriptValue(object->property("position"), injectorOptions.position); injectorOptions.positionSet = true; - } else if (it.name() == "orientation") { - quatFromScriptValue(object.property("orientation"), injectorOptions.orientation); - } else if (it.name() == "volume") { - if (it.value().isNumber()) { - injectorOptions.volume = it.value().toNumber(); + } else if (it->name() == "orientation") { + quatFromScriptValue(object->property("orientation"), injectorOptions.orientation); + } else if (it->name() == "volume") { + if (it->value()->isNumber()) { + injectorOptions.volume = it->value()->toNumber(); } else { qCWarning(audio) << "Audio injector options: volume is not a number"; } - } else if (it.name() == "loop") { - if (it.value().isBool()) { - injectorOptions.loop = it.value().toBool(); + } else if (it->name() == "loop") { + if (it->value()->isBool()) { + injectorOptions.loop = it->value()->toBool(); } else { qCWarning(audio) << "Audio injector options: loop is not a boolean"; } - } else if (it.name() == "ignorePenumbra") { - if (it.value().isBool()) { - injectorOptions.ignorePenumbra = it.value().toBool(); + } else if (it->name() == "ignorePenumbra") { + if (it->value()->isBool()) { + injectorOptions.ignorePenumbra = it->value()->toBool(); } else { qCWarning(audio) << "Audio injector options: ignorePenumbra is not a boolean"; } - } else if (it.name() == "localOnly") { - if (it.value().isBool()) { - injectorOptions.localOnly = it.value().toBool(); + } else if (it->name() == "localOnly") { + if (it->value()->isBool()) { + injectorOptions.localOnly = it->value()->toBool(); } else { qCWarning(audio) << "Audio injector options: localOnly is not a boolean"; } - } else if (it.name() == "secondOffset") { - if (it.value().isNumber()) { - injectorOptions.secondOffset = it.value().toNumber(); + } else if (it->name() == "secondOffset") { + if (it->value()->isNumber()) { + injectorOptions.secondOffset = it->value()->toNumber(); } else { qCWarning(audio) << "Audio injector options: secondOffset is not a number"; } - } else if (it.name() == "pitch") { - if (it.value().isNumber()) { - injectorOptions.pitch = it.value().toNumber(); + } else if (it->name() == "pitch") { + if (it->value()->isNumber()) { + injectorOptions.pitch = it->value()->toNumber(); } else { qCWarning(audio) << "Audio injector options: pitch is not a number"; } } else { - qCWarning(audio) << "Unknown audio injector option:" << it.name(); + qCWarning(audio) << "Unknown audio injector option:" << it->name(); } } } diff --git a/libraries/audio/src/AudioInjectorOptions.h b/libraries/audio/src/AudioInjectorOptions.h index 5dec8a02403..3605bc2a068 100644 --- a/libraries/audio/src/AudioInjectorOptions.h +++ b/libraries/audio/src/AudioInjectorOptions.h @@ -12,10 +12,13 @@ #ifndef hifi_AudioInjectorOptions_h #define hifi_AudioInjectorOptions_h -#include - #include #include +#include + +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; class AudioInjectorOptions { public: @@ -35,7 +38,7 @@ class AudioInjectorOptions { Q_DECLARE_METATYPE(AudioInjectorOptions); -QScriptValue injectorOptionsToScriptValue(QScriptEngine* engine, const AudioInjectorOptions& injectorOptions); -void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOptions& injectorOptions); +ScriptValuePointer injectorOptionsToScriptValue(ScriptEngine* engine, const AudioInjectorOptions& injectorOptions); +void injectorOptionsFromScriptValue(const ScriptValuePointer& object, AudioInjectorOptions& injectorOptions); #endif // hifi_AudioInjectorOptions_h diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 7c5dd3813bc..c6b68112285 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include "AudioRingBuffer.h" #include "AudioLogging.h" @@ -422,12 +424,12 @@ SoundProcessor::AudioProperties SoundProcessor::interpretAsMP3(const QByteArray& } -QScriptValue soundSharedPointerToScriptValue(QScriptEngine* engine, const SharedSoundPointer& in) { - return engine->newQObject(new SoundScriptingInterface(in), QScriptEngine::ScriptOwnership); +ScriptValuePointer soundSharedPointerToScriptValue(ScriptEngine* engine, const SharedSoundPointer& in) { + return engine->newQObject(new SoundScriptingInterface(in), ScriptEngine::ScriptOwnership); } -void soundSharedPointerFromScriptValue(const QScriptValue& object, SharedSoundPointer& out) { - if (auto soundInterface = qobject_cast(object.toQObject())) { +void soundSharedPointerFromScriptValue(const ScriptValuePointer& object, SharedSoundPointer& out) { + if (auto soundInterface = qobject_cast(object->toQObject())) { out = soundInterface->getSound(); } } diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h index 205e1cba33f..f3e08dd1af4 100644 --- a/libraries/audio/src/Sound.h +++ b/libraries/audio/src/Sound.h @@ -15,14 +15,17 @@ #include #include #include -#include +#include #include #include "AudioConstants.h" class AudioData; +class ScriptEngine; +class ScriptValue; using AudioDataPointer = std::shared_ptr; +using ScriptValuePointer = QSharedPointer; Q_DECLARE_METATYPE(AudioDataPointer); @@ -168,7 +171,7 @@ class SoundScriptingInterface : public QObject { }; Q_DECLARE_METATYPE(SharedSoundPointer) -QScriptValue soundSharedPointerToScriptValue(QScriptEngine* engine, const SharedSoundPointer& in); -void soundSharedPointerFromScriptValue(const QScriptValue& object, SharedSoundPointer& out); +ScriptValuePointer soundSharedPointerToScriptValue(ScriptEngine* engine, const SharedSoundPointer& in); +void soundSharedPointerFromScriptValue(const ScriptValuePointer& object, SharedSoundPointer& out); #endif // hifi_Sound_h diff --git a/libraries/avatars-renderer/CMakeLists.txt b/libraries/avatars-renderer/CMakeLists.txt index 0175d1113ae..7611f4dbeb5 100644 --- a/libraries/avatars-renderer/CMakeLists.txt +++ b/libraries/avatars-renderer/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME avatars-renderer) -setup_hifi_library(Network Script) +setup_hifi_library(Network) link_hifi_libraries(shared shaders gpu graphics animation material-networking model-networking script-engine render render-utils image entities-renderer physics) include_hifi_library_headers(avatars) include_hifi_library_headers(networking) diff --git a/libraries/avatars/CMakeLists.txt b/libraries/avatars/CMakeLists.txt index fc6d15cced9..a489eba6986 100644 --- a/libraries/avatars/CMakeLists.txt +++ b/libraries/avatars/CMakeLists.txt @@ -1,3 +1,4 @@ set(TARGET_NAME avatars) -setup_hifi_library(Network Script) +setup_hifi_library(Network) link_hifi_libraries(shared networking) +include_hifi_library_headers(script-engine) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index c5085e927d7..2426ab2d619 100755 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -35,11 +35,15 @@ #include #include #include +#include +#include +#include #include #include #include #include #include +#include #include "AvatarLogging.h" #include "AvatarTraits.h" @@ -2534,69 +2538,69 @@ QDataStream& operator>>(QDataStream& in, AttachmentData& attachment) { } void AttachmentDataObject::setModelURL(const QString& modelURL) { - AttachmentData data = qscriptvalue_cast(thisObject()); + AttachmentData data = scriptvalue_cast(thisObject()); data.modelURL = modelURL; thisObject() = engine()->toScriptValue(data); } QString AttachmentDataObject::getModelURL() const { - return qscriptvalue_cast(thisObject()).modelURL.toString(); + return scriptvalue_cast(thisObject()).modelURL.toString(); } void AttachmentDataObject::setJointName(const QString& jointName) { - AttachmentData data = qscriptvalue_cast(thisObject()); + AttachmentData data = scriptvalue_cast(thisObject()); data.jointName = jointName; thisObject() = engine()->toScriptValue(data); } QString AttachmentDataObject::getJointName() const { - return qscriptvalue_cast(thisObject()).jointName; + return scriptvalue_cast(thisObject()).jointName; } void AttachmentDataObject::setTranslation(const glm::vec3& translation) { - AttachmentData data = qscriptvalue_cast(thisObject()); + AttachmentData data = scriptvalue_cast(thisObject()); data.translation = translation; thisObject() = engine()->toScriptValue(data); } glm::vec3 AttachmentDataObject::getTranslation() const { - return qscriptvalue_cast(thisObject()).translation; + return scriptvalue_cast(thisObject()).translation; } void AttachmentDataObject::setRotation(const glm::quat& rotation) { - AttachmentData data = qscriptvalue_cast(thisObject()); + AttachmentData data = scriptvalue_cast(thisObject()); data.rotation = rotation; thisObject() = engine()->toScriptValue(data); } glm::quat AttachmentDataObject::getRotation() const { - return qscriptvalue_cast(thisObject()).rotation; + return scriptvalue_cast(thisObject()).rotation; } void AttachmentDataObject::setScale(float scale) { - AttachmentData data = qscriptvalue_cast(thisObject()); + AttachmentData data = scriptvalue_cast(thisObject()); data.scale = scale; thisObject() = engine()->toScriptValue(data); } float AttachmentDataObject::getScale() const { - return qscriptvalue_cast(thisObject()).scale; + return scriptvalue_cast(thisObject()).scale; } void AttachmentDataObject::setIsSoft(bool isSoft) { - AttachmentData data = qscriptvalue_cast(thisObject()); + AttachmentData data = scriptvalue_cast(thisObject()); data.isSoft = isSoft; thisObject() = engine()->toScriptValue(data); } bool AttachmentDataObject::getIsSoft() const { - return qscriptvalue_cast(thisObject()).isSoft; + return scriptvalue_cast(thisObject()).isSoft; } -void registerAvatarTypes(QScriptEngine* engine) { - qScriptRegisterSequenceMetaType >(engine); +void registerAvatarTypes(ScriptEngine* engine) { + scriptRegisterSequenceMetaType >(engine); engine->setDefaultPrototype(qMetaTypeId(), engine->newQObject( - new AttachmentDataObject(), QScriptEngine::ScriptOwnership)); + new AttachmentDataObject(), ScriptEngine::ScriptOwnership)); } void AvatarData::setRecordingBasis(std::shared_ptr recordingBasis) { @@ -3140,40 +3144,40 @@ glm::mat4 AvatarData::getControllerRightHandMatrix() const { * @property {SubmeshIntersection} extraInfo - Extra information on the mesh intersected if mesh was picked against, * {} if it wasn't. */ -QScriptValue RayToAvatarIntersectionResultToScriptValue(QScriptEngine* engine, const RayToAvatarIntersectionResult& value) { - QScriptValue obj = engine->newObject(); - obj.setProperty("intersects", value.intersects); - QScriptValue avatarIDValue = quuidToScriptValue(engine, value.avatarID); - obj.setProperty("avatarID", avatarIDValue); - obj.setProperty("distance", value.distance); - obj.setProperty("face", boxFaceToString(value.face)); - QScriptValue intersection = vec3ToScriptValue(engine, value.intersection); - - obj.setProperty("intersection", intersection); - QScriptValue surfaceNormal = vec3ToScriptValue(engine, value.surfaceNormal); - obj.setProperty("surfaceNormal", surfaceNormal); - obj.setProperty("jointIndex", value.jointIndex); - obj.setProperty("extraInfo", engine->toScriptValue(value.extraInfo)); +ScriptValuePointer RayToAvatarIntersectionResultToScriptValue(ScriptEngine* engine, const RayToAvatarIntersectionResult& value) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty("intersects", value.intersects); + ScriptValuePointer avatarIDValue = quuidToScriptValue(engine, value.avatarID); + obj->setProperty("avatarID", avatarIDValue); + obj->setProperty("distance", value.distance); + obj->setProperty("face", boxFaceToString(value.face)); + ScriptValuePointer intersection = vec3ToScriptValue(engine, value.intersection); + + obj->setProperty("intersection", intersection); + ScriptValuePointer surfaceNormal = vec3ToScriptValue(engine, value.surfaceNormal); + obj->setProperty("surfaceNormal", surfaceNormal); + obj->setProperty("jointIndex", value.jointIndex); + obj->setProperty("extraInfo", engine->toScriptValue(value.extraInfo)); return obj; } -void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, RayToAvatarIntersectionResult& value) { - value.intersects = object.property("intersects").toVariant().toBool(); - QScriptValue avatarIDValue = object.property("avatarID"); +void RayToAvatarIntersectionResultFromScriptValue(const ScriptValuePointer& object, RayToAvatarIntersectionResult& value) { + value.intersects = object->property("intersects")->toVariant().toBool(); + ScriptValuePointer avatarIDValue = object->property("avatarID"); quuidFromScriptValue(avatarIDValue, value.avatarID); - value.distance = object.property("distance").toVariant().toFloat(); - value.face = boxFaceFromString(object.property("face").toVariant().toString()); + value.distance = object->property("distance")->toVariant().toFloat(); + value.face = boxFaceFromString(object->property("face")->toVariant().toString()); - QScriptValue intersection = object.property("intersection"); - if (intersection.isValid()) { + ScriptValuePointer intersection = object->property("intersection"); + if (intersection->isValid()) { vec3FromScriptValue(intersection, value.intersection); } - QScriptValue surfaceNormal = object.property("surfaceNormal"); - if (surfaceNormal.isValid()) { + ScriptValuePointer surfaceNormal = object->property("surfaceNormal"); + if (surfaceNormal->isValid()) { vec3FromScriptValue(surfaceNormal, value.surfaceNormal); } - value.jointIndex = object.property("jointIndex").toInt32(); - value.extraInfo = object.property("extraInfo").toVariant().toMap(); + value.jointIndex = object->property("jointIndex")->toInt32(); + value.extraInfo = object->property("extraInfo")->toVariant().toMap(); } // these coefficients can be changed via JS for experimental tuning @@ -3186,8 +3190,8 @@ float AvatarData::_avatarSortCoefficientAge { 1.0f }; * An object with the UUIDs of avatar entities as keys and avatar entity properties objects as values. * @typedef {Object.} AvatarEntityMap */ -QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value) { - QScriptValue obj = engine->newObject(); +ScriptValuePointer AvatarEntityMapToScriptValue(ScriptEngine* engine, const AvatarEntityMap& value) { + ScriptValuePointer obj = engine->newObject(); for (auto entityID : value.keys()) { QByteArray entityProperties = value.value(entityID); QJsonDocument jsonEntityProperties = QJsonDocument::fromBinaryData(entityProperties); @@ -3197,22 +3201,22 @@ QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEnt QVariant variantEntityProperties = jsonEntityProperties.toVariant(); QVariantMap entityPropertiesMap = variantEntityProperties.toMap(); - QScriptValue scriptEntityProperties = variantMapToScriptValue(entityPropertiesMap, *engine); + ScriptValuePointer scriptEntityProperties = variantMapToScriptValue(entityPropertiesMap, *engine); QString key = entityID.toString(); - obj.setProperty(key, scriptEntityProperties); + obj->setProperty(key, scriptEntityProperties); } return obj; } -void AvatarEntityMapFromScriptValue(const QScriptValue& object, AvatarEntityMap& value) { - QScriptValueIterator itr(object); - while (itr.hasNext()) { - itr.next(); - QUuid EntityID = QUuid(itr.name()); +void AvatarEntityMapFromScriptValue(const ScriptValuePointer& object, AvatarEntityMap& value) { + ScriptValueIteratorPointer itr(object->newIterator()); + while (itr->hasNext()) { + itr->next(); + QUuid EntityID = QUuid(itr->name()); - QScriptValue scriptEntityProperties = itr.value(); - QVariant variantEntityProperties = scriptEntityProperties.toVariant(); + ScriptValuePointer scriptEntityProperties = itr->value(); + QVariant variantEntityProperties = scriptEntityProperties->toVariant(); QJsonDocument jsonEntityProperties = QJsonDocument::fromVariant(variantEntityProperties); QByteArray binaryEntityProperties = jsonEntityProperties.toBinaryData(); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index b25a6938d73..cfecd448f88 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -31,9 +31,8 @@ #include #include #include -#include -#include #include +#include #include #include @@ -49,12 +48,17 @@ #include #include #include +#include "Scriptable.h" #include "AABox.h" #include "AvatarTraits.h" #include "HeadData.h" #include "PathUtils.h" +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; + using AvatarSharedPointer = std::shared_ptr; using AvatarWeakPointer = std::weak_ptr; using AvatarHash = QHash; @@ -1924,7 +1928,7 @@ Q_DECLARE_METATYPE(AttachmentData) Q_DECLARE_METATYPE(QVector) /// Scriptable wrapper for attachments. -class AttachmentDataObject : public QObject, protected QScriptable { +class AttachmentDataObject : public QObject, protected Scriptable { Q_OBJECT Q_PROPERTY(QString modelURL READ getModelURL WRITE setModelURL) Q_PROPERTY(QString jointName READ getJointName WRITE setJointName) @@ -1954,7 +1958,7 @@ class AttachmentDataObject : public QObject, protected QScriptable { Q_INVOKABLE bool getIsSoft() const; }; -void registerAvatarTypes(QScriptEngine* engine); +void registerAvatarTypes(ScriptEngine* engine); class RayToAvatarIntersectionResult { public: @@ -1968,8 +1972,8 @@ class RayToAvatarIntersectionResult { QVariantMap extraInfo; }; Q_DECLARE_METATYPE(RayToAvatarIntersectionResult) -QScriptValue RayToAvatarIntersectionResultToScriptValue(QScriptEngine* engine, const RayToAvatarIntersectionResult& results); -void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, RayToAvatarIntersectionResult& results); +ScriptValuePointer RayToAvatarIntersectionResultToScriptValue(ScriptEngine* engine, const RayToAvatarIntersectionResult& results); +void RayToAvatarIntersectionResultFromScriptValue(const ScriptValuePointer& object, RayToAvatarIntersectionResult& results); // No JSDoc because it's not provided as a type to the script engine. class ParabolaToAvatarIntersectionResult { @@ -1986,8 +1990,8 @@ class ParabolaToAvatarIntersectionResult { Q_DECLARE_METATYPE(AvatarEntityMap) -QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value); -void AvatarEntityMapFromScriptValue(const QScriptValue& object, AvatarEntityMap& value); +ScriptValuePointer AvatarEntityMapToScriptValue(ScriptEngine* engine, const AvatarEntityMap& value); +void AvatarEntityMapFromScriptValue(const ScriptValuePointer& object, AvatarEntityMap& value); // faux joint indexes (-1 means invalid) const int NO_JOINT_INDEX = 65535; // -1 diff --git a/libraries/baking/CMakeLists.txt b/libraries/baking/CMakeLists.txt index 12fb1928771..a5f186aed10 100644 --- a/libraries/baking/CMakeLists.txt +++ b/libraries/baking/CMakeLists.txt @@ -5,3 +5,4 @@ link_hifi_libraries(shared shaders graphics networking procedural graphics-scrip include_hifi_library_headers(gpu) include_hifi_library_headers(hfm) include_hifi_library_headers(material-networking) +include_hifi_library_headers(script-engine) diff --git a/libraries/baking/src/MaterialBaker.cpp b/libraries/baking/src/MaterialBaker.cpp index 9a1b1b2d249..f1f99973256 100644 --- a/libraries/baking/src/MaterialBaker.cpp +++ b/libraries/baking/src/MaterialBaker.cpp @@ -20,6 +20,8 @@ #include #include +#include +#include #include @@ -28,6 +30,7 @@ std::function MaterialBaker::_getNextOvenWorkerThreadOperator; static int materialNum = 0; MaterialBaker::MaterialBaker(const QString& materialData, bool isURL, const QString& bakedOutputDir, QUrl destinationPath) : + _scriptEngine(newScriptEngine()), _materialData(materialData), _isURL(isURL), _destinationPath(destinationPath), @@ -209,13 +212,13 @@ void MaterialBaker::outputMaterial() { if (_materialResource->parsedMaterials.networkMaterials.size() == 1) { auto networkMaterial = _materialResource->parsedMaterials.networkMaterials.begin(); auto scriptableMaterial = scriptable::ScriptableMaterial(networkMaterial->second); - QVariant materialVariant = scriptable::scriptableMaterialToScriptValue(&_scriptEngine, scriptableMaterial).toVariant(); + QVariant materialVariant = scriptable::scriptableMaterialToScriptValue(_scriptEngine, scriptableMaterial)->toVariant(); json.insert("materials", QJsonDocument::fromVariant(materialVariant).object()); } else { QJsonArray materialArray; for (auto networkMaterial : _materialResource->parsedMaterials.networkMaterials) { auto scriptableMaterial = scriptable::ScriptableMaterial(networkMaterial.second); - QVariant materialVariant = scriptable::scriptableMaterialToScriptValue(&_scriptEngine, scriptableMaterial).toVariant(); + QVariant materialVariant = scriptable::scriptableMaterialToScriptValue(_scriptEngine, scriptableMaterial)->toVariant(); materialArray.append(QJsonDocument::fromVariant(materialVariant).object()); } json.insert("materials", materialArray); diff --git a/libraries/baking/src/MaterialBaker.h b/libraries/baking/src/MaterialBaker.h index 33123cfc732..44058521510 100644 --- a/libraries/baking/src/MaterialBaker.h +++ b/libraries/baking/src/MaterialBaker.h @@ -18,6 +18,7 @@ #include "baking/TextureFileNamer.h" #include +#include static const QString BAKED_MATERIAL_EXTENSION = ".baked.json"; @@ -67,7 +68,7 @@ private slots: QString _textureOutputDir; QString _bakedMaterialData; - QScriptEngine _scriptEngine; + ScriptEnginePointer _scriptEngine; static std::function _getNextOvenWorkerThreadOperator; TextureFileNamer _textureFileNamer; diff --git a/libraries/controllers/CMakeLists.txt b/libraries/controllers/CMakeLists.txt index 9c6bbf4aaeb..fe4fb058767 100644 --- a/libraries/controllers/CMakeLists.txt +++ b/libraries/controllers/CMakeLists.txt @@ -1,10 +1,11 @@ set(TARGET_NAME controllers) # set a default root dir for each of our optional externals if it was not passed -setup_hifi_library(Script Qml) +setup_hifi_library(Qml) # use setup_hifi_library macro to setup our project and link appropriate Qt modules link_hifi_libraries(shared) include_hifi_library_headers(networking) +include_hifi_library_headers(script-engine) GroupSources("src/controllers") diff --git a/libraries/controllers/src/controllers/Pose.cpp b/libraries/controllers/src/controllers/Pose.cpp index 6df4b4af818..c5968d65365 100644 --- a/libraries/controllers/src/controllers/Pose.cpp +++ b/libraries/controllers/src/controllers/Pose.cpp @@ -8,10 +8,11 @@ #include "Pose.h" -#include -#include +#include +#include #include +#include namespace controller { @@ -39,25 +40,25 @@ namespace controller { * @property {Vec3} angularVelocity - Angular velocity in rad/s. * @property {boolean} valid - true if the pose is valid, otherwise false. */ - QScriptValue Pose::toScriptValue(QScriptEngine* engine, const Pose& pose) { - QScriptValue obj = engine->newObject(); - obj.setProperty("translation", vec3ToScriptValue(engine, pose.translation)); - obj.setProperty("rotation", quatToScriptValue(engine, pose.rotation)); - obj.setProperty("velocity", vec3ToScriptValue(engine, pose.velocity)); - obj.setProperty("angularVelocity", vec3ToScriptValue(engine, pose.angularVelocity)); - obj.setProperty("valid", pose.valid); + ScriptValuePointer Pose::toScriptValue(ScriptEngine* engine, const Pose& pose) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty("translation", vec3ToScriptValue(engine, pose.translation)); + obj->setProperty("rotation", quatToScriptValue(engine, pose.rotation)); + obj->setProperty("velocity", vec3ToScriptValue(engine, pose.velocity)); + obj->setProperty("angularVelocity", vec3ToScriptValue(engine, pose.angularVelocity)); + obj->setProperty("valid", pose.valid); return obj; } - void Pose::fromScriptValue(const QScriptValue& object, Pose& pose) { - auto translation = object.property("translation"); - auto rotation = object.property("rotation"); - auto velocity = object.property("velocity"); - auto angularVelocity = object.property("angularVelocity"); - if (translation.isValid() && - rotation.isValid() && - velocity.isValid() && - angularVelocity.isValid()) { + void Pose::fromScriptValue(const ScriptValuePointer& object, Pose& pose) { + auto translation = object->property("translation"); + auto rotation = object->property("rotation"); + auto velocity = object->property("velocity"); + auto angularVelocity = object->property("angularVelocity"); + if (translation->isValid() && + rotation->isValid() && + velocity->isValid() && + angularVelocity->isValid()) { vec3FromScriptValue(translation, pose.translation); quatFromScriptValue(rotation, pose.rotation); vec3FromScriptValue(velocity, pose.velocity); diff --git a/libraries/controllers/src/controllers/Pose.h b/libraries/controllers/src/controllers/Pose.h index 186bbdd7339..0dff0293d15 100644 --- a/libraries/controllers/src/controllers/Pose.h +++ b/libraries/controllers/src/controllers/Pose.h @@ -10,9 +10,11 @@ #pragma once #ifndef hifi_controllers_Pose_h #define hifi_controllers_Pose_h +#include -class QScriptEngine; -class QScriptValue; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; #include @@ -44,8 +46,8 @@ namespace controller { Pose transform(const glm::mat4& mat) const; Pose postTransform(const glm::mat4& mat) const; - static QScriptValue toScriptValue(QScriptEngine* engine, const Pose& event); - static void fromScriptValue(const QScriptValue& object, Pose& event); + static ScriptValuePointer toScriptValue(ScriptEngine* engine, const Pose& event); + static void fromScriptValue(const ScriptValuePointer& object, Pose& event); }; } diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h index 076146a79dc..da3ac8461b1 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.h +++ b/libraries/controllers/src/controllers/ScriptingInterface.h @@ -29,7 +29,6 @@ #include #include -#include #include #include diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 6333792b3f1..101ce9e0f97 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -26,6 +26,10 @@ #include "StateController.h" #include "InputRecorder.h" #include "Logging.h" +#include "ScriptValueUtils.h" +#include +#include +#include #include "impl/conditionals/AndConditional.h" #include "impl/conditionals/NotConditional.h" @@ -388,69 +392,69 @@ int inputPairMetaTypeId = qRegisterMetaType(); int poseMetaTypeId = qRegisterMetaType("Pose"); int handMetaTypeId = qRegisterMetaType(); -QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input); -void inputFromScriptValue(const QScriptValue& object, Input& input); -QScriptValue actionToScriptValue(QScriptEngine* engine, const Action& action); -void actionFromScriptValue(const QScriptValue& object, Action& action); -QScriptValue inputPairToScriptValue(QScriptEngine* engine, const Input::NamedPair& inputPair); -void inputPairFromScriptValue(const QScriptValue& object, Input::NamedPair& inputPair); -QScriptValue handToScriptValue(QScriptEngine* engine, const controller::Hand& hand); -void handFromScriptValue(const QScriptValue& object, controller::Hand& hand); - -QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input) { - QScriptValue obj = engine->newObject(); - obj.setProperty("device", input.getDevice()); - obj.setProperty("channel", input.getChannel()); - obj.setProperty("type", (unsigned short)input.getType()); - obj.setProperty("id", input.getID()); +ScriptValuePointer inputToScriptValue(ScriptEngine* engine, const Input& input); +void inputFromScriptValue(const ScriptValuePointer& object, Input& input); +ScriptValuePointer actionToScriptValue(ScriptEngine* engine, const Action& action); +void actionFromScriptValue(const ScriptValuePointer& object, Action& action); +ScriptValuePointer inputPairToScriptValue(ScriptEngine* engine, const Input::NamedPair& inputPair); +void inputPairFromScriptValue(const ScriptValuePointer& object, Input::NamedPair& inputPair); +ScriptValuePointer handToScriptValue(ScriptEngine* engine, const controller::Hand& hand); +void handFromScriptValue(const ScriptValuePointer& object, controller::Hand& hand); + +ScriptValuePointer inputToScriptValue(ScriptEngine* engine, const Input& input) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty("device", input.getDevice()); + obj->setProperty("channel", input.getChannel()); + obj->setProperty("type", (unsigned short)input.getType()); + obj->setProperty("id", input.getID()); return obj; } -void inputFromScriptValue(const QScriptValue& object, Input& input) { - input.id = object.property("id").toInt32(); +void inputFromScriptValue(const ScriptValuePointer& object, Input& input) { + input.id = object->property("id")->toInt32(); } -QScriptValue actionToScriptValue(QScriptEngine* engine, const Action& action) { - QScriptValue obj = engine->newObject(); +ScriptValuePointer actionToScriptValue(ScriptEngine* engine, const Action& action) { + ScriptValuePointer obj = engine->newObject(); auto userInputMapper = DependencyManager::get(); - obj.setProperty("action", (int)action); - obj.setProperty("actionName", userInputMapper->getActionName(action)); + obj->setProperty("action", (int)action); + obj->setProperty("actionName", userInputMapper->getActionName(action)); return obj; } -void actionFromScriptValue(const QScriptValue& object, Action& action) { - action = Action(object.property("action").toVariant().toInt()); +void actionFromScriptValue(const ScriptValuePointer& object, Action& action) { + action = Action(object->property("action")->toVariant().toInt()); } -QScriptValue inputPairToScriptValue(QScriptEngine* engine, const Input::NamedPair& inputPair) { - QScriptValue obj = engine->newObject(); - obj.setProperty("input", inputToScriptValue(engine, inputPair.first)); - obj.setProperty("inputName", inputPair.second); +ScriptValuePointer inputPairToScriptValue(ScriptEngine* engine, const Input::NamedPair& inputPair) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty("input", inputToScriptValue(engine, inputPair.first)); + obj->setProperty("inputName", inputPair.second); return obj; } -void inputPairFromScriptValue(const QScriptValue& object, Input::NamedPair& inputPair) { - inputFromScriptValue(object.property("input"), inputPair.first); - inputPair.second = QString(object.property("inputName").toVariant().toString()); +void inputPairFromScriptValue(const ScriptValuePointer& object, Input::NamedPair& inputPair) { + inputFromScriptValue(object->property("input"), inputPair.first); + inputPair.second = QString(object->property("inputName")->toVariant().toString()); } -QScriptValue handToScriptValue(QScriptEngine* engine, const controller::Hand& hand) { - return engine->newVariant((int)hand); +ScriptValuePointer handToScriptValue(ScriptEngine* engine, const controller::Hand& hand) { + return engine->newValue((int)hand); } -void handFromScriptValue(const QScriptValue& object, controller::Hand& hand) { - hand = Hand(object.toVariant().toInt()); +void handFromScriptValue(const ScriptValuePointer& object, controller::Hand& hand) { + hand = Hand(object->toVariant().toInt()); } -void UserInputMapper::registerControllerTypes(QScriptEngine* engine) { - qScriptRegisterSequenceMetaType >(engine); - qScriptRegisterSequenceMetaType(engine); - qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue); - qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue); - qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue); - qScriptRegisterMetaType(engine, handToScriptValue, handFromScriptValue); +void UserInputMapper::registerControllerTypes(ScriptEngine* engine) { + scriptRegisterSequenceMetaType >(engine); + scriptRegisterSequenceMetaType(engine); + scriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue); + scriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue); + scriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue); + scriptRegisterMetaType(engine, handToScriptValue, handFromScriptValue); - qScriptRegisterMetaType(engine, Pose::toScriptValue, Pose::fromScriptValue); + scriptRegisterMetaType(engine, Pose::toScriptValue, Pose::fromScriptValue); } Input UserInputMapper::makeStandardInput(controller::StandardButtonChannel button) { @@ -658,21 +662,21 @@ Endpoint::Pointer UserInputMapper::endpointFor(const QJSValue& endpoint) { return Endpoint::Pointer(); } -Endpoint::Pointer UserInputMapper::endpointFor(const QScriptValue& endpoint) { - if (endpoint.isNumber()) { - return endpointFor(Input(endpoint.toInt32())); +Endpoint::Pointer UserInputMapper::endpointFor(const ScriptValuePointer& endpoint) { + if (endpoint->isNumber()) { + return endpointFor(Input(endpoint->toInt32())); } - if (endpoint.isFunction()) { + if (endpoint->isFunction()) { auto result = std::make_shared(endpoint); return result; } - if (endpoint.isArray()) { - int length = endpoint.property("length").toInteger(); + if (endpoint->isArray()) { + int length = endpoint->property("length")->toInteger(); Endpoint::List children; for (int i = 0; i < length; i++) { - QScriptValue arrayItem = endpoint.property(i); + ScriptValuePointer arrayItem = endpoint->property(i); Endpoint::Pointer destination = endpointFor(arrayItem); if (!destination) { return Endpoint::Pointer(); @@ -683,7 +687,7 @@ Endpoint::Pointer UserInputMapper::endpointFor(const QScriptValue& endpoint) { } - qWarning() << "Unsupported input type " << endpoint.toString(); + qWarning() << "Unsupported input type " << endpoint->toString(); return Endpoint::Pointer(); } @@ -883,12 +887,12 @@ Conditional::Pointer UserInputMapper::conditionalFor(const QJSValue& condition) return Conditional::Pointer(); } -Conditional::Pointer UserInputMapper::conditionalFor(const QScriptValue& condition) { - if (condition.isArray()) { - int length = condition.property("length").toInteger(); +Conditional::Pointer UserInputMapper::conditionalFor(const ScriptValuePointer& condition) { + if (condition->isArray()) { + int length = condition->property("length")->toInteger(); Conditional::List children; for (int i = 0; i < length; i++) { - Conditional::Pointer destination = conditionalFor(condition.property(i)); + Conditional::Pointer destination = conditionalFor(condition->property(i)); if (!destination) { return Conditional::Pointer(); } @@ -897,15 +901,15 @@ Conditional::Pointer UserInputMapper::conditionalFor(const QScriptValue& conditi return std::make_shared(children); } - if (condition.isNumber()) { - return conditionalFor(Input(condition.toInt32())); + if (condition->isNumber()) { + return conditionalFor(Input(condition->toInt32())); } - if (condition.isFunction()) { + if (condition->isFunction()) { return std::make_shared(condition); } - qWarning() << "Unsupported conditional type " << condition.toString(); + qWarning() << "Unsupported conditional type " << condition->toString(); return Conditional::Pointer(); } diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index ee8b34193fb..c9cfaab6af7 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -16,9 +16,9 @@ #include #include #include +#include #include -#include #include #include @@ -32,6 +32,9 @@ #include "Actions.h" #include "StateController.h" +class ScriptValue; +using ScriptValuePointer = QSharedPointer; + namespace controller { class RouteBuilderProxy; @@ -64,7 +67,7 @@ namespace controller { virtual ~UserInputMapper(); - static void registerControllerTypes(QScriptEngine* engine); + static void registerControllerTypes(ScriptEngine* engine); void registerDevice(InputDevice::Pointer device); InputDevice::Pointer getDevice(const Input& input); @@ -164,10 +167,10 @@ namespace controller { void enableMapping(const MappingPointer& mapping); void disableMapping(const MappingPointer& mapping); EndpointPointer endpointFor(const QJSValue& endpoint); - EndpointPointer endpointFor(const QScriptValue& endpoint); + EndpointPointer endpointFor(const ScriptValuePointer& endpoint); EndpointPointer compositeEndpointFor(EndpointPointer first, EndpointPointer second); ConditionalPointer conditionalFor(const QJSValue& endpoint); - ConditionalPointer conditionalFor(const QScriptValue& endpoint); + ConditionalPointer conditionalFor(const ScriptValuePointer& endpoint); ConditionalPointer conditionalFor(const Input& endpoint) const; MappingPointer parseMapping(const QJsonValue& json); diff --git a/libraries/controllers/src/controllers/impl/Endpoint.h b/libraries/controllers/src/controllers/impl/Endpoint.h index 692e427e165..00a4c264a9b 100644 --- a/libraries/controllers/src/controllers/impl/Endpoint.h +++ b/libraries/controllers/src/controllers/impl/Endpoint.h @@ -20,8 +20,6 @@ #include "../Input.h" #include "../Pose.h" -class QScriptValue; - namespace controller { /* * Encapsulates a particular input / output, diff --git a/libraries/controllers/src/controllers/impl/Filter.cpp b/libraries/controllers/src/controllers/impl/Filter.cpp index f230fb83dc9..2a175137036 100644 --- a/libraries/controllers/src/controllers/impl/Filter.cpp +++ b/libraries/controllers/src/controllers/impl/Filter.cpp @@ -9,7 +9,6 @@ #include "Filter.h" #include -#include #include #include diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp index 1af3f271bea..d82aece9e94 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp @@ -17,6 +17,7 @@ #include "RouteBuilderProxy.h" #include "../ScriptingInterface.h" #include "../Logging.h" +#include using namespace controller; @@ -26,8 +27,8 @@ QObject* MappingBuilderProxy::fromQml(const QJSValue& source) { return from(sourceEndpoint); } -QObject* MappingBuilderProxy::from(const QScriptValue& source) { - qCDebug(controllers) << "Creating new Route builder proxy from " << source.toString(); +QObject* MappingBuilderProxy::from(const ScriptValuePointer& source) { + qCDebug(controllers) << "Creating new Route builder proxy from " << source->toString(); auto sourceEndpoint = _parent.endpointFor(source); return from(sourceEndpoint); } @@ -49,7 +50,7 @@ QObject* MappingBuilderProxy::makeAxisQml(const QJSValue& source1, const QJSValu return from(_parent.compositeEndpointFor(source1Endpoint, source2Endpoint)); } -QObject* MappingBuilderProxy::makeAxis(const QScriptValue& source1, const QScriptValue& source2) { +QObject* MappingBuilderProxy::makeAxis(const ScriptValuePointer& source1, const ScriptValuePointer& source2) { auto source1Endpoint = _parent.endpointFor(source1); auto source2Endpoint = _parent.endpointFor(source2); return from(_parent.compositeEndpointFor(source1Endpoint, source2Endpoint)); diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h index 9dafc03f1f6..83d2f94877a 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h @@ -11,13 +11,15 @@ #include #include +#include #include "Mapping.h" #include "Endpoint.h" class QJSValue; -class QScriptValue; class QJsonValue; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; namespace controller { @@ -165,7 +167,7 @@ class MappingBuilderProxy : public QObject { * of the route data. If a function, it must return a number or a {@link Pose} value as the route data. * @returns {RouteObject} A route ready for mapping to an action or function using {@link RouteObject} methods. */ - Q_INVOKABLE QObject* from(const QScriptValue& source); + Q_INVOKABLE QObject* from(const ScriptValuePointer& source); /**jsdoc * Creates a new {@link RouteObject} from two numeric {@link Controller.Hardware} outputs, one applied in the negative @@ -187,7 +189,7 @@ class MappingBuilderProxy : public QObject { * Controller.disableMapping(MAPPING_NAME); * }); */ - Q_INVOKABLE QObject* makeAxis(const QScriptValue& source1, const QScriptValue& source2); + Q_INVOKABLE QObject* makeAxis(const ScriptValuePointer& source1, const ScriptValuePointer& source2); /**jsdoc * Enables or disables the mapping. When enabled, the routes in the mapping take effect. diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index 56ace233357..49308c6e412 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -17,6 +17,7 @@ #include "MappingBuilderProxy.h" #include "../ScriptingInterface.h" #include "../Logging.h" +#include #include "filters/ClampFilter.h" #include "filters/ConstrainToIntegerFilter.h" @@ -43,8 +44,8 @@ void RouteBuilderProxy::toQml(const QJSValue& destination) { return to(destinationEndpoint); } -void RouteBuilderProxy::to(const QScriptValue& destination) { - qCDebug(controllers) << "Completing route " << destination.toString(); +void RouteBuilderProxy::to(const ScriptValuePointer& destination) { + qCDebug(controllers) << "Completing route " << destination->toString(); auto destinationEndpoint = _parent.endpointFor(destination); return to(destinationEndpoint); } @@ -65,7 +66,7 @@ QObject* RouteBuilderProxy::peek(bool enable) { return this; } -QObject* RouteBuilderProxy::when(const QScriptValue& expression) { +QObject* RouteBuilderProxy::when(const ScriptValuePointer& expression) { // FIXME: Support "!" conditional in simple expression and array expression. // Note that "!" is supported when parsing a JSON file, in UserInputMapper::parseConditional(). auto newConditional = _parent.conditionalFor(expression); diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index 38b18346a81..a158334c005 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -10,6 +10,7 @@ #define hifi_Controllers_Impl_RouteBuilderProxy_h #include +#include #include "Filter.h" #include "Route.h" @@ -18,8 +19,9 @@ #include "../UserInputMapper.h" class QJSValue; -class QScriptValue; class QJsonValue; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; namespace controller { @@ -115,7 +117,7 @@ class RouteBuilderProxy : public QObject { * Controller.disableMapping(MAPPING_NAME); * }); */ - Q_INVOKABLE void to(const QScriptValue& destination); + Q_INVOKABLE void to(const ScriptValuePointer& destination); /**jsdoc * Enables or disables writing debug information for a route to the program log. @@ -193,7 +195,7 @@ class RouteBuilderProxy : public QObject { * Controller.disableMapping(MAPPING_NAME); * }); */ - Q_INVOKABLE QObject* when(const QScriptValue& expression); + Q_INVOKABLE QObject* when(const ScriptValuePointer& expression); /**jsdoc * Filters numeric route values to lie between two values; values outside this range are not passed on through the diff --git a/libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.cpp b/libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.cpp index 277b63ed112..d868ad343ef 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.cpp +++ b/libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.cpp @@ -10,6 +10,8 @@ #include +#include + using namespace controller; bool ScriptConditional::satisfied() { @@ -23,5 +25,5 @@ void ScriptConditional::updateValue() { return; } - _lastValue = _callable.call().toBool(); + _lastValue = _callable->call()->toBool(); } diff --git a/libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.h b/libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.h index 800692d02c7..0ac4cc4434d 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.h +++ b/libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.h @@ -11,22 +11,24 @@ #define hifi_Controllers_ScriptConditional_h #include - -#include +#include #include "../Conditional.h" +class ScriptValue; +using ScriptValuePointer = QSharedPointer; + namespace controller { class ScriptConditional : public QObject, public Conditional { Q_OBJECT; public: - ScriptConditional(const QScriptValue& callable) : _callable(callable) { } + ScriptConditional(const ScriptValuePointer& callable) : _callable(callable) {} virtual bool satisfied() override; protected: Q_INVOKABLE void updateValue(); private: - QScriptValue _callable; + ScriptValuePointer _callable; bool _lastValue { false }; }; diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp index 9f971d2f04c..90184afd230 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp @@ -11,18 +11,20 @@ #include +#include +#include #include using namespace controller; -QString formatException(const QScriptValue& exception) { +QString formatException(const ScriptValuePointer& exception) { QString note { "UncaughtException" }; QString result; - const auto message = exception.toString(); - const auto fileName = exception.property("fileName").toString(); - const auto lineNumber = exception.property("lineNumber").toString(); - const auto stacktrace = exception.property("stack").toString(); + const auto message = exception->toString(); + const auto fileName = exception->property("fileName")->toString(); + const auto lineNumber = exception->property("lineNumber")->toString(); + const auto stacktrace = exception->property("stack")->toString(); const QString SCRIPT_EXCEPTION_FORMAT = "[%0] %1 in %2:%3"; const QString SCRIPT_BACKTRACE_SEP = "\n "; @@ -45,13 +47,13 @@ void ScriptEndpoint::updateValue() { return; } - QScriptValue result = _callable.call(); - if (result.isError()) { + ScriptValuePointer result = _callable->call(); + if (result->isError()) { // print JavaScript exception qCDebug(controllers).noquote() << formatException(result); _lastValueRead = 0.0f; - } else if (result.isNumber()) { - _lastValueRead = (float)_callable.call().toNumber(); + } else if (result->isNumber()) { + _lastValueRead = (float)_callable->call()->toNumber(); } else { Pose::fromScriptValue(result, _lastPoseRead); _returnPose = true; @@ -73,9 +75,10 @@ void ScriptEndpoint::internalApply(float value, int sourceID) { Q_ARG(int, sourceID)); return; } - QScriptValue result = _callable.call(QScriptValue(), - QScriptValueList({ QScriptValue(value), QScriptValue(sourceID) })); - if (result.isError()) { + ScriptEnginePointer engine = _callable->engine(); + ScriptValuePointer result = _callable->call(ScriptValuePointer(), + ScriptValueList({ engine->newValue(value), engine->newValue(sourceID) })); + if (result->isError()) { // print JavaScript exception qCDebug(controllers).noquote() << formatException(result); } @@ -91,8 +94,8 @@ void ScriptEndpoint::updatePose() { QMetaObject::invokeMethod(this, "updatePose", Qt::QueuedConnection); return; } - QScriptValue result = _callable.call(); - if (result.isError()) { + ScriptValuePointer result = _callable->call(); + if (result->isError()) { // print JavaScript exception qCDebug(controllers).noquote() << formatException(result); } @@ -114,9 +117,10 @@ void ScriptEndpoint::internalApply(const Pose& newPose, int sourceID) { Q_ARG(int, sourceID)); return; } - QScriptValue result = _callable.call(QScriptValue(), - QScriptValueList({ Pose::toScriptValue(_callable.engine(), newPose), QScriptValue(sourceID) })); - if (result.isError()) { + ScriptEnginePointer engine = _callable->engine(); + ScriptValuePointer result = _callable->call(ScriptValuePointer(), + ScriptValueList({ Pose::toScriptValue(engine.get(), newPose), engine->newValue(sourceID) })); + if (result->isError()) { // print JavaScript exception qCDebug(controllers).noquote() << formatException(result); } diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h index 1aa1746b249..ff42812c4b2 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h @@ -10,17 +10,20 @@ #ifndef hifi_Controllers_ScriptEndpoint_h #define hifi_Controllers_ScriptEndpoint_h -#include +#include #include "../Endpoint.h" +class ScriptValue; +using ScriptValuePointer = QSharedPointer; + namespace controller { class ScriptEndpoint : public Endpoint { Q_OBJECT; public: using Endpoint::apply; - ScriptEndpoint(const QScriptValue& callable) + ScriptEndpoint(const ScriptValuePointer& callable) : Endpoint(Input::INVALID_INPUT), _callable(callable) { } @@ -39,7 +42,7 @@ class ScriptEndpoint : public Endpoint { Q_INVOKABLE void updatePose(); Q_INVOKABLE virtual void internalApply(const Pose& newValue, int sourceID); private: - QScriptValue _callable; + ScriptValuePointer _callable; float _lastValueRead { 0.0f }; AxisValue _lastValueWritten { 0.0f, 0, false }; diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index 5f40c8d5fe8..4ac69312eba 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME entities-renderer) -setup_hifi_library(Network Script) +setup_hifi_library(Network) link_hifi_libraries(shared workload gpu shaders procedural graphics material-networking model-networking script-engine render render-utils image qml ui pointers) include_hifi_library_headers(networking) include_hifi_library_headers(gl) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 1504afdebee..4662fa2ad06 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -15,7 +15,6 @@ #include #include -#include #include #include @@ -30,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -158,45 +158,45 @@ render::ItemID EntityTreeRenderer::renderableIdForEntityId(const EntityItemID& i int EntityTreeRenderer::_entitiesScriptEngineCount = 0; -void EntityTreeRenderer::setupEntityScriptEngineSignals(const ScriptEnginePointer& scriptEngine) { +void EntityTreeRenderer::setupEntityScriptEngineSignals(const ScriptManagerPointer& scriptManager) { auto entityScriptingInterface = DependencyManager::get(); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity, scriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { - scriptEngine->callEntityScriptMethod(entityID, "mousePressOnEntity", event); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity, scriptManager.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { + scriptManager->callEntityScriptMethod(entityID, "mousePressOnEntity", event); }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseDoublePressOnEntity, scriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { - scriptEngine->callEntityScriptMethod(entityID, "mouseDoublePressOnEntity", event); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseDoublePressOnEntity, scriptManager.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { + scriptManager->callEntityScriptMethod(entityID, "mouseDoublePressOnEntity", event); }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity, scriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { - scriptEngine->callEntityScriptMethod(entityID, "mouseMoveOnEntity", event); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity, scriptManager.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { + scriptManager->callEntityScriptMethod(entityID, "mouseMoveOnEntity", event); // FIXME: this is a duplicate of mouseMoveOnEntity, but it seems like some scripts might use this naming - scriptEngine->callEntityScriptMethod(entityID, "mouseMoveEvent", event); + scriptManager->callEntityScriptMethod(entityID, "mouseMoveEvent", event); }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity, scriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { - scriptEngine->callEntityScriptMethod(entityID, "mouseReleaseOnEntity", event); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity, scriptManager.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { + scriptManager->callEntityScriptMethod(entityID, "mouseReleaseOnEntity", event); }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity, scriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { - scriptEngine->callEntityScriptMethod(entityID, "clickDownOnEntity", event); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity, scriptManager.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { + scriptManager->callEntityScriptMethod(entityID, "clickDownOnEntity", event); }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::holdingClickOnEntity, scriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { - scriptEngine->callEntityScriptMethod(entityID, "holdingClickOnEntity", event); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::holdingClickOnEntity, scriptManager.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { + scriptManager->callEntityScriptMethod(entityID, "holdingClickOnEntity", event); }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickReleaseOnEntity, scriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { - scriptEngine->callEntityScriptMethod(entityID, "clickReleaseOnEntity", event); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickReleaseOnEntity, scriptManager.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { + scriptManager->callEntityScriptMethod(entityID, "clickReleaseOnEntity", event); }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, scriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { - scriptEngine->callEntityScriptMethod(entityID, "hoverEnterEntity", event); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, scriptManager.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { + scriptManager->callEntityScriptMethod(entityID, "hoverEnterEntity", event); }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity, scriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { - scriptEngine->callEntityScriptMethod(entityID, "hoverOverEntity", event); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity, scriptManager.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { + scriptManager->callEntityScriptMethod(entityID, "hoverOverEntity", event); }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, scriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { - scriptEngine->callEntityScriptMethod(entityID, "hoverLeaveEntity", event); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, scriptManager.data(), [&](const EntityItemID& entityID, const PointerEvent& event) { + scriptManager->callEntityScriptMethod(entityID, "hoverLeaveEntity", event); }); - connect(scriptEngine.data(), &ScriptEngine::entityScriptPreloadFinished, [&](const EntityItemID& entityID) { + connect(scriptManager.data(), &ScriptManager::entityScriptPreloadFinished, [&](const EntityItemID& entityID) { EntityItemPointer entity = getTree()->findEntityByID(entityID); if (entity) { entity->setScriptHasFinishedPreload(true); @@ -205,51 +205,51 @@ void EntityTreeRenderer::setupEntityScriptEngineSignals(const ScriptEnginePointe } void EntityTreeRenderer::resetPersistentEntitiesScriptEngine() { - if (_persistentEntitiesScriptEngine) { - _persistentEntitiesScriptEngine->unloadAllEntityScripts(true); - _persistentEntitiesScriptEngine->stop(); - _persistentEntitiesScriptEngine->waitTillDoneRunning(); - _persistentEntitiesScriptEngine->disconnectNonEssentialSignals(); + if (_persistentEntitiesScriptManager) { + _persistentEntitiesScriptManager->unloadAllEntityScripts(true); + _persistentEntitiesScriptManager->stop(); + _persistentEntitiesScriptManager->waitTillDoneRunning(); + _persistentEntitiesScriptManager->disconnectNonEssentialSignals(); } - _persistentEntitiesScriptEngine = scriptEngineFactory(ScriptEngine::ENTITY_CLIENT_SCRIPT, NO_SCRIPT, + _persistentEntitiesScriptManager = scriptManagerFactory(ScriptManager::ENTITY_CLIENT_SCRIPT, NO_SCRIPT, QString("about:Entities %1").arg(++_entitiesScriptEngineCount)); - DependencyManager::get()->runScriptInitializers(_persistentEntitiesScriptEngine); - _persistentEntitiesScriptEngine->runInThread(); - auto entitiesScriptEngineProvider = qSharedPointerCast(_persistentEntitiesScriptEngine); + DependencyManager::get()->runScriptInitializers(_persistentEntitiesScriptManager); + _persistentEntitiesScriptManager->runInThread(); + auto entitiesScriptEngineProvider = qSharedPointerCast(_persistentEntitiesScriptManager); auto entityScriptingInterface = DependencyManager::get(); entityScriptingInterface->setPersistentEntitiesScriptEngine(entitiesScriptEngineProvider); - setupEntityScriptEngineSignals(_persistentEntitiesScriptEngine); + setupEntityScriptEngineSignals(_persistentEntitiesScriptManager); } void EntityTreeRenderer::resetNonPersistentEntitiesScriptEngine() { - if (_nonPersistentEntitiesScriptEngine) { - _nonPersistentEntitiesScriptEngine->unloadAllEntityScripts(true); - _nonPersistentEntitiesScriptEngine->stop(); - _nonPersistentEntitiesScriptEngine->waitTillDoneRunning(); - _nonPersistentEntitiesScriptEngine->disconnectNonEssentialSignals(); + if (_nonPersistentEntitiesScriptManager) { + _nonPersistentEntitiesScriptManager->unloadAllEntityScripts(true); + _nonPersistentEntitiesScriptManager->stop(); + _nonPersistentEntitiesScriptManager->waitTillDoneRunning(); + _nonPersistentEntitiesScriptManager->disconnectNonEssentialSignals(); } - _nonPersistentEntitiesScriptEngine = scriptEngineFactory(ScriptEngine::ENTITY_CLIENT_SCRIPT, NO_SCRIPT, + _nonPersistentEntitiesScriptManager = scriptManagerFactory(ScriptManager::ENTITY_CLIENT_SCRIPT, NO_SCRIPT, QString("about:Entities %1").arg(++_entitiesScriptEngineCount)); - DependencyManager::get()->runScriptInitializers(_nonPersistentEntitiesScriptEngine); - _nonPersistentEntitiesScriptEngine->runInThread(); - auto entitiesScriptEngineProvider = qSharedPointerCast(_nonPersistentEntitiesScriptEngine); + DependencyManager::get()->runScriptInitializers(_nonPersistentEntitiesScriptManager); + _nonPersistentEntitiesScriptManager->runInThread(); + auto entitiesScriptEngineProvider = qSharedPointerCast(_nonPersistentEntitiesScriptManager); DependencyManager::get()->setNonPersistentEntitiesScriptEngine(entitiesScriptEngineProvider); - setupEntityScriptEngineSignals(_nonPersistentEntitiesScriptEngine); + setupEntityScriptEngineSignals(_nonPersistentEntitiesScriptManager); } void EntityTreeRenderer::stopDomainAndNonOwnedEntities() { leaveDomainAndNonOwnedEntities(); // unload and stop the engine - if (_nonPersistentEntitiesScriptEngine) { - QList entitiesWithEntityScripts = _nonPersistentEntitiesScriptEngine->getListOfEntityScriptIDs(); + if (_nonPersistentEntitiesScriptManager) { + QList entitiesWithEntityScripts = _nonPersistentEntitiesScriptManager->getListOfEntityScriptIDs(); foreach (const EntityItemID& entityID, entitiesWithEntityScripts) { EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID); if (entityItem && !entityItem->getScript().isEmpty()) { if (!(entityItem->isLocalEntity() || entityItem->isMyAvatarEntity())) { - _nonPersistentEntitiesScriptEngine->unloadEntityScript(entityID, true); + _nonPersistentEntitiesScriptManager->unloadEntityScript(entityID, true); } } } @@ -297,15 +297,15 @@ void EntityTreeRenderer::clear() { auto scene = _viewState->getMain3DScene(); if (_shuttingDown) { // unload and stop the engines - if (_nonPersistentEntitiesScriptEngine) { + if (_nonPersistentEntitiesScriptManager) { // do this here (instead of in deleter) to avoid marshalling unload signals back to this thread - _nonPersistentEntitiesScriptEngine->unloadAllEntityScripts(true); - _nonPersistentEntitiesScriptEngine->stop(); + _nonPersistentEntitiesScriptManager->unloadAllEntityScripts(true); + _nonPersistentEntitiesScriptManager->stop(); } - if (_persistentEntitiesScriptEngine) { + if (_persistentEntitiesScriptManager) { // do this here (instead of in deleter) to avoid marshalling unload signals back to this thread - _persistentEntitiesScriptEngine->unloadAllEntityScripts(true); - _persistentEntitiesScriptEngine->stop(); + _persistentEntitiesScriptManager->unloadAllEntityScripts(true); + _persistentEntitiesScriptManager->stop(); } if (scene) { @@ -343,16 +343,16 @@ void EntityTreeRenderer::clear() { } void EntityTreeRenderer::reloadEntityScripts() { - _persistentEntitiesScriptEngine->unloadAllEntityScripts(); - _persistentEntitiesScriptEngine->resetModuleCache(); - _nonPersistentEntitiesScriptEngine->unloadAllEntityScripts(); - _nonPersistentEntitiesScriptEngine->resetModuleCache(); + _persistentEntitiesScriptManager->unloadAllEntityScripts(); + _persistentEntitiesScriptManager->resetModuleCache(); + _nonPersistentEntitiesScriptManager->unloadAllEntityScripts(); + _nonPersistentEntitiesScriptManager->resetModuleCache(); for (const auto& entry : _entitiesInScene) { const auto& renderer = entry.second; const auto& entity = renderer->getEntity(); if (entity && !entity->getScript().isEmpty()) { - auto& scriptEngine = (entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine; + auto& scriptEngine = (entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptManager : _nonPersistentEntitiesScriptManager; scriptEngine->loadEntityScript(entity->getEntityItemID(), resolveScriptURL(entity->getScript()), true); } } @@ -376,11 +376,11 @@ void EntityTreeRenderer::init() { } void EntityTreeRenderer::shutdown() { - if (_persistentEntitiesScriptEngine) { - _persistentEntitiesScriptEngine->disconnectNonEssentialSignals(); // disconnect all slots/signals from the script engine, except essential + if (_persistentEntitiesScriptManager) { + _persistentEntitiesScriptManager->disconnectNonEssentialSignals(); // disconnect all slots/signals from the script engine, except essential } - if (_nonPersistentEntitiesScriptEngine) { - _nonPersistentEntitiesScriptEngine->disconnectNonEssentialSignals(); // disconnect all slots/signals from the script engine, except essential + if (_nonPersistentEntitiesScriptManager) { + _nonPersistentEntitiesScriptManager->disconnectNonEssentialSignals(); // disconnect all slots/signals from the script engine, except essential } _shuttingDown = true; @@ -696,14 +696,14 @@ void EntityTreeRenderer::checkEnterLeaveEntities() { // EntityItemIDs from here. The callEntityScriptMethod() method is robust against attempting to call scripts // for entity IDs that no longer exist. - if (_persistentEntitiesScriptEngine && _nonPersistentEntitiesScriptEngine) { + if (_persistentEntitiesScriptManager && _nonPersistentEntitiesScriptManager) { // for all of our previous containing entities, if they are no longer containing then send them a leave event foreach(const EntityItemID& entityID, _currentEntitiesInside) { if (!entitiesContainingAvatar.contains(entityID)) { emit leaveEntity(entityID); auto entity = getTree()->findEntityByEntityItemID(entityID); if (entity) { - auto& scriptEngine = (entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine; + auto& scriptEngine = (entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptManager : _nonPersistentEntitiesScriptManager; scriptEngine->callEntityScriptMethod(entityID, "leaveEntity"); } } @@ -715,7 +715,7 @@ void EntityTreeRenderer::checkEnterLeaveEntities() { emit enterEntity(entityID); auto entity = getTree()->findEntityByEntityItemID(entityID); if (entity) { - auto& scriptEngine = (entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine; + auto& scriptEngine = (entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptManager : _nonPersistentEntitiesScriptManager; scriptEngine->callEntityScriptMethod(entityID, "enterEntity"); } } @@ -733,8 +733,8 @@ void EntityTreeRenderer::leaveDomainAndNonOwnedEntities() { EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID); if (entityItem && !(entityItem->isLocalEntity() || entityItem->isMyAvatarEntity())) { emit leaveEntity(entityID); - if (_nonPersistentEntitiesScriptEngine) { - _nonPersistentEntitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity"); + if (_nonPersistentEntitiesScriptManager) { + _nonPersistentEntitiesScriptManager->callEntityScriptMethod(entityID, "leaveEntity"); } } else { currentEntitiesInsideToSave.insert(entityID); @@ -754,7 +754,7 @@ void EntityTreeRenderer::leaveAllEntities() { emit leaveEntity(entityID); EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID); if (entityItem) { - auto& scriptEngine = (entityItem->isLocalEntity() || entityItem->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine; + auto& scriptEngine = (entityItem->isLocalEntity() || entityItem->isMyAvatarEntity()) ? _persistentEntitiesScriptManager : _nonPersistentEntitiesScriptManager; if (scriptEngine) { scriptEngine->callEntityScriptMethod(entityID, "leaveEntity"); } @@ -1053,7 +1053,7 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) { return; } - auto& scriptEngine = (itr->second->getEntity()->isLocalEntity() || itr->second->getEntity()->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine; + auto& scriptEngine = (itr->second->getEntity()->isLocalEntity() || itr->second->getEntity()->isMyAvatarEntity()) ? _persistentEntitiesScriptManager : _nonPersistentEntitiesScriptManager; if (_tree && !_shuttingDown && scriptEngine && !itr->second->getEntity()->getScript().isEmpty()) { if (_currentEntitiesInside.contains(entityID)) { scriptEngine->callEntityScriptMethod(entityID, "leaveEntity"); @@ -1103,7 +1103,7 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, bool if (!entity) { return; } - auto& scriptEngine = (entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine; + auto& scriptEngine = (entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptManager : _nonPersistentEntitiesScriptManager; bool shouldLoad = entity->shouldPreloadScript() && scriptEngine; QString scriptUrl = entity->getScript(); if ((shouldLoad && unloadFirst) || scriptUrl.isEmpty()) { @@ -1224,7 +1224,7 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons if ((myNodeID == entityASimulatorID && entityAIsDynamic) || (myNodeID == entityBSimulatorID && (!entityAIsDynamic || entityASimulatorID.isNull()))) { playEntityCollisionSound(entityA, collision); emit collisionWithEntity(idA, idB, collision); - auto& scriptEngine = (entityA->isLocalEntity() || entityA->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine; + auto& scriptEngine = (entityA->isLocalEntity() || entityA->isMyAvatarEntity()) ? _persistentEntitiesScriptManager : _nonPersistentEntitiesScriptManager; if (scriptEngine) { scriptEngine->callEntityScriptMethod(idA, "collisionWithEntity", idB, collision); } @@ -1236,7 +1236,7 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons Collision invertedCollision(collision); invertedCollision.invert(); emit collisionWithEntity(idB, idA, invertedCollision); - auto& scriptEngine = (entityB->isLocalEntity() || entityB->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine; + auto& scriptEngine = (entityB->isLocalEntity() || entityB->isMyAvatarEntity()) ? _persistentEntitiesScriptManager : _nonPersistentEntitiesScriptManager; if (scriptEngine) { scriptEngine->callEntityScriptMethod(idB, "collisionWithEntity", idA, invertedCollision); } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 009e5f6c4f4..f4fdc222e8a 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include // for RayToEntityIntersectionResult @@ -32,6 +33,10 @@ class Model; class ScriptEngine; class ZoneEntityItem; class EntityItem; +class ScriptEngine; +class ScriptManager; +using ScriptEnginePointer = QSharedPointer; +using ScriptManagerPointer = QSharedPointer; namespace render { namespace entities { class EntityRenderer; @@ -175,7 +180,7 @@ public slots: void resetPersistentEntitiesScriptEngine(); void resetNonPersistentEntitiesScriptEngine(); - void setupEntityScriptEngineSignals(const ScriptEnginePointer& scriptEngine); + void setupEntityScriptEngineSignals(const ScriptManagerPointer& scriptManager); void findBestZoneAndMaybeContainingEntities(QSet& entitiesContainingAvatar); @@ -187,7 +192,7 @@ public slots: EntityItemID _currentHoverOverEntityID; EntityItemID _currentClickingOnEntityID; - QScriptValueList createEntityArgs(const EntityItemID& entityID); + ScriptValueList createEntityArgs(const EntityItemID& entityID); void checkEnterLeaveEntities(); void leaveDomainAndNonOwnedEntities(); void leaveAllEntities(); @@ -198,8 +203,8 @@ public slots: QSet _currentEntitiesInside; bool _wantScripts; - ScriptEnginePointer _nonPersistentEntitiesScriptEngine; // used for domain + non-owned avatar entities, cleared on domain switch - ScriptEnginePointer _persistentEntitiesScriptEngine; // used for local + owned avatar entities, persists on domain switch, cleared on reload content + ScriptManagerPointer _nonPersistentEntitiesScriptManager; // used for domain + non-owned avatar entities, cleared on domain switch + ScriptManagerPointer _persistentEntitiesScriptManager; // used for local + owned avatar entities, persists on domain switch, cleared on reload content void playEntityCollisionSound(const EntityItemPointer& entity, const Collision& collision); diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index bddd4b5e67d..f2c4cc83a72 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -1,6 +1,7 @@ set(TARGET_NAME entities) -setup_hifi_library(Network Script) +setup_hifi_library(Network) target_include_directories(${TARGET_NAME} PRIVATE "${OPENSSL_INCLUDE_DIR}") +include_hifi_library_headers(animation) include_hifi_library_headers(hfm) include_hifi_library_headers(model-serializers) include_hifi_library_headers(gpu) @@ -8,4 +9,5 @@ include_hifi_library_headers(image) include_hifi_library_headers(ktx) include_hifi_library_headers(material-networking) include_hifi_library_headers(procedural) +include_hifi_library_headers(script-engine) link_hifi_libraries(shared shaders networking octree avatars graphics model-networking) diff --git a/libraries/entities/src/AmbientLightPropertyGroup.cpp b/libraries/entities/src/AmbientLightPropertyGroup.cpp index 38017a684b1..19585943733 100644 --- a/libraries/entities/src/AmbientLightPropertyGroup.cpp +++ b/libraries/entities/src/AmbientLightPropertyGroup.cpp @@ -19,14 +19,14 @@ const float AmbientLightPropertyGroup::DEFAULT_AMBIENT_LIGHT_INTENSITY = 0.5f; -void AmbientLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, - QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { +void AmbientLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValuePointer& properties, + ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_LIGHT_INTENSITY, AmbientLight, ambientLight, AmbientIntensity, ambientIntensity); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_LIGHT_URL, AmbientLight, ambientLight, AmbientURL, ambientURL); } -void AmbientLightPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { +void AmbientLightPropertyGroup::copyFromScriptValue(const ScriptValuePointer& object, bool& _defaultSettings) { COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientLight, ambientIntensity, float, setAmbientIntensity); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientLight, ambientURL, QString, setAmbientURL); diff --git a/libraries/entities/src/AmbientLightPropertyGroup.h b/libraries/entities/src/AmbientLightPropertyGroup.h index d1e0ea0e551..7a9296450d5 100644 --- a/libraries/entities/src/AmbientLightPropertyGroup.h +++ b/libraries/entities/src/AmbientLightPropertyGroup.h @@ -17,7 +17,7 @@ #include -#include +#include #include "EntityItemPropertiesMacros.h" #include "PropertyGroup.h" @@ -26,6 +26,9 @@ class EncodeBitstreamParams; class OctreePacketData; class EntityTreeElementExtraEncodeData; class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; /**jsdoc * Ambient light is defined by the following properties: @@ -38,10 +41,10 @@ class ReadBitstreamToTreeParams; class AmbientLightPropertyGroup : public PropertyGroup { public: // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, - QScriptEngine* engine, bool skipDefaults, + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValuePointer& properties, + ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const override; - virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + virtual void copyFromScriptValue(const ScriptValuePointer& object, bool& _defaultSettings) override; void merge(const AmbientLightPropertyGroup& other); diff --git a/libraries/entities/src/AnimationPropertyGroup.cpp b/libraries/entities/src/AnimationPropertyGroup.cpp index 38230b4f50d..32c5478413a 100644 --- a/libraries/entities/src/AnimationPropertyGroup.cpp +++ b/libraries/entities/src/AnimationPropertyGroup.cpp @@ -65,7 +65,7 @@ bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b * @property {boolean} hold=false - true if the rotations and translations of the last frame played are * maintained when the animation stops playing, false if they aren't. */ -void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { +void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValuePointer& properties, ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_URL, Animation, animation, URL, url); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FPS, Animation, animation, FPS, fps); @@ -78,7 +78,7 @@ void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desire } -void AnimationPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { +void AnimationPropertyGroup::copyFromScriptValue(const ScriptValuePointer& object, bool& _defaultSettings) { COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, url, QString, setURL); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, allowTranslation, bool, setAllowTranslation); diff --git a/libraries/entities/src/AnimationPropertyGroup.h b/libraries/entities/src/AnimationPropertyGroup.h index bebfe2c1946..baa27b96136 100644 --- a/libraries/entities/src/AnimationPropertyGroup.h +++ b/libraries/entities/src/AnimationPropertyGroup.h @@ -17,8 +17,7 @@ #include -#include - +#include #include "EntityItemPropertiesMacros.h" #include "PropertyGroup.h" @@ -27,16 +26,19 @@ class EncodeBitstreamParams; class OctreePacketData; class EntityTreeElementExtraEncodeData; class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; class AnimationPropertyGroup : public PropertyGroup { public: static const float MAXIMUM_POSSIBLE_FRAME; // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, - QScriptEngine* engine, bool skipDefaults, + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValuePointer& properties, + ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const override; - virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + virtual void copyFromScriptValue(const ScriptValuePointer& object, bool& _defaultSettings) override; void merge(const AnimationPropertyGroup& other); diff --git a/libraries/entities/src/BloomPropertyGroup.cpp b/libraries/entities/src/BloomPropertyGroup.cpp index 2c4d46ab35f..672b3e9623a 100644 --- a/libraries/entities/src/BloomPropertyGroup.cpp +++ b/libraries/entities/src/BloomPropertyGroup.cpp @@ -16,13 +16,13 @@ #include "EntityItemProperties.h" #include "EntityItemPropertiesMacros.h" -void BloomPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { +void BloomPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValuePointer& properties, ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_BLOOM_INTENSITY, Bloom, bloom, BloomIntensity, bloomIntensity); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_BLOOM_THRESHOLD, Bloom, bloom, BloomThreshold, bloomThreshold); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_BLOOM_SIZE, Bloom, bloom, BloomSize, bloomSize); } -void BloomPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { +void BloomPropertyGroup::copyFromScriptValue(const ScriptValuePointer& object, bool& _defaultSettings) { COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(bloom, bloomIntensity, float, setBloomIntensity); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(bloom, bloomThreshold, float, setBloomThreshold); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(bloom, bloomSize, float, setBloomSize); diff --git a/libraries/entities/src/BloomPropertyGroup.h b/libraries/entities/src/BloomPropertyGroup.h index 12743c9298d..82ab986275c 100644 --- a/libraries/entities/src/BloomPropertyGroup.h +++ b/libraries/entities/src/BloomPropertyGroup.h @@ -15,7 +15,7 @@ #include #include -#include +#include #include "PropertyGroup.h" #include "EntityItemPropertiesMacros.h" @@ -25,6 +25,9 @@ class EncodeBitstreamParams; class OctreePacketData; class EntityTreeElementExtraEncodeData; class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; static const float INITIAL_BLOOM_INTENSITY { 0.25f }; static const float INITIAL_BLOOM_THRESHOLD { 0.7f }; @@ -40,10 +43,10 @@ static const float INITIAL_BLOOM_SIZE { 0.9f }; class BloomPropertyGroup : public PropertyGroup { public: // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, - QScriptEngine* engine, bool skipDefaults, + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValuePointer& properties, + ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const override; - virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + virtual void copyFromScriptValue(const ScriptValuePointer& object, bool& _defaultSettings) override; void merge(const BloomPropertyGroup& other); diff --git a/libraries/entities/src/EntityEditFilters.cpp b/libraries/entities/src/EntityEditFilters.cpp index 16dace0fc8f..eec85e1ab6e 100644 --- a/libraries/entities/src/EntityEditFilters.cpp +++ b/libraries/entities/src/EntityEditFilters.cpp @@ -15,6 +15,8 @@ #include #include +#include +#include QList EntityEditFilters::getZonesByPosition(glm::vec3& position) { QList zones; @@ -76,19 +78,19 @@ bool EntityEditFilters::filter(glm::vec3& position, EntityItemProperties& proper auto oldProperties = propertiesIn.getDesiredProperties(); auto specifiedProperties = propertiesIn.getChangedProperties(); propertiesIn.setDesiredProperties(specifiedProperties); - QScriptValue inputValues = propertiesIn.copyToScriptValue(filterData.engine, false, true, true); + ScriptValuePointer inputValues = propertiesIn.copyToScriptValue(filterData.engine, false, true, true); propertiesIn.setDesiredProperties(oldProperties); - auto in = QJsonValue::fromVariant(inputValues.toVariant()); // grab json copy now, because the inputValues might be side effected by the filter. + auto in = QJsonValue::fromVariant(inputValues->toVariant()); // grab json copy now, because the inputValues might be side effected by the filter. - QScriptValueList args; + ScriptValueList args; args << inputValues; args << filterType; // get the current properties for then entity and include them for the filter call if (existingEntity && filterData.wantsOriginalProperties) { auto currentProperties = existingEntity->getProperties(filterData.includedOriginalProperties); - QScriptValue currentValues = currentProperties.copyToScriptValue(filterData.engine, false, true, true); + ScriptValuePointer currentValues = currentProperties.copyToScriptValue(filterData.engine, false, true, true); args << currentValues; } @@ -98,22 +100,22 @@ bool EntityEditFilters::filter(glm::vec3& position, EntityItemProperties& proper auto zoneEntity = _tree->findEntityByEntityItemID(id); if (zoneEntity) { auto zoneProperties = zoneEntity->getProperties(filterData.includedZoneProperties); - QScriptValue zoneValues = zoneProperties.copyToScriptValue(filterData.engine, false, true, true); + ScriptValuePointer zoneValues = zoneProperties.copyToScriptValue(filterData.engine, false, true, true); if (filterData.wantsZoneBoundingBox) { bool success = true; AABox aaBox = zoneEntity->getAABox(success); if (success) { - QScriptValue boundingBox = filterData.engine->newObject(); - QScriptValue bottomRightNear = vec3ToScriptValue(filterData.engine, aaBox.getCorner()); - QScriptValue topFarLeft = vec3ToScriptValue(filterData.engine, aaBox.calcTopFarLeft()); - QScriptValue center = vec3ToScriptValue(filterData.engine, aaBox.calcCenter()); - QScriptValue boundingBoxDimensions = vec3ToScriptValue(filterData.engine, aaBox.getDimensions()); - boundingBox.setProperty("brn", bottomRightNear); - boundingBox.setProperty("tfl", topFarLeft); - boundingBox.setProperty("center", center); - boundingBox.setProperty("dimensions", boundingBoxDimensions); - zoneValues.setProperty("boundingBox", boundingBox); + ScriptValuePointer boundingBox = filterData.engine->newObject(); + ScriptValuePointer bottomRightNear = vec3ToScriptValue(filterData.engine, aaBox.getCorner()); + ScriptValuePointer topFarLeft = vec3ToScriptValue(filterData.engine, aaBox.calcTopFarLeft()); + ScriptValuePointer center = vec3ToScriptValue(filterData.engine, aaBox.calcCenter()); + ScriptValuePointer boundingBoxDimensions = vec3ToScriptValue(filterData.engine, aaBox.getDimensions()); + boundingBox->setProperty("brn", bottomRightNear); + boundingBox->setProperty("tfl", topFarLeft); + boundingBox->setProperty("center", center); + boundingBox->setProperty("dimensions", boundingBoxDimensions); + zoneValues->setProperty("boundingBox", boundingBox); } } @@ -122,32 +124,32 @@ bool EntityEditFilters::filter(glm::vec3& position, EntityItemProperties& proper // to be the fourth parameter, so we need to pad the args accordingly int EXPECTED_ARGS = 3; if (args.length() < EXPECTED_ARGS) { - args << QScriptValue(); + args << ScriptValuePointer(); } assert(args.length() == EXPECTED_ARGS); // we MUST have 3 args by now! args << zoneValues; } } - QScriptValue result = filterData.filterFn.call(_nullObjectForFilter, args); + ScriptValuePointer result = filterData.filterFn->call(_nullObjectForFilter, args); if (filterData.uncaughtExceptions()) { return false; } - if (result.isObject()) { + if (result->isObject()) { // make propertiesIn reflect the changes, for next filter... propertiesIn.copyFromScriptValue(result, false); // and update propertiesOut too. TODO: this could be more efficient... propertiesOut.copyFromScriptValue(result, false); // Javascript objects are == only if they are the same object. To compare arbitrary values, we need to use JSON. - auto out = QJsonValue::fromVariant(result.toVariant()); + auto out = QJsonValue::fromVariant(result->toVariant()); wasChanged |= (in != out); - } else if (result.isBool()) { + } else if (result->isBool()) { // if the filter returned false, then it's authoritative - if (!result.toBool()) { + if (!result->toBool()) { return false; } @@ -216,20 +218,20 @@ void EntityEditFilters::addFilter(EntityItemID entityID, QString filterURL) { } // Copied from ScriptEngine.cpp. We should make this a class method for reuse. -// Note: I've deliberately stopped short of using ScriptEngine instead of QScriptEngine, as that is out of project scope at this point. -static bool hasCorrectSyntax(const QScriptProgram& program) { - const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode()); +// Note: I've deliberately stopped short of using ScriptEngine instead of ScriptEngine, as that is out of project scope at this point. +static bool hasCorrectSyntax(const ScriptProgramPointer& program) { + const auto syntaxCheck = ScriptEngine::checkSyntax(program->sourceCode()); if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { const auto error = syntaxCheck.errorMessage(); const auto line = QString::number(syntaxCheck.errorLineNumber()); const auto column = QString::number(syntaxCheck.errorColumnNumber()); - const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, program.fileName(), line, column); + const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, program->fileName(), line, column); qCritical() << qPrintable(message); return false; } return true; } -static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName) { +static bool hadUncaughtExceptions(ScriptEngine& engine, const QString& fileName) { if (engine.hasUncaughtException()) { const auto backtrace = engine.uncaughtExceptionBacktrace(); const auto exception = engine.uncaughtException().toString(); @@ -255,16 +257,16 @@ void EntityEditFilters::scriptRequestFinished(EntityItemID entityID) { const QString urlString = scriptRequest->getUrl().toString(); auto scriptContents = scriptRequest->getData(); qInfo() << "Downloaded script:" << scriptContents; - QScriptProgram program(scriptContents, urlString); + // create a ScriptEngine for this script + ScriptEnginePointer engine = newScriptEngine(); + ScriptProgramPointer program = engine->newProgram(scriptContents, urlString); if (hasCorrectSyntax(program)) { - // create a QScriptEngine for this script - QScriptEngine* engine = new QScriptEngine(); engine->setObjectName("filter:" + entityID.toString()); engine->setProperty("type", "edit_filter"); engine->setProperty("fileName", urlString); engine->setProperty("entityID", entityID); - engine->globalObject().setProperty("Script", engine->newQObject(engine)); - DependencyManager::get()->runScriptInitializers(engine); + engine->globalObject()->setProperty("Script", engine->newQObject(engine)); + DependencyManager::get()->runScriptInitializers(engine.data()); engine->evaluate(scriptContents, urlString); if (!hadUncaughtExceptions(*engine, urlString)) { // put the engine in the engine map (so we don't leak them, etc...) @@ -273,74 +275,74 @@ void EntityEditFilters::scriptRequestFinished(EntityItemID entityID) { filterData.rejectAll = false; // define the uncaughtException function - QScriptEngine& engineRef = *engine; + ScriptEngine& engineRef = *engine; filterData.uncaughtExceptions = [&engineRef, urlString]() { return hadUncaughtExceptions(engineRef, urlString); }; // now get the filter function auto global = engine->globalObject(); auto entitiesObject = engine->newObject(); - entitiesObject.setProperty("ADD_FILTER_TYPE", EntityTree::FilterType::Add); - entitiesObject.setProperty("EDIT_FILTER_TYPE", EntityTree::FilterType::Edit); - entitiesObject.setProperty("PHYSICS_FILTER_TYPE", EntityTree::FilterType::Physics); - entitiesObject.setProperty("DELETE_FILTER_TYPE", EntityTree::FilterType::Delete); - global.setProperty("Entities", entitiesObject); - filterData.filterFn = global.property("filter"); - if (!filterData.filterFn.isFunction()) { + entitiesObject->setProperty("ADD_FILTER_TYPE", EntityTree::FilterType::Add); + entitiesObject->setProperty("EDIT_FILTER_TYPE", EntityTree::FilterType::Edit); + entitiesObject->setProperty("PHYSICS_FILTER_TYPE", EntityTree::FilterType::Physics); + entitiesObject->setProperty("DELETE_FILTER_TYPE", EntityTree::FilterType::Delete); + global->setProperty("Entities", entitiesObject); + filterData.filterFn = global->property("filter"); + if (!filterData.filterFn->isFunction()) { qDebug() << "Filter function specified but not found. Will reject all edits for those without lock rights."; - delete engine; + engine.reset(); filterData.rejectAll=true; } // if the wantsToFilterEdit is a boolean evaluate as a boolean, otherwise assume true - QScriptValue wantsToFilterAddValue = filterData.filterFn.property("wantsToFilterAdd"); - filterData.wantsToFilterAdd = wantsToFilterAddValue.isBool() ? wantsToFilterAddValue.toBool() : true; + ScriptValuePointer wantsToFilterAddValue = filterData.filterFn->property("wantsToFilterAdd"); + filterData.wantsToFilterAdd = wantsToFilterAddValue->isBool() ? wantsToFilterAddValue->toBool() : true; // if the wantsToFilterEdit is a boolean evaluate as a boolean, otherwise assume true - QScriptValue wantsToFilterEditValue = filterData.filterFn.property("wantsToFilterEdit"); - filterData.wantsToFilterEdit = wantsToFilterEditValue.isBool() ? wantsToFilterEditValue.toBool() : true; + ScriptValuePointer wantsToFilterEditValue = filterData.filterFn->property("wantsToFilterEdit"); + filterData.wantsToFilterEdit = wantsToFilterEditValue->isBool() ? wantsToFilterEditValue->toBool() : true; // if the wantsToFilterPhysics is a boolean evaluate as a boolean, otherwise assume true - QScriptValue wantsToFilterPhysicsValue = filterData.filterFn.property("wantsToFilterPhysics"); - filterData.wantsToFilterPhysics = wantsToFilterPhysicsValue.isBool() ? wantsToFilterPhysicsValue.toBool() : true; + ScriptValuePointer wantsToFilterPhysicsValue = filterData.filterFn->property("wantsToFilterPhysics"); + filterData.wantsToFilterPhysics = wantsToFilterPhysicsValue->isBool() ? wantsToFilterPhysicsValue->toBool() : true; // if the wantsToFilterDelete is a boolean evaluate as a boolean, otherwise assume false - QScriptValue wantsToFilterDeleteValue = filterData.filterFn.property("wantsToFilterDelete"); - filterData.wantsToFilterDelete = wantsToFilterDeleteValue.isBool() ? wantsToFilterDeleteValue.toBool() : false; + ScriptValuePointer wantsToFilterDeleteValue = filterData.filterFn->property("wantsToFilterDelete"); + filterData.wantsToFilterDelete = wantsToFilterDeleteValue->isBool() ? wantsToFilterDeleteValue->toBool() : false; // check to see if the filterFn has properties asking for Original props - QScriptValue wantsOriginalPropertiesValue = filterData.filterFn.property("wantsOriginalProperties"); + ScriptValuePointer wantsOriginalPropertiesValue = filterData.filterFn->property("wantsOriginalProperties"); // if the wantsOriginalProperties is a boolean, or a string, or list of strings, then evaluate as follows: // - boolean - true - include all original properties // false - no properties at all // - string - empty - no properties at all // any valid property - include just that property in the Original properties // - list of strings - include only those properties in the Original properties - if (wantsOriginalPropertiesValue.isBool()) { - filterData.wantsOriginalProperties = wantsOriginalPropertiesValue.toBool(); - } else if (wantsOriginalPropertiesValue.isString()) { - auto stringValue = wantsOriginalPropertiesValue.toString(); + if (wantsOriginalPropertiesValue->isBool()) { + filterData.wantsOriginalProperties = wantsOriginalPropertiesValue->toBool(); + } else if (wantsOriginalPropertiesValue->isString()) { + auto stringValue = wantsOriginalPropertiesValue->toString(); filterData.wantsOriginalProperties = !stringValue.isEmpty(); if (filterData.wantsOriginalProperties) { EntityPropertyFlagsFromScriptValue(wantsOriginalPropertiesValue, filterData.includedOriginalProperties); } - } else if (wantsOriginalPropertiesValue.isArray()) { + } else if (wantsOriginalPropertiesValue->isArray()) { EntityPropertyFlagsFromScriptValue(wantsOriginalPropertiesValue, filterData.includedOriginalProperties); filterData.wantsOriginalProperties = !filterData.includedOriginalProperties.isEmpty(); } // check to see if the filterFn has properties asking for Zone props - QScriptValue wantsZonePropertiesValue = filterData.filterFn.property("wantsZoneProperties"); + ScriptValuePointer wantsZonePropertiesValue = filterData.filterFn->property("wantsZoneProperties"); // if the wantsZoneProperties is a boolean, or a string, or list of strings, then evaluate as follows: // - boolean - true - include all Zone properties // false - no properties at all // - string - empty - no properties at all // any valid property - include just that property in the Zone properties // - list of strings - include only those properties in the Zone properties - if (wantsZonePropertiesValue.isBool()) { - filterData.wantsZoneProperties = wantsZonePropertiesValue.toBool(); + if (wantsZonePropertiesValue->isBool()) { + filterData.wantsZoneProperties = wantsZonePropertiesValue->toBool(); filterData.wantsZoneBoundingBox = filterData.wantsZoneProperties; // include this too - } else if (wantsZonePropertiesValue.isString()) { - auto stringValue = wantsZonePropertiesValue.toString(); + } else if (wantsZonePropertiesValue->isString()) { + auto stringValue = wantsZonePropertiesValue->toString(); filterData.wantsZoneProperties = !stringValue.isEmpty(); if (filterData.wantsZoneProperties) { if (stringValue == "boundingBox") { @@ -349,10 +351,10 @@ void EntityEditFilters::scriptRequestFinished(EntityItemID entityID) { EntityPropertyFlagsFromScriptValue(wantsZonePropertiesValue, filterData.includedZoneProperties); } } - } else if (wantsZonePropertiesValue.isArray()) { - auto length = wantsZonePropertiesValue.property("length").toInteger(); + } else if (wantsZonePropertiesValue->isArray()) { + auto length = wantsZonePropertiesValue->property("length")->toInteger(); for (int i = 0; i < length; i++) { - auto stringValue = wantsZonePropertiesValue.property(i).toString(); + auto stringValue = wantsZonePropertiesValue->property(i)->toString(); if (!stringValue.isEmpty()) { filterData.wantsZoneProperties = true; diff --git a/libraries/entities/src/EntityEditFilters.h b/libraries/entities/src/EntityEditFilters.h index d1cef0c5f09..fedd9e24f32 100644 --- a/libraries/entities/src/EntityEditFilters.h +++ b/libraries/entities/src/EntityEditFilters.h @@ -13,8 +13,7 @@ #include #include -#include -#include +#include #include #include @@ -23,11 +22,15 @@ #include "EntityItemProperties.h" #include "EntityTree.h" +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; + class EntityEditFilters : public QObject, public Dependency { Q_OBJECT public: struct FilterData { - QScriptValue filterFn; + ScriptValuePointer filterFn; bool wantsOriginalProperties { false }; bool wantsZoneProperties { false }; @@ -41,11 +44,11 @@ class EntityEditFilters : public QObject, public Dependency { bool wantsZoneBoundingBox { false }; std::function uncaughtExceptions; - QScriptEngine* engine; + ScriptEngine* engine; bool rejectAll; FilterData(): engine(nullptr), rejectAll(false) {}; - bool valid() { return (rejectAll || (engine != nullptr && filterFn.isFunction() && uncaughtExceptions)); } + bool valid() { return (rejectAll || (engine != nullptr && filterFn->isFunction() && uncaughtExceptions)); } }; EntityEditFilters() {}; @@ -68,7 +71,7 @@ private slots: EntityTreePointer _tree {}; bool _rejectAll {false}; - QScriptValue _nullObjectForFilter{}; + ScriptValuePointer _nullObjectForFilter{}; QReadWriteLock _lock; QMap _filterDataMap; diff --git a/libraries/entities/src/EntityItemID.cpp b/libraries/entities/src/EntityItemID.cpp index 28b8e109ca1..874a0fcd531 100644 --- a/libraries/entities/src/EntityItemID.cpp +++ b/libraries/entities/src/EntityItemID.cpp @@ -18,6 +18,8 @@ #include #include "RegisteredMetaTypes.h" +#include +#include int entityItemIDTypeID = qRegisterMetaType(); @@ -42,27 +44,27 @@ EntityItemID EntityItemID::readEntityItemIDFromBuffer(const unsigned char* data, return result; } -QScriptValue EntityItemID::toScriptValue(QScriptEngine* engine) const { +ScriptValuePointer EntityItemID::toScriptValue(ScriptEngine* engine) const { return EntityItemIDtoScriptValue(engine, *this); } -QScriptValue EntityItemIDtoScriptValue(QScriptEngine* engine, const EntityItemID& id) { +ScriptValuePointer EntityItemIDtoScriptValue(ScriptEngine* engine, const EntityItemID& id) { return quuidToScriptValue(engine, id); } -void EntityItemIDfromScriptValue(const QScriptValue &object, EntityItemID& id) { +void EntityItemIDfromScriptValue(const ScriptValuePointer &object, EntityItemID& id) { quuidFromScriptValue(object, id); } -QVector qVectorEntityItemIDFromScriptValue(const QScriptValue& array) { - if (!array.isArray()) { +QVector qVectorEntityItemIDFromScriptValue(const ScriptValuePointer& array) { + if (!array->isArray()) { return QVector(); } QVector newVector; - int length = array.property("length").toInteger(); + int length = array->property("length")->toInteger(); newVector.reserve(length); for (int i = 0; i < length; i++) { - QString uuidAsString = array.property(i).toString(); + QString uuidAsString = array->property(i)->toString(); EntityItemID fromString(uuidAsString); newVector << fromString; } diff --git a/libraries/entities/src/EntityItemID.h b/libraries/entities/src/EntityItemID.h index 5ff58ede362..499493fa10f 100644 --- a/libraries/entities/src/EntityItemID.h +++ b/libraries/entities/src/EntityItemID.h @@ -17,9 +17,13 @@ #include #include #include -#include +#include #include +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; + const QUuid UNKNOWN_ENTITY_ID; // null uuid /// Abstract ID for editing model items. Used in EntityItem JS API. @@ -29,7 +33,7 @@ class EntityItemID : public QUuid { EntityItemID(const QUuid& id); // EntityItemID(const EntityItemID& other); static EntityItemID readEntityItemIDFromBuffer(const unsigned char* data, int bytesLeftToRead); - QScriptValue toScriptValue(QScriptEngine* engine) const; + ScriptValuePointer toScriptValue(ScriptEngine* engine) const; bool isInvalidID() const { return *this == UNKNOWN_ENTITY_ID; } }; @@ -41,9 +45,9 @@ inline QDebug operator<<(QDebug debug, const EntityItemID& id) { Q_DECLARE_METATYPE(EntityItemID); Q_DECLARE_METATYPE(QVector); -QScriptValue EntityItemIDtoScriptValue(QScriptEngine* engine, const EntityItemID& properties); -void EntityItemIDfromScriptValue(const QScriptValue &object, EntityItemID& properties); -QVector qVectorEntityItemIDFromScriptValue(const QScriptValue& array); +ScriptValuePointer EntityItemIDtoScriptValue(ScriptEngine* engine, const EntityItemID& properties); +void EntityItemIDfromScriptValue(const ScriptValuePointer &object, EntityItemID& properties); +QVector qVectorEntityItemIDFromScriptValue(const ScriptValuePointer& array); // Allow the use of std::unordered_map with QUuid keys namespace std { template<> struct hash { size_t operator()(const EntityItemID& id) const; }; } diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 524c6316317..d58f70e12a8 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include "EntitiesLogging.h" #include "EntityItem.h" @@ -1569,14 +1570,14 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {Entities.RingGizmo} ring - The ring gizmo properties. */ -QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool skipDefaults, bool allowUnknownCreateTime, +ScriptValuePointer EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool skipDefaults, bool allowUnknownCreateTime, bool strictSemantics, EntityPsuedoPropertyFlags psueudoPropertyFlags) const { // If strictSemantics is true and skipDefaults is false, then all and only those properties are copied for which the property flag // is included in _desiredProperties, or is one of the specially enumerated ALWAYS properties below. // (There may be exceptions, but if so, they are bugs.) // In all other cases, you are welcome to inspect the code and try to figure out what was intended. I wish you luck. -HRS 1/18/17 - QScriptValue properties = engine->newObject(); + ScriptValuePointer properties = engine->newObject(); EntityItemProperties defaultEntityProperties; const bool psuedoPropertyFlagsActive = psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::FlagsActive); @@ -1603,7 +1604,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool } } if (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::LastEdited)) { - properties.setProperty("lastEdited", convertScriptValue(engine, _lastEdited)); + properties->setProperty("lastEdited", convertScriptValue(engine, _lastEdited)); } if (!skipDefaults) { COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DIMENSIONS, naturalDimensions); // gettable, but not settable @@ -1920,9 +1921,9 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool // Handle conversions to old 'textures' property from "imageURL" if (((!psuedoPropertyFlagsButDesiredEmpty && _desiredProperties.isEmpty()) || _desiredProperties.getHasProperty(PROP_IMAGE_URL)) && (!skipDefaults || defaultEntityProperties._imageURL != _imageURL)) { - QScriptValue textures = engine->newObject(); - textures.setProperty("tex.picture", _imageURL); - properties.setProperty("textures", textures); + ScriptValuePointer textures = engine->newObject(); + textures->setProperty("tex.picture", _imageURL); + properties->setProperty("textures", textures); } } @@ -1955,15 +1956,15 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::BoundingBox))) { AABox aaBox = getAABox(); - QScriptValue boundingBox = engine->newObject(); - QScriptValue bottomRightNear = vec3ToScriptValue(engine, aaBox.getCorner()); - QScriptValue topFarLeft = vec3ToScriptValue(engine, aaBox.calcTopFarLeft()); - QScriptValue center = vec3ToScriptValue(engine, aaBox.calcCenter()); - QScriptValue boundingBoxDimensions = vec3ToScriptValue(engine, aaBox.getDimensions()); - boundingBox.setProperty("brn", bottomRightNear); - boundingBox.setProperty("tfl", topFarLeft); - boundingBox.setProperty("center", center); - boundingBox.setProperty("dimensions", boundingBoxDimensions); + ScriptValuePointer boundingBox = engine->newObject(); + ScriptValuePointer bottomRightNear = vec3ToScriptValue(engine, aaBox.getCorner()); + ScriptValuePointer topFarLeft = vec3ToScriptValue(engine, aaBox.calcTopFarLeft()); + ScriptValuePointer center = vec3ToScriptValue(engine, aaBox.calcCenter()); + ScriptValuePointer boundingBoxDimensions = vec3ToScriptValue(engine, aaBox.getDimensions()); + boundingBox->setProperty("brn", bottomRightNear); + boundingBox->setProperty("tfl", topFarLeft); + boundingBox->setProperty("center", center); + boundingBox->setProperty("dimensions", boundingBoxDimensions); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(boundingBox, boundingBox); // gettable, but not settable } @@ -1976,7 +1977,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool if (!skipDefaults && !strictSemantics && (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::RenderInfo))) { - QScriptValue renderInfo = engine->newObject(); + ScriptValuePointer renderInfo = engine->newObject(); /**jsdoc * Information on how an entity is rendered. Properties are only filled in for Model entities; other @@ -1991,40 +1992,40 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool */ // currently only supported by models if (_type == EntityTypes::Model) { - renderInfo.setProperty("verticesCount", (int)getRenderInfoVertexCount()); // FIXME - theoretically the number of vertex could be > max int - renderInfo.setProperty("texturesSize", (int)getRenderInfoTextureSize()); // FIXME - theoretically the size of textures could be > max int - renderInfo.setProperty("hasTransparent", getRenderInfoHasTransparent()); - renderInfo.setProperty("drawCalls", getRenderInfoDrawCalls()); - renderInfo.setProperty("texturesCount", getRenderInfoTextureCount()); + renderInfo->setProperty("verticesCount", (int)getRenderInfoVertexCount()); // FIXME - theoretically the number of vertex could be > max int + renderInfo->setProperty("texturesSize", (int)getRenderInfoTextureSize()); // FIXME - theoretically the size of textures could be > max int + renderInfo->setProperty("hasTransparent", getRenderInfoHasTransparent()); + renderInfo->setProperty("drawCalls", getRenderInfoDrawCalls()); + renderInfo->setProperty("texturesCount", getRenderInfoTextureCount()); } COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(renderInfo, renderInfo); // Gettable but not settable } if (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::ClientOnly)) { - properties.setProperty("clientOnly", convertScriptValue(engine, getEntityHostType() == entity::HostType::AVATAR)); + properties->setProperty("clientOnly", convertScriptValue(engine, getEntityHostType() == entity::HostType::AVATAR)); } if (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::AvatarEntity)) { - properties.setProperty("avatarEntity", convertScriptValue(engine, getEntityHostType() == entity::HostType::AVATAR)); + properties->setProperty("avatarEntity", convertScriptValue(engine, getEntityHostType() == entity::HostType::AVATAR)); } if (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::LocalEntity)) { - properties.setProperty("localEntity", convertScriptValue(engine, getEntityHostType() == entity::HostType::LOCAL)); + properties->setProperty("localEntity", convertScriptValue(engine, getEntityHostType() == entity::HostType::LOCAL)); } if (_type != EntityTypes::PolyLine && (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::FaceCamera))) { - properties.setProperty("faceCamera", convertScriptValue(engine, getBillboardMode() == BillboardMode::YAW)); + properties->setProperty("faceCamera", convertScriptValue(engine, getBillboardMode() == BillboardMode::YAW)); } if (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::IsFacingAvatar)) { - properties.setProperty("isFacingAvatar", convertScriptValue(engine, getBillboardMode() == BillboardMode::FULL)); + properties->setProperty("isFacingAvatar", convertScriptValue(engine, getBillboardMode() == BillboardMode::FULL)); } return properties; } -void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool honorReadOnly) { - QScriptValue typeScriptValue = object.property("type"); - if (typeScriptValue.isValid()) { - setType(typeScriptValue.toVariant().toString()); +void EntityItemProperties::copyFromScriptValue(const ScriptValuePointer& object, bool honorReadOnly) { + ScriptValuePointer typeScriptValue = object->property("type"); + if (typeScriptValue->isValid()) { + setType(typeScriptValue->toVariant().toString()); } // Core @@ -2277,8 +2278,8 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool // Handle conversions from old 'textures' property to "imageURL" { - QScriptValue V = object.property("textures"); - if (_type == EntityTypes::Image && V.isValid() && !object.property("imageURL").isValid()) { + ScriptValuePointer V = object->property("textures"); + if (_type == EntityTypes::Image && V->isValid() && !object->property("imageURL")->isValid()) { bool isValid = false; QString textures = QString_convertFromScriptValue(V, isValid); if (isValid) { @@ -2296,9 +2297,9 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool // Handle old "faceCamera" and "isFacingAvatar" props if (_type != EntityTypes::PolyLine) { - QScriptValue P = object.property("faceCamera"); - if (P.isValid() && !object.property("billboardMode").isValid()) { - bool newValue = P.toVariant().toBool(); + ScriptValuePointer P = object->property("faceCamera"); + if (P->isValid() && !object->property("billboardMode")->isValid()) { + bool newValue = P->toVariant().toBool(); bool oldValue = getBillboardMode() == BillboardMode::YAW; if (_defaultSettings || newValue != oldValue) { setBillboardMode(newValue ? BillboardMode::YAW : BillboardMode::NONE); @@ -2306,9 +2307,9 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool } } { - QScriptValue P = object.property("isFacingAvatar"); - if (P.isValid() && !object.property("billboardMode").isValid() && !object.property("faceCamera").isValid()) { - bool newValue = P.toVariant().toBool(); + ScriptValuePointer P = object->property("isFacingAvatar"); + if (P->isValid() && !object->property("billboardMode")->isValid() && !object->property("faceCamera")->isValid()) { + bool newValue = P->toVariant().toBool(); bool oldValue = getBillboardMode() == BillboardMode::FULL; if (_defaultSettings || newValue != oldValue) { setBillboardMode(newValue ? BillboardMode::FULL : BillboardMode::NONE); @@ -2319,13 +2320,13 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool _lastEdited = usecTimestampNow(); } -void EntityItemProperties::copyFromJSONString(QScriptEngine& scriptEngine, const QString& jsonString) { +void EntityItemProperties::copyFromJSONString(ScriptEngine& scriptEngine, const QString& jsonString) { // DANGER: this method is expensive QJsonDocument propertiesDoc = QJsonDocument::fromJson(jsonString.toUtf8()); QJsonObject propertiesObj = propertiesDoc.object(); QVariant propertiesVariant(propertiesObj); QVariantMap propertiesMap = propertiesVariant.toMap(); - QScriptValue propertiesScriptValue = variantMapToScriptValue(propertiesMap, scriptEngine); + ScriptValuePointer propertiesScriptValue = variantMapToScriptValue(propertiesMap, scriptEngine); bool honorReadOnly = true; copyFromScriptValue(propertiesScriptValue, honorReadOnly); } @@ -2573,47 +2574,47 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { _lastEdited = usecTimestampNow(); } -QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties) { +ScriptValuePointer EntityItemPropertiesToScriptValue(ScriptEngine* engine, const EntityItemProperties& properties) { return properties.copyToScriptValue(engine, false); } -QScriptValue EntityItemNonDefaultPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties) { +ScriptValuePointer EntityItemNonDefaultPropertiesToScriptValue(ScriptEngine* engine, const EntityItemProperties& properties) { return properties.copyToScriptValue(engine, true); } -void EntityItemPropertiesFromScriptValueIgnoreReadOnly(const QScriptValue &object, EntityItemProperties& properties) { +void EntityItemPropertiesFromScriptValueIgnoreReadOnly(const ScriptValuePointer &object, EntityItemProperties& properties) { properties.copyFromScriptValue(object, false); } -void EntityItemPropertiesFromScriptValueHonorReadOnly(const QScriptValue &object, EntityItemProperties& properties) { +void EntityItemPropertiesFromScriptValueHonorReadOnly(const ScriptValuePointer &object, EntityItemProperties& properties) { properties.copyFromScriptValue(object, true); } -QScriptValue EntityPropertyFlagsToScriptValue(QScriptEngine* engine, const EntityPropertyFlags& flags) { +ScriptValuePointer EntityPropertyFlagsToScriptValue(ScriptEngine* engine, const EntityPropertyFlags& flags) { return EntityItemProperties::entityPropertyFlagsToScriptValue(engine, flags); } -void EntityPropertyFlagsFromScriptValue(const QScriptValue& object, EntityPropertyFlags& flags) { +void EntityPropertyFlagsFromScriptValue(const ScriptValuePointer& object, EntityPropertyFlags& flags) { EntityItemProperties::entityPropertyFlagsFromScriptValue(object, flags); } -QScriptValue EntityItemProperties::entityPropertyFlagsToScriptValue(QScriptEngine* engine, const EntityPropertyFlags& flags) { - QScriptValue result = engine->newObject(); +ScriptValuePointer EntityItemProperties::entityPropertyFlagsToScriptValue(ScriptEngine* engine, const EntityPropertyFlags& flags) { + ScriptValuePointer result = engine->newObject(); return result; } -void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue& object, EntityPropertyFlags& flags) { - if (object.isString()) { +void EntityItemProperties::entityPropertyFlagsFromScriptValue(const ScriptValuePointer& object, EntityPropertyFlags& flags) { + if (object->isString()) { EntityPropertyInfo propertyInfo; - if (getPropertyInfo(object.toString(), propertyInfo)) { + if (getPropertyInfo(object->toString(), propertyInfo)) { flags << propertyInfo.propertyEnum; } } - else if (object.isArray()) { - quint32 length = object.property("length").toInt32(); + else if (object->isArray()) { + quint32 length = object->property("length")->toInt32(); for (quint32 i = 0; i < length; i++) { - QString propertyName = object.property(i).toString(); + QString propertyName = object->property(i)->toString(); EntityPropertyInfo propertyInfo; if (getPropertyInfo(propertyName, propertyInfo)) { flags << propertyInfo.propertyEnum; @@ -3012,18 +3013,18 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr * @property {string} minimum - The minimum numerical value the property may have, if available, otherwise "". * @property {string} maximum - The maximum numerical value the property may have, if available, otherwise "". */ -QScriptValue EntityPropertyInfoToScriptValue(QScriptEngine* engine, const EntityPropertyInfo& propertyInfo) { - QScriptValue obj = engine->newObject(); - obj.setProperty("propertyEnum", propertyInfo.propertyEnum); - obj.setProperty("minimum", propertyInfo.minimum.toString()); - obj.setProperty("maximum", propertyInfo.maximum.toString()); +ScriptValuePointer EntityPropertyInfoToScriptValue(ScriptEngine* engine, const EntityPropertyInfo& propertyInfo) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty("propertyEnum", propertyInfo.propertyEnum); + obj->setProperty("minimum", propertyInfo.minimum.toString()); + obj->setProperty("maximum", propertyInfo.maximum.toString()); return obj; } -void EntityPropertyInfoFromScriptValue(const QScriptValue& object, EntityPropertyInfo& propertyInfo) { - propertyInfo.propertyEnum = (EntityPropertyList)object.property("propertyEnum").toVariant().toUInt(); - propertyInfo.minimum = object.property("minimum").toVariant(); - propertyInfo.maximum = object.property("maximum").toVariant(); +void EntityPropertyInfoFromScriptValue(const ScriptValuePointer& object, EntityPropertyInfo& propertyInfo) { + propertyInfo.propertyEnum = (EntityPropertyList)object->property("propertyEnum")->toVariant().toUInt(); + propertyInfo.minimum = object->property("minimum")->toVariant(); + propertyInfo.maximum = object->property("maximum")->toVariant(); } // TODO: Implement support for edit packets that can span an MTU sized buffer. We need to implement a mechanism for the @@ -5197,7 +5198,7 @@ void EntityItemProperties::convertToCloneProperties(const EntityItemID& entityID setCloneAvatarEntity(ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY); } -bool EntityItemProperties::blobToProperties(QScriptEngine& scriptEngine, const QByteArray& blob, EntityItemProperties& properties) { +bool EntityItemProperties::blobToProperties(ScriptEngine& scriptEngine, const QByteArray& blob, EntityItemProperties& properties) { // DANGER: this method is NOT efficient. // begin recipe for converting unfortunately-formatted-binary-blob to EntityItemProperties QJsonDocument jsonProperties = QJsonDocument::fromBinaryData(blob); @@ -5207,20 +5208,20 @@ bool EntityItemProperties::blobToProperties(QScriptEngine& scriptEngine, const Q } QVariant variant = jsonProperties.toVariant(); QVariantMap variantMap = variant.toMap(); - QScriptValue scriptValue = variantMapToScriptValue(variantMap, scriptEngine); + ScriptValuePointer scriptValue = variantMapToScriptValue(variantMap, scriptEngine); EntityItemPropertiesFromScriptValueIgnoreReadOnly(scriptValue, properties); // end recipe return true; } -void EntityItemProperties::propertiesToBlob(QScriptEngine& scriptEngine, const QUuid& myAvatarID, +void EntityItemProperties::propertiesToBlob(ScriptEngine& scriptEngine, const QUuid& myAvatarID, const EntityItemProperties& properties, QByteArray& blob, bool allProperties) { // DANGER: this method is NOT efficient. // begin recipe for extracting unfortunately-formatted-binary-blob from EntityItem - QScriptValue scriptValue = allProperties + ScriptValuePointer scriptValue = allProperties ? EntityItemPropertiesToScriptValue(&scriptEngine, properties) : EntityItemNonDefaultPropertiesToScriptValue(&scriptEngine, properties); - QVariant variantProperties = scriptValue.toVariant(); + QVariant variantProperties = scriptValue->toVariant(); QJsonDocument jsonProperties = QJsonDocument::fromVariant(variantProperties); // the ID of the parent/avatar changes from session to session. use a special UUID to indicate the avatar QJsonObject jsonObject = jsonProperties.object(); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 91de5400627..4184880ad12 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -21,11 +21,11 @@ #include #include -#include #include #include #include #include +#include #include #include @@ -69,6 +69,10 @@ #include "TextEffect.h" #include "TextAlignment.h" +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; + const quint64 UNKNOWN_CREATED_TIME = 0; using vec3Color = glm::vec3; @@ -96,7 +100,7 @@ EntityPropertyInfo makePropertyInfo(EntityPropertyList p, typename std::enable_i } /// A collection of properties of an entity item used in the scripting API. Translates between the actual properties of an -/// entity and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete +/// entity and a JavaScript style hash/ScriptValuePointer storing a set of properties. Used in scripting to set/get the complete /// set of entity item properties via JavaScript hashes/QScriptValues /// all units for SI units (meter, second, radian, etc) class EntityItemProperties { @@ -119,8 +123,8 @@ class EntityItemProperties { friend class ZoneEntityItem; friend class MaterialEntityItem; public: - static bool blobToProperties(QScriptEngine& scriptEngine, const QByteArray& blob, EntityItemProperties& properties); - static void propertiesToBlob(QScriptEngine& scriptEngine, const QUuid& myAvatarID, const EntityItemProperties& properties, + static bool blobToProperties(ScriptEngine& scriptEngine, const QByteArray& blob, EntityItemProperties& properties); + static void propertiesToBlob(ScriptEngine& scriptEngine, const QUuid& myAvatarID, const EntityItemProperties& properties, QByteArray& blob, bool allProperties = false); EntityItemProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()); @@ -133,13 +137,13 @@ class EntityItemProperties { EntityTypes::EntityType getType() const { return _type; } void setType(EntityTypes::EntityType type) { _type = type; } - virtual QScriptValue copyToScriptValue(QScriptEngine* engine, bool skipDefaults, bool allowUnknownCreateTime = false, + virtual ScriptValuePointer copyToScriptValue(ScriptEngine* engine, bool skipDefaults, bool allowUnknownCreateTime = false, bool strictSemantics = false, EntityPsuedoPropertyFlags psueudoPropertyFlags = EntityPsuedoPropertyFlags()) const; - virtual void copyFromScriptValue(const QScriptValue& object, bool honorReadOnly); - void copyFromJSONString(QScriptEngine& scriptEngine, const QString& jsonString); + virtual void copyFromScriptValue(const ScriptValuePointer& object, bool honorReadOnly); + void copyFromJSONString(ScriptEngine& scriptEngine, const QString& jsonString); - static QScriptValue entityPropertyFlagsToScriptValue(QScriptEngine* engine, const EntityPropertyFlags& flags); - static void entityPropertyFlagsFromScriptValue(const QScriptValue& object, EntityPropertyFlags& flags); + static ScriptValuePointer entityPropertyFlagsToScriptValue(ScriptEngine* engine, const EntityPropertyFlags& flags); + static void entityPropertyFlagsFromScriptValue(const ScriptValuePointer& object, EntityPropertyFlags& flags); static bool getPropertyInfo(const QString& propertyName, EntityPropertyInfo& propertyInfo); @@ -539,18 +543,18 @@ class EntityItemProperties { }; Q_DECLARE_METATYPE(EntityItemProperties); -QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties); -QScriptValue EntityItemNonDefaultPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties); -void EntityItemPropertiesFromScriptValueIgnoreReadOnly(const QScriptValue& object, EntityItemProperties& properties); -void EntityItemPropertiesFromScriptValueHonorReadOnly(const QScriptValue& object, EntityItemProperties& properties); +ScriptValuePointer EntityItemPropertiesToScriptValue(ScriptEngine* engine, const EntityItemProperties& properties); +ScriptValuePointer EntityItemNonDefaultPropertiesToScriptValue(ScriptEngine* engine, const EntityItemProperties& properties); +void EntityItemPropertiesFromScriptValueIgnoreReadOnly(const ScriptValuePointer& object, EntityItemProperties& properties); +void EntityItemPropertiesFromScriptValueHonorReadOnly(const ScriptValuePointer& object, EntityItemProperties& properties); Q_DECLARE_METATYPE(EntityPropertyFlags); -QScriptValue EntityPropertyFlagsToScriptValue(QScriptEngine* engine, const EntityPropertyFlags& flags); -void EntityPropertyFlagsFromScriptValue(const QScriptValue& object, EntityPropertyFlags& flags); +ScriptValuePointer EntityPropertyFlagsToScriptValue(ScriptEngine* engine, const EntityPropertyFlags& flags); +void EntityPropertyFlagsFromScriptValue(const ScriptValuePointer& object, EntityPropertyFlags& flags); Q_DECLARE_METATYPE(EntityPropertyInfo); -QScriptValue EntityPropertyInfoToScriptValue(QScriptEngine* engine, const EntityPropertyInfo& propertyInfo); -void EntityPropertyInfoFromScriptValue(const QScriptValue& object, EntityPropertyInfo& propertyInfo); +ScriptValuePointer EntityPropertyInfoToScriptValue(ScriptEngine* engine, const EntityPropertyInfo& propertyInfo); +void EntityPropertyInfoFromScriptValue(const ScriptValuePointer& object, EntityPropertyInfo& propertyInfo); // define these inline here so the macros work inline void EntityItemProperties::setPosition(const glm::vec3& value) diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index c25eb21e6c5..550e822ff2d 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -15,6 +15,9 @@ #include "EntityItemID.h" #include +#include "ScriptEngine.h" +#include "ScriptValue.h" +#include "ScriptValueUtils.h" #define APPEND_ENTITY_PROPERTY(P,V) \ if (requestedProperties.getHasProperty(P)) { \ @@ -99,118 +102,118 @@ changedProperties += P; \ } -inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::vec2& v) { return vec2ToScriptValue(e, v); } -inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::vec3& v) { return vec3ToScriptValue(e, v); } -inline QScriptValue vec3Color_convertScriptValue(QScriptEngine* e, const glm::vec3& v) { return vec3ColorToScriptValue(e, v); } -inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::u8vec3& v) { return u8vec3ToScriptValue(e, v); } -inline QScriptValue u8vec3Color_convertScriptValue(QScriptEngine* e, const glm::u8vec3& v) { return u8vec3ColorToScriptValue(e, v); } -inline QScriptValue convertScriptValue(QScriptEngine* e, float v) { return QScriptValue(v); } -inline QScriptValue convertScriptValue(QScriptEngine* e, int v) { return QScriptValue(v); } -inline QScriptValue convertScriptValue(QScriptEngine* e, bool v) { return QScriptValue(v); } -inline QScriptValue convertScriptValue(QScriptEngine* e, quint16 v) { return QScriptValue(v); } -inline QScriptValue convertScriptValue(QScriptEngine* e, quint32 v) { return QScriptValue(v); } -inline QScriptValue convertScriptValue(QScriptEngine* e, quint64 v) { return QScriptValue((qsreal)v); } -inline QScriptValue convertScriptValue(QScriptEngine* e, const QString& v) { return QScriptValue(v); } - -inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::quat& v) { return quatToScriptValue(e, v); } -inline QScriptValue convertScriptValue(QScriptEngine* e, const QScriptValue& v) { return v; } -inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector& v) {return qVectorVec3ToScriptValue(e, v); } -inline QScriptValue qVectorVec3Color_convertScriptValue(QScriptEngine* e, const QVector& v) {return qVectorVec3ColorToScriptValue(e, v); } -inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector& v) {return qVectorQuatToScriptValue(e, v); } -inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector& v) {return qVectorBoolToScriptValue(e, v); } -inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector& v) { return qVectorFloatToScriptValue(e, v); } -inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector& v) { return qVectorQUuidToScriptValue(e, v); } - -inline QScriptValue convertScriptValue(QScriptEngine* e, const QRect& v) { return qRectToScriptValue(e, v); } - -inline QScriptValue convertScriptValue(QScriptEngine* e, const QByteArray& v) { +inline ScriptValuePointer convertScriptValue(ScriptEngine* e, const glm::vec2& v) { return vec2ToScriptValue(e, v); } +inline ScriptValuePointer convertScriptValue(ScriptEngine* e, const glm::vec3& v) { return vec3ToScriptValue(e, v); } +inline ScriptValuePointer vec3Color_convertScriptValue(ScriptEngine* e, const glm::vec3& v) { return vec3ColorToScriptValue(e, v); } +inline ScriptValuePointer convertScriptValue(ScriptEngine* e, const glm::u8vec3& v) { return u8vec3ToScriptValue(e, v); } +inline ScriptValuePointer u8vec3Color_convertScriptValue(ScriptEngine* e, const glm::u8vec3& v) { return u8vec3ColorToScriptValue(e, v); } +inline ScriptValuePointer convertScriptValue(ScriptEngine* e, float v) { return e->newValue(v); } +inline ScriptValuePointer convertScriptValue(ScriptEngine* e, int v) { return e->newValue(v); } +inline ScriptValuePointer convertScriptValue(ScriptEngine* e, bool v) { return e->newValue(v); } +inline ScriptValuePointer convertScriptValue(ScriptEngine* e, quint16 v) { return e->newValue(v); } +inline ScriptValuePointer convertScriptValue(ScriptEngine* e, quint32 v) { return e->newValue(v); } +inline ScriptValuePointer convertScriptValue(ScriptEngine* e, quint64 v) { return e->newValue((double)v); } +inline ScriptValuePointer convertScriptValue(ScriptEngine* e, const QString& v) { return e->newValue(v); } + +inline ScriptValuePointer convertScriptValue(ScriptEngine* e, const glm::quat& v) { return quatToScriptValue(e, v); } +inline ScriptValuePointer convertScriptValue(ScriptEngine* e, const ScriptValuePointer& v) { return v; } +inline ScriptValuePointer convertScriptValue(ScriptEngine* e, const QVector& v) {return qVectorVec3ToScriptValue(e, v); } +inline ScriptValuePointer qVectorVec3Color_convertScriptValue(ScriptEngine* e, const QVector& v) {return qVectorVec3ColorToScriptValue(e, v); } +inline ScriptValuePointer convertScriptValue(ScriptEngine* e, const QVector& v) {return qVectorQuatToScriptValue(e, v); } +inline ScriptValuePointer convertScriptValue(ScriptEngine* e, const QVector& v) {return qVectorBoolToScriptValue(e, v); } +inline ScriptValuePointer convertScriptValue(ScriptEngine* e, const QVector& v) { return qVectorFloatToScriptValue(e, v); } +inline ScriptValuePointer convertScriptValue(ScriptEngine* e, const QVector& v) { return qVectorQUuidToScriptValue(e, v); } + +inline ScriptValuePointer convertScriptValue(ScriptEngine* e, const QRect& v) { return qRectToScriptValue(e, v); } + +inline ScriptValuePointer convertScriptValue(ScriptEngine* e, const QByteArray& v) { QByteArray b64 = v.toBase64(); - return QScriptValue(QString(b64)); + return e->newValue(QString(b64)); } -inline QScriptValue convertScriptValue(QScriptEngine* e, const EntityItemID& v) { return QScriptValue(QUuid(v).toString()); } +inline ScriptValuePointer convertScriptValue(ScriptEngine* e, const EntityItemID& v) { return e->newValue(QUuid(v).toString()); } -inline QScriptValue convertScriptValue(QScriptEngine* e, const AACube& v) { return aaCubeToScriptValue(e, v); } +inline ScriptValuePointer convertScriptValue(ScriptEngine* e, const AACube& v) { return aaCubeToScriptValue(e, v); } #define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(X,G,g,P,p) \ if ((desiredProperties.isEmpty() || desiredProperties.getHasProperty(X)) && \ (!skipDefaults || defaultEntityProperties.get##G().get##P() != get##P())) { \ - QScriptValue groupProperties = properties.property(#g); \ - if (!groupProperties.isValid()) { \ + ScriptValuePointer groupProperties = properties->property(#g); \ + if (!groupProperties->isValid()) { \ groupProperties = engine->newObject(); \ } \ - QScriptValue V = convertScriptValue(engine, get##P()); \ - groupProperties.setProperty(#p, V); \ - properties.setProperty(#g, groupProperties); \ + ScriptValuePointer V = convertScriptValue(engine, get##P()); \ + groupProperties->setProperty(#p, V); \ + properties->setProperty(#g, groupProperties); \ } #define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(X,G,g,P,p,T) \ if ((desiredProperties.isEmpty() || desiredProperties.getHasProperty(X)) && \ (!skipDefaults || defaultEntityProperties.get##G().get##P() != get##P())) { \ - QScriptValue groupProperties = properties.property(#g); \ - if (!groupProperties.isValid()) { \ + ScriptValuePointer groupProperties = properties->property(#g); \ + if (!groupProperties->isValid()) { \ groupProperties = engine->newObject(); \ } \ - QScriptValue V = T##_convertScriptValue(engine, get##P()); \ - groupProperties.setProperty(#p, V); \ - properties.setProperty(#g, groupProperties); \ + ScriptValuePointer V = T##_convertScriptValue(engine, get##P()); \ + groupProperties->setProperty(#p, V); \ + properties->setProperty(#g, groupProperties); \ } #define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(X,G,g,P,p,M) \ if ((desiredProperties.isEmpty() || desiredProperties.getHasProperty(X)) && \ (!skipDefaults || defaultEntityProperties.get##G().get##P() != get##P())) { \ - QScriptValue groupProperties = properties.property(#g); \ - if (!groupProperties.isValid()) { \ + ScriptValuePointer groupProperties = properties->property(#g); \ + if (!groupProperties->isValid()) { \ groupProperties = engine->newObject(); \ } \ - QScriptValue V = convertScriptValue(engine, M()); \ - groupProperties.setProperty(#p, V); \ - properties.setProperty(#g, groupProperties); \ + ScriptValuePointer V = convertScriptValue(engine, M()); \ + groupProperties->setProperty(#p, V); \ + properties->setProperty(#g, groupProperties); \ } #define COPY_PROPERTY_TO_QSCRIPTVALUE(p,P) \ if (((!psuedoPropertyFlagsButDesiredEmpty && _desiredProperties.isEmpty()) || _desiredProperties.getHasProperty(p)) && \ (!skipDefaults || defaultEntityProperties._##P != _##P)) { \ - QScriptValue V = convertScriptValue(engine, _##P); \ - properties.setProperty(#P, V); \ + ScriptValuePointer V = convertScriptValue(engine, _##P); \ + properties->setProperty(#P, V); \ } #define COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(p,P,T) \ if ((_desiredProperties.isEmpty() || _desiredProperties.getHasProperty(p)) && \ (!skipDefaults || defaultEntityProperties._##P != _##P)) { \ - QScriptValue V = T##_convertScriptValue(engine, _##P); \ - properties.setProperty(#P, V); \ + ScriptValuePointer V = T##_convertScriptValue(engine, _##P); \ + properties->setProperty(#P, V); \ } #define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(P, G) \ - properties.setProperty(#P, G); + properties->setProperty(#P, G); #define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(p, P, G) \ if (((!psuedoPropertyFlagsButDesiredEmpty && _desiredProperties.isEmpty()) || _desiredProperties.getHasProperty(p)) && \ (!skipDefaults || defaultEntityProperties._##P != _##P)) { \ - QScriptValue V = convertScriptValue(engine, G); \ - properties.setProperty(#P, V); \ + ScriptValuePointer V = convertScriptValue(engine, G); \ + properties->setProperty(#P, V); \ } #define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_TYPED(p, P, G, T) \ if ((_desiredProperties.isEmpty() || _desiredProperties.getHasProperty(p)) && \ (!skipDefaults || defaultEntityProperties._##P != _##P)) { \ - QScriptValue V = T##_convertScriptValue(engine, G); \ - properties.setProperty(#P, V); \ + ScriptValuePointer V = T##_convertScriptValue(engine, G); \ + properties->setProperty(#P, V); \ } // same as COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER but uses #X instead of #P in the setProperty() step #define COPY_PROXY_PROPERTY_TO_QSCRIPTVALUE_GETTER(p, P, X, G) \ if (((!psuedoPropertyFlagsButDesiredEmpty && _desiredProperties.isEmpty()) || _desiredProperties.getHasProperty(p)) && \ (!skipDefaults || defaultEntityProperties._##P != _##P)) { \ - QScriptValue V = convertScriptValue(engine, G); \ - properties.setProperty(#X, V); \ + ScriptValuePointer V = convertScriptValue(engine, G); \ + properties->setProperty(#X, V); \ } #define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_ALWAYS(P, G) \ if (!skipDefaults || defaultEntityProperties._##P != _##P) { \ - QScriptValue V = convertScriptValue(engine, G); \ - properties.setProperty(#P, V); \ + ScriptValuePointer V = convertScriptValue(engine, G); \ + properties->setProperty(#P, V); \ } typedef QVector qVectorVec3; @@ -218,100 +221,100 @@ typedef QVector qVectorQuat; typedef QVector qVectorBool; typedef QVector qVectorFloat; typedef QVector qVectorQUuid; -inline float float_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toFloat(&isValid); } -inline quint64 quint64_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toULongLong(&isValid); } -inline quint32 quint32_convertFromScriptValue(const QScriptValue& v, bool& isValid) { +inline float float_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { return v->toVariant().toFloat(&isValid); } +inline quint64 quint64_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { return v->toVariant().toULongLong(&isValid); } +inline quint32 quint32_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { // Use QString::toUInt() so that isValid is set to false if the number is outside the quint32 range. - return v.toString().toUInt(&isValid); + return v->toString().toUInt(&isValid); } -inline quint16 quint16_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toInt(&isValid); } -inline uint16_t uint16_t_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toInt(&isValid); } -inline uint32_t uint32_t_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toInt(&isValid); } -inline int int_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toInt(&isValid); } -inline bool bool_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toBool(); } -inline uint8_t uint8_t_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return (uint8_t)(0xff & v.toVariant().toInt(&isValid)); } -inline QString QString_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toString().trimmed(); } -inline QUuid QUuid_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toUuid(); } -inline EntityItemID EntityItemID_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toUuid(); } - -inline QByteArray QByteArray_convertFromScriptValue(const QScriptValue& v, bool& isValid) { +inline quint16 quint16_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { return v->toVariant().toInt(&isValid); } +inline uint16_t uint16_t_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { return v->toVariant().toInt(&isValid); } +inline uint32_t uint32_t_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { return v->toVariant().toInt(&isValid); } +inline int int_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { return v->toVariant().toInt(&isValid); } +inline bool bool_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { isValid = true; return v->toVariant().toBool(); } +inline uint8_t uint8_t_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { isValid = true; return (uint8_t)(0xff & v->toVariant().toInt(&isValid)); } +inline QString QString_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { isValid = true; return v->toVariant().toString().trimmed(); } +inline QUuid QUuid_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { isValid = true; return v->toVariant().toUuid(); } +inline EntityItemID EntityItemID_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { isValid = true; return v->toVariant().toUuid(); } + +inline QByteArray QByteArray_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { isValid = true; - QString b64 = v.toVariant().toString().trimmed(); + QString b64 = v->toVariant().toString().trimmed(); return QByteArray::fromBase64(b64.toUtf8()); } -inline glm::vec2 vec2_convertFromScriptValue(const QScriptValue& v, bool& isValid) { +inline glm::vec2 vec2_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { isValid = true; glm::vec2 vec2; vec2FromScriptValue(v, vec2); return vec2; } -inline glm::vec3 vec3_convertFromScriptValue(const QScriptValue& v, bool& isValid) { +inline glm::vec3 vec3_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { isValid = true; glm::vec3 vec3; vec3FromScriptValue(v, vec3); return vec3; } -inline glm::vec3 vec3Color_convertFromScriptValue(const QScriptValue& v, bool& isValid) { +inline glm::vec3 vec3Color_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { isValid = true; glm::vec3 vec3; vec3FromScriptValue(v, vec3); return vec3; } -inline glm::u8vec3 u8vec3Color_convertFromScriptValue(const QScriptValue& v, bool& isValid) { +inline glm::u8vec3 u8vec3Color_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { isValid = true; glm::u8vec3 vec3; u8vec3FromScriptValue(v, vec3); return vec3; } -inline AACube AACube_convertFromScriptValue(const QScriptValue& v, bool& isValid) { +inline AACube AACube_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { isValid = true; AACube result; aaCubeFromScriptValue(v, result); return result; } -inline qVectorFloat qVectorFloat_convertFromScriptValue(const QScriptValue& v, bool& isValid) { +inline qVectorFloat qVectorFloat_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { isValid = true; return qVectorFloatFromScriptValue(v); } -inline qVectorVec3 qVectorVec3_convertFromScriptValue(const QScriptValue& v, bool& isValid) { +inline qVectorVec3 qVectorVec3_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { isValid = true; return qVectorVec3FromScriptValue(v); } -inline qVectorQuat qVectorQuat_convertFromScriptValue(const QScriptValue& v, bool& isValid) { +inline qVectorQuat qVectorQuat_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { isValid = true; return qVectorQuatFromScriptValue(v); } -inline qVectorBool qVectorBool_convertFromScriptValue(const QScriptValue& v, bool& isValid) { +inline qVectorBool qVectorBool_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { isValid = true; return qVectorBoolFromScriptValue(v); } -inline qVectorQUuid qVectorQUuid_convertFromScriptValue(const QScriptValue& v, bool& isValid) { +inline qVectorQUuid qVectorQUuid_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { isValid = true; return qVectorQUuidFromScriptValue(v); } -inline glm::quat quat_convertFromScriptValue(const QScriptValue& v, bool& isValid) { +inline glm::quat quat_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { isValid = false; /// assume it can't be converted - QScriptValue x = v.property("x"); - QScriptValue y = v.property("y"); - QScriptValue z = v.property("z"); - QScriptValue w = v.property("w"); - if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { + ScriptValuePointer x = v->property("x"); + ScriptValuePointer y = v->property("y"); + ScriptValuePointer z = v->property("z"); + ScriptValuePointer w = v->property("w"); + if (x->isValid() && y->isValid() && z->isValid() && w->isValid()) { glm::quat newValue; - newValue.x = x.toVariant().toFloat(); - newValue.y = y.toVariant().toFloat(); - newValue.z = z.toVariant().toFloat(); - newValue.w = w.toVariant().toFloat(); + newValue.x = x->toVariant().toFloat(); + newValue.y = y->toVariant().toFloat(); + newValue.z = z->toVariant().toFloat(); + newValue.w = w->toVariant().toFloat(); isValid = !glm::isnan(newValue.x) && !glm::isnan(newValue.y) && !glm::isnan(newValue.z) && @@ -323,7 +326,7 @@ inline glm::quat quat_convertFromScriptValue(const QScriptValue& v, bool& isVali return glm::quat(); } -inline QRect QRect_convertFromScriptValue(const QScriptValue& v, bool& isValid) { +inline QRect QRect_convertFromScriptValue(const ScriptValuePointer& v, bool& isValid) { isValid = true; QRect rect; qRectFromScriptValue(v, rect); @@ -341,8 +344,8 @@ inline QRect QRect_convertFromScriptValue(const QScriptValue& v, bool& isValid) #define COPY_PROPERTY_FROM_QSCRIPTVALUE(P, T, S) \ { \ - QScriptValue V = object.property(#P); \ - if (V.isValid()) { \ + ScriptValuePointer V = object->property(#P); \ + if (V->isValid()) { \ bool isValid = false; \ T newValue = T##_convertFromScriptValue(V, isValid); \ if (isValid && (_defaultSettings || newValue != _##P)) { \ @@ -353,8 +356,8 @@ inline QRect QRect_convertFromScriptValue(const QScriptValue& v, bool& isValid) #define COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(P, T, S, G) \ { \ - QScriptValue V = object.property(#P); \ - if (V.isValid()) { \ + ScriptValuePointer V = object->property(#P); \ + if (V->isValid()) { \ bool isValid = false; \ T newValue = T##_convertFromScriptValue(V, isValid); \ if (isValid && (_defaultSettings || newValue != G())) { \ @@ -365,8 +368,8 @@ inline QRect QRect_convertFromScriptValue(const QScriptValue& v, bool& isValid) #define COPY_PROPERTY_FROM_QSCRIPTVALUE_NOCHECK(P, T, S) \ { \ - QScriptValue V = object.property(#P); \ - if (V.isValid()) { \ + ScriptValuePointer V = object->property(#P); \ + if (V->isValid()) { \ bool isValid = false; \ T newValue = T##_convertFromScriptValue(V, isValid); \ if (isValid && (_defaultSettings)) { \ @@ -377,10 +380,10 @@ inline QRect QRect_convertFromScriptValue(const QScriptValue& v, bool& isValid) #define COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(G, P, T, S) \ { \ - QScriptValue G = object.property(#G); \ - if (G.isValid()) { \ - QScriptValue V = G.property(#P); \ - if (V.isValid()) { \ + ScriptValuePointer G = object->property(#G); \ + if (G->isValid()) { \ + ScriptValuePointer V = G->property(#P); \ + if (V->isValid()) { \ bool isValid = false; \ T newValue = T##_convertFromScriptValue(V, isValid); \ if (isValid && (_defaultSettings || newValue != _##P)) { \ @@ -392,9 +395,9 @@ inline QRect QRect_convertFromScriptValue(const QScriptValue& v, bool& isValid) #define COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(P, S) \ { \ - QScriptValue P = object.property(#P); \ - if (P.isValid()) { \ - QString newValue = P.toVariant().toString(); \ + ScriptValuePointer P = object->property(#P); \ + if (P->isValid()) { \ + QString newValue = P->toVariant().toString(); \ if (_defaultSettings || newValue != get##S##AsString()) { \ set##S##FromString(newValue); \ } \ @@ -403,11 +406,11 @@ inline QRect QRect_convertFromScriptValue(const QScriptValue& v, bool& isValid) #define COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_ENUM(G, P, S) \ { \ - QScriptValue G = object.property(#G); \ - if (G.isValid()) { \ - QScriptValue P = G.property(#P); \ - if (P.isValid()) { \ - QString newValue = P.toVariant().toString(); \ + ScriptValuePointer G = object->property(#G); \ + if (G->isValid()) { \ + ScriptValuePointer P = G->property(#P); \ + if (P->isValid()) { \ + QString newValue = P->toVariant().toString(); \ if (_defaultSettings || newValue != get##S##AsString()) { \ set##S##FromString(newValue); \ } \ diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 90c521215e9..10541717974 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -41,6 +41,8 @@ #include #include #include "GrabPropertyGroup.h" +#include +#include const QString GRABBABLE_USER_DATA = "{\"grabbableKey\":{\"grabbable\":true}}"; const QString NOT_GRABBABLE_USER_DATA = "{\"grabbableKey\":{\"grabbable\":false}}"; @@ -672,21 +674,21 @@ struct EntityPropertiesResult { // Static method to make sure that we have the right script engine. // Using sender() or QtScriptable::engine() does not work for classes used by multiple threads (script-engines) -QScriptValue EntityScriptingInterface::getMultipleEntityProperties(QScriptContext* context, QScriptEngine* engine) { +ScriptValuePointer EntityScriptingInterface::getMultipleEntityProperties(ScriptContext* context, ScriptEngine* engine) { const int ARGUMENT_ENTITY_IDS = 0; const int ARGUMENT_EXTENDED_DESIRED_PROPERTIES = 1; auto entityScriptingInterface = DependencyManager::get(); - const auto entityIDs = qscriptvalue_cast>(context->argument(ARGUMENT_ENTITY_IDS)); + const auto entityIDs = scriptvalue_cast>(context->argument(ARGUMENT_ENTITY_IDS)); return entityScriptingInterface->getMultipleEntityPropertiesInternal(engine, entityIDs, context->argument(ARGUMENT_EXTENDED_DESIRED_PROPERTIES)); } -QScriptValue EntityScriptingInterface::getMultipleEntityPropertiesInternal(QScriptEngine* engine, QVector entityIDs, const QScriptValue& extendedDesiredProperties) { +ScriptValuePointer EntityScriptingInterface::getMultipleEntityPropertiesInternal(ScriptEngine* engine, QVector entityIDs, const ScriptValuePointer& extendedDesiredProperties) { PROFILE_RANGE(script_entities, __FUNCTION__); EntityPsuedoPropertyFlags psuedoPropertyFlags; - const auto readExtendedPropertyStringValue = [&](QScriptValue extendedProperty) { - const auto extendedPropertyString = extendedProperty.toString(); + const auto readExtendedPropertyStringValue = [&](ScriptValuePointer extendedProperty) { + const auto extendedPropertyString = extendedProperty->toString(); if (extendedPropertyString == "id") { psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::ID); } else if (extendedPropertyString == "type") { @@ -716,18 +718,18 @@ QScriptValue EntityScriptingInterface::getMultipleEntityPropertiesInternal(QScri } }; - if (extendedDesiredProperties.isString()) { + if (extendedDesiredProperties->isString()) { readExtendedPropertyStringValue(extendedDesiredProperties); psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::FlagsActive); - } else if (extendedDesiredProperties.isArray()) { - const quint32 length = extendedDesiredProperties.property("length").toInt32(); + } else if (extendedDesiredProperties->isArray()) { + const quint32 length = extendedDesiredProperties->property("length")->toInt32(); for (quint32 i = 0; i < length; i++) { - readExtendedPropertyStringValue(extendedDesiredProperties.property(i)); + readExtendedPropertyStringValue(extendedDesiredProperties->property(i)); } psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::FlagsActive); } - EntityPropertyFlags desiredProperties = qscriptvalue_cast(extendedDesiredProperties); + EntityPropertyFlags desiredProperties = scriptvalue_cast(extendedDesiredProperties); bool needsScriptSemantics = desiredProperties.getHasProperty(PROP_POSITION) || desiredProperties.getHasProperty(PROP_ROTATION) || desiredProperties.getHasProperty(PROP_LOCAL_POSITION) || @@ -774,18 +776,18 @@ QScriptValue EntityScriptingInterface::getMultipleEntityPropertiesInternal(QScri }); } } - QScriptValue finalResult = engine->newArray(resultProperties.size()); + ScriptValuePointer finalResult = engine->newArray(resultProperties.size()); quint32 i = 0; if (needsScriptSemantics) { PROFILE_RANGE(script_entities, "EntityScriptingInterface::getMultipleEntityProperties>Script Semantics"); foreach(const auto& result, resultProperties) { - finalResult.setProperty(i++, convertPropertiesToScriptSemantics(result.properties, result.scalesWithParent) + finalResult->setProperty(i++, convertPropertiesToScriptSemantics(result.properties, result.scalesWithParent) .copyToScriptValue(engine, false, false, false, psuedoPropertyFlags)); } } else { PROFILE_RANGE(script_entities, "EntityScriptingInterface::getMultipleEntityProperties>Skip Script Semantics"); foreach(const auto& result, resultProperties) { - finalResult.setProperty(i++, result.properties.copyToScriptValue(engine, false, false, false, psuedoPropertyFlags)); + finalResult->setProperty(i++, result.properties.copyToScriptValue(engine, false, false, false, psuedoPropertyFlags)); } } return finalResult; @@ -1060,14 +1062,14 @@ QSizeF EntityScriptingInterface::textSize(const QUuid& id, const QString& text) return EntityTree::textSize(id, text); } -void EntityScriptingInterface::setPersistentEntitiesScriptEngine(QSharedPointer engine) { +void EntityScriptingInterface::setPersistentEntitiesScriptEngine(QSharedPointer manager) { std::lock_guard lock(_entitiesScriptEngineLock); - _persistentEntitiesScriptEngine = engine; + _persistentEntitiesScriptManager = manager; } -void EntityScriptingInterface::setNonPersistentEntitiesScriptEngine(QSharedPointer engine) { +void EntityScriptingInterface::setNonPersistentEntitiesScriptEngine(QSharedPointer manager) { std::lock_guard lock(_entitiesScriptEngineLock); - _nonPersistentEntitiesScriptEngine = engine; + _nonPersistentEntitiesScriptManager = manager; } void EntityScriptingInterface::callEntityMethod(const QUuid& id, const QString& method, const QStringList& params) { @@ -1076,7 +1078,7 @@ void EntityScriptingInterface::callEntityMethod(const QUuid& id, const QString& auto entity = getEntityTree()->findEntityByEntityItemID(id); if (entity) { std::lock_guard lock(_entitiesScriptEngineLock); - auto& scriptEngine = (entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine; + auto& scriptEngine = (entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptManager : _nonPersistentEntitiesScriptManager; if (scriptEngine) { scriptEngine->callEntityScriptMethod(id, method, params); } @@ -1124,7 +1126,7 @@ void EntityScriptingInterface::handleEntityScriptCallMethodPacket(QSharedPointer auto entity = getEntityTree()->findEntityByEntityItemID(entityID); if (entity) { std::lock_guard lock(_entitiesScriptEngineLock); - auto& scriptEngine = (entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine; + auto& scriptEngine = (entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptManager : _nonPersistentEntitiesScriptManager; if (scriptEngine) { scriptEngine->callEntityScriptMethod(entityID, method, params, senderNode->getUUID()); } @@ -1258,7 +1260,7 @@ QVector EntityScriptingInterface::findEntitiesByName(const QString entity } RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, - const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) const { + const ScriptValuePointer& entityIdsToInclude, const ScriptValuePointer& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) const { PROFILE_RANGE(script_entities, __FUNCTION__); QVector entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude); QVector entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard); @@ -1334,21 +1336,21 @@ bool EntityScriptingInterface::reloadServerScripts(const QUuid& entityID) { return client->reloadServerScript(entityID); } -bool EntityPropertyMetadataRequest::script(EntityItemID entityID, QScriptValue handler) { +bool EntityPropertyMetadataRequest::script(EntityItemID entityID, ScriptValuePointer handler) { using LocalScriptStatusRequest = QFutureWatcher; LocalScriptStatusRequest* request = new LocalScriptStatusRequest; - QObject::connect(request, &LocalScriptStatusRequest::finished, _engine, [=]() mutable { + QObject::connect(request, &LocalScriptStatusRequest::finished, _manager, [=]() mutable { auto details = request->result().toMap(); - QScriptValue err, result; + ScriptValuePointer err, result; if (details.contains("isError")) { if (!details.contains("message")) { details["message"] = details["errorInfo"]; } - err = _engine->makeError(_engine->toScriptValue(details)); + err = _manager->engine()->makeError(_manager->engine()->toScriptValue(details)); } else { details["success"] = true; - result = _engine->toScriptValue(details); + result = _manager->engine()->toScriptValue(details); } callScopedHandlerObject(handler, err, result); request->deleteLater(); @@ -1361,19 +1363,19 @@ bool EntityPropertyMetadataRequest::script(EntityItemID entityID, QScriptValue h }, entityID); if (!request->isStarted()) { request->deleteLater(); - callScopedHandlerObject(handler, _engine->makeError("Entities Scripting Provider unavailable", "InternalError"), QScriptValue()); + callScopedHandlerObject(handler, _engine->makeError("Entities Scripting Provider unavailable", "InternalError"), ScriptValuePointer()); return false; } return true; } -bool EntityPropertyMetadataRequest::serverScripts(EntityItemID entityID, QScriptValue handler) { +bool EntityPropertyMetadataRequest::serverScripts(EntityItemID entityID, ScriptValuePointer handler) { auto client = DependencyManager::get(); auto request = client->createScriptStatusRequest(entityID); - QPointer engine = _engine; - QObject::connect(request, &GetScriptStatusRequest::finished, _engine, [=](GetScriptStatusRequest* request) mutable { - auto engine = _engine; - if (!engine) { + QPointer manager = _manager; + QObject::connect(request, &GetScriptStatusRequest::finished, _manager, [=](GetScriptStatusRequest* request) mutable { + auto manager = _manager; + if (!manager) { qCDebug(entities) << __FUNCTION__ << " -- engine destroyed while inflight" << entityID; return; } @@ -1383,7 +1385,7 @@ bool EntityPropertyMetadataRequest::serverScripts(EntityItemID entityID, QScript details["status"] = EntityScriptStatus_::valueToKey(request->getStatus()).toLower(); details["errorInfo"] = request->getErrorInfo(); - QScriptValue err, result; + ScriptValuePointer err, result; if (!details["success"].toBool()) { if (!details.contains("message") && details.contains("errorInfo")) { details["message"] = details["errorInfo"]; @@ -1391,9 +1393,9 @@ bool EntityPropertyMetadataRequest::serverScripts(EntityItemID entityID, QScript if (details["message"].toString().isEmpty()) { details["message"] = "entity server script details not found"; } - err = engine->makeError(engine->toScriptValue(details)); + err = manager->engine()->makeError(manager->engine()->toScriptValue(details)); } else { - result = engine->toScriptValue(details); + result = manager->engine()->toScriptValue(details); } callScopedHandlerObject(handler, err, result); request->deleteLater(); @@ -1402,12 +1404,12 @@ bool EntityPropertyMetadataRequest::serverScripts(EntityItemID entityID, QScript return true; } -bool EntityScriptingInterface::queryPropertyMetadata(const QUuid& entityID, QScriptValue property, QScriptValue scopeOrCallback, QScriptValue methodOrName) { - auto name = property.toString(); +bool EntityScriptingInterface::queryPropertyMetadata(const QUuid& entityID, ScriptValuePointer property, ScriptValuePointer scopeOrCallback, ScriptValuePointer methodOrName) { + auto name = property->toString(); auto handler = makeScopedHandlerObject(scopeOrCallback, methodOrName); - QPointer engine = dynamic_cast(handler.engine()); - if (!engine) { - qCDebug(entities) << "queryPropertyMetadata without detectable engine" << entityID << name; + QPointer manager = handler->engine()->manager(); + if (!manager) { + qCDebug(entities) << "queryPropertyMetadata without detectable script manager" << entityID << name; return false; } #ifdef DEBUG_ENGINE_STATE @@ -1415,9 +1417,9 @@ bool EntityScriptingInterface::queryPropertyMetadata(const QUuid& entityID, QScr qDebug() << "queryPropertyMetadata -- engine destroyed!" << (!engine ? "nullptr" : "engine"); }); #endif - if (!handler.property("callback").isFunction()) { - qDebug() << "!handler.callback.isFunction" << engine; - engine->raiseException(engine->makeError("callback is not a function", "TypeError")); + if (!handler->property("callback")->isFunction()) { + qDebug() << "!handler.callback.isFunction" << manager; + manager->engine()->raiseException(manager->engine()->makeError("callback is not a function", "TypeError")); return false; } @@ -1433,26 +1435,26 @@ bool EntityScriptingInterface::queryPropertyMetadata(const QUuid& entityID, QScr // This is an async callback pattern -- so if needed C++ can easily throttle or restrict queries later. - EntityPropertyMetadataRequest request(engine); + EntityPropertyMetadataRequest request(manager); if (name == "script") { return request.script(entityID, handler); } else if (name == "serverScripts") { return request.serverScripts(entityID, handler); } else { - engine->raiseException(engine->makeError("metadata for property " + name + " is not yet queryable")); - engine->maybeEmitUncaughtException(__FUNCTION__); + manager->engine()->raiseException(manager->engine()->makeError("metadata for property " + name + " is not yet queryable")); + manager->engine()->maybeEmitUncaughtException(__FUNCTION__); return false; } } -bool EntityScriptingInterface::getServerScriptStatus(const QUuid& entityID, QScriptValue callback) { +bool EntityScriptingInterface::getServerScriptStatus(const QUuid& entityID, ScriptValuePointer callback) { auto client = DependencyManager::get(); auto request = client->createScriptStatusRequest(entityID); - connect(request, &GetScriptStatusRequest::finished, callback.engine(), [callback](GetScriptStatusRequest* request) mutable { + connect(request, &GetScriptStatusRequest::finished, callback->engine(), [callback](GetScriptStatusRequest* request) mutable { QString statusString = EntityScriptStatus_::valueToKey(request->getStatus());; - QScriptValueList args { request->getResponseReceived(), request->getIsRunning(), statusString.toLower(), request->getErrorInfo() }; - callback.call(QScriptValue(), args); + ScriptValueList args { request->getResponseReceived(), request->getIsRunning(), statusString.toLower(), request->getErrorInfo() }; + callback->call(ScriptValuePointer(), args); request->deleteLater(); }); request->start(); @@ -1483,40 +1485,40 @@ bool EntityScriptingInterface::getDrawZoneBoundaries() const { return ZoneEntityItem::getDrawZoneBoundaries(); } -QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, const RayToEntityIntersectionResult& value) { - QScriptValue obj = engine->newObject(); - obj.setProperty("intersects", value.intersects); - obj.setProperty("accurate", value.accurate); - QScriptValue entityItemValue = EntityItemIDtoScriptValue(engine, value.entityID); - obj.setProperty("entityID", entityItemValue); - obj.setProperty("distance", value.distance); - obj.setProperty("face", boxFaceToString(value.face)); - - QScriptValue intersection = vec3ToScriptValue(engine, value.intersection); - obj.setProperty("intersection", intersection); - QScriptValue surfaceNormal = vec3ToScriptValue(engine, value.surfaceNormal); - obj.setProperty("surfaceNormal", surfaceNormal); - obj.setProperty("extraInfo", engine->toScriptValue(value.extraInfo)); +ScriptValuePointer RayToEntityIntersectionResultToScriptValue(ScriptEngine* engine, const RayToEntityIntersectionResult& value) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty("intersects", value.intersects); + obj->setProperty("accurate", value.accurate); + ScriptValuePointer entityItemValue = EntityItemIDtoScriptValue(engine, value.entityID); + obj->setProperty("entityID", entityItemValue); + obj->setProperty("distance", value.distance); + obj->setProperty("face", boxFaceToString(value.face)); + + ScriptValuePointer intersection = vec3ToScriptValue(engine, value.intersection); + obj->setProperty("intersection", intersection); + ScriptValuePointer surfaceNormal = vec3ToScriptValue(engine, value.surfaceNormal); + obj->setProperty("surfaceNormal", surfaceNormal); + obj->setProperty("extraInfo", engine->toScriptValue(value.extraInfo)); return obj; } -void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, RayToEntityIntersectionResult& value) { - value.intersects = object.property("intersects").toVariant().toBool(); - value.accurate = object.property("accurate").toVariant().toBool(); - QScriptValue entityIDValue = object.property("entityID"); +void RayToEntityIntersectionResultFromScriptValue(const ScriptValuePointer& object, RayToEntityIntersectionResult& value) { + value.intersects = object->property("intersects")->toVariant().toBool(); + value.accurate = object->property("accurate")->toVariant().toBool(); + ScriptValuePointer entityIDValue = object->property("entityID"); quuidFromScriptValue(entityIDValue, value.entityID); - value.distance = object.property("distance").toVariant().toFloat(); - value.face = boxFaceFromString(object.property("face").toVariant().toString()); + value.distance = object->property("distance")->toVariant().toFloat(); + value.face = boxFaceFromString(object->property("face")->toVariant().toString()); - QScriptValue intersection = object.property("intersection"); - if (intersection.isValid()) { + ScriptValuePointer intersection = object->property("intersection"); + if (intersection->isValid()) { vec3FromScriptValue(intersection, value.intersection); } - QScriptValue surfaceNormal = object.property("surfaceNormal"); - if (surfaceNormal.isValid()) { + ScriptValuePointer surfaceNormal = object->property("surfaceNormal"); + if (surfaceNormal->isValid()) { vec3FromScriptValue(surfaceNormal, value.surfaceNormal); } - value.extraInfo = object.property("extraInfo").toVariant().toMap(); + value.extraInfo = object->property("extraInfo")->toVariant().toMap(); } bool EntityScriptingInterface::polyVoxWorker(QUuid entityID, std::function actor) { @@ -2237,14 +2239,14 @@ bool EntityScriptingInterface::AABoxIntersectsCapsule(const glm::vec3& low, cons return aaBox.findCapsulePenetration(start, end, radius, penetration); } -void EntityScriptingInterface::getMeshes(const QUuid& entityID, QScriptValue callback) { +void EntityScriptingInterface::getMeshes(const QUuid& entityID, ScriptValuePointer callback) { PROFILE_RANGE(script_entities, __FUNCTION__); EntityItemPointer entity = static_cast(_entityTree->findEntityByEntityItemID(entityID)); if (!entity) { qCDebug(entities) << "EntityScriptingInterface::getMeshes no entity with ID" << entityID; - QScriptValueList args { callback.engine()->undefinedValue(), false }; - callback.call(QScriptValue(), args); + ScriptValueList args { callback->engine()->undefinedValue(), false }; + callback->call(ScriptValuePointer(), args); return; } @@ -2252,12 +2254,12 @@ void EntityScriptingInterface::getMeshes(const QUuid& entityID, QScriptValue cal bool success = entity->getMeshes(result); if (success) { - QScriptValue resultAsScriptValue = meshesToScriptValue(callback.engine(), result); - QScriptValueList args { resultAsScriptValue, true }; - callback.call(QScriptValue(), args); + ScriptValuePointer resultAsScriptValue = meshesToScriptValue(callback->engine(), result); + ScriptValueList args { resultAsScriptValue, true }; + callback->call(ScriptValuePointer(), args); } else { - QScriptValueList args { callback.engine()->undefinedValue(), false }; - callback.call(QScriptValue(), args); + ScriptValueList args { callback->engine()->undefinedValue(), false }; + callback->call(ScriptValuePointer(), args); } } diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 4647e263355..cba590f30a9 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -19,13 +19,15 @@ #include #include #include +#include #include #include #include #include -#include +#include "PointerEvent.h" #include +#include "ScriptManager.h" #include "PolyVoxEntityItem.h" #include "LineEntityItem.h" @@ -36,10 +38,12 @@ #include "EntitiesScriptEngineProvider.h" #include "EntityItemProperties.h" -#include "BaseScriptEngine.h" - class EntityTree; class MeshProxy; +class ScriptContext; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; extern const QString GRABBABLE_USER_DATA; extern const QString NOT_GRABBABLE_USER_DATA; @@ -50,11 +54,11 @@ extern const QString NOT_GRABBABLE_USER_DATA; // problems with their own Entity scripts. class EntityPropertyMetadataRequest { public: - EntityPropertyMetadataRequest(BaseScriptEngine* engine) : _engine(engine) {}; - bool script(EntityItemID entityID, QScriptValue handler); - bool serverScripts(EntityItemID entityID, QScriptValue handler); + EntityPropertyMetadataRequest(ScriptManager* manager) : _manager(manager == nullptr ? nullptr : manager){}; + bool script(EntityItemID entityID, ScriptValuePointer handler); + bool serverScripts(EntityItemID entityID, ScriptValuePointer handler); private: - QPointer _engine; + QPointer _manager; }; /**jsdoc @@ -85,8 +89,8 @@ class RayToEntityIntersectionResult { QVariantMap extraInfo; }; Q_DECLARE_METATYPE(RayToEntityIntersectionResult) -QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, const RayToEntityIntersectionResult& results); -void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, RayToEntityIntersectionResult& results); +ScriptValuePointer RayToEntityIntersectionResultToScriptValue(ScriptEngine* engine, const RayToEntityIntersectionResult& results); +void RayToEntityIntersectionResultFromScriptValue(const ScriptValuePointer& object, RayToEntityIntersectionResult& results); class ParabolaToEntityIntersectionResult { public: @@ -181,8 +185,8 @@ class EntityScriptingInterface : public OctreeScriptingInterface, public Depende void setEntityTree(EntityTreePointer modelTree); EntityTreePointer getEntityTree() { return _entityTree; } - void setPersistentEntitiesScriptEngine(QSharedPointer engine); - void setNonPersistentEntitiesScriptEngine(QSharedPointer engine); + void setPersistentEntitiesScriptEngine(QSharedPointer manager); + void setNonPersistentEntitiesScriptEngine(QSharedPointer manager); void resetActivityTracking(); ActivityTracking getActivityTracking() const { return _activityTracking; } @@ -208,8 +212,8 @@ class EntityScriptingInterface : public OctreeScriptingInterface, public Depende * var propertySets = Entities.getMultipleEntityProperties(entityIDs, "name"); * print("Nearby entity names: " + JSON.stringify(propertySets)); */ - static QScriptValue getMultipleEntityProperties(QScriptContext* context, QScriptEngine* engine); - QScriptValue getMultipleEntityPropertiesInternal(QScriptEngine* engine, QVector entityIDs, const QScriptValue& extendedDesiredProperties); + static ScriptValuePointer getMultipleEntityProperties(ScriptContext* context, ScriptEngine* engine); + ScriptValuePointer getMultipleEntityPropertiesInternal(ScriptEngine* engine, QVector entityIDs, const ScriptValuePointer& extendedDesiredProperties); QUuid addEntityInternal(const EntityItemProperties& properties, entity::HostType entityHostType); @@ -834,7 +838,7 @@ public slots: /// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate /// will be false. Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false, - const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue(), + const ScriptValuePointer& entityIdsToInclude = ScriptValuePointer(), const ScriptValuePointer& entityIdsToDiscard = ScriptValuePointer(), bool visibleOnly = false, bool collidableOnly = false) const; /**jsdoc @@ -863,7 +867,7 @@ public slots: * @param {string} errorInfo - "" if there is a server entity script running, otherwise it may contain extra * information on the error. */ - Q_INVOKABLE bool getServerScriptStatus(const QUuid& entityID, QScriptValue callback); + Q_INVOKABLE bool getServerScriptStatus(const QUuid& entityID, ScriptValuePointer callback); /**jsdoc * Gets metadata for certain entity properties such as script and serverScripts. @@ -893,8 +897,8 @@ public slots: * @param {object} result - The metadata for the requested entity property if there was no error, otherwise * undefined. */ - Q_INVOKABLE bool queryPropertyMetadata(const QUuid& entityID, QScriptValue property, QScriptValue scopeOrCallback, - QScriptValue methodOrName = QScriptValue()); + Q_INVOKABLE bool queryPropertyMetadata(const QUuid& entityID, ScriptValuePointer property, ScriptValuePointer scopeOrCallback, + ScriptValuePointer methodOrName = ScriptValuePointer()); /**jsdoc @@ -1907,7 +1911,7 @@ public slots: * {@link Graphics} API instead. */ // FIXME move to a renderable entity interface - Q_INVOKABLE void getMeshes(const QUuid& entityID, QScriptValue callback); + Q_INVOKABLE void getMeshes(const QUuid& entityID, ScriptValuePointer callback); /**jsdoc * Gets the object to world transform, excluding scale, of an entity. @@ -2532,7 +2536,7 @@ public slots: auto entity = getEntityTree()->findEntityByEntityItemID(id); if (entity) { std::lock_guard lock(_entitiesScriptEngineLock); - function((entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine); + function((entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptManager : _nonPersistentEntitiesScriptManager); } }; @@ -2563,8 +2567,8 @@ private slots: EntityTreePointer _entityTree; std::recursive_mutex _entitiesScriptEngineLock; - QSharedPointer _persistentEntitiesScriptEngine; - QSharedPointer _nonPersistentEntitiesScriptEngine; + QSharedPointer _persistentEntitiesScriptManager; + QSharedPointer _nonPersistentEntitiesScriptManager; bool _bidOnSimulationOwnership { false }; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index eeb42626c25..7242ab5c563 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -23,8 +23,6 @@ #include #include -#include - #include #include #include @@ -2848,8 +2846,8 @@ bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElementPointer } entityDescription["DataVersion"] = _persistDataVersion; entityDescription["Id"] = _persistID; - QScriptEngine scriptEngine; - RecurseOctreeToMapOperator theOperator(entityDescription, element, &scriptEngine, skipDefaultValues, + ScriptEnginePointer engine = newScriptEngine(); + RecurseOctreeToMapOperator theOperator(entityDescription, element, scriptEngine, skipDefaultValues, skipThoseWithBadParents, _myAvatar); withReadLock([&] { recurseTreeWithOperator(&theOperator); @@ -2995,10 +2993,10 @@ bool EntityTree::readFromMap(QVariantMap& map, const bool isImport) { // map will have a top-level list keyed as "Entities". This will be extracted // and iterated over. Each member of this list is converted to a QVariantMap, then - // to a QScriptValue, and then to EntityItemProperties. These properties are used + // to a ScriptValuePointer, and then to EntityItemProperties. These properties are used // to add the new entity to the EntityTree. QVariantList entitiesQList = map["Entities"].toList(); - QScriptEngine scriptEngine; + ScriptEnginePointer scriptEngine = newScriptEngine(); if (entitiesQList.length() == 0) { // Empty map or invalidly formed file. @@ -3009,7 +3007,7 @@ bool EntityTree::readFromMap(QVariantMap& map, const bool isImport) { bool success = true; foreach (QVariant entityVariant, entitiesQList) { - // QVariantMap --> QScriptValue --> EntityItemProperties --> Entity + // QVariantMap --> ScriptValuePointer --> EntityItemProperties --> Entity QVariantMap entityMap = entityVariant.toMap(); // handle parentJointName for wearables @@ -3022,7 +3020,7 @@ bool EntityTree::readFromMap(QVariantMap& map, const bool isImport) { " mapped it to parentJointIndex " << entityMap["parentJointIndex"].toInt(); } - QScriptValue entityScriptValue = variantMapToScriptValue(entityMap, scriptEngine); + ScriptValuePointer entityScriptValue = variantMapToScriptValue(entityMap, *scriptEngine); EntityItemProperties properties; EntityItemPropertiesFromScriptValueIgnoreReadOnly(entityScriptValue, properties); @@ -3173,8 +3171,8 @@ bool EntityTree::readFromMap(QVariantMap& map, const bool isImport) { } bool EntityTree::writeToJSON(QString& jsonString, const OctreeElementPointer& element) { - QScriptEngine scriptEngine; - RecurseOctreeToJSONOperator theOperator(element, &scriptEngine, jsonString); + ScriptEnginePointer engine = newScriptEngine(); + RecurseOctreeToJSONOperator theOperator(element, scriptEngine, jsonString); withReadLock([&] { recurseTreeWithOperator(&theOperator); }); diff --git a/libraries/entities/src/GrabPropertyGroup.cpp b/libraries/entities/src/GrabPropertyGroup.cpp index 7a9ba147d9b..f18d1c2c4a2 100644 --- a/libraries/entities/src/GrabPropertyGroup.cpp +++ b/libraries/entities/src/GrabPropertyGroup.cpp @@ -16,8 +16,8 @@ #include "EntityItemProperties.h" #include "EntityItemPropertiesMacros.h" -void GrabPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, - QScriptEngine* engine, bool skipDefaults, +void GrabPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValuePointer& properties, + ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_GRABBABLE, Grab, grab, Grabbable, grabbable); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_KINEMATIC, Grab, grab, GrabKinematic, grabKinematic); @@ -43,7 +43,7 @@ void GrabPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProp } -void GrabPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { +void GrabPropertyGroup::copyFromScriptValue(const ScriptValuePointer& object, bool& _defaultSettings) { COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, grabbable, bool, setGrabbable); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, grabKinematic, bool, setGrabKinematic); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, grabFollowsController, bool, setGrabFollowsController); diff --git a/libraries/entities/src/GrabPropertyGroup.h b/libraries/entities/src/GrabPropertyGroup.h index 9fa58273be1..4c8440ee935 100644 --- a/libraries/entities/src/GrabPropertyGroup.h +++ b/libraries/entities/src/GrabPropertyGroup.h @@ -16,7 +16,7 @@ #include -#include +#include #include "PropertyGroup.h" #include "EntityItemPropertiesMacros.h" @@ -25,6 +25,8 @@ class EntityItemProperties; class EncodeBitstreamParams; class OctreePacketData; class ReadBitstreamToTreeParams; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; static const bool INITIAL_GRABBABLE { true }; static const bool INITIAL_KINEMATIC { true }; @@ -72,10 +74,10 @@ static const glm::vec3 INITIAL_EQUIPPABLE_INDICATOR_OFFSET { glm::vec3(0.0f) }; class GrabPropertyGroup : public PropertyGroup { public: // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, - QScriptEngine* engine, bool skipDefaults, + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValuePointer& properties, + ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const override; - virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + virtual void copyFromScriptValue(const ScriptValuePointer& object, bool& _defaultSettings) override; void merge(const GrabPropertyGroup& other); diff --git a/libraries/entities/src/HazePropertyGroup.cpp b/libraries/entities/src/HazePropertyGroup.cpp index 632f73ced6e..d9b939babcc 100644 --- a/libraries/entities/src/HazePropertyGroup.cpp +++ b/libraries/entities/src/HazePropertyGroup.cpp @@ -16,7 +16,7 @@ #include "EntityItemProperties.h" #include "EntityItemPropertiesMacros.h" -void HazePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { +void HazePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValuePointer& properties, ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_RANGE, Haze, haze, HazeRange, hazeRange); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_HAZE_COLOR, Haze, haze, HazeColor, hazeColor, u8vec3Color); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_HAZE_GLARE_COLOR, Haze, haze, HazeGlareColor, hazeGlareColor, u8vec3Color); @@ -34,7 +34,7 @@ void HazePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProp COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_KEYLIGHT_ALTITUDE, Haze, haze, HazeKeyLightAltitude, hazeKeyLightAltitude); } -void HazePropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { +void HazePropertyGroup::copyFromScriptValue(const ScriptValuePointer& object, bool& _defaultSettings) { COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeRange, float, setHazeRange); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeColor, u8vec3Color, setHazeColor); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeGlareColor, u8vec3Color, setHazeGlareColor); diff --git a/libraries/entities/src/HazePropertyGroup.h b/libraries/entities/src/HazePropertyGroup.h index 59a36b6f7f9..a30b20a9c25 100644 --- a/libraries/entities/src/HazePropertyGroup.h +++ b/libraries/entities/src/HazePropertyGroup.h @@ -16,7 +16,7 @@ #include -#include +#include #include "PropertyGroup.h" #include "EntityItemPropertiesMacros.h" @@ -26,6 +26,9 @@ class EncodeBitstreamParams; class OctreePacketData; class EntityTreeElementExtraEncodeData; class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; static const float INITIAL_HAZE_RANGE{ 1000.0f }; static const glm::u8vec3 initialHazeGlareColor { 255, 229, 179 }; @@ -76,10 +79,10 @@ static const float INITIAL_KEY_LIGHT_ALTITUDE{ 200.0f }; class HazePropertyGroup : public PropertyGroup { public: // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, - QScriptEngine* engine, bool skipDefaults, + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValuePointer& properties, + ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const override; - virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + virtual void copyFromScriptValue(const ScriptValuePointer& object, bool& _defaultSettings) override; void merge(const HazePropertyGroup& other); diff --git a/libraries/entities/src/KeyLightPropertyGroup.cpp b/libraries/entities/src/KeyLightPropertyGroup.cpp index b70e94504df..f688b47af36 100644 --- a/libraries/entities/src/KeyLightPropertyGroup.cpp +++ b/libraries/entities/src/KeyLightPropertyGroup.cpp @@ -25,8 +25,8 @@ const bool KeyLightPropertyGroup::DEFAULT_KEYLIGHT_CAST_SHADOWS { false }; const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_SHADOW_BIAS { 0.5f }; const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_SHADOW_MAX_DISTANCE { 40.0f }; -void KeyLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, - QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { +void KeyLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValuePointer& properties, + ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_KEYLIGHT_COLOR, KeyLight, keyLight, Color, color, u8vec3Color); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_INTENSITY, KeyLight, keyLight, Intensity, intensity); @@ -36,7 +36,7 @@ void KeyLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desired COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_SHADOW_MAX_DISTANCE, KeyLight, keyLight, ShadowMaxDistance, shadowMaxDistance); } -void KeyLightPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { +void KeyLightPropertyGroup::copyFromScriptValue(const ScriptValuePointer& object, bool& _defaultSettings) { COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, color, u8vec3Color, setColor); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, intensity, float, setIntensity); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, direction, vec3, setDirection); diff --git a/libraries/entities/src/KeyLightPropertyGroup.h b/libraries/entities/src/KeyLightPropertyGroup.h index 6441c30d509..2760f8ac9b6 100644 --- a/libraries/entities/src/KeyLightPropertyGroup.h +++ b/libraries/entities/src/KeyLightPropertyGroup.h @@ -17,7 +17,7 @@ #include -#include +#include #include "EntityItemPropertiesMacros.h" #include "PropertyGroup.h" @@ -26,6 +26,9 @@ class EncodeBitstreamParams; class OctreePacketData; class EntityTreeElementExtraEncodeData; class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; /**jsdoc * A key light is defined by the following properties: @@ -45,10 +48,10 @@ class ReadBitstreamToTreeParams; class KeyLightPropertyGroup : public PropertyGroup { public: // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, - QScriptEngine* engine, bool skipDefaults, + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValuePointer& properties, + ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const override; - virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + virtual void copyFromScriptValue(const ScriptValuePointer& object, bool& _defaultSettings) override; void merge(const KeyLightPropertyGroup& other); diff --git a/libraries/entities/src/PropertyGroup.h b/libraries/entities/src/PropertyGroup.h index 8a9d7e9158c..9d74388caac 100644 --- a/libraries/entities/src/PropertyGroup.h +++ b/libraries/entities/src/PropertyGroup.h @@ -12,7 +12,7 @@ #ifndef hifi_PropertyGroup_h #define hifi_PropertyGroup_h -#include +#include #include @@ -24,6 +24,9 @@ class EncodeBitstreamParams; class OctreePacketData; class EntityTreeElementExtraEncodeData; class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; using EntityTreeElementExtraEncodeDataPointer = std::shared_ptr; @@ -32,8 +35,8 @@ class PropertyGroup { virtual ~PropertyGroup() = default; // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const = 0; - virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) = 0; + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValuePointer& properties, ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const = 0; + virtual void copyFromScriptValue(const ScriptValuePointer& object, bool& _defaultSettings) = 0; virtual void debugDump() const { } virtual void listChangedProperties(QList& out) { } diff --git a/libraries/entities/src/PulsePropertyGroup.cpp b/libraries/entities/src/PulsePropertyGroup.cpp index 54f81750da9..7db361df6f5 100644 --- a/libraries/entities/src/PulsePropertyGroup.cpp +++ b/libraries/entities/src/PulsePropertyGroup.cpp @@ -57,8 +57,8 @@ void PulsePropertyGroup::setAlphaModeFromString(const QString& pulseMode) { } } -void PulsePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, - QScriptEngine* engine, bool skipDefaults, +void PulsePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValuePointer& properties, + ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_PULSE_MIN, Pulse, pulse, Min, min); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_PULSE_MAX, Pulse, pulse, Max, max); @@ -67,7 +67,7 @@ void PulsePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredPro COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_PULSE_ALPHA_MODE, Pulse, pulse, AlphaMode, alphaMode, getAlphaModeAsString); } -void PulsePropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { +void PulsePropertyGroup::copyFromScriptValue(const ScriptValuePointer& object, bool& _defaultSettings) { COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(pulse, min, float, setMin); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(pulse, max, float, setMax); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(pulse, period, float, setPeriod); diff --git a/libraries/entities/src/PulsePropertyGroup.h b/libraries/entities/src/PulsePropertyGroup.h index 634ab654a75..062317e639e 100644 --- a/libraries/entities/src/PulsePropertyGroup.h +++ b/libraries/entities/src/PulsePropertyGroup.h @@ -13,7 +13,7 @@ #include -#include +#include #include @@ -24,6 +24,9 @@ class EntityItemProperties; class EncodeBitstreamParams; class OctreePacketData; class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; /**jsdoc * A color and alpha pulse that an entity may have. @@ -40,10 +43,10 @@ class ReadBitstreamToTreeParams; class PulsePropertyGroup : public PropertyGroup { public: // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, - QScriptEngine* engine, bool skipDefaults, + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValuePointer& properties, + ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const override; - virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + virtual void copyFromScriptValue(const ScriptValuePointer& object, bool& _defaultSettings) override; void merge(const PulsePropertyGroup& other); diff --git a/libraries/entities/src/RecurseOctreeToJSONOperator.cpp b/libraries/entities/src/RecurseOctreeToJSONOperator.cpp index b64a700abcc..aeeef8e796a 100644 --- a/libraries/entities/src/RecurseOctreeToJSONOperator.cpp +++ b/libraries/entities/src/RecurseOctreeToJSONOperator.cpp @@ -12,7 +12,7 @@ #include "RecurseOctreeToJSONOperator.h" #include "EntityItemProperties.h" -RecurseOctreeToJSONOperator::RecurseOctreeToJSONOperator(const OctreeElementPointer&, QScriptEngine* engine, +RecurseOctreeToJSONOperator::RecurseOctreeToJSONOperator(const OctreeElementPointer&, ScriptEngine* engine, QString jsonPrefix, bool skipDefaults, bool skipThoseWithBadParents): _engine(engine), _json(jsonPrefix), @@ -34,7 +34,7 @@ void RecurseOctreeToJSONOperator::processEntity(const EntityItemPointer& entity) return; // we weren't able to resolve a parent from _parentID, so don't save this entity. } - QScriptValue qScriptValues = _skipDefaults + ScriptValuePointer qScriptValues = _skipDefaults ? EntityItemNonDefaultPropertiesToScriptValue(_engine, entity->getProperties()) : EntityItemPropertiesToScriptValue(_engine, entity->getProperties()); @@ -45,6 +45,6 @@ void RecurseOctreeToJSONOperator::processEntity(const EntityItemPointer& entity) _json += "\n "; // Override default toString(): - qScriptValues.setProperty("toString", _toStringMethod); - _json += qScriptValues.toString(); + qScriptValues->setProperty("toString", _toStringMethod); + _json += qScriptValues->toString(); } diff --git a/libraries/entities/src/RecurseOctreeToJSONOperator.h b/libraries/entities/src/RecurseOctreeToJSONOperator.h index a1d388ed222..6f5b18aba1a 100644 --- a/libraries/entities/src/RecurseOctreeToJSONOperator.h +++ b/libraries/entities/src/RecurseOctreeToJSONOperator.h @@ -11,9 +11,14 @@ #include "EntityTree.h" +#include + +class ScriptValue; +using ScriptValuePointer = QSharedPointer; + class RecurseOctreeToJSONOperator : public RecurseOctreeOperator { public: - RecurseOctreeToJSONOperator(const OctreeElementPointer&, QScriptEngine* engine, QString jsonPrefix = QString(), bool skipDefaults = true, + RecurseOctreeToJSONOperator(const OctreeElementPointer&, ScriptEngine* engine, QString jsonPrefix = QString(), bool skipDefaults = true, bool skipThoseWithBadParents = false); virtual bool preRecursion(const OctreeElementPointer& element) override { return true; }; virtual bool postRecursion(const OctreeElementPointer& element) override; @@ -23,8 +28,8 @@ class RecurseOctreeToJSONOperator : public RecurseOctreeOperator { private: void processEntity(const EntityItemPointer& entity); - QScriptEngine* _engine; - QScriptValue _toStringMethod; + ScriptEngine* _engine; + ScriptValuePointer _toStringMethod; QString _json; const bool _skipDefaults; diff --git a/libraries/entities/src/RecurseOctreeToMapOperator.cpp b/libraries/entities/src/RecurseOctreeToMapOperator.cpp index 5be921112fc..d353e266db1 100644 --- a/libraries/entities/src/RecurseOctreeToMapOperator.cpp +++ b/libraries/entities/src/RecurseOctreeToMapOperator.cpp @@ -15,7 +15,7 @@ RecurseOctreeToMapOperator::RecurseOctreeToMapOperator(QVariantMap& map, const OctreeElementPointer& top, - QScriptEngine* engine, + ScriptEngine* engine, bool skipDefaultValues, bool skipThoseWithBadParents, std::shared_ptr myAvatar) : @@ -56,7 +56,7 @@ bool RecurseOctreeToMapOperator::postRecursion(const OctreeElementPointer& eleme } EntityItemProperties properties = entityItem->getProperties(); - QScriptValue qScriptValues; + ScriptValuePointer qScriptValues; if (_skipDefaultValues) { qScriptValues = EntityItemNonDefaultPropertiesToScriptValue(_engine, properties); } else { @@ -70,11 +70,11 @@ bool RecurseOctreeToMapOperator::postRecursion(const OctreeElementPointer& eleme auto jointNames = _myAvatar->getJointNames(); auto parentJointIndex = entityItem->getParentJointIndex(); if (parentJointIndex < jointNames.count()) { - qScriptValues.setProperty("parentJointName", jointNames.at(parentJointIndex)); + qScriptValues->setProperty("parentJointName", jointNames.at(parentJointIndex)); } } - entitiesQList << qScriptValues.toVariant(); + entitiesQList << qScriptValues->toVariant(); }); _map["Entities"] = entitiesQList; diff --git a/libraries/entities/src/RecurseOctreeToMapOperator.h b/libraries/entities/src/RecurseOctreeToMapOperator.h index 985ec9de35c..9fbd9f41b29 100644 --- a/libraries/entities/src/RecurseOctreeToMapOperator.h +++ b/libraries/entities/src/RecurseOctreeToMapOperator.h @@ -11,16 +11,18 @@ #include "EntityTree.h" +class ScriptEngine; + class RecurseOctreeToMapOperator : public RecurseOctreeOperator { public: - RecurseOctreeToMapOperator(QVariantMap& map, const OctreeElementPointer& top, QScriptEngine* engine, bool skipDefaultValues, + RecurseOctreeToMapOperator(QVariantMap& map, const OctreeElementPointer& top, ScriptEngine* engine, bool skipDefaultValues, bool skipThoseWithBadParents, std::shared_ptr myAvatar); bool preRecursion(const OctreeElementPointer& element) override; bool postRecursion(const OctreeElementPointer& element) override; private: QVariantMap& _map; OctreeElementPointer _top; - QScriptEngine* _engine; + ScriptEngine* _engine; bool _withinTop; bool _skipDefaultValues; bool _skipThoseWithBadParents; diff --git a/libraries/entities/src/RingGizmoPropertyGroup.cpp b/libraries/entities/src/RingGizmoPropertyGroup.cpp index 387cb1d688e..1dcc443609f 100644 --- a/libraries/entities/src/RingGizmoPropertyGroup.cpp +++ b/libraries/entities/src/RingGizmoPropertyGroup.cpp @@ -20,8 +20,8 @@ const float RingGizmoPropertyGroup::MAX_ALPHA = 1.0f; const float RingGizmoPropertyGroup::MIN_RADIUS = 0.0f; const float RingGizmoPropertyGroup::MAX_RADIUS = 0.5f; -void RingGizmoPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, - QScriptEngine* engine, bool skipDefaults, +void RingGizmoPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValuePointer& properties, + ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_START_ANGLE, Ring, ring, StartAngle, startAngle); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_END_ANGLE, Ring, ring, EndAngle, endAngle); @@ -46,7 +46,7 @@ void RingGizmoPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desire COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_MINOR_TICK_MARKS_COLOR, Ring, ring, MinorTickMarksColor, minorTickMarksColor, u8vec3Color); } -void RingGizmoPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { +void RingGizmoPropertyGroup::copyFromScriptValue(const ScriptValuePointer& object, bool& _defaultSettings) { COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ring, startAngle, float, setStartAngle); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ring, endAngle, float, setEndAngle); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ring, innerRadius, float, setInnerRadius); diff --git a/libraries/entities/src/RingGizmoPropertyGroup.h b/libraries/entities/src/RingGizmoPropertyGroup.h index 1f23152fc71..064ef79f271 100644 --- a/libraries/entities/src/RingGizmoPropertyGroup.h +++ b/libraries/entities/src/RingGizmoPropertyGroup.h @@ -11,7 +11,7 @@ #include -#include +#include #include "PropertyGroup.h" #include "EntityItemPropertiesMacros.h" @@ -21,6 +21,9 @@ class EntityItemProperties; class EncodeBitstreamParams; class OctreePacketData; class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; using u8vec3Color = glm::u8vec3; @@ -56,10 +59,10 @@ using u8vec3Color = glm::u8vec3; class RingGizmoPropertyGroup : public PropertyGroup { public: // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, - QScriptEngine* engine, bool skipDefaults, + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValuePointer& properties, + ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const override; - virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + virtual void copyFromScriptValue(const ScriptValuePointer& object, bool& _defaultSettings) override; void merge(const RingGizmoPropertyGroup& other); diff --git a/libraries/entities/src/SkyboxPropertyGroup.cpp b/libraries/entities/src/SkyboxPropertyGroup.cpp index 89ffa95dbe9..4cd16c75791 100644 --- a/libraries/entities/src/SkyboxPropertyGroup.cpp +++ b/libraries/entities/src/SkyboxPropertyGroup.cpp @@ -18,12 +18,12 @@ const glm::u8vec3 SkyboxPropertyGroup::DEFAULT_COLOR = { 0, 0, 0 }; -void SkyboxPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { +void SkyboxPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValuePointer& properties, ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_SKYBOX_COLOR, Skybox, skybox, Color, color, u8vec3Color); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_SKYBOX_URL, Skybox, skybox, URL, url); } -void SkyboxPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { +void SkyboxPropertyGroup::copyFromScriptValue(const ScriptValuePointer& object, bool& _defaultSettings) { COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(skybox, color, u8vec3Color, setColor); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(skybox, url, QString, setURL); } diff --git a/libraries/entities/src/SkyboxPropertyGroup.h b/libraries/entities/src/SkyboxPropertyGroup.h index 0c128aa7308..20d8663c73f 100644 --- a/libraries/entities/src/SkyboxPropertyGroup.h +++ b/libraries/entities/src/SkyboxPropertyGroup.h @@ -16,7 +16,7 @@ #include -#include +#include #include @@ -28,6 +28,9 @@ class EncodeBitstreamParams; class OctreePacketData; class EntityTreeElementExtraEncodeData; class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; /**jsdoc * A skybox is defined by the following properties: @@ -39,10 +42,10 @@ class ReadBitstreamToTreeParams; class SkyboxPropertyGroup : public PropertyGroup { public: // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, - QScriptEngine* engine, bool skipDefaults, + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValuePointer& properties, + ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const override; - virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + virtual void copyFromScriptValue(const ScriptValuePointer& object, bool& _defaultSettings) override; void merge(const SkyboxPropertyGroup& other); diff --git a/libraries/graphics-scripting/src/graphics-scripting/Forward.h b/libraries/graphics-scripting/src/graphics-scripting/Forward.h index 29f80794223..89fd8d353a2 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/Forward.h +++ b/libraries/graphics-scripting/src/graphics-scripting/Forward.h @@ -22,7 +22,6 @@ using ModelPointer = std::shared_ptr; namespace gpu { class BufferView; } -class QScriptEngine; namespace scriptable { using Mesh = graphics::Mesh; diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index a27eff9cbee..197e71a5540 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -17,15 +17,16 @@ #include "ScriptableMeshPart.h" #include #include -#include -#include -#include +#include +#include +#include +#include #include #include #include #include -GraphicsScriptingInterface::GraphicsScriptingInterface(QObject* parent) : QObject(parent), QScriptable() { +GraphicsScriptingInterface::GraphicsScriptingInterface(QObject* parent) : QObject(parent), Scriptable() { } void GraphicsScriptingInterface::jsThrowError(const QString& error) { @@ -314,17 +315,17 @@ namespace { } namespace scriptable { - template int registerQPointerMetaType(QScriptEngine* engine) { + template int registerQPointerMetaType(ScriptEngine* engine) { qScriptRegisterSequenceMetaType>>(engine); return qScriptRegisterMetaType>( engine, - [](QScriptEngine* engine, const QPointer& object) -> QScriptValue { + [](ScriptEngine* engine, const QPointer& object) -> ScriptValuePointer { if (!object) { - return QScriptValue::NullValue; + return ScriptValuePointer::NullValue; } - return engine->newQObject(object, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::AutoCreateDynamicProperties); + return engine->newQObject(object, ScriptEngine::QtOwnership, ScriptEngine::ExcludeDeleteLater | ScriptEngine::AutoCreateDynamicProperties); }, - [](const QScriptValue& value, QPointer& out) { + [](const ScriptValuePointer& value, QPointer& out) { auto obj = value.toQObject(); #ifdef SCRIPTABLE_MESH_DEBUG qCInfo(graphics_scripting) << "qpointer_qobject_cast" << obj << value.toString(); @@ -347,12 +348,12 @@ namespace scriptable { ); } - QScriptValue qVectorScriptableMaterialLayerToScriptValue(QScriptEngine* engine, const QVector& vector) { - return qScriptValueFromSequence(engine, vector); + ScriptValuePointer qVectorScriptableMaterialLayerToScriptValue(ScriptEngine* engine, const QVector& vector) { + return scriptValueFromSequence(engine, vector); } - void qVectorScriptableMaterialLayerFromScriptValue(const QScriptValue& array, QVector& result) { - qScriptValueToSequence(array, result); + void qVectorScriptableMaterialLayerFromScriptValue(const ScriptValuePointer& array, QVector& result) { + scriptValueToSequence(array, result); } /**jsdoc @@ -469,204 +470,204 @@ namespace scriptable { * @property {boolean} defaultFallthrough - true if all properties fall through to the material below unless * they are set, false if properties respect their individual fall-through settings. */ - QScriptValue scriptableMaterialToScriptValue(QScriptEngine* engine, const scriptable::ScriptableMaterial &material) { - QScriptValue obj = engine->newObject(); - obj.setProperty("name", material.name); - obj.setProperty("model", material.model); + ScriptValuePointer scriptableMaterialToScriptValue(ScriptEngine* engine, const scriptable::ScriptableMaterial &material) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty("name", material.name); + obj->setProperty("model", material.model); bool hasPropertyFallthroughs = !material.propertyFallthroughs.empty(); - const QScriptValue FALLTHROUGH("fallthrough"); + const ScriptValuePointer FALLTHROUGH("fallthrough"); if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::OPACITY_VAL_BIT)) { - obj.setProperty("opacity", FALLTHROUGH); + obj->setProperty("opacity", FALLTHROUGH); } else if (material.key.isTranslucentFactor()) { - obj.setProperty("opacity", material.opacity); + obj->setProperty("opacity", material.opacity); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::ALBEDO_VAL_BIT)) { - obj.setProperty("albedo", FALLTHROUGH); + obj->setProperty("albedo", FALLTHROUGH); } else if (material.key.isAlbedo()) { - obj.setProperty("albedo", vec3ColorToScriptValue(engine, material.albedo)); + obj->setProperty("albedo", vec3ColorToScriptValue(engine, material.albedo)); } if (material.model.toStdString() == graphics::Material::HIFI_PBR) { if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::OPACITY_CUTOFF_VAL_BIT)) { - obj.setProperty("opacityCutoff", FALLTHROUGH); + obj->setProperty("opacityCutoff", FALLTHROUGH); } else if (material.key.isOpacityCutoff()) { - obj.setProperty("opacityCutoff", material.opacityCutoff); + obj->setProperty("opacityCutoff", material.opacityCutoff); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::OPACITY_MAP_MODE_BIT)) { - obj.setProperty("opacityMapMode", FALLTHROUGH); + obj->setProperty("opacityMapMode", FALLTHROUGH); } else if (material.key.isOpacityMapMode()) { - obj.setProperty("opacityMapMode", material.opacityMapMode); + obj->setProperty("opacityMapMode", material.opacityMapMode); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::GLOSSY_VAL_BIT)) { - obj.setProperty("roughness", FALLTHROUGH); + obj->setProperty("roughness", FALLTHROUGH); } else if (material.key.isGlossy()) { - obj.setProperty("roughness", material.roughness); + obj->setProperty("roughness", material.roughness); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_VAL_BIT)) { - obj.setProperty("metallic", FALLTHROUGH); + obj->setProperty("metallic", FALLTHROUGH); } else if (material.key.isMetallic()) { - obj.setProperty("metallic", material.metallic); + obj->setProperty("metallic", material.metallic); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_VAL_BIT)) { - obj.setProperty("scattering", FALLTHROUGH); + obj->setProperty("scattering", FALLTHROUGH); } else if (material.key.isScattering()) { - obj.setProperty("scattering", material.scattering); + obj->setProperty("scattering", material.scattering); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::UNLIT_VAL_BIT)) { - obj.setProperty("unlit", FALLTHROUGH); + obj->setProperty("unlit", FALLTHROUGH); } else if (material.key.isUnlit()) { - obj.setProperty("unlit", material.unlit); + obj->setProperty("unlit", material.unlit); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::EMISSIVE_VAL_BIT)) { - obj.setProperty("emissive", FALLTHROUGH); + obj->setProperty("emissive", FALLTHROUGH); } else if (material.key.isEmissive()) { - obj.setProperty("emissive", vec3ColorToScriptValue(engine, material.emissive)); + obj->setProperty("emissive", vec3ColorToScriptValue(engine, material.emissive)); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::EMISSIVE_MAP_BIT)) { - obj.setProperty("emissiveMap", FALLTHROUGH); + obj->setProperty("emissiveMap", FALLTHROUGH); } else if (!material.emissiveMap.isEmpty()) { - obj.setProperty("emissiveMap", material.emissiveMap); + obj->setProperty("emissiveMap", material.emissiveMap); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::ALBEDO_MAP_BIT)) { - obj.setProperty("albedoMap", FALLTHROUGH); + obj->setProperty("albedoMap", FALLTHROUGH); } else if (!material.albedoMap.isEmpty()) { - obj.setProperty("albedoMap", material.albedoMap); + obj->setProperty("albedoMap", material.albedoMap); } if (!material.opacityMap.isEmpty()) { - obj.setProperty("opacityMap", material.opacityMap); + obj->setProperty("opacityMap", material.opacityMap); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::OCCLUSION_MAP_BIT)) { - obj.setProperty("occlusionMap", FALLTHROUGH); + obj->setProperty("occlusionMap", FALLTHROUGH); } else if (!material.occlusionMap.isEmpty()) { - obj.setProperty("occlusionMap", material.occlusionMap); + obj->setProperty("occlusionMap", material.occlusionMap); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::LIGHT_MAP_BIT)) { - obj.setProperty("lightMap", FALLTHROUGH); + obj->setProperty("lightMap", FALLTHROUGH); } else if (!material.lightMap.isEmpty()) { - obj.setProperty("lightMap", material.lightMap); + obj->setProperty("lightMap", material.lightMap); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_MAP_BIT)) { - obj.setProperty("scatteringMap", FALLTHROUGH); + obj->setProperty("scatteringMap", FALLTHROUGH); } else if (!material.scatteringMap.isEmpty()) { - obj.setProperty("scatteringMap", material.scatteringMap); + obj->setProperty("scatteringMap", material.scatteringMap); } // Only set one of each of these if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_MAP_BIT)) { - obj.setProperty("metallicMap", FALLTHROUGH); + obj->setProperty("metallicMap", FALLTHROUGH); } else if (!material.metallicMap.isEmpty()) { - obj.setProperty("metallicMap", material.metallicMap); + obj->setProperty("metallicMap", material.metallicMap); } else if (!material.specularMap.isEmpty()) { - obj.setProperty("specularMap", material.specularMap); + obj->setProperty("specularMap", material.specularMap); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::ROUGHNESS_MAP_BIT)) { - obj.setProperty("roughnessMap", FALLTHROUGH); + obj->setProperty("roughnessMap", FALLTHROUGH); } else if (!material.roughnessMap.isEmpty()) { - obj.setProperty("roughnessMap", material.roughnessMap); + obj->setProperty("roughnessMap", material.roughnessMap); } else if (!material.glossMap.isEmpty()) { - obj.setProperty("glossMap", material.glossMap); + obj->setProperty("glossMap", material.glossMap); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::NORMAL_MAP_BIT)) { - obj.setProperty("normalMap", FALLTHROUGH); + obj->setProperty("normalMap", FALLTHROUGH); } else if (!material.normalMap.isEmpty()) { - obj.setProperty("normalMap", material.normalMap); + obj->setProperty("normalMap", material.normalMap); } else if (!material.bumpMap.isEmpty()) { - obj.setProperty("bumpMap", material.bumpMap); + obj->setProperty("bumpMap", material.bumpMap); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::TEXCOORDTRANSFORM0)) { - obj.setProperty("texCoordTransform0", FALLTHROUGH); + obj->setProperty("texCoordTransform0", FALLTHROUGH); } else if (material.texCoordTransforms[0] != mat4()) { - obj.setProperty("texCoordTransform0", mat4toScriptValue(engine, material.texCoordTransforms[0])); + obj->setProperty("texCoordTransform0", mat4toScriptValue(engine, material.texCoordTransforms[0])); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::TEXCOORDTRANSFORM1)) { - obj.setProperty("texCoordTransform1", FALLTHROUGH); + obj->setProperty("texCoordTransform1", FALLTHROUGH); } else if (material.texCoordTransforms[1] != mat4()) { - obj.setProperty("texCoordTransform1", mat4toScriptValue(engine, material.texCoordTransforms[1])); + obj->setProperty("texCoordTransform1", mat4toScriptValue(engine, material.texCoordTransforms[1])); } // These need to be implemented, but set the fallthrough for now if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::LIGHTMAP_PARAMS)) { - obj.setProperty("lightmapParams", FALLTHROUGH); + obj->setProperty("lightmapParams", FALLTHROUGH); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::MATERIAL_PARAMS)) { - obj.setProperty("materialParams", FALLTHROUGH); + obj->setProperty("materialParams", FALLTHROUGH); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::CULL_FACE_MODE)) { - obj.setProperty("cullFaceMode", FALLTHROUGH); + obj->setProperty("cullFaceMode", FALLTHROUGH); } else if (!material.cullFaceMode.isEmpty()) { - obj.setProperty("cullFaceMode", material.cullFaceMode); + obj->setProperty("cullFaceMode", material.cullFaceMode); } } else if (material.model.toStdString() == graphics::Material::HIFI_SHADER_SIMPLE) { - obj.setProperty("procedural", material.procedural); + obj->setProperty("procedural", material.procedural); } - obj.setProperty("defaultFallthrough", material.defaultFallthrough); + obj->setProperty("defaultFallthrough", material.defaultFallthrough); return obj; } - void scriptableMaterialFromScriptValue(const QScriptValue &object, scriptable::ScriptableMaterial& material) { - // No need to convert from QScriptValue to ScriptableMaterial + void scriptableMaterialFromScriptValue(const ScriptValuePointer &object, scriptable::ScriptableMaterial& material) { + // No need to convert from ScriptValuePointer to ScriptableMaterial } - QScriptValue scriptableMaterialLayerToScriptValue(QScriptEngine* engine, const scriptable::ScriptableMaterialLayer &materialLayer) { - QScriptValue obj = engine->newObject(); - obj.setProperty("material", scriptableMaterialToScriptValue(engine, materialLayer.material)); - obj.setProperty("priority", materialLayer.priority); + ScriptValuePointer scriptableMaterialLayerToScriptValue(ScriptEngine* engine, const scriptable::ScriptableMaterialLayer &materialLayer) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty("material", scriptableMaterialToScriptValue(engine, materialLayer.material)); + obj->setProperty("priority", materialLayer.priority); return obj; } - void scriptableMaterialLayerFromScriptValue(const QScriptValue &object, scriptable::ScriptableMaterialLayer& materialLayer) { - // No need to convert from QScriptValue to ScriptableMaterialLayer + void scriptableMaterialLayerFromScriptValue(const ScriptValuePointer &object, scriptable::ScriptableMaterialLayer& materialLayer) { + // No need to convert from ScriptValuePointer to ScriptableMaterialLayer } - QScriptValue multiMaterialMapToScriptValue(QScriptEngine* engine, const scriptable::MultiMaterialMap& map) { - QScriptValue obj = engine->newObject(); + ScriptValuePointer multiMaterialMapToScriptValue(ScriptEngine* engine, const scriptable::MultiMaterialMap& map) { + ScriptValuePointer obj = engine->newObject(); for (auto key : map.keys()) { - obj.setProperty(key, qVectorScriptableMaterialLayerToScriptValue(engine, map[key])); + obj->setProperty(key, qVectorScriptableMaterialLayerToScriptValue(engine, map[key])); } return obj; } - void multiMaterialMapFromScriptValue(const QScriptValue& map, scriptable::MultiMaterialMap& result) { - // No need to convert from QScriptValue to MultiMaterialMap + void multiMaterialMapFromScriptValue(const ScriptValuePointer& map, scriptable::MultiMaterialMap& result) { + // No need to convert from ScriptValuePointer to MultiMaterialMap } - template int registerDebugEnum(QScriptEngine* engine, const DebugEnums& debugEnums) { + template int registerDebugEnum(ScriptEngine* engine, const DebugEnums& debugEnums) { static const DebugEnums& instance = debugEnums; return qScriptRegisterMetaType( engine, - [](QScriptEngine* engine, const T& topology) -> QScriptValue { + [](ScriptEngine* engine, const T& topology) -> ScriptValuePointer { return instance.value(topology); }, - [](const QScriptValue& value, T& topology) { + [](const ScriptValuePointer& value, T& topology) { topology = instance.key(value.toString()); } ); } } -void GraphicsScriptingInterface::registerMetaTypes(QScriptEngine* engine) { - qScriptRegisterSequenceMetaType>(engine); +void GraphicsScriptingInterface::registerMetaTypes(ScriptEngine* engine) { + scriptRegisterSequenceMetaType>(engine); scriptable::registerQPointerMetaType(engine); scriptable::registerQPointerMetaType(engine); @@ -677,10 +678,10 @@ void GraphicsScriptingInterface::registerMetaTypes(QScriptEngine* engine) { scriptable::registerDebugEnum(engine, gpu::SEMANTICS); scriptable::registerDebugEnum(engine, gpu::DIMENSIONS); - qScriptRegisterMetaType(engine, scriptable::scriptableMaterialToScriptValue, scriptable::scriptableMaterialFromScriptValue); - qScriptRegisterMetaType(engine, scriptable::scriptableMaterialLayerToScriptValue, scriptable::scriptableMaterialLayerFromScriptValue); - qScriptRegisterMetaType(engine, scriptable::qVectorScriptableMaterialLayerToScriptValue, scriptable::qVectorScriptableMaterialLayerFromScriptValue); - qScriptRegisterMetaType(engine, scriptable::multiMaterialMapToScriptValue, scriptable::multiMaterialMapFromScriptValue); + scriptRegisterMetaType(engine, scriptable::scriptableMaterialToScriptValue, scriptable::scriptableMaterialFromScriptValue); + scriptRegisterMetaType(engine, scriptable::scriptableMaterialLayerToScriptValue, scriptable::scriptableMaterialLayerFromScriptValue); + scriptRegisterMetaType(engine, scriptable::qVectorScriptableMaterialLayerToScriptValue, scriptable::qVectorScriptableMaterialLayerFromScriptValue); + scriptRegisterMetaType(engine, scriptable::multiMaterialMapToScriptValue, scriptable::multiMaterialMapFromScriptValue); Q_UNUSED(metaTypeIds); } diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h index 0f24ae84613..1a47f4c78d8 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h @@ -14,12 +14,12 @@ #include #include -#include -#include - #include "ScriptableMesh.h" #include #include "RegisteredMetaTypes.h" +#include + +class ScriptEngine; /**jsdoc @@ -34,11 +34,11 @@ * @hifi-avatar */ -class GraphicsScriptingInterface : public QObject, public QScriptable, public Dependency { +class GraphicsScriptingInterface : public QObject, public Scriptable, public Dependency { Q_OBJECT public: - static void registerMetaTypes(QScriptEngine* engine); + static void registerMetaTypes(ScriptEngine* engine); GraphicsScriptingInterface(QObject* parent = nullptr); public slots: @@ -149,7 +149,7 @@ public slots: }; namespace scriptable { - QScriptValue scriptableMaterialToScriptValue(QScriptEngine* engine, const scriptable::ScriptableMaterial &material); + ScriptValuePointer scriptableMaterialToScriptValue(ScriptEngine* engine, const scriptable::ScriptableMaterial &material); }; Q_DECLARE_METATYPE(NestableType) diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.cpp index 92dddc953e7..b5671655b36 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.cpp @@ -7,11 +7,12 @@ #include "GraphicsScriptingUtil.h" -#include - #include #include #include +#include "ScriptContext.h" +#include "ScriptEngine.h" +#include "ScriptValue.h" using buffer_helpers::glmVecToVariant; @@ -77,27 +78,27 @@ QVariant toVariant(const gpu::Element& element) { }; } -QScriptValue jsBindCallback(QScriptValue value) { - if (value.isObject() && value.property("callback").isFunction()) { +ScriptValuePointer jsBindCallback(ScriptValuePointer value) { + if (value->isObject() && value->property("callback")->isFunction()) { // value is already a bound callback return value; } - auto engine = value.engine(); + auto engine = value->engine(); auto context = engine ? engine->currentContext() : nullptr; auto length = context ? context->argumentCount() : 0; - QScriptValue scope = context ? context->thisObject() : QScriptValue::NullValue; - QScriptValue method; + ScriptValuePointer scope = context ? context->thisObject() : engine->nullValue(); + ScriptValuePointer method; #ifdef SCRIPTABLE_MESH_DEBUG qCInfo(graphics_scripting) << "jsBindCallback" << engine << length << scope.toQObject() << method.toString(); #endif // find position in the incoming JS Function.arguments array (so we can test for the two-argument case) for (int i = 0; context && i < length; i++) { - if (context->argument(i).strictlyEquals(value)) { + if (context->argument(i)->strictlyEquals(value)) { method = context->argument(i+1); } } - if (method.isFunction() || method.isString()) { + if (method->isFunction() || method->isString()) { // interpret as `API.func(..., scope, function callback(){})` or `API.func(..., scope, "methodName")` scope = value; } else { @@ -111,9 +112,9 @@ QScriptValue jsBindCallback(QScriptValue value) { } template -T this_qobject_cast(QScriptEngine* engine) { +T this_qobject_cast(ScriptEngine* engine) { auto context = engine ? engine->currentContext() : nullptr; - return qscriptvalue_cast(context ? context->thisObject() : QScriptValue::NullValue); + return scriptvalue_cast(context ? context->thisObject() : ScriptValuePointer::NullValue); } QString toDebugString(QObject* tmp) { QString s; diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h index 1ca62277ff5..ec979045529 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h @@ -1,8 +1,7 @@ #pragma once -#include -#include #include +#include #include #include #include @@ -10,6 +9,10 @@ #include #include +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; + class Extents; class AABox; namespace gpu { @@ -24,15 +27,15 @@ namespace scriptable { QVariant toVariant(const glm::mat4& mat4); // helper that automatically resolves Qt-signal-like scoped callbacks - // ... C++ side: `void MyClass::asyncMethod(..., QScriptValue callback)` + // ... C++ side: `void MyClass::asyncMethod(..., ScriptValuePointer callback)` // ... JS side: // * `API.asyncMethod(..., function(){})` // * `API.asyncMethod(..., scope, function(){})` // * `API.asyncMethod(..., scope, "methodName")` - QScriptValue jsBindCallback(QScriptValue callback); + ScriptValuePointer jsBindCallback(ScriptValuePointer callback); // cast engine->thisObject() => C++ class instance - template T this_qobject_cast(QScriptEngine* engine); + template T this_qobject_cast(ScriptEngine* engine); QString toDebugString(QObject* tmp); template QString toDebugString(std::shared_ptr tmp); diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp index 3de5119fa76..7c85d917953 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp @@ -7,17 +7,18 @@ #include "ScriptableMesh.h" -#include +#include #include #include #include -#include #include #include #include #include +#include +#include #include "Forward.h" #include "ScriptableMeshPart.h" @@ -27,7 +28,7 @@ // #define SCRIPTABLE_MESH_DEBUG 1 scriptable::ScriptableMesh::ScriptableMesh(const ScriptableMeshBase& other) - : ScriptableMeshBase(other), QScriptable() { + : ScriptableMeshBase(other), Scriptable() { auto mesh = getMeshPointer(); QString name = mesh ? QString::fromStdString(mesh->modelName) : ""; if (name.isEmpty()) { @@ -264,7 +265,7 @@ bool scriptable::ScriptableMesh::setVertexProperty(glm::uint32 vertexIndex, cons * @param {number} index - The vertex index. * @param {object} properties - The properties of the mesh, per {@link GraphicsMesh}. */ -glm::uint32 scriptable::ScriptableMesh::forEachVertex(QScriptValue _callback) { +glm::uint32 scriptable::ScriptableMesh::forEachVertex(ScriptValuePointer _callback) { auto mesh = getMeshPointer(); if (!mesh) { return 0; @@ -272,16 +273,16 @@ glm::uint32 scriptable::ScriptableMesh::forEachVertex(QScriptValue _callback) { auto scopedHandler = jsBindCallback(_callback); // destructure so we can still invoke callback scoped, but with a custom signature (obj, i, jsMesh) - auto scope = scopedHandler.property("scope"); - auto callback = scopedHandler.property("callback"); - auto js = engine() ? engine() : scopedHandler.engine(); // cache value to avoid resolving each iteration + auto scope = scopedHandler->property("scope"); + auto callback = scopedHandler->property("callback"); + auto js = engine() ? engine() : scopedHandler->engine(); // cache value to avoid resolving each iteration if (!js) { return 0; } - auto meshPart = js ? js->toScriptValue(getSelf()) : QScriptValue::NullValue; + auto meshPart = js ? js->toScriptValue(getSelf()) : js->nullValue(); int numProcessed = 0; buffer_helpers::mesh::forEachVertex(mesh, [&](glm::uint32 index, const QVariantMap& values) { - auto result = callback.call(scope, { js->toScriptValue(values), index, meshPart }); + auto result = callback->call(scope, { js->toScriptValue(values), index, meshPart }); if (js->hasUncaughtException()) { js->currentContext()->throwValue(js->uncaughtException()); return false; @@ -302,7 +303,7 @@ glm::uint32 scriptable::ScriptableMesh::forEachVertex(QScriptValue _callback) { * @returns {Object|boolean} The attribute values to update the vertex with, or * false to not update the vertex. */ -glm::uint32 scriptable::ScriptableMesh::updateVertexAttributes(QScriptValue _callback) { +glm::uint32 scriptable::ScriptableMesh::updateVertexAttributes(ScriptValuePointer _callback) { auto mesh = getMeshPointer(); if (!mesh) { return 0; @@ -310,18 +311,18 @@ glm::uint32 scriptable::ScriptableMesh::updateVertexAttributes(QScriptValue _cal auto scopedHandler = jsBindCallback(_callback); // destructure so we can still invoke callback scoped, but with a custom signature (obj, i, jsMesh) - auto scope = scopedHandler.property("scope"); - auto callback = scopedHandler.property("callback"); - auto js = engine() ? engine() : scopedHandler.engine(); // cache value to avoid resolving each iteration + auto scope = scopedHandler->property("scope"); + auto callback = scopedHandler->property("callback"); + auto js = engine() ? engine() : scopedHandler->engine(); // cache value to avoid resolving each iteration if (!js) { return 0; } - auto meshPart = js ? js->toScriptValue(getSelf()) : QScriptValue::NullValue; + auto meshPart = js ? js->toScriptValue(getSelf()) : js->nullValue(); int numProcessed = 0; auto attributeViews = buffer_helpers::mesh::getAllBufferViews(mesh); buffer_helpers::mesh::forEachVertex(mesh, [&](glm::uint32 index, const QVariantMap& values) { auto obj = js->toScriptValue(values); - auto result = callback.call(scope, { obj, index, meshPart }); + auto result = callback->call(scope, { obj, index, meshPart }); if (js->hasUncaughtException()) { js->currentContext()->throwValue(js->uncaughtException()); return false; @@ -335,9 +336,9 @@ glm::uint32 scriptable::ScriptableMesh::updateVertexAttributes(QScriptValue _cal obj = result; } for (const auto& a : attributeViews) { - const auto& attribute = obj.property(a.first); - if (attribute.isValid()) { - buffer_helpers::setValue(a.second, index, attribute.toVariant()); + const auto& attribute = obj->property(a.first); + if (attribute->isValid()) { + buffer_helpers::setValue(a.second, index, attribute->toVariant()); } } numProcessed++; diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h index 0e7eecc03b9..0c431bda305 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h @@ -20,12 +20,15 @@ #include #include #include -#include -#include +#include #include "GraphicsScriptingUtil.h" #include +#include + +class ScriptValue; +using ScriptValuePointer = QSharedPointer; namespace scriptable { /**jsdoc @@ -65,7 +68,7 @@ namespace scriptable { * @borrows GraphicsMesh.getVertextAttributes as getVertextAttributes * @borrows GraphicsMesh.setVertextAttributes as setVertextAttributes */ - class ScriptableMesh : public ScriptableMeshBase, QScriptable { + class ScriptableMesh : public ScriptableMeshBase, Scriptable { Q_OBJECT public: Q_PROPERTY(glm::uint32 numParts READ getNumParts) @@ -83,11 +86,11 @@ namespace scriptable { operator const ScriptableMeshBase*() const { return (qobject_cast(this)); } ScriptableMesh(WeakModelProviderPointer provider, ScriptableModelBasePointer model, MeshPointer mesh, QObject* parent) - : ScriptableMeshBase(provider, model, mesh, parent), QScriptable() { strongMesh = mesh; } + : ScriptableMeshBase(provider, model, mesh, parent), Scriptable() { strongMesh = mesh; } ScriptableMesh(MeshPointer mesh, QObject* parent) - : ScriptableMeshBase(WeakModelProviderPointer(), nullptr, mesh, parent), QScriptable() { strongMesh = mesh; } + : ScriptableMeshBase(WeakModelProviderPointer(), nullptr, mesh, parent), Scriptable() { strongMesh = mesh; } ScriptableMesh(const ScriptableMeshBase& other); - ScriptableMesh(const ScriptableMesh& other) : ScriptableMeshBase(other), QScriptable() {}; + ScriptableMesh(const ScriptableMesh& other) : ScriptableMeshBase(other), Scriptable() {}; virtual ~ScriptableMesh(); const scriptable::MeshPointer getOwnedMeshPointer() const { return strongMesh; } @@ -224,7 +227,7 @@ namespace scriptable { */ scriptable::ScriptableMeshPointer cloneMesh(); - // QScriptEngine-specific wrappers + // ScriptEngine-specific wrappers /**jsdoc * Updates vertex attributes by calling a function for each vertex. The function can return modified attributes to @@ -233,7 +236,7 @@ namespace scriptable { * @param {GraphicsMesh~updateVertexAttributesCallback} callback - The function to call for each vertex. * @returns {number} The number of vertices the callback was called for. */ - glm::uint32 updateVertexAttributes(QScriptValue callback); + glm::uint32 updateVertexAttributes(ScriptValuePointer callback); /**jsdoc * Calls a function for each vertex. @@ -241,7 +244,7 @@ namespace scriptable { * @param {GraphicsMesh~forEachVertexCallback} callback - The function to call for each vertex. * @returns {number} The number of vertices the callback was called for. */ - glm::uint32 forEachVertex(QScriptValue callback); + glm::uint32 forEachVertex(ScriptValuePointer callback); /**jsdoc * Checks if an index is valid and, optionally, that vertex has a particular attribute. diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp index f14c63b5606..d12dc0c60cc 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp @@ -11,8 +11,7 @@ #include #include -#include -#include +#include #include #include #include @@ -78,12 +77,12 @@ QVariantList scriptable::ScriptableMeshPart::queryVertexAttributes(QVariant sele return parentMesh->queryVertexAttributes(selector); } -glm::uint32 scriptable::ScriptableMeshPart::forEachVertex(QScriptValue _callback) { +glm::uint32 scriptable::ScriptableMeshPart::forEachVertex(ScriptValuePointer _callback) { // TODO: limit to vertices within the part's indexed range? return isValid() ? parentMesh->forEachVertex(_callback) : 0; } -glm::uint32 scriptable::ScriptableMeshPart::updateVertexAttributes(QScriptValue _callback) { +glm::uint32 scriptable::ScriptableMeshPart::updateVertexAttributes(ScriptValuePointer _callback) { // TODO: limit to vertices within the part's indexed range? return isValid() ? parentMesh->updateVertexAttributes(_callback) : 0; } diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.h index 878b239f3d2..0d65d4f609f 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.h @@ -7,7 +7,13 @@ #pragma once +#include + #include "ScriptableMesh.h" +#include + +class ScriptValue; +using ScriptValuePointer = QSharedPointer; namespace scriptable { /**jsdoc @@ -55,7 +61,7 @@ namespace scriptable { * @borrows GraphicsMesh.getVertexAttributes as getVertextAttributes * @borrows GraphicsMesh.setVertexAttributes as setVertextAttributes */ - class ScriptableMeshPart : public QObject, QScriptable { + class ScriptableMeshPart : public QObject, Scriptable { Q_OBJECT Q_PROPERTY(bool valid READ isValid) Q_PROPERTY(glm::uint32 partIndex MEMBER partIndex CONSTANT) @@ -78,7 +84,7 @@ namespace scriptable { public: ScriptableMeshPart(scriptable::ScriptableMeshPointer parentMesh, int partIndex); ScriptableMeshPart& operator=(const ScriptableMeshPart& view) { parentMesh=view.parentMesh; return *this; }; - ScriptableMeshPart(const ScriptableMeshPart& other) : QObject(other.parent()), QScriptable(), parentMesh(other.parentMesh), partIndex(other.partIndex) {} + ScriptableMeshPart(const ScriptableMeshPart& other) : QObject(other.parent()), Scriptable(), parentMesh(other.parentMesh), partIndex(other.partIndex) {} bool isValid() const { auto mesh = getMeshPointer(); return mesh && partIndex < mesh->getNumParts(); } public slots: @@ -273,7 +279,7 @@ namespace scriptable { QString toOBJ(); - // QScriptEngine-specific wrappers + // ScriptEngine-specific wrappers /**jsdoc * Updates vertex attributes by calling a function for each vertex in the whole mesh (i.e., the parent and @@ -282,7 +288,7 @@ namespace scriptable { * @param {GraphicsMesh~updateVertexAttributesCallback} callback - The function to call for each vertex. * @returns {number} The number of vertices the callback was called for. */ - glm::uint32 updateVertexAttributes(QScriptValue callback); + glm::uint32 updateVertexAttributes(ScriptValuePointer callback); /**jsdoc * Calls a function for each vertex in the whole mesh (i.e., parent and mesh parts). @@ -290,7 +296,7 @@ namespace scriptable { * @param {GraphicsMesh~forEachVertexCallback} callback - The function to call for each vertex. * @returns {number} The number of vertices the callback was called for. */ - glm::uint32 forEachVertex(QScriptValue callback); + glm::uint32 forEachVertex(ScriptValuePointer callback); /**jsdoc * Checks if an index is valid and, optionally, that vertex has a particular attribute. diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp index 28cd49e7c47..04de035b77d 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp @@ -10,7 +10,7 @@ #include "ScriptableModel.h" -#include +#include #include "GraphicsScriptingUtil.h" #include "ScriptableMesh.h" @@ -262,7 +262,7 @@ scriptable::ScriptableMeshes scriptable::ScriptableModel::getMeshes() { } #if 0 -glm::uint32 scriptable::ScriptableModel::forEachVertexAttribute(QScriptValue callback) { +glm::uint32 scriptable::ScriptableModel::forEachVertexAttribute(ScriptValuePointer callback) { glm::uint32 result = 0; scriptable::ScriptableMeshes in = getMeshes(); if (in.size()) { diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h index a6f135c3214..f64653135c3 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h @@ -10,8 +10,6 @@ #include "Forward.h" #include "GraphicsScriptingUtil.h" -class QScriptValue; - namespace scriptable { using ScriptableMeshes = QVector; diff --git a/libraries/model-networking/src/model-networking/SimpleMeshProxy.h b/libraries/model-networking/src/model-networking/SimpleMeshProxy.h index 9282557a4fb..58c55a5e1f9 100644 --- a/libraries/model-networking/src/model-networking/SimpleMeshProxy.h +++ b/libraries/model-networking/src/model-networking/SimpleMeshProxy.h @@ -12,10 +12,6 @@ #ifndef hifi_SimpleMeshProxy_h #define hifi_SimpleMeshProxy_h -#include -#include -#include - #include class SimpleMeshProxy : public MeshProxy { diff --git a/libraries/networking/CMakeLists.txt b/libraries/networking/CMakeLists.txt index 9f63f2cb00f..09b0e182de2 100644 --- a/libraries/networking/CMakeLists.txt +++ b/libraries/networking/CMakeLists.txt @@ -1,6 +1,7 @@ set(TARGET_NAME networking) setup_hifi_library(Network) link_hifi_libraries(shared platform) +include_hifi_library_headers(script-engine) target_openssl() target_tbb() diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index bbd743cf955..3df3fce6cd3 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include diff --git a/libraries/networking/src/BaseAssetScriptingInterface.h b/libraries/networking/src/BaseAssetScriptingInterface.h index 7d118e1979d..18ba8c294a4 100644 --- a/libraries/networking/src/BaseAssetScriptingInterface.h +++ b/libraries/networking/src/BaseAssetScriptingInterface.h @@ -9,7 +9,7 @@ // // BaseAssetScriptingInterface contains the engine-agnostic support code that can be used from -// both QML JS and QScriptEngine JS engine implementations +// both QML JS and ScriptEngine JS engine implementations #ifndef hifi_BaseAssetScriptingInterface_h #define hifi_BaseAssetScriptingInterface_h diff --git a/libraries/networking/src/LocationScriptingInterface.cpp b/libraries/networking/src/LocationScriptingInterface.cpp index 39845558a8a..23884cab1e3 100644 --- a/libraries/networking/src/LocationScriptingInterface.cpp +++ b/libraries/networking/src/LocationScriptingInterface.cpp @@ -12,22 +12,25 @@ #include "LocationScriptingInterface.h" #include "AddressManager.h" +#include +#include +#include LocationScriptingInterface* LocationScriptingInterface::getInstance() { static LocationScriptingInterface sharedInstance; return &sharedInstance; } -QScriptValue LocationScriptingInterface::locationGetter(QScriptContext* context, QScriptEngine* engine) { +ScriptValuePointer LocationScriptingInterface::locationGetter(ScriptContext* context, ScriptEngine* engine) { return engine->newQObject(DependencyManager::get().data()); } -QScriptValue LocationScriptingInterface::locationSetter(QScriptContext* context, QScriptEngine* engine) { - const QVariant& argumentVariant = context->argument(0).toVariant(); +ScriptValuePointer LocationScriptingInterface::locationSetter(ScriptContext* context, ScriptEngine* engine) { + const QVariant& argumentVariant = context->argument(0)->toVariant(); // just try and convert the argument to a string, should be a hifi:// address QMetaObject::invokeMethod(DependencyManager::get().data(), "handleLookupString", Q_ARG(const QString&, argumentVariant.toString())); - return QScriptValue::UndefinedValue; + return engine->undefinedValue(); } diff --git a/libraries/networking/src/LocationScriptingInterface.h b/libraries/networking/src/LocationScriptingInterface.h index f1086ffa600..0fc6b6ec724 100644 --- a/libraries/networking/src/LocationScriptingInterface.h +++ b/libraries/networking/src/LocationScriptingInterface.h @@ -12,15 +12,20 @@ #ifndef hifi_LocationScriptingInterface_h #define hifi_LocationScriptingInterface_h -#include +#include + +class ScriptContext; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; class LocationScriptingInterface : public QObject { Q_OBJECT public: static LocationScriptingInterface* getInstance(); - static QScriptValue locationGetter(QScriptContext* context, QScriptEngine* engine); - static QScriptValue locationSetter(QScriptContext* context, QScriptEngine* engine); + static ScriptValuePointer locationGetter(ScriptContext* context, ScriptEngine* engine); + static ScriptValuePointer locationSetter(ScriptContext* context, ScriptEngine* engine); private: LocationScriptingInterface() {}; }; diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 73af38fd369..125640d0b9d 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -28,8 +28,6 @@ #include #include -#include - #include #include "ResourceManager.h" @@ -234,7 +232,7 @@ protected slots: void updateTotalSize(const qint64& deltaSize); - // Prefetches a resource to be held by the QScriptEngine. + // Prefetches a resource to be held by the ScriptEngine. // Left as a protected member so subclasses can overload prefetch // and delegate to it (see TextureCache::prefetch(const QUrl&, int). ScriptableResource* prefetch(const QUrl& url, void* extra, size_t extraHash); @@ -252,10 +250,10 @@ private slots: void clearATPAssets(); protected: - // Prefetches a resource to be held by the QScriptEngine. + // Prefetches a resource to be held by the ScriptEngine. // Pointers created through this method should be owned by the caller, - // which should be a QScriptEngine with ScriptableResource registered, so that - // the QScriptEngine will delete the pointer when it is garbage collected. + // which should be a ScriptEngine with ScriptableResource registered, so that + // the ScriptEngine will delete the pointer when it is garbage collected. // JSDoc is provided on more general function signature. Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url) { return prefetch(url, nullptr, std::numeric_limits::max()); } diff --git a/libraries/physics/CMakeLists.txt b/libraries/physics/CMakeLists.txt index ad4900e4ba1..2d6bbe27b3d 100644 --- a/libraries/physics/CMakeLists.txt +++ b/libraries/physics/CMakeLists.txt @@ -14,5 +14,6 @@ include_hifi_library_headers(gpu) include_hifi_library_headers(hfm) include_hifi_library_headers(model-serializers) include_hifi_library_headers(graphics) +include_hifi_library_headers(script-engine) target_bullet() diff --git a/libraries/pointers/CMakeLists.txt b/libraries/pointers/CMakeLists.txt index e33c76e2495..184e9ae5549 100644 --- a/libraries/pointers/CMakeLists.txt +++ b/libraries/pointers/CMakeLists.txt @@ -2,4 +2,4 @@ set(TARGET_NAME pointers) setup_hifi_library() GroupSources(src) link_hifi_libraries(shared controllers) - +include_hifi_library_headers(script-engine) diff --git a/libraries/recording/CMakeLists.txt b/libraries/recording/CMakeLists.txt index b42a4018f8c..5821688eb06 100644 --- a/libraries/recording/CMakeLists.txt +++ b/libraries/recording/CMakeLists.txt @@ -1,7 +1,7 @@ set(TARGET_NAME recording) # set a default root dir for each of our optional externals if it was not passed -setup_hifi_library(Script) +setup_hifi_library() # use setup_hifi_library macro to setup our project and link appropriate Qt modules link_hifi_libraries(shared networking) diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index 4c444bcd155..fb825d4ecfd 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET_NAME render-utils) # pull in the resources.qrc file qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc") -setup_hifi_library(Gui Network Qml Quick Script) +setup_hifi_library(Gui Network Qml Quick) link_hifi_libraries(shared task ktx gpu shaders graphics graphics-scripting material-networking model-networking render animation model-serializers image procedural) include_hifi_library_headers(audio) include_hifi_library_headers(networking) diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 930f9422603..bf8cef81f33 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -45,7 +45,6 @@ #define SKIN_DQ class AbstractViewStateInterface; -class QScriptEngine; class ViewFrustum; diff --git a/libraries/script-engine-qtscript/CMakeLists.txt b/libraries/script-engine-qtscript/CMakeLists.txt new file mode 100644 index 00000000000..65a80d0ff34 --- /dev/null +++ b/libraries/script-engine-qtscript/CMakeLists.txt @@ -0,0 +1,10 @@ +set(TARGET_NAME script-engine-qtscript) +# FIXME Move undo scripting interface to application and remove Widgets +setup_hifi_library(Gui Script ScriptTools) + +link_hifi_libraries() +include_hifi_library_headers(animation) +include_hifi_library_headers(entities) +include_hifi_library_headers(networking) +include_hifi_library_headers(shared) +include_hifi_library_headers(script-engine) \ No newline at end of file diff --git a/libraries/script-engine/src/ArrayBufferClass.h b/libraries/script-engine-qtscript/src/ArrayBufferClass.h similarity index 95% rename from libraries/script-engine/src/ArrayBufferClass.h rename to libraries/script-engine-qtscript/src/ArrayBufferClass.h index 3255ab30e70..3048ce15c61 100644 --- a/libraries/script-engine/src/ArrayBufferClass.h +++ b/libraries/script-engine-qtscript/src/ArrayBufferClass.h @@ -21,12 +21,12 @@ #include #include -class ScriptEngine; +class ScriptEngineQtScript; class ArrayBufferClass : public QObject, public QScriptClass { Q_OBJECT public: - ArrayBufferClass(ScriptEngine* scriptEngine); + ArrayBufferClass(ScriptEngineQtScript* scriptEngine); QScriptValue newInstance(qint32 size); QScriptValue newInstance(const QByteArray& ba); diff --git a/libraries/script-engine/src/ArrayBufferPrototype.cpp b/libraries/script-engine-qtscript/src/ArrayBufferPrototype.cpp similarity index 98% rename from libraries/script-engine/src/ArrayBufferPrototype.cpp rename to libraries/script-engine-qtscript/src/ArrayBufferPrototype.cpp index d75482aa2e6..2523a47e27e 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.cpp +++ b/libraries/script-engine-qtscript/src/ArrayBufferPrototype.cpp @@ -13,8 +13,8 @@ #include -#include -#include +#include +#include #include "ArrayBufferClass.h" diff --git a/libraries/script-engine/src/ArrayBufferPrototype.h b/libraries/script-engine-qtscript/src/ArrayBufferPrototype.h similarity index 100% rename from libraries/script-engine/src/ArrayBufferPrototype.h rename to libraries/script-engine-qtscript/src/ArrayBufferPrototype.h diff --git a/libraries/script-engine/src/ArrayBufferViewClass.cpp b/libraries/script-engine-qtscript/src/ArrayBufferViewClass.cpp similarity index 92% rename from libraries/script-engine/src/ArrayBufferViewClass.cpp rename to libraries/script-engine-qtscript/src/ArrayBufferViewClass.cpp index cf776ed8340..fa1c114b5be 100644 --- a/libraries/script-engine/src/ArrayBufferViewClass.cpp +++ b/libraries/script-engine-qtscript/src/ArrayBufferViewClass.cpp @@ -10,11 +10,12 @@ // #include "ArrayBufferViewClass.h" +#include "ScriptEngineQtScript.h" Q_DECLARE_METATYPE(QByteArray*) -ArrayBufferViewClass::ArrayBufferViewClass(ScriptEngine* scriptEngine) : -QObject(scriptEngine), +ArrayBufferViewClass::ArrayBufferViewClass(ScriptEngineQtScript* scriptEngine) : + QObject(scriptEngine), QScriptClass(scriptEngine), _scriptEngine(scriptEngine) { // Save string handles for quick lookup diff --git a/libraries/script-engine/src/ArrayBufferViewClass.h b/libraries/script-engine-qtscript/src/ArrayBufferViewClass.h similarity index 88% rename from libraries/script-engine/src/ArrayBufferViewClass.h rename to libraries/script-engine-qtscript/src/ArrayBufferViewClass.h index a1f7f8245fa..120592b099c 100644 --- a/libraries/script-engine/src/ArrayBufferViewClass.h +++ b/libraries/script-engine-qtscript/src/ArrayBufferViewClass.h @@ -20,7 +20,7 @@ #include #include -#include "ScriptEngine.h" +class ScriptEngineQtScript; static const QString BUFFER_PROPERTY_NAME = "buffer"; static const QString BYTE_OFFSET_PROPERTY_NAME = "byteOffset"; @@ -29,9 +29,9 @@ static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength"; class ArrayBufferViewClass : public QObject, public QScriptClass { Q_OBJECT public: - ArrayBufferViewClass(ScriptEngine* scriptEngine); + ArrayBufferViewClass(ScriptEngineQtScript* scriptEngine); - ScriptEngine* getScriptEngine() { return _scriptEngine; } + ScriptEngineQtScript* getScriptEngine() { return _scriptEngine; } virtual QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, @@ -46,7 +46,7 @@ class ArrayBufferViewClass : public QObject, public QScriptClass { QScriptString _byteOffsetName; QScriptString _byteLengthName; - ScriptEngine* _scriptEngine; + ScriptEngineQtScript* _scriptEngine; }; #endif // hifi_ArrayBufferViewClass_h diff --git a/libraries/shared/src/BaseScriptEngine.cpp b/libraries/script-engine-qtscript/src/BaseScriptEngine.cpp similarity index 100% rename from libraries/shared/src/BaseScriptEngine.cpp rename to libraries/script-engine-qtscript/src/BaseScriptEngine.cpp diff --git a/libraries/shared/src/BaseScriptEngine.h b/libraries/script-engine-qtscript/src/BaseScriptEngine.h similarity index 74% rename from libraries/shared/src/BaseScriptEngine.h rename to libraries/script-engine-qtscript/src/BaseScriptEngine.h index 443c7b05000..145e432aade 100644 --- a/libraries/shared/src/BaseScriptEngine.h +++ b/libraries/script-engine-qtscript/src/BaseScriptEngine.h @@ -16,8 +16,8 @@ #include #include -class ScriptEngine; -using ScriptEnginePointer = QSharedPointer; +class ScriptEngineQtScript; +using ScriptEngineQtScriptPointer = QSharedPointer; // common base class for extending QScriptEngine itself class BaseScriptEngine : public QScriptEngine, public QEnableSharedFromThis { @@ -80,19 +80,6 @@ class BaseScriptEngine : public QScriptEngine, public QEnableSharedFromThisReport the details of an unhandled exception. - * Script.unhandledException.connect(function (exception) { - * print("Unhandled exception: " + JSON.stringify(exception)); - * }); - * var properties = JSON.parse("{ x: 1"); // Invalid JSON string. - */ - void unhandledException(const QScriptValue& exception); - protected: // like `newFunction`, but allows mapping inline C++ lambdas with captures as callable QScriptValues // even though the context/engine parameters are redundant in most cases, the function signature matches `newFunction` @@ -105,18 +92,6 @@ class BaseScriptEngine : public QScriptEngine, public QEnableSharedFromThisglobalObject(); // Save string handles for quick lookup diff --git a/libraries/script-engine/src/DataViewClass.h b/libraries/script-engine-qtscript/src/DataViewClass.h similarity index 93% rename from libraries/script-engine/src/DataViewClass.h rename to libraries/script-engine-qtscript/src/DataViewClass.h index 72c920a7273..8bdf9bba4a4 100644 --- a/libraries/script-engine/src/DataViewClass.h +++ b/libraries/script-engine-qtscript/src/DataViewClass.h @@ -17,7 +17,7 @@ class DataViewClass : public ArrayBufferViewClass { Q_OBJECT public: - DataViewClass(ScriptEngine* scriptEngine); + DataViewClass(ScriptEngineQtScript* scriptEngine); QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 byteLength); QString name() const override; diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine-qtscript/src/DataViewPrototype.cpp similarity index 99% rename from libraries/script-engine/src/DataViewPrototype.cpp rename to libraries/script-engine-qtscript/src/DataViewPrototype.cpp index ef757a5cb4d..ff540f5300a 100644 --- a/libraries/script-engine/src/DataViewPrototype.cpp +++ b/libraries/script-engine-qtscript/src/DataViewPrototype.cpp @@ -12,9 +12,12 @@ #include "DataViewPrototype.h" #include +#include #include +#include + #include "DataViewClass.h" Q_DECLARE_METATYPE(QByteArray*) diff --git a/libraries/script-engine/src/DataViewPrototype.h b/libraries/script-engine-qtscript/src/DataViewPrototype.h similarity index 100% rename from libraries/script-engine/src/DataViewPrototype.h rename to libraries/script-engine-qtscript/src/DataViewPrototype.h diff --git a/libraries/script-engine-qtscript/src/ScriptEngineQtScript.cpp b/libraries/script-engine-qtscript/src/ScriptEngineQtScript.cpp new file mode 100644 index 00000000000..059b77e1ae8 --- /dev/null +++ b/libraries/script-engine-qtscript/src/ScriptEngineQtScript.cpp @@ -0,0 +1,914 @@ +// +// ScriptEngineQtScript.cpp +// libraries/script-engine-qtscript/src +// +// Created by Brad Hefta-Gaub on 12/14/13. +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ScriptEngineQtScript.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include + +//#include +//#include +#include + +//#include "ArrayBufferViewClass.h" +//#include "AssetScriptingInterface.h" +//#include "BatchLoader.h" +//#include "BaseScriptEngine.h" +//#include "DataViewClass.h" +//#include "EventTypes.h" +//#include "FileScriptingInterface.h" // unzip project +//#include "MenuItemProperties.h" +//#include "ScriptAudioInjector.h" +//#include "ScriptAvatarData.h" +//#include "ScriptCache.h" +//#include "TypedArrays.h" +//#include "XMLHttpRequestClass.h" +//#include "WebSocketClass.h" +//#include "RecordingScriptingInterface.h" +//#include "ScriptEngines.h" +//#include "StackTestScriptingInterface.h" +//#include "ModelScriptingInterface.h" + +//#include + +//#include "../../midi/src/Midi.h" // FIXME why won't a simpler include work? +//#include "MIDIEvent.h" + +//#include "SettingHandle.h" +//#include +//#include +//#include + +static const int MAX_MODULE_ID_LENGTH { 4096 }; +static const int MAX_DEBUG_VALUE_LENGTH { 80 }; + +static const QScriptEngine::QObjectWrapOptions DEFAULT_QOBJECT_WRAP_OPTIONS = + QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects; +static const QScriptValue::PropertyFlags READONLY_PROP_FLAGS { QScriptValue::ReadOnly | QScriptValue::Undeletable }; +static const QScriptValue::PropertyFlags READONLY_HIDDEN_PROP_FLAGS { READONLY_PROP_FLAGS | QScriptValue::SkipInEnumeration }; + +static const bool HIFI_AUTOREFRESH_FILE_SCRIPTS { true }; + +Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature) +int functionSignatureMetaID = qRegisterMetaType(); + +int scriptEnginePointerMetaID = qRegisterMetaType(); + +static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) { + // assemble the message by concatenating our arguments + QString message = ""; + for (int i = 0; i < context->argumentCount(); i++) { + if (i > 0) { + message += " "; + } + message += context->argument(i).toString(); + } + + // was this generated by a script engine? If we don't recognize it then send the message and exit + ScriptEngineQtScript* scriptEngine = qobject_cast(engine); + if (!scriptEngine) { + qCDebug(scriptengine_script, "%s", qUtf8Printable(message)); + return QScriptValue(); + } + + // This message was sent by one of our script engines, let's try to see if we can find the source. + // Note that the first entry in the backtrace should be "print" and is somewhat useless to us + AbstractLoggerInterface* loggerInterface = AbstractLoggerInterface::get(); + if (loggerInterface && loggerInterface->showSourceDebugging()) { + QScriptContext* userContext = context; + while (userContext && QScriptContextInfo(userContext).functionType() == QScriptContextInfo::NativeFunction) { + userContext = userContext->parentContext(); + } + QString location; + if (userContext) { + QScriptContextInfo contextInfo(userContext); + QString fileName = contextInfo.fileName(); + int lineNumber = contextInfo.lineNumber(); + QString functionName = contextInfo.functionName(); + + location = functionName; + if (!fileName.isEmpty()) { + if (location.isEmpty()) { + location = fileName; + } else { + location = QString("%1 at %2").arg(location).arg(fileName); + } + } + if (lineNumber != -1) { + location = QString("%1:%2").arg(location).arg(lineNumber); + } + } + if (location.isEmpty()) { + location = scriptEngine->getFilename(); + } + + // give the script engine a chance to notify the system about this message + scriptEngine->print(message); + + // send the message to debug log + qCDebug(scriptengine_script, "[%s] %s", qUtf8Printable(location), qUtf8Printable(message)); + } else { + scriptEngine->print(message); + // prefix the script engine name to help disambiguate messages in the main debug log + qCDebug(scriptengine_script, "[%s] %s", qUtf8Printable(scriptEngine->getFilename()), qUtf8Printable(message)); + } + + return QScriptValue(); +} + +QString ScriptEngineQtScript::logException(const QScriptValue& exception) { + auto message = formatException(exception, _enableExtendedJSExceptions.get()); + scriptErrorMessage(message); + return message; +} + +ScriptEngineQtScript::ScriptEngineQtScript(ScriptManager* scriptManager) : + BaseScriptEngine(), + _manager(scriptManager), + _arrayBufferClass(new ArrayBufferClass(this)) +{ + if (_manager) { + connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) { + if (hasUncaughtException()) { + // the engine's uncaughtException() seems to produce much better stack traces here + emit _manager->unhandledException(cloneUncaughtException("signalHandlerException")); + clearExceptions(); + } else { + // ... but may not always be available -- so if needed we fallback to the passed exception + emit _manager->unhandledException(exception); + } + }, Qt::DirectConnection); + } + + setProcessEventsInterval(MSECS_PER_SECOND); + + // this is where all unhandled exceptions end up getting logged + connect(this, &BaseScriptEngine::unhandledException, this, [this](const QScriptValue& err) { + auto output = err.engine() == this ? err : makeError(err); + if (!output.property("detail").isValid()) { + output.setProperty("detail", "UnhandledException"); + } + logException(output); + }); +} + +bool ScriptEngineQtScript::isDebugMode() const { +#if defined(DEBUG) + return true; +#else + return false; +#endif +} + +ScriptEngineQtScript::~ScriptEngineQtScript() {} + +void ScriptEngineQtScript::disconnectNonEssentialSignals() { + disconnect(); + QThread* workerThread; + // Ensure the thread should be running, and does exist + if (_isRunning && _isThreaded && (workerThread = Base::thread())) { + connect(this, &QObject::destroyed, workerThread, &QThread::quit); + connect(workerThread, &QThread::finished, workerThread, &QObject::deleteLater); + } +} + +void ScriptEngineQtScript::executeOnScriptThread(std::function function, const Qt::ConnectionType& type ) { + if (QThread::currentThread() != Base::thread()) { + QMetaObject::invokeMethod(this, "executeOnScriptThread", type, Q_ARG(std::function, function)); + return; + } + + function(); +} + +QString ScriptEngineQtScript::getFilename() const { + QStringList fileNameParts = _fileNameString.split("/"); + QString lastPart; + if (!fileNameParts.isEmpty()) { + lastPart = fileNameParts.last(); + } + return lastPart; +} + +void ScriptEngineQtScript::scriptErrorMessage(const QString& message) { + qCCritical(scriptengine, "[%s] %s", qUtf8Printable(getFilename()), qUtf8Printable(message)); + emit errorMessage(message, getFilename()); +} + +void ScriptEngineQtScript::scriptWarningMessage(const QString& message) { + qCWarning(scriptengine, "[%s] %s", qUtf8Printable(getFilename()), qUtf8Printable(message)); + emit warningMessage(message, getFilename()); +} + +void ScriptEngineQtScript::scriptInfoMessage(const QString& message) { + qCInfo(scriptengine, "[%s] %s", qUtf8Printable(getFilename()), qUtf8Printable(message)); + emit infoMessage(message, getFilename()); +} + +void ScriptEngineQtScript::scriptPrintedMessage(const QString& message) { + qCDebug(scriptengine, "[%s] %s", qUtf8Printable(getFilename()), qUtf8Printable(message)); + emit printedMessage(message, getFilename()); +} + +void ScriptEngineQtScript::clearDebugLogWindow() { + emit clearDebugWindow(); +} + +// Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of +// callAnimationStateHandler requires that the type be registered. +// These two are meaningful, if we ever do want to use them... +static QScriptValue animVarMapToScriptValue(QScriptEngine* engine, const AnimVariantMap& parameters) { + QStringList unused; + return parameters.animVariantMapToScriptValue(engine, unused, false); +} +static void animVarMapFromScriptValue(const QScriptValue& value, AnimVariantMap& parameters) { + parameters.animVariantMapFromScriptValue(value); +} +// ... while these two are not. But none of the four are ever used. +static QScriptValue resultHandlerToScriptValue(QScriptEngine* engine, + const AnimVariantResultHandler& resultHandler) { + qCCritical(scriptengine) << "Attempt to marshall result handler to javascript"; + assert(false); + return QScriptValue(); +} +static void resultHandlerFromScriptValue(const QScriptValue& value, AnimVariantResultHandler& resultHandler) { + qCCritical(scriptengine) << "Attempt to marshall result handler from javascript"; + assert(false); +} + +// Templated qScriptRegisterMetaType fails to compile with raw pointers +using ScriptableResourceRawPtr = ScriptableResource*; + +static QScriptValue scriptableResourceToScriptValue(QScriptEngine* engine, + const ScriptableResourceRawPtr& resource) { + if (!resource) { + return QScriptValue(); // probably shutting down + } + + // The first script to encounter this resource will track its memory. + // In this way, it will be more likely to GC. + // This fails in the case that the resource is used across many scripts, but + // in that case it would be too difficult to tell which one should track the memory, and + // this serves the common case (use in a single script). + auto data = resource->getResource(); + if (data && !resource->isInScript()) { + resource->setInScript(true); + QObject::connect(data.data(), SIGNAL(updateSize(qint64)), engine, SLOT(updateMemoryCost(qint64))); + } + + auto object = engine->newQObject( + const_cast(resource), + QScriptEngine::ScriptOwnership, + DEFAULT_QOBJECT_WRAP_OPTIONS); + return object; +} + +static void scriptableResourceFromScriptValue(const QScriptValue& value, ScriptableResourceRawPtr& resource) { + resource = static_cast(value.toQObject()); +} + +/**jsdoc + * The Resource API provides values that define the possible loading states of a resource. + * + * @namespace Resource + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * @hifi-server-entity + * @hifi-assignment-client + * + * @property {Resource.State} State - The possible loading states of a resource. Read-only. + */ +static QScriptValue createScriptableResourcePrototype(ScriptEngineQtScriptPointer engine) { + auto baseEngine = static_cast(engine.data()); + auto prototype = baseEngine->newObject(); + + // Expose enum State to JS/QML via properties + QObject* state = new QObject(engine.data()); + state->setObjectName("ResourceState"); + auto metaEnum = QMetaEnum::fromType(); + for (int i = 0; i < metaEnum.keyCount(); ++i) { + state->setProperty(metaEnum.key(i), metaEnum.value(i)); + } + + auto prototypeState = baseEngine->newQObject(state, QScriptEngine::QtOwnership, + QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeSlots | QScriptEngine::ExcludeSuperClassMethods); + prototype.setProperty("State", prototypeState); + + return prototype; +} + +void ScriptEngineQtScript::resetModuleCache(bool deleteScriptCache) { + if (QThread::currentThread() != Base::thread()) { + executeOnScriptThread([=]() { resetModuleCache(deleteScriptCache); }); + return; + } + auto jsRequire = Base::globalObject().property("Script").property("require"); + auto cache = jsRequire.property("cache"); + auto cacheMeta = jsRequire.data(); + + if (deleteScriptCache) { + QScriptValueIterator it(cache); + while (it.hasNext()) { + it.next(); + if (it.flags() & QScriptValue::SkipInEnumeration) { + continue; + } + qCDebug(scriptengine) << "resetModuleCache(true) -- staging " << it.name() << " for cache reset at next require"; + cacheMeta.setProperty(it.name(), true); + } + } + cache = Base::newObject(); + if (!cacheMeta.isObject()) { + cacheMeta = Base::newObject(); + cacheMeta.setProperty("id", "Script.require.cacheMeta"); + cacheMeta.setProperty("type", "cacheMeta"); + jsRequire.setData(cacheMeta); + } + cache.setProperty("__created__", (double)QDateTime::currentMSecsSinceEpoch(), QScriptValue::SkipInEnumeration); +#if DEBUG_JS_MODULES + cache.setProperty("__meta__", cacheMeta, READONLY_HIDDEN_PROP_FLAGS); +#endif + jsRequire.setProperty("cache", cache, READONLY_PROP_FLAGS); +} + +void ScriptEngineQtScript::registerValue(const QString& valueName, QScriptValue value) { + if (QThread::currentThread() != Base::thread()) { +#ifdef THREAD_DEBUGGING + qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::registerValue() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; +#endif + QMetaObject::invokeMethod(this, "registerValue", + Q_ARG(const QString&, valueName), + Q_ARG(QScriptValue, value)); + return; + } + + QStringList pathToValue = valueName.split("."); + int partsToGo = pathToValue.length(); + QScriptValue partObject = globalObject(); + + for (const auto& pathPart : pathToValue) { + partsToGo--; + if (!partObject.property(pathPart).isValid()) { + if (partsToGo > 0) { + //QObject *object = new QObject; + QScriptValue partValue = newArray(); //newQObject(object, QScriptEngine::ScriptOwnership); + partObject.setProperty(pathPart, partValue); + } else { + partObject.setProperty(pathPart, value); + } + } + partObject = partObject.property(pathPart); + } +} + +void ScriptEngineQtScript::registerGlobalObject(const QString& name, QObject* object) { + if (QThread::currentThread() != Base::thread()) { +#ifdef THREAD_DEBUGGING + qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::registerGlobalObject() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; +#endif + QMetaObject::invokeMethod(this, "registerGlobalObject", + Q_ARG(const QString&, name), + Q_ARG(QObject*, object)); + return; + } +#ifdef THREAD_DEBUGGING + qCDebug(scriptengine) << "ScriptEngineQtScript::registerGlobalObject() called on thread [" << QThread::currentThread() << "] name:" << name; +#endif + + if (!Base::globalObject().property(name).isValid()) { + if (object) { + QScriptValue value = Base::newQObject(object, QScriptEngine::QtOwnership, DEFAULT_QOBJECT_WRAP_OPTIONS); + Base::globalObject().setProperty(name, value); + } else { + Base::globalObject().setProperty(name, QScriptValue()); + } + } +} + +void ScriptEngineQtScript::registerFunction(const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) { + if (QThread::currentThread() != Base::thread()) { +#ifdef THREAD_DEBUGGING + qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; +#endif + QMetaObject::invokeMethod(this, "registerFunction", + Q_ARG(const QString&, name), + Q_ARG(QScriptEngine::FunctionSignature, functionSignature), + Q_ARG(int, numArguments)); + return; + } +#ifdef THREAD_DEBUGGING + qCDebug(scriptengine) << "ScriptEngineQtScript::registerFunction() called on thread [" << QThread::currentThread() << "] name:" << name; +#endif + + QScriptValue scriptFun = Base::newFunction(functionSignature, numArguments); + Base::globalObject().setProperty(name, scriptFun); +} + +void ScriptEngineQtScript::registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) { + if (QThread::currentThread() != Base::thread()) { +#ifdef THREAD_DEBUGGING + qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] parent:" << parent << "name:" << name; +#endif + QMetaObject::invokeMethod(this, "registerFunction", + Q_ARG(const QString&, name), + Q_ARG(QScriptEngine::FunctionSignature, functionSignature), + Q_ARG(int, numArguments)); + return; + } +#ifdef THREAD_DEBUGGING + qCDebug(scriptengine) << "ScriptEngineQtScript::registerFunction() called on thread [" << QThread::currentThread() << "] parent:" << parent << "name:" << name; +#endif + + QScriptValue object = Base::globalObject().property(parent); + if (object.isValid()) { + QScriptValue scriptFun = Base::newFunction(functionSignature, numArguments); + object.setProperty(name, scriptFun); + } +} + +void ScriptEngineQtScript::registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter, + QScriptEngine::FunctionSignature setter, const QString& parent) { + if (QThread::currentThread() != Base::thread()) { +#ifdef THREAD_DEBUGGING + qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::registerGetterSetter() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " + " name:" << name << "parent:" << parent; +#endif + QMetaObject::invokeMethod(this, "registerGetterSetter", + Q_ARG(const QString&, name), + Q_ARG(QScriptEngine::FunctionSignature, getter), + Q_ARG(QScriptEngine::FunctionSignature, setter), + Q_ARG(const QString&, parent)); + return; + } +#ifdef THREAD_DEBUGGING + qCDebug(scriptengine) << "ScriptEngineQtScript::registerGetterSetter() called on thread [" << QThread::currentThread() << "] name:" << name << "parent:" << parent; +#endif + + QScriptValue setterFunction = Base::newFunction(setter, 1); + QScriptValue getterFunction = Base::newFunction(getter); + + if (!parent.isNull() && !parent.isEmpty()) { + QScriptValue object = Base::globalObject().property(parent); + if (object.isValid()) { + object.setProperty(name, setterFunction, QScriptValue::PropertySetter); + object.setProperty(name, getterFunction, QScriptValue::PropertyGetter); + } + } else { + Base::globalObject().setProperty(name, setterFunction, QScriptValue::PropertySetter); + Base::globalObject().setProperty(name, getterFunction, QScriptValue::PropertyGetter); + } +} + +// this is not redundant -- the version in BaseScriptEngine is specifically not Q_INVOKABLE +QScriptValue ScriptEngineQtScript::evaluateInClosure(const QScriptValue& closure, const QScriptProgram& program) { + return BaseScriptEngine::evaluateInClosure(closure, program); +} + +QScriptValue ScriptEngineQtScript::evaluate(const QString& sourceCode, const QString& fileName, int lineNumber) { + QSharedPointer scriptEngines(_scriptEngines); + if (!scriptEngines || scriptEngines->isStopped()) { + return QScriptValue(); // bail early + } + + if (QThread::currentThread() != Base::thread()) { + QScriptValue result; +#ifdef THREAD_DEBUGGING + qCDebug(scriptengine) << "*** WARNING *** ScriptEngineQtScript::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " + "sourceCode:" << sourceCode << " fileName:" << fileName << "lineNumber:" << lineNumber; +#endif + BLOCKING_INVOKE_METHOD(this, "evaluate", + Q_RETURN_ARG(QScriptValue, result), + Q_ARG(const QString&, sourceCode), + Q_ARG(const QString&, fileName), + Q_ARG(int, lineNumber)); + return result; + } + + // Check syntax + auto syntaxError = lintScript(sourceCode, fileName); + if (syntaxError.isError()) { + if (!isEvaluating()) { + syntaxError.setProperty("detail", "evaluate"); + } + raiseException(syntaxError); + maybeEmitUncaughtException("lint"); + return syntaxError; + } + QScriptProgram program { sourceCode, fileName, lineNumber }; + if (program.isNull()) { + // can this happen? + auto err = makeError("could not create QScriptProgram for " + fileName); + raiseException(err); + maybeEmitUncaughtException("compile"); + return err; + } + + QScriptValue result; + { + result = BaseScriptEngine::evaluate(program); + maybeEmitUncaughtException("evaluate"); + } + return result; +} + +void ScriptEngineQtScript::updateMemoryCost(const qint64& deltaSize) { + if (deltaSize > 0) { + // We've patched qt to fix https://highfidelity.atlassian.net/browse/BUGZ-46 on mac and windows only. +#if defined(Q_OS_WIN) || defined(Q_OS_MAC) + reportAdditionalMemoryCost(deltaSize); +#endif + } +} + +void ScriptEngineQtScript::print(const QString& message) { + emit printedMessage(message, getFilename()); +} + + +void ScriptEngineQtScript::beginProfileRange(const QString& label) const { + PROFILE_SYNC_BEGIN(script, label.toStdString().c_str(), label.toStdString().c_str()); +} + +void ScriptEngineQtScript::endProfileRange(const QString& label) const { + PROFILE_SYNC_END(script, label.toStdString().c_str(), label.toStdString().c_str()); +} + +// retrieves the current parent module from the JS scope chain +QScriptValue ScriptEngineQtScript::currentModule() { + if (!IS_THREADSAFE_INVOCATION(Base::thread(), __FUNCTION__)) { + return unboundNullValue(); + } + auto jsRequire = Base::globalObject().property("Script").property("require"); + auto cache = jsRequire.property("cache"); + auto candidate = QScriptValue(); + for (auto c = Base::currentContext(); c && !candidate.isObject(); c = c->parentContext()) { + QScriptContextInfo contextInfo { c }; + candidate = cache.property(contextInfo.fileName()); + } + if (!candidate.isObject()) { + return QScriptValue(); + } + return candidate; +} + +// replaces or adds "module" to "parent.children[]" array +// (for consistency with Node.js and userscript cache invalidation without "cache busters") +bool ScriptEngineQtScript::registerModuleWithParent(const QScriptValue& module, const QScriptValue& parent) { + auto children = parent.property("children"); + if (children.isArray()) { + auto key = module.property("id"); + auto length = children.property("length").toInt32(); + for (int i = 0; i < length; i++) { + if (children.property(i).property("id").strictlyEquals(key)) { + qCDebug(scriptengine_module) << key.toString() << " updating parent.children[" << i << "] = module"; + children.setProperty(i, module); + return true; + } + } + qCDebug(scriptengine_module) << key.toString() << " appending parent.children[" << length << "] = module"; + children.setProperty(length, module); + return true; + } else if (parent.isValid()) { + qCDebug(scriptengine_module) << "registerModuleWithParent -- unrecognized parent" << parent.toVariant().toString(); + } + return false; +} + +// creates a new JS "module" Object with default metadata properties +QScriptValue ScriptEngineQtScript::newModule(const QString& modulePath, const QScriptValue& parent) { + auto closure = Base::newObject(); + auto exports = Base::newObject(); + auto module = Base::newObject(); + qCDebug(scriptengine_module) << "newModule" << parent.property("filename").toString(); + + closure.setProperty("module", module, READONLY_PROP_FLAGS); + + // note: this becomes the "exports" free variable, so should not be set read only + closure.setProperty("exports", exports); + + // make the closure available to module instantiation + module.setProperty("__closure__", closure, READONLY_HIDDEN_PROP_FLAGS); + + // for consistency with Node.js Module + module.setProperty("id", modulePath, READONLY_PROP_FLAGS); + module.setProperty("filename", modulePath, READONLY_PROP_FLAGS); + module.setProperty("exports", exports); // not readonly + module.setProperty("loaded", false, READONLY_PROP_FLAGS); + module.setProperty("parent", parent, READONLY_PROP_FLAGS); + module.setProperty("children", Base::newArray(), READONLY_PROP_FLAGS); + + // module.require is a bound version of require that always resolves relative to that module's path + auto boundRequire = QScriptEngine::evaluate("(function(id) { return Script.require(Script.require.resolve(id, this.filename)); })", "(boundRequire)"); + module.setProperty("require", boundRequire, READONLY_PROP_FLAGS); + + return module; +} + +// synchronously fetch a module's source code using BatchLoader +QVariantMap ScriptEngineQtScript::fetchModuleSource(const QString& modulePath, const bool forceDownload) { + using UrlMap = QMap; + auto scriptCache = DependencyManager::get(); + QVariantMap req; + qCDebug(scriptengine_module) << "require.fetchModuleSource: " << QUrl(modulePath).fileName() << QThread::currentThread(); + + auto onload = [=, &req](const UrlMap& data, const UrlMap& _status) { + auto url = modulePath; + auto status = _status[url]; + auto contents = data[url]; + if (isStopping()) { + req["status"] = "Stopped"; + req["success"] = false; + } else { + req["url"] = url; + req["status"] = status; + req["success"] = ScriptCache::isSuccessStatus(status); + req["contents"] = contents; + } + }; + + if (forceDownload) { + qCDebug(scriptengine_module) << "require.requestScript -- clearing cache for" << modulePath; + scriptCache->deleteScript(modulePath); + } + BatchLoader* loader = new BatchLoader(QList({ modulePath })); + connect(loader, &BatchLoader::finished, this, onload); + connect(this, &QObject::destroyed, loader, &QObject::deleteLater); + // fail faster? (since require() blocks the engine thread while resolving dependencies) + const int MAX_RETRIES = 1; + + loader->start(MAX_RETRIES); + + if (!loader->isFinished()) { + // This lambda can get called AFTER this local scope has completed. + // This is why we pass smart ptrs to the lambda instead of references to local variables. + auto monitor = std::make_shared(); + auto loop = std::make_shared(); + QObject::connect(loader, &BatchLoader::finished, this, [monitor, loop] { + monitor->stop(); + loop->quit(); + }); + + // this helps detect the case where stop() is invoked during the download + // but not seen in time to abort processing in onload()... + connect(monitor.get(), &QTimer::timeout, this, [this, loop] { + if (isStopping()) { + loop->exit(-1); + } + }); + monitor->start(500); + loop->exec(); + } + loader->deleteLater(); + return req; +} + +// evaluate a pending module object using the fetched source code +QScriptValue ScriptEngineQtScript::instantiateModule(const QScriptValue& module, const QString& sourceCode) { + QScriptValue result; + auto modulePath = module.property("filename").toString(); + auto closure = module.property("__closure__"); + + qCDebug(scriptengine_module) << QString("require.instantiateModule: %1 / %2 bytes") + .arg(QUrl(modulePath).fileName()).arg(sourceCode.length()); + + if (module.property("content-type").toString() == "application/json") { + qCDebug(scriptengine_module) << "... parsing as JSON"; + closure.setProperty("__json", sourceCode); + result = evaluateInClosure(closure, { "module.exports = JSON.parse(__json)", modulePath }); + } else { + // scoped vars for consistency with Node.js + closure.setProperty("require", module.property("require")); + closure.setProperty("__filename", modulePath, READONLY_HIDDEN_PROP_FLAGS); + closure.setProperty("__dirname", QString(modulePath).replace(QRegExp("/[^/]*$"), ""), READONLY_HIDDEN_PROP_FLAGS); + result = evaluateInClosure(closure, { sourceCode, modulePath }); + } + maybeEmitUncaughtException(__FUNCTION__); + return result; +} + +// CommonJS/Node.js like require/module support +QScriptValue ScriptEngineQtScript::require(const QString& moduleId) { + qCDebug(scriptengine_module) << "ScriptEngineQtScript::require(" << moduleId.left(MAX_DEBUG_VALUE_LENGTH) << ")"; + if (!IS_THREADSAFE_INVOCATION(Base::thread(), __FUNCTION__)) { + return unboundNullValue(); + } + + auto jsRequire = Base::globalObject().property("Script").property("require"); + auto cacheMeta = jsRequire.data(); + auto cache = jsRequire.property("cache"); + auto parent = currentModule(); + + auto throwModuleError = [&](const QString& modulePath, const QScriptValue& error) { + cache.setProperty(modulePath, Base::nullValue()); + if (!error.isNull()) { +#ifdef DEBUG_JS_MODULES + qCWarning(scriptengine_module) << "throwing module error:" << error.toString() << modulePath << error.property("stack").toString(); +#endif + raiseException(error); + } + maybeEmitUncaughtException("module"); + return unboundNullValue(); + }; + + // start by resolving the moduleId into a fully-qualified path/URL + QString modulePath = _requireResolve(moduleId); + if (modulePath.isNull() || hasUncaughtException()) { + // the resolver already threw an exception -- bail early + maybeEmitUncaughtException(__FUNCTION__); + return unboundNullValue(); + } + + // check the resolved path against the cache + auto module = cache.property(modulePath); + + // modules get cached in `Script.require.cache` and (similar to Node.js) users can access it + // to inspect particular entries and invalidate them by deleting the key: + // `delete Script.require.cache[Script.require.resolve(moduleId)];` + + // Check to see if we should invalidate the cache based on a user setting. + Setting::Handle getCachebustSetting {"cachebustScriptRequire", false }; + + // cacheMeta is just used right now to tell deleted keys apart from undefined ones + bool invalidateCache = getCachebustSetting.get() || (module.isUndefined() && cacheMeta.property(moduleId).isValid()); + + // reset the cacheMeta record so invalidation won't apply next time, even if the module fails to load + cacheMeta.setProperty(modulePath, QScriptValue()); + + auto exports = module.property("exports"); + if (!invalidateCache && exports.isObject()) { + // we have found a cached module -- just need to possibly register it with current parent + qCDebug(scriptengine_module) << QString("require - using cached module for '%1' (loaded: %2)") + .arg(moduleId).arg(module.property("loaded").toString()); + registerModuleWithParent(module, parent); + maybeEmitUncaughtException("cached module"); + return exports; + } + + // bootstrap / register new empty module + module = newModule(modulePath, parent); + registerModuleWithParent(module, parent); + + // add it to the cache (this is done early so any cyclic dependencies pick up) + cache.setProperty(modulePath, module); + + // download the module source + auto req = fetchModuleSource(modulePath, invalidateCache); + + if (!req.contains("success") || !req["success"].toBool()) { + auto error = QString("error retrieving script (%1)").arg(req["status"].toString()); + return throwModuleError(modulePath, error); + } + +#if DEBUG_JS_MODULES + qCDebug(scriptengine_module) << "require.loaded: " << + QUrl(req["url"].toString()).fileName() << req["status"].toString(); +#endif + + auto sourceCode = req["contents"].toString(); + + if (QUrl(modulePath).fileName().endsWith(".json", Qt::CaseInsensitive)) { + module.setProperty("content-type", "application/json"); + } else { + module.setProperty("content-type", "application/javascript"); + } + + // evaluate the module + auto result = instantiateModule(module, sourceCode); + + if (result.isError() && !result.strictlyEquals(module.property("exports"))) { + qCWarning(scriptengine_module) << "-- result.isError --" << result.toString(); + return throwModuleError(modulePath, result); + } + + // mark as fully-loaded + module.setProperty("loaded", true, READONLY_PROP_FLAGS); + + // set up a new reference point for detecting cache key deletion + cacheMeta.setProperty(modulePath, module); + + qCDebug(scriptengine_module) << "//ScriptEngineQtScript::require(" << moduleId << ")"; + + maybeEmitUncaughtException(__FUNCTION__); + return module.property("exports"); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// ScriptEngine implementation + +ScriptValuePointer ScriptEngineQtScript::globalObject() const { + return Base::globalObject(); +} + +ScriptManager* ScriptEngineQtScript::manager() const { +} + +ScriptValuePointer ScriptEngineQtScript::newArray(uint length) { + return Base::newArray(length); +} + +ScriptValuePointer ScriptEngineQtScript::newArrayBuffer(const QByteArray& message) { + QScriptValue data = Base::newVariant(QVariant::fromValue(message)); + QScriptValue ctor = Base::globalObject().property("ArrayBuffer"); + auto array = qscriptvalue_cast(ctor.data()); + if (!array) { + return undefinedValue() + } + return Base::newObject(array, data); +} + +ScriptValuePointer ScriptEngineQtScript::newObject() { + return Base::newObject(); +} + +ScriptProgramPointer ScriptEngineQtScript::newProgram(const QString& sourceCode, const QString& fileName) { +} + +ScriptValuePointer ScriptEngineQtScript::newQObject(QObject* obj) { +} + +ScriptValuePointer ScriptEngineQtScript::newValue(bool value) { +} + +ScriptValuePointer ScriptEngineQtScript::newValue(int value) { +} + +ScriptValuePointer ScriptEngineQtScript::newValue(uint value) { +} + +ScriptValuePointer ScriptEngineQtScript::newValue(double value) { +} + +ScriptValuePointer ScriptEngineQtScript::newValue(const QString& value) { +} + +ScriptValuePointer ScriptEngineQtScript::newValue(const QLatin1String& value) { +} + +ScriptValuePointer ScriptEngineQtScript::newValue(const char* value) { +} + +ScriptValuePointer ScriptEngineQtScript::newVariant(const QVariant& value) { +} + +ScriptValuePointer ScriptEngineQtScript::nullValue() { + return Base::nullValue(); +} + +void ScriptEngineQtScript::setDefaultPrototype(int metaTypeId, const ScriptValuePointer& prototype) { +} + +ScriptValuePointer ScriptEngineQtScript::undefinedValue() { + return Base::undefinedValue(); +} diff --git a/libraries/script-engine-qtscript/src/ScriptEngineQtScript.h b/libraries/script-engine-qtscript/src/ScriptEngineQtScript.h new file mode 100644 index 00000000000..6f618f551ae --- /dev/null +++ b/libraries/script-engine-qtscript/src/ScriptEngineQtScript.h @@ -0,0 +1,508 @@ +// +// ScriptEngineQtScript.h +// libraries/script-engine-qtscript/src +// +// Created by Brad Hefta-Gaub on 12/14/13. +// Copyright 2013 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ScriptEngineQtScript_h +#define hifi_ScriptEngineQtScript_h + +//#include +//#include + +//#include +//#include +//#include +//#include +//#include + +#include +#include +#include +#include +#include +#include +#include + +//#include +//#include +//#include +//#include +//#include +//#include +//#include +#include +#include + +//#include "PointerEvent.h" +#include "ArrayBufferClass.h" +//#include "AssetScriptingInterface.h" +//#include "AudioScriptingInterface.h" +#include "BaseScriptEngine.h" +//#include "ExternalResource.h" +//#include "Quat.h" +//#include "Mat4.h" +//#include "ScriptCache.h" +//#include "ScriptUUID.h" +//#include "Vec3.h" +//#include "ConsoleScriptingInterface.h" +//#include "SettingHandle.h" +//#include "Profile.h" + +//class QScriptEngineDebugger; +class ScriptManager; + +Q_DECLARE_METATYPE(ScriptEngineQtScriptPointer) + +/**jsdoc + * The Script API provides facilities for working with scripts. + * + * @namespace Script + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * @hifi-server-entity + * @hifi-assignment-client + * + * @property {string} context - The context that the script is running in: + *
    + *
  • "client": An Interface or avatar script.
  • + *
  • "entity_client": A client entity script.
  • + *
  • "entity_server": A server entity script.
  • + *
  • "agent": An assignment client script.
  • + *
+ * Read-only. + * @property {string} type - The type of script that is running: + *
    + *
  • "client": An Interface script.
  • + *
  • "entity_client": A client entity script.
  • + *
  • "avatar": An avatar script.
  • + *
  • "entity_server": A server entity script.
  • + *
  • "agent": An assignment client script.
  • + *
+ * Read-only. + * @property {string} filename - The filename of the script file. + * Read-only. + * @property {Script.ResourceBuckets} ExternalPaths - External resource buckets. + */ +class ScriptEngineQtScript : public BaseScriptEngine, public ScriptEngine { + Q_OBJECT + using Base = BaseScriptEngine; + +public: // ScriptEngine implementation + virtual ScriptValuePointer globalObject() const; + virtual ScriptManager* manager() const; + virtual ScriptValuePointer newArray(uint length = 0); + virtual ScriptValuePointer newArrayBuffer(const QByteArray& message); + virtual ScriptValuePointer newObject(); + virtual ScriptProgramPointer newProgram(const QString& sourceCode, const QString& fileName); + virtual ScriptValuePointer newQObject(QObject* obj); + virtual ScriptValuePointer newValue(bool value); + virtual ScriptValuePointer newValue(int value); + virtual ScriptValuePointer newValue(uint value); + virtual ScriptValuePointer newValue(double value); + virtual ScriptValuePointer newValue(const QString& value); + virtual ScriptValuePointer newValue(const QLatin1String& value); + virtual ScriptValuePointer newValue(const char* value); + virtual ScriptValuePointer newVariant(const QVariant& value); + virtual ScriptValuePointer nullValue(); + virtual void setDefaultPrototype(int metaTypeId, const ScriptValuePointer& prototype); + virtual ScriptValuePointer undefinedValue(); + +public: + ScriptEngineQtScript(ScriptManager* scriptManager = nullptr); + ~ScriptEngineQtScript(); + + QString getFilename() const; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // NOTE - these are NOT intended to be public interfaces available to scripts, the are only Q_INVOKABLE so we can + // properly ensure they are only called on the correct thread + + /**jsdoc + * @function Script.registerGlobalObject + * @param {string} name - Name. + * @param {object} object - Object. + * @deprecated This function is deprecated and will be removed. + */ + /// registers a global object by name + Q_INVOKABLE void registerGlobalObject(const QString& name, QObject* object); + + /**jsdoc + * @function Script.registerGetterSetter + * @param {string} name - Name. + * @param {function} getter - Getter. + * @param {function} setter - Setter. + * @param {string} [parent=""] - Parent. + * @deprecated This function is deprecated and will be removed. + */ + /// registers a global getter/setter + Q_INVOKABLE void registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter, + QScriptEngine::FunctionSignature setter, const QString& parent = QString("")); + + /**jsdoc + * @function Script.registerFunction + * @param {string} name - Name. + * @param {function} function - Function. + * @param {number} [numArguments=-1] - Number of arguments. + * @deprecated This function is deprecated and will be removed. + */ + /// register a global function + Q_INVOKABLE void registerFunction(const QString& name, QScriptEngine::FunctionSignature fun, int numArguments = -1); + + /**jsdoc + * @function Script.registerFunction + * @param {string} parent - Parent. + * @param {string} name - Name. + * @param {function} function - Function. + * @param {number} [numArguments=-1] - Number of arguments. + * @deprecated This function is deprecated and will be removed. + */ + /// register a function as a method on a previously registered global object + Q_INVOKABLE void registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature fun, + int numArguments = -1); + + /**jsdoc + * @function Script.registerEnum + * @param {string} name - Name. + * @param {object} enum - Enum. + * @deprecated This function is deprecated and will be removed. + */ + // WARNING: This function must be called after a registerGlobalObject that creates the namespace this enum is located in, or + // the globalObject won't function. E.g., if you have a Foo object and a Foo.FooType enum, Foo must be registered first. + /// registers a global enum + Q_INVOKABLE void registerEnum(const QString& enumName, QMetaEnum newEnum); + + /**jsdoc + * @function Script.registerValue + * @param {string} name - Name. + * @param {object} value - Value. + * @deprecated This function is deprecated and will be removed. + */ + /// registers a global object by name + Q_INVOKABLE void registerValue(const QString& valueName, QScriptValue value); + + /**jsdoc + * @function Script.evaluate + * @param {string} program - Program. + * @param {string} filename - File name. + * @param {number} [lineNumber=-1] - Line number. + * @returns {object} Object. + * @deprecated This function is deprecated and will be removed. + */ + /// evaluate some code in the context of the ScriptEngineQtScript and return the result + Q_INVOKABLE QScriptValue evaluate(const QString& program, const QString& fileName, int lineNumber = 1); // this is also used by the script tool widget + + /**jsdoc + * @function Script.evaluateInClosure + * @param {object} locals - Locals. + * @param {object} program - Program. + * @returns {object} Object. + * @deprecated This function is deprecated and will be removed. + */ + Q_INVOKABLE QScriptValue evaluateInClosure(const QScriptValue& locals, const QScriptProgram& program); + + /**jsdoc + * Checks whether the application was compiled as a debug build. + * @function Script.isDebugMode + * @returns {boolean} true if the application was compiled as a debug build, false if it was + * compiled as a release build. + */ + Q_INVOKABLE bool isDebugMode() const; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // MODULE related methods + + /**jsdoc + * Provides access to methods or objects provided in an external JavaScript or JSON file. + * See {@link https://docs.vircadia.dev/script/js-tips.html} for further details. + * @function Script.require + * @param {string} module - The module to use. May be a JavaScript file, a JSON file, or the name of a system module such + * as "appUi" (i.e., the "appUi.js" system module JavaScript file). + * @returns {object|array} The value assigned to module.exports in the JavaScript file, or the value defined + * in the JSON file. + */ + Q_INVOKABLE QScriptValue require(const QString& moduleId); + + /**jsdoc + * @function Script.resetModuleCache + * @param {boolean} [deleteScriptCache=false] - Delete script cache. + * @deprecated This function is deprecated and will be removed. + */ + Q_INVOKABLE void resetModuleCache(bool deleteScriptCache = false); + + QScriptValue currentModule(); + bool registerModuleWithParent(const QScriptValue& module, const QScriptValue& parent); + QScriptValue newModule(const QString& modulePath, const QScriptValue& parent = QScriptValue()); + QVariantMap fetchModuleSource(const QString& modulePath, const bool forceDownload = false); + QScriptValue instantiateModule(const QScriptValue& module, const QString& sourceCode); + + /**jsdoc + * Prints a message to the program log and emits {@link Script.printedMessage}. + *

Alternatively, you can use {@link print} or one of the {@link console} API methods.

+ * @function Script.print + * @param {string} message - The message to print. + */ + Q_INVOKABLE void print(const QString& message); + + /**jsdoc + * Starts timing a section of code in order to send usage data about it to Vircadia. Shouldn't be used outside of the + * standard scripts. + * @function Script.beginProfileRange + * @param {string} label - A name that identifies the section of code. + */ + Q_INVOKABLE void beginProfileRange(const QString& label) const; + + /**jsdoc + * Finishes timing a section of code in order to send usage data about it to Vircadia. Shouldn't be used outside of + * the standard scripts. + * @function Script.endProfileRange + * @param {string} label - A name that identifies the section of code. + */ + Q_INVOKABLE void endProfileRange(const QString& label) const; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Entity Script Related methods + + /**jsdoc + * Manually runs the JavaScript garbage collector which reclaims memory by disposing of objects that are no longer + * reachable. + * @function Script.requestGarbageCollection + */ + Q_INVOKABLE void requestGarbageCollection() { collectGarbage(); } + + bool isFinished() const { return _isFinished; } // used by Application and ScriptWidget + bool isRunning() const { return _isRunning; } // used by ScriptWidget + + // this is used by code in ScriptEngines.cpp during the "reload all" operation + bool isStopping() const { return _isStopping; } + + bool isDebuggable() const { return _debuggable; } + + void disconnectNonEssentialSignals(); + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // These are currently used by Application to track if a script is user loaded or not. Consider finding a solution + // inside of Application so that the ScriptEngineQtScript class is not polluted by this notion + void setUserLoaded(bool isUserLoaded) { _isUserLoaded = isUserLoaded; } + bool isUserLoaded() const { return _isUserLoaded; } + + void setQuitWhenFinished(const bool quitWhenFinished) { _quitWhenFinished = quitWhenFinished; } + bool isQuitWhenFinished() const { return _quitWhenFinished; } + + // NOTE - this is used by the TypedArray implementation. we need to review this for thread safety + ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } + + void setEmitScriptUpdatesFunction(std::function func) { _emitScriptUpdates = func; } + + void scriptErrorMessage(const QString& message); + void scriptWarningMessage(const QString& message); + void scriptInfoMessage(const QString& message); + void scriptPrintedMessage(const QString& message); + void clearDebugLogWindow(); + +public slots: + + /**jsdoc + * @function Script.updateMemoryCost + * @param {number} deltaSize - Delta size. + * @deprecated This function is deprecated and will be removed. + */ + void updateMemoryCost(const qint64&); + +signals: + + /**jsdoc + * @function Script.scriptLoaded + * @param {string} filename - File name. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + void scriptLoaded(const QString& scriptFilename); + + /**jsdoc + * @function Script.errorLoadingScript + * @param {string} filename - File name. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + void errorLoadingScript(const QString& scriptFilename); + + /**jsdoc + * Triggered frequently at a system-determined interval. + * @function Script.update + * @param {number} deltaTime - The time since the last update, in s. + * @returns {Signal} + * @example Report script update intervals. + * Script.update.connect(function (deltaTime) { + * print("Update: " + deltaTime); + * }); + */ + void update(float deltaTime); + + /**jsdoc + * Triggered when the script is stopping. + * @function Script.scriptEnding + * @returns {Signal} + * @example Report when a script is stopping. + * print("Script started"); + * + * Script.scriptEnding.connect(function () { + * print("Script ending"); + * }); + * + * Script.setTimeout(function () { + * print("Stopping script"); + * Script.stop(); + * }, 1000); + */ + void scriptEnding(); + + /**jsdoc + * @function Script.finished + * @param {string} filename - File name. + * @param {object} engine - Engine. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + void finished(const QString& fileNameString, ScriptEngineQtScriptPointer); + + /**jsdoc + * @function Script.cleanupMenuItem + * @param {string} menuItem - Menu item. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + void cleanupMenuItem(const QString& menuItemString); + + /**jsdoc + * Triggered when the script prints a message to the program log via {@link print}, {@link Script.print}, + * {@link console.log}, {@link console.debug}, {@link console.group}, {@link console.groupEnd}, {@link console.time}, or + * {@link console.timeEnd}. + * @function Script.printedMessage + * @param {string} message - The message. + * @param {string} scriptName - The name of the script that generated the message. + * @returns {Signal} + */ + void printedMessage(const QString& message, const QString& scriptName); + + /**jsdoc + * Triggered when the script generates an error, {@link console.error} or {@link console.exception} is called, or + * {@link console.assert} is called and fails. + * @function Script.errorMessage + * @param {string} message - The error message. + * @param {string} scriptName - The name of the script that generated the error message. + * @returns {Signal} + */ + void errorMessage(const QString& message, const QString& scriptName); + + /**jsdoc + * Triggered when the script generates a warning or {@link console.warn} is called. + * @function Script.warningMessage + * @param {string} message - The warning message. + * @param {string} scriptName - The name of the script that generated the warning message. + * @returns {Signal} + */ + void warningMessage(const QString& message, const QString& scriptName); + + /**jsdoc + * Triggered when the script generates an information message or {@link console.info} is called. + * @function Script.infoMessage + * @param {string} message - The information message. + * @param {string} scriptName - The name of the script that generated the information message. + * @returns {Signal} + */ + void infoMessage(const QString& message, const QString& scriptName); + + /**jsdoc + * Triggered when the running state of the script changes, e.g., from running to stopping. + * @function Script.runningStateChanged + * @returns {Signal} + */ + void runningStateChanged(); + + /**jsdoc + * @function Script.clearDebugWindow + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + void clearDebugWindow(); + + /**jsdoc + * @function Script.loadScript + * @param {string} scriptName - Script name. + * @param {boolean} isUserLoaded - Is user loaded. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + void loadScript(const QString& scriptName, bool isUserLoaded); + + /**jsdoc + * @function Script.reloadScript + * @param {string} scriptName - Script name. + * @param {boolean} isUserLoaded - Is user loaded. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + void reloadScript(const QString& scriptName, bool isUserLoaded); + + /**jsdoc + * Triggered when the script has stopped. + * @function Script.doneRunning + * @returns {Signal} + */ + void doneRunning(); + + /**jsdoc + * @function Script.entityScriptDetailsUpdated + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + // Emitted when an entity script is added or removed, or when the status of an entity + // script is updated (goes from RUNNING to ERROR_RUNNING_SCRIPT, for example) + void entityScriptDetailsUpdated(); + +protected: + + /**jsdoc + * @function Script.executeOnScriptThread + * @param {function} function - Function. + * @param {ConnectionType} [type=2] - Connection type. + * @deprecated This function is deprecated and will be removed. + */ + Q_INVOKABLE void executeOnScriptThread(std::function function, const Qt::ConnectionType& type = Qt::QueuedConnection ); + + QString logException(const QScriptValue& exception); + void setParentURL(const QString& parentURL) { _parentURL = parentURL; } + + QPointer _manager; + + QString _parentURL; + std::atomic _isFinished { false }; + std::atomic _isRunning { false }; + std::atomic _isStopping { false }; + bool _isInitialized { false }; + + bool _isThreaded { false }; + QScriptEngineDebugger* _debugger { nullptr }; + bool _debuggable { false }; + qint64 _lastUpdate; + + std::atomic _isUserLoaded { false }; + bool _isReloading { false }; + + std::atomic _quitWhenFinished; + + ArrayBufferClass* _arrayBufferClass; + + std::function _emitScriptUpdates{ []() { return true; } }; +}; + +#endif // hifi_ScriptEngineQtScript_h diff --git a/libraries/script-engine/src/TypedArrayPrototype.cpp b/libraries/script-engine-qtscript/src/TypedArrayPrototype.cpp similarity index 99% rename from libraries/script-engine/src/TypedArrayPrototype.cpp rename to libraries/script-engine-qtscript/src/TypedArrayPrototype.cpp index a1f3ff87e8d..17da12b845c 100644 --- a/libraries/script-engine/src/TypedArrayPrototype.cpp +++ b/libraries/script-engine-qtscript/src/TypedArrayPrototype.cpp @@ -11,6 +11,8 @@ #include "TypedArrayPrototype.h" +#include + #include "TypedArrays.h" Q_DECLARE_METATYPE(QByteArray*) diff --git a/libraries/script-engine/src/TypedArrayPrototype.h b/libraries/script-engine-qtscript/src/TypedArrayPrototype.h similarity index 89% rename from libraries/script-engine/src/TypedArrayPrototype.h rename to libraries/script-engine-qtscript/src/TypedArrayPrototype.h index 86d578ace0b..3539016ff5b 100644 --- a/libraries/script-engine/src/TypedArrayPrototype.h +++ b/libraries/script-engine-qtscript/src/TypedArrayPrototype.h @@ -12,7 +12,9 @@ #ifndef hifi_TypedArrayPrototype_h #define hifi_TypedArrayPrototype_h -#include "ArrayBufferViewClass.h" +#include +#include +#include class TypedArrayPrototype : public QObject, public QScriptable { Q_OBJECT diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine-qtscript/src/TypedArrays.cpp similarity index 92% rename from libraries/script-engine/src/TypedArrays.cpp rename to libraries/script-engine-qtscript/src/TypedArrays.cpp index f2c3d3fd3db..8143dfb1fa7 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine-qtscript/src/TypedArrays.cpp @@ -13,12 +13,17 @@ #include -#include "ScriptEngine.h" +#include + +#include + +#include "ArrayBufferClass.h" +#include "ScriptEngineQtScript.h" #include "TypedArrayPrototype.h" Q_DECLARE_METATYPE(QByteArray*) -TypedArray::TypedArray(ScriptEngine* scriptEngine, QString name) : ArrayBufferViewClass(scriptEngine) { +TypedArray::TypedArray(ScriptEngineQtScript* scriptEngine, QString name) : ArrayBufferViewClass(scriptEngine) { _bytesPerElementName = engine()->toStringHandle(BYTES_PER_ELEMENT_PROPERTY_NAME.toLatin1()); _lengthName = engine()->toStringHandle(LENGTH_PROPERTY_NAME.toLatin1()); _name = engine()->toStringHandle(name.toLatin1()); @@ -229,7 +234,7 @@ void setPropertyHelper(QByteArray* arrayBuffer, const QScriptString& name, uint } } -Int8ArrayClass::Int8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_8_ARRAY_CLASS_NAME) { +Int8ArrayClass::Int8ArrayClass(ScriptEngineQtScript* scriptEngine) : TypedArray(scriptEngine, INT_8_ARRAY_CLASS_NAME) { setBytesPerElement(sizeof(qint8)); } @@ -245,7 +250,7 @@ void Int8ArrayClass::setProperty(QScriptValue &object, const QScriptString &name setPropertyHelper(ba, name, id, value); } -Uint8ArrayClass::Uint8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, UINT_8_ARRAY_CLASS_NAME) { +Uint8ArrayClass::Uint8ArrayClass(ScriptEngineQtScript* scriptEngine) : TypedArray(scriptEngine, UINT_8_ARRAY_CLASS_NAME) { setBytesPerElement(sizeof(quint8)); } @@ -261,7 +266,7 @@ void Uint8ArrayClass::setProperty(QScriptValue& object, const QScriptString& nam setPropertyHelper(ba, name, id, value); } -Uint8ClampedArrayClass::Uint8ClampedArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, UINT_8_CLAMPED_ARRAY_CLASS_NAME) { +Uint8ClampedArrayClass::Uint8ClampedArrayClass(ScriptEngineQtScript* scriptEngine) : TypedArray(scriptEngine, UINT_8_CLAMPED_ARRAY_CLASS_NAME) { setBytesPerElement(sizeof(quint8)); } @@ -287,7 +292,7 @@ void Uint8ClampedArrayClass::setProperty(QScriptValue& object, const QScriptStri } } -Int16ArrayClass::Int16ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_16_ARRAY_CLASS_NAME) { +Int16ArrayClass::Int16ArrayClass(ScriptEngineQtScript* scriptEngine) : TypedArray(scriptEngine, INT_16_ARRAY_CLASS_NAME) { setBytesPerElement(sizeof(qint16)); } @@ -303,7 +308,7 @@ void Int16ArrayClass::setProperty(QScriptValue& object, const QScriptString& nam setPropertyHelper(ba, name, id, value); } -Uint16ArrayClass::Uint16ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, UINT_16_ARRAY_CLASS_NAME) { +Uint16ArrayClass::Uint16ArrayClass(ScriptEngineQtScript* scriptEngine) : TypedArray(scriptEngine, UINT_16_ARRAY_CLASS_NAME) { setBytesPerElement(sizeof(quint16)); } @@ -319,7 +324,7 @@ void Uint16ArrayClass::setProperty(QScriptValue& object, const QScriptString& na setPropertyHelper(ba, name, id, value); } -Int32ArrayClass::Int32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_32_ARRAY_CLASS_NAME) { +Int32ArrayClass::Int32ArrayClass(ScriptEngineQtScript* scriptEngine) : TypedArray(scriptEngine, INT_32_ARRAY_CLASS_NAME) { setBytesPerElement(sizeof(qint32)); } @@ -335,7 +340,7 @@ void Int32ArrayClass::setProperty(QScriptValue& object, const QScriptString& nam setPropertyHelper(ba, name, id, value); } -Uint32ArrayClass::Uint32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, UINT_32_ARRAY_CLASS_NAME) { +Uint32ArrayClass::Uint32ArrayClass(ScriptEngineQtScript* scriptEngine) : TypedArray(scriptEngine, UINT_32_ARRAY_CLASS_NAME) { setBytesPerElement(sizeof(quint32)); } @@ -352,7 +357,7 @@ void Uint32ArrayClass::setProperty(QScriptValue& object, const QScriptString& na setPropertyHelper(ba, name, id, value); } -Float32ArrayClass::Float32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, FLOAT_32_ARRAY_CLASS_NAME) { +Float32ArrayClass::Float32ArrayClass(ScriptEngineQtScript* scriptEngine) : TypedArray(scriptEngine, FLOAT_32_ARRAY_CLASS_NAME) { setBytesPerElement(sizeof(float)); } @@ -389,7 +394,7 @@ void Float32ArrayClass::setProperty(QScriptValue& object, const QScriptString& n } } -Float64ArrayClass::Float64ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, FLOAT_64_ARRAY_CLASS_NAME) { +Float64ArrayClass::Float64ArrayClass(ScriptEngineQtScript* scriptEngine) : TypedArray(scriptEngine, FLOAT_64_ARRAY_CLASS_NAME) { setBytesPerElement(sizeof(double)); } diff --git a/libraries/script-engine/src/TypedArrays.h b/libraries/script-engine-qtscript/src/TypedArrays.h similarity index 89% rename from libraries/script-engine/src/TypedArrays.h rename to libraries/script-engine-qtscript/src/TypedArrays.h index 141e7870d90..24a3bda04f6 100644 --- a/libraries/script-engine/src/TypedArrays.h +++ b/libraries/script-engine-qtscript/src/TypedArrays.h @@ -30,7 +30,7 @@ static const QString FLOAT_64_ARRAY_CLASS_NAME = "Float64Array"; class TypedArray : public ArrayBufferViewClass { Q_OBJECT public: - TypedArray(ScriptEngine* scriptEngine, QString name); + TypedArray(ScriptEngineQtScript* scriptEngine, QString name); virtual QScriptValue newInstance(quint32 length); virtual QScriptValue newInstance(QScriptValue array); virtual QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length); @@ -67,7 +67,7 @@ class TypedArray : public ArrayBufferViewClass { class Int8ArrayClass : public TypedArray { Q_OBJECT public: - Int8ArrayClass(ScriptEngine* scriptEngine); + Int8ArrayClass(ScriptEngineQtScript* scriptEngine); QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; @@ -76,7 +76,7 @@ class Int8ArrayClass : public TypedArray { class Uint8ArrayClass : public TypedArray { Q_OBJECT public: - Uint8ArrayClass(ScriptEngine* scriptEngine); + Uint8ArrayClass(ScriptEngineQtScript* scriptEngine); QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; @@ -85,7 +85,7 @@ class Uint8ArrayClass : public TypedArray { class Uint8ClampedArrayClass : public TypedArray { Q_OBJECT public: - Uint8ClampedArrayClass(ScriptEngine* scriptEngine); + Uint8ClampedArrayClass(ScriptEngineQtScript* scriptEngine); QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; @@ -94,7 +94,7 @@ class Uint8ClampedArrayClass : public TypedArray { class Int16ArrayClass : public TypedArray { Q_OBJECT public: - Int16ArrayClass(ScriptEngine* scriptEngine); + Int16ArrayClass(ScriptEngineQtScript* scriptEngine); QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; @@ -103,7 +103,7 @@ class Int16ArrayClass : public TypedArray { class Uint16ArrayClass : public TypedArray { Q_OBJECT public: - Uint16ArrayClass(ScriptEngine* scriptEngine); + Uint16ArrayClass(ScriptEngineQtScript* scriptEngine); QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; @@ -112,7 +112,7 @@ class Uint16ArrayClass : public TypedArray { class Int32ArrayClass : public TypedArray { Q_OBJECT public: - Int32ArrayClass(ScriptEngine* scriptEngine); + Int32ArrayClass(ScriptEngineQtScript* scriptEngine); QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; @@ -121,7 +121,7 @@ class Int32ArrayClass : public TypedArray { class Uint32ArrayClass : public TypedArray { Q_OBJECT public: - Uint32ArrayClass(ScriptEngine* scriptEngine); + Uint32ArrayClass(ScriptEngineQtScript* scriptEngine); QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; @@ -130,7 +130,7 @@ class Uint32ArrayClass : public TypedArray { class Float32ArrayClass : public TypedArray { Q_OBJECT public: - Float32ArrayClass(ScriptEngine* scriptEngine); + Float32ArrayClass(ScriptEngineQtScript* scriptEngine); QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; @@ -139,7 +139,7 @@ class Float32ArrayClass : public TypedArray { class Float64ArrayClass : public TypedArray { Q_OBJECT public: - Float64ArrayClass(ScriptEngine* scriptEngine); + Float64ArrayClass(ScriptEngineQtScript* scriptEngine); QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 6def6c185fa..f26ce434822 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -1,18 +1,28 @@ set(TARGET_NAME script-engine) # FIXME Move undo scripting interface to application and remove Widgets -setup_hifi_library(Gui Network Script ScriptTools WebSockets Widgets) +setup_hifi_library(Network WebSockets) target_zlib() if (NOT ANDROID) target_quazip() endif () -link_hifi_libraries(shared networking shaders material-networking model-networking recording avatars model-serializers entities controllers animation audio midi) -include_hifi_library_headers(gl) -include_hifi_library_headers(hfm) +link_hifi_libraries(script-engine-qtscript shaders) +include_hifi_library_headers(animation) +include_hifi_library_headers(audio) +include_hifi_library_headers(avatars) +include_hifi_library_headers(controllers) +include_hifi_library_headers(entities) include_hifi_library_headers(gpu) -include_hifi_library_headers(ktx) +include_hifi_library_headers(hfm) include_hifi_library_headers(image) +include_hifi_library_headers(ktx) include_hifi_library_headers(graphics) +include_hifi_library_headers(material-networking) +include_hifi_library_headers(model-networking) +include_hifi_library_headers(model-serializers) +include_hifi_library_headers(networking) include_hifi_library_headers(octree) -include_hifi_library_headers(procedural) \ No newline at end of file +include_hifi_library_headers(procedural) +include_hifi_library_headers(recording) +include_hifi_library_headers(shared) diff --git a/libraries/script-engine/src/AbstractScriptingServicesInterface.h b/libraries/script-engine/src/AbstractScriptingServicesInterface.h index ac26b166b6c..24d821fa529 100644 --- a/libraries/script-engine/src/AbstractScriptingServicesInterface.h +++ b/libraries/script-engine/src/AbstractScriptingServicesInterface.h @@ -12,13 +12,16 @@ #ifndef hifi_AbstractScriptingServicesInterface_h #define hifi_AbstractScriptingServicesInterface_h -#include +#include + +class ScriptManager; +using ScriptManagerPointer = QSharedPointer; /// Interface provided by Application to other objects that need access to scripting services of the application class AbstractScriptingServicesInterface { public: /// Registers application specific services with a script engine. - virtual void registerScriptEngineWithApplicationServices(const ScriptEnginePointer& scriptEngine) = 0; + virtual void registerScriptEngineWithApplicationServices(const ScriptManagerPointer& scriptEngine) = 0; }; diff --git a/libraries/script-engine/src/ArrayBufferClass.cpp b/libraries/script-engine/src/ArrayBufferClass.cpp deleted file mode 100644 index 6734114932f..00000000000 --- a/libraries/script-engine/src/ArrayBufferClass.cpp +++ /dev/null @@ -1,180 +0,0 @@ -// -// ArrayBufferClass.cpp -// -// -// Created by Clement on 7/3/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "ArrayBufferClass.h" - -#include - -#include "ArrayBufferPrototype.h" -#include "DataViewClass.h" -#include "ScriptEngine.h" -#include "TypedArrays.h" - - -static const QString CLASS_NAME = "ArrayBuffer"; - -// FIXME: Q_DECLARE_METATYPE is global and really belongs in a shared header file, not per .cpp like this -// (see DataViewClass.cpp, etc. which would also have to be updated to resolve) -Q_DECLARE_METATYPE(QScriptClass*) -Q_DECLARE_METATYPE(QByteArray*) -int qScriptClassPointerMetaTypeId = qRegisterMetaType(); -int qByteArrayPointerMetaTypeId = qRegisterMetaType(); - -ArrayBufferClass::ArrayBufferClass(ScriptEngine* scriptEngine) : -QObject(scriptEngine), -QScriptClass(scriptEngine) { - qScriptRegisterMetaType(engine(), toScriptValue, fromScriptValue); - QScriptValue global = engine()->globalObject(); - - // Save string handles for quick lookup - _name = engine()->toStringHandle(CLASS_NAME.toLatin1()); - _byteLength = engine()->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1()); - - // build prototype - _proto = engine()->newQObject(new ArrayBufferPrototype(this), - QScriptEngine::QtOwnership, - QScriptEngine::SkipMethodsInEnumeration | - QScriptEngine::ExcludeSuperClassMethods | - QScriptEngine::ExcludeSuperClassProperties); - _proto.setPrototype(global.property("Object").property("prototype")); - - // Register constructor - _ctor = engine()->newFunction(construct, _proto); - _ctor.setData(engine()->toScriptValue(this)); - - engine()->globalObject().setProperty(name(), _ctor); - - // Registering other array types - // The script engine is there parent so it'll delete them with itself - new DataViewClass(scriptEngine); - new Int8ArrayClass(scriptEngine); - new Uint8ArrayClass(scriptEngine); - new Uint8ClampedArrayClass(scriptEngine); - new Int16ArrayClass(scriptEngine); - new Uint16ArrayClass(scriptEngine); - new Int32ArrayClass(scriptEngine); - new Uint32ArrayClass(scriptEngine); - new Float32ArrayClass(scriptEngine); - new Float64ArrayClass(scriptEngine); -} - -QScriptValue ArrayBufferClass::newInstance(qint32 size) { - const qint32 MAX_LENGTH = 100000000; - if (size < 0) { - engine()->evaluate("throw \"ArgumentError: negative length\""); - return QScriptValue(); - } - if (size > MAX_LENGTH) { - engine()->evaluate("throw \"ArgumentError: absurd length\""); - return QScriptValue(); - } - // We've patched qt to fix https://highfidelity.atlassian.net/browse/BUGZ-46 on mac and windows only. -#if defined(Q_OS_WIN) || defined(Q_OS_MAC) - engine()->reportAdditionalMemoryCost(size); -#endif - QScriptEngine* eng = engine(); - QVariant variant = QVariant::fromValue(QByteArray(size, 0)); - QScriptValue data = eng->newVariant(variant); - return engine()->newObject(this, data); -} - -QScriptValue ArrayBufferClass::newInstance(const QByteArray& ba) { - QScriptValue data = engine()->newVariant(QVariant::fromValue(ba)); - return engine()->newObject(this, data); -} - -QScriptValue ArrayBufferClass::construct(QScriptContext* context, QScriptEngine* engine) { - ArrayBufferClass* cls = qscriptvalue_cast(context->callee().data()); - if (!cls) { - // return if callee (function called) is not of type ArrayBuffer - return QScriptValue(); - } - QScriptValue arg = context->argument(0); - if (!arg.isValid() || !arg.isNumber()) { - return QScriptValue(); - } - - quint32 size = arg.toInt32(); - QScriptValue newObject = cls->newInstance(size); - - if (context->isCalledAsConstructor()) { - // if called with keyword new, replace this object. - context->setThisObject(newObject); - return engine->undefinedValue(); - } - - return newObject; -} - -QScriptClass::QueryFlags ArrayBufferClass::queryProperty(const QScriptValue& object, - const QScriptString& name, - QueryFlags flags, uint* id) { - QByteArray* ba = qscriptvalue_cast(object.data()); - if (ba && name == _byteLength) { - // if the property queried is byteLength, only handle read access - return flags &= HandlesReadAccess; - } - return 0; // No access -} - -QScriptValue ArrayBufferClass::property(const QScriptValue& object, - const QScriptString& name, uint id) { - QByteArray* ba = qscriptvalue_cast(object.data()); - if (ba && name == _byteLength) { - return ba->length(); - } - return QScriptValue(); -} - -QScriptValue::PropertyFlags ArrayBufferClass::propertyFlags(const QScriptValue& object, - const QScriptString& name, uint id) { - return QScriptValue::Undeletable; -} - -QString ArrayBufferClass::name() const { - return _name.toString(); -} - -QScriptValue ArrayBufferClass::prototype() const { - return _proto; -} - -QScriptValue ArrayBufferClass::toScriptValue(QScriptEngine* engine, const QByteArray& ba) { - QScriptValue ctor = engine->globalObject().property(CLASS_NAME); - ArrayBufferClass* cls = qscriptvalue_cast(ctor.data()); - if (!cls) { - if (engine->currentContext()) { - engine->currentContext()->throwError("arrayBufferClass::toScriptValue -- could not get " + CLASS_NAME + " class constructor"); - } - return QScriptValue::NullValue; - } - return cls->newInstance(ba); -} - -void ArrayBufferClass::fromScriptValue(const QScriptValue& object, QByteArray& byteArray) { - if (object.isString()) { - // UTF-8 encoded String - byteArray = object.toString().toUtf8(); - } else if (object.isArray()) { - // Array of uint8s eg: [ 128, 3, 25, 234 ] - auto Uint8Array = object.engine()->globalObject().property("Uint8Array"); - auto typedArray = Uint8Array.construct(QScriptValueList{object}); - if (QByteArray* buffer = qscriptvalue_cast(typedArray.property("buffer"))) { - byteArray = *buffer; - } - } else if (object.isObject()) { - // ArrayBuffer instance (or any JS class that supports coercion into QByteArray*) - if (QByteArray* buffer = qscriptvalue_cast(object.data())) { - byteArray = *buffer; - } - } -} - diff --git a/libraries/script-engine/src/AssetScriptingInterface.cpp b/libraries/script-engine/src/AssetScriptingInterface.cpp index f088ad7a38c..283beebdd3a 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.cpp +++ b/libraries/script-engine/src/AssetScriptingInterface.cpp @@ -13,18 +13,18 @@ #include #include -#include #include #include #include -#include #include #include #include #include "ScriptEngine.h" #include "ScriptEngineLogging.h" +#include "ScriptManager.h" +#include "ScriptValue.h" #include #include @@ -59,7 +59,7 @@ bool AssetScriptingInterface::initializeCache() { } } -void AssetScriptingInterface::uploadData(QString data, QScriptValue callback) { +void AssetScriptingInterface::uploadData(QString data, ScriptValuePointer callback) { auto handler = jsBindCallback(thisObject(), callback); QByteArray dataByteArray = data.toUtf8(); auto upload = DependencyManager::get()->createUpload(dataByteArray); @@ -83,7 +83,7 @@ void AssetScriptingInterface::uploadData(QString data, QScriptValue callback) { upload->start(); } -void AssetScriptingInterface::setMapping(QString path, QString hash, QScriptValue callback) { +void AssetScriptingInterface::setMapping(QString path, QString hash, ScriptValuePointer callback) { auto handler = jsBindCallback(thisObject(), callback); auto setMappingRequest = assetClient()->createSetMappingRequest(path, hash); Promise deferred = makePromise(__FUNCTION__); @@ -107,7 +107,7 @@ void AssetScriptingInterface::setMapping(QString path, QString hash, QScriptValu * @typedef {object} Assets.DownloadDataError * @property {string} errorMessage - "" if the download was successful, otherwise a description of the error. */ -void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callback) { +void AssetScriptingInterface::downloadData(QString urlString, ScriptValuePointer callback) { // FIXME: historically this API method failed silently when given a non-atp prefixed // urlString (or if the AssetRequest failed). // .. is that by design or could we update without breaking things to provide better feedback to scripts? @@ -149,7 +149,7 @@ void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callb assetRequest->start(); } -void AssetScriptingInterface::setBakingEnabled(QString path, bool enabled, QScriptValue callback) { +void AssetScriptingInterface::setBakingEnabled(QString path, bool enabled, ScriptValuePointer callback) { auto setBakingEnabledRequest = DependencyManager::get()->createSetBakingEnabledRequest({ path }, enabled); Promise deferred = jsPromiseReady(makePromise(__FUNCTION__), thisObject(), callback); @@ -179,11 +179,11 @@ void AssetScriptingInterface::sendFakedHandshake() { #endif -void AssetScriptingInterface::getMapping(QString asset, QScriptValue callback) { +void AssetScriptingInterface::getMapping(QString asset, ScriptValuePointer callback) { auto path = AssetUtils::getATPUrl(asset).path(); auto handler = jsBindCallback(thisObject(), callback); JS_VERIFY(AssetUtils::isValidFilePath(path), "invalid ATP file path: " + asset + "(path:"+path+")"); - JS_VERIFY(callback.isFunction(), "expected second parameter to be a callback function"); + JS_VERIFY(callback->isFunction(), "expected second parameter to be a callback function"); Promise promise = getAssetInfo(path); promise->ready([=](QString error, QVariantMap result) { jsCallback(handler, error, result.value("hash").toString()); @@ -202,19 +202,19 @@ bool AssetScriptingInterface::jsVerify(bool condition, const QString& error) { return false; } -QScriptValue AssetScriptingInterface::jsBindCallback(QScriptValue scope, QScriptValue callback) { - QScriptValue handler = ::makeScopedHandlerObject(scope, callback); - QScriptValue value = handler.property("callback"); - if (!jsVerify(handler.isObject() && value.isFunction(), - QString("jsBindCallback -- .callback is not a function (%1)").arg(value.toVariant().typeName()))) { - return QScriptValue(); +ScriptValuePointer AssetScriptingInterface::jsBindCallback(ScriptValuePointer scope, ScriptValuePointer callback) { + ScriptValuePointer handler = ::makeScopedHandlerObject(scope, callback); + ScriptValuePointer value = handler->property("callback"); + if (!jsVerify(handler->isObject() && value->isFunction(), + QString("jsBindCallback -- .callback is not a function (%1)").arg(value->toVariant().typeName()))) { + return ScriptValuePointer(); } return handler; } -Promise AssetScriptingInterface::jsPromiseReady(Promise promise, QScriptValue scope, QScriptValue callback) { +Promise AssetScriptingInterface::jsPromiseReady(Promise promise, ScriptValuePointer scope, ScriptValuePointer callback) { auto handler = jsBindCallback(scope, callback); - if (!jsVerify(handler.isValid(), "jsPromiseReady -- invalid callback handler")) { + if (!jsVerify(handler->isValid(), "jsPromiseReady -- invalid callback handler")) { return nullptr; } return promise->ready([this, handler](QString error, QVariantMap result) { @@ -222,25 +222,25 @@ Promise AssetScriptingInterface::jsPromiseReady(Promise promise, QScriptValue sc }); } -void AssetScriptingInterface::jsCallback(const QScriptValue& handler, - const QScriptValue& error, const QScriptValue& result) { +void AssetScriptingInterface::jsCallback(const ScriptValuePointer& handler, + const ScriptValuePointer& error, const ScriptValuePointer& result) { Q_ASSERT(thread() == QThread::currentThread()); - auto errorValue = !error.toBool() ? QScriptValue::NullValue : error; - JS_VERIFY(handler.isObject() && handler.property("callback").isFunction(), + auto errorValue = !error->toBool() ? engine()->nullValue() : error; + JS_VERIFY(handler->isObject() && handler->property("callback")->isFunction(), QString("jsCallback -- .callback is not a function (%1)") - .arg(handler.property("callback").toVariant().typeName())); + .arg(handler->property("callback")->toVariant().typeName())); ::callScopedHandlerObject(handler, errorValue, result); } -void AssetScriptingInterface::jsCallback(const QScriptValue& handler, - const QScriptValue& error, const QVariantMap& result) { +void AssetScriptingInterface::jsCallback(const ScriptValuePointer& handler, + const ScriptValuePointer& error, const QVariantMap& result) { Q_ASSERT(thread() == QThread::currentThread()); - Q_ASSERT(handler.engine()); - auto engine = handler.engine(); + Q_ASSERT(handler->engine()); + auto engine = handler->engine(); jsCallback(handler, error, engine->toScriptValue(result)); } -void AssetScriptingInterface::deleteAsset(QScriptValue options, QScriptValue scope, QScriptValue callback) { +void AssetScriptingInterface::deleteAsset(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback) { jsVerify(false, "TODO: deleteAsset API"); } @@ -270,14 +270,14 @@ void AssetScriptingInterface::deleteAsset(QScriptValue options, QScriptValue sco * @property {boolean} [wasRedirected] - true if the downloaded data is the baked version of the asset, * false if it isn't baked. */ -void AssetScriptingInterface::getAsset(QScriptValue options, QScriptValue scope, QScriptValue callback) { - JS_VERIFY(options.isObject() || options.isString(), "expected request options Object or URL as first parameter"); - - auto decompress = options.property("decompress").toBool() || options.property("compressed").toBool(); - auto responseType = options.property("responseType").toString().toLower(); - auto url = options.property("url").toString(); - if (options.isString()) { - url = options.toString(); +void AssetScriptingInterface::getAsset(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback) { + JS_VERIFY(options->isObject() || options->isString(), "expected request options Object or URL as first parameter"); + + auto decompress = options->property("decompress")->toBool() || options->property("compressed")->toBool(); + auto responseType = options->property("responseType")->toString().toLower(); + auto url = options->property("url")->toString(); + if (options->isString()) { + url = options->toString(); } if (responseType.isEmpty()) { responseType = "text"; @@ -336,10 +336,10 @@ void AssetScriptingInterface::getAsset(QScriptValue options, QScriptValue scope, * @property {boolean} [wasRedirected] - true if the resolved data is for the baked version of the asset, * false if it isn't. */ -void AssetScriptingInterface::resolveAsset(QScriptValue options, QScriptValue scope, QScriptValue callback) { +void AssetScriptingInterface::resolveAsset(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback) { const QString& URL{ "url" }; - auto url = (options.isString() ? options : options.property(URL)).toString(); + auto url = (options->isString() ? options : options->property(URL))->toString(); auto asset = AssetUtils::getATPUrl(url).path(); JS_VERIFY(AssetUtils::isValidFilePath(asset) || AssetUtils::isValidHash(asset), @@ -363,10 +363,10 @@ void AssetScriptingInterface::resolveAsset(QScriptValue options, QScriptValue sc * @property {string|object|ArrayBuffer} [response] - The decompressed data. * @property {Assets.ResponseType} [responseType] - The type of the decompressed data in response. */ -void AssetScriptingInterface::decompressData(QScriptValue options, QScriptValue scope, QScriptValue callback) { - auto data = options.property("data"); - QByteArray dataByteArray = qscriptvalue_cast(data); - auto responseType = options.property("responseType").toString().toLower(); +void AssetScriptingInterface::decompressData(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback) { + auto data = options->property("data"); + QByteArray dataByteArray = scriptvalue_cast(data); + auto responseType = options->property("responseType")->toString().toLower(); if (responseType.isEmpty()) { responseType = "text"; } @@ -404,10 +404,10 @@ namespace { * @property {string} [contentType] - The MIME type of the compressed data, i.e., "application/gzip". * @property {ArrayBuffer} [data] - The compressed data. */ -void AssetScriptingInterface::compressData(QScriptValue options, QScriptValue scope, QScriptValue callback) { - auto data = options.property("data").isValid() ? options.property("data") : options; - QByteArray dataByteArray = data.isString() ? data.toString().toUtf8() : qscriptvalue_cast(data); - int level = options.property("level").isNumber() ? options.property("level").toInt32() : DEFAULT_GZIP_COMPRESSION_LEVEL; +void AssetScriptingInterface::compressData(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback) { + auto data = options->property("data")->isValid() ? options->property("data") : options; + QByteArray dataByteArray = data->isString() ? data->toString().toUtf8() : scriptvalue_cast(data); + int level = options->property("level")->isNumber() ? options->property("level")->toInt32() : DEFAULT_GZIP_COMPRESSION_LEVEL; JS_VERIFY(level >= DEFAULT_GZIP_COMPRESSION_LEVEL || level <= MAX_GZIP_COMPRESSION_LEVEL, QString("invalid .level %1").arg(level)); jsPromiseReady(compressBytes(dataByteArray, level), scope, callback); } @@ -433,18 +433,18 @@ void AssetScriptingInterface::compressData(QScriptValue options, QScriptValue sc * @property {string} [url] - The atp: URL of the content: using the path if specified, otherwise the hash. * @property {string} [path] - The uploaded content's mapped path, if specified. */ -void AssetScriptingInterface::putAsset(QScriptValue options, QScriptValue scope, QScriptValue callback) { - auto compress = options.property("compress").toBool() || options.property("compressed").toBool(); - auto data = options.isObject() ? options.property("data") : options; - auto rawPath = options.property("path").toString(); +void AssetScriptingInterface::putAsset(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback) { + auto compress = options->property("compress")->toBool() || options->property("compressed")->toBool(); + auto data = options->isObject() ? options->property("data") : options; + auto rawPath = options->property("path")->toString(); auto path = AssetUtils::getATPUrl(rawPath).path(); - QByteArray dataByteArray = data.isString() ? data.toString().toUtf8() : qscriptvalue_cast(data); + QByteArray dataByteArray = data->isString() ? data->toString().toUtf8() : scriptvalue_cast(data); JS_VERIFY(path.isEmpty() || AssetUtils::isValidFilePath(path), QString("expected valid ATP file path '%1' ('%2')").arg(rawPath).arg(path)); JS_VERIFY(dataByteArray.size() > 0, - QString("expected non-zero .data (got %1 / #%2 bytes)").arg(data.toVariant().typeName()).arg(dataByteArray.size())); + QString("expected non-zero .data (got %1 / #%2 bytes)").arg(data->toVariant().typeName()).arg(dataByteArray.size())); // [compressed] => uploaded to server => [mapped to path] Promise prepared = makePromise("putAsset::prepared"); @@ -489,8 +489,8 @@ void AssetScriptingInterface::putAsset(QScriptValue options, QScriptValue scope, * @property {string} url - The URL of the cached asset to get information on. Must start with "atp:" or * "cache:". */ -void AssetScriptingInterface::queryCacheMeta(QScriptValue options, QScriptValue scope, QScriptValue callback) { - QString url = options.isString() ? options.toString() : options.property("url").toString(); +void AssetScriptingInterface::queryCacheMeta(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback) { + QString url = options->isString() ? options->toString() : options->property("url")->toString(); JS_VERIFY(QUrl(url).isValid(), QString("Invalid URL '%1'").arg(url)); jsPromiseReady(Parent::queryCacheMeta(url), scope, callback); } @@ -504,16 +504,16 @@ void AssetScriptingInterface::queryCacheMeta(QScriptValue options, QScriptValue * @property {string} url - The URL of the asset to load from cache. Must start with "atp:" or * "cache:". */ -void AssetScriptingInterface::loadFromCache(QScriptValue options, QScriptValue scope, QScriptValue callback) { +void AssetScriptingInterface::loadFromCache(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback) { QString url, responseType; bool decompress = false; - if (options.isString()) { - url = options.toString(); + if (options->isString()) { + url = options->toString(); responseType = "text"; } else { - url = options.property("url").toString(); - responseType = options.property("responseType").isValid() ? options.property("responseType").toString() : "text"; - decompress = options.property("decompress").toBool() || options.property("compressed").toBool(); + url = options->property("url")->toString(); + responseType = options->property("responseType")->isValid() ? options->property("responseType")->toString() : "text"; + decompress = options->property("decompress")->toBool() || options->property("compressed")->toBool(); } JS_VERIFY(QUrl(url).isValid(), QString("Invalid URL '%1'").arg(url)); JS_VERIFY(RESPONSE_TYPES.contains(responseType), @@ -523,14 +523,14 @@ void AssetScriptingInterface::loadFromCache(QScriptValue options, QScriptValue s } bool AssetScriptingInterface::canWriteCacheValue(const QUrl& url) { - auto scriptEngine = qobject_cast(engine()); - if (!scriptEngine) { + auto scriptManager = engine()->manager(); + if (!scriptManager) { return false; } // allow cache writes only from Client, EntityServer and Agent scripts bool isAllowedContext = ( - scriptEngine->isClientScript() || - scriptEngine->isAgentScript() + scriptManager->isClientScript() || + scriptManager->isAgentScript() ); if (!isAllowedContext) { return false; @@ -546,17 +546,17 @@ bool AssetScriptingInterface::canWriteCacheValue(const QUrl& url) { * @property {string} [url] - The URL to associate with the cache item. Must start with "atp:" or * "cache:". If not specified, the URL is "atp:" followed by the SHA256 hash of the content. */ -void AssetScriptingInterface::saveToCache(QScriptValue options, QScriptValue scope, QScriptValue callback) { - JS_VERIFY(options.isObject(), QString("expected options object as first parameter not: %1").arg(options.toVariant().typeName())); +void AssetScriptingInterface::saveToCache(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback) { + JS_VERIFY(options->isObject(), QString("expected options object as first parameter not: %1").arg(options->toVariant().typeName())); - QString url = options.property("url").toString(); - QByteArray data = qscriptvalue_cast(options.property("data")); - QVariantMap headers = qscriptvalue_cast(options.property("headers")); + QString url = options->property("url")->toString(); + QByteArray data = scriptvalue_cast(options->property("data")); + QVariantMap headers = scriptvalue_cast(options->property("headers")); saveToCache(url, data, headers, scope, callback); } -void AssetScriptingInterface::saveToCache(const QUrl& rawURL, const QByteArray& data, const QVariantMap& metadata, QScriptValue scope, QScriptValue callback) { +void AssetScriptingInterface::saveToCache(const QUrl& rawURL, const QByteArray& data, const QVariantMap& metadata, ScriptValuePointer scope, ScriptValuePointer callback) { QUrl url = rawURL; if (url.path().isEmpty() && !data.isEmpty()) { // generate a valid ATP URL from the data -- appending any existing fragment or querystring values diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h index 3ba7632cd0a..8871fdebf6a 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.h +++ b/libraries/script-engine/src/AssetScriptingInterface.h @@ -16,13 +16,16 @@ #include #include -#include -#include #include #include #include -#include #include +#include + +#include "Scriptable.h" + +class ScriptValue; +using ScriptValuePointer = QSharedPointer; /**jsdoc * The Assets API provides facilities for interacting with the domain's asset server and the client cache. @@ -41,7 +44,7 @@ * @hifi-server-entity * @hifi-assignment-client */ -class AssetScriptingInterface : public BaseAssetScriptingInterface, QScriptable { +class AssetScriptingInterface : public BaseAssetScriptingInterface, Scriptable { Q_OBJECT public: using Parent = BaseAssetScriptingInterface; @@ -72,7 +75,7 @@ class AssetScriptingInterface : public BaseAssetScriptingInterface, QScriptable * }); * }); */ - Q_INVOKABLE void uploadData(QString data, QScriptValue callback); + Q_INVOKABLE void uploadData(QString data, ScriptValuePointer callback); /**jsdoc * Called when an {@link Assets.downloadData} call is complete. @@ -108,7 +111,7 @@ class AssetScriptingInterface : public BaseAssetScriptingInterface, QScriptable * }); * }, 1000); */ - Q_INVOKABLE void downloadData(QString url, QScriptValue callback); + Q_INVOKABLE void downloadData(QString url, ScriptValuePointer callback); /**jsdoc * Called when an {@link Assets.setMapping} call is complete. @@ -122,7 +125,7 @@ class AssetScriptingInterface : public BaseAssetScriptingInterface, QScriptable * @param {string} hash - The hash in the asset server. * @param {Assets~setMappingCallback} callback - The function to call upon completion. */ - Q_INVOKABLE void setMapping(QString path, QString hash, QScriptValue callback); + Q_INVOKABLE void setMapping(QString path, QString hash, ScriptValuePointer callback); /**jsdoc * Called when an {@link Assets.getMapping} call is complete. @@ -146,7 +149,7 @@ class AssetScriptingInterface : public BaseAssetScriptingInterface, QScriptable * }); * } */ - Q_INVOKABLE void getMapping(QString path, QScriptValue callback); + Q_INVOKABLE void getMapping(QString path, ScriptValuePointer callback); /**jsdoc * Called when an {@link Assets.setBakingEnabled} call is complete. @@ -162,7 +165,7 @@ class AssetScriptingInterface : public BaseAssetScriptingInterface, QScriptable * @param {Assets~setBakingEnabledCallback} callback - The function to call upon completion. */ // Note: Second callback parameter not documented because it's always {}. - Q_INVOKABLE void setBakingEnabled(QString path, bool enabled, QScriptValue callback); + Q_INVOKABLE void setBakingEnabled(QString path, bool enabled, ScriptValuePointer callback); #if (PR_BUILD || DEV_BUILD) /** @@ -218,7 +221,7 @@ class AssetScriptingInterface : public BaseAssetScriptingInterface, QScriptable * } * ); */ - Q_INVOKABLE void getAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); + Q_INVOKABLE void getAsset(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback = ScriptValuePointer()); /**jsdoc * Called when an {@link Assets.putAsset} call is complete. @@ -255,7 +258,7 @@ class AssetScriptingInterface : public BaseAssetScriptingInterface, QScriptable * } * ); */ - Q_INVOKABLE void putAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); + Q_INVOKABLE void putAsset(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback = ScriptValuePointer()); /**jsdoc * Called when an {@link Assets.deleteAsset} call is complete. @@ -272,7 +275,7 @@ class AssetScriptingInterface : public BaseAssetScriptingInterface, QScriptable * @param {object} scope - The scope that the callback function is defined in. * @param {Assets~deleteAssetCallback} callback - The function to call upon completion. */ - Q_INVOKABLE void deleteAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); + Q_INVOKABLE void deleteAsset(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback = ScriptValuePointer()); /**jsdoc * Called when an {@link Assets.resolveAsset} call is complete. @@ -306,7 +309,7 @@ class AssetScriptingInterface : public BaseAssetScriptingInterface, QScriptable * } * ); */ - Q_INVOKABLE void resolveAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); + Q_INVOKABLE void resolveAsset(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback = ScriptValuePointer()); /**jsdoc * Called when an {@link Assets.decompressData} call is complete. @@ -327,7 +330,7 @@ class AssetScriptingInterface : public BaseAssetScriptingInterface, QScriptable * in a string. If the name of a function or a function identifier, it must be a member of the scope specified by * scopeOrCallback.

*/ - Q_INVOKABLE void decompressData(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); + Q_INVOKABLE void decompressData(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback = ScriptValuePointer()); /**jsdoc * Called when an {@link Assets.compressData} call is complete. @@ -349,7 +352,7 @@ class AssetScriptingInterface : public BaseAssetScriptingInterface, QScriptable * in a string. If the name of a function or a function identifier, it must be a member of the scope specified by * scopeOrCallback.

*/ - Q_INVOKABLE void compressData(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); + Q_INVOKABLE void compressData(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback = ScriptValuePointer()); /**jsdoc * Initializes the cache if it isn't already initialized. @@ -394,7 +397,7 @@ class AssetScriptingInterface : public BaseAssetScriptingInterface, QScriptable * print("- Status: " + JSON.stringify(status)); * }); */ - Q_INVOKABLE void getCacheStatus(QScriptValue scope, QScriptValue callback = QScriptValue()) { + Q_INVOKABLE void getCacheStatus(ScriptValuePointer scope, ScriptValuePointer callback = ScriptValuePointer()) { jsPromiseReady(Parent::getCacheStatus(), scope, callback); } @@ -434,7 +437,7 @@ class AssetScriptingInterface : public BaseAssetScriptingInterface, QScriptable * } * ); */ - Q_INVOKABLE void queryCacheMeta(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); + Q_INVOKABLE void queryCacheMeta(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback = ScriptValuePointer()); /**jsdoc * Called when an {@link Assets.loadFromCache} call is complete. @@ -474,7 +477,7 @@ class AssetScriptingInterface : public BaseAssetScriptingInterface, QScriptable * } * ); */ - Q_INVOKABLE void loadFromCache(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); + Q_INVOKABLE void loadFromCache(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback = ScriptValuePointer()); /**jsdoc * Called when an {@link Assets.saveToCache} call is complete. @@ -513,7 +516,7 @@ class AssetScriptingInterface : public BaseAssetScriptingInterface, QScriptable * } * ); */ - Q_INVOKABLE void saveToCache(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); + Q_INVOKABLE void saveToCache(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback = ScriptValuePointer()); /**jsdoc * Saves asset data to the cache directly, without downloading it from a URL. @@ -533,13 +536,13 @@ class AssetScriptingInterface : public BaseAssetScriptingInterface, QScriptable * scopeOrCallback.

*/ Q_INVOKABLE void saveToCache(const QUrl& url, const QByteArray& data, const QVariantMap& metadata, - QScriptValue scope, QScriptValue callback = QScriptValue()); + ScriptValuePointer scope, ScriptValuePointer callback = ScriptValuePointer()); protected: - QScriptValue jsBindCallback(QScriptValue scope, QScriptValue callback = QScriptValue()); - Promise jsPromiseReady(Promise promise, QScriptValue scope, QScriptValue callback = QScriptValue()); + ScriptValuePointer jsBindCallback(ScriptValuePointer scope, ScriptValuePointer callback = ScriptValuePointer()); + Promise jsPromiseReady(Promise promise, ScriptValuePointer scope, ScriptValuePointer callback = ScriptValuePointer()); - void jsCallback(const QScriptValue& handler, const QScriptValue& error, const QVariantMap& result); - void jsCallback(const QScriptValue& handler, const QScriptValue& error, const QScriptValue& result); + void jsCallback(const ScriptValuePointer& handler, const ScriptValuePointer& error, const QVariantMap& result); + void jsCallback(const ScriptValuePointer& handler, const ScriptValuePointer& error, const ScriptValuePointer& result); bool jsVerify(bool condition, const QString& error); }; diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index a55cac292f8..ce0da698a5b 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -17,10 +17,11 @@ #include "ScriptAudioInjector.h" #include "ScriptEngineLogging.h" +#include "ScriptEngine.h" -void registerAudioMetaTypes(QScriptEngine* engine) { - qScriptRegisterMetaType(engine, injectorOptionsToScriptValue, injectorOptionsFromScriptValue); - qScriptRegisterMetaType(engine, soundSharedPointerToScriptValue, soundSharedPointerFromScriptValue); +void registerAudioMetaTypes(ScriptEngine* engine) { + scriptRegisterMetaType(engine, injectorOptionsToScriptValue, injectorOptionsFromScriptValue); + scriptRegisterMetaType(engine, soundSharedPointerToScriptValue, soundSharedPointerFromScriptValue); } diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index 0553679f8a4..05483febc22 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -18,6 +18,7 @@ #include class ScriptAudioInjector; +class ScriptEngine; class AudioScriptingInterface : public QObject, public Dependency { Q_OBJECT @@ -287,6 +288,6 @@ class AudioScriptingInterface : public QObject, public Dependency { AbstractAudioInterface* _localAudioInterface { nullptr }; }; -void registerAudioMetaTypes(QScriptEngine* engine); +void registerAudioMetaTypes(ScriptEngine* engine); #endif // hifi_AudioScriptingInterface_h diff --git a/libraries/script-engine/src/ConsoleScriptingInterface.cpp b/libraries/script-engine/src/ConsoleScriptingInterface.cpp index 60de04aa9ea..b36bd547a14 100644 --- a/libraries/script-engine/src/ConsoleScriptingInterface.cpp +++ b/libraries/script-engine/src/ConsoleScriptingInterface.cpp @@ -19,7 +19,10 @@ #include +#include "ScriptContext.h" #include "ScriptEngine.h" +#include "ScriptManager.h" +#include "ScriptValue.h" #define INDENTATION 4 // 1 Tab - 4 spaces const QString LINE_SEPARATOR = "\n "; @@ -27,71 +30,71 @@ const QString SPACE_SEPARATOR = " "; const QString STACK_TRACE_FORMAT = "\n[Stacktrace]%1%2"; QList ConsoleScriptingInterface::_groupDetails = QList(); -QScriptValue ConsoleScriptingInterface::info(QScriptContext* context, QScriptEngine* engine) { - if (ScriptEngine* scriptEngine = qobject_cast(engine)) { - scriptEngine->scriptInfoMessage(appendArguments(context)); +ScriptValuePointer ConsoleScriptingInterface::info(ScriptContext* context, ScriptEngine* engine) { + if (ScriptManager* scriptManager = engine->manager()) { + scriptManager->scriptInfoMessage(appendArguments(context)); } - return QScriptValue::NullValue; + return engine->nullValue(); } -QScriptValue ConsoleScriptingInterface::log(QScriptContext* context, QScriptEngine* engine) { +ScriptValuePointer ConsoleScriptingInterface::log(ScriptContext* context, ScriptEngine* engine) { QString message = appendArguments(context); if (_groupDetails.count() == 0) { - if (ScriptEngine* scriptEngine = qobject_cast(engine)) { - scriptEngine->scriptPrintedMessage(message); + if (ScriptManager* scriptManager = engine->manager()) { + scriptManager->scriptPrintedMessage(message); } } else { logGroupMessage(message, engine); } - return QScriptValue::NullValue; + return engine->nullValue(); } -QScriptValue ConsoleScriptingInterface::debug(QScriptContext* context, QScriptEngine* engine) { - if (ScriptEngine* scriptEngine = qobject_cast(engine)) { - scriptEngine->scriptPrintedMessage(appendArguments(context)); +ScriptValuePointer ConsoleScriptingInterface::debug(ScriptContext* context, ScriptEngine* engine) { + if (ScriptManager* scriptManager = engine->manager()) { + scriptManager->scriptPrintedMessage(appendArguments(context)); } - return QScriptValue::NullValue; + return engine->nullValue(); } -QScriptValue ConsoleScriptingInterface::warn(QScriptContext* context, QScriptEngine* engine) { - if (ScriptEngine* scriptEngine = qobject_cast(engine)) { - scriptEngine->scriptWarningMessage(appendArguments(context)); +ScriptValuePointer ConsoleScriptingInterface::warn(ScriptContext* context, ScriptEngine* engine) { + if (ScriptManager* scriptManager = engine->manager()) { + scriptManager->scriptWarningMessage(appendArguments(context)); } - return QScriptValue::NullValue; + return engine->nullValue(); } -QScriptValue ConsoleScriptingInterface::error(QScriptContext* context, QScriptEngine* engine) { - if (ScriptEngine* scriptEngine = qobject_cast(engine)) { - scriptEngine->scriptErrorMessage(appendArguments(context)); +ScriptValuePointer ConsoleScriptingInterface::error(ScriptContext* context, ScriptEngine* engine) { + if (ScriptManager* scriptManager = engine->manager()) { + scriptManager->scriptErrorMessage(appendArguments(context)); } - return QScriptValue::NullValue; + return engine->nullValue(); } -QScriptValue ConsoleScriptingInterface::exception(QScriptContext* context, QScriptEngine* engine) { - if (ScriptEngine* scriptEngine = qobject_cast(engine)) { - scriptEngine->scriptErrorMessage(appendArguments(context)); +ScriptValuePointer ConsoleScriptingInterface::exception(ScriptContext* context, ScriptEngine* engine) { + if (ScriptManager* scriptManager = engine->manager()) { + scriptManager->scriptErrorMessage(appendArguments(context)); } - return QScriptValue::NullValue; + return engine->nullValue(); } void ConsoleScriptingInterface::time(QString labelName) { _timerDetails.insert(labelName, QDateTime::currentDateTime().toUTC()); QString message = QString("%1: Timer started").arg(labelName); - if (ScriptEngine* scriptEngine = qobject_cast(engine())) { - scriptEngine->scriptPrintedMessage(message); + if (ScriptManager* scriptManager = engine()->manager()) { + scriptManager->scriptPrintedMessage(message); } } void ConsoleScriptingInterface::timeEnd(QString labelName) { - if (ScriptEngine* scriptEngine = qobject_cast(engine())) { + if (ScriptManager* scriptManager = engine()->manager()) { if (!_timerDetails.contains(labelName)) { - scriptEngine->scriptErrorMessage("No such label found " + labelName); + scriptManager->scriptErrorMessage("No such label found " + labelName); return; } if (_timerDetails.value(labelName).isNull()) { _timerDetails.remove(labelName); - scriptEngine->scriptErrorMessage("Invalid start time for " + labelName); + scriptManager->scriptErrorMessage("Invalid start time for " + labelName); return; } QDateTime _startTime = _timerDetails.value(labelName); @@ -101,18 +104,18 @@ void ConsoleScriptingInterface::timeEnd(QString labelName) { QString message = QString("%1: %2ms").arg(labelName).arg(QString::number(diffInMS)); _timerDetails.remove(labelName); - scriptEngine->scriptPrintedMessage(message); + scriptManager->scriptPrintedMessage(message); } } -QScriptValue ConsoleScriptingInterface::assertion(QScriptContext* context, QScriptEngine* engine) { +ScriptValuePointer ConsoleScriptingInterface::assertion(ScriptContext* context, ScriptEngine* engine) { QString message; bool condition = false; for (int i = 0; i < context->argumentCount(); i++) { if (i == 0) { - condition = context->argument(i).toBool(); // accept first value as condition + condition = context->argument(i)->toBool(); // accept first value as condition } else { - message += SPACE_SEPARATOR + context->argument(i).toString(); // accept other parameters as message + message += SPACE_SEPARATOR + context->argument(i)->toString(); // accept other parameters as message } } @@ -123,63 +126,64 @@ QScriptValue ConsoleScriptingInterface::assertion(QScriptContext* context, QScri } else { assertionResult = QString("Assertion failed : %1").arg(message); } - if (ScriptEngine* scriptEngine = qobject_cast(engine)) { - scriptEngine->scriptErrorMessage(assertionResult); + if (ScriptManager* scriptManager = engine->manager()) { + scriptManager->scriptErrorMessage(assertionResult); } } - return QScriptValue::NullValue; + return engine->nullValue(); } void ConsoleScriptingInterface::trace() { - if (ScriptEngine* scriptEngine = qobject_cast(engine())) { - scriptEngine->scriptPrintedMessage + ScriptEnginePointer scriptEngine = engine(); + if (ScriptManager* scriptManager = scriptEngine->manager()) { + scriptManager->scriptPrintedMessage (QString(STACK_TRACE_FORMAT).arg(LINE_SEPARATOR, scriptEngine->currentContext()->backtrace().join(LINE_SEPARATOR))); } } void ConsoleScriptingInterface::clear() { - if (ScriptEngine* scriptEngine = qobject_cast(engine())) { - scriptEngine->clearDebugLogWindow(); + if (ScriptManager* scriptManager = engine()->manager()) { + scriptManager->clearDebugLogWindow(); } } -QScriptValue ConsoleScriptingInterface::group(QScriptContext* context, QScriptEngine* engine) { - logGroupMessage(context->argument(0).toString(), engine); // accept first parameter as label - _groupDetails.push_back(context->argument(0).toString()); - return QScriptValue::NullValue; +ScriptValuePointer ConsoleScriptingInterface::group(ScriptContext* context, ScriptEngine* engine) { + logGroupMessage(context->argument(0)->toString(), engine); // accept first parameter as label + _groupDetails.push_back(context->argument(0)->toString()); + return engine->nullValue(); } -QScriptValue ConsoleScriptingInterface::groupCollapsed(QScriptContext* context, QScriptEngine* engine) { - logGroupMessage(context->argument(0).toString(), engine); // accept first parameter as label - _groupDetails.push_back(context->argument(0).toString()); - return QScriptValue::NullValue; +ScriptValuePointer ConsoleScriptingInterface::groupCollapsed(ScriptContext* context, ScriptEngine* engine) { + logGroupMessage(context->argument(0)->toString(), engine); // accept first parameter as label + _groupDetails.push_back(context->argument(0)->toString()); + return engine->nullValue(); } -QScriptValue ConsoleScriptingInterface::groupEnd(QScriptContext* context, QScriptEngine* engine) { +ScriptValuePointer ConsoleScriptingInterface::groupEnd(ScriptContext* context, ScriptEngine* engine) { ConsoleScriptingInterface::_groupDetails.removeLast(); - return QScriptValue::NullValue; + return engine->nullValue(); } -QString ConsoleScriptingInterface::appendArguments(QScriptContext* context) { +QString ConsoleScriptingInterface::appendArguments(ScriptContext* context) { QString message; for (int i = 0; i < context->argumentCount(); i++) { if (i > 0) { message += SPACE_SEPARATOR; } - message += context->argument(i).toString(); + message += context->argument(i)->toString(); } return message; } -void ConsoleScriptingInterface::logGroupMessage(QString message, QScriptEngine* engine) { +void ConsoleScriptingInterface::logGroupMessage(QString message, ScriptEngine* engine) { int _addSpaces = _groupDetails.count() * INDENTATION; QString logMessage; for (int i = 0; i < _addSpaces; i++) { logMessage.append(SPACE_SEPARATOR); } logMessage.append(message); - if (ScriptEngine* scriptEngine = qobject_cast(engine)) { - scriptEngine->scriptPrintedMessage(logMessage); + if (ScriptManager* scriptManager = engine->manager()) { + scriptManager->scriptPrintedMessage(logMessage); } } diff --git a/libraries/script-engine/src/ConsoleScriptingInterface.h b/libraries/script-engine/src/ConsoleScriptingInterface.h index 7eec44d1037..82fbfc9e410 100644 --- a/libraries/script-engine/src/ConsoleScriptingInterface.h +++ b/libraries/script-engine/src/ConsoleScriptingInterface.h @@ -24,7 +24,14 @@ #include #include #include -#include +#include + +#include "Scriptable.h" + +class ScriptContext; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; /**jsdoc * The console API provides program logging facilities. @@ -38,7 +45,7 @@ * @hifi-assignment-client */ // Scriptable interface of "console" object. Used exclusively in the JavaScript API -class ConsoleScriptingInterface : public QObject, protected QScriptable { +class ConsoleScriptingInterface : public QObject, protected Scriptable { Q_OBJECT public: @@ -48,7 +55,7 @@ class ConsoleScriptingInterface : public QObject, protected QScriptable { * @function console.info * @param {...*} [message] - The message values to log. */ - static QScriptValue info(QScriptContext* context, QScriptEngine* engine); + static ScriptValuePointer info(ScriptContext* context, ScriptEngine* engine); /**jsdoc * Logs a message to the program log and triggers {@link Script.printedMessage}. @@ -66,7 +73,7 @@ class ConsoleScriptingInterface : public QObject, protected QScriptable { * // string 7 true * // INFO - Console.log message: "string 7 true" in [console.js] */ - static QScriptValue log(QScriptContext* context, QScriptEngine* engine); + static ScriptValuePointer log(ScriptContext* context, ScriptEngine* engine); /**jsdoc * Logs a message to the program log and triggers {@link Script.printedMessage}. @@ -74,7 +81,7 @@ class ConsoleScriptingInterface : public QObject, protected QScriptable { * @function console.debug * @param {...*} [message] - The message values to log. */ - static QScriptValue debug(QScriptContext* context, QScriptEngine* engine); + static ScriptValuePointer debug(ScriptContext* context, ScriptEngine* engine); /**jsdoc * Logs a "WARNING" message to the program log and triggers {@link Script.warningMessage}. @@ -82,7 +89,7 @@ class ConsoleScriptingInterface : public QObject, protected QScriptable { * @function console.warn * @param {...*} [message] - The message values to log. */ - static QScriptValue warn(QScriptContext* context, QScriptEngine* engine); + static ScriptValuePointer warn(ScriptContext* context, ScriptEngine* engine); /**jsdoc * Logs an "ERROR" message to the program log and triggers {@link Script.errorMessage}. @@ -90,7 +97,7 @@ class ConsoleScriptingInterface : public QObject, protected QScriptable { * @function console.error * @param {...*} [message] - The message values to log. */ - static QScriptValue error(QScriptContext* context, QScriptEngine* engine); + static ScriptValuePointer error(ScriptContext* context, ScriptEngine* engine); /**jsdoc * A synonym of {@link console.error}. @@ -99,7 +106,7 @@ class ConsoleScriptingInterface : public QObject, protected QScriptable { * @function console.exception * @param {...*} [message] - The message values to log. */ - static QScriptValue exception(QScriptContext* context, QScriptEngine* engine); + static ScriptValuePointer exception(ScriptContext* context, ScriptEngine* engine); /**jsdoc * Logs an "ERROR" message to the program log and triggers {@link Script.errorMessage}, if a test condition fails. @@ -122,7 +129,7 @@ class ConsoleScriptingInterface : public QObject, protected QScriptable { * // INFO - Script continues running. */ // Note: Is registered in the script engine as "assert" - static QScriptValue assertion(QScriptContext* context, QScriptEngine* engine); + static ScriptValuePointer assertion(ScriptContext* context, ScriptEngine* engine); /**jsdoc * Logs a message to the program log and triggers {@link Script.printedMessage}, then starts indenting subsequent @@ -150,7 +157,7 @@ class ConsoleScriptingInterface : public QObject, protected QScriptable { * // Sentence 5 * //Sentence 6 */ - static QScriptValue group(QScriptContext* context, QScriptEngine* engine); + static ScriptValuePointer group(ScriptContext* context, ScriptEngine* engine); /**jsdoc * Has the same behavior as {@link console.group}. @@ -159,13 +166,13 @@ class ConsoleScriptingInterface : public QObject, protected QScriptable { * @function console.groupCollapsed * @param {*} message - The message value to log. */ - static QScriptValue groupCollapsed(QScriptContext* context, QScriptEngine* engine); + static ScriptValuePointer groupCollapsed(ScriptContext* context, ScriptEngine* engine); /**jsdoc * Finishes a group of indented {@link console.log} messages. * @function console.groupEnd */ - static QScriptValue groupEnd(QScriptContext* context, QScriptEngine* engine); + static ScriptValuePointer groupEnd(ScriptContext* context, ScriptEngine* engine); public slots: @@ -215,8 +222,8 @@ public slots: private: QHash _timerDetails; static QList _groupDetails; - static void logGroupMessage(QString message, QScriptEngine* engine); - static QString appendArguments(QScriptContext* context); + static void logGroupMessage(QString message, ScriptEngine* engine); + static QString appendArguments(ScriptContext* context); }; #endif // hifi_ConsoleScriptingInterface_h diff --git a/libraries/script-engine/src/EventTypes.cpp b/libraries/script-engine/src/EventTypes.cpp index 94c074d44e1..8fcdfde7551 100644 --- a/libraries/script-engine/src/EventTypes.cpp +++ b/libraries/script-engine/src/EventTypes.cpp @@ -13,16 +13,17 @@ #include "KeyEvent.h" #include "MouseEvent.h" -#include "SpatialEvent.h" #include "PointerEvent.h" +#include "ScriptEngine.h" +#include "SpatialEvent.h" #include "TouchEvent.h" #include "WheelEvent.h" -void registerEventTypes(QScriptEngine* engine) { - qScriptRegisterMetaType(engine, KeyEvent::toScriptValue, KeyEvent::fromScriptValue); - qScriptRegisterMetaType(engine, MouseEvent::toScriptValue, MouseEvent::fromScriptValue); - qScriptRegisterMetaType(engine, PointerEvent::toScriptValue, PointerEvent::fromScriptValue); - qScriptRegisterMetaType(engine, TouchEvent::toScriptValue, TouchEvent::fromScriptValue); - qScriptRegisterMetaType(engine, WheelEvent::toScriptValue, WheelEvent::fromScriptValue); - qScriptRegisterMetaType(engine, SpatialEvent::toScriptValue, SpatialEvent::fromScriptValue); +void registerEventTypes(ScriptEngine* engine) { + scriptRegisterMetaType(engine, KeyEvent::toScriptValue, KeyEvent::fromScriptValue); + scriptRegisterMetaType(engine, MouseEvent::toScriptValue, MouseEvent::fromScriptValue); + scriptRegisterMetaType(engine, PointerEvent::toScriptValue, PointerEvent::fromScriptValue); + scriptRegisterMetaType(engine, TouchEvent::toScriptValue, TouchEvent::fromScriptValue); + scriptRegisterMetaType(engine, WheelEvent::toScriptValue, WheelEvent::fromScriptValue); + scriptRegisterMetaType(engine, SpatialEvent::toScriptValue, SpatialEvent::fromScriptValue); } diff --git a/libraries/script-engine/src/EventTypes.h b/libraries/script-engine/src/EventTypes.h index 146ab338d61..26128ea2ec4 100644 --- a/libraries/script-engine/src/EventTypes.h +++ b/libraries/script-engine/src/EventTypes.h @@ -12,8 +12,8 @@ #ifndef hifi_EventTypes_h #define hifi_EventTypes_h -#include +class ScriptEngine; -void registerEventTypes(QScriptEngine* engine); +void registerEventTypes(ScriptEngine* engine); #endif // hifi_EventTypes_h diff --git a/libraries/script-engine/src/KeyEvent.cpp b/libraries/script-engine/src/KeyEvent.cpp index 1887486054f..598fd19a355 100644 --- a/libraries/script-engine/src/KeyEvent.cpp +++ b/libraries/script-engine/src/KeyEvent.cpp @@ -12,9 +12,10 @@ #include "KeyEvent.h" #include -#include #include "ScriptEngineLogging.h" +#include "ScriptEngine.h" +#include "ScriptValue.h" KeyEvent::KeyEvent() : key(0), @@ -173,37 +174,37 @@ KeyEvent::operator QKeySequence() const { * print(JSON.stringify(event)); * }); */ -QScriptValue KeyEvent::toScriptValue(QScriptEngine* engine, const KeyEvent& event) { - QScriptValue obj = engine->newObject(); - obj.setProperty("key", event.key); - obj.setProperty("text", event.text); - obj.setProperty("isShifted", event.isShifted); - obj.setProperty("isMeta", event.isMeta); - obj.setProperty("isControl", event.isControl); - obj.setProperty("isAlt", event.isAlt); - obj.setProperty("isKeypad", event.isKeypad); - obj.setProperty("isAutoRepeat", event.isAutoRepeat); +ScriptValuePointer KeyEvent::toScriptValue(ScriptEngine* engine, const KeyEvent& event) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty("key", event.key); + obj->setProperty("text", event.text); + obj->setProperty("isShifted", event.isShifted); + obj->setProperty("isMeta", event.isMeta); + obj->setProperty("isControl", event.isControl); + obj->setProperty("isAlt", event.isAlt); + obj->setProperty("isKeypad", event.isKeypad); + obj->setProperty("isAutoRepeat", event.isAutoRepeat); return obj; } -void KeyEvent::fromScriptValue(const QScriptValue& object, KeyEvent& event) { +void KeyEvent::fromScriptValue(const ScriptValuePointer& object, KeyEvent& event) { event.isValid = false; // assume the worst - event.isMeta = object.property("isMeta").toVariant().toBool(); - event.isControl = object.property("isControl").toVariant().toBool(); - event.isAlt = object.property("isAlt").toVariant().toBool(); - event.isKeypad = object.property("isKeypad").toVariant().toBool(); - event.isAutoRepeat = object.property("isAutoRepeat").toVariant().toBool(); + event.isMeta = object->property("isMeta")->toVariant().toBool(); + event.isControl = object->property("isControl")->toVariant().toBool(); + event.isAlt = object->property("isAlt")->toVariant().toBool(); + event.isKeypad = object->property("isKeypad")->toVariant().toBool(); + event.isAutoRepeat = object->property("isAutoRepeat")->toVariant().toBool(); - QScriptValue key = object.property("key"); - if (key.isValid()) { - event.key = key.toVariant().toInt(); + ScriptValuePointer key = object->property("key"); + if (key->isValid()) { + event.key = key->toVariant().toInt(); event.text = QString(QChar(event.key)); event.isValid = true; } else { - QScriptValue text = object.property("text"); - if (text.isValid()) { - event.text = object.property("text").toVariant().toString(); + ScriptValuePointer text = object->property("text"); + if (text->isValid()) { + event.text = object->property("text")->toVariant().toString(); // if the text is a special command, then map it here... // TODO: come up with more elegant solution here, a map? is there a Qt function that gives nice names for keys? @@ -282,9 +283,9 @@ void KeyEvent::fromScriptValue(const QScriptValue& object, KeyEvent& event) { } } - QScriptValue isShifted = object.property("isShifted"); - if (isShifted.isValid()) { - event.isShifted = isShifted.toVariant().toBool(); + ScriptValuePointer isShifted = object->property("isShifted"); + if (isShifted->isValid()) { + event.isShifted = isShifted->toVariant().toBool(); } else { // if no isShifted was included, get it from the text QChar character = event.text.at(0); diff --git a/libraries/script-engine/src/KeyEvent.h b/libraries/script-engine/src/KeyEvent.h index 4d66192a74f..237317bafd1 100644 --- a/libraries/script-engine/src/KeyEvent.h +++ b/libraries/script-engine/src/KeyEvent.h @@ -13,7 +13,11 @@ #define hifi_KeyEvent_h #include -#include +#include + +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; class KeyEvent { public: @@ -22,8 +26,8 @@ class KeyEvent { bool operator==(const KeyEvent& other) const; operator QKeySequence() const; - static QScriptValue toScriptValue(QScriptEngine* engine, const KeyEvent& event); - static void fromScriptValue(const QScriptValue& object, KeyEvent& event); + static ScriptValuePointer toScriptValue(ScriptEngine* engine, const KeyEvent& event); + static void fromScriptValue(const ScriptValuePointer& object, KeyEvent& event); int key; QString text; diff --git a/libraries/script-engine/src/MIDIEvent.cpp b/libraries/script-engine/src/MIDIEvent.cpp index b32c5d9d87e..779a3aec2ed 100644 --- a/libraries/script-engine/src/MIDIEvent.cpp +++ b/libraries/script-engine/src/MIDIEvent.cpp @@ -11,8 +11,13 @@ #include "MIDIEvent.h" -void registerMIDIMetaTypes(QScriptEngine* engine) { - qScriptRegisterMetaType(engine, midiEventToScriptValue, midiEventFromScriptValue); +#include + +#include "ScriptEngine.h" +#include "ScriptValue.h" + +void registerMIDIMetaTypes(ScriptEngine* engine) { + scriptRegisterMetaType(engine, midiEventToScriptValue, midiEventFromScriptValue); } const QString MIDI_DELTA_TIME_PROP_NAME = "deltaTime"; @@ -20,18 +25,18 @@ const QString MIDI_EVENT_TYPE_PROP_NAME = "type"; const QString MIDI_DATA_1_PROP_NAME = "data1"; const QString MIDI_DATA_2_PROP_NAME = "data2"; -QScriptValue midiEventToScriptValue(QScriptEngine* engine, const MIDIEvent& event) { - QScriptValue obj = engine->newObject(); - obj.setProperty(MIDI_DELTA_TIME_PROP_NAME, event.deltaTime); - obj.setProperty(MIDI_EVENT_TYPE_PROP_NAME, event.type); - obj.setProperty(MIDI_DATA_1_PROP_NAME, event.data1); - obj.setProperty(MIDI_DATA_2_PROP_NAME, event.data2); +ScriptValuePointer midiEventToScriptValue(ScriptEngine* engine, const MIDIEvent& event) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty(MIDI_DELTA_TIME_PROP_NAME, event.deltaTime); + obj->setProperty(MIDI_EVENT_TYPE_PROP_NAME, event.type); + obj->setProperty(MIDI_DATA_1_PROP_NAME, event.data1); + obj->setProperty(MIDI_DATA_2_PROP_NAME, event.data2); return obj; } -void midiEventFromScriptValue(const QScriptValue &object, MIDIEvent& event) { - event.deltaTime = object.property(MIDI_DELTA_TIME_PROP_NAME).toVariant().toDouble(); - event.type = object.property(MIDI_EVENT_TYPE_PROP_NAME).toVariant().toUInt(); - event.data1 = object.property(MIDI_DATA_1_PROP_NAME).toVariant().toUInt(); - event.data2 = object.property(MIDI_DATA_2_PROP_NAME).toVariant().toUInt(); +void midiEventFromScriptValue(const ScriptValuePointer &object, MIDIEvent& event) { + event.deltaTime = object->property(MIDI_DELTA_TIME_PROP_NAME)->toVariant().toDouble(); + event.type = object->property(MIDI_EVENT_TYPE_PROP_NAME)->toVariant().toUInt(); + event.data1 = object->property(MIDI_DATA_1_PROP_NAME)->toVariant().toUInt(); + event.data2 = object->property(MIDI_DATA_2_PROP_NAME)->toVariant().toUInt(); } \ No newline at end of file diff --git a/libraries/script-engine/src/MIDIEvent.h b/libraries/script-engine/src/MIDIEvent.h index 6bf4a4b72b3..716b8c535eb 100644 --- a/libraries/script-engine/src/MIDIEvent.h +++ b/libraries/script-engine/src/MIDIEvent.h @@ -9,11 +9,15 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include - #ifndef hifi_MIDIEvent_h #define hifi_MIDIEvent_h +#include + +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; + class MIDIEvent { public: double deltaTime; @@ -24,9 +28,9 @@ class MIDIEvent { Q_DECLARE_METATYPE(MIDIEvent) -void registerMIDIMetaTypes(QScriptEngine* engine); +void registerMIDIMetaTypes(ScriptEngine* engine); -QScriptValue midiEventToScriptValue(QScriptEngine* engine, const MIDIEvent& event); -void midiEventFromScriptValue(const QScriptValue &object, MIDIEvent& event); +ScriptValuePointer midiEventToScriptValue(ScriptEngine* engine, const MIDIEvent& event); +void midiEventFromScriptValue(const ScriptValuePointer &object, MIDIEvent& event); #endif // hifi_MIDIEvent_h \ No newline at end of file diff --git a/libraries/script-engine/src/Mat4.cpp b/libraries/script-engine/src/Mat4.cpp index d4d73a46ccd..755744777da 100644 --- a/libraries/script-engine/src/Mat4.cpp +++ b/libraries/script-engine/src/Mat4.cpp @@ -20,6 +20,7 @@ #include "ScriptEngineLogging.h" #include "ScriptEngine.h" +#include "ScriptManager.h" glm::mat4 Mat4::multiply(const glm::mat4& m1, const glm::mat4& m2) const { return m1 * m2; @@ -87,7 +88,7 @@ void Mat4::print(const QString& label, const glm::mat4& m, bool transpose) const QString message = QString("%1 %2").arg(qPrintable(label)); message = message.arg(glm::to_string(out).c_str()); qCDebug(scriptengine) << message; - if (ScriptEngine* scriptEngine = qobject_cast(engine())) { - scriptEngine->print(message); + if (ScriptManager* scriptManager = engine()->manager()) { + scriptManager->print(message); } } diff --git a/libraries/script-engine/src/Mat4.h b/libraries/script-engine/src/Mat4.h index 33fd8895413..d8004b9b8b0 100644 --- a/libraries/script-engine/src/Mat4.h +++ b/libraries/script-engine/src/Mat4.h @@ -16,10 +16,10 @@ #include #include -#include #include #include #include "RegisteredMetaTypes.h" +#include "Scriptable.h" /**jsdoc * The Mat4 API provides facilities for generating and using 4 x 4 matrices. These matrices are typically used to @@ -37,7 +37,7 @@ */ /// Scriptable Mat4 object. Used exclusively in the JavaScript API -class Mat4 : public QObject, protected QScriptable { +class Mat4 : public QObject, protected Scriptable { Q_OBJECT public slots: diff --git a/libraries/script-engine/src/MenuItemProperties.cpp b/libraries/script-engine/src/MenuItemProperties.cpp index af9c4a3d0ab..3a0db0af1de 100644 --- a/libraries/script-engine/src/MenuItemProperties.cpp +++ b/libraries/script-engine/src/MenuItemProperties.cpp @@ -14,6 +14,8 @@ #include #include +#include "ScriptEngine.h" +#include "ScriptValue.h" MenuItemProperties::MenuItemProperties(const QString& menuName, const QString& menuItemName, const QString& shortcutKey, bool checkable, bool checked, bool separator) : @@ -40,12 +42,12 @@ MenuItemProperties::MenuItemProperties(const QString& menuName, const QString& m { } -void registerMenuItemProperties(QScriptEngine* engine) { - qScriptRegisterMetaType(engine, menuItemPropertiesToScriptValue, menuItemPropertiesFromScriptValue); +void registerMenuItemProperties(ScriptEngine* engine) { + scriptRegisterMetaType(engine, menuItemPropertiesToScriptValue, menuItemPropertiesFromScriptValue); } -QScriptValue menuItemPropertiesToScriptValue(QScriptEngine* engine, const MenuItemProperties& properties) { - QScriptValue obj = engine->newObject(); +ScriptValuePointer menuItemPropertiesToScriptValue(ScriptEngine* engine, const MenuItemProperties& properties) { + ScriptValuePointer obj = engine->newObject(); // not supported return obj; } @@ -70,32 +72,32 @@ QScriptValue menuItemPropertiesToScriptValue(QScriptEngine* engine, const MenuIt * @property {string} [afterItem] - The name of the menu item to place this menu item after. * @property {string} [grouping] - The name of grouping to add this menu item to. */ -void menuItemPropertiesFromScriptValue(const QScriptValue& object, MenuItemProperties& properties) { - properties.menuName = object.property("menuName").toVariant().toString(); - properties.menuItemName = object.property("menuItemName").toVariant().toString(); - properties.isCheckable = object.property("isCheckable").toVariant().toBool(); - properties.isChecked = object.property("isChecked").toVariant().toBool(); - properties.isSeparator = object.property("isSeparator").toVariant().toBool(); +void menuItemPropertiesFromScriptValue(const ScriptValuePointer& object, MenuItemProperties& properties) { + properties.menuName = object->property("menuName")->toVariant().toString(); + properties.menuItemName = object->property("menuItemName")->toVariant().toString(); + properties.isCheckable = object->property("isCheckable")->toVariant().toBool(); + properties.isChecked = object->property("isChecked")->toVariant().toBool(); + properties.isSeparator = object->property("isSeparator")->toVariant().toBool(); // handle the shortcut key options in order... - QScriptValue shortcutKeyValue = object.property("shortcutKey"); - if (shortcutKeyValue.isValid()) { - properties.shortcutKey = shortcutKeyValue.toVariant().toString(); + ScriptValuePointer shortcutKeyValue = object->property("shortcutKey"); + if (shortcutKeyValue->isValid()) { + properties.shortcutKey = shortcutKeyValue->toVariant().toString(); properties.shortcutKeySequence = properties.shortcutKey; } else { - QScriptValue shortcutKeyEventValue = object.property("shortcutKeyEvent"); - if (shortcutKeyEventValue.isValid()) { + ScriptValuePointer shortcutKeyEventValue = object->property("shortcutKeyEvent"); + if (shortcutKeyEventValue->isValid()) { KeyEvent::fromScriptValue(shortcutKeyEventValue, properties.shortcutKeyEvent); properties.shortcutKeySequence = properties.shortcutKeyEvent; } } - if (object.property("position").isValid()) { - properties.position = object.property("position").toVariant().toInt(); + if (object->property("position")->isValid()) { + properties.position = object->property("position")->toVariant().toInt(); } - properties.beforeItem = object.property("beforeItem").toVariant().toString(); - properties.afterItem = object.property("afterItem").toVariant().toString(); - properties.grouping = object.property("grouping").toVariant().toString(); + properties.beforeItem = object->property("beforeItem")->toVariant().toString(); + properties.afterItem = object->property("afterItem")->toVariant().toString(); + properties.grouping = object->property("grouping")->toVariant().toString(); } diff --git a/libraries/script-engine/src/MenuItemProperties.h b/libraries/script-engine/src/MenuItemProperties.h index 9de43378383..1422b784227 100644 --- a/libraries/script-engine/src/MenuItemProperties.h +++ b/libraries/script-engine/src/MenuItemProperties.h @@ -12,10 +12,13 @@ #ifndef hifi_MenuItemProperties_h #define hifi_MenuItemProperties_h -#include - #include "KeyEvent.h" +#include + +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; class MenuItemProperties { public: @@ -49,9 +52,9 @@ class MenuItemProperties { static const int UNSPECIFIED_POSITION = -1; }; Q_DECLARE_METATYPE(MenuItemProperties) -QScriptValue menuItemPropertiesToScriptValue(QScriptEngine* engine, const MenuItemProperties& props); -void menuItemPropertiesFromScriptValue(const QScriptValue& object, MenuItemProperties& props); -void registerMenuItemProperties(QScriptEngine* engine); +ScriptValuePointer menuItemPropertiesToScriptValue(ScriptEngine* engine, const MenuItemProperties& props); +void menuItemPropertiesFromScriptValue(const ScriptValuePointer& object, MenuItemProperties& props); +void registerMenuItemProperties(ScriptEngine* engine); diff --git a/libraries/script-engine/src/ModelScriptingInterface.cpp b/libraries/script-engine/src/ModelScriptingInterface.cpp index 2e9a9ca14a9..989a0db95fc 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.cpp +++ b/libraries/script-engine/src/ModelScriptingInterface.cpp @@ -10,20 +10,19 @@ // #include "ModelScriptingInterface.h" -#include -#include -#include #include #include "ScriptEngine.h" +#include "ScriptEngineCast.h" #include "ScriptEngineLogging.h" +#include "ScriptValueUtils.h" #include "OBJWriter.h" ModelScriptingInterface::ModelScriptingInterface(QObject* parent) : QObject(parent) { - _modelScriptEngine = qobject_cast(parent); + _modelScriptEngine = qobject_cast(parent); - qScriptRegisterSequenceMetaType>(_modelScriptEngine); - qScriptRegisterMetaType(_modelScriptEngine, meshFaceToScriptValue, meshFaceFromScriptValue); - qScriptRegisterMetaType(_modelScriptEngine, qVectorMeshFaceToScriptValue, qVectorMeshFaceFromScriptValue); + scriptRegisterSequenceMetaType>(_modelScriptEngine); + scriptRegisterMetaType(_modelScriptEngine, meshFaceToScriptValue, meshFaceFromScriptValue); + scriptRegisterMetaType(_modelScriptEngine, qVectorMeshFaceToScriptValue, qVectorMeshFaceFromScriptValue); } QString ModelScriptingInterface::meshToOBJ(MeshProxyList in) { @@ -35,7 +34,7 @@ QString ModelScriptingInterface::meshToOBJ(MeshProxyList in) { return writeOBJToString(meshes); } -QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { +ScriptValuePointer ModelScriptingInterface::appendMeshes(MeshProxyList in) { // figure out the size of the resulting mesh size_t totalVertexCount { 0 }; size_t totalColorCount { 0 }; @@ -143,13 +142,13 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { return meshToScriptValue(_modelScriptEngine, resultProxy); } -QScriptValue ModelScriptingInterface::transformMesh(glm::mat4 transform, MeshProxy* meshProxy) { +ScriptValuePointer ModelScriptingInterface::transformMesh(glm::mat4 transform, MeshProxy* meshProxy) { if (!meshProxy) { - return QScriptValue(false); + return ScriptValuePointer(false); } MeshPointer mesh = meshProxy->getMeshPointer(); if (!mesh) { - return QScriptValue(false); + return ScriptValuePointer(false); } const auto inverseTransposeTransform = glm::inverse(glm::transpose(transform)); @@ -161,13 +160,13 @@ QScriptValue ModelScriptingInterface::transformMesh(glm::mat4 transform, MeshPro return meshToScriptValue(_modelScriptEngine, resultProxy); } -QScriptValue ModelScriptingInterface::getVertexCount(MeshProxy* meshProxy) { +ScriptValuePointer ModelScriptingInterface::getVertexCount(MeshProxy* meshProxy) { if (!meshProxy) { - return QScriptValue(false); + return ScriptValuePointer(false); } MeshPointer mesh = meshProxy->getMeshPointer(); if (!mesh) { - return QScriptValue(false); + return ScriptValuePointer(false); } gpu::BufferView::Index numVertices = (gpu::BufferView::Index)mesh->getNumVertices(); @@ -175,20 +174,20 @@ QScriptValue ModelScriptingInterface::getVertexCount(MeshProxy* meshProxy) { return numVertices; } -QScriptValue ModelScriptingInterface::getVertex(MeshProxy* meshProxy, int vertexIndex) { +ScriptValuePointer ModelScriptingInterface::getVertex(MeshProxy* meshProxy, int vertexIndex) { if (!meshProxy) { - return QScriptValue(false); + return ScriptValuePointer(false); } MeshPointer mesh = meshProxy->getMeshPointer(); if (!mesh) { - return QScriptValue(false); + return ScriptValuePointer(false); } const gpu::BufferView& vertexBufferView = mesh->getVertexBuffer(); gpu::BufferView::Index numVertices = (gpu::BufferView::Index)mesh->getNumVertices(); if (vertexIndex < 0 || vertexIndex >= numVertices) { - return QScriptValue(false); + return ScriptValuePointer(false); } glm::vec3 pos = vertexBufferView.get(vertexIndex); @@ -196,7 +195,7 @@ QScriptValue ModelScriptingInterface::getVertex(MeshProxy* meshProxy, int vertex } -QScriptValue ModelScriptingInterface::newMesh(const QVector& vertices, +ScriptValuePointer ModelScriptingInterface::newMesh(const QVector& vertices, const QVector& normals, const QVector& faces) { graphics::MeshPointer mesh(new graphics::Mesh()); diff --git a/libraries/script-engine/src/ModelScriptingInterface.h b/libraries/script-engine/src/ModelScriptingInterface.h index 39170bb370b..7de414b34e8 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.h +++ b/libraries/script-engine/src/ModelScriptingInterface.h @@ -13,9 +13,13 @@ #define hifi_ModelScriptingInterface_h #include +#include #include -class QScriptEngine; + +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; /**jsdoc * The Model API provides the ability to manipulate meshes. You can get the meshes for an entity using @@ -52,7 +56,7 @@ class ModelScriptingInterface : public QObject { * @param {MeshProxy[]} meshes - The meshes to combine. * @returns {MeshProxy} The combined mesh. */ - Q_INVOKABLE QScriptValue appendMeshes(MeshProxyList in); + Q_INVOKABLE ScriptValuePointer appendMeshes(MeshProxyList in); /**jsdoc * Transforms the vertices in a mesh. @@ -61,7 +65,7 @@ class ModelScriptingInterface : public QObject { * @param {MeshProxy} mesh - The mesh to apply the transform to. * @returns {MeshProxy|boolean} The transformed mesh, if valid. false if an error. */ - Q_INVOKABLE QScriptValue transformMesh(glm::mat4 transform, MeshProxy* meshProxy); + Q_INVOKABLE ScriptValuePointer transformMesh(glm::mat4 transform, MeshProxy* meshProxy); /**jsdoc * Creates a new mesh. @@ -71,7 +75,7 @@ class ModelScriptingInterface : public QObject { * @param {MeshFace[]} faces - The faces in the mesh. * @returns {MeshProxy} A new mesh. */ - Q_INVOKABLE QScriptValue newMesh(const QVector& vertices, + Q_INVOKABLE ScriptValuePointer newMesh(const QVector& vertices, const QVector& normals, const QVector& faces); @@ -81,7 +85,7 @@ class ModelScriptingInterface : public QObject { * @param {MeshProxy} mesh - The mesh to count the vertices in. * @returns {number|boolean} The number of vertices in the mesh, if valid. false if an error. */ - Q_INVOKABLE QScriptValue getVertexCount(MeshProxy* meshProxy); + Q_INVOKABLE ScriptValuePointer getVertexCount(MeshProxy* meshProxy); /**jsdoc * Gets the position of a vertex in a mesh. @@ -90,10 +94,10 @@ class ModelScriptingInterface : public QObject { * @param {number} index - The index of the vertex to get. * @returns {Vec3|boolean} The local position of the vertex relative to the mesh, if valid. false if an error. */ - Q_INVOKABLE QScriptValue getVertex(MeshProxy* meshProxy, int vertexIndex); + Q_INVOKABLE ScriptValuePointer getVertex(MeshProxy* meshProxy, int vertexIndex); private: - QScriptEngine* _modelScriptEngine { nullptr }; + ScriptEngine* _modelScriptEngine { nullptr }; }; #endif // hifi_ModelScriptingInterface_h diff --git a/libraries/script-engine/src/MouseEvent.cpp b/libraries/script-engine/src/MouseEvent.cpp index f3a5cb80c05..218f675ef5c 100644 --- a/libraries/script-engine/src/MouseEvent.cpp +++ b/libraries/script-engine/src/MouseEvent.cpp @@ -11,8 +11,8 @@ #include "MouseEvent.h" -#include -#include +#include "ScriptEngine.h" +#include "ScriptValue.h" MouseEvent::MouseEvent() : x(0.0f), @@ -86,22 +86,22 @@ MouseEvent::MouseEvent(const QMouseEvent& event) : * print(JSON.stringify(event)); * }); */ -QScriptValue MouseEvent::toScriptValue(QScriptEngine* engine, const MouseEvent& event) { - QScriptValue obj = engine->newObject(); - obj.setProperty("x", event.x); - obj.setProperty("y", event.y); - obj.setProperty("button", event.button); - obj.setProperty("isLeftButton", event.isLeftButton); - obj.setProperty("isRightButton", event.isRightButton); - obj.setProperty("isMiddleButton", event.isMiddleButton); - obj.setProperty("isShifted", event.isShifted); - obj.setProperty("isMeta", event.isMeta); - obj.setProperty("isControl", event.isControl); - obj.setProperty("isAlt", event.isAlt); +ScriptValuePointer MouseEvent::toScriptValue(ScriptEngine* engine, const MouseEvent& event) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty("x", event.x); + obj->setProperty("y", event.y); + obj->setProperty("button", event.button); + obj->setProperty("isLeftButton", event.isLeftButton); + obj->setProperty("isRightButton", event.isRightButton); + obj->setProperty("isMiddleButton", event.isMiddleButton); + obj->setProperty("isShifted", event.isShifted); + obj->setProperty("isMeta", event.isMeta); + obj->setProperty("isControl", event.isControl); + obj->setProperty("isAlt", event.isAlt); return obj; } -void MouseEvent::fromScriptValue(const QScriptValue& object, MouseEvent& event) { +void MouseEvent::fromScriptValue(const ScriptValuePointer& object, MouseEvent& event) { // nothing for now... } diff --git a/libraries/script-engine/src/MouseEvent.h b/libraries/script-engine/src/MouseEvent.h index 04e474366d6..a61d7d01660 100644 --- a/libraries/script-engine/src/MouseEvent.h +++ b/libraries/script-engine/src/MouseEvent.h @@ -13,19 +13,21 @@ #define hifi_MouseEvent_h #include -#include +#include -class QScriptEngine; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; class MouseEvent { public: MouseEvent(); MouseEvent(const QMouseEvent& event); - static QScriptValue toScriptValue(QScriptEngine* engine, const MouseEvent& event); - static void fromScriptValue(const QScriptValue& object, MouseEvent& event); + static ScriptValuePointer toScriptValue(ScriptEngine* engine, const MouseEvent& event); + static void fromScriptValue(const ScriptValuePointer& object, MouseEvent& event); - QScriptValue toScriptValue(QScriptEngine* engine) const { return MouseEvent::toScriptValue(engine, *this); } + ScriptValuePointer toScriptValue(ScriptEngine* engine) const { return MouseEvent::toScriptValue(engine, *this); } int x; int y; diff --git a/libraries/shared/src/PointerEvent.cpp b/libraries/script-engine/src/PointerEvent.cpp similarity index 72% rename from libraries/shared/src/PointerEvent.cpp rename to libraries/script-engine/src/PointerEvent.cpp index 6882efd5886..d5fc771f5f3 100644 --- a/libraries/shared/src/PointerEvent.cpp +++ b/libraries/script-engine/src/PointerEvent.cpp @@ -11,10 +11,10 @@ #include "PointerEvent.h" -#include -#include - #include "RegisteredMetaTypes.h" +#include "ScriptEngine.h" +#include "ScriptValue.h" +#include "ScriptValueUtils.h" static bool areFlagsSet(uint32_t flags, uint32_t mask) { return (flags & mask) != 0; @@ -125,97 +125,97 @@ void PointerEvent::setButton(Button button) { * * @typedef {number} KeyboardModifiers */ -QScriptValue PointerEvent::toScriptValue(QScriptEngine* engine, const PointerEvent& event) { - QScriptValue obj = engine->newObject(); +ScriptValuePointer PointerEvent::toScriptValue(ScriptEngine* engine, const PointerEvent& event) { + ScriptValuePointer obj = engine->newObject(); switch (event._type) { case Press: - obj.setProperty("type", "Press"); + obj->setProperty("type", "Press"); break; case DoublePress: - obj.setProperty("type", "DoublePress"); + obj->setProperty("type", "DoublePress"); break; case Release: - obj.setProperty("type", "Release"); + obj->setProperty("type", "Release"); break; default: case Move: - obj.setProperty("type", "Move"); + obj->setProperty("type", "Move"); break; }; - obj.setProperty("id", event._id); + obj->setProperty("id", event._id); - QScriptValue pos2D = engine->newObject(); - pos2D.setProperty("x", event._pos2D.x); - pos2D.setProperty("y", event._pos2D.y); - obj.setProperty("pos2D", pos2D); + ScriptValuePointer pos2D = engine->newObject(); + pos2D->setProperty("x", event._pos2D.x); + pos2D->setProperty("y", event._pos2D.y); + obj->setProperty("pos2D", pos2D); - QScriptValue pos3D = engine->newObject(); - pos3D.setProperty("x", event._pos3D.x); - pos3D.setProperty("y", event._pos3D.y); - pos3D.setProperty("z", event._pos3D.z); - obj.setProperty("pos3D", pos3D); + ScriptValuePointer pos3D = engine->newObject(); + pos3D->setProperty("x", event._pos3D.x); + pos3D->setProperty("y", event._pos3D.y); + pos3D->setProperty("z", event._pos3D.z); + obj->setProperty("pos3D", pos3D); - QScriptValue normal = engine->newObject(); - normal.setProperty("x", event._normal.x); - normal.setProperty("y", event._normal.y); - normal.setProperty("z", event._normal.z); - obj.setProperty("normal", normal); + ScriptValuePointer normal = engine->newObject(); + normal->setProperty("x", event._normal.x); + normal->setProperty("y", event._normal.y); + normal->setProperty("z", event._normal.z); + obj->setProperty("normal", normal); - QScriptValue direction = engine->newObject(); - direction.setProperty("x", event._direction.x); - direction.setProperty("y", event._direction.y); - direction.setProperty("z", event._direction.z); - obj.setProperty("direction", direction); + ScriptValuePointer direction = engine->newObject(); + direction->setProperty("x", event._direction.x); + direction->setProperty("y", event._direction.y); + direction->setProperty("z", event._direction.z); + obj->setProperty("direction", direction); bool isPrimaryButton = false; bool isSecondaryButton = false; bool isTertiaryButton = false; switch (event._button) { case NoButtons: - obj.setProperty("button", "None"); + obj->setProperty("button", "None"); break; case PrimaryButton: - obj.setProperty("button", "Primary"); + obj->setProperty("button", "Primary"); isPrimaryButton = true; break; case SecondaryButton: - obj.setProperty("button", "Secondary"); + obj->setProperty("button", "Secondary"); isSecondaryButton = true; break; case TertiaryButton: - obj.setProperty("button", "Tertiary"); + obj->setProperty("button", "Tertiary"); isTertiaryButton = true; break; } if (isPrimaryButton) { - obj.setProperty("isPrimaryButton", isPrimaryButton); - obj.setProperty("isLeftButton", isPrimaryButton); + obj->setProperty("isPrimaryButton", isPrimaryButton); + obj->setProperty("isLeftButton", isPrimaryButton); } if (isSecondaryButton) { - obj.setProperty("isSecondaryButton", isSecondaryButton); - obj.setProperty("isRightButton", isSecondaryButton); + obj->setProperty("isSecondaryButton", isSecondaryButton); + obj->setProperty("isRightButton", isSecondaryButton); } if (isTertiaryButton) { - obj.setProperty("isTertiaryButton", isTertiaryButton); - obj.setProperty("isMiddleButton", isTertiaryButton); + obj->setProperty("isTertiaryButton", isTertiaryButton); + obj->setProperty("isMiddleButton", isTertiaryButton); } - obj.setProperty("isPrimaryHeld", areFlagsSet(event._buttons, PrimaryButton)); - obj.setProperty("isSecondaryHeld", areFlagsSet(event._buttons, SecondaryButton)); - obj.setProperty("isTertiaryHeld", areFlagsSet(event._buttons, TertiaryButton)); + obj->setProperty("isPrimaryHeld", areFlagsSet(event._buttons, PrimaryButton)); + obj->setProperty("isSecondaryHeld", areFlagsSet(event._buttons, SecondaryButton)); + obj->setProperty("isTertiaryHeld", areFlagsSet(event._buttons, TertiaryButton)); - obj.setProperty("keyboardModifiers", QScriptValue(event.getKeyboardModifiers())); + obj->setProperty("keyboardModifiers", engine->newValue(event.getKeyboardModifiers())); return obj; } -void PointerEvent::fromScriptValue(const QScriptValue& object, PointerEvent& event) { - if (object.isObject()) { - QScriptValue type = object.property("type"); - QString typeStr = type.isString() ? type.toString() : "Move"; +void PointerEvent::fromScriptValue(const ScriptValuePointer& object, PointerEvent& event) { + if (object->isObject()) { + ScriptValuePointer type = object->property("type"); + QString typeStr = type->isString() ? type->toString() : "Move"; if (typeStr == "Press") { event._type = Press; } else if (typeStr == "DoublePress") { @@ -226,16 +226,16 @@ void PointerEvent::fromScriptValue(const QScriptValue& object, PointerEvent& eve event._type = Move; } - QScriptValue id = object.property("id"); - event._id = id.isNumber() ? (uint32_t)id.toNumber() : 0; + ScriptValuePointer id = object->property("id"); + event._id = id->isNumber() ? (uint32_t)id->toNumber() : 0; - vec2FromScriptValue(object.property("pos2D"), event._pos2D); - vec3FromScriptValue(object.property("pos3D"), event._pos3D); - vec3FromScriptValue(object.property("normal"), event._normal); - vec3FromScriptValue(object.property("direction"), event._direction); + vec2FromScriptValue(object->property("pos2D"), event._pos2D); + vec3FromScriptValue(object->property("pos3D"), event._pos3D); + vec3FromScriptValue(object->property("normal"), event._normal); + vec3FromScriptValue(object->property("direction"), event._direction); - QScriptValue button = object.property("button"); - QString buttonStr = type.isString() ? button.toString() : "NoButtons"; + ScriptValuePointer button = object->property("button"); + QString buttonStr = type->isString() ? button->toString() : "NoButtons"; if (buttonStr == "Primary") { event._button = PrimaryButton; @@ -247,9 +247,9 @@ void PointerEvent::fromScriptValue(const QScriptValue& object, PointerEvent& eve event._button = NoButtons; } - bool primary = object.property("isPrimaryHeld").toBool(); - bool secondary = object.property("isSecondaryHeld").toBool(); - bool tertiary = object.property("isTertiaryHeld").toBool(); + bool primary = object->property("isPrimaryHeld")->toBool(); + bool secondary = object->property("isSecondaryHeld")->toBool(); + bool tertiary = object->property("isTertiaryHeld")->toBool(); event._buttons = 0; if (primary) { event._buttons |= PrimaryButton; @@ -261,7 +261,7 @@ void PointerEvent::fromScriptValue(const QScriptValue& object, PointerEvent& eve event._buttons |= TertiaryButton; } - event._keyboardModifiers = (Qt::KeyboardModifiers)(object.property("keyboardModifiers").toUInt32()); + event._keyboardModifiers = (Qt::KeyboardModifiers)(object->property("keyboardModifiers")->toUInt32()); } } diff --git a/libraries/shared/src/PointerEvent.h b/libraries/script-engine/src/PointerEvent.h similarity index 89% rename from libraries/shared/src/PointerEvent.h rename to libraries/script-engine/src/PointerEvent.h index ea774aa67b1..197b144bdba 100644 --- a/libraries/shared/src/PointerEvent.h +++ b/libraries/script-engine/src/PointerEvent.h @@ -16,7 +16,11 @@ #include #include -#include +#include + +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; class PointerEvent { public: @@ -44,10 +48,10 @@ class PointerEvent { const glm::vec3& normal, const glm::vec3& direction, Button button = NoButtons, uint32_t buttons = NoButtons, Qt::KeyboardModifiers keyboardModifiers = Qt::NoModifier); - static QScriptValue toScriptValue(QScriptEngine* engine, const PointerEvent& event); - static void fromScriptValue(const QScriptValue& object, PointerEvent& event); + static ScriptValuePointer toScriptValue(ScriptEngine* engine, const PointerEvent& event); + static void fromScriptValue(const ScriptValuePointer& object, PointerEvent& event); - QScriptValue toScriptValue(QScriptEngine* engine) const { return PointerEvent::toScriptValue(engine, *this); } + ScriptValuePointer toScriptValue(ScriptEngine* engine) const { return PointerEvent::toScriptValue(engine, *this); } EventType getType() const { return _type; } uint32_t getID() const { return _id; } diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp index 8335cb9adfc..724ad34416c 100644 --- a/libraries/script-engine/src/Quat.cpp +++ b/libraries/script-engine/src/Quat.cpp @@ -18,6 +18,7 @@ #include "ScriptEngineLogging.h" #include "ScriptEngine.h" +#include "ScriptManager.h" quat Quat::normalize(const glm::quat& q) { return glm::normalize(q); @@ -123,8 +124,8 @@ void Quat::print(const QString& label, const glm::quat& q, bool asDegrees) { message = message.arg(glm::to_string(glm::dquat(q)).c_str()); } qCDebug(scriptengine) << message; - if (ScriptEngine* scriptEngine = qobject_cast(engine())) { - scriptEngine->print(message); + if (ScriptManager* scriptManager = engine()->manager()) { + scriptManager->print(message); } } diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index c1eaa977971..2cba09a124a 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -18,10 +18,11 @@ #include #include -#include #include +#include "Scriptable.h" + /**jsdoc * A quaternion value. See also the {@link Quat(0)|Quat} API. * @typedef {object} Quat @@ -53,7 +54,7 @@ */ /// Scriptable interface a Quaternion helper class object. Used exclusively in the JavaScript API -class Quat : public QObject, protected QScriptable { +class Quat : public QObject, protected Scriptable { Q_OBJECT Q_PROPERTY(glm::quat IDENTITY READ IDENTITY CONSTANT) diff --git a/libraries/script-engine/src/RecordingScriptingInterface.cpp b/libraries/script-engine/src/RecordingScriptingInterface.cpp index cbcf94662e2..f666eb9549f 100644 --- a/libraries/script-engine/src/RecordingScriptingInterface.cpp +++ b/libraries/script-engine/src/RecordingScriptingInterface.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include @@ -28,6 +27,7 @@ #include #include "ScriptEngineLogging.h" +#include "ScriptValue.h" using namespace recording; @@ -54,18 +54,18 @@ float RecordingScriptingInterface::playerLength() const { return _player->length(); } -void RecordingScriptingInterface::playClip(NetworkClipLoaderPointer clipLoader, const QString& url, QScriptValue callback) { +void RecordingScriptingInterface::playClip(NetworkClipLoaderPointer clipLoader, const QString& url, ScriptValuePointer callback) { _player->queueClip(clipLoader->getClip()); - if (callback.isFunction()) { - QScriptValueList args { true, url }; - callback.call(QScriptValue(), args); + if (callback->isFunction()) { + ScriptValueList args { true, url }; + callback->call(ScriptValuePointer(), args); } } -void RecordingScriptingInterface::loadRecording(const QString& url, QScriptValue callback) { +void RecordingScriptingInterface::loadRecording(const QString& url, ScriptValuePointer callback) { if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "loadRecording", Q_ARG(const QString&, url), Q_ARG(QScriptValue, callback)); + BLOCKING_INVOKE_METHOD(this, "loadRecording", Q_ARG(const QString&, url), Q_ARG(ScriptValuePointer, callback)); return; } @@ -83,7 +83,7 @@ void RecordingScriptingInterface::loadRecording(const QString& url, QScriptValue auto weakClipLoader = clipLoader.toWeakRef(); // when clip loaded, call the callback with the URL and success boolean - connect(clipLoader.data(), &recording::NetworkClipLoader::clipLoaded, callback.engine(), + connect(clipLoader.data(), &recording::NetworkClipLoader::clipLoaded, callback->engine(), [this, weakClipLoader, url, callback]() mutable { if (auto clipLoader = weakClipLoader.toStrongRef()) { @@ -97,12 +97,12 @@ void RecordingScriptingInterface::loadRecording(const QString& url, QScriptValue }); // when clip load fails, call the callback with the URL and failure boolean - connect(clipLoader.data(), &recording::NetworkClipLoader::failed, callback.engine(), [this, weakClipLoader, url, callback](QNetworkReply::NetworkError error) mutable { + connect(clipLoader.data(), &recording::NetworkClipLoader::failed, callback->engine(), [this, weakClipLoader, url, callback](QNetworkReply::NetworkError error) mutable { qCDebug(scriptengine) << "Failed to load recording from\"" << url << '"'; - if (callback.isFunction()) { - QScriptValueList args { false, url }; - callback.call(QScriptValue(), args); + if (callback->isFunction()) { + ScriptValueList args { false, url }; + callback->call(ScriptValuePointer(), args); } if (auto clipLoader = weakClipLoader.toStrongRef()) { @@ -248,8 +248,8 @@ void RecordingScriptingInterface::saveRecording(const QString& filename) { recording::Clip::toFile(filename, _lastClip); } -bool RecordingScriptingInterface::saveRecordingToAsset(QScriptValue getClipAtpUrl) { - if (!getClipAtpUrl.isFunction()) { +bool RecordingScriptingInterface::saveRecordingToAsset(ScriptValuePointer getClipAtpUrl) { + if (!getClipAtpUrl->isFunction()) { qCWarning(scriptengine) << "The argument is not a function."; return false; } @@ -258,7 +258,7 @@ bool RecordingScriptingInterface::saveRecordingToAsset(QScriptValue getClipAtpUr bool result; BLOCKING_INVOKE_METHOD(this, "saveRecordingToAsset", Q_RETURN_ARG(bool, result), - Q_ARG(QScriptValue, getClipAtpUrl)); + Q_ARG(ScriptValuePointer, getClipAtpUrl)); return result; } @@ -269,7 +269,7 @@ bool RecordingScriptingInterface::saveRecordingToAsset(QScriptValue getClipAtpUr if (auto upload = DependencyManager::get()->createUpload(recording::Clip::toBuffer(_lastClip))) { QObject::connect(upload, &AssetUpload::finished, - getClipAtpUrl.engine(), [=](AssetUpload* upload, const QString& hash) mutable { + getClipAtpUrl->engine(), [=](AssetUpload* upload, const QString& hash) mutable { QString clip_atp_url = ""; if (upload->getError() == AssetUpload::NoError) { @@ -280,9 +280,9 @@ bool RecordingScriptingInterface::saveRecordingToAsset(QScriptValue getClipAtpUr qCWarning(scriptengine) << "Error during the Asset upload."; } - QScriptValueList args; + ScriptValueList args; args << clip_atp_url; - getClipAtpUrl.call(QScriptValue(), args); + getClipAtpUrl->call(ScriptValuePointer(), args); }); upload->start(); return true; diff --git a/libraries/script-engine/src/RecordingScriptingInterface.h b/libraries/script-engine/src/RecordingScriptingInterface.h index fd9c2d64e65..457623576a0 100644 --- a/libraries/script-engine/src/RecordingScriptingInterface.h +++ b/libraries/script-engine/src/RecordingScriptingInterface.h @@ -13,15 +13,15 @@ #include #include +#include -#include #include #include #include #include -class QScriptEngine; -class QScriptValue; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; /**jsdoc * The Recording API makes and plays back recordings of voice and avatar movements. Playback may be done on a @@ -68,7 +68,7 @@ public slots: * }); * } */ - void loadRecording(const QString& url, QScriptValue callback = QScriptValue()); + void loadRecording(const QString& url, ScriptValuePointer callback = ScriptValuePointer()); /**jsdoc @@ -332,7 +332,7 @@ public slots: * } * }, 5000); */ - bool saveRecordingToAsset(QScriptValue getClipAtpUrl); + bool saveRecordingToAsset(ScriptValuePointer getClipAtpUrl); /**jsdoc * Loads the most recently made recording and plays it back on your avatar. @@ -365,7 +365,7 @@ public slots: QSet _clipLoaders; private: - void playClip(recording::NetworkClipLoaderPointer clipLoader, const QString& url, QScriptValue callback); + void playClip(recording::NetworkClipLoaderPointer clipLoader, const QString& url, ScriptValuePointer callback); }; #endif // hifi_RecordingScriptingInterface_h diff --git a/libraries/script-engine/src/SceneScriptingInterface.h b/libraries/script-engine/src/SceneScriptingInterface.h index 66c3f2ee96c..e232eb4de43 100644 --- a/libraries/script-engine/src/SceneScriptingInterface.h +++ b/libraries/script-engine/src/SceneScriptingInterface.h @@ -12,7 +12,6 @@ #ifndef hifi_SceneScriptingInterface_h #define hifi_SceneScriptingInterface_h -#include #include /**jsdoc diff --git a/libraries/script-engine/src/ScriptAudioInjector.cpp b/libraries/script-engine/src/ScriptAudioInjector.cpp index 0e42ec31e70..324a463ef2f 100644 --- a/libraries/script-engine/src/ScriptAudioInjector.cpp +++ b/libraries/script-engine/src/ScriptAudioInjector.cpp @@ -11,19 +11,21 @@ #include "ScriptAudioInjector.h" +#include "ScriptEngine.h" #include "ScriptEngineLogging.h" +#include "ScriptValue.h" -QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in) { +ScriptValuePointer injectorToScriptValue(ScriptEngine* engine, ScriptAudioInjector* const& in) { // The AudioScriptingInterface::playSound method can return null, so we need to account for that. if (!in) { - return QScriptValue(QScriptValue::NullValue); + return engine->nullValue(); } - return engine->newQObject(in, QScriptEngine::ScriptOwnership); + return engine->newQObject(in, ScriptEngine::ScriptOwnership); } -void injectorFromScriptValue(const QScriptValue& object, ScriptAudioInjector*& out) { - out = qobject_cast(object.toQObject()); +void injectorFromScriptValue(const ScriptValuePointer& object, ScriptAudioInjector*& out) { + out = qobject_cast(object->toQObject()); } ScriptAudioInjector::ScriptAudioInjector(const AudioInjectorPointer& injector) : diff --git a/libraries/script-engine/src/ScriptAudioInjector.h b/libraries/script-engine/src/ScriptAudioInjector.h index 5f086019de4..48e3f5c912e 100644 --- a/libraries/script-engine/src/ScriptAudioInjector.h +++ b/libraries/script-engine/src/ScriptAudioInjector.h @@ -13,9 +13,14 @@ #define hifi_ScriptAudioInjector_h #include +#include #include +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; + /**jsdoc * Plays or "injects" the content of an audio file. * @@ -139,12 +144,12 @@ public slots: private: QWeakPointer _injector; - friend QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in); + friend ScriptValuePointer injectorToScriptValue(ScriptEngine* engine, ScriptAudioInjector* const& in); }; Q_DECLARE_METATYPE(ScriptAudioInjector*) -QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in); -void injectorFromScriptValue(const QScriptValue& object, ScriptAudioInjector*& out); +ScriptValuePointer injectorToScriptValue(ScriptEngine* engine, ScriptAudioInjector* const& in); +void injectorFromScriptValue(const ScriptValuePointer& object, ScriptAudioInjector*& out); #endif // hifi_ScriptAudioInjector_h diff --git a/libraries/script-engine/src/ScriptContext.h b/libraries/script-engine/src/ScriptContext.h new file mode 100644 index 00000000000..a93744f947f --- /dev/null +++ b/libraries/script-engine/src/ScriptContext.h @@ -0,0 +1,30 @@ +// +// ScriptContext.h +// libraries/script-engine/src +// +// Created by Heather Anderson on 5/1/21. +// Copyright 2021 Vircadia contributors. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ScriptContext_h +#define hifi_ScriptContext_h + +#include + +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; +using ScriptEnginePointer = QSharedPointer; + +class ScriptContext { +public: + virtual int argumentCount() const = 0; + virtual ScriptValuePointer argument(int index) const = 0; + virtual ScriptEnginePointer engine() const = 0; + virtual ScriptValuePointer thisObject() const = 0; +}; + +#endif // hifi_ScriptContext_h \ No newline at end of file diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index a19e63a665c..88dd4f0904c 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -13,1010 +13,120 @@ #ifndef hifi_ScriptEngine_h #define hifi_ScriptEngine_h -#include -#include - +#include #include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "PointerEvent.h" -#include "ArrayBufferClass.h" -#include "AssetScriptingInterface.h" -#include "AudioScriptingInterface.h" -#include "BaseScriptEngine.h" -#include "ExternalResource.h" -#include "Quat.h" -#include "Mat4.h" -#include "ScriptCache.h" -#include "ScriptUUID.h" -#include "Vec3.h" -#include "ConsoleScriptingInterface.h" -#include "SettingHandle.h" -#include "Profile.h" - -class QScriptEngineDebugger; - -static const QString NO_SCRIPT(""); - -static const int SCRIPT_FPS = 60; -static const int DEFAULT_MAX_ENTITY_PPS = 9000; -static const int DEFAULT_ENTITY_PPS_PER_SCRIPT = 900; - -class ScriptEngines; +#include + +class QByteArray; +class QLatin1String; +class QString; +class QThread; +class QVariant; +class ScriptContext; +class ScriptEngine; +class ScriptManager; +class ScriptProgram; +class ScriptValue; +using ScriptEnginePointer = QSharedPointer; +using ScriptProgramPointer = QSharedPointer; +using ScriptValuePointer = QSharedPointer; Q_DECLARE_METATYPE(ScriptEnginePointer) -class CallbackData { +class ScriptEngine { public: - QScriptValue function; - EntityItemID definingEntityIdentifier; - QUrl definingSandboxURL; -}; - -class DeferredLoadEntity { -public: - EntityItemID entityID; - QString entityScript; - //bool forceRedownload; -}; - -struct EntityScriptContentAvailable { - EntityItemID entityID; - QString scriptOrURL; - QString contents; - bool isURL; - bool success; - QString status; -}; - -typedef std::unordered_map EntityScriptContentAvailableMap; - -typedef QList CallbackList; -typedef QHash RegisteredEventHandlers; - -class EntityScriptDetails { -public: - EntityScriptStatus status { EntityScriptStatus::PENDING }; - - // If status indicates an error, this contains a human-readable string giving more information about the error. - QString errorInfo { "" }; - - QString scriptText { "" }; - QScriptValue scriptObject { QScriptValue() }; - int64_t lastModified { 0 }; - QUrl definingSandboxURL { QUrl("about:EntityScript") }; -}; - -/**jsdoc - * The Script API provides facilities for working with scripts. - * - * @namespace Script - * - * @hifi-interface - * @hifi-client-entity - * @hifi-avatar - * @hifi-server-entity - * @hifi-assignment-client - * - * @property {string} context - The context that the script is running in: - *
    - *
  • "client": An Interface or avatar script.
  • - *
  • "entity_client": A client entity script.
  • - *
  • "entity_server": A server entity script.
  • - *
  • "agent": An assignment client script.
  • - *
- * Read-only. - * @property {string} type - The type of script that is running: - *
    - *
  • "client": An Interface script.
  • - *
  • "entity_client": A client entity script.
  • - *
  • "avatar": An avatar script.
  • - *
  • "entity_server": A server entity script.
  • - *
  • "agent": An assignment client script.
  • - *
- * Read-only. - * @property {string} filename - The filename of the script file. - * Read-only. - * @property {Script.ResourceBuckets} ExternalPaths - External resource buckets. - */ -class ScriptEngine : public BaseScriptEngine, public EntitiesScriptEngineProvider { - Q_OBJECT - Q_PROPERTY(QString context READ getContext) - Q_PROPERTY(QString type READ getTypeAsString) - Q_PROPERTY(QString fileName MEMBER _fileNameString CONSTANT) -public: - - enum Context { - CLIENT_SCRIPT, - ENTITY_CLIENT_SCRIPT, - ENTITY_SERVER_SCRIPT, - AGENT_SCRIPT + typedef ScriptValuePointer (*FunctionSignature)(ScriptContext*, ScriptEngine*); + typedef ScriptValuePointer (*MarshalFunction)(ScriptEngine*, const void*); + typedef void (*DemarshalFunction)(const ScriptValuePointer&, void*); + + enum ValueOwnership { + QtOwnership = 0, + ScriptOwnership = 1, + AutoOwnership = 2, }; - enum Type { - CLIENT, - ENTITY_CLIENT, - ENTITY_SERVER, - AGENT, - AVATAR + enum QObjectWrapOption { + ExcludeChildObjects = 0x0001, // The script object will not expose child objects as properties. + ExcludeSuperClassMethods = 0x0002, // The script object will not expose signals and slots inherited from the superclass. + ExcludeSuperClassProperties = 0x0004, // The script object will not expose properties inherited from the superclass. + ExcludeSuperClassContents = 0x0006, // Shorthand form for ExcludeSuperClassMethods | ExcludeSuperClassProperties + ExcludeDeleteLater = 0x0010, // The script object will not expose the QObject::deleteLater() slot. + ExcludeSlots = 0x0020, // The script object will not expose the QObject's slots. + AutoCreateDynamicProperties = 0x0100, // Properties that don't already exist in the QObject will be created as dynamic properties of that object, rather than as properties of the script object. + PreferExistingWrapperObject = 0x0200, // If a wrapper object with the requested configuration already exists, return that object. + SkipMethodsInEnumeration = 0x0008, // Don't include methods (signals and slots) when enumerating the object's properties. }; - Q_ENUM(Type) - - static int processLevelMaxRetries; - ScriptEngine(Context context, const QString& scriptContents = NO_SCRIPT, const QString& fileNameString = QString("about:ScriptEngine")); - ~ScriptEngine(); - - /// run the script in a dedicated thread. This will have the side effect of evalulating - /// the current script contents and calling run(). Callers will likely want to register the script with external - /// services before calling this. - void runInThread(); - - void runDebuggable(); - - /// run the script in the callers thread, exit when stop() is called. - void run(); - - QString getFilename() const; - - QList getListOfEntityScriptIDs(); - - /**jsdoc - * Stops and unloads the current script. - *

Warning: If an assignment client script, the script gets restarted after stopping.

- * @function Script.stop - * @param {boolean} [marshal=false] - Marshal. - *

Deprecated: This parameter is deprecated and will be removed.

- * @example Stop a script after 5s. - * Script.setInterval(function () { - * print("Hello"); - * }, 1000); - * - * Script.setTimeout(function () { - * Script.stop(true); - * }, 5000); - */ - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // NOTE - this is intended to be a public interface for Agent scripts, and local scripts, but not for EntityScripts - Q_INVOKABLE void stop(bool marshal = false); - - // Stop any evaluating scripts and wait for the scripting thread to finish. - void waitTillDoneRunning(bool shutdown = false); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // NOTE - these are NOT intended to be public interfaces available to scripts, the are only Q_INVOKABLE so we can - // properly ensure they are only called on the correct thread - - /**jsdoc - * @function Script.registerGlobalObject - * @param {string} name - Name. - * @param {object} object - Object. - * @deprecated This function is deprecated and will be removed. - */ - /// registers a global object by name - Q_INVOKABLE void registerGlobalObject(const QString& name, QObject* object); - - /**jsdoc - * @function Script.registerGetterSetter - * @param {string} name - Name. - * @param {function} getter - Getter. - * @param {function} setter - Setter. - * @param {string} [parent=""] - Parent. - * @deprecated This function is deprecated and will be removed. - */ - /// registers a global getter/setter - Q_INVOKABLE void registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter, - QScriptEngine::FunctionSignature setter, const QString& parent = QString("")); - - /**jsdoc - * @function Script.registerFunction - * @param {string} name - Name. - * @param {function} function - Function. - * @param {number} [numArguments=-1] - Number of arguments. - * @deprecated This function is deprecated and will be removed. - */ - /// register a global function - Q_INVOKABLE void registerFunction(const QString& name, QScriptEngine::FunctionSignature fun, int numArguments = -1); - - /**jsdoc - * @function Script.registerFunction - * @param {string} parent - Parent. - * @param {string} name - Name. - * @param {function} function - Function. - * @param {number} [numArguments=-1] - Number of arguments. - * @deprecated This function is deprecated and will be removed. - */ - /// register a function as a method on a previously registered global object - Q_INVOKABLE void registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature fun, - int numArguments = -1); - - /**jsdoc - * @function Script.registerEnum - * @param {string} name - Name. - * @param {object} enum - Enum. - * @deprecated This function is deprecated and will be removed. - */ - // WARNING: This function must be called after a registerGlobalObject that creates the namespace this enum is located in, or - // the globalObject won't function. E.g., if you have a Foo object and a Foo.FooType enum, Foo must be registered first. - /// registers a global enum - Q_INVOKABLE void registerEnum(const QString& enumName, QMetaEnum newEnum); - - /**jsdoc - * @function Script.registerValue - * @param {string} name - Name. - * @param {object} value - Value. - * @deprecated This function is deprecated and will be removed. - */ - /// registers a global object by name - Q_INVOKABLE void registerValue(const QString& valueName, QScriptValue value); - - /**jsdoc - * @function Script.evaluate - * @param {string} program - Program. - * @param {string} filename - File name. - * @param {number} [lineNumber=-1] - Line number. - * @returns {object} Object. - * @deprecated This function is deprecated and will be removed. - */ - /// evaluate some code in the context of the ScriptEngine and return the result - Q_INVOKABLE QScriptValue evaluate(const QString& program, const QString& fileName, int lineNumber = 1); // this is also used by the script tool widget - - /**jsdoc - * @function Script.evaluateInClosure - * @param {object} locals - Locals. - * @param {object} program - Program. - * @returns {object} Object. - * @deprecated This function is deprecated and will be removed. - */ - Q_INVOKABLE QScriptValue evaluateInClosure(const QScriptValue& locals, const QScriptProgram& program); - - /// if the script engine is not already running, this will download the URL and start the process of seting it up - /// to run... NOTE - this is used by Application currently to load the url. We don't really want it to be exposed - /// to scripts. we may not need this to be invokable - void loadURL(const QUrl& scriptURL, bool reload); - bool hasValidScriptSuffix(const QString& scriptFileName); - - /**jsdoc - * Gets the context that the script is running in: Interface/avatar, client entity, server entity, or assignment client. - * @function Script.getContext - * @returns {string} The context that the script is running in: - *
    - *
  • "client": An Interface or avatar script.
  • - *
  • "entity_client": A client entity script.
  • - *
  • "entity_server": A server entity script.
  • - *
  • "agent": An assignment client script.
  • - *
- */ - Q_INVOKABLE QString getContext() const; - - /**jsdoc - * Checks whether the script is running as an Interface or avatar script. - * @function Script.isClientScript - * @returns {boolean} true if the script is running as an Interface or avatar script, false if it - * isn't. - */ - Q_INVOKABLE bool isClientScript() const { return _context == CLIENT_SCRIPT; } - - /**jsdoc - * Checks whether the application was compiled as a debug build. - * @function Script.isDebugMode - * @returns {boolean} true if the application was compiled as a debug build, false if it was - * compiled as a release build. - */ - Q_INVOKABLE bool isDebugMode() const; - - /**jsdoc - * Checks whether the script is running as a client entity script. - * @function Script.isEntityClientScript - * @returns {boolean} true if the script is running as a client entity script, false if it isn't. - */ - Q_INVOKABLE bool isEntityClientScript() const { return _context == ENTITY_CLIENT_SCRIPT; } - - /**jsdoc - * Checks whether the script is running as a server entity script. - * @function Script.isEntityServerScript - * @returns {boolean} true if the script is running as a server entity script, false if it isn't. - */ - Q_INVOKABLE bool isEntityServerScript() const { return _context == ENTITY_SERVER_SCRIPT; } - - /**jsdoc - * Checks whether the script is running as an assignment client script. - * @function Script.isAgentScript - * @returns {boolean} true if the script is running as an assignment client script, false if it - * isn't. - */ - Q_INVOKABLE bool isAgentScript() const { return _context == AGENT_SCRIPT; } - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // NOTE - these are intended to be public interfaces available to scripts - - /**jsdoc - * Adds a function to the list of functions called when a particular event occurs on a particular entity. - *

See also, the {@link Entities} API.

- * @function Script.addEventHandler - * @param {Uuid} entityID - The ID of the entity. - * @param {Script.EntityEvent} eventName - The name of the event. - * @param {Script~entityEventCallback|Script~pointerEventCallback|Script~collisionEventCallback} handler - The function to - * call when the event occurs on the entity. It can be either the name of a function or an in-line definition. - * @example Report when a mouse press occurs on a particular entity. - * var entityID = Entities.addEntity({ - * type: "Box", - * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), - * dimensions: { x: 0.5, y: 0.5, z: 0.5 }, - * lifetime: 300 // Delete after 5 minutes. - * }); - * - * function reportMousePress(entityID, event) { - * print("Mouse pressed on entity: " + JSON.stringify(event)); - * } - * - * Script.addEventHandler(entityID, "mousePressOnEntity", reportMousePress); - */ - Q_INVOKABLE void addEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler); - - /**jsdoc - * Removes a function from the list of functions called when an entity event occurs on a particular entity. - *

See also, the {@link Entities} API.

- * @function Script.removeEventHandler - * @param {Uuid} entityID - The ID of the entity. - * @param {Script.EntityEvent} eventName - The name of the entity event. - * @param {function} handler - The name of the function to no longer call when the entity event occurs on the entity. - */ - Q_INVOKABLE void removeEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler); - - /**jsdoc - * Starts running another script in Interface, if it isn't already running. The script is not automatically loaded next - * time Interface starts. - *

Supported Script Types: Interface Scripts • Avatar Scripts

- *

See also, {@link ScriptDiscoveryService.loadScript}.

- * @function Script.load - * @param {string} filename - The URL of the script to load. This can be relative to the current script's URL. - * @example Load a script from another script. - * // First file: scriptA.js - * print("This is script A"); - * - * // Second file: scriptB.js - * print("This is script B"); - * Script.load("scriptA.js"); - * - * // If you run scriptB.js you should see both scripts in the Running Scripts dialog. - * // And you should see the following output: - * // This is script B - * // This is script A - */ - Q_INVOKABLE void load(const QString& loadfile); - - /**jsdoc - * Includes JavaScript from other files in the current script. If a callback is specified, the files are loaded and - * included asynchronously, otherwise they are included synchronously (i.e., script execution blocks while the files are - * included). - * @function Script.include - * @variation 0 - * @param {string[]} filenames - The URLs of the scripts to include. Each can be relative to the current script. - * @param {function} [callback=null] - The function to call back when the scripts have been included. It can be either the - * name of a function or an in-line definition. - */ - Q_INVOKABLE void include(const QStringList& includeFiles, QScriptValue callback = QScriptValue()); - - /**jsdoc - * Includes JavaScript from another file in the current script. If a callback is specified, the file is loaded and included - * asynchronously, otherwise it is included synchronously (i.e., script execution blocks while the file is included). - * @function Script.include - * @param {string} filename - The URL of the script to include. It can be relative to the current script. - * @param {function} [callback=null] - The function to call back when the script has been included. It can be either the - * name of a function or an in-line definition. - * @example Include a script file asynchronously. - * // First file: scriptA.js - * print("This is script A"); - * - * // Second file: scriptB.js - * print("This is script B"); - * Script.include("scriptA.js", function () { - * print("Script A has been included"); - * }); - * - * // If you run scriptB.js you should see only scriptB.js in the running scripts list. - * // And you should see the following output: - * // This is script B - * // This is script A - * // Script A has been included - */ - Q_INVOKABLE void include(const QString& includeFile, QScriptValue callback = QScriptValue()); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // MODULE related methods - - /**jsdoc - * Provides access to methods or objects provided in an external JavaScript or JSON file. - * See {@link https://docs.vircadia.dev/script/js-tips.html} for further details. - * @function Script.require - * @param {string} module - The module to use. May be a JavaScript file, a JSON file, or the name of a system module such - * as "appUi" (i.e., the "appUi.js" system module JavaScript file). - * @returns {object|array} The value assigned to module.exports in the JavaScript file, or the value defined - * in the JSON file. - */ - Q_INVOKABLE QScriptValue require(const QString& moduleId); - - /**jsdoc - * @function Script.resetModuleCache - * @param {boolean} [deleteScriptCache=false] - Delete script cache. - * @deprecated This function is deprecated and will be removed. - */ - Q_INVOKABLE void resetModuleCache(bool deleteScriptCache = false); - - QScriptValue currentModule(); - bool registerModuleWithParent(const QScriptValue& module, const QScriptValue& parent); - QScriptValue newModule(const QString& modulePath, const QScriptValue& parent = QScriptValue()); - QVariantMap fetchModuleSource(const QString& modulePath, const bool forceDownload = false); - QScriptValue instantiateModule(const QScriptValue& module, const QString& sourceCode); - - /**jsdoc - * Calls a function repeatedly, at a set interval. - * @function Script.setInterval - * @param {function} function - The function to call. This can be either the name of a function or an in-line definition. - * @param {number} interval - The interval at which to call the function, in ms. - * @returns {object} A handle to the interval timer. This can be used in {@link Script.clearInterval}. - * @example Print a message every second. - * Script.setInterval(function () { - * print("Interval timer fired"); - * }, 1000); - */ - Q_INVOKABLE QObject* setInterval(const QScriptValue& function, int intervalMS); - - /**jsdoc - * Calls a function once, after a delay. - * @function Script.setTimeout - * @param {function} function - The function to call. This can be either the name of a function or an in-line definition. - * @param {number} timeout - The delay after which to call the function, in ms. - * @returns {object} A handle to the timeout timer. This can be used in {@link Script.clearTimeout}. - * @example Print a message once, after a second. - * Script.setTimeout(function () { - * print("Timeout timer fired"); - * }, 1000); - */ - Q_INVOKABLE QObject* setTimeout(const QScriptValue& function, int timeoutMS); - - /**jsdoc - * Stops an interval timer set by {@link Script.setInterval|setInterval}. - * @function Script.clearInterval - * @param {object} timer - The interval timer to stop. - * @example Stop an interval timer. - * // Print a message every second. - * var timer = Script.setInterval(function () { - * print("Interval timer fired"); - * }, 1000); - * - * // Stop the timer after 10 seconds. - * Script.setTimeout(function () { - * print("Stop interval timer"); - * Script.clearInterval(timer); - * }, 10000); - */ - Q_INVOKABLE void clearInterval(QObject* timer) { stopTimer(reinterpret_cast(timer)); } - - /**jsdoc - * Stops a timeout timer set by {@link Script.setTimeout|setTimeout}. - * @function Script.clearTimeout - * @param {object} timer - The timeout timer to stop. - * @example Stop a timeout timer. - * // Print a message after two seconds. - * var timer = Script.setTimeout(function () { - * print("Timer fired"); - * }, 2000); - * - * // Uncomment the following line to stop the timer from firing. - * //Script.clearTimeout(timer); - */ - Q_INVOKABLE void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast(timer)); } - - /**jsdoc - * Prints a message to the program log and emits {@link Script.printedMessage}. - *

Alternatively, you can use {@link print} or one of the {@link console} API methods.

- * @function Script.print - * @param {string} message - The message to print. - */ - Q_INVOKABLE void print(const QString& message); - - /**jsdoc - * Resolves a relative path to an absolute path. The relative path is relative to the script's location. - * @function Script.resolvePath - * @param {string} path - The relative path to resolve. - * @returns {string} The absolute path. - * @example Report the directory and filename of the running script. - * print(Script.resolvePath("")); - * @example Report the directory of the running script. - * print(Script.resolvePath(".")); - * @example Report the path to a file located relative to the running script. - * print(Script.resolvePath("../assets/sounds/hello.wav")); - */ - Q_INVOKABLE QUrl resolvePath(const QString& path) const; - - /**jsdoc - * Gets the path to the resources directory for QML files. - * @function Script.resourcesPath - * @returns {string} The path to the resources directory for QML files. - */ - Q_INVOKABLE QUrl resourcesPath() const; - - /**jsdoc - * Starts timing a section of code in order to send usage data about it to Vircadia. Shouldn't be used outside of the - * standard scripts. - * @function Script.beginProfileRange - * @param {string} label - A name that identifies the section of code. - */ - Q_INVOKABLE void beginProfileRange(const QString& label) const; + Q_DECLARE_FLAGS(QObjectWrapOptions, QObjectWrapOption); - /**jsdoc - * Finishes timing a section of code in order to send usage data about it to Vircadia. Shouldn't be used outside of - * the standard scripts. - * @function Script.endProfileRange - * @param {string} label - A name that identifies the section of code. - */ - Q_INVOKABLE void endProfileRange(const QString& label) const; - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Entity Script Related methods +public: + virtual ScriptContext* currentContext() const = 0; + virtual ScriptValuePointer globalObject() const = 0; + virtual ScriptManager* manager() const = 0; + virtual ScriptValuePointer newArray(uint length = 0) = 0; + virtual ScriptValuePointer newArrayBuffer(const QByteArray& message) = 0; + virtual ScriptValuePointer newFunction(FunctionSignature fun, int length = 0) = 0; + virtual ScriptValuePointer newObject() = 0; + virtual ScriptProgramPointer newProgram(const QString& sourceCode, const QString& fileName) = 0; + virtual ScriptValuePointer newQObject(QObject *object, ValueOwnership ownership = QtOwnership, const QObjectWrapOptions &options = QObjectWrapOptions()) = 0; + virtual ScriptValuePointer newValue(bool value) = 0; + virtual ScriptValuePointer newValue(int value) = 0; + virtual ScriptValuePointer newValue(uint value) = 0; + virtual ScriptValuePointer newValue(double value) = 0; + virtual ScriptValuePointer newValue(const QString& value) = 0; + virtual ScriptValuePointer newValue(const QLatin1String& value) = 0; + virtual ScriptValuePointer newValue(const char* value) = 0; + virtual ScriptValuePointer newVariant(const QVariant& value) = 0; + virtual ScriptValuePointer nullValue() = 0; + virtual void registerFunction(const QString& name, FunctionSignature fun, int numArguments = -1) = 0; + virtual void registerFunction(const QString& parent, const QString& name, FunctionSignature fun, int numArguments = -1) = 0; + virtual void registerGetterSetter(const QString& name, FunctionSignature getter, FunctionSignature setter, const QString& parent = QString("")) = 0; + virtual void registerGlobalObject(const QString& name, QObject* object) = 0; + virtual void setDefaultPrototype(int metaTypeId, const ScriptValuePointer& prototype) = 0; + inline QThread* thread() const; + virtual ScriptValuePointer undefinedValue() = 0; - /**jsdoc - * Checks whether an entity has an entity script running. - * @function Script.isEntityScriptRunning - * @param {Uuid} entityID - The ID of the entity. - * @returns {boolean} true if the entity has an entity script running, false if it doesn't. - */ - Q_INVOKABLE bool isEntityScriptRunning(const EntityItemID& entityID) { - QReadLocker locker { &_entityScriptsLock }; - auto it = _entityScripts.constFind(entityID); - return it != _entityScripts.constEnd() && it->status == EntityScriptStatus::RUNNING; +public: + template + inline T fromScriptValue(const ScriptValuePointer& value) { + return scriptvalue_cast(value); } - QVariant cloneEntityScriptDetails(const EntityItemID& entityID); - QFuture getLocalEntityScriptDetails(const EntityItemID& entityID) override; - - /**jsdoc - * @function Script.loadEntityScript - * @param {Uuid} entityID - Entity ID. - * @param {string} script - Script. - * @param {boolean} forceRedownload - Force re-download. - * @deprecated This function is deprecated and will be removed. - */ - Q_INVOKABLE void loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload); - - /**jsdoc - * @function Script.unloadEntityScript - * @param {Uuid} entityID - Entity ID. - * @param {boolean} [shouldRemoveFromMap=false] - Should remove from map. - * @deprecated This function is deprecated and will be removed. - */ - Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID, bool shouldRemoveFromMap = false); // will call unload method - - /**jsdoc - * @function Script.unloadAllEntityScripts - * @param {boolean} [blockingCall=false] - Wait for completion if call moved to another thread. - * @deprecated This function is deprecated and will be removed. - */ - Q_INVOKABLE void unloadAllEntityScripts(bool blockingCall = false); - - /**jsdoc - * Calls a method in an entity script. - * @function Script.callEntityScriptMethod - * @param {Uuid} entityID - The ID of the entity running the entity script. - * @param {string} methodName - The name of the method to call. - * @param {string[]} [parameters=[]] - The parameters to call the specified method with. - * @param {Uuid} [remoteCallerID=Uuid.NULL] - An ID that identifies the caller. - */ - Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, - const QStringList& params = QStringList(), - const QUuid& remoteCallerID = QUuid()) override; - - /**jsdoc - * Calls a method in an entity script. - * @function Script.callEntityScriptMethod - * @param {Uuid} entityID - Entity ID. - * @param {string} methodName - Method name. - * @param {PointerEvent} event - Pointer event. - * @deprecated This function is deprecated and will be removed. - */ - Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const PointerEvent& event); - - /**jsdoc - * Calls a method in an entity script. - * @function Script.callEntityScriptMethod - * @param {Uuid} entityID - Entity ID. - * @param {string} methodName - Method name. - * @param {Uuid} otherID - Other entity ID. - * @param {Collision} collision - Collision. - * @deprecated This function is deprecated and will be removed. - */ - Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision); - - /**jsdoc - * Manually runs the JavaScript garbage collector which reclaims memory by disposing of objects that are no longer - * reachable. - * @function Script.requestGarbageCollection - */ - Q_INVOKABLE void requestGarbageCollection() { collectGarbage(); } - - /**jsdoc - * @function Script.generateUUID - * @returns {Uuid} A new UUID. - * @deprecated This function is deprecated and will be removed. Use {@link Uuid(0).generate|Uuid.generate} instead. - */ - Q_INVOKABLE QUuid generateUUID() { return QUuid::createUuid(); } - - void setType(Type type) { _type = type; }; - Type getType() { return _type; }; - QString getTypeAsString() const; - - bool isFinished() const { return _isFinished; } // used by Application and ScriptWidget - bool isRunning() const { return _isRunning; } // used by ScriptWidget - - // this is used by code in ScriptEngines.cpp during the "reload all" operation - bool isStopping() const { return _isStopping; } - - bool isDebuggable() const { return _debuggable; } - - void disconnectNonEssentialSignals(); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // These are currently used by Application to track if a script is user loaded or not. Consider finding a solution - // inside of Application so that the ScriptEngine class is not polluted by this notion - void setUserLoaded(bool isUserLoaded) { _isUserLoaded = isUserLoaded; } - bool isUserLoaded() const { return _isUserLoaded; } - - void setQuitWhenFinished(const bool quitWhenFinished) { _quitWhenFinished = quitWhenFinished; } - bool isQuitWhenFinished() const { return _quitWhenFinished; } - - // NOTE - this is used by the TypedArray implementation. we need to review this for thread safety - ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } - - void setEmitScriptUpdatesFunction(std::function func) { _emitScriptUpdates = func; } - - void scriptErrorMessage(const QString& message); - void scriptWarningMessage(const QString& message); - void scriptInfoMessage(const QString& message); - void scriptPrintedMessage(const QString& message); - void clearDebugLogWindow(); - int getNumRunningEntityScripts() const; - bool getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const; - bool hasEntityScriptDetails(const EntityItemID& entityID) const; - - void setScriptEngines(QSharedPointer& scriptEngines) { _scriptEngines = scriptEngines; } - - /**jsdoc - * Gets the URL for an asset in an external resource bucket. (The location where the bucket is hosted may change over time - * but this method will return the asset's current URL.) - * @function Script.getExternalPath - * @param {Script.ResourceBucket} bucket - The external resource bucket that the asset is in. - * @param {string} path - The path within the external resource bucket where the asset is located. - *

Normally, this should start with a path or filename to be appended to the bucket URL. - * Alternatively, it can be a relative path starting with ./ or ../, to navigate within the - * resource bucket's URL.

- * @Returns {string} The URL of an external asset. - * @example Report the URL of a default particle. - * print(Script.getExternalPath(Script.ExternalPaths.Assets, "Bazaar/Assets/Textures/Defaults/Interface/default_particle.png")); - * @example Report the root directory where the Vircadia assets are located. - * print(Script.getExternalPath(Script.ExternalPaths.Assets, ".")); - */ - Q_INVOKABLE QString getExternalPath(ExternalResource::Bucket bucket, const QString& path); - -public slots: - - /**jsdoc - * @function Script.callAnimationStateHandler - * @param {function} callback - Callback function. - * @param {object} parameters - Parameters. - * @param {string[]} names - Names. - * @param {boolean} useNames - Use names. - * @param {function} resultHandler - Result handler. - * @deprecated This function is deprecated and will be removed. - */ - void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler); - - /**jsdoc - * @function Script.updateMemoryCost - * @param {number} deltaSize - Delta size. - * @deprecated This function is deprecated and will be removed. - */ - void updateMemoryCost(const qint64&); - -signals: - - /**jsdoc - * @function Script.scriptLoaded - * @param {string} filename - File name. - * @returns {Signal} - * @deprecated This signal is deprecated and will be removed. - */ - void scriptLoaded(const QString& scriptFilename); - - /**jsdoc - * @function Script.errorLoadingScript - * @param {string} filename - File name. - * @returns {Signal} - * @deprecated This signal is deprecated and will be removed. - */ - void errorLoadingScript(const QString& scriptFilename); - - /**jsdoc - * Triggered frequently at a system-determined interval. - * @function Script.update - * @param {number} deltaTime - The time since the last update, in s. - * @returns {Signal} - * @example Report script update intervals. - * Script.update.connect(function (deltaTime) { - * print("Update: " + deltaTime); - * }); - */ - void update(float deltaTime); - - /**jsdoc - * Triggered when the script is stopping. - * @function Script.scriptEnding - * @returns {Signal} - * @example Report when a script is stopping. - * print("Script started"); - * - * Script.scriptEnding.connect(function () { - * print("Script ending"); - * }); - * - * Script.setTimeout(function () { - * print("Stopping script"); - * Script.stop(); - * }, 1000); - */ - void scriptEnding(); - - /**jsdoc - * @function Script.finished - * @param {string} filename - File name. - * @param {object} engine - Engine. - * @returns {Signal} - * @deprecated This signal is deprecated and will be removed. - */ - void finished(const QString& fileNameString, ScriptEnginePointer); - - /**jsdoc - * @function Script.cleanupMenuItem - * @param {string} menuItem - Menu item. - * @returns {Signal} - * @deprecated This signal is deprecated and will be removed. - */ - void cleanupMenuItem(const QString& menuItemString); - /**jsdoc - * Triggered when the script prints a message to the program log via {@link print}, {@link Script.print}, - * {@link console.log}, {@link console.debug}, {@link console.group}, {@link console.groupEnd}, {@link console.time}, or - * {@link console.timeEnd}. - * @function Script.printedMessage - * @param {string} message - The message. - * @param {string} scriptName - The name of the script that generated the message. - * @returns {Signal} - */ - void printedMessage(const QString& message, const QString& scriptName); - - /**jsdoc - * Triggered when the script generates an error, {@link console.error} or {@link console.exception} is called, or - * {@link console.assert} is called and fails. - * @function Script.errorMessage - * @param {string} message - The error message. - * @param {string} scriptName - The name of the script that generated the error message. - * @returns {Signal} - */ - void errorMessage(const QString& message, const QString& scriptName); - - /**jsdoc - * Triggered when the script generates a warning or {@link console.warn} is called. - * @function Script.warningMessage - * @param {string} message - The warning message. - * @param {string} scriptName - The name of the script that generated the warning message. - * @returns {Signal} - */ - void warningMessage(const QString& message, const QString& scriptName); - - /**jsdoc - * Triggered when the script generates an information message or {@link console.info} is called. - * @function Script.infoMessage - * @param {string} message - The information message. - * @param {string} scriptName - The name of the script that generated the information message. - * @returns {Signal} - */ - void infoMessage(const QString& message, const QString& scriptName); - - /**jsdoc - * Triggered when the running state of the script changes, e.g., from running to stopping. - * @function Script.runningStateChanged - * @returns {Signal} - */ - void runningStateChanged(); - - /**jsdoc - * @function Script.clearDebugWindow - * @returns {Signal} - * @deprecated This signal is deprecated and will be removed. - */ - void clearDebugWindow(); - - /**jsdoc - * @function Script.loadScript - * @param {string} scriptName - Script name. - * @param {boolean} isUserLoaded - Is user loaded. - * @returns {Signal} - * @deprecated This signal is deprecated and will be removed. - */ - void loadScript(const QString& scriptName, bool isUserLoaded); - - /**jsdoc - * @function Script.reloadScript - * @param {string} scriptName - Script name. - * @param {boolean} isUserLoaded - Is user loaded. - * @returns {Signal} - * @deprecated This signal is deprecated and will be removed. - */ - void reloadScript(const QString& scriptName, bool isUserLoaded); - - /**jsdoc - * Triggered when the script has stopped. - * @function Script.doneRunning - * @returns {Signal} - */ - void doneRunning(); - - /**jsdoc - * @function Script.entityScriptDetailsUpdated - * @returns {Signal} - * @deprecated This signal is deprecated and will be removed. - */ - // Emitted when an entity script is added or removed, or when the status of an entity - // script is updated (goes from RUNNING to ERROR_RUNNING_SCRIPT, for example) - void entityScriptDetailsUpdated(); - - /**jsdoc - * Triggered when the script starts for the user. See also, {@link Entities.preload}. - *

Supported Script Types: Client Entity Scripts • Server Entity Scripts

- * @function Script.entityScriptPreloadFinished - * @param {Uuid} entityID - The ID of the entity that the script is running in. - * @returns {Signal} - * @example Get the ID of the entity that a client entity script is running in. - * var entityScript = function () { - * this.entityID = Uuid.NULL; - * }; - * - * Script.entityScriptPreloadFinished.connect(function (entityID) { - * this.entityID = entityID; - * print("Entity ID: " + this.entityID); - * }); - * - * var entityID = Entities.addEntity({ - * type: "Box", - * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), - * dimensions: { x: 0.5, y: 0.5, z: 0.5 }, - * color: { red: 255, green: 0, blue: 0 }, - * script: "(" + entityScript + ")", // Could host the script on a Web server instead. - * lifetime: 300 // Delete after 5 minutes. - * }); - */ - // Emitted when an entity script has finished running preload - void entityScriptPreloadFinished(const EntityItemID& entityID); - -protected: - void init(); - - /**jsdoc - * @function Script.executeOnScriptThread - * @param {function} function - Function. - * @param {ConnectionType} [type=2] - Connection type. - * @deprecated This function is deprecated and will be removed. - */ - Q_INVOKABLE void executeOnScriptThread(std::function function, const Qt::ConnectionType& type = Qt::QueuedConnection ); - - /**jsdoc - * @function Script._requireResolve - * @param {string} module - Module. - * @param {string} [relativeTo=""] - Relative to. - * @returns {string} Result. - * @deprecated This function is deprecated and will be removed. - */ - // note: this is not meant to be called directly, but just to have QMetaObject take care of wiring it up in general; - // then inside of init() we just have to do "Script.require.resolve = Script._requireResolve;" - Q_INVOKABLE QString _requireResolve(const QString& moduleId, const QString& relativeTo = QString()); - - QString logException(const QScriptValue& exception); - void timerFired(); - void stopAllTimers(); - void stopAllTimersForEntityScript(const EntityItemID& entityID); - void refreshFileScript(const EntityItemID& entityID); - void updateEntityScriptStatus(const EntityItemID& entityID, const EntityScriptStatus& status, const QString& errorInfo = QString()); - void setEntityScriptDetails(const EntityItemID& entityID, const EntityScriptDetails& details); - void setParentURL(const QString& parentURL) { _parentURL = parentURL; } - - QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot); - void stopTimer(QTimer* timer); - - QHash _registeredHandlers; - void forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHanderArgs); - - /**jsdoc - * @function Script.entityScriptContentAvailable - * @param {Uuid} entityID - Entity ID. - * @param {string} scriptOrURL - Path. - * @param {string} contents - Contents. - * @param {boolean} isURL - Is a URL. - * @param {boolean} success - Success. - * @param {string} status - Status. - * @deprecated This function is deprecated and will be removed. - */ - Q_INVOKABLE void entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success, const QString& status); - - EntityItemID currentEntityIdentifier; // Contains the defining entity script entity id during execution, if any. Empty for interface script execution. - QUrl currentSandboxURL; // The toplevel url string for the entity script that loaded the code being executed, else empty. - void doWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, std::function operation); - void callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, QScriptValue function, QScriptValue thisObject, QScriptValueList args); - - Context _context; - Type _type; - QString _scriptContents; - QString _parentURL; - std::atomic _isFinished { false }; - std::atomic _isRunning { false }; - std::atomic _isStopping { false }; - bool _isInitialized { false }; - QHash _timerFunctionMap; - QSet _includedURLs; - mutable QReadWriteLock _entityScriptsLock { QReadWriteLock::Recursive }; - QHash _entityScripts; - EntityScriptContentAvailableMap _contentAvailableQueue; - - bool _isThreaded { false }; - QScriptEngineDebugger* _debugger { nullptr }; - bool _debuggable { false }; - qint64 _lastUpdate; - - QString _fileNameString; - Quat _quatLibrary; - Vec3 _vec3Library; - Mat4 _mat4Library; - ScriptUUID _uuidLibrary; - ConsoleScriptingInterface _consoleScriptingInterface; - std::atomic _isUserLoaded { false }; - bool _isReloading { false }; - - std::atomic _quitWhenFinished; - - ArrayBufferClass* _arrayBufferClass; - - AssetScriptingInterface* _assetScriptingInterface; - - std::function _emitScriptUpdates{ []() { return true; } }; - - std::recursive_mutex _lock; - - std::chrono::microseconds _totalTimerExecution { 0 }; - - static const QString _SETTINGS_ENABLE_EXTENDED_MODULE_COMPAT; - static const QString _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS; - - Setting::Handle _enableExtendedJSExceptions { _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS, true }; + template + inline ScriptValuePointer toScriptValue(const T& value) { + return scriptValueFromValue(this, value); + } - QWeakPointer _scriptEngines; +public: // not for public use, but I don't like how Qt strings this along with private friend functions + virtual ScriptValuePointer create(int type, const void* ptr) = 0; + virtual bool convert(const ScriptValuePointer& value, int type, void* ptr) = 0; + virtual void registerCustomType(int type, MarshalFunction mf, DemarshalFunction df, const ScriptValuePointer& prototype) = 0; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(ScriptEngine::QObjectWrapOptions) + +ScriptEnginePointer newScriptEngine(); -ScriptEnginePointer scriptEngineFactory(ScriptEngine::Context context, - const QString& scriptContents, - const QString& fileNameString); +// Standardized CPS callback helpers (see: http://fredkschott.com/post/2014/03/understanding-error-first-callbacks-in-node-js/) +// These two helpers allow async JS APIs that use a callback parameter to be more friendly to scripters by accepting thisObject +// context and adopting a consistent and intuitable callback signature: +// function callback(err, result) { if (err) { ... } else { /* do stuff with result */ } } +// +// To use, first pass the user-specified callback args in the same order used with optionally-scoped Qt signal connections: +// auto handler = makeScopedHandlerObject(scopeOrCallback, optionalMethodOrName); +// And then invoke the scoped handler later per CPS conventions: +// auto result = callScopedHandlerObject(handler, err, result); +ScriptValuePointer makeScopedHandlerObject(ScriptValuePointer scopeOrCallback, ScriptValuePointer methodOrName); +ScriptValuePointer callScopedHandlerObject(ScriptValuePointer handler, ScriptValuePointer err, ScriptValuePointer result); + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Inline implementations +/* +QThread* ScriptEngine::thread() const { + QObject* qobject = toQObject(); + if (qobject == nullptr) { + return nullptr; + } + return qobject->thread(); +} +*/ -#endif // hifi_ScriptEngine_h +#endif // hifi_ScriptEngine_h diff --git a/libraries/script-engine/src/ScriptEngineCast.h b/libraries/script-engine/src/ScriptEngineCast.h new file mode 100644 index 00000000000..ba56f920d4b --- /dev/null +++ b/libraries/script-engine/src/ScriptEngineCast.h @@ -0,0 +1,99 @@ +// +// ScriptEngineCast.h +// libraries/script-engine/src +// +// Created by Heather Anderson on 5/9/2021. +// Copyright 2021 Vircadia contributors. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ScriptEngineCast_h +#define hifi_ScriptEngineCast_h + +// Object conversion helpers (copied from QScriptEngine) + +#include "ScriptEngine.h" +#include "ScriptValue.h" + +template +inline ScriptValuePointer scriptValueFromValue(ScriptEngine* engine, const T& t) { + if (!engine) { + return ScriptValuePointer(); + } + + return engine->create(qMetaTypeId(), &t); +} + +template <> +inline ScriptValuePointer scriptValueFromValue(ScriptEngine* engine, const QVariant& v) { + if (!engine) { + return ScriptValuePointer(); + } + + return engine->create(v.userType(), v.data()); +} + +template +T scriptvalue_cast(const ScriptValuePointer& value) { + T t; + const int id = qMetaTypeId(); + + auto engine = value->engine(); + if (engine && engine->convert(value, id, &t)) { + return t; + } else if (value->isVariant()) { + return qvariant_cast(value->toVariant()); + } + + return T(); +} + +template <> +inline QVariant scriptvalue_cast(const ScriptValuePointer& value) { + return value->toVariant(); +} + +template +int scriptRegisterMetaType(ScriptEngine* eng, + ScriptValuePointer (*toScriptValue)(ScriptEngine*, const T& t), + void (*fromScriptValue)(const ScriptValuePointer&, T& t), + const ScriptValuePointer& prototype = ScriptValuePointer(), + T* = 0) { + const int id = qRegisterMetaType(); // make sure it's registered + eng->registerCustomType(id, reinterpret_cast(toScriptValue), + reinterpret_cast(fromScriptValue), prototype); + return id; +} + +template +ScriptValuePointer scriptValueFromSequence(ScriptEngine* eng, const Container& cont) { + ScriptValuePointer a = eng->newArray(); + typename Container::const_iterator begin = cont.begin(); + typename Container::const_iterator end = cont.end(); + typename Container::const_iterator it; + quint32 i; + for (it = begin, i = 0; it != end; ++it, ++i) { + a->setProperty(i, eng->toScriptValue(*it)); + } + return a; +} + +template +void scriptValueToSequence(const ScriptValuePointer& value, Container& cont) { + quint32 len = value->property(QLatin1String("length"))->toUInt32(); + for (quint32 i = 0; i < len; ++i) { + ScriptValuePointer item = value->property(i); + cont.push_back(scriptvalue_cast(item)); + } +} + +template +int scriptRegisterSequenceMetaType(ScriptEngine* engine, + const ScriptValuePointer& prototype = ScriptValuePointer(), + T* = 0) { + return scriptRegisterMetaType(engine, scriptValueFromSequence, scriptValueToSequence, prototype); +} + +#endif // hifi_ScriptEngineCast_h diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index d091e6e4b52..08fd446766f 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -18,6 +19,7 @@ #include #include +#include "ScriptCache.h" #include "ScriptEngine.h" #include "ScriptEngineLogging.h" @@ -65,7 +67,7 @@ void ScriptEngines::onErrorLoadingScript(const QString& url) { emit errorLoadingScript(url); } -ScriptEngines::ScriptEngines(ScriptEngine::Context context, const QUrl& defaultScriptsOverride) +ScriptEngines::ScriptEngines(ScriptManager::Context context, const QUrl& defaultScriptsOverride) : _context(context), _defaultScriptsOverride(defaultScriptsOverride) { scriptGatekeeper.initialize(); @@ -138,53 +140,53 @@ QUrl expandScriptUrl(const QUrl& rawScriptURL) { QObject* scriptsModel(); -void ScriptEngines::addScriptEngine(ScriptEnginePointer engine) { +void ScriptEngines::addScriptEngine(ScriptManagerPointer manager) { if (!_isStopped) { QMutexLocker locker(&_allScriptsMutex); - _allKnownScriptEngines.insert(engine); + _allKnownScriptManagers.insert(manager); } } -void ScriptEngines::removeScriptEngine(ScriptEnginePointer engine) { +void ScriptEngines::removeScriptEngine(ScriptManagerPointer manager) { // If we're not already in the middle of stopping all scripts, then we should remove ourselves // from the list of running scripts. We don't do this if we're in the process of stopping all scripts // because that method removes scripts from its list as it iterates them if (!_isStopped) { QMutexLocker locker(&_allScriptsMutex); - _allKnownScriptEngines.remove(engine); + _allKnownScriptManagers.remove(manager); } } void ScriptEngines::shutdownScripting() { _isStopped = true; QMutexLocker locker(&_allScriptsMutex); - qCDebug(scriptengine) << "Stopping all scripts.... currently known scripts:" << _allKnownScriptEngines.size(); + qCDebug(scriptengine) << "Stopping all scripts.... currently known scripts:" << _allKnownScriptManagers.size(); - QMutableSetIterator i(_allKnownScriptEngines); + QMutableSetIterator i(_allKnownScriptManagers); while (i.hasNext()) { - ScriptEnginePointer scriptEngine = i.next(); - QString scriptName = scriptEngine->getFilename(); + ScriptManagerPointer scriptManager = i.next(); + QString scriptName = scriptManager->getFilename(); // NOTE: typically all script engines are running. But there's at least one known exception to this, the // "entities sandbox" which is only used to evaluate entities scripts to test their validity before using // them. We don't need to stop scripts that aren't running. // TODO: Scripts could be shut down faster if we spread them across a threadpool. - if (scriptEngine->isRunning()) { + if (scriptManager->isRunning()) { qCDebug(scriptengine) << "about to shutdown script:" << scriptName; // We disconnect any script engine signals from the application because we don't want to do any // extra stopScript/loadScript processing that the Application normally does when scripts start // and stop. We can safely short circuit this because we know we're in the "quitting" process - scriptEngine->disconnect(this); + scriptManager->disconnect(this); // Gracefully stop the engine's scripting thread - scriptEngine->stop(); + scriptManager->stop(); // We need to wait for the engine to be done running before we proceed, because we don't // want any of the scripts final "scriptEnding()" or pending "update()" methods from accessing // any application state after we leave this stopAllScripts() method qCDebug(scriptengine) << "waiting on script:" << scriptName; - scriptEngine->waitTillDoneRunning(true); + scriptManager->waitTillDoneRunning(true); qCDebug(scriptengine) << "done waiting on script:" << scriptName; } // Once the script is stopped, we can remove it from our set @@ -372,8 +374,8 @@ void ScriptEngines::saveScripts() { QVariantList list; { - QReadLocker lock(&_scriptEnginesHashLock); - for (auto it = _scriptEnginesHash.begin(); it != _scriptEnginesHash.end(); ++it) { + QReadLocker lock(&_scriptManagersHashLock); + for (auto it = _scriptManagersHash.begin(); it != _scriptManagersHash.end(); ++it) { // Save user-loaded scripts, only if they are set to quit when finished if (it.value() && it.value()->isUserLoaded() && !it.value()->isQuitWhenFinished()) { auto normalizedUrl = normalizeScriptURL(it.key()); @@ -390,8 +392,8 @@ void ScriptEngines::saveScripts() { } QStringList ScriptEngines::getRunningScripts() { - QReadLocker lock(&_scriptEnginesHashLock); - QList urls = _scriptEnginesHash.keys(); + QReadLocker lock(&_scriptManagersHashLock); + QList urls = _scriptManagersHash.keys(); QStringList result; for (auto url : urls) { result.append(url.toString()); @@ -400,33 +402,33 @@ QStringList ScriptEngines::getRunningScripts() { } void ScriptEngines::stopAllScripts(bool restart) { - QReadLocker lock(&_scriptEnginesHashLock); + QReadLocker lock(&_scriptManagersHashLock); if (_isReloading) { return; } - for (QHash::const_iterator it = _scriptEnginesHash.constBegin(); - it != _scriptEnginesHash.constEnd(); it++) { - ScriptEnginePointer scriptEngine = it.value(); + for (QHash::const_iterator it = _scriptManagersHash.constBegin(); + it != _scriptManagersHash.constEnd(); it++) { + ScriptManagerPointer scriptManager = it.value(); // skip already stopped scripts - if (scriptEngine->isFinished() || scriptEngine->isStopping()) { + if (scriptManager->isFinished() || scriptManager->isStopping()) { continue; } bool isOverrideScript = it.key().toString().compare(this->_defaultScriptsOverride.toString()) == 0; // queue user scripts if restarting - if (restart && (scriptEngine->isUserLoaded() || isOverrideScript)) { + if (restart && (scriptManager->isUserLoaded() || isOverrideScript)) { _isReloading = true; - ScriptEngine::Type type = scriptEngine->getType(); + ScriptManager::Type type = scriptManager->getType(); - connect(scriptEngine.data(), &ScriptEngine::finished, this, [this, type, isOverrideScript] (QString scriptName) { + connect(scriptManager.data(), &ScriptManager::finished, this, [this, type, isOverrideScript](QString scriptName) { reloadScript(scriptName, !isOverrideScript)->setType(type); }); } // stop all scripts - scriptEngine->stop(); + scriptManager->stop(); } if (restart) { @@ -446,23 +448,23 @@ bool ScriptEngines::stopScript(const QString& rawScriptURL, bool restart) { scriptURL = normalizeScriptURL(QUrl::fromLocalFile(rawScriptURL)); } - QReadLocker lock(&_scriptEnginesHashLock); - if (_scriptEnginesHash.contains(scriptURL)) { - ScriptEnginePointer scriptEngine = _scriptEnginesHash[scriptURL]; + QReadLocker lock(&_scriptManagersHashLock); + if (_scriptManagersHash.contains(scriptURL)) { + ScriptManagerPointer scriptManager = _scriptManagersHash[scriptURL]; if (restart) { - bool isUserLoaded = scriptEngine->isUserLoaded(); - ScriptEngine::Type type = scriptEngine->getType(); + bool isUserLoaded = scriptManager->isUserLoaded(); + ScriptManager::Type type = scriptManager->getType(); auto scriptCache = DependencyManager::get(); scriptCache->deleteScript(scriptURL); - if (!scriptEngine->isStopping()) { - connect(scriptEngine.data(), &ScriptEngine::finished, + if (!scriptManager->isStopping()) { + connect(scriptManager.data(), &ScriptManager::finished, this, [this, isUserLoaded, type](QString scriptName, ScriptEnginePointer engine) { reloadScript(scriptName, isUserLoaded)->setType(type); }); } } - scriptEngine->stop(); + scriptManager->stop(); stoppedScript = true; } } @@ -480,11 +482,11 @@ void ScriptEngines::reloadAllScripts() { stopAllScripts(true); } -ScriptEnginePointer ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserLoaded, bool loadScriptFromEditor, +ScriptManagerPointer ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserLoaded, bool loadScriptFromEditor, bool activateMainWindow, bool reload, bool quitWhenFinished) { if (thread() != QThread::currentThread()) { - ScriptEnginePointer result { nullptr }; - BLOCKING_INVOKE_METHOD(this, "loadScript", Q_RETURN_ARG(ScriptEnginePointer, result), + ScriptManagerPointer result { nullptr }; + BLOCKING_INVOKE_METHOD(this, "loadScript", Q_RETURN_ARG(ScriptManagerPointer, result), Q_ARG(QUrl, scriptFilename), Q_ARG(bool, isUserLoaded), Q_ARG(bool, loadScriptFromEditor), @@ -508,41 +510,41 @@ ScriptEnginePointer ScriptEngines::loadScript(const QUrl& scriptFilename, bool i scriptUrl = QUrl(FileUtils::selectFile(scriptUrl.toString())); - auto scriptEngine = getScriptEngine(scriptUrl); - if (scriptEngine && !scriptEngine->isStopping()) { - return scriptEngine; + auto scriptManager = getScriptEngine(scriptUrl); + if (scriptManager && !scriptManager->isStopping()) { + return scriptManager; } - scriptEngine = scriptEngineFactory(_context, NO_SCRIPT, "about:" + scriptFilename.fileName()); - scriptEngine->setUserLoaded(isUserLoaded); - scriptEngine->setQuitWhenFinished(quitWhenFinished); + scriptManager = scriptManagerFactory(_context, NO_SCRIPT, "about:" + scriptFilename.fileName()); + scriptManager->setUserLoaded(isUserLoaded); + scriptManager->setQuitWhenFinished(quitWhenFinished); if (scriptFilename.isEmpty() || !scriptUrl.isValid()) { - launchScriptEngine(scriptEngine); + launchScriptEngine(scriptManager); } else { // connect to the appropriate signals of this script engine - connect(scriptEngine.data(), &ScriptEngine::scriptLoaded, this, &ScriptEngines::onScriptEngineLoaded); - connect(scriptEngine.data(), &ScriptEngine::errorLoadingScript, this, &ScriptEngines::onScriptEngineError); + connect(scriptManager.data(), &ScriptManager::scriptLoaded, this, &ScriptEngines::onScriptEngineLoaded); + connect(scriptManager.data(), &ScriptManager::errorLoadingScript, this, &ScriptEngines::onScriptEngineError); // Shutdown Interface when script finishes, if requested if (quitWhenFinished) { - connect(scriptEngine.data(), &ScriptEngine::finished, this, &ScriptEngines::quitWhenFinished); + connect(scriptManager.data(), &ScriptManager::finished, this, &ScriptEngines::quitWhenFinished); } // get the script engine object to load the script at the designated script URL - scriptEngine->loadURL(scriptUrl, reload); + scriptManager->loadURL(scriptUrl, reload); } - return scriptEngine; + return scriptManager; } -ScriptEnginePointer ScriptEngines::getScriptEngine(const QUrl& rawScriptURL) { - ScriptEnginePointer result; +ScriptManagerPointer ScriptEngines::getScriptEngine(const QUrl& rawScriptURL) { + ScriptManagerPointer result; { - QReadLocker lock(&_scriptEnginesHashLock); + QReadLocker lock(&_scriptManagersHashLock); const QUrl scriptURL = normalizeScriptURL(rawScriptURL); - auto it = _scriptEnginesHash.find(scriptURL); - if (it != _scriptEnginesHash.end()) { + auto it = _scriptManagersHash.find(scriptURL); + if (it != _scriptManagersHash.end()) { result = it.value(); } } @@ -552,16 +554,15 @@ ScriptEnginePointer ScriptEngines::getScriptEngine(const QUrl& rawScriptURL) { // FIXME - change to new version of ScriptCache loading notification void ScriptEngines::onScriptEngineLoaded(const QString& rawScriptURL) { UserActivityLogger::getInstance().loadedScript(rawScriptURL); - QSharedPointer baseScriptEngine = qobject_cast(sender())->sharedFromThis(); - ScriptEnginePointer scriptEngine = qSharedPointerCast(baseScriptEngine); + ScriptManagerPointer scriptEngine = qobject_cast(sender())->sharedFromThis(); launchScriptEngine(scriptEngine); { - QWriteLocker lock(&_scriptEnginesHashLock); + QWriteLocker lock(&_scriptManagersHashLock); QUrl url = QUrl(rawScriptURL); QUrl normalized = normalizeScriptURL(url); - _scriptEnginesHash.insertMulti(normalized, scriptEngine); + _scriptManagersHash.insertMulti(normalized, scriptEngine); } // Update settings with new script @@ -573,50 +574,50 @@ void ScriptEngines::quitWhenFinished() { qApp->quit(); } -int ScriptEngines::runScriptInitializers(ScriptEnginePointer scriptEngine) { - auto nativeCount = DependencyManager::get()->runScriptInitializers(scriptEngine.data()); - return nativeCount + ScriptInitializerMixin::runScriptInitializers(scriptEngine); +int ScriptEngines::runScriptInitializers(ScriptManagerPointer scriptManager) { + auto nativeCount = DependencyManager::get()->runScriptInitializers(scriptManager->engine().data()); + return nativeCount + ScriptInitializerMixin::runScriptInitializers(scriptManager); } -void ScriptEngines::launchScriptEngine(ScriptEnginePointer scriptEngine) { - connect(scriptEngine.data(), &ScriptEngine::finished, this, &ScriptEngines::onScriptFinished, Qt::DirectConnection); - connect(scriptEngine.data(), &ScriptEngine::loadScript, [this](const QString& scriptName, bool userLoaded) { +void ScriptEngines::launchScriptEngine(ScriptManagerPointer scriptManager) { + connect(scriptManager.data(), &ScriptManager::finished, this, &ScriptEngines::onScriptFinished, Qt::DirectConnection); + connect(scriptManager.data(), &ScriptManager::loadScript, [this](const QString& scriptName, bool userLoaded) { loadScript(scriptName, userLoaded); }); - connect(scriptEngine.data(), &ScriptEngine::reloadScript, [this](const QString& scriptName, bool userLoaded) { + connect(scriptManager.data(), &ScriptManager::reloadScript, [this](const QString& scriptName, bool userLoaded) { loadScript(scriptName, userLoaded, false, false, true); }); // register our application services and set it off on its own thread - runScriptInitializers(scriptEngine); + runScriptInitializers(scriptManager); // FIXME disabling 'shift key' debugging for now. If you start up the application with // the shift key held down, it triggers a deadlock because of script interfaces running // on the main thread - auto const wantDebug = scriptEngine->isDebuggable(); // || (qApp->queryKeyboardModifiers() & Qt::ShiftModifier); + auto const wantDebug = scriptManager->isDebuggable(); // || (qApp->queryKeyboardModifiers() & Qt::ShiftModifier); if (HIFI_SCRIPT_DEBUGGABLES && wantDebug) { - scriptEngine->runDebuggable(); + scriptManager->runDebuggable(); } else { - scriptEngine->runInThread(); + scriptManager->runInThread(); } } -void ScriptEngines::onScriptFinished(const QString& rawScriptURL, ScriptEnginePointer engine) { +void ScriptEngines::onScriptFinished(const QString& rawScriptURL, ScriptManagerPointer manager) { bool removed = false; { - QWriteLocker lock(&_scriptEnginesHashLock); + QWriteLocker lock(&_scriptManagersHashLock); const QUrl scriptURL = normalizeScriptURL(QUrl(rawScriptURL)); - for (auto it = _scriptEnginesHash.find(scriptURL); it != _scriptEnginesHash.end(); ++it) { - if (it.value() == engine) { - _scriptEnginesHash.erase(it); + for (auto it = _scriptManagersHash.find(scriptURL); it != _scriptManagersHash.end(); ++it) { + if (it.value() == manager) { + _scriptManagersHash.erase(it); removed = true; break; } } } - removeScriptEngine(engine); + removeScriptEngine(manager); if (removed && !_isReloading) { // Update settings with removed script diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h index 8c897f70fd6..eca37cd3580 100644 --- a/libraries/script-engine/src/ScriptEngines.h +++ b/libraries/script-engine/src/ScriptEngines.h @@ -16,18 +16,18 @@ #include #include #include +#include +#include #include #include #include +#include "ScriptManager.h" -#include "ScriptEngine.h" #include "ScriptsModel.h" #include "ScriptsModelFilter.h" #include "ScriptGatekeeper.h" -class ScriptEngine; - /**jsdoc * The ScriptDiscoveryService API provides facilities to work with Interface scripts. * @@ -50,7 +50,7 @@ class ScriptEngine; * Read-only. */ -class ScriptEngines : public QObject, public Dependency, public ScriptInitializerMixin { +class ScriptEngines : public QObject, public Dependency, public ScriptInitializerMixin { Q_OBJECT Q_PROPERTY(ScriptsModel* scriptsModel READ scriptsModel CONSTANT) @@ -58,8 +58,8 @@ class ScriptEngines : public QObject, public Dependency, public ScriptInitialize Q_PROPERTY(QString debugScriptUrl READ getDebugScriptUrl WRITE setDebugScriptUrl) public: - ScriptEngines(ScriptEngine::Context context, const QUrl& defaultScriptsOverride = QUrl()); - int runScriptInitializers(ScriptEnginePointer engine) override; + ScriptEngines(ScriptManager::Context context, const QUrl& defaultScriptsOverride = QUrl()); + int runScriptInitializers(ScriptManagerPointer manager) override; void loadScripts(); void saveScripts(); @@ -71,7 +71,7 @@ class ScriptEngines : public QObject, public Dependency, public ScriptInitialize void reloadLocalFiles(); QStringList getRunningScripts(); - ScriptEnginePointer getScriptEngine(const QUrl& scriptHash); + ScriptManagerPointer getScriptEngine(const QUrl& scriptHash); ScriptsModel* scriptsModel() { return &_scriptsModel; }; ScriptsModelFilter* scriptsModelFilter() { return &_scriptsModelFilter; }; @@ -107,7 +107,7 @@ class ScriptEngines : public QObject, public Dependency, public ScriptInitialize * false to not close Interface. * @returns {object} An empty object, {}. */ - Q_INVOKABLE ScriptEnginePointer loadScript(const QUrl& scriptFilename = QString(), + Q_INVOKABLE ScriptManagerPointer loadScript(const QUrl& scriptFilename = QString(), bool isUserLoaded = true, bool loadScriptFromEditor = false, bool activateMainWindow = false, bool reload = false, bool quitWhenFinished = false); /**jsdoc @@ -176,7 +176,7 @@ class ScriptEngines : public QObject, public Dependency, public ScriptInitialize void shutdownScripting(); bool isStopped() const { return _isStopped; } - void addScriptEngine(ScriptEnginePointer); + void addScriptEngine(ScriptManagerPointer); ScriptGatekeeper scriptGatekeeper; @@ -322,26 +322,24 @@ protected slots: /**jsdoc * @function ScriptDiscoveryService.onScriptFinished * @param {string} scriptName - Script name. - * @param {object} engine - Engine. + * @param {object} manager - Script manager. * @deprecated This function is deprecated and will be removed. */ // Deprecated because it wasn't intended to be in the API. - void onScriptFinished(const QString& fileNameString, ScriptEnginePointer engine); + void onScriptFinished(const QString& fileNameString, ScriptManagerPointer manager); protected: - friend class ScriptEngine; - - ScriptEnginePointer reloadScript(const QString& scriptName, bool isUserLoaded = true) { return loadScript(scriptName, isUserLoaded, false, false, true); } - void removeScriptEngine(ScriptEnginePointer); + ScriptManagerPointer reloadScript(const QString& scriptName, bool isUserLoaded = true) { return loadScript(scriptName, isUserLoaded, false, false, true); } + void removeScriptEngine(ScriptManagerPointer); void onScriptEngineLoaded(const QString& scriptFilename); void quitWhenFinished(); void onScriptEngineError(const QString& scriptFilename); - void launchScriptEngine(ScriptEnginePointer); + void launchScriptEngine(ScriptManagerPointer); - ScriptEngine::Context _context; - QReadWriteLock _scriptEnginesHashLock; - QHash _scriptEnginesHash; - QSet _allKnownScriptEngines; + ScriptManager::Context _context; + QReadWriteLock _scriptManagersHashLock; + QHash _scriptManagersHash; + QSet _allKnownScriptManagers; QMutex _allScriptsMutex; ScriptsModel _scriptsModel; ScriptsModelFilter _scriptsModelFilter; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptManager.cpp similarity index 73% rename from libraries/script-engine/src/ScriptEngine.cpp rename to libraries/script-engine/src/ScriptManager.cpp index 9fbf7a28015..8d50ba7995d 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptManager.cpp @@ -1,5 +1,5 @@ // -// ScriptEngine.cpp +// ScriptManager.cpp // libraries/script-engine/src // // Created by Brad Hefta-Gaub on 12/14/13. @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "ScriptEngine.h" +#include "ScriptManager.h" #include #include @@ -32,12 +32,6 @@ #include #include -#include -#include -#include - -#include - #include #include #include @@ -60,6 +54,7 @@ #include #include "ArrayBufferViewClass.h" +#include "AudioScriptingInterface.h" #include "AssetScriptingInterface.h" #include "BatchLoader.h" #include "BaseScriptEngine.h" @@ -70,6 +65,7 @@ #include "ScriptAudioInjector.h" #include "ScriptAvatarData.h" #include "ScriptCache.h" +#include "ScriptEngineCast.h" #include "ScriptEngineLogging.h" #include "TypedArrays.h" #include "XMLHttpRequestClass.h" @@ -89,42 +85,42 @@ #include #include -const QString ScriptEngine::_SETTINGS_ENABLE_EXTENDED_EXCEPTIONS { +const QString ScriptManager::_SETTINGS_ENABLE_EXTENDED_EXCEPTIONS { "com.highfidelity.experimental.enableExtendedJSExceptions" }; static const int MAX_MODULE_ID_LENGTH { 4096 }; static const int MAX_DEBUG_VALUE_LENGTH { 80 }; -static const QScriptEngine::QObjectWrapOptions DEFAULT_QOBJECT_WRAP_OPTIONS = - QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects; -static const QScriptValue::PropertyFlags READONLY_PROP_FLAGS { QScriptValue::ReadOnly | QScriptValue::Undeletable }; -static const QScriptValue::PropertyFlags READONLY_HIDDEN_PROP_FLAGS { READONLY_PROP_FLAGS | QScriptValue::SkipInEnumeration }; +static const ScriptEngine::QObjectWrapOptions DEFAULT_QOBJECT_WRAP_OPTIONS = + ScriptEngine::ExcludeDeleteLater | ScriptEngine::ExcludeChildObjects; +static const ScriptValue::PropertyFlags READONLY_PROP_FLAGS{ ScriptValue::ReadOnly | ScriptValue::Undeletable }; +static const ScriptValue::PropertyFlags READONLY_HIDDEN_PROP_FLAGS{ READONLY_PROP_FLAGS | ScriptValue::SkipInEnumeration }; static const bool HIFI_AUTOREFRESH_FILE_SCRIPTS { true }; -Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature) -int functionSignatureMetaID = qRegisterMetaType(); +Q_DECLARE_METATYPE(ScriptEngine::FunctionSignature) +int functionSignatureMetaID = qRegisterMetaType(); -int scriptEnginePointerMetaID = qRegisterMetaType(); +int scriptManagerPointerMetaID = qRegisterMetaType(); Q_DECLARE_METATYPE(ExternalResource::Bucket); -static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) { +static ScriptValuePointer debugPrint(ScriptContext* context, ScriptEngine* engine) { // assemble the message by concatenating our arguments QString message = ""; for (int i = 0; i < context->argumentCount(); i++) { if (i > 0) { message += " "; } - message += context->argument(i).toString(); + message += context->argument(i)->toString(); } // was this generated by a script engine? If we don't recognize it then send the message and exit - ScriptEngine* scriptEngine = qobject_cast(engine); - if (!scriptEngine) { + ScriptManager* scriptManager = engine->manager(); + if (!scriptManager) { qCDebug(scriptengine_script, "%s", qUtf8Printable(message)); - return QScriptValue(); + return ScriptValuePointer(); } // This message was sent by one of our script engines, let's try to see if we can find the source. @@ -155,32 +151,32 @@ static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) { } } if (location.isEmpty()) { - location = scriptEngine->getFilename(); + location = scriptManager->getFilename(); } // give the script engine a chance to notify the system about this message - scriptEngine->print(message); + scriptManager->print(message); // send the message to debug log qCDebug(scriptengine_script, "[%s] %s", qUtf8Printable(location), qUtf8Printable(message)); } else { - scriptEngine->print(message); + scriptManager->print(message); // prefix the script engine name to help disambiguate messages in the main debug log - qCDebug(scriptengine_script, "[%s] %s", qUtf8Printable(scriptEngine->getFilename()), qUtf8Printable(message)); + qCDebug(scriptengine_script, "[%s] %s", qUtf8Printable(getFilename()), qUtf8Printable(message)); } - return QScriptValue(); + return ScriptValuePointer(); } Q_DECLARE_METATYPE(controller::InputController*) //static int inputControllerPointerId = qRegisterMetaType(); -QScriptValue inputControllerToScriptValue(QScriptEngine *engine, controller::InputController* const &in) { - return engine->newQObject(in, QScriptEngine::QtOwnership, DEFAULT_QOBJECT_WRAP_OPTIONS); +ScriptValuePointer inputControllerToScriptValue(ScriptEngine* engine, controller::InputController* const& in) { + return engine->newQObject(in, ScriptEngine::QtOwnership, DEFAULT_QOBJECT_WRAP_OPTIONS); } -void inputControllerFromScriptValue(const QScriptValue &object, controller::InputController* &out) { - out = qobject_cast(object.toQObject()); +void inputControllerFromScriptValue(const ScriptValuePointer& object, controller::InputController*& out) { + out = qobject_cast(object->toQObject()); } // FIXME Come up with a way to properly encode entity IDs in filename @@ -203,31 +199,30 @@ QString encodeEntityIdIntoEntityUrl(const QString& url, const QString& entityID) return url + " [EntityID:" + entityID + "]"; } -QString ScriptEngine::logException(const QScriptValue& exception) { +QString ScriptManager::logException(const ScriptValuePointer& exception) { auto message = formatException(exception, _enableExtendedJSExceptions.get()); scriptErrorMessage(message); return message; } -ScriptEnginePointer scriptEngineFactory(ScriptEngine::Context context, +ScriptManagerPointer scriptManagerFactory(ScriptManager::Context context, const QString& scriptContents, const QString& fileNameString) { - ScriptEngine* engine = new ScriptEngine(context, scriptContents, fileNameString); - ScriptEnginePointer engineSP = ScriptEnginePointer(engine, &QObject::deleteLater); + ScriptManager* engine = new ScriptManager(context, scriptContents, fileNameString); + ScriptManagerPointer engineSP = ScriptManagerPointer(engine, &QObject::deleteLater); auto scriptEngines = DependencyManager::get(); - scriptEngines->addScriptEngine(qSharedPointerCast(engineSP)); + scriptEngines->addScriptEngine(qSharedPointerCast(engineSP)); engine->setScriptEngines(scriptEngines); return engineSP; } -int ScriptEngine::processLevelMaxRetries { ScriptRequest::MAX_RETRIES }; -ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const QString& fileNameString) : - BaseScriptEngine(), +int ScriptManager::processLevelMaxRetries { ScriptRequest::MAX_RETRIES }; +ScriptManager::ScriptManager(Context context, const QString& scriptContents, const QString& fileNameString) : + QObject(), _context(context), _scriptContents(scriptContents), _timerFunctionMap(), _fileNameString(fileNameString), - _arrayBufferClass(new ArrayBufferClass(this)), _assetScriptingInterface(new AssetScriptingInterface(this)) { switch (_context) { @@ -245,26 +240,14 @@ ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const break; } - connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) { - if (hasUncaughtException()) { - // the engine's uncaughtException() seems to produce much better stack traces here - emit unhandledException(cloneUncaughtException("signalHandlerException")); - clearExceptions(); - } else { - // ... but may not always be available -- so if needed we fallback to the passed exception - emit unhandledException(exception); - } - }, Qt::DirectConnection); - - setProcessEventsInterval(MSECS_PER_SECOND); if (isEntityServerScript()) { qCDebug(scriptengine) << "isEntityServerScript() -- limiting maxRetries to 1"; processLevelMaxRetries = 1; } // this is where all unhandled exceptions end up getting logged - connect(this, &BaseScriptEngine::unhandledException, this, [this](const QScriptValue& err) { - auto output = err.engine() == this ? err : makeError(err); + connect(this, &BaseScriptEngine::unhandledException, this, [this](const ScriptValuePointer& err) { + auto output = err->engine() == this ? err : makeError(err); if (!output.property("detail").isValid()) { output.setProperty("detail", "UnhandledException"); } @@ -272,7 +255,7 @@ ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const }); if (_type == Type::ENTITY_CLIENT || _type == Type::ENTITY_SERVER) { - QObject::connect(this, &ScriptEngine::update, this, [this]() { + QObject::connect(this, &ScriptManager::update, this, [this]() { // process pending entity script content if (!_contentAvailableQueue.empty() && !(_isFinished || _isStopping)) { EntityScriptContentAvailableMap pending; @@ -286,12 +269,12 @@ ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const } } -QString ScriptEngine::getTypeAsString() const { +QString ScriptManager::getTypeAsString() const { auto value = QVariant::fromValue(_type).toString(); return value.isEmpty() ? "unknown" : value.toLower(); } -QString ScriptEngine::getContext() const { +QString ScriptManager::getContext() const { switch (_context) { case CLIENT_SCRIPT: return "client"; @@ -307,7 +290,7 @@ QString ScriptEngine::getContext() const { return "unknown"; } -bool ScriptEngine::isDebugMode() const { +bool ScriptManager::isDebugMode() const { #if defined(DEBUG) return true; #else @@ -315,9 +298,9 @@ bool ScriptEngine::isDebugMode() const { #endif } -ScriptEngine::~ScriptEngine() {} +ScriptManager::~ScriptManager() {} -void ScriptEngine::disconnectNonEssentialSignals() { +void ScriptManager::disconnectNonEssentialSignals() { disconnect(); QThread* workerThread; // Ensure the thread should be running, and does exist @@ -327,7 +310,7 @@ void ScriptEngine::disconnectNonEssentialSignals() { } } -void ScriptEngine::runDebuggable() { +void ScriptManager::runDebuggable() { static QMenuBar* menuBar { nullptr }; static QMenu* scriptDebugMenu { nullptr }; static size_t scriptMenuCount { 0 }; @@ -360,11 +343,11 @@ void ScriptEngine::runDebuggable() { qWarning() << "Unable to add script debug menu"; } - QScriptValue result = evaluate(_scriptContents, _fileNameString); + ScriptValuePointer result = evaluate(_scriptContents, _fileNameString); _lastUpdate = usecTimestampNow(); QTimer* timer = new QTimer(this); - connect(this, &ScriptEngine::finished, [this, timer, parentMenu, scriptMenu] { + connect(this, &ScriptManager::finished, [this, timer, parentMenu, scriptMenu] { if (scriptMenu) { parentMenu->removeAction(scriptMenu->menuAction()); --scriptMenuCount; @@ -384,7 +367,7 @@ void ScriptEngine::runDebuggable() { stopAllTimers(); // make sure all our timers are stopped if the script is ending emit scriptEnding(); - emit finished(_fileNameString, qSharedPointerCast(sharedFromThis())); + emit finished(_fileNameString, qSharedPointerCast(sharedFromThis())); _isRunning = false; emit runningStateChanged(); @@ -417,8 +400,8 @@ void ScriptEngine::runDebuggable() { } -void ScriptEngine::runInThread() { - Q_ASSERT_X(!_isThreaded, "ScriptEngine::runInThread()", "runInThread should not be called more than once"); +void ScriptManager::runInThread() { + Q_ASSERT_X(!_isThreaded, "ScriptManager::runInThread()", "runInThread should not be called more than once"); if (_isThreaded) { return; @@ -446,7 +429,7 @@ void ScriptEngine::runInThread() { workerThread->start(); } -void ScriptEngine::executeOnScriptThread(std::function function, const Qt::ConnectionType& type ) { +void ScriptManager::executeOnScriptThread(std::function function, const Qt::ConnectionType& type ) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "executeOnScriptThread", type, Q_ARG(std::function, function)); return; @@ -455,14 +438,14 @@ void ScriptEngine::executeOnScriptThread(std::function function, const Q function(); } -void ScriptEngine::waitTillDoneRunning(bool shutdown) { +void ScriptManager::waitTillDoneRunning(bool shutdown) { // Engine should be stopped already, but be defensive stop(); auto workerThread = thread(); if (workerThread == QThread::currentThread()) { - qCWarning(scriptengine) << "ScriptEngine::waitTillDoneRunning called, but the script is on the same thread:" << getFilename(); + qCWarning(scriptengine) << "ScriptManager::waitTillDoneRunning called, but the script is on the same thread:" << getFilename(); return; } @@ -545,7 +528,7 @@ void ScriptEngine::waitTillDoneRunning(bool shutdown) { } } -QString ScriptEngine::getFilename() const { +QString ScriptManager::getFilename() const { QStringList fileNameParts = _fileNameString.split("/"); QString lastPart; if (!fileNameParts.isEmpty()) { @@ -554,13 +537,13 @@ QString ScriptEngine::getFilename() const { return lastPart; } -bool ScriptEngine::hasValidScriptSuffix(const QString& scriptFileName) { +bool ScriptManager::hasValidScriptSuffix(const QString& scriptFileName) { QFileInfo fileInfo(scriptFileName); QString scriptSuffixToLower = fileInfo.completeSuffix().toLower(); return scriptSuffixToLower.contains(QString("js"), Qt::CaseInsensitive); } -void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) { +void ScriptManager::loadURL(const QUrl& scriptURL, bool reload) { if (_isRunning) { return; } @@ -598,48 +581,48 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) { }, reload, maxRetries); } -void ScriptEngine::scriptErrorMessage(const QString& message) { +void ScriptManager::scriptErrorMessage(const QString& message) { qCCritical(scriptengine, "[%s] %s", qUtf8Printable(getFilename()), qUtf8Printable(message)); emit errorMessage(message, getFilename()); } -void ScriptEngine::scriptWarningMessage(const QString& message) { +void ScriptManager::scriptWarningMessage(const QString& message) { qCWarning(scriptengine, "[%s] %s", qUtf8Printable(getFilename()), qUtf8Printable(message)); emit warningMessage(message, getFilename()); } -void ScriptEngine::scriptInfoMessage(const QString& message) { +void ScriptManager::scriptInfoMessage(const QString& message) { qCInfo(scriptengine, "[%s] %s", qUtf8Printable(getFilename()), qUtf8Printable(message)); emit infoMessage(message, getFilename()); } -void ScriptEngine::scriptPrintedMessage(const QString& message) { +void ScriptManager::scriptPrintedMessage(const QString& message) { qCDebug(scriptengine, "[%s] %s", qUtf8Printable(getFilename()), qUtf8Printable(message)); emit printedMessage(message, getFilename()); } -void ScriptEngine::clearDebugLogWindow() { +void ScriptManager::clearDebugLogWindow() { emit clearDebugWindow(); } // Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of // callAnimationStateHandler requires that the type be registered. // These two are meaningful, if we ever do want to use them... -static QScriptValue animVarMapToScriptValue(QScriptEngine* engine, const AnimVariantMap& parameters) { +static ScriptValuePointer animVarMapToScriptValue(ScriptEngine* engine, const AnimVariantMap& parameters) { QStringList unused; return parameters.animVariantMapToScriptValue(engine, unused, false); } -static void animVarMapFromScriptValue(const QScriptValue& value, AnimVariantMap& parameters) { +static void animVarMapFromScriptValue(const ScriptValuePointer& value, AnimVariantMap& parameters) { parameters.animVariantMapFromScriptValue(value); } // ... while these two are not. But none of the four are ever used. -static QScriptValue resultHandlerToScriptValue(QScriptEngine* engine, +static ScriptValuePointer resultHandlerToScriptValue(ScriptEngine* engine, const AnimVariantResultHandler& resultHandler) { qCCritical(scriptengine) << "Attempt to marshall result handler to javascript"; assert(false); - return QScriptValue(); + return ScriptValuePointer(); } -static void resultHandlerFromScriptValue(const QScriptValue& value, AnimVariantResultHandler& resultHandler) { +static void resultHandlerFromScriptValue(const ScriptValuePointer& value, AnimVariantResultHandler& resultHandler) { qCCritical(scriptengine) << "Attempt to marshall result handler from javascript"; assert(false); } @@ -647,10 +630,10 @@ static void resultHandlerFromScriptValue(const QScriptValue& value, AnimVariantR // Templated qScriptRegisterMetaType fails to compile with raw pointers using ScriptableResourceRawPtr = ScriptableResource*; -static QScriptValue scriptableResourceToScriptValue(QScriptEngine* engine, +static ScriptValuePointer scriptableResourceToScriptValue(ScriptEngine* engine, const ScriptableResourceRawPtr& resource) { if (!resource) { - return QScriptValue(); // probably shutting down + return ScriptValuePointer(); // probably shutting down } // The first script to encounter this resource will track its memory. @@ -666,13 +649,13 @@ static QScriptValue scriptableResourceToScriptValue(QScriptEngine* engine, auto object = engine->newQObject( const_cast(resource), - QScriptEngine::ScriptOwnership, + ScriptEngine::ScriptOwnership, DEFAULT_QOBJECT_WRAP_OPTIONS); return object; } -static void scriptableResourceFromScriptValue(const QScriptValue& value, ScriptableResourceRawPtr& resource) { - resource = static_cast(value.toQObject()); +static void scriptableResourceFromScriptValue(const ScriptValuePointer& value, ScriptableResourceRawPtr& resource) { + resource = static_cast(value->toQObject()); } /**jsdoc @@ -688,7 +671,7 @@ static void scriptableResourceFromScriptValue(const QScriptValue& value, Scripta * * @property {Resource.State} State - The possible loading states of a resource. Read-only. */ -static QScriptValue createScriptableResourcePrototype(ScriptEnginePointer engine) { +static ScriptValuePointer createScriptableResourcePrototype(ScriptManagerPointer manager) { auto prototype = engine->newObject(); // Expose enum State to JS/QML via properties @@ -699,32 +682,32 @@ static QScriptValue createScriptableResourcePrototype(ScriptEnginePointer engine state->setProperty(metaEnum.key(i), metaEnum.value(i)); } - auto prototypeState = engine->newQObject(state, QScriptEngine::QtOwnership, - QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeSlots | QScriptEngine::ExcludeSuperClassMethods); + auto prototypeState = engine->newQObject(state, ScriptEngine::QtOwnership, + ScriptEngine::ExcludeDeleteLater | ScriptEngine::ExcludeSlots | ScriptEngine::ExcludeSuperClassMethods); prototype.setProperty("State", prototypeState); return prototype; } -QScriptValue avatarDataToScriptValue(QScriptEngine* engine, ScriptAvatarData* const& in) { - return engine->newQObject(in, QScriptEngine::ScriptOwnership, DEFAULT_QOBJECT_WRAP_OPTIONS); +ScriptValuePointer avatarDataToScriptValue(ScriptEngine* engine, ScriptAvatarData* const& in) { + return engine->newQObject(in, ScriptEngine::ScriptOwnership, DEFAULT_QOBJECT_WRAP_OPTIONS); } -void avatarDataFromScriptValue(const QScriptValue& object, ScriptAvatarData*& out) { +void avatarDataFromScriptValue(const ScriptValuePointer& object, ScriptAvatarData*& out) { // This is not implemented because there are no slots/properties that take an AvatarSharedPointer from a script assert(false); out = nullptr; } -QScriptValue externalResourceBucketToScriptValue(QScriptEngine* engine, ExternalResource::Bucket const& in) { - return QScriptValue((int)in); +ScriptValuePointer externalResourceBucketToScriptValue(ScriptEngine* engine, ExternalResource::Bucket const& in) { + return engine->newValue((int)in); } -void externalResourceBucketFromScriptValue(const QScriptValue& object, ExternalResource::Bucket& out) { - out = static_cast(object.toInt32()); +void externalResourceBucketFromScriptValue(const ScriptValuePointer& object, ExternalResource::Bucket& out) { + out = static_cast(object->toInt32()); } -void ScriptEngine::resetModuleCache(bool deleteScriptCache) { +void ScriptManager::resetModuleCache(bool deleteScriptCache) { if (QThread::currentThread() != thread()) { executeOnScriptThread([=]() { resetModuleCache(deleteScriptCache); }); return; @@ -737,7 +720,7 @@ void ScriptEngine::resetModuleCache(bool deleteScriptCache) { QScriptValueIterator it(cache); while (it.hasNext()) { it.next(); - if (it.flags() & QScriptValue::SkipInEnumeration) { + if (it.flags() & ScriptValuePointer::SkipInEnumeration) { continue; } qCDebug(scriptengine) << "resetModuleCache(true) -- staging " << it.name() << " for cache reset at next require"; @@ -751,51 +734,52 @@ void ScriptEngine::resetModuleCache(bool deleteScriptCache) { cacheMeta.setProperty("type", "cacheMeta"); jsRequire.setData(cacheMeta); } - cache.setProperty("__created__", (double)QDateTime::currentMSecsSinceEpoch(), QScriptValue::SkipInEnumeration); + cache.setProperty("__created__", (double)QDateTime::currentMSecsSinceEpoch(), ScriptValuePointer::SkipInEnumeration); #if DEBUG_JS_MODULES cache.setProperty("__meta__", cacheMeta, READONLY_HIDDEN_PROP_FLAGS); #endif jsRequire.setProperty("cache", cache, READONLY_PROP_FLAGS); } -void ScriptEngine::init() { +void ScriptManager::init() { if (_isInitialized) { return; // only initialize once } _isInitialized = true; + auto scriptEngine = engine().data(); auto entityScriptingInterface = DependencyManager::get(); entityScriptingInterface->init(); // register various meta-types - registerMetaTypes(this); - registerMIDIMetaTypes(this); - registerEventTypes(this); - registerMenuItemProperties(this); - registerAnimationTypes(this); - registerAvatarTypes(this); - registerAudioMetaTypes(this); - - qScriptRegisterMetaType(this, EntityPropertyFlagsToScriptValue, EntityPropertyFlagsFromScriptValue); - qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValueHonorReadOnly); - qScriptRegisterMetaType(this, EntityPropertyInfoToScriptValue, EntityPropertyInfoFromScriptValue); - qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue); - qScriptRegisterMetaType(this, RayToEntityIntersectionResultToScriptValue, RayToEntityIntersectionResultFromScriptValue); - qScriptRegisterMetaType(this, RayToAvatarIntersectionResultToScriptValue, RayToAvatarIntersectionResultFromScriptValue); - qScriptRegisterMetaType(this, AvatarEntityMapToScriptValue, AvatarEntityMapFromScriptValue); - qScriptRegisterSequenceMetaType>(this); - qScriptRegisterSequenceMetaType>(this); - - qScriptRegisterSequenceMetaType>(this); - qScriptRegisterSequenceMetaType>(this); - qScriptRegisterSequenceMetaType>(this); - - QScriptValue xmlHttpRequestConstructorValue = newFunction(XMLHttpRequestClass::constructor); - globalObject().setProperty("XMLHttpRequest", xmlHttpRequestConstructorValue); - - QScriptValue webSocketConstructorValue = newFunction(WebSocketClass::constructor); - globalObject().setProperty("WebSocket", webSocketConstructorValue); + registerMetaTypes(scriptEngine); + registerMIDIMetaTypes(scriptEngine); + registerEventTypes(scriptEngine); + registerMenuItemProperties(scriptEngine); + registerAnimationTypes(scriptEngine); + registerAvatarTypes(scriptEngine); + registerAudioMetaTypes(scriptEngine); + + scriptRegisterMetaType(scriptEngine, EntityPropertyFlagsToScriptValue, EntityPropertyFlagsFromScriptValue); + scriptRegisterMetaType(scriptEngine, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValueHonorReadOnly); + scriptRegisterMetaType(scriptEngine, EntityPropertyInfoToScriptValue, EntityPropertyInfoFromScriptValue); + scriptRegisterMetaType(scriptEngine, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue); + scriptRegisterMetaType(scriptEngine, RayToEntityIntersectionResultToScriptValue, RayToEntityIntersectionResultFromScriptValue); + scriptRegisterMetaType(scriptEngine, RayToAvatarIntersectionResultToScriptValue, RayToAvatarIntersectionResultFromScriptValue); + scriptRegisterMetaType(scriptEngine, AvatarEntityMapToScriptValue, AvatarEntityMapFromScriptValue); + scriptRegisterSequenceMetaType>(scriptEngine); + scriptRegisterSequenceMetaType>(scriptEngine); + + scriptRegisterSequenceMetaType>(scriptEngine); + scriptRegisterSequenceMetaType>(scriptEngine); + scriptRegisterSequenceMetaType>(scriptEngine); + + ScriptValuePointer xmlHttpRequestConstructorValue = scriptEngine->newFunction(XMLHttpRequestClass::constructor); + scriptEngine->globalObject()->setProperty("XMLHttpRequest", xmlHttpRequestConstructorValue); + + ScriptValuePointer webSocketConstructorValue = scriptEngine->newFunction(WebSocketClass::constructor); + scriptEngine->globalObject()->setProperty("WebSocket", webSocketConstructorValue); /**jsdoc * Prints a message to the program log and emits {@link Script.printedMessage}. @@ -804,249 +788,109 @@ void ScriptEngine::init() { * @function print * @param {...*} [message] - The message values to print. */ - globalObject().setProperty("print", newFunction(debugPrint)); + scriptEngine->globalObject()->setProperty("print", scriptEngine->newFunction(debugPrint)); - QScriptValue audioEffectOptionsConstructorValue = newFunction(AudioEffectOptions::constructor); - globalObject().setProperty("AudioEffectOptions", audioEffectOptionsConstructorValue); + ScriptValuePointer audioEffectOptionsConstructorValue = scriptEngine->newFunction(AudioEffectOptions::constructor); + scriptEngine->globalObject()->setProperty("AudioEffectOptions", audioEffectOptionsConstructorValue); - qScriptRegisterMetaType(this, injectorToScriptValue, injectorFromScriptValue); - qScriptRegisterMetaType(this, inputControllerToScriptValue, inputControllerFromScriptValue); - qScriptRegisterMetaType(this, avatarDataToScriptValue, avatarDataFromScriptValue); - qScriptRegisterMetaType(this, animationDetailsToScriptValue, animationDetailsFromScriptValue); - qScriptRegisterMetaType(this, webSocketToScriptValue, webSocketFromScriptValue); - qScriptRegisterMetaType(this, qWSCloseCodeToScriptValue, qWSCloseCodeFromScriptValue); - qScriptRegisterMetaType(this, wscReadyStateToScriptValue, wscReadyStateFromScriptValue); + scriptRegisterMetaType(scriptEngine, injectorToScriptValue, injectorFromScriptValue); + scriptRegisterMetaType(scriptEngine, inputControllerToScriptValue, inputControllerFromScriptValue); + scriptRegisterMetaType(scriptEngine, avatarDataToScriptValue, avatarDataFromScriptValue); + scriptRegisterMetaType(scriptEngine, animationDetailsToScriptValue, animationDetailsFromScriptValue); + scriptRegisterMetaType(scriptEngine, webSocketToScriptValue, webSocketFromScriptValue); + scriptRegisterMetaType(scriptEngine, qWSCloseCodeToScriptValue, qWSCloseCodeFromScriptValue); + scriptRegisterMetaType(scriptEngine, wscReadyStateToScriptValue, wscReadyStateFromScriptValue); - // NOTE: You do not want to end up creating new instances of singletons here. They will be on the ScriptEngine thread - // and are likely to be unusable if we "reset" the ScriptEngine by creating a new one (on a whole new thread). + // NOTE: You do not want to end up creating new instances of singletons here. They will be on the ScriptManager thread + // and are likely to be unusable if we "reset" the ScriptManager by creating a new one (on a whole new thread). - registerGlobalObject("Script", this); + scriptEngine->registerGlobalObject("Script", this); { // set up Script.require.resolve and Script.require.cache - auto Script = globalObject().property("Script"); - auto require = Script.property("require"); - auto resolve = Script.property("_requireResolve"); - require.setProperty("resolve", resolve, READONLY_PROP_FLAGS); + auto Script = scriptEngine->globalObject()->property("Script"); + auto require = Script->property("require"); + auto resolve = Script->property("_requireResolve"); + require->setProperty("resolve", resolve, READONLY_PROP_FLAGS); resetModuleCache(); } - qScriptRegisterMetaType(this, externalResourceBucketToScriptValue, externalResourceBucketFromScriptValue); - registerEnum("Script.ExternalPaths", QMetaEnum::fromType()); - - registerGlobalObject("Audio", DependencyManager::get().data()); - - registerGlobalObject("Midi", DependencyManager::get().data()); - - registerGlobalObject("Entities", entityScriptingInterface.data()); - registerFunction("Entities", "getMultipleEntityProperties", EntityScriptingInterface::getMultipleEntityProperties); - registerGlobalObject("Quat", &_quatLibrary); - registerGlobalObject("Vec3", &_vec3Library); - registerGlobalObject("Mat4", &_mat4Library); - registerGlobalObject("Uuid", &_uuidLibrary); - registerGlobalObject("Messages", DependencyManager::get().data()); - registerGlobalObject("File", new FileScriptingInterface(this)); - registerGlobalObject("console", &_consoleScriptingInterface); - registerFunction("console", "info", ConsoleScriptingInterface::info, currentContext()->argumentCount()); - registerFunction("console", "log", ConsoleScriptingInterface::log, currentContext()->argumentCount()); - registerFunction("console", "debug", ConsoleScriptingInterface::debug, currentContext()->argumentCount()); - registerFunction("console", "warn", ConsoleScriptingInterface::warn, currentContext()->argumentCount()); - registerFunction("console", "error", ConsoleScriptingInterface::error, currentContext()->argumentCount()); - registerFunction("console", "exception", ConsoleScriptingInterface::exception, currentContext()->argumentCount()); - registerFunction("console", "assert", ConsoleScriptingInterface::assertion, currentContext()->argumentCount()); - registerFunction("console", "group", ConsoleScriptingInterface::group, 1); - registerFunction("console", "groupCollapsed", ConsoleScriptingInterface::groupCollapsed, 1); - registerFunction("console", "groupEnd", ConsoleScriptingInterface::groupEnd, 0); - - qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue); - qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue); + scriptRegisterMetaType(scriptEngine, externalResourceBucketToScriptValue, externalResourceBucketFromScriptValue); + scriptEngine->registerEnum("Script.ExternalPaths", QMetaEnum::fromType()); + + scriptEngine->registerGlobalObject("Audio", DependencyManager::get().data()); + + scriptEngine->registerGlobalObject("Midi", DependencyManager::get().data()); + + scriptEngine->registerGlobalObject("Entities", entityScriptingInterface.data()); + scriptEngine->registerFunction("Entities", "getMultipleEntityProperties", + EntityScriptingInterface::getMultipleEntityProperties); + scriptEngine->registerGlobalObject("Quat", &_quatLibrary); + scriptEngine->registerGlobalObject("Vec3", &_vec3Library); + scriptEngine->registerGlobalObject("Mat4", &_mat4Library); + scriptEngine->registerGlobalObject("Uuid", &_uuidLibrary); + scriptEngine->registerGlobalObject("Messages", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("File", new FileScriptingInterface(this)); + scriptEngine->registerGlobalObject("console", &_consoleScriptingInterface); + scriptEngine->registerFunction("console", "info", ConsoleScriptingInterface::info, scriptEngine->currentContext()->argumentCount()); + scriptEngine->registerFunction("console", "log", ConsoleScriptingInterface::log, scriptEngine->currentContext()->argumentCount()); + scriptEngine->registerFunction("console", "debug", ConsoleScriptingInterface::debug, scriptEngine->currentContext()->argumentCount()); + scriptEngine->registerFunction("console", "warn", ConsoleScriptingInterface::warn, scriptEngine->currentContext()->argumentCount()); + scriptEngine->registerFunction("console", "error", ConsoleScriptingInterface::error, scriptEngine->currentContext()->argumentCount()); + scriptEngine->registerFunction("console", "exception", ConsoleScriptingInterface::exception, scriptEngine->currentContext()->argumentCount()); + scriptEngine->registerFunction("console", "assert", ConsoleScriptingInterface::assertion, scriptEngine->currentContext()->argumentCount()); + scriptEngine->registerFunction("console", "group", ConsoleScriptingInterface::group, 1); + scriptEngine->registerFunction("console", "groupCollapsed", ConsoleScriptingInterface::groupCollapsed, 1); + scriptEngine->registerFunction("console", "groupEnd", ConsoleScriptingInterface::groupEnd, 0); + + scriptRegisterMetaType(scriptEngine, animVarMapToScriptValue, animVarMapFromScriptValue); + scriptRegisterMetaType(scriptEngine, resultHandlerToScriptValue, resultHandlerFromScriptValue); // Scriptable cache access - auto resourcePrototype = createScriptableResourcePrototype(qSharedPointerCast(sharedFromThis())); - globalObject().setProperty("Resource", resourcePrototype); - setDefaultPrototype(qMetaTypeId(), resourcePrototype); - qScriptRegisterMetaType(this, scriptableResourceToScriptValue, scriptableResourceFromScriptValue); + auto resourcePrototype = createScriptableResourcePrototype(qSharedPointerCast(sharedFromThis())); + scriptEngine->globalObject()->setProperty("Resource", resourcePrototype); + scriptEngine->setDefaultPrototype(qMetaTypeId(), resourcePrototype); + scriptRegisterMetaType(scriptEngine, scriptableResourceToScriptValue, scriptableResourceFromScriptValue); // constants - globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE))); + scriptEngine->globalObject()->setProperty("TREE_SCALE", scriptEngine->newValue(TREE_SCALE)); - registerGlobalObject("Assets", _assetScriptingInterface); - registerGlobalObject("Resources", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("Assets", _assetScriptingInterface); + scriptEngine->registerGlobalObject("Resources", DependencyManager::get().data()); - registerGlobalObject("DebugDraw", &DebugDraw::getInstance()); + scriptEngine->registerGlobalObject("DebugDraw", &DebugDraw::getInstance()); - registerGlobalObject("Model", new ModelScriptingInterface(this)); - qScriptRegisterMetaType(this, meshToScriptValue, meshFromScriptValue); - qScriptRegisterMetaType(this, meshesToScriptValue, meshesFromScriptValue); + scriptEngine->registerGlobalObject("Model", new ModelScriptingInterface(this)); + scriptRegisterMetaType(scriptEngine, meshToScriptValue, meshFromScriptValue); + scriptRegisterMetaType(scriptEngine, meshesToScriptValue, meshesFromScriptValue); - registerGlobalObject("UserActivityLogger", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("UserActivityLogger", DependencyManager::get().data()); #if DEV_BUILD || PR_BUILD - registerGlobalObject("StackTest", new StackTestScriptingInterface(this)); + scriptEngine->registerGlobalObject("StackTest", new StackTestScriptingInterface(this)); #endif - globalObject().setProperty("KALILA", "isWaifu"); - globalObject().setProperty("Kute", newFunction([](QScriptContext* context, QScriptEngine* engine) -> QScriptValue { - return context->argument(0).toString().toLower() == "kalila" ? true : false; + scriptEngine->globalObject()->setProperty("KALILA", "isWaifu"); + scriptEngine->globalObject()->setProperty("Kute", scriptEngine->newFunction([](ScriptContext* context, ScriptEngine* engine) -> ScriptValuePointer { + return context->engine()->newValue(context->argument(0)->toString().toLower() == "kalila" ? true : false); })); } -void ScriptEngine::registerEnum(const QString& enumName, QMetaEnum newEnum) { - if (!newEnum.isValid()) { - qCCritical(scriptengine) << "registerEnum called on invalid enum with name " << enumName; - return; - } - - for (int i = 0; i < newEnum.keyCount(); i++) { - const char* keyName = newEnum.key(i); - QString fullName = enumName + "." + keyName; - registerValue(fullName, newEnum.keyToValue(keyName)); - } -} - -void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { - if (QThread::currentThread() != thread()) { -#ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::registerValue() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; -#endif - QMetaObject::invokeMethod(this, "registerValue", - Q_ARG(const QString&, valueName), - Q_ARG(QScriptValue, value)); - return; - } - - QStringList pathToValue = valueName.split("."); - int partsToGo = pathToValue.length(); - QScriptValue partObject = globalObject(); - - for (const auto& pathPart : pathToValue) { - partsToGo--; - if (!partObject.property(pathPart).isValid()) { - if (partsToGo > 0) { - //QObject *object = new QObject; - QScriptValue partValue = newArray(); //newQObject(object, QScriptEngine::ScriptOwnership); - partObject.setProperty(pathPart, partValue); - } else { - partObject.setProperty(pathPart, value); - } - } - partObject = partObject.property(pathPart); - } -} - -void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { - if (QThread::currentThread() != thread()) { -#ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::registerGlobalObject() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; -#endif - QMetaObject::invokeMethod(this, "registerGlobalObject", - Q_ARG(const QString&, name), - Q_ARG(QObject*, object)); - return; - } -#ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "ScriptEngine::registerGlobalObject() called on thread [" << QThread::currentThread() << "] name:" << name; -#endif - - if (!globalObject().property(name).isValid()) { - if (object) { - QScriptValue value = newQObject(object, QScriptEngine::QtOwnership, DEFAULT_QOBJECT_WRAP_OPTIONS); - globalObject().setProperty(name, value); - } else { - globalObject().setProperty(name, QScriptValue()); - } - } -} - -void ScriptEngine::registerFunction(const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) { - if (QThread::currentThread() != thread()) { -#ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; -#endif - QMetaObject::invokeMethod(this, "registerFunction", - Q_ARG(const QString&, name), - Q_ARG(QScriptEngine::FunctionSignature, functionSignature), - Q_ARG(int, numArguments)); - return; - } -#ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "ScriptEngine::registerFunction() called on thread [" << QThread::currentThread() << "] name:" << name; -#endif - - QScriptValue scriptFun = newFunction(functionSignature, numArguments); - globalObject().setProperty(name, scriptFun); -} - -void ScriptEngine::registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) { - if (QThread::currentThread() != thread()) { -#ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] parent:" << parent << "name:" << name; -#endif - QMetaObject::invokeMethod(this, "registerFunction", - Q_ARG(const QString&, name), - Q_ARG(QScriptEngine::FunctionSignature, functionSignature), - Q_ARG(int, numArguments)); - return; - } -#ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "ScriptEngine::registerFunction() called on thread [" << QThread::currentThread() << "] parent:" << parent << "name:" << name; -#endif - - QScriptValue object = globalObject().property(parent); - if (object.isValid()) { - QScriptValue scriptFun = newFunction(functionSignature, numArguments); - object.setProperty(name, scriptFun); - } -} - -void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter, - QScriptEngine::FunctionSignature setter, const QString& parent) { - if (QThread::currentThread() != thread()) { -#ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::registerGetterSetter() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " - " name:" << name << "parent:" << parent; -#endif - QMetaObject::invokeMethod(this, "registerGetterSetter", - Q_ARG(const QString&, name), - Q_ARG(QScriptEngine::FunctionSignature, getter), - Q_ARG(QScriptEngine::FunctionSignature, setter), - Q_ARG(const QString&, parent)); - return; - } -#ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "ScriptEngine::registerGetterSetter() called on thread [" << QThread::currentThread() << "] name:" << name << "parent:" << parent; -#endif - - QScriptValue setterFunction = newFunction(setter, 1); - QScriptValue getterFunction = newFunction(getter); - - if (!parent.isNull() && !parent.isEmpty()) { - QScriptValue object = globalObject().property(parent); - if (object.isValid()) { - object.setProperty(name, setterFunction, QScriptValue::PropertySetter); - object.setProperty(name, getterFunction, QScriptValue::PropertyGetter); - } - } else { - globalObject().setProperty(name, setterFunction, QScriptValue::PropertySetter); - globalObject().setProperty(name, getterFunction, QScriptValue::PropertyGetter); - } -} - // Unregister the handlers for this eventName and entityID. -void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler) { +void ScriptManager::removeEventHandler(const EntityItemID& entityID, const QString& eventName, ScriptValuePointer handler) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::removeEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "*** WARNING *** ScriptManager::removeEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " "entityID:" << entityID << " eventName:" << eventName; #endif QMetaObject::invokeMethod(this, "removeEventHandler", Q_ARG(const EntityItemID&, entityID), Q_ARG(const QString&, eventName), - Q_ARG(QScriptValue, handler)); + Q_ARG(ScriptValuePointer, handler)); return; } #ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "ScriptEngine::removeEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName; + qCDebug(scriptengine) << "ScriptManager::removeEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName; #endif if (!_registeredHandlers.contains(entityID)) { @@ -1054,7 +898,7 @@ void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QStrin } RegisteredEventHandlers& handlersOnEntity = _registeredHandlers[entityID]; CallbackList& handlersForEvent = handlersOnEntity[eventName]; - // QScriptValue does not have operator==(), so we can't use QList::removeOne and friends. So iterate. + // ScriptValuePointer does not have operator==(), so we can't use QList::removeOne and friends. So iterate. for (int i = 0; i < handlersForEvent.count(); ++i) { if (handlersForEvent[i].function.equals(handler)) { handlersForEvent.removeAt(i); @@ -1063,21 +907,21 @@ void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QStrin } } // Register the handler. -void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler) { +void ScriptManager::addEventHandler(const EntityItemID& entityID, const QString& eventName, ScriptValuePointer handler) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::addEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "*** WARNING *** ScriptManager::addEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " "entityID:" << entityID << " eventName:" << eventName; #endif QMetaObject::invokeMethod(this, "addEventHandler", Q_ARG(const EntityItemID&, entityID), Q_ARG(const QString&, eventName), - Q_ARG(QScriptValue, handler)); + Q_ARG(ScriptValuePointer, handler)); return; } #ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "ScriptEngine::addEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName; + qCDebug(scriptengine) << "ScriptManager::addEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName; #endif if (_registeredHandlers.count() == 0) { // First time any per-entity handler has been added in this script... @@ -1200,59 +1044,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& handlersForEvent << handlerData; // Note that the same handler can be added many times. See removeEntityEventHandler(). } -// this is not redundant -- the version in BaseScriptEngine is specifically not Q_INVOKABLE -QScriptValue ScriptEngine::evaluateInClosure(const QScriptValue& closure, const QScriptProgram& program) { - return BaseScriptEngine::evaluateInClosure(closure, program); -} - -QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fileName, int lineNumber) { - QSharedPointer scriptEngines(_scriptEngines); - if (!scriptEngines || scriptEngines->isStopped()) { - return QScriptValue(); // bail early - } - - if (QThread::currentThread() != thread()) { - QScriptValue result; -#ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " - "sourceCode:" << sourceCode << " fileName:" << fileName << "lineNumber:" << lineNumber; -#endif - BLOCKING_INVOKE_METHOD(this, "evaluate", - Q_RETURN_ARG(QScriptValue, result), - Q_ARG(const QString&, sourceCode), - Q_ARG(const QString&, fileName), - Q_ARG(int, lineNumber)); - return result; - } - - // Check syntax - auto syntaxError = lintScript(sourceCode, fileName); - if (syntaxError.isError()) { - if (!isEvaluating()) { - syntaxError.setProperty("detail", "evaluate"); - } - raiseException(syntaxError); - maybeEmitUncaughtException("lint"); - return syntaxError; - } - QScriptProgram program { sourceCode, fileName, lineNumber }; - if (program.isNull()) { - // can this happen? - auto err = makeError("could not create QScriptProgram for " + fileName); - raiseException(err); - maybeEmitUncaughtException("compile"); - return err; - } - - QScriptValue result; - { - result = BaseScriptEngine::evaluate(program); - maybeEmitUncaughtException("evaluate"); - } - return result; -} - -void ScriptEngine::run() { +void ScriptManager::run() { if (QThread::currentThread() != qApp->thread() && _context == Context::CLIENT_SCRIPT) { // Flag that we're allowed to access local HTML files on UI created from C++ calls on this thread // (because we're a client script) @@ -1300,7 +1092,7 @@ void ScriptEngine::run() { std::chrono::microseconds totalUpdates(0); - // TODO: Integrate this with signals/slots instead of reimplementing throttling for ScriptEngine + // TODO: Integrate this with signals/slots instead of reimplementing throttling for ScriptManager while (!_isFinished) { auto beforeSleep = clock::now(); @@ -1434,7 +1226,7 @@ void ScriptEngine::run() { } } - emit finished(_fileNameString, qSharedPointerCast(sharedFromThis())); + emit finished(_fileNameString, qSharedPointerCast(sharedFromThis())); // Don't leave our local-file-access flag laying around, reset it to false when the scriptengine // thread is finished @@ -1446,7 +1238,7 @@ void ScriptEngine::run() { // NOTE: This is private because it must be called on the same thread that created the timers, which is why // we want to only call it in our own run "shutdown" processing. -void ScriptEngine::stopAllTimers() { +void ScriptManager::stopAllTimers() { QMutableHashIterator i(_timerFunctionMap); int j {0}; while (i.hasNext()) { @@ -1457,7 +1249,7 @@ void ScriptEngine::stopAllTimers() { } } -void ScriptEngine::stopAllTimersForEntityScript(const EntityItemID& entityID) { +void ScriptManager::stopAllTimersForEntityScript(const EntityItemID& entityID) { // We could maintain a separate map of entityID => QTimer, but someone will have to prove to me that it's worth the complexity. -HRS QVector toDelete; QMutableHashIterator i(_timerFunctionMap); @@ -1475,7 +1267,7 @@ void ScriptEngine::stopAllTimersForEntityScript(const EntityItemID& entityID) { } -void ScriptEngine::stop(bool marshal) { +void ScriptManager::stop(bool marshal) { _isStopping = true; // this can be done on any thread if (marshal) { @@ -1489,34 +1281,34 @@ void ScriptEngine::stop(bool marshal) { } // Other threads can invoke this through invokeMethod, which causes the callback to be asynchronously executed in this script's thread. -void ScriptEngine::callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler) { +void ScriptManager::callAnimationStateHandler(ScriptValuePointer callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::callAnimationStateHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; + qCDebug(scriptengine) << "*** WARNING *** ScriptManager::callAnimationStateHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; #endif QMetaObject::invokeMethod(this, "callAnimationStateHandler", - Q_ARG(QScriptValue, callback), + Q_ARG(ScriptValuePointer, callback), Q_ARG(AnimVariantMap, parameters), Q_ARG(QStringList, names), Q_ARG(bool, useNames), Q_ARG(AnimVariantResultHandler, resultHandler)); return; } - QScriptValue javascriptParameters = parameters.animVariantMapToScriptValue(this, names, useNames); - QScriptValueList callingArguments; + ScriptValuePointer javascriptParameters = parameters.animVariantMapToScriptValue(this, names, useNames); + ScriptValueList callingArguments; callingArguments << javascriptParameters; assert(currentEntityIdentifier.isInvalidID()); // No animation state handlers from entity scripts. - QScriptValue result = callback.call(QScriptValue(), callingArguments); + ScriptValuePointer result = callback->call(ScriptValuePointer(), callingArguments); // validate result from callback function. - if (result.isValid() && result.isObject()) { + if (result->isValid() && result->isObject()) { resultHandler(result); } else { - qCWarning(scriptengine) << "ScriptEngine::callAnimationStateHandler invalid return argument from callback, expected an object"; + qCWarning(scriptengine) << "ScriptManager::callAnimationStateHandler invalid return argument from callback, expected an object"; } } -void ScriptEngine::updateMemoryCost(const qint64& deltaSize) { +void ScriptManager::updateMemoryCost(const qint64& deltaSize) { if (deltaSize > 0) { // We've patched qt to fix https://highfidelity.atlassian.net/browse/BUGZ-46 on mac and windows only. #if defined(Q_OS_WIN) || defined(Q_OS_MAC) @@ -1525,7 +1317,7 @@ void ScriptEngine::updateMemoryCost(const qint64& deltaSize) { } } -void ScriptEngine::timerFired() { +void ScriptManager::timerFired() { { QSharedPointer scriptEngines(_scriptEngines); if (!scriptEngines || scriptEngines->isStopped()) { @@ -1544,19 +1336,19 @@ void ScriptEngine::timerFired() { } // call the associated JS function, if it exists - if (timerData.function.isValid()) { + if (timerData.function->isValid()) { PROFILE_RANGE(script, __FUNCTION__); auto preTimer = p_high_resolution_clock::now(); - callWithEnvironment(timerData.definingEntityIdentifier, timerData.definingSandboxURL, timerData.function, timerData.function, QScriptValueList()); + callWithEnvironment(timerData.definingEntityIdentifier, timerData.definingSandboxURL, timerData.function, timerData.function, ScriptValueList()); auto postTimer = p_high_resolution_clock::now(); auto elapsed = (postTimer - preTimer); _totalTimerExecution += std::chrono::duration_cast(elapsed); } else { - qCWarning(scriptengine) << "timerFired -- invalid function" << timerData.function.toVariant().toString(); + qCWarning(scriptengine) << "timerFired -- invalid function" << timerData.function->toVariant().toString(); } } -QObject* ScriptEngine::setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot) { +QObject* ScriptManager::setupTimerWithInterval(const ScriptValuePointer& function, int intervalMS, bool isSingleShot) { // create the timer, add it to the map, and start it QTimer* newTimer = new QTimer(this); newTimer->setSingleShot(isSingleShot); @@ -1567,10 +1359,10 @@ QObject* ScriptEngine::setupTimerWithInterval(const QScriptValue& function, int newTimer->setTimerType(Qt::PreciseTimer); } - connect(newTimer, &QTimer::timeout, this, &ScriptEngine::timerFired); + connect(newTimer, &QTimer::timeout, this, &ScriptManager::timerFired); // make sure the timer stops when the script does - connect(this, &ScriptEngine::scriptEnding, newTimer, &QTimer::stop); + connect(this, &ScriptManager::scriptEnding, newTimer, &QTimer::stop); CallbackData timerData = { function, currentEntityIdentifier, currentSandboxURL }; @@ -1580,7 +1372,7 @@ QObject* ScriptEngine::setupTimerWithInterval(const QScriptValue& function, int return newTimer; } -QObject* ScriptEngine::setInterval(const QScriptValue& function, int intervalMS) { +QObject* ScriptManager::setInterval(const ScriptValuePointer& function, int intervalMS) { QSharedPointer scriptEngines(_scriptEngines); if (!scriptEngines || scriptEngines->isStopped()) { scriptWarningMessage("Script.setInterval() while shutting down is ignored... parent script:" + getFilename()); @@ -1590,7 +1382,7 @@ QObject* ScriptEngine::setInterval(const QScriptValue& function, int intervalMS) return setupTimerWithInterval(function, intervalMS, false); } -QObject* ScriptEngine::setTimeout(const QScriptValue& function, int timeoutMS) { +QObject* ScriptManager::setTimeout(const ScriptValuePointer& function, int timeoutMS) { QSharedPointer scriptEngines(_scriptEngines); if (!scriptEngines || scriptEngines->isStopped()) { scriptWarningMessage("Script.setTimeout() while shutting down is ignored... parent script:" + getFilename()); @@ -1600,7 +1392,7 @@ QObject* ScriptEngine::setTimeout(const QScriptValue& function, int timeoutMS) { return setupTimerWithInterval(function, timeoutMS, true); } -void ScriptEngine::stopTimer(QTimer *timer) { +void ScriptManager::stopTimer(QTimer *timer) { if (_timerFunctionMap.contains(timer)) { timer->stop(); _timerFunctionMap.remove(timer); @@ -1610,7 +1402,7 @@ void ScriptEngine::stopTimer(QTimer *timer) { } } -QUrl ScriptEngine::resolvePath(const QString& include) const { +QUrl ScriptManager::resolvePath(const QString& include) const { QUrl url(include); // first lets check to see if it's already a full URL -- or a Windows path like "c:/" if (include.startsWith("/") || url.scheme().length() == 1) { @@ -1650,25 +1442,25 @@ QUrl ScriptEngine::resolvePath(const QString& include) const { return url; } -QUrl ScriptEngine::resourcesPath() const { +QUrl ScriptManager::resourcesPath() const { return QUrl(PathUtils::resourcesUrl()); } -void ScriptEngine::print(const QString& message) { +void ScriptManager::print(const QString& message) { emit printedMessage(message, getFilename()); } -void ScriptEngine::beginProfileRange(const QString& label) const { +void ScriptManager::beginProfileRange(const QString& label) const { PROFILE_SYNC_BEGIN(script, label.toStdString().c_str(), label.toStdString().c_str()); } -void ScriptEngine::endProfileRange(const QString& label) const { +void ScriptManager::endProfileRange(const QString& label) const { PROFILE_SYNC_END(script, label.toStdString().c_str(), label.toStdString().c_str()); } // Script.require.resolve -- like resolvePath, but performs more validation and throws exceptions on invalid module identifiers (for consistency with Node.js) -QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& relativeTo) { +QString ScriptManager::_requireResolve(const QString& moduleId, const QString& relativeTo) { if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { return QString(); } @@ -1681,7 +1473,7 @@ QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& re } auto message = QString("Cannot find module '%1' (%2)").arg(displayId); - auto throwResolveError = [&](const QScriptValue& error) -> QString { + auto throwResolveError = [&](const ScriptValuePointer& error) -> QString { raiseException(error); maybeEmitUncaughtException("require.resolve"); return QString(); @@ -1696,7 +1488,7 @@ QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& re } // this regex matches: absolute, dotted or path-like URLs - // (ie: the kind of stuff ScriptEngine::resolvePath already handles) + // (ie: the kind of stuff ScriptManager::resolvePath already handles) QRegularExpression qualified ("^\\w+:|^/|^[.]{1,2}(/|$)"); // this is for module.require (which is a bound version of require that's always relative to the module path) @@ -1760,52 +1552,52 @@ QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& re } // retrieves the current parent module from the JS scope chain -QScriptValue ScriptEngine::currentModule() { +ScriptValuePointer ScriptManager::currentModule() { if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { return unboundNullValue(); } - auto jsRequire = globalObject().property("Script").property("require"); + auto jsRequire = globalObject()->property("Script")->property("require"); auto cache = jsRequire.property("cache"); - auto candidate = QScriptValue(); + auto candidate = ScriptValuePointer(); for (auto c = currentContext(); c && !candidate.isObject(); c = c->parentContext()) { QScriptContextInfo contextInfo { c }; candidate = cache.property(contextInfo.fileName()); } - if (!candidate.isObject()) { - return QScriptValue(); + if (!candidate->isObject()) { + return ScriptValuePointer(); } return candidate; } // replaces or adds "module" to "parent.children[]" array // (for consistency with Node.js and userscript cache invalidation without "cache busters") -bool ScriptEngine::registerModuleWithParent(const QScriptValue& module, const QScriptValue& parent) { - auto children = parent.property("children"); - if (children.isArray()) { - auto key = module.property("id"); - auto length = children.property("length").toInt32(); +bool ScriptManager::registerModuleWithParent(const ScriptValuePointer& module, const ScriptValuePointer& parent) { + auto children = parent->property("children"); + if (children->isArray()) { + auto key = module->property("id"); + auto length = children->property("length")->toInt32(); for (int i = 0; i < length; i++) { - if (children.property(i).property("id").strictlyEquals(key)) { - qCDebug(scriptengine_module) << key.toString() << " updating parent.children[" << i << "] = module"; - children.setProperty(i, module); + if (children->property(i)->property("id")->strictlyEquals(key)) { + qCDebug(scriptengine_module) << key->toString() << " updating parent.children[" << i << "] = module"; + children->setProperty(i, module); return true; } } - qCDebug(scriptengine_module) << key.toString() << " appending parent.children[" << length << "] = module"; - children.setProperty(length, module); + qCDebug(scriptengine_module) << key->toString() << " appending parent.children[" << length << "] = module"; + children->setProperty(length, module); return true; - } else if (parent.isValid()) { + } else if (parent->isValid()) { qCDebug(scriptengine_module) << "registerModuleWithParent -- unrecognized parent" << parent.toVariant().toString(); } return false; } // creates a new JS "module" Object with default metadata properties -QScriptValue ScriptEngine::newModule(const QString& modulePath, const QScriptValue& parent) { +ScriptValuePointer ScriptManager::newModule(const QString& modulePath, const ScriptValuePointer& parent) { auto closure = newObject(); auto exports = newObject(); auto module = newObject(); - qCDebug(scriptengine_module) << "newModule" << parent.property("filename").toString(); + qCDebug(scriptengine_module) << "newModule" << parent->property("filename").toString(); closure.setProperty("module", module, READONLY_PROP_FLAGS); @@ -1824,14 +1616,14 @@ QScriptValue ScriptEngine::newModule(const QString& modulePath, const QScriptVal module.setProperty("children", newArray(), READONLY_PROP_FLAGS); // module.require is a bound version of require that always resolves relative to that module's path - auto boundRequire = QScriptEngine::evaluate("(function(id) { return Script.require(Script.require.resolve(id, this.filename)); })", "(boundRequire)"); + auto boundRequire = engine->evaluate("(function(id) { return Script.require(Script.require.resolve(id, this.filename)); })", "(boundRequire)"); module.setProperty("require", boundRequire, READONLY_PROP_FLAGS); return module; } // synchronously fetch a module's source code using BatchLoader -QVariantMap ScriptEngine::fetchModuleSource(const QString& modulePath, const bool forceDownload) { +QVariantMap ScriptManager::fetchModuleSource(const QString& modulePath, const bool forceDownload) { using UrlMap = QMap; auto scriptCache = DependencyManager::get(); QVariantMap req; @@ -1889,23 +1681,23 @@ QVariantMap ScriptEngine::fetchModuleSource(const QString& modulePath, const boo } // evaluate a pending module object using the fetched source code -QScriptValue ScriptEngine::instantiateModule(const QScriptValue& module, const QString& sourceCode) { - QScriptValue result; - auto modulePath = module.property("filename").toString(); - auto closure = module.property("__closure__"); +ScriptValuePointer ScriptManager::instantiateModule(const ScriptValuePointer& module, const QString& sourceCode) { + ScriptValuePointer result; + auto modulePath = module->property("filename")->toString(); + auto closure = module->property("__closure__"); qCDebug(scriptengine_module) << QString("require.instantiateModule: %1 / %2 bytes") .arg(QUrl(modulePath).fileName()).arg(sourceCode.length()); - if (module.property("content-type").toString() == "application/json") { + if (module->property("content-type")->toString() == "application/json") { qCDebug(scriptengine_module) << "... parsing as JSON"; - closure.setProperty("__json", sourceCode); + closure->setProperty("__json", sourceCode); result = evaluateInClosure(closure, { "module.exports = JSON.parse(__json)", modulePath }); } else { // scoped vars for consistency with Node.js - closure.setProperty("require", module.property("require")); - closure.setProperty("__filename", modulePath, READONLY_HIDDEN_PROP_FLAGS); - closure.setProperty("__dirname", QString(modulePath).replace(QRegExp("/[^/]*$"), ""), READONLY_HIDDEN_PROP_FLAGS); + closure->setProperty("require", module->property("require")); + closure->setProperty("__filename", modulePath, READONLY_HIDDEN_PROP_FLAGS); + closure->setProperty("__dirname", QString(modulePath).replace(QRegExp("/[^/]*$"), ""), READONLY_HIDDEN_PROP_FLAGS); result = evaluateInClosure(closure, { sourceCode, modulePath }); } maybeEmitUncaughtException(__FUNCTION__); @@ -1913,18 +1705,18 @@ QScriptValue ScriptEngine::instantiateModule(const QScriptValue& module, const Q } // CommonJS/Node.js like require/module support -QScriptValue ScriptEngine::require(const QString& moduleId) { - qCDebug(scriptengine_module) << "ScriptEngine::require(" << moduleId.left(MAX_DEBUG_VALUE_LENGTH) << ")"; +ScriptValuePointer ScriptManager::require(const QString& moduleId) { + qCDebug(scriptengine_module) << "ScriptManager::require(" << moduleId.left(MAX_DEBUG_VALUE_LENGTH) << ")"; if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { return unboundNullValue(); } - auto jsRequire = globalObject().property("Script").property("require"); + auto jsRequire = globalObject()->property("Script")->property("require"); auto cacheMeta = jsRequire.data(); auto cache = jsRequire.property("cache"); auto parent = currentModule(); - auto throwModuleError = [&](const QString& modulePath, const QScriptValue& error) { + auto throwModuleError = [&](const QString& modulePath, const ScriptValuePointer& error) { cache.setProperty(modulePath, nullValue()); if (!error.isNull()) { #ifdef DEBUG_JS_MODULES @@ -1958,7 +1750,7 @@ QScriptValue ScriptEngine::require(const QString& moduleId) { bool invalidateCache = getCachebustSetting.get() || (module.isUndefined() && cacheMeta.property(moduleId).isValid()); // reset the cacheMeta record so invalidation won't apply next time, even if the module fails to load - cacheMeta.setProperty(modulePath, QScriptValue()); + cacheMeta.setProperty(modulePath, ScriptValuePointer()); auto exports = module.property("exports"); if (!invalidateCache && exports.isObject()) { @@ -2001,8 +1793,8 @@ QScriptValue ScriptEngine::require(const QString& moduleId) { // evaluate the module auto result = instantiateModule(module, sourceCode); - if (result.isError() && !result.strictlyEquals(module.property("exports"))) { - qCWarning(scriptengine_module) << "-- result.isError --" << result.toString(); + if (result->isError() && !result->strictlyEquals(module.property("exports"))) { + qCWarning(scriptengine_module) << "-- result.isError --" << result->toString(); return throwModuleError(modulePath, result); } @@ -2012,7 +1804,7 @@ QScriptValue ScriptEngine::require(const QString& moduleId) { // set up a new reference point for detecting cache key deletion cacheMeta.setProperty(modulePath, module); - qCDebug(scriptengine_module) << "//ScriptEngine::require(" << moduleId << ")"; + qCDebug(scriptengine_module) << "//ScriptManager::require(" << moduleId << ")"; maybeEmitUncaughtException(__FUNCTION__); return module.property("exports"); @@ -2022,7 +1814,7 @@ QScriptValue ScriptEngine::require(const QString& moduleId) { // when all of the files have finished loading. // If no callback is specified, the included files will be loaded synchronously and will block execution until // all of the files have finished loading. -void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) { +void ScriptManager::include(const QStringList& includeFiles, ScriptValuePointer callback) { if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { return; } @@ -2099,8 +1891,8 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac } _parentURL = parentURL; - if (callback.isFunction()) { - callWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, QScriptValue(callback), QScriptValue(), QScriptValueList()); + if (callback->isFunction()) { + callWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, ScriptValuePointer(callback), ScriptValuePointer(), ScriptValueList()); } loader->deleteLater(); @@ -2113,14 +1905,14 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac loader->start(processLevelMaxRetries); - if (!callback.isFunction() && !loader->isFinished()) { + if (!callback->isFunction() && !loader->isFinished()) { QEventLoop loop; QObject::connect(loader, &BatchLoader::finished, &loop, &QEventLoop::quit); loop.exec(); } } -void ScriptEngine::include(const QString& includeFile, QScriptValue callback) { +void ScriptManager::include(const QString& includeFile, ScriptValuePointer callback) { QSharedPointer scriptEngines(_scriptEngines); if (!scriptEngines || scriptEngines->isStopped()) { scriptWarningMessage("Script.include() while shutting down is ignored... includeFile:" @@ -2134,9 +1926,9 @@ void ScriptEngine::include(const QString& includeFile, QScriptValue callback) { } // NOTE: The load() command is similar to the include() command except that it loads the script -// as a stand-alone script. To accomplish this, the ScriptEngine class just emits a signal which +// as a stand-alone script. To accomplish this, the ScriptManager class just emits a signal which // the Application or other context will connect to in order to know to actually load the script -void ScriptEngine::load(const QString& loadFile) { +void ScriptManager::load(const QString& loadFile) { if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { return; } @@ -2163,9 +1955,9 @@ void ScriptEngine::load(const QString& loadFile) { } // Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args -void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHandlerArgs) { +void ScriptManager::forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, ScriptValueList eventHandlerArgs) { if (QThread::currentThread() != thread()) { - qCDebug(scriptengine) << "*** ERROR *** ScriptEngine::forwardHandlerCall() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; + qCDebug(scriptengine) << "*** ERROR *** ScriptManager::forwardHandlerCall() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; assert(false); return ; } @@ -2183,12 +1975,12 @@ void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QStrin // and the entity scripts may be for entities other than the one this is a handler for. // Fortunately, the definingEntityIdentifier captured the entity script id (if any) when the handler was added. CallbackData& handler = handlersForEvent[i]; - callWithEnvironment(handler.definingEntityIdentifier, handler.definingSandboxURL, handler.function, QScriptValue(), eventHandlerArgs); + callWithEnvironment(handler.definingEntityIdentifier, handler.definingSandboxURL, handler.function, ScriptValuePointer(), eventHandlerArgs); } } } -int ScriptEngine::getNumRunningEntityScripts() const { +int ScriptManager::getNumRunningEntityScripts() const { QReadLocker locker { &_entityScriptsLock }; int sum = 0; for (const auto& st : _entityScripts) { @@ -2199,7 +1991,7 @@ int ScriptEngine::getNumRunningEntityScripts() const { return sum; } -void ScriptEngine::setEntityScriptDetails(const EntityItemID& entityID, const EntityScriptDetails& details) { +void ScriptManager::setEntityScriptDetails(const EntityItemID& entityID, const EntityScriptDetails& details) { { QWriteLocker locker { &_entityScriptsLock }; _entityScripts[entityID] = details; @@ -2207,7 +1999,7 @@ void ScriptEngine::setEntityScriptDetails(const EntityItemID& entityID, const En emit entityScriptDetailsUpdated(); } -void ScriptEngine::updateEntityScriptStatus(const EntityItemID& entityID, const EntityScriptStatus &status, const QString& errorInfo) { +void ScriptManager::updateEntityScriptStatus(const EntityItemID& entityID, const EntityScriptStatus &status, const QString& errorInfo) { { QWriteLocker locker { &_entityScriptsLock }; EntityScriptDetails& details = _entityScripts[entityID]; @@ -2217,7 +2009,7 @@ void ScriptEngine::updateEntityScriptStatus(const EntityItemID& entityID, const emit entityScriptDetailsUpdated(); } -QVariant ScriptEngine::cloneEntityScriptDetails(const EntityItemID& entityID) { +QVariant ScriptManager::cloneEntityScriptDetails(const EntityItemID& entityID) { static const QVariant NULL_VARIANT { qVariantFromValue((QObject*)nullptr) }; QVariantMap map; if (entityID.isNull()) { @@ -2259,11 +2051,11 @@ QVariant ScriptEngine::cloneEntityScriptDetails(const EntityItemID& entityID) { return map; } -QFuture ScriptEngine::getLocalEntityScriptDetails(const EntityItemID& entityID) { - return QtConcurrent::run(this, &ScriptEngine::cloneEntityScriptDetails, entityID); +QFuture ScriptManager::getLocalEntityScriptDetails(const EntityItemID& entityID) { + return QtConcurrent::run(this, &ScriptManager::cloneEntityScriptDetails, entityID); } -bool ScriptEngine::getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const { +bool ScriptManager::getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const { QReadLocker locker { &_entityScriptsLock }; auto it = _entityScripts.constFind(entityID); if (it == _entityScripts.constEnd()) { @@ -2273,12 +2065,12 @@ bool ScriptEngine::getEntityScriptDetails(const EntityItemID& entityID, EntitySc return true; } -bool ScriptEngine::hasEntityScriptDetails(const EntityItemID& entityID) const { +bool ScriptManager::hasEntityScriptDetails(const EntityItemID& entityID) const { QReadLocker locker { &_entityScriptsLock }; return _entityScripts.contains(entityID); } -void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) { +void ScriptManager::loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "loadEntityScript", Q_ARG(const EntityItemID&, entityID), @@ -2325,7 +2117,7 @@ void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& [this, weakRef, entityScript, entityID](const QString& url, const QString& contents, bool isURL, bool success, const QString& status) { QSharedPointer strongRef(weakRef); if (!strongRef) { - qCWarning(scriptengine) << "loadEntityScript.contentAvailable -- ScriptEngine was deleted during getScriptContents!!"; + qCWarning(scriptengine) << "loadEntityScript.contentAvailable -- ScriptManager was deleted during getScriptContents!!"; return; } if (isStopping()) { @@ -2378,10 +2170,10 @@ void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& // The JSDoc is for the callEntityScriptMethod() call in this method. // since all of these operations can be asynch we will always do the actual work in the response handler // for the download -void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success , const QString& status) { +void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success , const QString& status) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::entityScriptContentAvailable() called on wrong thread [" + qCDebug(scriptengine) << "*** WARNING *** ScriptManager::entityScriptContentAvailable() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " "entityID:" << entityID << "scriptOrURL:" << scriptOrURL << "contents:" << contents << "isURL:" << isURL << "success:" << success; @@ -2398,7 +2190,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co } #ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "ScriptEngine::entityScriptContentAvailable() thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]"; + qCDebug(scriptengine) << "ScriptManager::entityScriptContentAvailable() thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]"; #endif auto scriptCache = DependencyManager::get(); @@ -2455,14 +2247,14 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co const int SANDBOX_TIMEOUT = 0.25 * MSECS_PER_SECOND; BaseScriptEngine sandbox; sandbox.setProcessEventsInterval(SANDBOX_TIMEOUT); - QScriptValue testConstructor, exception; + ScriptValuePointer testConstructor, exception; if (atoi(getenv("UNSAFE_ENTITY_SCRIPTS") ? getenv("UNSAFE_ENTITY_SCRIPTS") : "0")) { QTimer timeout; timeout.setSingleShot(true); timeout.start(SANDBOX_TIMEOUT); connect(&timeout, &QTimer::timeout, [=, &sandbox]{ - qCDebug(scriptengine) << "ScriptEngine::entityScriptContentAvailable timeout"; + qCDebug(scriptengine) << "ScriptManager::entityScriptContentAvailable timeout"; // Guard against infinite loops and non-performant code sandbox.raiseException( @@ -2475,7 +2267,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co if (sandbox.hasUncaughtException()) { exception = sandbox.cloneUncaughtException(QString("(preflight %1)").arg(entityID.toString())); sandbox.clearExceptions(); - } else if (testConstructor.isError()) { + } else if (testConstructor->isError()) { exception = testConstructor; } } else { @@ -2515,7 +2307,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co // END bypass whitelist based on current domain. // Start processing scripts through the whitelist. - if (ScriptEngine::getContext() == "entity_server") { // If running on the server, do not engage whitelist. + if (ScriptManager::getContext() == "entity_server") { // If running on the server, do not engage whitelist. passList = true; } else if (!passList) { // If waved through, do not engage whitelist. for (const auto& str : safeURLPrefixes) { @@ -2538,7 +2330,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co timeout.setSingleShot(true); timeout.start(SANDBOX_TIMEOUT); connect(&timeout, &QTimer::timeout, [=, &sandbox] { - qCDebug(scriptengine) << "ScriptEngine::entityScriptContentAvailable timeout"; + qCDebug(scriptengine) << "ScriptManager::entityScriptContentAvailable timeout"; // Guard against infinite loops and non-performant code sandbox.raiseException( @@ -2550,7 +2342,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co if (sandbox.hasUncaughtException()) { exception = sandbox.cloneUncaughtException(QString("(preflight %1)").arg(entityID.toString())); sandbox.clearExceptions(); - } else if (testConstructor.isError()) { + } else if (testConstructor->isError()) { exception = testConstructor; } } @@ -2560,7 +2352,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co // exception = makeError("UNSAFE_ENTITY_SCRIPTS == 0"); } - if (exception.isError()) { + if (exception->isError()) { // create a local copy using makeError to decouple from the sandbox engine exception = makeError(exception); setError(formatException(exception, _enableExtendedJSExceptions.get()), EntityScriptStatus::ERROR_RUNNING_SCRIPT); @@ -2569,12 +2361,12 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co } // CONSTRUCTOR VIABILITY - if (!testConstructor.isFunction()) { - QString testConstructorType = QString(testConstructor.toVariant().typeName()); + if (!testConstructor->isFunction()) { + QString testConstructorType = QString(testConstructor->toVariant().typeName()); if (testConstructorType == "") { testConstructorType = "empty"; } - QString testConstructorValue = testConstructor.toString(); + QString testConstructorValue = testConstructor->toString(); if (testConstructorValue.size() > MAX_DEBUG_VALUE_LENGTH) { testConstructorValue = testConstructorValue.mid(0, MAX_DEBUG_VALUE_LENGTH) + "..."; } @@ -2598,11 +2390,11 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co } // THE ACTUAL EVALUATION AND CONSTRUCTION - QScriptValue entityScriptConstructor, entityScriptObject; + ScriptValuePointer entityScriptConstructor, entityScriptObject; QUrl sandboxURL = currentSandboxURL.isEmpty() ? scriptOrURL : currentSandboxURL; auto initialization = [&]{ entityScriptConstructor = evaluate(contents, fileName); - entityScriptObject = entityScriptConstructor.construct(); + entityScriptObject = entityScriptConstructor->construct(); if (hasUncaughtException()) { entityScriptObject = cloneUncaughtException("(construct " + entityID.toString() + ")"); @@ -2612,7 +2404,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co doWithEnvironment(entityID, sandboxURL, initialization); - if (entityScriptObject.isError()) { + if (entityScriptObject->isError()) { auto exception = entityScriptObject; setError(formatException(exception, _enableExtendedJSExceptions.get()), EntityScriptStatus::ERROR_RUNNING_SCRIPT); emit unhandledException(exception); @@ -2645,10 +2437,10 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co * @returns {Signal} */ // The JSDoc is for the callEntityScriptMethod() call in this method. -void ScriptEngine::unloadEntityScript(const EntityItemID& entityID, bool shouldRemoveFromMap) { +void ScriptManager::unloadEntityScript(const EntityItemID& entityID, bool shouldRemoveFromMap) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::unloadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "*** WARNING *** ScriptManager::unloadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " "entityID:" << entityID; #endif @@ -2658,7 +2450,7 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID, bool shouldR return; } #ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "ScriptEngine::unloadEntityScript() called on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "ScriptManager::unloadEntityScript() called on correct thread [" << thread() << "] " "entityID:" << entityID; #endif @@ -2694,15 +2486,15 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID, bool shouldR } } -QList ScriptEngine::getListOfEntityScriptIDs() { +QList ScriptManager::getListOfEntityScriptIDs() { QReadLocker locker{ &_entityScriptsLock }; return _entityScripts.keys(); } -void ScriptEngine::unloadAllEntityScripts(bool blockingCall) { +void ScriptManager::unloadAllEntityScripts(bool blockingCall) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::unloadAllEntityScripts() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; + qCDebug(scriptengine) << "*** WARNING *** ScriptManager::unloadAllEntityScripts() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; #endif QMetaObject::invokeMethod(this, "unloadAllEntityScripts", @@ -2710,7 +2502,7 @@ void ScriptEngine::unloadAllEntityScripts(bool blockingCall) { return; } #ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "ScriptEngine::unloadAllEntityScripts() called on correct thread [" << thread() << "]"; + qCDebug(scriptengine) << "ScriptManager::unloadAllEntityScripts() called on correct thread [" << thread() << "]"; #endif QList keys; @@ -2736,7 +2528,7 @@ void ScriptEngine::unloadAllEntityScripts(bool blockingCall) { #endif // DEBUG_ENGINE_STATE } -void ScriptEngine::refreshFileScript(const EntityItemID& entityID) { +void ScriptManager::refreshFileScript(const EntityItemID& entityID) { if (!HIFI_AUTOREFRESH_FILE_SCRIPTS || !hasEntityScriptDetails(entityID)) { return; } @@ -2768,14 +2560,14 @@ void ScriptEngine::refreshFileScript(const EntityItemID& entityID) { // Even if entityID is supplied as currentEntityIdentifier, this still documents the source // of the code being executed (e.g., if we ever sandbox different entity scripts, or provide different // global values for different entity scripts). -void ScriptEngine::doWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, std::function operation) { +void ScriptManager::doWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, std::function operation) { EntityItemID oldIdentifier = currentEntityIdentifier; QUrl oldSandboxURL = currentSandboxURL; currentEntityIdentifier = entityID; currentSandboxURL = sandboxURL; #if DEBUG_CURRENT_ENTITY - QScriptValue oldData = this->globalObject().property("debugEntityID"); + ScriptValuePointer oldData = this->globalObject().property("debugEntityID"); this->globalObject().setProperty("debugEntityID", entityID.toScriptValue(this)); // Make the entityID available to javascript as a global. operation(); this->globalObject().setProperty("debugEntityID", oldData); @@ -2787,17 +2579,17 @@ void ScriptEngine::doWithEnvironment(const EntityItemID& entityID, const QUrl& s currentSandboxURL = oldSandboxURL; } -void ScriptEngine::callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, QScriptValue function, QScriptValue thisObject, QScriptValueList args) { +void ScriptManager::callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, ScriptValuePointer function, ScriptValuePointer thisObject, ScriptValueList args) { auto operation = [&]() { function.call(thisObject, args); }; doWithEnvironment(entityID, sandboxURL, operation); } -void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params, const QUuid& remoteCallerID) { +void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params, const QUuid& remoteCallerID) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "*** WARNING *** ScriptManager::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " "entityID:" << entityID << "methodName:" << methodName; #endif @@ -2809,7 +2601,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS return; } #ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "ScriptManager::callEntityScriptMethod() called on correct thread [" << thread() << "] " "entityID:" << entityID << "methodName:" << methodName; #endif @@ -2822,7 +2614,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS QWriteLocker locker { &_entityScriptsLock }; details = _entityScripts[entityID]; } - QScriptValue entityScript = details.scriptObject; // previously loaded + ScriptValuePointer entityScript = details.scriptObject; // previously loaded // If this is a remote call, we need to check to see if the function is remotely callable // we do this by checking for the existance of the 'remotelyCallable' property on the @@ -2832,11 +2624,11 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS if (remoteCallerID == QUuid()) { callAllowed = true; } else { - if (entityScript.property("remotelyCallable").isArray()) { - auto callables = entityScript.property("remotelyCallable"); - auto callableCount = callables.property("length").toInteger(); + if (entityScript->property("remotelyCallable")->isArray()) { + auto callables = entityScript->property("remotelyCallable"); + auto callableCount = callables->property("length")->toInteger(); for (int i = 0; i < callableCount; i++) { - auto callable = callables.property(i).toString(); + auto callable = callables->property(i)->toString(); if (callable == methodName) { callAllowed = true; break; @@ -2848,23 +2640,25 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS } } - if (callAllowed && entityScript.property(methodName).isFunction()) { - QScriptValueList args; - args << entityID.toScriptValue(this); - args << qScriptValueFromSequence(this, params); + if (callAllowed && entityScript->property(methodName)->isFunction()) { + auto scriptEngine = engine().data(); - QScriptValue oldData = this->globalObject().property("Script").property("remoteCallerID"); - this->globalObject().property("Script").setProperty("remoteCallerID", remoteCallerID.toString()); // Make the remoteCallerID available to javascript as a global. - callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args); - this->globalObject().property("Script").setProperty("remoteCallerID", oldData); + ScriptValueList args; + args << entityID.toScriptValue(scriptEngine); + args << scriptValueFromSequence(scriptEngine, params); + + ScriptValuePointer oldData = scriptEngine->globalObject()->property("Script")->property("remoteCallerID"); + scriptEngine->globalObject()->property("Script")->setProperty("remoteCallerID", remoteCallerID.toString()); // Make the remoteCallerID available to javascript as a global. + callWithEnvironment(entityID, details.definingSandboxURL, entityScript->property(methodName), entityScript, args); + scriptEngine->globalObject()->property("Script")->setProperty("remoteCallerID", oldData); } } } -void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const PointerEvent& event) { +void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const PointerEvent& event) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "*** WARNING *** ScriptManager::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " "entityID:" << entityID << "methodName:" << methodName << "event: mouseEvent"; #endif @@ -2875,7 +2669,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS return; } #ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "ScriptManager::callEntityScriptMethod() called on correct thread [" << thread() << "] " "entityID:" << entityID << "methodName:" << methodName << "event: pointerEvent"; #endif @@ -2888,20 +2682,22 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS QWriteLocker locker { &_entityScriptsLock }; details = _entityScripts[entityID]; } - QScriptValue entityScript = details.scriptObject; // previously loaded - if (entityScript.property(methodName).isFunction()) { - QScriptValueList args; - args << entityID.toScriptValue(this); - args << event.toScriptValue(this); - callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args); + ScriptValuePointer entityScript = details.scriptObject; // previously loaded + if (entityScript->property(methodName)->isFunction()) { + auto scriptEngine = engine().data(); + + ScriptValueList args; + args << entityID.toScriptValue(scriptEngine); + args << event.toScriptValue(scriptEngine); + callWithEnvironment(entityID, details.definingSandboxURL, entityScript->property(methodName), entityScript, args); } } } -void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision) { +void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "*** WARNING *** ScriptManager::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision"; #endif @@ -2913,7 +2709,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS return; } #ifdef THREAD_DEBUGGING - qCDebug(scriptengine) << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "ScriptManager::callEntityScriptMethod() called on correct thread [" << thread() << "] " "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision"; #endif @@ -2926,17 +2722,19 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS QWriteLocker locker { &_entityScriptsLock }; details = _entityScripts[entityID]; } - QScriptValue entityScript = details.scriptObject; // previously loaded - if (entityScript.property(methodName).isFunction()) { - QScriptValueList args; - args << entityID.toScriptValue(this); - args << otherID.toScriptValue(this); - args << collisionToScriptValue(this, collision); - callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args); + ScriptValuePointer entityScript = details.scriptObject; // previously loaded + if (entityScript->property(methodName)->isFunction()) { + auto scriptEngine = engine().data(); + + ScriptValueList args; + args << entityID.toScriptValue(scriptEngine); + args << otherID.toScriptValue(scriptEngine); + args << collisionToScriptValue(scriptEngine, collision); + callWithEnvironment(entityID, details.definingSandboxURL, entityScript->property(methodName), entityScript, args); } } } -QString ScriptEngine::getExternalPath(ExternalResource::Bucket bucket, const QString& path) { +QString ScriptManager::getExternalPath(ExternalResource::Bucket bucket, const QString& path) { return ExternalResource::getInstance()->getUrl(bucket, path); } diff --git a/libraries/script-engine/src/ScriptManager.h b/libraries/script-engine/src/ScriptManager.h new file mode 100644 index 00000000000..d38137e3a51 --- /dev/null +++ b/libraries/script-engine/src/ScriptManager.h @@ -0,0 +1,941 @@ +// +// ScriptManager.h +// libraries/script-engine/src +// +// Created by Brad Hefta-Gaub on 12/14/13. +// Copyright 2013 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ScriptManager_h +#define hifi_ScriptManager_h + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "EntityItemID.h" +#include "EntitiesScriptEngineProvider.h" +#include +#include +#include + +#include "AssetScriptingInterface.h" +#include "ConsoleScriptingInterface.h" +#include "Mat4.h" +#include "PointerEvent.h" +#include "Quat.h" +#include "ScriptUUID.h" +#include "Vec3.h" + +class QScriptEngineDebugger; + +static const QString NO_SCRIPT(""); + +static const int SCRIPT_FPS = 60; +static const int DEFAULT_MAX_ENTITY_PPS = 9000; +static const int DEFAULT_ENTITY_PPS_PER_SCRIPT = 900; + +class ScriptEngine; +class ScriptEngines; +class ScriptManager; +class ScriptValue; +using ScriptEnginePointer = QSharedPointer; +using ScriptManagerPointer = QSharedPointer; +using ScriptValuePointer = QSharedPointer; +using ScriptValueList = QList; + +Q_DECLARE_METATYPE(ScriptManagerPointer) + +class CallbackData { +public: + ScriptValuePointer function; + EntityItemID definingEntityIdentifier; + QUrl definingSandboxURL; +}; + +class DeferredLoadEntity { +public: + EntityItemID entityID; + QString entityScript; + //bool forceRedownload; +}; + +struct EntityScriptContentAvailable { + EntityItemID entityID; + QString scriptOrURL; + QString contents; + bool isURL; + bool success; + QString status; +}; + +typedef std::unordered_map EntityScriptContentAvailableMap; + +typedef QList CallbackList; +typedef QHash RegisteredEventHandlers; + +class EntityScriptDetails { +public: + EntityScriptStatus status { EntityScriptStatus::PENDING }; + + // If status indicates an error, this contains a human-readable string giving more information about the error. + QString errorInfo { "" }; + + QString scriptText { "" }; + ScriptValuePointer scriptObject{ ScriptValuePointer() }; + int64_t lastModified { 0 }; + QUrl definingSandboxURL { QUrl("about:EntityScript") }; +}; + +/**jsdoc + * The Script API provides facilities for working with scripts. + * + * @namespace Script + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * @hifi-server-entity + * @hifi-assignment-client + * + * @property {string} context - The context that the script is running in: + *
    + *
  • "client": An Interface or avatar script.
  • + *
  • "entity_client": A client entity script.
  • + *
  • "entity_server": A server entity script.
  • + *
  • "agent": An assignment client script.
  • + *
+ * Read-only. + * @property {string} type - The type of script that is running: + *
    + *
  • "client": An Interface script.
  • + *
  • "entity_client": A client entity script.
  • + *
  • "avatar": An avatar script.
  • + *
  • "entity_server": A server entity script.
  • + *
  • "agent": An assignment client script.
  • + *
+ * Read-only. + * @property {string} filename - The filename of the script file. + * Read-only. + * @property {Script.ResourceBuckets} ExternalPaths - External resource buckets. + */ +class ScriptManager : public QObject, public EntitiesScriptEngineProvider { + Q_OBJECT + Q_PROPERTY(QString context READ getContext) + Q_PROPERTY(QString type READ getTypeAsString) + Q_PROPERTY(QString fileName MEMBER _fileNameString CONSTANT) +public: + + enum Context { + CLIENT_SCRIPT, + ENTITY_CLIENT_SCRIPT, + ENTITY_SERVER_SCRIPT, + AGENT_SCRIPT + }; + + enum Type { + CLIENT, + ENTITY_CLIENT, + ENTITY_SERVER, + AGENT, + AVATAR + }; + Q_ENUM(Type) + + static int processLevelMaxRetries; + ScriptManager(Context context, const QString& scriptContents = NO_SCRIPT, const QString& fileNameString = QString("about:ScriptEngine")); + ~ScriptManager(); + + /// run the script in a dedicated thread. This will have the side effect of evalulating + /// the current script contents and calling run(). Callers will likely want to register the script with external + /// services before calling this. + void runInThread(); + + void runDebuggable(); + + /// run the script in the callers thread, exit when stop() is called. + void run(); + + QString getFilename() const; + + ScriptEnginePointer engine(); + + QList getListOfEntityScriptIDs(); + + /**jsdoc + * Stops and unloads the current script. + *

Warning: If an assignment client script, the script gets restarted after stopping.

+ * @function Script.stop + * @param {boolean} [marshal=false] - Marshal. + *

Deprecated: This parameter is deprecated and will be removed.

+ * @example Stop a script after 5s. + * Script.setInterval(function () { + * print("Hello"); + * }, 1000); + * + * Script.setTimeout(function () { + * Script.stop(true); + * }, 5000); + */ + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // NOTE - this is intended to be a public interface for Agent scripts, and local scripts, but not for EntityScripts + Q_INVOKABLE void stop(bool marshal = false); + + // Stop any evaluating scripts and wait for the scripting thread to finish. + void waitTillDoneRunning(bool shutdown = false); + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // NOTE - these are NOT intended to be public interfaces available to scripts, the are only Q_INVOKABLE so we can + // properly ensure they are only called on the correct thread + + /// if the script engine is not already running, this will download the URL and start the process of seting it up + /// to run... NOTE - this is used by Application currently to load the url. We don't really want it to be exposed + /// to scripts. we may not need this to be invokable + void loadURL(const QUrl& scriptURL, bool reload); + bool hasValidScriptSuffix(const QString& scriptFileName); + + /**jsdoc + * Gets the context that the script is running in: Interface/avatar, client entity, server entity, or assignment client. + * @function Script.getContext + * @returns {string} The context that the script is running in: + *
    + *
  • "client": An Interface or avatar script.
  • + *
  • "entity_client": A client entity script.
  • + *
  • "entity_server": A server entity script.
  • + *
  • "agent": An assignment client script.
  • + *
+ */ + Q_INVOKABLE QString getContext() const; + + /**jsdoc + * Checks whether the script is running as an Interface or avatar script. + * @function Script.isClientScript + * @returns {boolean} true if the script is running as an Interface or avatar script, false if it + * isn't. + */ + Q_INVOKABLE bool isClientScript() const { return _context == CLIENT_SCRIPT; } + + /**jsdoc + * Checks whether the application was compiled as a debug build. + * @function Script.isDebugMode + * @returns {boolean} true if the application was compiled as a debug build, false if it was + * compiled as a release build. + */ + Q_INVOKABLE bool isDebugMode() const; + + /**jsdoc + * Checks whether the script is running as a client entity script. + * @function Script.isEntityClientScript + * @returns {boolean} true if the script is running as a client entity script, false if it isn't. + */ + Q_INVOKABLE bool isEntityClientScript() const { return _context == ENTITY_CLIENT_SCRIPT; } + + /**jsdoc + * Checks whether the script is running as a server entity script. + * @function Script.isEntityServerScript + * @returns {boolean} true if the script is running as a server entity script, false if it isn't. + */ + Q_INVOKABLE bool isEntityServerScript() const { return _context == ENTITY_SERVER_SCRIPT; } + + /**jsdoc + * Checks whether the script is running as an assignment client script. + * @function Script.isAgentScript + * @returns {boolean} true if the script is running as an assignment client script, false if it + * isn't. + */ + Q_INVOKABLE bool isAgentScript() const { return _context == AGENT_SCRIPT; } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // NOTE - these are intended to be public interfaces available to scripts + + /**jsdoc + * Adds a function to the list of functions called when a particular event occurs on a particular entity. + *

See also, the {@link Entities} API.

+ * @function Script.addEventHandler + * @param {Uuid} entityID - The ID of the entity. + * @param {Script.EntityEvent} eventName - The name of the event. + * @param {Script~entityEventCallback|Script~pointerEventCallback|Script~collisionEventCallback} handler - The function to + * call when the event occurs on the entity. It can be either the name of a function or an in-line definition. + * @example Report when a mouse press occurs on a particular entity. + * var entityID = Entities.addEntity({ + * type: "Box", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * dimensions: { x: 0.5, y: 0.5, z: 0.5 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + * + * function reportMousePress(entityID, event) { + * print("Mouse pressed on entity: " + JSON.stringify(event)); + * } + * + * Script.addEventHandler(entityID, "mousePressOnEntity", reportMousePress); + */ + Q_INVOKABLE void addEventHandler(const EntityItemID& entityID, const QString& eventName, ScriptValuePointer handler); + + /**jsdoc + * Removes a function from the list of functions called when an entity event occurs on a particular entity. + *

See also, the {@link Entities} API.

+ * @function Script.removeEventHandler + * @param {Uuid} entityID - The ID of the entity. + * @param {Script.EntityEvent} eventName - The name of the entity event. + * @param {function} handler - The name of the function to no longer call when the entity event occurs on the entity. + */ + Q_INVOKABLE void removeEventHandler(const EntityItemID& entityID, const QString& eventName, ScriptValuePointer handler); + + /**jsdoc + * Starts running another script in Interface, if it isn't already running. The script is not automatically loaded next + * time Interface starts. + *

Supported Script Types: Interface Scripts • Avatar Scripts

+ *

See also, {@link ScriptDiscoveryService.loadScript}.

+ * @function Script.load + * @param {string} filename - The URL of the script to load. This can be relative to the current script's URL. + * @example Load a script from another script. + * // First file: scriptA.js + * print("This is script A"); + * + * // Second file: scriptB.js + * print("This is script B"); + * Script.load("scriptA.js"); + * + * // If you run scriptB.js you should see both scripts in the Running Scripts dialog. + * // And you should see the following output: + * // This is script B + * // This is script A + */ + Q_INVOKABLE void load(const QString& loadfile); + + /**jsdoc + * Includes JavaScript from other files in the current script. If a callback is specified, the files are loaded and + * included asynchronously, otherwise they are included synchronously (i.e., script execution blocks while the files are + * included). + * @function Script.include + * @variation 0 + * @param {string[]} filenames - The URLs of the scripts to include. Each can be relative to the current script. + * @param {function} [callback=null] - The function to call back when the scripts have been included. It can be either the + * name of a function or an in-line definition. + */ + Q_INVOKABLE void include(const QStringList& includeFiles, ScriptValuePointer callback = ScriptValuePointer()); + + /**jsdoc + * Includes JavaScript from another file in the current script. If a callback is specified, the file is loaded and included + * asynchronously, otherwise it is included synchronously (i.e., script execution blocks while the file is included). + * @function Script.include + * @param {string} filename - The URL of the script to include. It can be relative to the current script. + * @param {function} [callback=null] - The function to call back when the script has been included. It can be either the + * name of a function or an in-line definition. + * @example Include a script file asynchronously. + * // First file: scriptA.js + * print("This is script A"); + * + * // Second file: scriptB.js + * print("This is script B"); + * Script.include("scriptA.js", function () { + * print("Script A has been included"); + * }); + * + * // If you run scriptB.js you should see only scriptB.js in the running scripts list. + * // And you should see the following output: + * // This is script B + * // This is script A + * // Script A has been included + */ + Q_INVOKABLE void include(const QString& includeFile, ScriptValuePointer callback = ScriptValuePointer()); + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // MODULE related methods + + /**jsdoc + * Provides access to methods or objects provided in an external JavaScript or JSON file. + * See {@link https://docs.vircadia.dev/script/js-tips.html} for further details. + * @function Script.require + * @param {string} module - The module to use. May be a JavaScript file, a JSON file, or the name of a system module such + * as "appUi" (i.e., the "appUi.js" system module JavaScript file). + * @returns {object|array} The value assigned to module.exports in the JavaScript file, or the value defined + * in the JSON file. + */ + Q_INVOKABLE ScriptValuePointer require(const QString& moduleId); + + /**jsdoc + * @function Script.resetModuleCache + * @param {boolean} [deleteScriptCache=false] - Delete script cache. + * @deprecated This function is deprecated and will be removed. + */ + Q_INVOKABLE void resetModuleCache(bool deleteScriptCache = false); + + ScriptValuePointer currentModule(); + bool registerModuleWithParent(const ScriptValuePointer& module, const ScriptValuePointer& parent); + ScriptValuePointer newModule(const QString& modulePath, const ScriptValuePointer& parent = ScriptValuePointer()); + QVariantMap fetchModuleSource(const QString& modulePath, const bool forceDownload = false); + ScriptValuePointer instantiateModule(const ScriptValuePointer& module, const QString& sourceCode); + + /**jsdoc + * Calls a function repeatedly, at a set interval. + * @function Script.setInterval + * @param {function} function - The function to call. This can be either the name of a function or an in-line definition. + * @param {number} interval - The interval at which to call the function, in ms. + * @returns {object} A handle to the interval timer. This can be used in {@link Script.clearInterval}. + * @example Print a message every second. + * Script.setInterval(function () { + * print("Interval timer fired"); + * }, 1000); + */ + Q_INVOKABLE QObject* setInterval(const ScriptValuePointer& function, int intervalMS); + + /**jsdoc + * Calls a function once, after a delay. + * @function Script.setTimeout + * @param {function} function - The function to call. This can be either the name of a function or an in-line definition. + * @param {number} timeout - The delay after which to call the function, in ms. + * @returns {object} A handle to the timeout timer. This can be used in {@link Script.clearTimeout}. + * @example Print a message once, after a second. + * Script.setTimeout(function () { + * print("Timeout timer fired"); + * }, 1000); + */ + Q_INVOKABLE QObject* setTimeout(const ScriptValuePointer& function, int timeoutMS); + + /**jsdoc + * Stops an interval timer set by {@link Script.setInterval|setInterval}. + * @function Script.clearInterval + * @param {object} timer - The interval timer to stop. + * @example Stop an interval timer. + * // Print a message every second. + * var timer = Script.setInterval(function () { + * print("Interval timer fired"); + * }, 1000); + * + * // Stop the timer after 10 seconds. + * Script.setTimeout(function () { + * print("Stop interval timer"); + * Script.clearInterval(timer); + * }, 10000); + */ + Q_INVOKABLE void clearInterval(QObject* timer) { stopTimer(reinterpret_cast(timer)); } + + /**jsdoc + * Stops a timeout timer set by {@link Script.setTimeout|setTimeout}. + * @function Script.clearTimeout + * @param {object} timer - The timeout timer to stop. + * @example Stop a timeout timer. + * // Print a message after two seconds. + * var timer = Script.setTimeout(function () { + * print("Timer fired"); + * }, 2000); + * + * // Uncomment the following line to stop the timer from firing. + * //Script.clearTimeout(timer); + */ + Q_INVOKABLE void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast(timer)); } + + /**jsdoc + * Prints a message to the program log and emits {@link Script.printedMessage}. + *

Alternatively, you can use {@link print} or one of the {@link console} API methods.

+ * @function Script.print + * @param {string} message - The message to print. + */ + Q_INVOKABLE void print(const QString& message); + + /**jsdoc + * Resolves a relative path to an absolute path. The relative path is relative to the script's location. + * @function Script.resolvePath + * @param {string} path - The relative path to resolve. + * @returns {string} The absolute path. + * @example Report the directory and filename of the running script. + * print(Script.resolvePath("")); + * @example Report the directory of the running script. + * print(Script.resolvePath(".")); + * @example Report the path to a file located relative to the running script. + * print(Script.resolvePath("../assets/sounds/hello.wav")); + */ + Q_INVOKABLE QUrl resolvePath(const QString& path) const; + + /**jsdoc + * Gets the path to the resources directory for QML files. + * @function Script.resourcesPath + * @returns {string} The path to the resources directory for QML files. + */ + Q_INVOKABLE QUrl resourcesPath() const; + + /**jsdoc + * Starts timing a section of code in order to send usage data about it to Vircadia. Shouldn't be used outside of the + * standard scripts. + * @function Script.beginProfileRange + * @param {string} label - A name that identifies the section of code. + */ + Q_INVOKABLE void beginProfileRange(const QString& label) const; + + /**jsdoc + * Finishes timing a section of code in order to send usage data about it to Vircadia. Shouldn't be used outside of + * the standard scripts. + * @function Script.endProfileRange + * @param {string} label - A name that identifies the section of code. + */ + Q_INVOKABLE void endProfileRange(const QString& label) const; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Entity Script Related methods + + /**jsdoc + * Checks whether an entity has an entity script running. + * @function Script.isEntityScriptRunning + * @param {Uuid} entityID - The ID of the entity. + * @returns {boolean} true if the entity has an entity script running, false if it doesn't. + */ + Q_INVOKABLE bool isEntityScriptRunning(const EntityItemID& entityID) { + QReadLocker locker { &_entityScriptsLock }; + auto it = _entityScripts.constFind(entityID); + return it != _entityScripts.constEnd() && it->status == EntityScriptStatus::RUNNING; + } + QVariant cloneEntityScriptDetails(const EntityItemID& entityID); + QFuture getLocalEntityScriptDetails(const EntityItemID& entityID) override; + + /**jsdoc + * @function Script.loadEntityScript + * @param {Uuid} entityID - Entity ID. + * @param {string} script - Script. + * @param {boolean} forceRedownload - Force re-download. + * @deprecated This function is deprecated and will be removed. + */ + Q_INVOKABLE void loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload); + + /**jsdoc + * @function Script.unloadEntityScript + * @param {Uuid} entityID - Entity ID. + * @param {boolean} [shouldRemoveFromMap=false] - Should remove from map. + * @deprecated This function is deprecated and will be removed. + */ + Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID, bool shouldRemoveFromMap = false); // will call unload method + + /**jsdoc + * @function Script.unloadAllEntityScripts + * @param {boolean} [blockingCall=false] - Wait for completion if call moved to another thread. + * @deprecated This function is deprecated and will be removed. + */ + Q_INVOKABLE void unloadAllEntityScripts(bool blockingCall = false); + + /**jsdoc + * Calls a method in an entity script. + * @function Script.callEntityScriptMethod + * @param {Uuid} entityID - The ID of the entity running the entity script. + * @param {string} methodName - The name of the method to call. + * @param {string[]} [parameters=[]] - The parameters to call the specified method with. + * @param {Uuid} [remoteCallerID=Uuid.NULL] - An ID that identifies the caller. + */ + Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, + const QStringList& params = QStringList(), + const QUuid& remoteCallerID = QUuid()) override; + + /**jsdoc + * Calls a method in an entity script. + * @function Script.callEntityScriptMethod + * @param {Uuid} entityID - Entity ID. + * @param {string} methodName - Method name. + * @param {PointerEvent} event - Pointer event. + * @deprecated This function is deprecated and will be removed. + */ + Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const PointerEvent& event); + + /**jsdoc + * Calls a method in an entity script. + * @function Script.callEntityScriptMethod + * @param {Uuid} entityID - Entity ID. + * @param {string} methodName - Method name. + * @param {Uuid} otherID - Other entity ID. + * @param {Collision} collision - Collision. + * @deprecated This function is deprecated and will be removed. + */ + Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision); + + /**jsdoc + * @function Script.generateUUID + * @returns {Uuid} A new UUID. + * @deprecated This function is deprecated and will be removed. Use {@link Uuid(0).generate|Uuid.generate} instead. + */ + Q_INVOKABLE QUuid generateUUID() { return QUuid::createUuid(); } + + void setType(Type type) { _type = type; }; + Type getType() { return _type; }; + QString getTypeAsString() const; + + bool isFinished() const { return _isFinished; } // used by Application and ScriptWidget + bool isRunning() const { return _isRunning; } // used by ScriptWidget + + // this is used by code in ScriptEngines.cpp during the "reload all" operation + bool isStopping() const { return _isStopping; } + + bool isDebuggable() const { return _debuggable; } + + void disconnectNonEssentialSignals(); + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // These are currently used by Application to track if a script is user loaded or not. Consider finding a solution + // inside of Application so that the ScriptManager class is not polluted by this notion + void setUserLoaded(bool isUserLoaded) { _isUserLoaded = isUserLoaded; } + bool isUserLoaded() const { return _isUserLoaded; } + + void setQuitWhenFinished(const bool quitWhenFinished) { _quitWhenFinished = quitWhenFinished; } + bool isQuitWhenFinished() const { return _quitWhenFinished; } + + void setEmitScriptUpdatesFunction(std::function func) { _emitScriptUpdates = func; } + + void scriptErrorMessage(const QString& message); + void scriptWarningMessage(const QString& message); + void scriptInfoMessage(const QString& message); + void scriptPrintedMessage(const QString& message); + void clearDebugLogWindow(); + int getNumRunningEntityScripts() const; + bool getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const; + bool hasEntityScriptDetails(const EntityItemID& entityID) const; + + void setScriptEngines(QSharedPointer& scriptEngines) { _scriptEngines = scriptEngines; } + + /**jsdoc + * Gets the URL for an asset in an external resource bucket. (The location where the bucket is hosted may change over time + * but this method will return the asset's current URL.) + * @function Script.getExternalPath + * @param {Script.ResourceBucket} bucket - The external resource bucket that the asset is in. + * @param {string} path - The path within the external resource bucket where the asset is located. + *

Normally, this should start with a path or filename to be appended to the bucket URL. + * Alternatively, it can be a relative path starting with ./ or ../, to navigate within the + * resource bucket's URL.

+ * @Returns {string} The URL of an external asset. + * @example Report the URL of a default particle. + * print(Script.getExternalPath(Script.ExternalPaths.Assets, "Bazaar/Assets/Textures/Defaults/Interface/default_particle.png")); + * @example Report the root directory where the Vircadia assets are located. + * print(Script.getExternalPath(Script.ExternalPaths.Assets, ".")); + */ + Q_INVOKABLE QString getExternalPath(ExternalResource::Bucket bucket, const QString& path); + +public slots: + + /**jsdoc + * @function Script.callAnimationStateHandler + * @param {function} callback - Callback function. + * @param {object} parameters - Parameters. + * @param {string[]} names - Names. + * @param {boolean} useNames - Use names. + * @param {function} resultHandler - Result handler. + * @deprecated This function is deprecated and will be removed. + */ + void callAnimationStateHandler(ScriptValuePointer callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler); + + /**jsdoc + * @function Script.updateMemoryCost + * @param {number} deltaSize - Delta size. + * @deprecated This function is deprecated and will be removed. + */ + void updateMemoryCost(const qint64&); + +signals: + + /**jsdoc + * @function Script.scriptLoaded + * @param {string} filename - File name. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + void scriptLoaded(const QString& scriptFilename); + + /**jsdoc + * @function Script.errorLoadingScript + * @param {string} filename - File name. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + void errorLoadingScript(const QString& scriptFilename); + + /**jsdoc + * Triggered frequently at a system-determined interval. + * @function Script.update + * @param {number} deltaTime - The time since the last update, in s. + * @returns {Signal} + * @example Report script update intervals. + * Script.update.connect(function (deltaTime) { + * print("Update: " + deltaTime); + * }); + */ + void update(float deltaTime); + + /**jsdoc + * Triggered when the script is stopping. + * @function Script.scriptEnding + * @returns {Signal} + * @example Report when a script is stopping. + * print("Script started"); + * + * Script.scriptEnding.connect(function () { + * print("Script ending"); + * }); + * + * Script.setTimeout(function () { + * print("Stopping script"); + * Script.stop(); + * }, 1000); + */ + void scriptEnding(); + + /**jsdoc + * @function Script.finished + * @param {string} filename - File name. + * @param {object} engine - Engine. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + void finished(const QString& fileNameString, ScriptManagerPointer); + + /**jsdoc + * @function Script.cleanupMenuItem + * @param {string} menuItem - Menu item. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + void cleanupMenuItem(const QString& menuItemString); + + /**jsdoc + * Triggered when the script prints a message to the program log via {@link print}, {@link Script.print}, + * {@link console.log}, {@link console.debug}, {@link console.group}, {@link console.groupEnd}, {@link console.time}, or + * {@link console.timeEnd}. + * @function Script.printedMessage + * @param {string} message - The message. + * @param {string} scriptName - The name of the script that generated the message. + * @returns {Signal} + */ + void printedMessage(const QString& message, const QString& scriptName); + + /**jsdoc + * Triggered when the script generates an error, {@link console.error} or {@link console.exception} is called, or + * {@link console.assert} is called and fails. + * @function Script.errorMessage + * @param {string} message - The error message. + * @param {string} scriptName - The name of the script that generated the error message. + * @returns {Signal} + */ + void errorMessage(const QString& message, const QString& scriptName); + + /**jsdoc + * Triggered when the script generates a warning or {@link console.warn} is called. + * @function Script.warningMessage + * @param {string} message - The warning message. + * @param {string} scriptName - The name of the script that generated the warning message. + * @returns {Signal} + */ + void warningMessage(const QString& message, const QString& scriptName); + + /**jsdoc + * Triggered when the script generates an information message or {@link console.info} is called. + * @function Script.infoMessage + * @param {string} message - The information message. + * @param {string} scriptName - The name of the script that generated the information message. + * @returns {Signal} + */ + void infoMessage(const QString& message, const QString& scriptName); + + /**jsdoc + * Triggered when the running state of the script changes, e.g., from running to stopping. + * @function Script.runningStateChanged + * @returns {Signal} + */ + void runningStateChanged(); + + /**jsdoc + * @function Script.clearDebugWindow + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + void clearDebugWindow(); + + /**jsdoc + * @function Script.loadScript + * @param {string} scriptName - Script name. + * @param {boolean} isUserLoaded - Is user loaded. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + void loadScript(const QString& scriptName, bool isUserLoaded); + + /**jsdoc + * @function Script.reloadScript + * @param {string} scriptName - Script name. + * @param {boolean} isUserLoaded - Is user loaded. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + void reloadScript(const QString& scriptName, bool isUserLoaded); + + /**jsdoc + * Triggered when the script has stopped. + * @function Script.doneRunning + * @returns {Signal} + */ + void doneRunning(); + + /**jsdoc + * @function Script.entityScriptDetailsUpdated + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + // Emitted when an entity script is added or removed, or when the status of an entity + // script is updated (goes from RUNNING to ERROR_RUNNING_SCRIPT, for example) + void entityScriptDetailsUpdated(); + + /**jsdoc + * Triggered when the script starts for the user. See also, {@link Entities.preload}. + *

Supported Script Types: Client Entity Scripts • Server Entity Scripts

+ * @function Script.entityScriptPreloadFinished + * @param {Uuid} entityID - The ID of the entity that the script is running in. + * @returns {Signal} + * @example Get the ID of the entity that a client entity script is running in. + * var entityScript = function () { + * this.entityID = Uuid.NULL; + * }; + * + * Script.entityScriptPreloadFinished.connect(function (entityID) { + * this.entityID = entityID; + * print("Entity ID: " + this.entityID); + * }); + * + * var entityID = Entities.addEntity({ + * type: "Box", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * dimensions: { x: 0.5, y: 0.5, z: 0.5 }, + * color: { red: 255, green: 0, blue: 0 }, + * script: "(" + entityScript + ")", // Could host the script on a Web server instead. + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + // Emitted when an entity script has finished running preload + void entityScriptPreloadFinished(const EntityItemID& entityID); + + /**jsdoc + * Triggered when a script generates an unhandled exception. + * @function Script.unhandledException + * @param {object} exception - The details of the exception. + * @returns {Signal} + * @example Report the details of an unhandled exception. + * Script.unhandledException.connect(function (exception) { + * print("Unhandled exception: " + JSON.stringify(exception)); + * }); + * var properties = JSON.parse("{ x: 1"); // Invalid JSON string. + */ + void unhandledException(const ScriptValuePointer& exception); + +protected: + void init(); + + /**jsdoc + * @function Script.executeOnScriptThread + * @param {function} function - Function. + * @param {ConnectionType} [type=2] - Connection type. + * @deprecated This function is deprecated and will be removed. + */ + Q_INVOKABLE void executeOnScriptThread(std::function function, const Qt::ConnectionType& type = Qt::QueuedConnection ); + + /**jsdoc + * @function Script._requireResolve + * @param {string} module - Module. + * @param {string} [relativeTo=""] - Relative to. + * @returns {string} Result. + * @deprecated This function is deprecated and will be removed. + */ + // note: this is not meant to be called directly, but just to have QMetaObject take care of wiring it up in general; + // then inside of init() we just have to do "Script.require.resolve = Script._requireResolve;" + Q_INVOKABLE QString _requireResolve(const QString& moduleId, const QString& relativeTo = QString()); + + QString logException(const ScriptValuePointer& exception); + void timerFired(); + void stopAllTimers(); + void stopAllTimersForEntityScript(const EntityItemID& entityID); + void refreshFileScript(const EntityItemID& entityID); + void updateEntityScriptStatus(const EntityItemID& entityID, const EntityScriptStatus& status, const QString& errorInfo = QString()); + void setEntityScriptDetails(const EntityItemID& entityID, const EntityScriptDetails& details); + void setParentURL(const QString& parentURL) { _parentURL = parentURL; } + + QObject* setupTimerWithInterval(const ScriptValuePointer& function, int intervalMS, bool isSingleShot); + void stopTimer(QTimer* timer); + + QHash _registeredHandlers; + void forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, ScriptValueList eventHanderArgs); + + /**jsdoc + * @function Script.entityScriptContentAvailable + * @param {Uuid} entityID - Entity ID. + * @param {string} scriptOrURL - Path. + * @param {string} contents - Contents. + * @param {boolean} isURL - Is a URL. + * @param {boolean} success - Success. + * @param {string} status - Status. + * @deprecated This function is deprecated and will be removed. + */ + Q_INVOKABLE void entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success, const QString& status); + + EntityItemID currentEntityIdentifier; // Contains the defining entity script entity id during execution, if any. Empty for interface script execution. + QUrl currentSandboxURL; // The toplevel url string for the entity script that loaded the code being executed, else empty. + void doWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, std::function operation); + void callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, ScriptValuePointer function, ScriptValuePointer thisObject, ScriptValueList args); + + Context _context; + Type _type; + QString _scriptContents; + QString _parentURL; + std::atomic _isFinished { false }; + std::atomic _isRunning { false }; + std::atomic _isStopping { false }; + bool _isInitialized { false }; + QHash _timerFunctionMap; + QSet _includedURLs; + mutable QReadWriteLock _entityScriptsLock { QReadWriteLock::Recursive }; + QHash _entityScripts; + EntityScriptContentAvailableMap _contentAvailableQueue; + + bool _isThreaded { false }; + QScriptEngineDebugger* _debugger { nullptr }; + bool _debuggable { false }; + qint64 _lastUpdate; + + QString _fileNameString; + Quat _quatLibrary; + Vec3 _vec3Library; + Mat4 _mat4Library; + ScriptUUID _uuidLibrary; + ConsoleScriptingInterface _consoleScriptingInterface; + std::atomic _isUserLoaded { false }; + bool _isReloading { false }; + + std::atomic _quitWhenFinished; + + AssetScriptingInterface* _assetScriptingInterface; + + std::function _emitScriptUpdates{ []() { return true; } }; + + std::recursive_mutex _lock; + + std::chrono::microseconds _totalTimerExecution { 0 }; + + static const QString _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS; + + Setting::Handle _enableExtendedJSExceptions { _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS, true }; + + QWeakPointer _scriptEngines; +}; + +ScriptManagerPointer scriptManagerFactory(ScriptManager::Context context, + const QString& scriptContents, + const QString& fileNameString); + +#endif // hifi_ScriptManager_h diff --git a/libraries/script-engine/src/ScriptProgram.h b/libraries/script-engine/src/ScriptProgram.h new file mode 100644 index 00000000000..caf9e43a84c --- /dev/null +++ b/libraries/script-engine/src/ScriptProgram.h @@ -0,0 +1,24 @@ +// +// ScriptProgram.h +// libraries/script-engine/src +// +// Created by Heather Anderson on 5/2/21. +// Copyright 2021 Vircadia contributors. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ScriptProgram_h +#define hifi_ScriptProgram_h + +#include + +class ScriptProgram; +using ScriptProgramPointer = QSharedPointer; + +class ScriptProgram { +public: +}; + +#endif // hifi_ScriptProgram_h \ No newline at end of file diff --git a/libraries/script-engine/src/ScriptUUID.cpp b/libraries/script-engine/src/ScriptUUID.cpp index f88803c87c3..b9661e31eda 100644 --- a/libraries/script-engine/src/ScriptUUID.cpp +++ b/libraries/script-engine/src/ScriptUUID.cpp @@ -17,6 +17,7 @@ #include "ScriptEngineLogging.h" #include "ScriptEngine.h" +#include "ScriptManager.h" QUuid ScriptUUID::fromString(const QString& s) { return QUuid(s); @@ -42,7 +43,7 @@ void ScriptUUID::print(const QString& label, const QUuid& id) { QString message = QString("%1 %2").arg(qPrintable(label)); message = message.arg(id.toString()); qCDebug(scriptengine) << message; - if (ScriptEngine* scriptEngine = qobject_cast(engine())) { - scriptEngine->print(message); + if (ScriptManager* scriptManager = engine()->manager()) { + scriptManager->print(message); } } diff --git a/libraries/script-engine/src/ScriptUUID.h b/libraries/script-engine/src/ScriptUUID.h index b667447aaa1..5bfde9131d3 100644 --- a/libraries/script-engine/src/ScriptUUID.h +++ b/libraries/script-engine/src/ScriptUUID.h @@ -16,7 +16,8 @@ #include #include -#include + +#include "Scriptable.h" /**jsdoc * The Uuid API provides facilities for working with UUIDs. @@ -34,7 +35,7 @@ */ /// Scriptable interface for a UUID helper class object. Used exclusively in the JavaScript API -class ScriptUUID : public QObject, protected QScriptable { +class ScriptUUID : public QObject, protected Scriptable { Q_OBJECT Q_PROPERTY(QString NULL READ NULL_UUID CONSTANT) // String for use in scripts. diff --git a/libraries/script-engine/src/ScriptValue.h b/libraries/script-engine/src/ScriptValue.h new file mode 100644 index 00000000000..fdf8b9a5e26 --- /dev/null +++ b/libraries/script-engine/src/ScriptValue.h @@ -0,0 +1,185 @@ +// +// ScriptValue.h +// libraries/script-engine/src +// +// Created by Heather Anderson on 4/25/21. +// Copyright 2021 Vircadia contributors. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ScriptValue_h +#define hifi_ScriptValue_h + +#include +#include +#include +#include + +class QObject; +class ScriptEngine; +class ScriptValue; +class ScriptValueIterator; +using ScriptEnginePointer = QSharedPointer; +using ScriptValuePointer = QSharedPointer; +using ScriptValueList = QList; +using ScriptValueIteratorPointer = QSharedPointer; + +class ScriptValue { +public: + enum ResolveFlag + { + ResolveLocal = 0, + ResolvePrototype = 1, + }; + using ResolveFlags = QFlags; + + enum PropertyFlag + { + ReadOnly = 0x00000001, + Undeletable = 0x00000002, + SkipInEnumeration = 0x00000004, + PropertyGetter = 0x00000008, + PropertySetter = 0x00000010, + KeepExistingFlags = 0x00000800, + }; + Q_DECLARE_FLAGS(PropertyFlags, PropertyFlag); + +public: + virtual ScriptValuePointer call(const ScriptValuePointer& thisObject = ScriptValuePointer(), + const ScriptValueList& args = ScriptValueList()) = 0; + virtual ScriptValuePointer call(const ScriptValuePointer& thisObject, const ScriptValuePointer& arguments) = 0; + virtual ScriptEnginePointer engine() const = 0; + inline bool isArray() const; + inline bool isBool() const; + inline bool isError() const; + inline bool isFunction() const; + inline bool isNumber() const; + inline bool isNull() const; + inline bool isObject() const; + inline bool isString() const; + inline bool isUndefined() const; + inline bool isValid() const; + virtual ScriptValueIteratorPointer newIterator() = 0; + virtual ScriptValuePointer property(const QString& name, const ResolveFlags& mode = ResolvePrototype) const = 0; + virtual ScriptValuePointer property(quint32 arrayIndex, const ResolveFlags& mode = ResolvePrototype) const = 0; + virtual void setProperty(const QString& name, + const ScriptValuePointer& value, + const PropertyFlags& flags = KeepExistingFlags) = 0; + virtual void setProperty(quint32 arrayIndex, + const ScriptValuePointer& value, + const PropertyFlags& flags = KeepExistingFlags) = 0; + template void setProperty(const QString& name, const TYP& value, + const PropertyFlags& flags = KeepExistingFlags); + template void setProperty(quint32 arrayIndex, const TYP& value, + const PropertyFlags& flags = KeepExistingFlags); + virtual void setPrototype(const ScriptValuePointer& prototype) = 0; + + virtual bool toBool() const = 0; + virtual qint32 toInt32() const = 0; + virtual double toInteger() const = 0; + virtual double toNumber() const = 0; + virtual QString toString() const = 0; + virtual quint16 toUInt16() const = 0; + virtual quint32 toUInt32() const = 0; + virtual QVariant toVariant() const = 0; + virtual QObject* toQObject() const = 0; + +protected: + virtual bool isArrayInternal() const = 0; + virtual bool isBoolInternal() const = 0; + virtual bool isErrorInternal() const = 0; + virtual bool isFunctionInternal() const = 0; + virtual bool isNumberInternal() const = 0; + virtual bool isNullInternal() const = 0; + virtual bool isObjectInternal() const = 0; + virtual bool isStringInternal() const = 0; + virtual bool isUndefinedInternal() const = 0; + virtual bool isValidInternal() const = 0; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(ScriptValue::PropertyFlags) + +bool ScriptValue::isArray() const { + assert(this != NULL); + if (this == NULL) return false; + return isArrayInternal(); +} + +bool ScriptValue::isBool() const { + assert(this != NULL); + if (this == NULL) + return false; + return isBoolInternal(); +} + +bool ScriptValue::isError() const { + assert(this != NULL); + if (this == NULL) + return false; + return isErrorInternal(); +} + +bool ScriptValue::isFunction() const { + assert(this != NULL); + if (this == NULL) + return false; + return isFunctionInternal(); +} + +bool ScriptValue::isNumber() const { + assert(this != NULL); + if (this == NULL) + return false; + return isNumberInternal(); +} + +bool ScriptValue::isNull() const { + assert(this != NULL); + if (this == NULL) + return false; + return isNullInternal(); +} + +bool ScriptValue::isObject() const { + assert(this != NULL); + if (this == NULL) + return false; + return isObjectInternal(); +} + +bool ScriptValue::isString() const { + assert(this != NULL); + if (this == NULL) + return false; + return isStringInternal(); +} + +bool ScriptValue::isUndefined() const { + assert(this != NULL); + if (this == NULL) + return true; + return isUndefinedInternal(); +} + +bool ScriptValue::isValid() const { + assert(this != NULL); + if (this == NULL) + return false; + return isValidInternal(); +} + +template +void ScriptValue::setProperty(const QString& name, const TYP& value, const PropertyFlags& flags) { + setProperty(name, engine()->newValue(value), flags); +} + +template +void ScriptValue::setProperty(quint32 arrayIndex, const TYP& value, const PropertyFlags& flags) { + setProperty(arrayIndex, engine()->newValue(value), flags); +} + +template +T scriptvalue_cast(const ScriptValuePointer& value); + +#endif // hifi_ScriptValue_h \ No newline at end of file diff --git a/libraries/script-engine/src/ScriptValueIterator.h b/libraries/script-engine/src/ScriptValueIterator.h new file mode 100644 index 00000000000..f0390005100 --- /dev/null +++ b/libraries/script-engine/src/ScriptValueIterator.h @@ -0,0 +1,31 @@ +// +// ScriptValueIterator.h +// libraries/script-engine/src +// +// Created by Heather Anderson on 5/2/21. +// Copyright 2021 Vircadia contributors. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ScriptValueIterator_h +#define hifi_ScriptValueIterator_h + +#include +#include + +class ScriptValue; +class ScriptValueIterator; +using ScriptValuePointer = QSharedPointer; +using ScriptValueIteratorPointer = QSharedPointer; + +class ScriptValueIterator { +public: + virtual bool hasNext() const = 0; + virtual QString name() const = 0; + virtual void next() = 0; + virtual ScriptValuePointer value() const = 0; +}; + +#endif // hifi_ScriptValueIterator_h \ No newline at end of file diff --git a/libraries/script-engine/src/ScriptValueUtils.cpp b/libraries/script-engine/src/ScriptValueUtils.cpp new file mode 100644 index 00000000000..352cce4f6d3 --- /dev/null +++ b/libraries/script-engine/src/ScriptValueUtils.cpp @@ -0,0 +1,888 @@ +// +// ScriptValueUtils.cpp +// libraries/shared/src +// +// Created by Anthony Thibault on 4/15/16. +// Copyright 2016 High Fidelity, Inc. +// +// Utilities for working with QtScriptValues +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ScriptValueUtils.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ScriptEngine.h" +#include "ScriptEngineCast.h" +#include "ScriptValueIterator.h" + +bool isListOfStrings(const ScriptValuePointer& arg) { + if (!arg->isArray()) { + return false; + } + + auto lengthProperty = arg->property("length"); + if (!lengthProperty->isNumber()) { + return false; + } + + int length = lengthProperty->toInt32(); + for (int i = 0; i < length; i++) { + if (!arg->property(i)->isString()) { + return false; + } + } + + return true; +} + +void registerMetaTypes(ScriptEngine* engine) { + scriptRegisterMetaType(engine, vec2ToScriptValue, vec2FromScriptValue); + scriptRegisterMetaType(engine, vec3ToScriptValue, vec3FromScriptValue); + scriptRegisterMetaType(engine, u8vec3ToScriptValue, u8vec3FromScriptValue); + scriptRegisterMetaType(engine, vec4toScriptValue, vec4FromScriptValue); + scriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue); + scriptRegisterMetaType(engine, mat4toScriptValue, mat4FromScriptValue); + + scriptRegisterMetaType(engine, qVectorVec3ToScriptValue, qVectorVec3FromScriptValue); + scriptRegisterMetaType(engine, qVectorQuatToScriptValue, qVectorQuatFromScriptValue); + scriptRegisterMetaType(engine, qVectorBoolToScriptValue, qVectorBoolFromScriptValue); + scriptRegisterMetaType(engine, qVectorFloatToScriptValue, qVectorFloatFromScriptValue); + scriptRegisterMetaType(engine, qVectorIntToScriptValue, qVectorIntFromScriptValue); + scriptRegisterMetaType(engine, qVectorQUuidToScriptValue, qVectorQUuidFromScriptValue); + + scriptRegisterMetaType(engine, qSizeFToScriptValue, qSizeFFromScriptValue); + scriptRegisterMetaType(engine, qRectToScriptValue, qRectFromScriptValue); + scriptRegisterMetaType(engine, qURLToScriptValue, qURLFromScriptValue); + scriptRegisterMetaType(engine, qColorToScriptValue, qColorFromScriptValue); + + scriptRegisterMetaType(engine, pickRayToScriptValue, pickRayFromScriptValue); + scriptRegisterMetaType(engine, collisionToScriptValue, collisionFromScriptValue); + scriptRegisterMetaType(engine, quuidToScriptValue, quuidFromScriptValue); + scriptRegisterMetaType(engine, aaCubeToScriptValue, aaCubeFromScriptValue); + + scriptRegisterMetaType(engine, stencilMaskModeToScriptValue, stencilMaskModeFromScriptValue); + + scriptRegisterMetaType(engine, promiseToScriptValue, promiseFromScriptValue); + + scriptRegisterSequenceMetaType>(engine); +} + +ScriptValuePointer vec2ToScriptValue(ScriptEngine* engine, const glm::vec2& vec2) { + auto prototype = engine->globalObject()->property("__hifi_vec2__"); + if (!prototype->property("defined")->toBool()) { + prototype = engine->evaluate( + "__hifi_vec2__ = Object.defineProperties({}, { " + "defined: { value: true }," + "0: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "1: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "u: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "v: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }" + "})"); + } + ScriptValuePointer value = engine->newObject(); + value->setProperty("x", vec2.x); + value->setProperty("y", vec2.y); + value->setPrototype(prototype); + return value; +} + +void vec2FromScriptValue(const ScriptValuePointer& object, glm::vec2& vec2) { + if (object->isNumber()) { + vec2 = glm::vec2(object->toVariant().toFloat()); + } else if (object->isArray()) { + QVariantList list = object->toVariant().toList(); + if (list.length() == 2) { + vec2.x = list[0].toFloat(); + vec2.y = list[1].toFloat(); + } + } else { + ScriptValuePointer x = object->property("x"); + if (!x->isValid()) { + x = object->property("u"); + } + + ScriptValuePointer y = object->property("y"); + if (!y->isValid()) { + y = object->property("v"); + } + + vec2.x = x->toVariant().toFloat(); + vec2.y = y->toVariant().toFloat(); + } +} + +ScriptValuePointer vec3ToScriptValue(ScriptEngine* engine, const glm::vec3& vec3) { + auto prototype = engine->globalObject()->property("__hifi_vec3__"); + if (!prototype->property("defined")->toBool()) { + prototype = engine->evaluate( + "__hifi_vec3__ = Object.defineProperties({}, { " + "defined: { value: true }," + "0: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "1: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "2: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }," + "r: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "g: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "b: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }," + "red: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "green: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "blue: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }" + "})"); + } + ScriptValuePointer value = engine->newObject(); + value->setProperty("x", vec3.x); + value->setProperty("y", vec3.y); + value->setProperty("z", vec3.z); + value->setPrototype(prototype); + return value; +} + +ScriptValuePointer vec3ColorToScriptValue(ScriptEngine* engine, const glm::vec3& vec3) { + auto prototype = engine->globalObject()->property("__hifi_vec3_color__"); + if (!prototype->property("defined")->toBool()) { + prototype = engine->evaluate( + "__hifi_vec3_color__ = Object.defineProperties({}, { " + "defined: { value: true }," + "0: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," + "1: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," + "2: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }," + "r: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," + "g: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," + "b: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }," + "x: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," + "y: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," + "z: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }" + "})"); + } + ScriptValuePointer value = engine->newObject(); + value->setProperty("red", vec3.x); + value->setProperty("green", vec3.y); + value->setProperty("blue", vec3.z); + value->setPrototype(prototype); + return value; +} + +void vec3FromScriptValue(const ScriptValuePointer& object, glm::vec3& vec3) { + if (object->isNumber()) { + vec3 = glm::vec3(object->toVariant().toFloat()); + } else if (object->isString()) { + QColor qColor(object->toString()); + if (qColor.isValid()) { + vec3.x = qColor.red(); + vec3.y = qColor.green(); + vec3.z = qColor.blue(); + } + } else if (object->isArray()) { + QVariantList list = object->toVariant().toList(); + if (list.length() == 3) { + vec3.x = list[0].toFloat(); + vec3.y = list[1].toFloat(); + vec3.z = list[2].toFloat(); + } + } else { + ScriptValuePointer x = object->property("x"); + if (!x->isValid()) { + x = object->property("r"); + } + if (!x->isValid()) { + x = object->property("red"); + } + + ScriptValuePointer y = object->property("y"); + if (!y->isValid()) { + y = object->property("g"); + } + if (!y->isValid()) { + y = object->property("green"); + } + + ScriptValuePointer z = object->property("z"); + if (!z->isValid()) { + z = object->property("b"); + } + if (!z->isValid()) { + z = object->property("blue"); + } + + vec3.x = x->toVariant().toFloat(); + vec3.y = y->toVariant().toFloat(); + vec3.z = z->toVariant().toFloat(); + } +} + +ScriptValuePointer u8vec3ToScriptValue(ScriptEngine* engine, const glm::u8vec3& vec3) { + auto prototype = engine->globalObject()->property("__hifi_u8vec3__"); + if (!prototype->property("defined")->toBool()) { + prototype = engine->evaluate( + "__hifi_u8vec3__ = Object.defineProperties({}, { " + "defined: { value: true }," + "0: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "1: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "2: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }," + "r: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "g: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "b: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }," + "red: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "green: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "blue: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }" + "})"); + } + ScriptValuePointer value = engine->newObject(); + value->setProperty("x", vec3.x); + value->setProperty("y", vec3.y); + value->setProperty("z", vec3.z); + value->setPrototype(prototype); + return value; +} + +ScriptValuePointer u8vec3ColorToScriptValue(ScriptEngine* engine, const glm::u8vec3& vec3) { + auto prototype = engine->globalObject()->property("__hifi_u8vec3_color__"); + if (!prototype->property("defined")->toBool()) { + prototype = engine->evaluate( + "__hifi_u8vec3_color__ = Object.defineProperties({}, { " + "defined: { value: true }," + "0: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," + "1: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," + "2: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }," + "r: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," + "g: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," + "b: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }," + "x: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," + "y: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," + "z: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }" + "})"); + } + ScriptValuePointer value = engine->newObject(); + value->setProperty("red", vec3.x); + value->setProperty("green", vec3.y); + value->setProperty("blue", vec3.z); + value->setPrototype(prototype); + return value; +} + +void u8vec3FromScriptValue(const ScriptValuePointer& object, glm::u8vec3& vec3) { + if (object->isNumber()) { + vec3 = glm::vec3(object->toVariant().toUInt()); + } else if (object->isString()) { + QColor qColor(object->toString()); + if (qColor.isValid()) { + vec3.x = (uint8_t)qColor.red(); + vec3.y = (uint8_t)qColor.green(); + vec3.z = (uint8_t)qColor.blue(); + } + } else if (object->isArray()) { + QVariantList list = object->toVariant().toList(); + if (list.length() == 3) { + vec3.x = list[0].toUInt(); + vec3.y = list[1].toUInt(); + vec3.z = list[2].toUInt(); + } + } else { + ScriptValuePointer x = object->property("x"); + if (!x->isValid()) { + x = object->property("r"); + } + if (!x->isValid()) { + x = object->property("red"); + } + + ScriptValuePointer y = object->property("y"); + if (!y->isValid()) { + y = object->property("g"); + } + if (!y->isValid()) { + y = object->property("green"); + } + + ScriptValuePointer z = object->property("z"); + if (!z->isValid()) { + z = object->property("b"); + } + if (!z->isValid()) { + z = object->property("blue"); + } + + vec3.x = x->toVariant().toUInt(); + vec3.y = y->toVariant().toUInt(); + vec3.z = z->toVariant().toUInt(); + } +} + +ScriptValuePointer vec4toScriptValue(ScriptEngine* engine, const glm::vec4& vec4) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty("x", vec4.x); + obj->setProperty("y", vec4.y); + obj->setProperty("z", vec4.z); + obj->setProperty("w", vec4.w); + return obj; +} + +void vec4FromScriptValue(const ScriptValuePointer& object, glm::vec4& vec4) { + vec4.x = object->property("x")->toVariant().toFloat(); + vec4.y = object->property("y")->toVariant().toFloat(); + vec4.z = object->property("z")->toVariant().toFloat(); + vec4.w = object->property("w")->toVariant().toFloat(); +} + +ScriptValuePointer mat4toScriptValue(ScriptEngine* engine, const glm::mat4& mat4) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty("r0c0", mat4[0][0]); + obj->setProperty("r1c0", mat4[0][1]); + obj->setProperty("r2c0", mat4[0][2]); + obj->setProperty("r3c0", mat4[0][3]); + obj->setProperty("r0c1", mat4[1][0]); + obj->setProperty("r1c1", mat4[1][1]); + obj->setProperty("r2c1", mat4[1][2]); + obj->setProperty("r3c1", mat4[1][3]); + obj->setProperty("r0c2", mat4[2][0]); + obj->setProperty("r1c2", mat4[2][1]); + obj->setProperty("r2c2", mat4[2][2]); + obj->setProperty("r3c2", mat4[2][3]); + obj->setProperty("r0c3", mat4[3][0]); + obj->setProperty("r1c3", mat4[3][1]); + obj->setProperty("r2c3", mat4[3][2]); + obj->setProperty("r3c3", mat4[3][3]); + return obj; +} + +void mat4FromScriptValue(const ScriptValuePointer& object, glm::mat4& mat4) { + mat4[0][0] = object->property("r0c0")->toVariant().toFloat(); + mat4[0][1] = object->property("r1c0")->toVariant().toFloat(); + mat4[0][2] = object->property("r2c0")->toVariant().toFloat(); + mat4[0][3] = object->property("r3c0")->toVariant().toFloat(); + mat4[1][0] = object->property("r0c1")->toVariant().toFloat(); + mat4[1][1] = object->property("r1c1")->toVariant().toFloat(); + mat4[1][2] = object->property("r2c1")->toVariant().toFloat(); + mat4[1][3] = object->property("r3c1")->toVariant().toFloat(); + mat4[2][0] = object->property("r0c2")->toVariant().toFloat(); + mat4[2][1] = object->property("r1c2")->toVariant().toFloat(); + mat4[2][2] = object->property("r2c2")->toVariant().toFloat(); + mat4[2][3] = object->property("r3c2")->toVariant().toFloat(); + mat4[3][0] = object->property("r0c3")->toVariant().toFloat(); + mat4[3][1] = object->property("r1c3")->toVariant().toFloat(); + mat4[3][2] = object->property("r2c3")->toVariant().toFloat(); + mat4[3][3] = object->property("r3c3")->toVariant().toFloat(); +} + +ScriptValuePointer qVectorVec3ColorToScriptValue(ScriptEngine* engine, const QVector& vector) { + ScriptValuePointer array = engine->newArray(); + for (int i = 0; i < vector.size(); i++) { + array->setProperty(i, vec3ColorToScriptValue(engine, vector.at(i))); + } + return array; +} + +ScriptValuePointer qVectorVec3ToScriptValue(ScriptEngine* engine, const QVector& vector) { + ScriptValuePointer array = engine->newArray(); + for (int i = 0; i < vector.size(); i++) { + array->setProperty(i, vec3ToScriptValue(engine, vector.at(i))); + } + return array; +} + +QVector qVectorVec3FromScriptValue(const ScriptValuePointer& array) { + QVector newVector; + int length = array->property("length")->toInteger(); + + for (int i = 0; i < length; i++) { + glm::vec3 newVec3 = glm::vec3(); + vec3FromScriptValue(array->property(i), newVec3); + newVector << newVec3; + } + return newVector; +} + +void qVectorVec3FromScriptValue(const ScriptValuePointer& array, QVector& vector) { + int length = array->property("length")->toInteger(); + + for (int i = 0; i < length; i++) { + glm::vec3 newVec3 = glm::vec3(); + vec3FromScriptValue(array->property(i), newVec3); + vector << newVec3; + } +} + +ScriptValuePointer quatToScriptValue(ScriptEngine* engine, const glm::quat& quat) { + ScriptValuePointer obj = engine->newObject(); + if (quat.x != quat.x || quat.y != quat.y || quat.z != quat.z || quat.w != quat.w) { + // if quat contains a NaN don't try to convert it + return obj; + } + obj->setProperty("x", quat.x); + obj->setProperty("y", quat.y); + obj->setProperty("z", quat.z); + obj->setProperty("w", quat.w); + return obj; +} + +void quatFromScriptValue(const ScriptValuePointer& object, glm::quat& quat) { + quat.x = object->property("x")->toVariant().toFloat(); + quat.y = object->property("y")->toVariant().toFloat(); + quat.z = object->property("z")->toVariant().toFloat(); + quat.w = object->property("w")->toVariant().toFloat(); + + // enforce normalized quaternion + float length = glm::length(quat); + if (length > FLT_EPSILON) { + quat /= length; + } else { + quat = glm::quat(); + } +} + +ScriptValuePointer qVectorQuatToScriptValue(ScriptEngine* engine, const QVector& vector) { + ScriptValuePointer array = engine->newArray(); + for (int i = 0; i < vector.size(); i++) { + array->setProperty(i, quatToScriptValue(engine, vector.at(i))); + } + return array; +} + +ScriptValuePointer qVectorBoolToScriptValue(ScriptEngine* engine, const QVector& vector) { + ScriptValuePointer array = engine->newArray(); + for (int i = 0; i < vector.size(); i++) { + array->setProperty(i, vector.at(i)); + } + return array; +} + +QVector qVectorFloatFromScriptValue(const ScriptValuePointer& array) { + if (!array->isArray()) { + return QVector(); + } + QVector newVector; + int length = array->property("length")->toInteger(); + newVector.reserve(length); + for (int i = 0; i < length; i++) { + if (array->property(i)->isNumber()) { + newVector << array->property(i)->toNumber(); + } + } + + return newVector; +} + +ScriptValuePointer qVectorQUuidToScriptValue(ScriptEngine* engine, const QVector& vector) { + ScriptValuePointer array = engine->newArray(); + for (int i = 0; i < vector.size(); i++) { + array->setProperty(i, quuidToScriptValue(engine, vector.at(i))); + } + return array; +} + +void qVectorQUuidFromScriptValue(const ScriptValuePointer& array, QVector& vector) { + int length = array->property("length")->toInteger(); + + for (int i = 0; i < length; i++) { + vector << array->property(i)->toVariant().toUuid(); + } +} + +QVector qVectorQUuidFromScriptValue(const ScriptValuePointer& array) { + if (!array->isArray()) { + return QVector(); + } + QVector newVector; + int length = array->property("length")->toInteger(); + newVector.reserve(length); + for (int i = 0; i < length; i++) { + QString uuidAsString = array->property(i)->toString(); + QUuid fromString(uuidAsString); + newVector << fromString; + } + return newVector; +} + +ScriptValuePointer qVectorFloatToScriptValue(ScriptEngine* engine, const QVector& vector) { + ScriptValuePointer array = engine->newArray(); + for (int i = 0; i < vector.size(); i++) { + float num = vector.at(i); + array->setProperty(i, engine->newValue(num)); + } + return array; +} + +ScriptValuePointer qVectorIntToScriptValue(ScriptEngine* engine, const QVector& vector) { + ScriptValuePointer array = engine->newArray(); + for (int i = 0; i < vector.size(); i++) { + int num = vector.at(i); + array->setProperty(i, engine->newValue(num)); + } + return array; +} + +void qVectorFloatFromScriptValue(const ScriptValuePointer& array, QVector& vector) { + int length = array->property("length")->toInteger(); + + for (int i = 0; i < length; i++) { + vector << array->property(i)->toVariant().toFloat(); + } +} + +void qVectorIntFromScriptValue(const ScriptValuePointer& array, QVector& vector) { + int length = array->property("length")->toInteger(); + + for (int i = 0; i < length; i++) { + vector << array->property(i)->toVariant().toInt(); + } +} + +QVector qVectorQuatFromScriptValue(const ScriptValuePointer& array) { + QVector newVector; + int length = array->property("length")->toInteger(); + + for (int i = 0; i < length; i++) { + glm::quat newQuat = glm::quat(); + quatFromScriptValue(array->property(i), newQuat); + newVector << newQuat; + } + return newVector; +} + +void qVectorQuatFromScriptValue(const ScriptValuePointer& array, QVector& vector) { + int length = array->property("length")->toInteger(); + + for (int i = 0; i < length; i++) { + glm::quat newQuat = glm::quat(); + quatFromScriptValue(array->property(i), newQuat); + vector << newQuat; + } +} + +QVector qVectorBoolFromScriptValue(const ScriptValuePointer& array) { + QVector newVector; + int length = array->property("length")->toInteger(); + + for (int i = 0; i < length; i++) { + newVector << array->property(i)->toBool(); + } + return newVector; +} + +void qVectorBoolFromScriptValue(const ScriptValuePointer& array, QVector& vector) { + int length = array->property("length")->toInteger(); + + for (int i = 0; i < length; i++) { + vector << array->property(i)->toBool(); + } +} + +ScriptValuePointer qRectToScriptValue(ScriptEngine* engine, const QRect& rect) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty("x", rect.x()); + obj->setProperty("y", rect.y()); + obj->setProperty("width", rect.width()); + obj->setProperty("height", rect.height()); + return obj; +} + +void qRectFromScriptValue(const ScriptValuePointer& object, QRect& rect) { + rect.setX(object->property("x")->toVariant().toInt()); + rect.setY(object->property("y")->toVariant().toInt()); + rect.setWidth(object->property("width")->toVariant().toInt()); + rect.setHeight(object->property("height")->toVariant().toInt()); +} + +ScriptValuePointer qRectFToScriptValue(ScriptEngine* engine, const QRectF& rect) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty("x", rect.x()); + obj->setProperty("y", rect.y()); + obj->setProperty("width", rect.width()); + obj->setProperty("height", rect.height()); + return obj; +} + +void qRectFFromScriptValue(const ScriptValuePointer& object, QRectF& rect) { + rect.setX(object->property("x")->toVariant().toFloat()); + rect.setY(object->property("y")->toVariant().toFloat()); + rect.setWidth(object->property("width")->toVariant().toFloat()); + rect.setHeight(object->property("height")->toVariant().toFloat()); +} + +ScriptValuePointer qColorToScriptValue(ScriptEngine* engine, const QColor& color) { + ScriptValuePointer object = engine->newObject(); + object->setProperty("red", color.red()); + object->setProperty("green", color.green()); + object->setProperty("blue", color.blue()); + object->setProperty("alpha", color.alpha()); + return object; +} + +/**jsdoc + * An axis-aligned cube, defined as the bottom right near (minimum axes values) corner of the cube plus the dimension of its + * sides. + * @typedef {object} AACube + * @property {number} x - X coordinate of the brn corner of the cube. + * @property {number} y - Y coordinate of the brn corner of the cube. + * @property {number} z - Z coordinate of the brn corner of the cube. + * @property {number} scale - The dimensions of each side of the cube. + */ +ScriptValuePointer aaCubeToScriptValue(ScriptEngine* engine, const AACube& aaCube) { + ScriptValuePointer obj = engine->newObject(); + const glm::vec3& corner = aaCube.getCorner(); + obj->setProperty("x", corner.x); + obj->setProperty("y", corner.y); + obj->setProperty("z", corner.z); + obj->setProperty("scale", aaCube.getScale()); + return obj; +} + +void aaCubeFromScriptValue(const ScriptValuePointer& object, AACube& aaCube) { + glm::vec3 corner; + corner.x = object->property("x")->toVariant().toFloat(); + corner.y = object->property("y")->toVariant().toFloat(); + corner.z = object->property("z")->toVariant().toFloat(); + float scale = object->property("scale")->toVariant().toFloat(); + + aaCube.setBox(corner, scale); +} + +void qColorFromScriptValue(const ScriptValuePointer& object, QColor& color) { + if (object->isNumber()) { + color.setRgb(object->toUInt32()); + + } else if (object->isString()) { + color.setNamedColor(object->toString()); + + } else { + ScriptValuePointer alphaValue = object->property("alpha"); + color.setRgb(object->property("red")->toInt32(), object->property("green")->toInt32(), object->property("blue")->toInt32(), + alphaValue->isNumber() ? alphaValue->toInt32() : 255); + } +} + +ScriptValuePointer qURLToScriptValue(ScriptEngine* engine, const QUrl& url) { + return url.toString(); +} + +void qURLFromScriptValue(const ScriptValuePointer& object, QUrl& url) { + url = object->toString(); +} + +ScriptValuePointer pickRayToScriptValue(ScriptEngine* engine, const PickRay& pickRay) { + ScriptValuePointer obj = engine->newObject(); + ScriptValuePointer origin = vec3ToScriptValue(engine, pickRay.origin); + obj->setProperty("origin", origin); + ScriptValuePointer direction = vec3ToScriptValue(engine, pickRay.direction); + obj->setProperty("direction", direction); + return obj; +} + +void pickRayFromScriptValue(const ScriptValuePointer& object, PickRay& pickRay) { + ScriptValuePointer originValue = object->property("origin"); + if (originValue->isValid()) { + auto x = originValue->property("x"); + auto y = originValue->property("y"); + auto z = originValue->property("z"); + if (x->isValid() && y->isValid() && z->isValid()) { + pickRay.origin.x = x->toVariant().toFloat(); + pickRay.origin.y = y->toVariant().toFloat(); + pickRay.origin.z = z->toVariant().toFloat(); + } + } + ScriptValuePointer directionValue = object->property("direction"); + if (directionValue->isValid()) { + auto x = directionValue->property("x"); + auto y = directionValue->property("y"); + auto z = directionValue->property("z"); + if (x->isValid() && y->isValid() && z->isValid()) { + pickRay.direction.x = x->toVariant().toFloat(); + pickRay.direction.y = y->toVariant().toFloat(); + pickRay.direction.z = z->toVariant().toFloat(); + } + } +} + +/**jsdoc + * Details of a collision between avatars and entities. + * @typedef {object} Collision + * @property {ContactEventType} type - The contact type of the collision event. + * @property {Uuid} idA - The ID of one of the avatars or entities in the collision. + * @property {Uuid} idB - The ID of the other of the avatars or entities in the collision. + * @property {Vec3} penetration - The amount of penetration between the two items. + * @property {Vec3} contactPoint - The point of contact. + * @property {Vec3} velocityChange - The change in relative velocity of the two items, in m/s. + */ +ScriptValuePointer collisionToScriptValue(ScriptEngine* engine, const Collision& collision) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty("type", collision.type); + obj->setProperty("idA", quuidToScriptValue(engine, collision.idA)); + obj->setProperty("idB", quuidToScriptValue(engine, collision.idB)); + obj->setProperty("penetration", vec3ToScriptValue(engine, collision.penetration)); + obj->setProperty("contactPoint", vec3ToScriptValue(engine, collision.contactPoint)); + obj->setProperty("velocityChange", vec3ToScriptValue(engine, collision.velocityChange)); + return obj; +} + +void collisionFromScriptValue(const ScriptValuePointer& object, Collision& collision) { + // TODO: implement this when we know what it means to accept collision events from JS +} + +ScriptValuePointer quuidToScriptValue(ScriptEngine* engine, const QUuid& uuid) { + if (uuid.isNull()) { + return engine->nullValue(); + } + ScriptValuePointer obj(uuid.toString()); + return obj; +} + +void quuidFromScriptValue(const ScriptValuePointer& object, QUuid& uuid) { + if (object.isNull()) { + uuid = QUuid(); + return; + } + QString uuidAsString = object->toVariant().toString(); + QUuid fromString(uuidAsString); + uuid = fromString; +} + +/**jsdoc + * A 2D size value. + * @typedef {object} Size + * @property {number} height - The height value. + * @property {number} width - The width value. + */ +ScriptValuePointer qSizeFToScriptValue(ScriptEngine* engine, const QSizeF& qSizeF) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty("width", qSizeF.width()); + obj->setProperty("height", qSizeF.height()); + return obj; +} + +void qSizeFFromScriptValue(const ScriptValuePointer& object, QSizeF& qSizeF) { + qSizeF.setWidth(object->property("width")->toVariant().toFloat()); + qSizeF.setHeight(object->property("height")->toVariant().toFloat()); +} + +/**jsdoc + * The details of an animation that is playing. + * @typedef {object} Avatar.AnimationDetails + * @property {string} role - Not used. + * @property {string} url - The URL to the animation file. Animation files need to be in glTF or FBX format but only need to + * contain the avatar skeleton and animation data. glTF models may be in JSON or binary format (".gltf" or ".glb" URLs + * respectively). + *

Warning: glTF animations currently do not always animate correctly.

+ * @property {number} fps - The frames per second(FPS) rate for the animation playback. 30 FPS is normal speed. + * @property {number} priority - Not used. + * @property {boolean} loop - true if the animation should loop, false if it shouldn't. + * @property {boolean} hold - Not used. + * @property {number} firstFrame - The frame the animation should start at. + * @property {number} lastFrame - The frame the animation should stop at. + * @property {boolean} running - Not used. + * @property {number} currentFrame - The current frame being played. + * @property {boolean} startAutomatically - Not used. + * @property {boolean} allowTranslation - Not used. + */ +ScriptValuePointer animationDetailsToScriptValue(ScriptEngine* engine, const AnimationDetails& details) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty("role", details.role); + obj->setProperty("url", details.url.toString()); + obj->setProperty("fps", details.fps); + obj->setProperty("priority", details.priority); + obj->setProperty("loop", details.loop); + obj->setProperty("hold", details.hold); + obj->setProperty("startAutomatically", details.startAutomatically); + obj->setProperty("firstFrame", details.firstFrame); + obj->setProperty("lastFrame", details.lastFrame); + obj->setProperty("running", details.running); + obj->setProperty("currentFrame", details.currentFrame); + obj->setProperty("allowTranslation", details.allowTranslation); + return obj; +} + +void animationDetailsFromScriptValue(const ScriptValuePointer& object, AnimationDetails& details) { + // nothing for now... +} + +ScriptValuePointer meshToScriptValue(ScriptEngine* engine, MeshProxy* const& in) { + return engine->newQObject(in, ScriptEngine::QtOwnership, + ScriptEngine::ExcludeDeleteLater | ScriptEngine::ExcludeChildObjects); +} + +void meshFromScriptValue(const ScriptValuePointer& value, MeshProxy*& out) { + out = qobject_cast(value->toQObject()); +} + +ScriptValuePointer meshesToScriptValue(ScriptEngine* engine, const MeshProxyList& in) { + // ScriptValueList result; + ScriptValuePointer result = engine->newArray(); + int i = 0; + foreach (MeshProxy* const meshProxy, in) { result->setProperty(i++, meshToScriptValue(engine, meshProxy)); } + return result; +} + +void meshesFromScriptValue(const ScriptValuePointer& value, MeshProxyList& out) { + ScriptValueIteratorPointer itr(value->newIterator()); + + qDebug() << "in meshesFromScriptValue, value.length =" << value->property("length")->toInt32(); + + while (itr->hasNext()) { + itr->next(); + MeshProxy* meshProxy = scriptvalue_cast(itr->value()); + if (meshProxy) { + out.append(meshProxy); + } else { + qDebug() << "null meshProxy"; + } + } +} + +/**jsdoc + * A triangle in a mesh. + * @typedef {object} MeshFace + * @property {number[]} vertices - The indexes of the three vertices that make up the face. + */ +ScriptValuePointer meshFaceToScriptValue(ScriptEngine* engine, const MeshFace& meshFace) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty("vertices", qVectorIntToScriptValue(engine, meshFace.vertexIndices)); + return obj; +} + +void meshFaceFromScriptValue(const ScriptValuePointer& object, MeshFace& meshFaceResult) { + qVectorIntFromScriptValue(object->property("vertices"), meshFaceResult.vertexIndices); +} + +ScriptValuePointer qVectorMeshFaceToScriptValue(ScriptEngine* engine, const QVector& vector) { + ScriptValuePointer array = engine->newArray(); + for (int i = 0; i < vector.size(); i++) { + array->setProperty(i, meshFaceToScriptValue(engine, vector.at(i))); + } + return array; +} + +void qVectorMeshFaceFromScriptValue(const ScriptValuePointer& array, QVector& result) { + int length = array->property("length")->toInteger(); + result.clear(); + + for (int i = 0; i < length; i++) { + MeshFace meshFace = MeshFace(); + meshFaceFromScriptValue(array->property(i), meshFace); + result << meshFace; + } +} + +ScriptValuePointer stencilMaskModeToScriptValue(ScriptEngine* engine, const StencilMaskMode& stencilMode) { + return engine->newValue((int)stencilMode); +} + +void stencilMaskModeFromScriptValue(const ScriptValuePointer& object, StencilMaskMode& stencilMode) { + stencilMode = StencilMaskMode(object->toVariant().toInt()); +} + +void promiseFromScriptValue(const ScriptValuePointer& object, std::shared_ptr& promise) { + Q_ASSERT(false); +} +ScriptValuePointer promiseToScriptValue(ScriptEngine* engine, const std::shared_ptr& promise) { + return engine->newQObject(promise.get()); +} diff --git a/libraries/script-engine/src/ScriptValueUtils.h b/libraries/script-engine/src/ScriptValueUtils.h new file mode 100644 index 00000000000..f387f6c6d1e --- /dev/null +++ b/libraries/script-engine/src/ScriptValueUtils.h @@ -0,0 +1,262 @@ +// +// ScriptValueUtils.h +// libraries/shared/src +// +// Created by Anthony Thibault on 4/15/16. +// Copyright 2016 High Fidelity, Inc. +// +// Utilities for working with QtScriptValues +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ScriptValueUtils_h +#define hifi_ScriptValueUtils_h + +#include + +#include +#include + +#include "ScriptValue.h" + +bool isListOfStrings(const ScriptValuePointer& value); + + +void registerMetaTypes(ScriptEngine* engine); + +// Mat4 +/**jsdoc + * A 4 x 4 matrix, typically containing a scale, rotation, and translation transform. See also the {@link Mat4(0)|Mat4} object. + * + * @typedef {object} Mat4 + * @property {number} r0c0 - Row 0, column 0 value. + * @property {number} r1c0 - Row 1, column 0 value. + * @property {number} r2c0 - Row 2, column 0 value. + * @property {number} r3c0 - Row 3, column 0 value. + * @property {number} r0c1 - Row 0, column 1 value. + * @property {number} r1c1 - Row 1, column 1 value. + * @property {number} r2c1 - Row 2, column 1 value. + * @property {number} r3c1 - Row 3, column 1 value. + * @property {number} r0c2 - Row 0, column 2 value. + * @property {number} r1c2 - Row 1, column 2 value. + * @property {number} r2c2 - Row 2, column 2 value. + * @property {number} r3c2 - Row 3, column 2 value. + * @property {number} r0c3 - Row 0, column 3 value. + * @property {number} r1c3 - Row 1, column 3 value. + * @property {number} r2c3 - Row 2, column 3 value. + * @property {number} r3c3 - Row 3, column 3 value. + */ +ScriptValuePointer mat4toScriptValue(ScriptEngine* engine, const glm::mat4& mat4); +void mat4FromScriptValue(const ScriptValuePointer& object, glm::mat4& mat4); + +/**jsdoc +* A 2-dimensional vector. +* +* @typedef {object} Vec2 +* @property {number} x - X-coordinate of the vector. Synonyms: u. +* @property {number} y - Y-coordinate of the vector. Synonyms: v. +* @example Vec2s can be set in multiple ways and modified with their aliases, but still stringify in the same way +* Entities.editEntity(, { materialMappingPos: { x: 0.1, y: 0.2 }}); // { x: 0.1, y: 0.2 } +* Entities.editEntity(, { materialMappingPos: { u: 0.3, v: 0.4 }}); // { x: 0.3, y: 0.4 } +* Entities.editEntity(, { materialMappingPos: [0.5, 0.6] }); // { x: 0.5, y: 0.6 } +* Entities.editEntity(, { materialMappingPos: 0.7 }); // { x: 0.7, y: 0.7 } +* var color = Entities.getEntityProperties().materialMappingPos; // { x: 0.7, y: 0.7 } +* color.v = 0.8; // { x: 0.7, y: 0.8 } +*/ +ScriptValuePointer vec2ToScriptValue(ScriptEngine* engine, const glm::vec2& vec2); +void vec2FromScriptValue(const ScriptValuePointer& object, glm::vec2& vec2); + +/**jsdoc +* A 3-dimensional vector. See also the {@link Vec3(0)|Vec3} object. +* +* @typedef {object} Vec3 +* @property {number} x - X-coordinate of the vector. Synonyms: r, red. +* @property {number} y - Y-coordinate of the vector. Synonyms: g, green. +* @property {number} z - Z-coordinate of the vector. Synonyms: b, blue. +* @example Vec3 values can be set in multiple ways and modified with their aliases, but still stringify in the same +* way. +* Entities.editEntity(, { position: { x: 1, y: 2, z: 3 }}); // { x: 1, y: 2, z: 3 } +* Entities.editEntity(, { position: { r: 4, g: 5, b: 6 }}); // { x: 4, y: 5, z: 6 } +* Entities.editEntity(, { position: { red: 7, green: 8, blue: 9 }}); // { x: 7, y: 8, z: 9 } +* Entities.editEntity(, { position: [10, 11, 12] }); // { x: 10, y: 11, z: 12 } +* Entities.editEntity(, { position: 13 }); // { x: 13, y: 13, z: 13 } +* var position = Entities.getEntityProperties().position; // { x: 13, y: 13, z: 13 } +* position.g = 14; // { x: 13, y: 14, z: 13 } +* position.blue = 15; // { x: 13, y: 14, z: 15 } +* Entities.editEntity(, { position: "red"}); // { x: 255, y: 0, z: 0 } +* Entities.editEntity(, { position: "#00FF00"}); // { x: 0, y: 255, z: 0 } +*/ +ScriptValuePointer vec3ToScriptValue(ScriptEngine* engine, const glm::vec3& vec3); +ScriptValuePointer vec3ColorToScriptValue(ScriptEngine* engine, const glm::vec3& vec3); +void vec3FromScriptValue(const ScriptValuePointer& object, glm::vec3& vec3); + +/**jsdoc + * A color vector. See also the {@link Vec3(0)|Vec3} object. + * + * @typedef {object} Color + * @property {number} red - Red component value. Integer in the range 0 - 255. Synonyms: r, x. + * @property {number} green - Green component value. Integer in the range 0 - 255. Synonyms: g, y. + * @property {number} blue - Blue component value. Integer in the range 0 - 255. Synonyms: b, z. + * @example Colors can be set in multiple ways and modified with their aliases, but still stringify in the same way + * Entities.editEntity(, { color: { x: 1, y: 2, z: 3 }}); // { red: 1, green: 2, blue: 3 } + * Entities.editEntity(, { color: { r: 4, g: 5, b: 6 }}); // { red: 4, green: 5, blue: 6 } + * Entities.editEntity(, { color: { red: 7, green: 8, blue: 9 }}); // { red: 7, green: 8, blue: 9 } + * Entities.editEntity(, { color: [10, 11, 12] }); // { red: 10, green: 11, blue: 12 } + * Entities.editEntity(, { color: 13 }); // { red: 13, green: 13, blue: 13 } + * var color = Entities.getEntityProperties().color; // { red: 13, green: 13, blue: 13 } + * color.g = 14; // { red: 13, green: 14, blue: 13 } + * color.blue = 15; // { red: 13, green: 14, blue: 15 } + * Entities.editEntity(, { color: "red"}); // { red: 255, green: 0, blue: 0 } + * Entities.editEntity(, { color: "#00FF00"}); // { red: 0, green: 255, blue: 0 } + */ +/**jsdoc + * A color vector with real values. Values may also be null. See also the {@link Vec3(0)|Vec3} object. + * + * @typedef {object} ColorFloat + * @property {number} red - Red component value. Real in the range 0 - 255. Synonyms: r, x. + * @property {number} green - Green component value. Real in the range 0 - 255. Synonyms: g, y. + * @property {number} blue - Blue component value. Real in the range 0 - 255. Synonyms: b, z. + * @example ColorFloats can be set in multiple ways and modified with their aliases, but still stringify in the same way + * Entities.editEntity(, { color: { x: 1, y: 2, z: 3 }}); // { red: 1, green: 2, blue: 3 } + * Entities.editEntity(, { color: { r: 4, g: 5, b: 6 }}); // { red: 4, green: 5, blue: 6 } + * Entities.editEntity(, { color: { red: 7, green: 8, blue: 9 }}); // { red: 7, green: 8, blue: 9 } + * Entities.editEntity(, { color: [10, 11, 12] }); // { red: 10, green: 11, blue: 12 } + * Entities.editEntity(, { color: 13 }); // { red: 13, green: 13, blue: 13 } + * var color = Entities.getEntityProperties().color; // { red: 13, green: 13, blue: 13 } + * color.g = 14; // { red: 13, green: 14, blue: 13 } + * color.blue = 15; // { red: 13, green: 14, blue: 15 } + * Entities.editEntity(, { color: "red"}); // { red: 255, green: 0, blue: 0 } + * Entities.editEntity(, { color: "#00FF00"}); // { red: 0, green: 255, blue: 0 } + */ +ScriptValuePointer u8vec3ToScriptValue(ScriptEngine* engine, const glm::u8vec3& vec3); +ScriptValuePointer u8vec3ColorToScriptValue(ScriptEngine* engine, const glm::u8vec3& vec3); +void u8vec3FromScriptValue(const ScriptValuePointer& object, glm::u8vec3& vec3); + +/**jsdoc + * A 4-dimensional vector. + * + * @typedef {object} Vec4 + * @property {number} x - X-coordinate of the vector. + * @property {number} y - Y-coordinate of the vector. + * @property {number} z - Z-coordinate of the vector. + * @property {number} w - W-coordinate of the vector. + */ +ScriptValuePointer vec4toScriptValue(ScriptEngine* engine, const glm::vec4& vec4); +void vec4FromScriptValue(const ScriptValuePointer& object, glm::vec4& vec4); + +// Quaternions +ScriptValuePointer quatToScriptValue(ScriptEngine* engine, const glm::quat& quat); +void quatFromScriptValue(const ScriptValuePointer& object, glm::quat& quat); + +/**jsdoc + * Defines a rectangular portion of an image or screen, or similar. + * @typedef {object} Rect + * @property {number} x - Left, x-coordinate value. + * @property {number} y - Top, y-coordinate value. + * @property {number} width - Width of the rectangle. + * @property {number} height - Height of the rectangle. + */ +class QRect; +ScriptValuePointer qRectToScriptValue(ScriptEngine* engine, const QRect& rect); +void qRectFromScriptValue(const ScriptValuePointer& object, QRect& rect); + +class QRectF; +ScriptValuePointer qRectFToScriptValue(ScriptEngine* engine, const QRectF& rect); +void qRectFFromScriptValue(const ScriptValuePointer& object, QRectF& rect); + +// QColor +class QColor; +ScriptValuePointer qColorToScriptValue(ScriptEngine* engine, const QColor& color); +void qColorFromScriptValue(const ScriptValuePointer& object, QColor& color); + +class QUrl; +ScriptValuePointer qURLToScriptValue(ScriptEngine* engine, const QUrl& url); +void qURLFromScriptValue(const ScriptValuePointer& object, QUrl& url); + +// vector +Q_DECLARE_METATYPE(QVector) +ScriptValuePointer qVectorVec3ToScriptValue(ScriptEngine* engine, const QVector& vector); +ScriptValuePointer qVectorVec3ColorToScriptValue(ScriptEngine* engine, const QVector& vector); +void qVectorVec3FromScriptValue(const ScriptValuePointer& array, QVector& vector); +QVector qVectorVec3FromScriptValue(const ScriptValuePointer& array); + +// vector +Q_DECLARE_METATYPE(QVector) +ScriptValuePointer qVectorQuatToScriptValue(ScriptEngine* engine, const QVector& vector); +void qVectorQuatFromScriptValue(const ScriptValuePointer& array, QVector& vector); +QVector qVectorQuatFromScriptValue(const ScriptValuePointer& array); + +// vector +ScriptValuePointer qVectorBoolToScriptValue(ScriptEngine* engine, const QVector& vector); +void qVectorBoolFromScriptValue(const ScriptValuePointer& array, QVector& vector); +QVector qVectorBoolFromScriptValue(const ScriptValuePointer& array); + +// vector +ScriptValuePointer qVectorFloatToScriptValue(ScriptEngine* engine, const QVector& vector); +void qVectorFloatFromScriptValue(const ScriptValuePointer& array, QVector& vector); +QVector qVectorFloatFromScriptValue(const ScriptValuePointer& array); + +// vector +ScriptValuePointer qVectorIntToScriptValue(ScriptEngine* engine, const QVector& vector); +void qVectorIntFromScriptValue(const ScriptValuePointer& array, QVector& vector); + +ScriptValuePointer qVectorQUuidToScriptValue(ScriptEngine* engine, const QVector& vector); +void qVectorQUuidFromScriptValue(const ScriptValuePointer& array, QVector& vector); +QVector qVectorQUuidFromScriptValue(const ScriptValuePointer& array); + +class AACube; +ScriptValuePointer aaCubeToScriptValue(ScriptEngine* engine, const AACube& aaCube); +void aaCubeFromScriptValue(const ScriptValuePointer& object, AACube& aaCube); + +class PickRay; +ScriptValuePointer pickRayToScriptValue(ScriptEngine* engine, const PickRay& pickRay); +void pickRayFromScriptValue(const ScriptValuePointer& object, PickRay& pickRay); + +class Collision; +ScriptValuePointer collisionToScriptValue(ScriptEngine* engine, const Collision& collision); +void collisionFromScriptValue(const ScriptValuePointer& object, Collision& collision); + +/**jsdoc + * UUIDs (Universally Unique IDentifiers) are used to uniquely identify entities, avatars, and the like. They are represented + * in JavaScript as strings in the format, "{nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn}", where the "n"s are + * hexadecimal digits. + * @typedef {string} Uuid + */ +//Q_DECLARE_METATYPE(QUuid) // don't need to do this for QUuid since it's already a meta type +ScriptValuePointer quuidToScriptValue(ScriptEngine* engine, const QUuid& uuid); +void quuidFromScriptValue(const ScriptValuePointer& object, QUuid& uuid); + +//Q_DECLARE_METATYPE(QSizeF) // Don't need to to this becase it's arleady a meta type +class QSizeF; +ScriptValuePointer qSizeFToScriptValue(ScriptEngine* engine, const QSizeF& qSizeF); +void qSizeFFromScriptValue(const ScriptValuePointer& object, QSizeF& qSizeF); + +class AnimationDetails; +ScriptValuePointer animationDetailsToScriptValue(ScriptEngine* engine, const AnimationDetails& event); +void animationDetailsFromScriptValue(const ScriptValuePointer& object, AnimationDetails& event); + +class MeshProxy; +ScriptValuePointer meshToScriptValue(ScriptEngine* engine, MeshProxy* const& in); +void meshFromScriptValue(const ScriptValuePointer& value, MeshProxy*& out); + +class MeshProxyList; +ScriptValuePointer meshesToScriptValue(ScriptEngine* engine, const MeshProxyList& in); +void meshesFromScriptValue(const ScriptValuePointer& value, MeshProxyList& out); + +class MeshFace; +ScriptValuePointer meshFaceToScriptValue(ScriptEngine* engine, const MeshFace& meshFace); +void meshFaceFromScriptValue(const ScriptValuePointer& object, MeshFace& meshFaceResult); +ScriptValuePointer qVectorMeshFaceToScriptValue(ScriptEngine* engine, const QVector& vector); +void qVectorMeshFaceFromScriptValue(const ScriptValuePointer& array, QVector& result); + +enum class StencilMaskMode; +ScriptValuePointer stencilMaskModeToScriptValue(ScriptEngine* engine, const StencilMaskMode& stencilMode); +void stencilMaskModeFromScriptValue(const ScriptValuePointer& object, StencilMaskMode& stencilMode); + +class MiniPromise; +void promiseFromScriptValue(const ScriptValuePointer& object, std::shared_ptr& promise); +ScriptValuePointer promiseToScriptValue(ScriptEngine* engine, const std::shared_ptr& promise); + +#endif // #define hifi_ScriptValueUtils_h diff --git a/libraries/script-engine/src/Scriptable.h b/libraries/script-engine/src/Scriptable.h new file mode 100644 index 00000000000..b6d08a2bfe8 --- /dev/null +++ b/libraries/script-engine/src/Scriptable.h @@ -0,0 +1,59 @@ +// +// Scriptable.h +// libraries/script-engine/src +// +// Created by Heather Anderson on 5/1/21. +// Copyright 2021 Vircadia contributors. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_Scriptable_h +#define hifi_Scriptable_h + +#include + +#include "ScriptContext.h" + +class ScriptEngine; +class ScriptValue; +using ScriptEnginePointer = QSharedPointer; +using ScriptValuePointer = QSharedPointer; + +class Scriptable { +public: + static inline ScriptEnginePointer engine(); + static ScriptContext* context(); + static inline ScriptValuePointer thisObject(); + static inline int argumentCount(); + static inline ScriptValuePointer argument(int index); + + static void setContext(ScriptContext* context); +}; + +ScriptEnginePointer Scriptable::engine() { + ScriptContext* scriptContext = context(); + if (!scriptContext) return nullptr; + return scriptContext->engine(); +} + +ScriptValuePointer Scriptable::thisObject() { + ScriptContext* scriptContext = context(); + if (!scriptContext) return nullptr; + return scriptContext->thisObject(); +} + +int Scriptable::argumentCount() { + ScriptContext* scriptContext = context(); + if (!scriptContext) return 0; + return scriptContext->argumentCount(); +} + +ScriptValuePointer Scriptable::argument(int index) { + ScriptContext* scriptContext = context(); + if (!scriptContext) return ScriptValuePointer(); + return scriptContext->argument(index); +} + +#endif // hifi_Scriptable_h \ No newline at end of file diff --git a/libraries/script-engine/src/ScriptsModel.h b/libraries/script-engine/src/ScriptsModel.h index 0721600efc4..a55ae7163e9 100644 --- a/libraries/script-engine/src/ScriptsModel.h +++ b/libraries/script-engine/src/ScriptsModel.h @@ -16,8 +16,11 @@ #include #include #include +#include class TreeNodeFolder; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; enum ScriptOrigin { SCRIPT_ORIGIN_LOCAL, @@ -140,10 +143,10 @@ class ScriptsModel : public QAbstractItemModel { // No JSDoc because the particulars of the parent class is provided in the @class description. int columnCount(const QModelIndex& parent = QModelIndex()) const override; - // Not exposed in the API because no conversion between TreeNodeBase and QScriptValue is provided. + // Not exposed in the API because no conversion between TreeNodeBase and ScriptValuePointer is provided. TreeNodeBase* getTreeNodeFromIndex(const QModelIndex& index) const; - // Not exposed in the API because no conversion between TreeNodeBase and QScriptValue is provided. + // Not exposed in the API because no conversion between TreeNodeBase and ScriptValuePointer is provided. QList getFolderNodes(TreeNodeFolder* parent) const; enum Role { diff --git a/libraries/script-engine/src/SpatialEvent.cpp b/libraries/script-engine/src/SpatialEvent.cpp index 8520c0c485b..0c5d96db45b 100644 --- a/libraries/script-engine/src/SpatialEvent.cpp +++ b/libraries/script-engine/src/SpatialEvent.cpp @@ -12,6 +12,9 @@ #include "SpatialEvent.h" #include +#include "ScriptEngine.h" +#include "ScriptValueUtils.h" +#include "ScriptValue.h" SpatialEvent::SpatialEvent() : locTranslation(0.0f), @@ -30,17 +33,17 @@ SpatialEvent::SpatialEvent(const SpatialEvent& event) { } -QScriptValue SpatialEvent::toScriptValue(QScriptEngine* engine, const SpatialEvent& event) { - QScriptValue obj = engine->newObject(); +ScriptValuePointer SpatialEvent::toScriptValue(ScriptEngine* engine, const SpatialEvent& event) { + ScriptValuePointer obj = engine->newObject(); - obj.setProperty("locTranslation", vec3ToScriptValue(engine, event.locTranslation) ); - obj.setProperty("locRotation", quatToScriptValue(engine, event.locRotation) ); - obj.setProperty("absTranslation", vec3ToScriptValue(engine, event.absTranslation) ); - obj.setProperty("absRotation", quatToScriptValue(engine, event.absRotation) ); + obj->setProperty("locTranslation", vec3ToScriptValue(engine, event.locTranslation) ); + obj->setProperty("locRotation", quatToScriptValue(engine, event.locRotation) ); + obj->setProperty("absTranslation", vec3ToScriptValue(engine, event.absTranslation)); + obj->setProperty("absRotation", quatToScriptValue(engine, event.absRotation)); return obj; } -void SpatialEvent::fromScriptValue(const QScriptValue& object,SpatialEvent& event) { +void SpatialEvent::fromScriptValue(const ScriptValuePointer& object,SpatialEvent& event) { // nothing for now... } diff --git a/libraries/script-engine/src/SpatialEvent.h b/libraries/script-engine/src/SpatialEvent.h index 9063e487969..a43b97b5242 100644 --- a/libraries/script-engine/src/SpatialEvent.h +++ b/libraries/script-engine/src/SpatialEvent.h @@ -14,16 +14,19 @@ #include #include +#include -#include +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; class SpatialEvent { public: SpatialEvent(); SpatialEvent(const SpatialEvent& other); - static QScriptValue toScriptValue(QScriptEngine* engine, const SpatialEvent& event); - static void fromScriptValue(const QScriptValue& object, SpatialEvent& event); + static ScriptValuePointer toScriptValue(ScriptEngine* engine, const SpatialEvent& event); + static void fromScriptValue(const ScriptValuePointer& object, SpatialEvent& event); glm::vec3 locTranslation; glm::quat locRotation; diff --git a/libraries/script-engine/src/TouchEvent.cpp b/libraries/script-engine/src/TouchEvent.cpp index 050fe8b0fd2..64d64226932 100644 --- a/libraries/script-engine/src/TouchEvent.cpp +++ b/libraries/script-engine/src/TouchEvent.cpp @@ -11,12 +11,12 @@ #include "TouchEvent.h" -#include -#include - #include #include "RegisteredMetaTypes.h" +#include "ScriptEngine.h" +#include "ScriptValue.h" +#include "ScriptValueUtils.h" TouchEvent::TouchEvent() : x(0.0f), @@ -204,47 +204,47 @@ void TouchEvent::calculateMetaAttributes(const TouchEvent& other) { * print(JSON.stringify(event)); * }); */ -QScriptValue TouchEvent::toScriptValue(QScriptEngine* engine, const TouchEvent& event) { - QScriptValue obj = engine->newObject(); - obj.setProperty("x", event.x); - obj.setProperty("y", event.y); - obj.setProperty("isPressed", event.isPressed); - obj.setProperty("isMoved", event.isMoved); - obj.setProperty("isStationary", event.isStationary); - obj.setProperty("isReleased", event.isReleased); - obj.setProperty("isShifted", event.isShifted); - obj.setProperty("isMeta", event.isMeta); - obj.setProperty("isControl", event.isControl); - obj.setProperty("isAlt", event.isAlt); - obj.setProperty("touchPoints", event.touchPoints); - - QScriptValue pointsObj = engine->newArray(); +ScriptValuePointer TouchEvent::toScriptValue(ScriptEngine* engine, const TouchEvent& event) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty("x", event.x); + obj->setProperty("y", event.y); + obj->setProperty("isPressed", event.isPressed); + obj->setProperty("isMoved", event.isMoved); + obj->setProperty("isStationary", event.isStationary); + obj->setProperty("isReleased", event.isReleased); + obj->setProperty("isShifted", event.isShifted); + obj->setProperty("isMeta", event.isMeta); + obj->setProperty("isControl", event.isControl); + obj->setProperty("isAlt", event.isAlt); + obj->setProperty("touchPoints", event.touchPoints); + + ScriptValuePointer pointsObj = engine->newArray(); int index = 0; foreach (glm::vec2 point, event.points) { - QScriptValue thisPoint = vec2ToScriptValue(engine, point); - pointsObj.setProperty(index, thisPoint); + ScriptValuePointer thisPoint = vec2ToScriptValue(engine, point); + pointsObj->setProperty(index, thisPoint); index++; } - obj.setProperty("points", pointsObj); - obj.setProperty("radius", event.radius); - obj.setProperty("isPinching", event.isPinching); - obj.setProperty("isPinchOpening", event.isPinchOpening); - - obj.setProperty("angle", event.angle); - obj.setProperty("deltaAngle", event.deltaAngle); - QScriptValue anglesObj = engine->newArray(); + obj->setProperty("points", pointsObj); + obj->setProperty("radius", event.radius); + obj->setProperty("isPinching", event.isPinching); + obj->setProperty("isPinchOpening", event.isPinchOpening); + + obj->setProperty("angle", event.angle); + obj->setProperty("deltaAngle", event.deltaAngle); + ScriptValuePointer anglesObj = engine->newArray(); index = 0; foreach (float angle, event.angles) { - anglesObj.setProperty(index, angle); + anglesObj->setProperty(index, angle); index++; } - obj.setProperty("angles", anglesObj); + obj->setProperty("angles", anglesObj); - obj.setProperty("isRotating", event.isRotating); - obj.setProperty("rotating", event.rotating); + obj->setProperty("isRotating", event.isRotating); + obj->setProperty("rotating", event.rotating); return obj; } -void TouchEvent::fromScriptValue(const QScriptValue& object, TouchEvent& event) { +void TouchEvent::fromScriptValue(const ScriptValuePointer& object, TouchEvent& event) { // nothing for now... } diff --git a/libraries/script-engine/src/TouchEvent.h b/libraries/script-engine/src/TouchEvent.h index 62cb1b18016..e49cb71d014 100644 --- a/libraries/script-engine/src/TouchEvent.h +++ b/libraries/script-engine/src/TouchEvent.h @@ -16,9 +16,11 @@ #include #include +#include -class QScriptValue; -class QScriptEngine; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; class TouchEvent { public: @@ -26,8 +28,8 @@ class TouchEvent { TouchEvent(const QTouchEvent& event); TouchEvent(const QTouchEvent& event, const TouchEvent& other); - static QScriptValue toScriptValue(QScriptEngine* engine, const TouchEvent& event); - static void fromScriptValue(const QScriptValue& object, TouchEvent& event); + static ScriptValuePointer toScriptValue(ScriptEngine* engine, const TouchEvent& event); + static void fromScriptValue(const ScriptValuePointer& object, TouchEvent& event); float x; float y; diff --git a/libraries/shared/src/VariantMapToScriptValue.cpp b/libraries/script-engine/src/VariantMapToScriptValue.cpp similarity index 61% rename from libraries/shared/src/VariantMapToScriptValue.cpp rename to libraries/script-engine/src/VariantMapToScriptValue.cpp index 156a438bd74..1b206e410d0 100644 --- a/libraries/shared/src/VariantMapToScriptValue.cpp +++ b/libraries/script-engine/src/VariantMapToScriptValue.cpp @@ -15,20 +15,20 @@ #include "SharedLogging.h" -QScriptValue variantToScriptValue(QVariant& qValue, QScriptEngine& scriptEngine) { +ScriptValuePointer variantToScriptValue(QVariant& qValue, ScriptEngine& scriptEngine) { switch(qValue.type()) { case QVariant::Bool: - return qValue.toBool(); + return scriptEngine.newValue(qValue.toBool()); break; case QVariant::Int: - return qValue.toInt(); + return scriptEngine.newValue(qValue.toInt()); break; case QVariant::Double: - return qValue.toDouble(); + return scriptEngine.newValue(qValue.toDouble()); break; case QVariant::String: case QVariant::Url: - return qValue.toString(); + return scriptEngine.newValue(qValue.toString()); break; case QVariant::Map: { QVariantMap childMap = qValue.toMap(); @@ -42,35 +42,35 @@ QScriptValue variantToScriptValue(QVariant& qValue, QScriptEngine& scriptEngine) } default: if (qValue.canConvert()) { - return qValue.toFloat(); + return scriptEngine.newValue(qValue.toFloat()); } //qCDebug(shared) << "unhandled QScript type" << qValue.type(); break; } - return QScriptValue(); + return ScriptValuePointer(); } -QScriptValue variantMapToScriptValue(QVariantMap& variantMap, QScriptEngine& scriptEngine) { - QScriptValue scriptValue = scriptEngine.newObject(); +ScriptValuePointer variantMapToScriptValue(QVariantMap& variantMap, ScriptEngine& scriptEngine) { + ScriptValuePointer scriptValue = scriptEngine.newObject(); for (QVariantMap::const_iterator iter = variantMap.begin(); iter != variantMap.end(); ++iter) { QString key = iter.key(); QVariant qValue = iter.value(); - scriptValue.setProperty(key, variantToScriptValue(qValue, scriptEngine)); + scriptValue->setProperty(key, variantToScriptValue(qValue, scriptEngine)); } return scriptValue; } -QScriptValue variantListToScriptValue(QVariantList& variantList, QScriptEngine& scriptEngine) { +ScriptValuePointer variantListToScriptValue(QVariantList& variantList, ScriptEngine& scriptEngine) { - QScriptValue scriptValue = scriptEngine.newArray(); + ScriptValuePointer scriptValue = scriptEngine.newArray(); for (int i = 0; i < variantList.size(); i++) { - scriptValue.setProperty(i, variantToScriptValue(variantList[i], scriptEngine)); + scriptValue->setProperty(i, variantToScriptValue(variantList[i], scriptEngine)); } return scriptValue; diff --git a/libraries/script-engine/src/VariantMapToScriptValue.h b/libraries/script-engine/src/VariantMapToScriptValue.h new file mode 100644 index 00000000000..2700decedd3 --- /dev/null +++ b/libraries/script-engine/src/VariantMapToScriptValue.h @@ -0,0 +1,18 @@ +// +// VariantMapToScriptValue.h +// libraries/shared/src/ +// +// Created by Brad Hefta-Gaub on 12/6/13. +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include "ScriptValue.h" +#include "ScriptEngine.h" + +ScriptValuePointer variantToScriptValue(QVariant& qValue, ScriptEngine& scriptEngine); +ScriptValuePointer variantMapToScriptValue(QVariantMap& variantMap, ScriptEngine& scriptEngine); +ScriptValuePointer variantListToScriptValue(QVariantList& variantList, ScriptEngine& scriptEngine); diff --git a/libraries/script-engine/src/Vec3.cpp b/libraries/script-engine/src/Vec3.cpp index 2d3d4454c34..84df545bd2c 100644 --- a/libraries/script-engine/src/Vec3.cpp +++ b/libraries/script-engine/src/Vec3.cpp @@ -21,6 +21,7 @@ #include "NumericalConstants.h" #include "ScriptEngine.h" #include "ScriptEngineLogging.h" +#include "ScriptManager.h" float Vec3::orientedAngle(const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& v3) { @@ -32,8 +33,8 @@ void Vec3::print(const QString& label, const glm::vec3& v) { QString message = QString("%1 %2").arg(qPrintable(label)); message = message.arg(glm::to_string(glm::dvec3(v)).c_str()); qCDebug(scriptengine) << message; - if (ScriptEngine* scriptEngine = qobject_cast(engine())) { - scriptEngine->print(message); + if (ScriptManager* scriptManager = engine()->manager()) { + scriptManager->print(message); } } diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index a8a8adece35..1882dece19c 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -17,9 +17,9 @@ #include #include -#include #include "GLMHelpers.h" +#include "Scriptable.h" /**jsdoc * The Vec3 API provides facilities for generating and manipulating 3-dimensional vectors. Vircadia uses a @@ -73,7 +73,7 @@ */ /// Scriptable interface a Vec3ernion helper class object. Used exclusively in the JavaScript API -class Vec3 : public QObject, protected QScriptable { +class Vec3 : public QObject, protected Scriptable { Q_OBJECT Q_PROPERTY(glm::vec3 UNIT_X READ UNIT_X CONSTANT) Q_PROPERTY(glm::vec3 UNIT_Y READ UNIT_Y CONSTANT) diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 00d379aff4d..cd7a0c93160 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -14,11 +14,12 @@ #include "WebSocketClass.h" +#include "ScriptContext.h" #include "ScriptEngine.h" - #include "ScriptEngineLogging.h" +#include "ScriptValue.h" -WebSocketClass::WebSocketClass(QScriptEngine* engine, QString url) : +WebSocketClass::WebSocketClass(ScriptEngine* engine, QString url) : _webSocket(new QWebSocket()), _engine(engine) { @@ -26,7 +27,7 @@ WebSocketClass::WebSocketClass(QScriptEngine* engine, QString url) : _webSocket->open(url); } -WebSocketClass::WebSocketClass(QScriptEngine* engine, QWebSocket* qWebSocket) : +WebSocketClass::WebSocketClass(ScriptEngine* engine, QWebSocket* qWebSocket) : _webSocket(qWebSocket), _engine(engine) { @@ -43,24 +44,24 @@ void WebSocketClass::initialize() { _binaryType = QStringLiteral("arraybuffer"); } -QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine) { +ScriptValuePointer WebSocketClass::constructor(ScriptContext* context, ScriptEngine* engine) { QString url; if (context->argumentCount() > 0) { - url = context->argument(0).toString(); + url = context->argument(0)->toString(); } - return engine->newQObject(new WebSocketClass(engine, url), QScriptEngine::ScriptOwnership); + return engine->newQObject(new WebSocketClass(engine, url), ScriptEngine::ScriptOwnership); } WebSocketClass::~WebSocketClass() { _webSocket->deleteLater(); } -void WebSocketClass::send(QScriptValue message) { - if (message.isObject()) { - QByteArray ba = qscriptvalue_cast(message); +void WebSocketClass::send(ScriptValuePointer message) { + if (message->isObject()) { + QByteArray ba = scriptvalue_cast(message); _webSocket->sendBinaryMessage(ba); } else { - _webSocket->sendTextMessage(message.toString()); + _webSocket->sendTextMessage(message->toString()); } } @@ -90,14 +91,14 @@ void WebSocketClass::close(QWebSocketProtocol::CloseCode closeCode, QString reas */ void WebSocketClass::handleOnClose() { bool hasError = (_webSocket->error() != QAbstractSocket::UnknownSocketError); - if (_onCloseEvent.isFunction()) { - QScriptValueList args; - QScriptValue arg = _engine->newObject(); - arg.setProperty("code", hasError ? QWebSocketProtocol::CloseCodeAbnormalDisconnection : _webSocket->closeCode()); - arg.setProperty("reason", _webSocket->closeReason()); - arg.setProperty("wasClean", !hasError); + if (_onCloseEvent->isFunction()) { + ScriptValueList args; + ScriptValuePointer arg = _engine->newObject(); + arg->setProperty("code", hasError ? QWebSocketProtocol::CloseCodeAbnormalDisconnection : _webSocket->closeCode()); + arg->setProperty("reason", _webSocket->closeReason()); + arg->setProperty("wasClean", !hasError); args << arg; - _onCloseEvent.call(QScriptValue(), args); + _onCloseEvent->call(ScriptValuePointer(), args); } } @@ -153,8 +154,8 @@ void WebSocketClass::handleOnClose() { * @typedef {number} WebSocket.SocketError */ void WebSocketClass::handleOnError(QAbstractSocket::SocketError error) { - if (_onErrorEvent.isFunction()) { - _onErrorEvent.call(); + if (_onErrorEvent->isFunction()) { + _onErrorEvent->call(); } } @@ -169,31 +170,26 @@ void WebSocketClass::handleOnError(QAbstractSocket::SocketError error) { * @property {string} data - The message content. */ void WebSocketClass::handleOnMessage(const QString& message) { - if (_onMessageEvent.isFunction()) { - QScriptValueList args; - QScriptValue arg = _engine->newObject(); - arg.setProperty("data", message); + if (_onMessageEvent->isFunction()) { + ScriptValueList args; + ScriptValuePointer arg = _engine->newObject(); + arg->setProperty("data", message); args << arg; - _onMessageEvent.call(QScriptValue(), args); + _onMessageEvent->call(ScriptValuePointer(), args); } } void WebSocketClass::handleOnBinaryMessage(const QByteArray& message) { - if (_onMessageEvent.isFunction()) { - QScriptValueList args; - QScriptValue arg = _engine->newObject(); - QScriptValue data = _engine->newVariant(QVariant::fromValue(message)); - QScriptValue ctor = _engine->globalObject().property("ArrayBuffer"); - auto array = qscriptvalue_cast(ctor.data()); - QScriptValue arrayBuffer; - if (!array) { + if (_onMessageEvent->isFunction()) { + ScriptValueList args; + ScriptValuePointer arg = _engine->newObject(); + ScriptValuePointer arrayBuffer = _engine->newArrayBuffer(message); + if (arrayBuffer->isUndefined()) { qCWarning(scriptengine) << "WebSocketClass::handleOnBinaryMessage !ArrayBuffer"; - } else { - arrayBuffer = _engine->newObject(array, data); } - arg.setProperty("data", arrayBuffer); + arg->setProperty("data", arrayBuffer); args << arg; - _onMessageEvent.call(QScriptValue(), args); + _onMessageEvent->call(ScriptValuePointer(), args); } } @@ -202,31 +198,31 @@ void WebSocketClass::handleOnBinaryMessage(const QByteArray& message) { * @callback WebSocket~onOpenCallback */ void WebSocketClass::handleOnOpen() { - if (_onOpenEvent.isFunction()) { - _onOpenEvent.call(); + if (_onOpenEvent->isFunction()) { + _onOpenEvent->call(); } } -QScriptValue qWSCloseCodeToScriptValue(QScriptEngine* engine, const QWebSocketProtocol::CloseCode &closeCode) { +ScriptValuePointer qWSCloseCodeToScriptValue(ScriptEngine* engine, const QWebSocketProtocol::CloseCode &closeCode) { return closeCode; } -void qWSCloseCodeFromScriptValue(const QScriptValue &object, QWebSocketProtocol::CloseCode &closeCode) { - closeCode = (QWebSocketProtocol::CloseCode)object.toUInt16(); +void qWSCloseCodeFromScriptValue(const ScriptValuePointer &object, QWebSocketProtocol::CloseCode &closeCode) { + closeCode = (QWebSocketProtocol::CloseCode)object->toUInt16(); } -QScriptValue webSocketToScriptValue(QScriptEngine* engine, WebSocketClass* const &in) { - return engine->newQObject(in, QScriptEngine::ScriptOwnership); +ScriptValuePointer webSocketToScriptValue(ScriptEngine* engine, WebSocketClass* const &in) { + return engine->newQObject(in, ScriptEngine::ScriptOwnership); } -void webSocketFromScriptValue(const QScriptValue &object, WebSocketClass* &out) { - out = qobject_cast(object.toQObject()); +void webSocketFromScriptValue(const ScriptValuePointer &object, WebSocketClass* &out) { + out = qobject_cast(object->toQObject()); } -QScriptValue wscReadyStateToScriptValue(QScriptEngine* engine, const WebSocketClass::ReadyState& readyState) { +ScriptValuePointer wscReadyStateToScriptValue(ScriptEngine* engine, const WebSocketClass::ReadyState& readyState) { return readyState; } -void wscReadyStateFromScriptValue(const QScriptValue& object, WebSocketClass::ReadyState& readyState) { - readyState = (WebSocketClass::ReadyState)object.toUInt16(); +void wscReadyStateFromScriptValue(const ScriptValuePointer& object, WebSocketClass::ReadyState& readyState) { + readyState = (WebSocketClass::ReadyState)object->toUInt16(); } diff --git a/libraries/script-engine/src/WebSocketClass.h b/libraries/script-engine/src/WebSocketClass.h index a3faaadbb59..aee4a2c3591 100644 --- a/libraries/script-engine/src/WebSocketClass.h +++ b/libraries/script-engine/src/WebSocketClass.h @@ -13,8 +13,13 @@ #define hifi_WebSocketClass_h #include -#include #include +#include + +class ScriptContext; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; /**jsdoc * Provides a bi-directional, event-driven communication session between the script and another WebSocket connection. It is a @@ -84,10 +89,10 @@ class WebSocketClass : public QObject { Q_PROPERTY(ulong bufferedAmount READ getBufferedAmount) Q_PROPERTY(QString extensions READ getExtensions) - Q_PROPERTY(QScriptValue onclose READ getOnClose WRITE setOnClose) - Q_PROPERTY(QScriptValue onerror READ getOnError WRITE setOnError) - Q_PROPERTY(QScriptValue onmessage READ getOnMessage WRITE setOnMessage) - Q_PROPERTY(QScriptValue onopen READ getOnOpen WRITE setOnOpen) + Q_PROPERTY(ScriptValuePointer onclose READ getOnClose WRITE setOnClose) + Q_PROPERTY(ScriptValuePointer onerror READ getOnError WRITE setOnError) + Q_PROPERTY(ScriptValuePointer onmessage READ getOnMessage WRITE setOnMessage) + Q_PROPERTY(ScriptValuePointer onopen READ getOnOpen WRITE setOnOpen) Q_PROPERTY(QString protocol READ getProtocol) Q_PROPERTY(WebSocketClass::ReadyState readyState READ getReadyState) @@ -99,11 +104,11 @@ class WebSocketClass : public QObject { Q_PROPERTY(WebSocketClass::ReadyState CLOSED READ getClosed CONSTANT) public: - WebSocketClass(QScriptEngine* engine, QString url); - WebSocketClass(QScriptEngine* engine, QWebSocket* qWebSocket); + WebSocketClass(ScriptEngine* engine, QString url); + WebSocketClass(ScriptEngine* engine, QWebSocket* qWebSocket); ~WebSocketClass(); - static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); + static ScriptValuePointer constructor(ScriptContext* context, ScriptEngine* engine); /**jsdoc * The state of a WebSocket connection. @@ -164,17 +169,17 @@ class WebSocketClass : public QObject { } } - void setOnClose(QScriptValue eventFunction) { _onCloseEvent = eventFunction; } - QScriptValue getOnClose() { return _onCloseEvent; } + void setOnClose(ScriptValuePointer eventFunction) { _onCloseEvent = eventFunction; } + ScriptValuePointer getOnClose() { return _onCloseEvent; } - void setOnError(QScriptValue eventFunction) { _onErrorEvent = eventFunction; } - QScriptValue getOnError() { return _onErrorEvent; } + void setOnError(ScriptValuePointer eventFunction) { _onErrorEvent = eventFunction; } + ScriptValuePointer getOnError() { return _onErrorEvent; } - void setOnMessage(QScriptValue eventFunction) { _onMessageEvent = eventFunction; } - QScriptValue getOnMessage() { return _onMessageEvent; } + void setOnMessage(ScriptValuePointer eventFunction) { _onMessageEvent = eventFunction; } + ScriptValuePointer getOnMessage() { return _onMessageEvent; } - void setOnOpen(QScriptValue eventFunction) { _onOpenEvent = eventFunction; } - QScriptValue getOnOpen() { return _onOpenEvent; } + void setOnOpen(ScriptValuePointer eventFunction) { _onOpenEvent = eventFunction; } + ScriptValuePointer getOnOpen() { return _onOpenEvent; } public slots: @@ -183,7 +188,7 @@ public slots: * @function WebSocket.send * @param {string|object} message - The message to send. If an object, it is converted to a string. */ - void send(QScriptValue message); + void send(ScriptValuePointer message); /**jsdoc * Closes the connection. @@ -221,12 +226,12 @@ public slots: private: QWebSocket* _webSocket; - QScriptEngine* _engine; + ScriptEngine* _engine; - QScriptValue _onCloseEvent; - QScriptValue _onErrorEvent; - QScriptValue _onMessageEvent; - QScriptValue _onOpenEvent; + ScriptValuePointer _onCloseEvent; + ScriptValuePointer _onErrorEvent; + ScriptValuePointer _onMessageEvent; + ScriptValuePointer _onOpenEvent; QString _binaryType; @@ -244,13 +249,13 @@ private slots: Q_DECLARE_METATYPE(QWebSocketProtocol::CloseCode); Q_DECLARE_METATYPE(WebSocketClass::ReadyState); -QScriptValue qWSCloseCodeToScriptValue(QScriptEngine* engine, const QWebSocketProtocol::CloseCode& closeCode); -void qWSCloseCodeFromScriptValue(const QScriptValue& object, QWebSocketProtocol::CloseCode& closeCode); +ScriptValuePointer qWSCloseCodeToScriptValue(ScriptEngine* engine, const QWebSocketProtocol::CloseCode& closeCode); +void qWSCloseCodeFromScriptValue(const ScriptValuePointer& object, QWebSocketProtocol::CloseCode& closeCode); -QScriptValue webSocketToScriptValue(QScriptEngine* engine, WebSocketClass* const &in); -void webSocketFromScriptValue(const QScriptValue &object, WebSocketClass* &out); +ScriptValuePointer webSocketToScriptValue(ScriptEngine* engine, WebSocketClass* const &in); +void webSocketFromScriptValue(const ScriptValuePointer &object, WebSocketClass* &out); -QScriptValue wscReadyStateToScriptValue(QScriptEngine* engine, const WebSocketClass::ReadyState& readyState); -void wscReadyStateFromScriptValue(const QScriptValue& object, WebSocketClass::ReadyState& readyState); +ScriptValuePointer wscReadyStateToScriptValue(ScriptEngine* engine, const WebSocketClass::ReadyState& readyState); +void wscReadyStateFromScriptValue(const ScriptValuePointer& object, WebSocketClass::ReadyState& readyState); #endif // hifi_WebSocketClass_h diff --git a/libraries/script-engine/src/WebSocketServerClass.cpp b/libraries/script-engine/src/WebSocketServerClass.cpp index 860170a3f98..17677a1fc69 100644 --- a/libraries/script-engine/src/WebSocketServerClass.cpp +++ b/libraries/script-engine/src/WebSocketServerClass.cpp @@ -13,9 +13,11 @@ #include "WebSocketServerClass.h" +#include "ScriptContext.h" #include "ScriptEngine.h" +#include "ScriptValue.h" -WebSocketServerClass::WebSocketServerClass(QScriptEngine* engine, const QString& serverName, const quint16 port) : +WebSocketServerClass::WebSocketServerClass(ScriptEngine* engine, const QString& serverName, const quint16 port) : _webSocketServer(serverName, QWebSocketServer::SslMode::NonSecureMode), _engine(engine) { @@ -24,24 +26,24 @@ WebSocketServerClass::WebSocketServerClass(QScriptEngine* engine, const QString& } } -QScriptValue WebSocketServerClass::constructor(QScriptContext* context, QScriptEngine* engine) { +ScriptValuePointer WebSocketServerClass::constructor(ScriptContext* context, ScriptEngine* engine) { // the serverName is used in handshakes QString serverName = QStringLiteral("HighFidelity - Scripted WebSocket Listener"); // port 0 will auto-assign a free port quint16 port = 0; - QScriptValue callee = context->callee(); + ScriptValuePointer callee = context->callee(); if (context->argumentCount() > 0) { - QScriptValue options = context->argument(0); - QScriptValue portOption = options.property(QStringLiteral("port")); - if (portOption.isValid() && portOption.isNumber()) { - port = portOption.toNumber(); + ScriptValuePointer options = context->argument(0); + ScriptValuePointer portOption = options->property(QStringLiteral("port")); + if (portOption->isValid() && portOption->isNumber()) { + port = portOption->toNumber(); } - QScriptValue serverNameOption = options.property(QStringLiteral("serverName")); - if (serverNameOption.isValid() && serverNameOption.isString()) { - serverName = serverNameOption.toString(); + ScriptValuePointer serverNameOption = options->property(QStringLiteral("serverName")); + if (serverNameOption->isValid() && serverNameOption->isString()) { + serverName = serverNameOption->toString(); } } - return engine->newQObject(new WebSocketServerClass(engine, serverName, port), QScriptEngine::ScriptOwnership); + return engine->newQObject(new WebSocketServerClass(engine, serverName, port), ScriptEngine::ScriptOwnership); } WebSocketServerClass::~WebSocketServerClass() { diff --git a/libraries/script-engine/src/WebSocketServerClass.h b/libraries/script-engine/src/WebSocketServerClass.h index a78e8544a3c..dbaa0855986 100644 --- a/libraries/script-engine/src/WebSocketServerClass.h +++ b/libraries/script-engine/src/WebSocketServerClass.h @@ -13,9 +13,14 @@ #define hifi_WebSocketServerClass_h #include -#include #include #include "WebSocketClass.h" +#include + +class ScriptContext; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; /**jsdoc * Manages {@link WebSocket}s in server entity and assignment client scripts. @@ -80,14 +85,14 @@ class WebSocketServerClass : public QObject { Q_PROPERTY(bool listening READ isListening) public: - WebSocketServerClass(QScriptEngine* engine, const QString& serverName, const quint16 port); + WebSocketServerClass(ScriptEngine* engine, const QString& serverName, const quint16 port); ~WebSocketServerClass(); QString getURL() { return _webSocketServer.serverUrl().toDisplayString(); } quint16 getPort() { return _webSocketServer.serverPort(); } bool isListening() { return _webSocketServer.isListening(); } - static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); + static ScriptValuePointer constructor(ScriptContext* context, ScriptEngine* engine); public slots: @@ -99,7 +104,7 @@ public slots: private: QWebSocketServer _webSocketServer; - QScriptEngine* _engine; + ScriptEngine* _engine; QList _clients; private slots: diff --git a/libraries/script-engine/src/WheelEvent.cpp b/libraries/script-engine/src/WheelEvent.cpp index 2bc321691f1..a0c19392d9f 100644 --- a/libraries/script-engine/src/WheelEvent.cpp +++ b/libraries/script-engine/src/WheelEvent.cpp @@ -11,8 +11,8 @@ #include "WheelEvent.h" -#include -#include +#include "ScriptEngine.h" +#include "ScriptValue.h" WheelEvent::WheelEvent() : x(0.0f), @@ -81,22 +81,22 @@ WheelEvent::WheelEvent(const QWheelEvent& event) { * print(JSON.stringify(event)); * }); */ -QScriptValue WheelEvent::toScriptValue(QScriptEngine* engine, const WheelEvent& event) { - QScriptValue obj = engine->newObject(); - obj.setProperty("x", event.x); - obj.setProperty("y", event.y); - obj.setProperty("delta", event.delta); - obj.setProperty("orientation", event.orientation); - obj.setProperty("isLeftButton", event.isLeftButton); - obj.setProperty("isRightButton", event.isRightButton); - obj.setProperty("isMiddleButton", event.isMiddleButton); - obj.setProperty("isShifted", event.isShifted); - obj.setProperty("isMeta", event.isMeta); - obj.setProperty("isControl", event.isControl); - obj.setProperty("isAlt", event.isAlt); +ScriptValuePointer WheelEvent::toScriptValue(ScriptEngine* engine, const WheelEvent& event) { + ScriptValuePointer obj = engine->newObject(); + obj->setProperty("x", event.x); + obj->setProperty("y", event.y); + obj->setProperty("delta", event.delta); + obj->setProperty("orientation", event.orientation); + obj->setProperty("isLeftButton", event.isLeftButton); + obj->setProperty("isRightButton", event.isRightButton); + obj->setProperty("isMiddleButton", event.isMiddleButton); + obj->setProperty("isShifted", event.isShifted); + obj->setProperty("isMeta", event.isMeta); + obj->setProperty("isControl", event.isControl); + obj->setProperty("isAlt", event.isAlt); return obj; } -void WheelEvent::fromScriptValue(const QScriptValue& object, WheelEvent& event) { +void WheelEvent::fromScriptValue(const ScriptValuePointer& object, WheelEvent& event) { // nothing for now... } diff --git a/libraries/script-engine/src/WheelEvent.h b/libraries/script-engine/src/WheelEvent.h index 88ac8285780..d2303b99734 100644 --- a/libraries/script-engine/src/WheelEvent.h +++ b/libraries/script-engine/src/WheelEvent.h @@ -14,17 +14,19 @@ #include #include +#include -class QScriptValue; -class QScriptEngine; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; class WheelEvent { public: WheelEvent(); WheelEvent(const QWheelEvent& event); - static QScriptValue toScriptValue(QScriptEngine* engine, const WheelEvent& event); - static void fromScriptValue(const QScriptValue& object, WheelEvent& event); + static ScriptValuePointer toScriptValue(ScriptEngine* engine, const WheelEvent& event); + static void fromScriptValue(const ScriptValuePointer& object, WheelEvent& event); int x; int y; diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp index 1a4ec52f73d..c78d287fe00 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.cpp +++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp @@ -23,12 +23,16 @@ #include #include "ResourceRequestObserver.h" +#include "ScriptContext.h" #include "ScriptEngine.h" +#include "ScriptValue.h" Q_DECLARE_METATYPE(QByteArray*) -XMLHttpRequestClass::XMLHttpRequestClass(QScriptEngine* engine) : +XMLHttpRequestClass::XMLHttpRequestClass(ScriptEngine* engine) : _engine(engine), + _onTimeout(engine->nullValue()), + _onReadyStateChange(engine->nullValue()), _timer(this) { _request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); @@ -39,15 +43,15 @@ XMLHttpRequestClass::~XMLHttpRequestClass() { if (_reply) { _reply->deleteLater(); } } -QScriptValue XMLHttpRequestClass::constructor(QScriptContext* context, QScriptEngine* engine) { - return engine->newQObject(new XMLHttpRequestClass(engine), QScriptEngine::ScriptOwnership); +ScriptValuePointer XMLHttpRequestClass::constructor(ScriptContext* context, ScriptEngine* engine) { + return engine->newQObject(new XMLHttpRequestClass(engine), ScriptEngine::ScriptOwnership); } -QScriptValue XMLHttpRequestClass::getStatus() const { +ScriptValuePointer XMLHttpRequestClass::getStatus() const { if (_reply) { - return QScriptValue(_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt()); + return _engine->newValue(_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt()); } - return QScriptValue(0); + return ScriptValuePointer(0); } QString XMLHttpRequestClass::getStatusText() const { @@ -87,7 +91,7 @@ void XMLHttpRequestClass::requestDownloadProgress(qint64 bytesReceived, qint64 b } } -QScriptValue XMLHttpRequestClass::getAllResponseHeaders() const { +ScriptValuePointer XMLHttpRequestClass::getAllResponseHeaders() const { if (_reply) { QList headerList = _reply->rawHeaderPairs(); QByteArray headers; @@ -97,16 +101,16 @@ QScriptValue XMLHttpRequestClass::getAllResponseHeaders() const { headers.append(headerList[i].second); headers.append("\n"); } - return QString(headers.data()); + return _engine->newValue(QString(headers.data())); } - return QScriptValue(""); + return _engine->newValue(""); } -QScriptValue XMLHttpRequestClass::getResponseHeader(const QString& name) const { +ScriptValuePointer XMLHttpRequestClass::getResponseHeader(const QString& name) const { if (_reply && _reply->hasRawHeader(name.toLatin1())) { - return QScriptValue(QString(_reply->rawHeader(name.toLatin1()))); + return _engine->newValue(QString(_reply->rawHeader(name.toLatin1()))); } - return QScriptValue::NullValue; + return _engine->nullValue(); } /**jsdoc @@ -116,8 +120,8 @@ QScriptValue XMLHttpRequestClass::getResponseHeader(const QString& name) const { void XMLHttpRequestClass::setReadyState(ReadyState readyState) { if (readyState != _readyState) { _readyState = readyState; - if (_onReadyStateChange.isFunction()) { - _onReadyStateChange.call(QScriptValue::NullValue); + if (_onReadyStateChange->isFunction()) { + _onReadyStateChange->call(_onReadyStateChange->engine()->nullValue()); } } } @@ -153,17 +157,17 @@ void XMLHttpRequestClass::open(const QString& method, const QString& url, bool a } void XMLHttpRequestClass::send() { - send(QScriptValue::NullValue); + send(_engine->nullValue()); } -void XMLHttpRequestClass::send(const QScriptValue& data) { +void XMLHttpRequestClass::send(const ScriptValuePointer& data) { if (_readyState == OPENED && !_reply) { if (!data.isNull()) { - if (data.isObject()) { - _sendData = qscriptvalue_cast(data); + if (data->isObject()) { + _sendData = scriptvalue_cast(data); } else { - _sendData = data.toString().toUtf8(); + _sendData = data->toString().toUtf8(); } } @@ -193,8 +197,8 @@ void XMLHttpRequestClass::doSend() { * @callback XMLHttpRequest~onTimeoutCallback */ void XMLHttpRequestClass::requestTimeout() { - if (_onTimeout.isFunction()) { - _onTimeout.call(QScriptValue::NullValue); + if (_onTimeout->isFunction()) { + _onTimeout->call(_engine->nullValue()); } abortRequest(); _errorCode = QNetworkReply::TimeoutError; @@ -215,15 +219,14 @@ void XMLHttpRequestClass::requestFinished() { if (_responseType == "json") { _responseData = _engine->evaluate("(" + QString(_rawResponseData.data()) + ")"); - if (_responseData.isError()) { + if (_responseData->isError()) { _engine->clearExceptions(); - _responseData = QScriptValue::NullValue; + _responseData = _engine->nullValue(); } } else if (_responseType == "arraybuffer") { - QScriptValue data = _engine->newVariant(QVariant::fromValue(_rawResponseData)); - _responseData = _engine->newObject(reinterpret_cast(_engine)->getArrayBufferClass(), data); + _responseData = _engine->newArrayBuffer(_rawResponseData); } else { - _responseData = QScriptValue(QString(_rawResponseData.data())); + _responseData = _engine->newValue(QString(_rawResponseData.data())); } } diff --git a/libraries/script-engine/src/XMLHttpRequestClass.h b/libraries/script-engine/src/XMLHttpRequestClass.h index 01127e786db..8d3221c1955 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.h +++ b/libraries/script-engine/src/XMLHttpRequestClass.h @@ -16,10 +16,14 @@ #include #include #include -#include -#include -#include #include +#include + +#include "ScriptEngine.h" + +class ScriptContext; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; /* XMlHttpRequest object @@ -51,7 +55,7 @@ XMlHttpRequest.open(QString,QString,bool,QString) function XMlHttpRequest.open(QString,QString,bool) function XMlHttpRequest.open(QString,QString) function XMlHttpRequest.send() function -XMlHttpRequest.send(QScriptValue) function +XMlHttpRequest.send(ScriptValuePointer) function XMlHttpRequest.getAllResponseHeaders() function XMlHttpRequest.getResponseHeader(QString) function */ @@ -151,13 +155,13 @@ XMlHttpRequest.getResponseHeader(QString) function */ class XMLHttpRequestClass : public QObject { Q_OBJECT - Q_PROPERTY(QScriptValue response READ getResponse) - Q_PROPERTY(QScriptValue responseText READ getResponseText) + Q_PROPERTY(ScriptValuePointer response READ getResponse) + Q_PROPERTY(ScriptValuePointer responseText READ getResponseText) Q_PROPERTY(QString responseType READ getResponseType WRITE setResponseType) - Q_PROPERTY(QScriptValue status READ getStatus) + Q_PROPERTY(ScriptValuePointer status READ getStatus) Q_PROPERTY(QString statusText READ getStatusText) - Q_PROPERTY(QScriptValue readyState READ getReadyState) - Q_PROPERTY(QScriptValue errorCode READ getError) + Q_PROPERTY(ScriptValuePointer readyState READ getReadyState) + Q_PROPERTY(ScriptValuePointer errorCode READ getError) Q_PROPERTY(int timeout READ getTimeout WRITE setTimeout) Q_PROPERTY(int UNSENT READ getUnsent) @@ -167,10 +171,10 @@ class XMLHttpRequestClass : public QObject { Q_PROPERTY(int DONE READ getDone) // Callbacks - Q_PROPERTY(QScriptValue ontimeout READ getOnTimeout WRITE setOnTimeout) - Q_PROPERTY(QScriptValue onreadystatechange READ getOnReadyStateChange WRITE setOnReadyStateChange) + Q_PROPERTY(ScriptValuePointer ontimeout READ getOnTimeout WRITE setOnTimeout) + Q_PROPERTY(ScriptValuePointer onreadystatechange READ getOnReadyStateChange WRITE setOnReadyStateChange) public: - XMLHttpRequestClass(QScriptEngine* engine); + XMLHttpRequestClass(ScriptEngine* engine); ~XMLHttpRequestClass(); static const int MAXIMUM_REDIRECTS = 5; @@ -208,23 +212,23 @@ class XMLHttpRequestClass : public QObject { int getLoading() const { return LOADING; }; int getDone() const { return DONE; }; - static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); + static ScriptValuePointer constructor(ScriptContext* context, ScriptEngine* engine); int getTimeout() const { return _timeout; } void setTimeout(int timeout) { _timeout = timeout; } - QScriptValue getResponse() const { return _responseData; } - QScriptValue getResponseText() const { return QScriptValue(QString(_rawResponseData.data())); } + ScriptValuePointer getResponse() const { return _responseData; } + ScriptValuePointer getResponseText() const { return _engine->newValue(QString(_rawResponseData.data())); } QString getResponseType() const { return _responseType; } void setResponseType(const QString& responseType) { _responseType = responseType; } - QScriptValue getReadyState() const { return QScriptValue(_readyState); } - QScriptValue getError() const { return QScriptValue(_errorCode); } - QScriptValue getStatus() const; + ScriptValuePointer getReadyState() const { return _engine->newValue(_readyState); } + ScriptValuePointer getError() const { return _engine->newValue(_errorCode); } + ScriptValuePointer getStatus() const; QString getStatusText() const; - QScriptValue getOnTimeout() const { return _onTimeout; } - void setOnTimeout(QScriptValue function) { _onTimeout = function; } - QScriptValue getOnReadyStateChange() const { return _onReadyStateChange; } - void setOnReadyStateChange(QScriptValue function) { _onReadyStateChange = function; } + ScriptValuePointer getOnTimeout() const { return _onTimeout; } + void setOnTimeout(ScriptValuePointer function) { _onTimeout = function; } + ScriptValuePointer getOnReadyStateChange() const { return _onReadyStateChange; } + void setOnReadyStateChange(ScriptValuePointer function) { _onReadyStateChange = function; } public slots: @@ -263,14 +267,14 @@ public slots: * @param {*} [data] - The data to send. */ void send(); - void send(const QScriptValue& data); + void send(const ScriptValuePointer& data); /**jsdoc * Gets the response headers. * @function XMLHttpRequest.getAllResponseHeaders * @returns {string} The response headers, separated by "\n" characters. */ - QScriptValue getAllResponseHeaders() const; + ScriptValuePointer getAllResponseHeaders() const; /**jsdoc * Gets a response header. @@ -278,7 +282,7 @@ public slots: * @param {string} name - * @returns {string} The response header. */ - QScriptValue getResponseHeader(const QString& name) const; + ScriptValuePointer getResponseHeader(const QString& name) const; signals: @@ -296,7 +300,7 @@ public slots: void disconnectFromReply(QNetworkReply* reply); void abortRequest(); - QScriptEngine* _engine { nullptr }; + ScriptEngine* _engine { nullptr }; bool _async { true }; QUrl _url; QString _method; @@ -305,9 +309,9 @@ public slots: QNetworkReply* _reply { nullptr }; QByteArray _sendData; QByteArray _rawResponseData; - QScriptValue _responseData; - QScriptValue _onTimeout { QScriptValue::NullValue }; - QScriptValue _onReadyStateChange { QScriptValue::NullValue }; + ScriptValuePointer _responseData; + ScriptValuePointer _onTimeout; + ScriptValuePointer _onReadyStateChange; ReadyState _readyState { XMLHttpRequestClass::UNSENT }; /**jsdoc diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index 57904be5867..e9291974f56 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -3,7 +3,7 @@ set(TARGET_NAME shared) include_directories("${QT_DIR}/include/QtCore/${QT_VERSION}/QtCore" "${QT_DIR}/include/QtCore/${QT_VERSION}") # TODO: there isn't really a good reason to have Script linked here - let's get what is requiring it out (RegisteredMetaTypes.cpp) -setup_hifi_library(Gui Network Script) +setup_hifi_library(Gui Network) if (WIN32) target_link_libraries(${TARGET_NAME} Wbemuuid.lib) diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index d47cc0769a5..367a106a69b 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -22,8 +22,6 @@ #include #include #include -#include -#include #include int uint32MetaTypeId = qRegisterMetaType("uint32"); @@ -46,81 +44,6 @@ int voidLambdaType = qRegisterMetaType>(); int variantLambdaType = qRegisterMetaType>(); int stencilModeMetaTypeId = qRegisterMetaType(); -void registerMetaTypes(QScriptEngine* engine) { - qScriptRegisterMetaType(engine, vec2ToScriptValue, vec2FromScriptValue); - qScriptRegisterMetaType(engine, vec3ToScriptValue, vec3FromScriptValue); - qScriptRegisterMetaType(engine, u8vec3ToScriptValue, u8vec3FromScriptValue); - qScriptRegisterMetaType(engine, vec4toScriptValue, vec4FromScriptValue); - qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue); - qScriptRegisterMetaType(engine, mat4toScriptValue, mat4FromScriptValue); - - qScriptRegisterMetaType(engine, qVectorVec3ToScriptValue, qVectorVec3FromScriptValue); - qScriptRegisterMetaType(engine, qVectorQuatToScriptValue, qVectorQuatFromScriptValue); - qScriptRegisterMetaType(engine, qVectorBoolToScriptValue, qVectorBoolFromScriptValue); - qScriptRegisterMetaType(engine, qVectorFloatToScriptValue, qVectorFloatFromScriptValue); - qScriptRegisterMetaType(engine, qVectorIntToScriptValue, qVectorIntFromScriptValue); - qScriptRegisterMetaType(engine, qVectorQUuidToScriptValue, qVectorQUuidFromScriptValue); - - qScriptRegisterMetaType(engine, qSizeFToScriptValue, qSizeFFromScriptValue); - qScriptRegisterMetaType(engine, qRectToScriptValue, qRectFromScriptValue); - qScriptRegisterMetaType(engine, qURLToScriptValue, qURLFromScriptValue); - qScriptRegisterMetaType(engine, qColorToScriptValue, qColorFromScriptValue); - - qScriptRegisterMetaType(engine, pickRayToScriptValue, pickRayFromScriptValue); - qScriptRegisterMetaType(engine, collisionToScriptValue, collisionFromScriptValue); - qScriptRegisterMetaType(engine, quuidToScriptValue, quuidFromScriptValue); - qScriptRegisterMetaType(engine, aaCubeToScriptValue, aaCubeFromScriptValue); - - qScriptRegisterMetaType(engine, stencilMaskModeToScriptValue, stencilMaskModeFromScriptValue); - - qScriptRegisterSequenceMetaType>(engine); -} - -QScriptValue vec2ToScriptValue(QScriptEngine* engine, const glm::vec2& vec2) { - auto prototype = engine->globalObject().property("__hifi_vec2__"); - if (!prototype.property("defined").toBool()) { - prototype = engine->evaluate( - "__hifi_vec2__ = Object.defineProperties({}, { " - "defined: { value: true }," - "0: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," - "1: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," - "u: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," - "v: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }" - "})" - ); - } - QScriptValue value = engine->newObject(); - value.setProperty("x", vec2.x); - value.setProperty("y", vec2.y); - value.setPrototype(prototype); - return value; -} - -void vec2FromScriptValue(const QScriptValue& object, glm::vec2& vec2) { - if (object.isNumber()) { - vec2 = glm::vec2(object.toVariant().toFloat()); - } else if (object.isArray()) { - QVariantList list = object.toVariant().toList(); - if (list.length() == 2) { - vec2.x = list[0].toFloat(); - vec2.y = list[1].toFloat(); - } - } else { - QScriptValue x = object.property("x"); - if (!x.isValid()) { - x = object.property("u"); - } - - QScriptValue y = object.property("y"); - if (!y.isValid()) { - y = object.property("v"); - } - - vec2.x = x.toVariant().toFloat(); - vec2.y = y.toVariant().toFloat(); - } -} - QVariant vec2ToVariant(const glm::vec2 &vec2) { if (vec2.x != vec2.x || vec2.y != vec2.y) { // if vec2 contains a NaN don't try to convert it @@ -170,206 +93,6 @@ glm::vec2 vec2FromVariant(const QVariant &object) { return vec2FromVariant(object, valid); } -QScriptValue vec3ToScriptValue(QScriptEngine* engine, const glm::vec3& vec3) { - auto prototype = engine->globalObject().property("__hifi_vec3__"); - if (!prototype.property("defined").toBool()) { - prototype = engine->evaluate( - "__hifi_vec3__ = Object.defineProperties({}, { " - "defined: { value: true }," - "0: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," - "1: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," - "2: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }," - "r: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," - "g: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," - "b: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }," - "red: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," - "green: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," - "blue: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }" - "})" - ); - } - QScriptValue value = engine->newObject(); - value.setProperty("x", vec3.x); - value.setProperty("y", vec3.y); - value.setProperty("z", vec3.z); - value.setPrototype(prototype); - return value; -} - -QScriptValue vec3ColorToScriptValue(QScriptEngine* engine, const glm::vec3& vec3) { - auto prototype = engine->globalObject().property("__hifi_vec3_color__"); - if (!prototype.property("defined").toBool()) { - prototype = engine->evaluate( - "__hifi_vec3_color__ = Object.defineProperties({}, { " - "defined: { value: true }," - "0: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," - "1: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," - "2: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }," - "r: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," - "g: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," - "b: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }," - "x: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," - "y: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," - "z: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }" - "})" - ); - } - QScriptValue value = engine->newObject(); - value.setProperty("red", vec3.x); - value.setProperty("green", vec3.y); - value.setProperty("blue", vec3.z); - value.setPrototype(prototype); - return value; -} - -void vec3FromScriptValue(const QScriptValue& object, glm::vec3& vec3) { - if (object.isNumber()) { - vec3 = glm::vec3(object.toVariant().toFloat()); - } else if (object.isString()) { - QColor qColor(object.toString()); - if (qColor.isValid()) { - vec3.x = qColor.red(); - vec3.y = qColor.green(); - vec3.z = qColor.blue(); - } - } else if (object.isArray()) { - QVariantList list = object.toVariant().toList(); - if (list.length() == 3) { - vec3.x = list[0].toFloat(); - vec3.y = list[1].toFloat(); - vec3.z = list[2].toFloat(); - } - } else { - QScriptValue x = object.property("x"); - if (!x.isValid()) { - x = object.property("r"); - } - if (!x.isValid()) { - x = object.property("red"); - } - - QScriptValue y = object.property("y"); - if (!y.isValid()) { - y = object.property("g"); - } - if (!y.isValid()) { - y = object.property("green"); - } - - QScriptValue z = object.property("z"); - if (!z.isValid()) { - z = object.property("b"); - } - if (!z.isValid()) { - z = object.property("blue"); - } - - vec3.x = x.toVariant().toFloat(); - vec3.y = y.toVariant().toFloat(); - vec3.z = z.toVariant().toFloat(); - } -} - -QScriptValue u8vec3ToScriptValue(QScriptEngine* engine, const glm::u8vec3& vec3) { - auto prototype = engine->globalObject().property("__hifi_u8vec3__"); - if (!prototype.property("defined").toBool()) { - prototype = engine->evaluate( - "__hifi_u8vec3__ = Object.defineProperties({}, { " - "defined: { value: true }," - "0: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," - "1: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," - "2: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }," - "r: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," - "g: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," - "b: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }," - "red: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," - "green: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," - "blue: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }" - "})" - ); - } - QScriptValue value = engine->newObject(); - value.setProperty("x", vec3.x); - value.setProperty("y", vec3.y); - value.setProperty("z", vec3.z); - value.setPrototype(prototype); - return value; -} - -QScriptValue u8vec3ColorToScriptValue(QScriptEngine* engine, const glm::u8vec3& vec3) { - auto prototype = engine->globalObject().property("__hifi_u8vec3_color__"); - if (!prototype.property("defined").toBool()) { - prototype = engine->evaluate( - "__hifi_u8vec3_color__ = Object.defineProperties({}, { " - "defined: { value: true }," - "0: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," - "1: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," - "2: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }," - "r: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," - "g: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," - "b: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }," - "x: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," - "y: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," - "z: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }" - "})" - ); - } - QScriptValue value = engine->newObject(); - value.setProperty("red", vec3.x); - value.setProperty("green", vec3.y); - value.setProperty("blue", vec3.z); - value.setPrototype(prototype); - return value; -} - -void u8vec3FromScriptValue(const QScriptValue& object, glm::u8vec3& vec3) { - if (object.isNumber()) { - vec3 = glm::vec3(object.toVariant().toUInt()); - } else if (object.isString()) { - QColor qColor(object.toString()); - if (qColor.isValid()) { - vec3.x = (uint8_t)qColor.red(); - vec3.y = (uint8_t)qColor.green(); - vec3.z = (uint8_t)qColor.blue(); - } - } else if (object.isArray()) { - QVariantList list = object.toVariant().toList(); - if (list.length() == 3) { - vec3.x = list[0].toUInt(); - vec3.y = list[1].toUInt(); - vec3.z = list[2].toUInt(); - } - } else { - QScriptValue x = object.property("x"); - if (!x.isValid()) { - x = object.property("r"); - } - if (!x.isValid()) { - x = object.property("red"); - } - - QScriptValue y = object.property("y"); - if (!y.isValid()) { - y = object.property("g"); - } - if (!y.isValid()) { - y = object.property("green"); - } - - QScriptValue z = object.property("z"); - if (!z.isValid()) { - z = object.property("b"); - } - if (!z.isValid()) { - z = object.property("blue"); - } - - vec3.x = x.toVariant().toUInt(); - vec3.y = y.toVariant().toUInt(); - vec3.z = z.toVariant().toUInt(); - } -} - QVariant vec3toVariant(const glm::vec3& vec3) { if (vec3.x != vec3.x || vec3.y != vec3.y || vec3.z != vec3.z) { // if vec3 contains a NaN don't try to convert it @@ -540,22 +263,6 @@ glm::u8vec3 u8vec3FromVariant(const QVariant& object) { return u8vec3FromVariant(object, valid); } -QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4) { - QScriptValue obj = engine->newObject(); - obj.setProperty("x", vec4.x); - obj.setProperty("y", vec4.y); - obj.setProperty("z", vec4.z); - obj.setProperty("w", vec4.w); - return obj; -} - -void vec4FromScriptValue(const QScriptValue& object, glm::vec4& vec4) { - vec4.x = object.property("x").toVariant().toFloat(); - vec4.y = object.property("y").toVariant().toFloat(); - vec4.z = object.property("z").toVariant().toFloat(); - vec4.w = object.property("w").toVariant().toFloat(); -} - QVariant vec4toVariant(const glm::vec4& vec4) { if (isNaN(vec4.x) || isNaN(vec4.y) || isNaN(vec4.z) || isNaN(vec4.w)) { // if vec4 contains a NaN don't try to convert it @@ -606,46 +313,6 @@ glm::vec4 vec4FromVariant(const QVariant& object) { return vec4FromVariant(object, valid); } -QScriptValue mat4toScriptValue(QScriptEngine* engine, const glm::mat4& mat4) { - QScriptValue obj = engine->newObject(); - obj.setProperty("r0c0", mat4[0][0]); - obj.setProperty("r1c0", mat4[0][1]); - obj.setProperty("r2c0", mat4[0][2]); - obj.setProperty("r3c0", mat4[0][3]); - obj.setProperty("r0c1", mat4[1][0]); - obj.setProperty("r1c1", mat4[1][1]); - obj.setProperty("r2c1", mat4[1][2]); - obj.setProperty("r3c1", mat4[1][3]); - obj.setProperty("r0c2", mat4[2][0]); - obj.setProperty("r1c2", mat4[2][1]); - obj.setProperty("r2c2", mat4[2][2]); - obj.setProperty("r3c2", mat4[2][3]); - obj.setProperty("r0c3", mat4[3][0]); - obj.setProperty("r1c3", mat4[3][1]); - obj.setProperty("r2c3", mat4[3][2]); - obj.setProperty("r3c3", mat4[3][3]); - return obj; -} - -void mat4FromScriptValue(const QScriptValue& object, glm::mat4& mat4) { - mat4[0][0] = object.property("r0c0").toVariant().toFloat(); - mat4[0][1] = object.property("r1c0").toVariant().toFloat(); - mat4[0][2] = object.property("r2c0").toVariant().toFloat(); - mat4[0][3] = object.property("r3c0").toVariant().toFloat(); - mat4[1][0] = object.property("r0c1").toVariant().toFloat(); - mat4[1][1] = object.property("r1c1").toVariant().toFloat(); - mat4[1][2] = object.property("r2c1").toVariant().toFloat(); - mat4[1][3] = object.property("r3c1").toVariant().toFloat(); - mat4[2][0] = object.property("r0c2").toVariant().toFloat(); - mat4[2][1] = object.property("r1c2").toVariant().toFloat(); - mat4[2][2] = object.property("r2c2").toVariant().toFloat(); - mat4[2][3] = object.property("r3c2").toVariant().toFloat(); - mat4[3][0] = object.property("r0c3").toVariant().toFloat(); - mat4[3][1] = object.property("r1c3").toVariant().toFloat(); - mat4[3][2] = object.property("r2c3").toVariant().toFloat(); - mat4[3][3] = object.property("r3c3").toVariant().toFloat(); -} - QVariant mat4ToVariant(const glm::mat4& mat4) { if (mat4 != mat4) { // NaN @@ -721,72 +388,6 @@ glm::mat4 mat4FromVariant(const QVariant& object) { return mat4FromVariant(object, valid); } -QScriptValue qVectorVec3ColorToScriptValue(QScriptEngine* engine, const QVector& vector) { - QScriptValue array = engine->newArray(); - for (int i = 0; i < vector.size(); i++) { - array.setProperty(i, vec3ColorToScriptValue(engine, vector.at(i))); - } - return array; -} - -QScriptValue qVectorVec3ToScriptValue(QScriptEngine* engine, const QVector& vector) { - QScriptValue array = engine->newArray(); - for (int i = 0; i < vector.size(); i++) { - array.setProperty(i, vec3ToScriptValue(engine, vector.at(i))); - } - return array; -} - -QVector qVectorVec3FromScriptValue(const QScriptValue& array) { - QVector newVector; - int length = array.property("length").toInteger(); - - for (int i = 0; i < length; i++) { - glm::vec3 newVec3 = glm::vec3(); - vec3FromScriptValue(array.property(i), newVec3); - newVector << newVec3; - } - return newVector; -} - -void qVectorVec3FromScriptValue(const QScriptValue& array, QVector& vector) { - int length = array.property("length").toInteger(); - - for (int i = 0; i < length; i++) { - glm::vec3 newVec3 = glm::vec3(); - vec3FromScriptValue(array.property(i), newVec3); - vector << newVec3; - } -} - -QScriptValue quatToScriptValue(QScriptEngine* engine, const glm::quat& quat) { - QScriptValue obj = engine->newObject(); - if (quat.x != quat.x || quat.y != quat.y || quat.z != quat.z || quat.w != quat.w) { - // if quat contains a NaN don't try to convert it - return obj; - } - obj.setProperty("x", quat.x); - obj.setProperty("y", quat.y); - obj.setProperty("z", quat.z); - obj.setProperty("w", quat.w); - return obj; -} - -void quatFromScriptValue(const QScriptValue& object, glm::quat &quat) { - quat.x = object.property("x").toVariant().toFloat(); - quat.y = object.property("y").toVariant().toFloat(); - quat.z = object.property("z").toVariant().toFloat(); - quat.w = object.property("w").toVariant().toFloat(); - - // enforce normalized quaternion - float length = glm::length(quat); - if (length > FLT_EPSILON) { - quat /= length; - } else { - quat = glm::quat(); - } -} - glm::quat quatFromVariant(const QVariant &object, bool& isValid) { glm::quat q; if (object.canConvert()) { @@ -844,159 +445,6 @@ QVariant quatToVariant(const glm::quat& quat) { return result; } -QScriptValue qVectorQuatToScriptValue(QScriptEngine* engine, const QVector& vector) { - QScriptValue array = engine->newArray(); - for (int i = 0; i < vector.size(); i++) { - array.setProperty(i, quatToScriptValue(engine, vector.at(i))); - } - return array; -} - -QScriptValue qVectorBoolToScriptValue(QScriptEngine* engine, const QVector& vector) { - QScriptValue array = engine->newArray(); - for (int i = 0; i < vector.size(); i++) { - array.setProperty(i, vector.at(i)); - } - return array; -} - -QVector qVectorFloatFromScriptValue(const QScriptValue& array) { - if(!array.isArray()) { - return QVector(); - } - QVector newVector; - int length = array.property("length").toInteger(); - newVector.reserve(length); - for (int i = 0; i < length; i++) { - if(array.property(i).isNumber()) { - newVector << array.property(i).toNumber(); - } - } - - return newVector; -} - -QScriptValue qVectorQUuidToScriptValue(QScriptEngine* engine, const QVector& vector) { - QScriptValue array = engine->newArray(); - for (int i = 0; i < vector.size(); i++) { - array.setProperty(i, quuidToScriptValue(engine, vector.at(i))); - } - return array; -} - -void qVectorQUuidFromScriptValue(const QScriptValue& array, QVector& vector) { - int length = array.property("length").toInteger(); - - for (int i = 0; i < length; i++) { - vector << array.property(i).toVariant().toUuid(); - } -} - -QVector qVectorQUuidFromScriptValue(const QScriptValue& array) { - if (!array.isArray()) { - return QVector(); - } - QVector newVector; - int length = array.property("length").toInteger(); - newVector.reserve(length); - for (int i = 0; i < length; i++) { - QString uuidAsString = array.property(i).toString(); - QUuid fromString(uuidAsString); - newVector << fromString; - } - return newVector; -} - -QScriptValue qVectorFloatToScriptValue(QScriptEngine* engine, const QVector& vector) { - QScriptValue array = engine->newArray(); - for (int i = 0; i < vector.size(); i++) { - float num = vector.at(i); - array.setProperty(i, QScriptValue(num)); - } - return array; -} - -QScriptValue qVectorIntToScriptValue(QScriptEngine* engine, const QVector& vector) { - QScriptValue array = engine->newArray(); - for (int i = 0; i < vector.size(); i++) { - int num = vector.at(i); - array.setProperty(i, QScriptValue(num)); - } - return array; -} - -void qVectorFloatFromScriptValue(const QScriptValue& array, QVector& vector) { - int length = array.property("length").toInteger(); - - for (int i = 0; i < length; i++) { - vector << array.property(i).toVariant().toFloat(); - } -} - -void qVectorIntFromScriptValue(const QScriptValue& array, QVector& vector) { - int length = array.property("length").toInteger(); - - for (int i = 0; i < length; i++) { - vector << array.property(i).toVariant().toInt(); - } -} - -QVector qVectorQuatFromScriptValue(const QScriptValue& array){ - QVector newVector; - int length = array.property("length").toInteger(); - - for (int i = 0; i < length; i++) { - glm::quat newQuat = glm::quat(); - quatFromScriptValue(array.property(i), newQuat); - newVector << newQuat; - } - return newVector; -} - -void qVectorQuatFromScriptValue(const QScriptValue& array, QVector& vector ) { - int length = array.property("length").toInteger(); - - for (int i = 0; i < length; i++) { - glm::quat newQuat = glm::quat(); - quatFromScriptValue(array.property(i), newQuat); - vector << newQuat; - } -} - -QVector qVectorBoolFromScriptValue(const QScriptValue& array){ - QVector newVector; - int length = array.property("length").toInteger(); - - for (int i = 0; i < length; i++) { - newVector << array.property(i).toBool(); - } - return newVector; -} - -void qVectorBoolFromScriptValue(const QScriptValue& array, QVector& vector ) { - int length = array.property("length").toInteger(); - - for (int i = 0; i < length; i++) { - vector << array.property(i).toBool(); - } -} - -QScriptValue qRectToScriptValue(QScriptEngine* engine, const QRect& rect) { - QScriptValue obj = engine->newObject(); - obj.setProperty("x", rect.x()); - obj.setProperty("y", rect.y()); - obj.setProperty("width", rect.width()); - obj.setProperty("height", rect.height()); - return obj; -} - -void qRectFromScriptValue(const QScriptValue &object, QRect& rect) { - rect.setX(object.property("x").toVariant().toInt()); - rect.setY(object.property("y").toVariant().toInt()); - rect.setWidth(object.property("width").toVariant().toInt()); - rect.setHeight(object.property("height").toVariant().toInt()); -} - QVariant qRectToVariant(const QRect& rect) { QVariantMap obj; obj["x"] = rect.x(); @@ -1028,22 +476,6 @@ QRect qRectFromVariant(const QVariant& object) { return qRectFromVariant(object, valid); } -QScriptValue qRectFToScriptValue(QScriptEngine* engine, const QRectF& rect) { - QScriptValue obj = engine->newObject(); - obj.setProperty("x", rect.x()); - obj.setProperty("y", rect.y()); - obj.setProperty("width", rect.width()); - obj.setProperty("height", rect.height()); - return obj; -} - -void qRectFFromScriptValue(const QScriptValue &object, QRectF& rect) { - rect.setX(object.property("x").toVariant().toFloat()); - rect.setY(object.property("y").toVariant().toFloat()); - rect.setWidth(object.property("width").toVariant().toFloat()); - rect.setHeight(object.property("height").toVariant().toFloat()); -} - QVariant qRectFToVariant(const QRectF& rect) { QVariantMap obj; obj["x"] = rect.x(); @@ -1075,100 +507,6 @@ QRectF qRectFFromVariant(const QVariant& object) { return qRectFFromVariant(object, valid); } -QScriptValue qColorToScriptValue(QScriptEngine* engine, const QColor& color) { - QScriptValue object = engine->newObject(); - object.setProperty("red", color.red()); - object.setProperty("green", color.green()); - object.setProperty("blue", color.blue()); - object.setProperty("alpha", color.alpha()); - return object; -} - -/**jsdoc - * An axis-aligned cube, defined as the bottom right near (minimum axes values) corner of the cube plus the dimension of its - * sides. - * @typedef {object} AACube - * @property {number} x - X coordinate of the brn corner of the cube. - * @property {number} y - Y coordinate of the brn corner of the cube. - * @property {number} z - Z coordinate of the brn corner of the cube. - * @property {number} scale - The dimensions of each side of the cube. - */ -QScriptValue aaCubeToScriptValue(QScriptEngine* engine, const AACube& aaCube) { - QScriptValue obj = engine->newObject(); - const glm::vec3& corner = aaCube.getCorner(); - obj.setProperty("x", corner.x); - obj.setProperty("y", corner.y); - obj.setProperty("z", corner.z); - obj.setProperty("scale", aaCube.getScale()); - return obj; -} - -void aaCubeFromScriptValue(const QScriptValue &object, AACube& aaCube) { - glm::vec3 corner; - corner.x = object.property("x").toVariant().toFloat(); - corner.y = object.property("y").toVariant().toFloat(); - corner.z = object.property("z").toVariant().toFloat(); - float scale = object.property("scale").toVariant().toFloat(); - - aaCube.setBox(corner, scale); -} - -void qColorFromScriptValue(const QScriptValue& object, QColor& color) { - if (object.isNumber()) { - color.setRgb(object.toUInt32()); - - } else if (object.isString()) { - color.setNamedColor(object.toString()); - - } else { - QScriptValue alphaValue = object.property("alpha"); - color.setRgb(object.property("red").toInt32(), object.property("green").toInt32(), object.property("blue").toInt32(), - alphaValue.isNumber() ? alphaValue.toInt32() : 255); - } -} - -QScriptValue qURLToScriptValue(QScriptEngine* engine, const QUrl& url) { - return url.toString(); -} - -void qURLFromScriptValue(const QScriptValue& object, QUrl& url) { - url = object.toString(); -} - -QScriptValue pickRayToScriptValue(QScriptEngine* engine, const PickRay& pickRay) { - QScriptValue obj = engine->newObject(); - QScriptValue origin = vec3ToScriptValue(engine, pickRay.origin); - obj.setProperty("origin", origin); - QScriptValue direction = vec3ToScriptValue(engine, pickRay.direction); - obj.setProperty("direction", direction); - return obj; -} - -void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay) { - QScriptValue originValue = object.property("origin"); - if (originValue.isValid()) { - auto x = originValue.property("x"); - auto y = originValue.property("y"); - auto z = originValue.property("z"); - if (x.isValid() && y.isValid() && z.isValid()) { - pickRay.origin.x = x.toVariant().toFloat(); - pickRay.origin.y = y.toVariant().toFloat(); - pickRay.origin.z = z.toVariant().toFloat(); - } - } - QScriptValue directionValue = object.property("direction"); - if (directionValue.isValid()) { - auto x = directionValue.property("x"); - auto y = directionValue.property("y"); - auto z = directionValue.property("z"); - if (x.isValid() && y.isValid() && z.isValid()) { - pickRay.direction.x = x.toVariant().toFloat(); - pickRay.direction.y = y.toVariant().toFloat(); - pickRay.direction.z = z.toVariant().toFloat(); - } - } -} - /**jsdoc * Details of a collision between avatars and entities. * @typedef {object} Collision @@ -1179,63 +517,12 @@ void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay) { * @property {Vec3} contactPoint - The point of contact. * @property {Vec3} velocityChange - The change in relative velocity of the two items, in m/s. */ -QScriptValue collisionToScriptValue(QScriptEngine* engine, const Collision& collision) { - QScriptValue obj = engine->newObject(); - obj.setProperty("type", collision.type); - obj.setProperty("idA", quuidToScriptValue(engine, collision.idA)); - obj.setProperty("idB", quuidToScriptValue(engine, collision.idB)); - obj.setProperty("penetration", vec3ToScriptValue(engine, collision.penetration)); - obj.setProperty("contactPoint", vec3ToScriptValue(engine, collision.contactPoint)); - obj.setProperty("velocityChange", vec3ToScriptValue(engine, collision.velocityChange)); - return obj; -} - -void collisionFromScriptValue(const QScriptValue &object, Collision& collision) { - // TODO: implement this when we know what it means to accept collision events from JS -} - void Collision::invert() { std::swap(idA, idB); contactPoint += penetration; penetration *= -1.0f; } -QScriptValue quuidToScriptValue(QScriptEngine* engine, const QUuid& uuid) { - if (uuid.isNull()) { - return QScriptValue::NullValue; - } - QScriptValue obj(uuid.toString()); - return obj; -} - -void quuidFromScriptValue(const QScriptValue& object, QUuid& uuid) { - if (object.isNull()) { - uuid = QUuid(); - return; - } - QString uuidAsString = object.toVariant().toString(); - QUuid fromString(uuidAsString); - uuid = fromString; -} - -/**jsdoc - * A 2D size value. - * @typedef {object} Size - * @property {number} height - The height value. - * @property {number} width - The width value. - */ -QScriptValue qSizeFToScriptValue(QScriptEngine* engine, const QSizeF& qSizeF) { - QScriptValue obj = engine->newObject(); - obj.setProperty("width", qSizeF.width()); - obj.setProperty("height", qSizeF.height()); - return obj; -} - -void qSizeFFromScriptValue(const QScriptValue& object, QSizeF& qSizeF) { - qSizeF.setWidth(object.property("width").toVariant().toFloat()); - qSizeF.setHeight(object.property("height").toVariant().toFloat()); -} - AnimationDetails::AnimationDetails() : role(), url(), fps(0.0f), priority(0.0f), loop(false), hold(false), startAutomatically(false), firstFrame(0.0f), lastFrame(0.0f), running(false), currentFrame(0.0f) { @@ -1248,116 +535,6 @@ AnimationDetails::AnimationDetails(QString role, QUrl url, float fps, float prio running(running), currentFrame(currentFrame), allowTranslation(allowTranslation) { } -/**jsdoc - * The details of an animation that is playing. - * @typedef {object} Avatar.AnimationDetails - * @property {string} role - Not used. - * @property {string} url - The URL to the animation file. Animation files need to be in glTF or FBX format but only need to - * contain the avatar skeleton and animation data. glTF models may be in JSON or binary format (".gltf" or ".glb" URLs - * respectively). - *

Warning: glTF animations currently do not always animate correctly.

- * @property {number} fps - The frames per second(FPS) rate for the animation playback. 30 FPS is normal speed. - * @property {number} priority - Not used. - * @property {boolean} loop - true if the animation should loop, false if it shouldn't. - * @property {boolean} hold - Not used. - * @property {number} firstFrame - The frame the animation should start at. - * @property {number} lastFrame - The frame the animation should stop at. - * @property {boolean} running - Not used. - * @property {number} currentFrame - The current frame being played. - * @property {boolean} startAutomatically - Not used. - * @property {boolean} allowTranslation - Not used. - */ -QScriptValue animationDetailsToScriptValue(QScriptEngine* engine, const AnimationDetails& details) { - QScriptValue obj = engine->newObject(); - obj.setProperty("role", details.role); - obj.setProperty("url", details.url.toString()); - obj.setProperty("fps", details.fps); - obj.setProperty("priority", details.priority); - obj.setProperty("loop", details.loop); - obj.setProperty("hold", details.hold); - obj.setProperty("startAutomatically", details.startAutomatically); - obj.setProperty("firstFrame", details.firstFrame); - obj.setProperty("lastFrame", details.lastFrame); - obj.setProperty("running", details.running); - obj.setProperty("currentFrame", details.currentFrame); - obj.setProperty("allowTranslation", details.allowTranslation); - return obj; -} - -void animationDetailsFromScriptValue(const QScriptValue& object, AnimationDetails& details) { - // nothing for now... -} - -QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in) { - return engine->newQObject(in, QScriptEngine::QtOwnership, - QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); -} - -void meshFromScriptValue(const QScriptValue& value, MeshProxy* &out) { - out = qobject_cast(value.toQObject()); -} - -QScriptValue meshesToScriptValue(QScriptEngine* engine, const MeshProxyList &in) { - // QScriptValueList result; - QScriptValue result = engine->newArray(); - int i = 0; - foreach(MeshProxy* const meshProxy, in) { - result.setProperty(i++, meshToScriptValue(engine, meshProxy)); - } - return result; -} - -void meshesFromScriptValue(const QScriptValue& value, MeshProxyList &out) { - QScriptValueIterator itr(value); - - qDebug() << "in meshesFromScriptValue, value.length =" << value.property("length").toInt32(); - - while (itr.hasNext()) { - itr.next(); - MeshProxy* meshProxy = qscriptvalue_cast(itr.value()); - if (meshProxy) { - out.append(meshProxy); - } else { - qDebug() << "null meshProxy"; - } - } -} - - -/**jsdoc - * A triangle in a mesh. - * @typedef {object} MeshFace - * @property {number[]} vertices - The indexes of the three vertices that make up the face. - */ -QScriptValue meshFaceToScriptValue(QScriptEngine* engine, const MeshFace &meshFace) { - QScriptValue obj = engine->newObject(); - obj.setProperty("vertices", qVectorIntToScriptValue(engine, meshFace.vertexIndices)); - return obj; -} - -void meshFaceFromScriptValue(const QScriptValue &object, MeshFace& meshFaceResult) { - qVectorIntFromScriptValue(object.property("vertices"), meshFaceResult.vertexIndices); -} - -QScriptValue qVectorMeshFaceToScriptValue(QScriptEngine* engine, const QVector& vector) { - QScriptValue array = engine->newArray(); - for (int i = 0; i < vector.size(); i++) { - array.setProperty(i, meshFaceToScriptValue(engine, vector.at(i))); - } - return array; -} - -void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector& result) { - int length = array.property("length").toInteger(); - result.clear(); - - for (int i = 0; i < length; i++) { - MeshFace meshFace = MeshFace(); - meshFaceFromScriptValue(array.property(i), meshFace); - result << meshFace; - } -} - QVariantMap parseTexturesToMap(QString newTextures, const QVariantMap& defaultTextures) { // If textures are unset, revert to original textures if (newTextures.isEmpty()) { @@ -1392,11 +569,3 @@ QVariantMap parseTexturesToMap(QString newTextures, const QVariantMap& defaultTe return toReturn; } - -QScriptValue stencilMaskModeToScriptValue(QScriptEngine* engine, const StencilMaskMode& stencilMode) { - return engine->newVariant((int)stencilMode); -} - -void stencilMaskModeFromScriptValue(const QScriptValue& object, StencilMaskMode& stencilMode) { - stencilMode = StencilMaskMode(object.toVariant().toInt()); -} \ No newline at end of file diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 49a73d40c27..8a731d147ef 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -12,7 +12,6 @@ #ifndef hifi_RegisteredMetaTypes_h #define hifi_RegisteredMetaTypes_h -#include #include #include @@ -43,8 +42,6 @@ Q_DECLARE_METATYPE(AACube) Q_DECLARE_METATYPE(std::function); Q_DECLARE_METATYPE(std::function); -void registerMetaTypes(QScriptEngine* engine); - // Mat4 /**jsdoc * A 4 x 4 matrix, typically containing a scale, rotation, and translation transform. See also the {@link Mat4(0)|Mat4} object. @@ -67,9 +64,6 @@ void registerMetaTypes(QScriptEngine* engine); * @property {number} r2c3 - Row 2, column 3 value. * @property {number} r3c3 - Row 3, column 3 value. */ -QScriptValue mat4toScriptValue(QScriptEngine* engine, const glm::mat4& mat4); -void mat4FromScriptValue(const QScriptValue& object, glm::mat4& mat4); - QVariant mat4ToVariant(const glm::mat4& mat4); glm::mat4 mat4FromVariant(const QVariant& object, bool& valid); glm::mat4 mat4FromVariant(const QVariant& object); @@ -88,9 +82,6 @@ glm::mat4 mat4FromVariant(const QVariant& object); * var color = Entities.getEntityProperties().materialMappingPos; // { x: 0.7, y: 0.7 } * color.v = 0.8; // { x: 0.7, y: 0.8 } */ -QScriptValue vec2ToScriptValue(QScriptEngine* engine, const glm::vec2& vec2); -void vec2FromScriptValue(const QScriptValue& object, glm::vec2& vec2); - QVariant vec2ToVariant(const glm::vec2& vec2); glm::vec2 vec2FromVariant(const QVariant& object, bool& valid); glm::vec2 vec2FromVariant(const QVariant& object); @@ -115,10 +106,6 @@ glm::vec2 vec2FromVariant(const QVariant& object); * Entities.editEntity(, { position: "red"}); // { x: 255, y: 0, z: 0 } * Entities.editEntity(, { position: "#00FF00"}); // { x: 0, y: 255, z: 0 } */ -QScriptValue vec3ToScriptValue(QScriptEngine* engine, const glm::vec3& vec3); -QScriptValue vec3ColorToScriptValue(QScriptEngine* engine, const glm::vec3& vec3); -void vec3FromScriptValue(const QScriptValue& object, glm::vec3& vec3); - QVariant vec3toVariant(const glm::vec3& vec3); glm::vec3 vec3FromVariant(const QVariant &object, bool& valid); glm::vec3 vec3FromVariant(const QVariant &object); @@ -161,10 +148,6 @@ glm::vec3 vec3FromVariant(const QVariant &object); * Entities.editEntity(, { color: "red"}); // { red: 255, green: 0, blue: 0 } * Entities.editEntity(, { color: "#00FF00"}); // { red: 0, green: 255, blue: 0 } */ -QScriptValue u8vec3ToScriptValue(QScriptEngine* engine, const glm::u8vec3& vec3); -QScriptValue u8vec3ColorToScriptValue(QScriptEngine* engine, const glm::u8vec3& vec3); -void u8vec3FromScriptValue(const QScriptValue& object, glm::u8vec3& vec3); - QVariant u8vec3toVariant(const glm::u8vec3& vec3); QVariant u8vec3ColortoVariant(const glm::u8vec3& vec3); glm::u8vec3 u8vec3FromVariant(const QVariant &object, bool& valid); @@ -179,16 +162,11 @@ glm::u8vec3 u8vec3FromVariant(const QVariant &object); * @property {number} z - Z-coordinate of the vector. * @property {number} w - W-coordinate of the vector. */ -QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4); -void vec4FromScriptValue(const QScriptValue& object, glm::vec4& vec4); QVariant vec4toVariant(const glm::vec4& vec4); glm::vec4 vec4FromVariant(const QVariant &object, bool& valid); glm::vec4 vec4FromVariant(const QVariant &object); // Quaternions -QScriptValue quatToScriptValue(QScriptEngine* engine, const glm::quat& quat); -void quatFromScriptValue(const QScriptValue &object, glm::quat& quat); - QVariant quatToVariant(const glm::quat& quat); glm::quat quatFromVariant(const QVariant &object, bool& isValid); glm::quat quatFromVariant(const QVariant &object); @@ -201,59 +179,14 @@ glm::quat quatFromVariant(const QVariant &object); * @property {number} width - Width of the rectangle. * @property {number} height - Height of the rectangle. */ -QScriptValue qRectToScriptValue(QScriptEngine* engine, const QRect& rect); -void qRectFromScriptValue(const QScriptValue& object, QRect& rect); QRect qRectFromVariant(const QVariant& object, bool& isValid); QRect qRectFromVariant(const QVariant& object); QVariant qRectToVariant(const QRect& rect); -QScriptValue qRectFToScriptValue(QScriptEngine* engine, const QRectF& rect); -void qRectFFromScriptValue(const QScriptValue& object, QRectF& rect); QRectF qRectFFromVariant(const QVariant& object, bool& isValid); QRectF qRectFFromVariant(const QVariant& object); QVariant qRectFToVariant(const QRectF& rect); -// QColor -QScriptValue qColorToScriptValue(QScriptEngine* engine, const QColor& color); -void qColorFromScriptValue(const QScriptValue& object, QColor& color); - -QScriptValue qURLToScriptValue(QScriptEngine* engine, const QUrl& url); -void qURLFromScriptValue(const QScriptValue& object, QUrl& url); - -// vector -Q_DECLARE_METATYPE(QVector) -QScriptValue qVectorVec3ToScriptValue(QScriptEngine* engine, const QVector& vector); -QScriptValue qVectorVec3ColorToScriptValue(QScriptEngine* engine, const QVector& vector); -void qVectorVec3FromScriptValue(const QScriptValue& array, QVector& vector); -QVector qVectorVec3FromScriptValue(const QScriptValue& array); - -// vector -Q_DECLARE_METATYPE(QVector) -QScriptValue qVectorQuatToScriptValue(QScriptEngine* engine, const QVector& vector); -void qVectorQuatFromScriptValue(const QScriptValue& array, QVector& vector); -QVector qVectorQuatFromScriptValue(const QScriptValue& array); - -// vector -QScriptValue qVectorBoolToScriptValue(QScriptEngine* engine, const QVector& vector); -void qVectorBoolFromScriptValue(const QScriptValue& array, QVector& vector); -QVector qVectorBoolFromScriptValue(const QScriptValue& array); - -// vector -QScriptValue qVectorFloatToScriptValue(QScriptEngine* engine, const QVector& vector); -void qVectorFloatFromScriptValue(const QScriptValue& array, QVector& vector); -QVector qVectorFloatFromScriptValue(const QScriptValue& array); - -// vector -QScriptValue qVectorIntToScriptValue(QScriptEngine* engine, const QVector& vector); -void qVectorIntFromScriptValue(const QScriptValue& array, QVector& vector); - -QScriptValue qVectorQUuidToScriptValue(QScriptEngine* engine, const QVector& vector); -void qVectorQUuidFromScriptValue(const QScriptValue& array, QVector& vector); -QVector qVectorQUuidFromScriptValue(const QScriptValue& array); - -QScriptValue aaCubeToScriptValue(QScriptEngine* engine, const AACube& aaCube); -void aaCubeFromScriptValue(const QScriptValue &object, AACube& aaCube); - // MathPicks also have to overide operator== for their type class MathPick { public: @@ -292,8 +225,6 @@ class PickRay : public MathPick { } }; Q_DECLARE_METATYPE(PickRay) -QScriptValue pickRayToScriptValue(QScriptEngine* engine, const PickRay& pickRay); -void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay); /**jsdoc * The tip of a stylus. @@ -644,22 +575,6 @@ class Collision { glm::vec3 velocityChange; }; Q_DECLARE_METATYPE(Collision) -QScriptValue collisionToScriptValue(QScriptEngine* engine, const Collision& collision); -void collisionFromScriptValue(const QScriptValue &object, Collision& collision); - -/**jsdoc - * UUIDs (Universally Unique IDentifiers) are used to uniquely identify entities, avatars, and the like. They are represented - * in JavaScript as strings in the format, "{nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn}", where the "n"s are - * hexadecimal digits. - * @typedef {string} Uuid - */ -//Q_DECLARE_METATYPE(QUuid) // don't need to do this for QUuid since it's already a meta type -QScriptValue quuidToScriptValue(QScriptEngine* engine, const QUuid& uuid); -void quuidFromScriptValue(const QScriptValue& object, QUuid& uuid); - -//Q_DECLARE_METATYPE(QSizeF) // Don't need to to this becase it's arleady a meta type -QScriptValue qSizeFToScriptValue(QScriptEngine* engine, const QSizeF& qSizeF); -void qSizeFFromScriptValue(const QScriptValue& object, QSizeF& qSizeF); class AnimationDetails { public: @@ -681,8 +596,6 @@ class AnimationDetails { bool allowTranslation; }; Q_DECLARE_METATYPE(AnimationDetails); -QScriptValue animationDetailsToScriptValue(QScriptEngine* engine, const AnimationDetails& event); -void animationDetailsFromScriptValue(const QScriptValue& object, AnimationDetails& event); namespace graphics { class Mesh; @@ -733,12 +646,6 @@ class MeshProxyList : public QList {}; // typedef and using fight wi Q_DECLARE_METATYPE(MeshProxyList); -QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in); -void meshFromScriptValue(const QScriptValue& value, MeshProxy* &out); - -QScriptValue meshesToScriptValue(QScriptEngine* engine, const MeshProxyList &in); -void meshesFromScriptValue(const QScriptValue& value, MeshProxyList &out); - class MeshFace { public: @@ -752,15 +659,8 @@ class MeshFace { Q_DECLARE_METATYPE(MeshFace) Q_DECLARE_METATYPE(QVector) -QScriptValue meshFaceToScriptValue(QScriptEngine* engine, const MeshFace &meshFace); -void meshFaceFromScriptValue(const QScriptValue &object, MeshFace& meshFaceResult); -QScriptValue qVectorMeshFaceToScriptValue(QScriptEngine* engine, const QVector& vector); -void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector& result); - QVariantMap parseTexturesToMap(QString textures, const QVariantMap& defaultTextures); Q_DECLARE_METATYPE(StencilMaskMode) -QScriptValue stencilMaskModeToScriptValue(QScriptEngine* engine, const StencilMaskMode& stencilMode); -void stencilMaskModeFromScriptValue(const QScriptValue& object, StencilMaskMode& stencilMode); #endif // hifi_RegisteredMetaTypes_h diff --git a/libraries/shared/src/ScriptValueUtils.cpp b/libraries/shared/src/ScriptValueUtils.cpp deleted file mode 100644 index e352c0546d9..00000000000 --- a/libraries/shared/src/ScriptValueUtils.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// -// ScriptValueUtils.cpp -// libraries/shared/src -// -// Created by Anthony Thibault on 4/15/16. -// Copyright 2016 High Fidelity, Inc. -// -// Utilities for working with QtScriptValues -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "ScriptValueUtils.h" - -bool isListOfStrings(const QScriptValue& arg) { - if (!arg.isArray()) { - return false; - } - - auto lengthProperty = arg.property("length"); - if (!lengthProperty.isNumber()) { - return false; - } - - int length = lengthProperty.toInt32(); - for (int i = 0; i < length; i++) { - if (!arg.property(i).isString()) { - return false; - } - } - - return true; -} diff --git a/libraries/shared/src/ScriptValueUtils.h b/libraries/shared/src/ScriptValueUtils.h deleted file mode 100644 index dc2f41997b4..00000000000 --- a/libraries/shared/src/ScriptValueUtils.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// ScriptValueUtils.h -// libraries/shared/src -// -// Created by Anthony Thibault on 4/15/16. -// Copyright 2016 High Fidelity, Inc. -// -// Utilities for working with QtScriptValues -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_ScriptValueUtils_h -#define hifi_ScriptValueUtils_h - -#include - -bool isListOfStrings(const QScriptValue& value); - -#endif // #define hifi_ScriptValueUtils_h diff --git a/libraries/shared/src/VariantMapToScriptValue.h b/libraries/shared/src/VariantMapToScriptValue.h deleted file mode 100644 index 4b283671b43..00000000000 --- a/libraries/shared/src/VariantMapToScriptValue.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// VariantMapToScriptValue.h -// libraries/shared/src/ -// -// Created by Brad Hefta-Gaub on 12/6/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include - -QScriptValue variantToScriptValue(QVariant& qValue, QScriptEngine& scriptEngine); -QScriptValue variantMapToScriptValue(QVariantMap& variantMap, QScriptEngine& scriptEngine); -QScriptValue variantListToScriptValue(QVariantList& variantList, QScriptEngine& scriptEngine); diff --git a/libraries/shared/src/shared/MiniPromises.cpp b/libraries/shared/src/shared/MiniPromises.cpp index 21a3f44d50f..57b4496b96f 100644 --- a/libraries/shared/src/shared/MiniPromises.cpp +++ b/libraries/shared/src/shared/MiniPromises.cpp @@ -7,20 +7,5 @@ // #include "MiniPromises.h" -#include -#include int MiniPromise::metaTypeID = qRegisterMetaType("MiniPromise::Promise"); - -namespace { - void promiseFromScriptValue(const QScriptValue& object, MiniPromise::Promise& promise) { - Q_ASSERT(false); - } - QScriptValue promiseToScriptValue(QScriptEngine *engine, const MiniPromise::Promise& promise) { - return engine->newQObject(promise.get()); - } -} -void MiniPromise::registerMetaTypes(QObject* engine) { - auto scriptEngine = qobject_cast(engine); - qScriptRegisterMetaType(scriptEngine, promiseToScriptValue, promiseFromScriptValue); -} diff --git a/libraries/shared/src/shared/MiniPromises.h b/libraries/shared/src/shared/MiniPromises.h index 30b57ad7b84..e49bc48d18e 100644 --- a/libraries/shared/src/shared/MiniPromises.h +++ b/libraries/shared/src/shared/MiniPromises.h @@ -42,7 +42,6 @@ class MiniPromise : public QObject, public std::enable_shared_from_this; using Promise = std::shared_ptr; - static void registerMetaTypes(QObject* engine); static int metaTypeID; MiniPromise() {} diff --git a/libraries/shared/src/shared/ScriptInitializerMixin.h b/libraries/shared/src/shared/ScriptInitializerMixin.h index cefa33c2d3b..789606331d2 100644 --- a/libraries/shared/src/shared/ScriptInitializerMixin.h +++ b/libraries/shared/src/shared/ScriptInitializerMixin.h @@ -13,7 +13,6 @@ #include #include "../DependencyManager.h" -class QScriptEngine; class ScriptEngine; template class ScriptInitializerMixin { @@ -36,11 +35,11 @@ template class ScriptInitializerMixin { std::list _scriptInitializers; }; -class ScriptInitializers : public ScriptInitializerMixin, public Dependency { +class ScriptInitializers : public ScriptInitializerMixin, public Dependency { public: - // Lightweight `QScriptEngine*` initializer (only depends on built-in Qt components) + // Lightweight `ScriptEngine*` initializer (only depends on built-in Qt components) // example registration: - // eg: [&](QScriptEngine* engine) { + // eg: [&](ScriptEngine* engine) { // engine->globalObject().setProperties("API", engine->newQObject(...instance...)) // }; }; diff --git a/libraries/ui/CMakeLists.txt b/libraries/ui/CMakeLists.txt index 3625bd4f3e7..d7be1d3e618 100644 --- a/libraries/ui/CMakeLists.txt +++ b/libraries/ui/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME ui) -setup_hifi_library(OpenGL Multimedia Network Qml Quick Script WebChannel WebSockets XmlPatterns ${PLATFORM_QT_COMPONENTS}) -link_hifi_libraries(shared networking qml gl audio audio-client plugins pointers) +setup_hifi_library(OpenGL Multimedia Network Qml Quick WebChannel WebSockets XmlPatterns ${PLATFORM_QT_COMPONENTS}) +link_hifi_libraries(shared networking qml gl audio audio-client plugins pointers script-engine) include_hifi_library_headers(controllers) # Required for some low level GL interaction in the OffscreenQMLSurface diff --git a/libraries/ui/src/QmlFragmentClass.cpp b/libraries/ui/src/QmlFragmentClass.cpp index 1219094afcb..440ece91073 100644 --- a/libraries/ui/src/QmlFragmentClass.cpp +++ b/libraries/ui/src/QmlFragmentClass.cpp @@ -9,21 +9,22 @@ #include "QmlFragmentClass.h" #include -#include -#include #include +#include +#include +#include std::mutex QmlFragmentClass::_mutex; -std::map QmlFragmentClass::_fragments; +std::map QmlFragmentClass::_fragments; QmlFragmentClass::QmlFragmentClass(bool restricted, QString id) : QmlWindowClass(restricted), qml(id) { } // Method called by Qt scripts to create a new bottom menu bar in Android -QScriptValue QmlFragmentClass::internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted) { +ScriptValuePointer QmlFragmentClass::internal_constructor(ScriptContext* context, ScriptEngine* engine, bool restricted) { #ifndef DISABLE_QML std::lock_guard guard(_mutex); - auto qml = context->argument(0).toVariant().toMap().value("qml"); + auto qml = context->argument(0)->toVariant().toMap().value("qml"); if (qml.isValid()) { // look up tabletId in the map. auto iter = _fragments.find(qml.toString()); @@ -33,7 +34,7 @@ QScriptValue QmlFragmentClass::internal_constructor(QScriptContext* context, QSc } } else { qWarning() << "QmlFragmentClass could not build instance " << qml; - return QScriptValue(); + return ScriptValuePointer(); } auto properties = parseArguments(context); @@ -45,12 +46,12 @@ QScriptValue QmlFragmentClass::internal_constructor(QScriptContext* context, QSc } else { retVal->initQml(properties); } - connect(engine, &QScriptEngine::destroyed, retVal, &QmlWindowClass::deleteLater); - QScriptValue scriptObject = engine->newQObject(retVal); + connect(engine, &ScriptEngine::destroyed, retVal, &QmlWindowClass::deleteLater); + ScriptValuePointer scriptObject = engine->newQObject(retVal); _fragments[qml.toString()] = scriptObject; return scriptObject; #else - return QScriptValue(); + return ScriptValuePointer(); #endif } diff --git a/libraries/ui/src/QmlFragmentClass.h b/libraries/ui/src/QmlFragmentClass.h index ea80b2bd13e..67a4ffe1ab6 100644 --- a/libraries/ui/src/QmlFragmentClass.h +++ b/libraries/ui/src/QmlFragmentClass.h @@ -10,18 +10,24 @@ #define hifi_ui_QmlFragmentClass_h #include "QmlWindowClass.h" +#include + +class ScriptContext; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; class QmlFragmentClass : public QmlWindowClass { Q_OBJECT private: - static QScriptValue internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted); + static ScriptValuePointer internal_constructor(ScriptContext* context, ScriptEngine* engine, bool restricted); public: - static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine) { + static ScriptValuePointer constructor(ScriptContext* context, ScriptEngine* engine) { return internal_constructor(context, engine, false); } - static QScriptValue restricted_constructor(QScriptContext* context, QScriptEngine* engine ){ + static ScriptValuePointer restricted_constructor(ScriptContext* context, ScriptEngine* engine ){ return internal_constructor(context, engine, true); } @@ -46,7 +52,7 @@ public slots: QString qmlSource() const override { return qml; } static std::mutex _mutex; - static std::map _fragments; + static std::map _fragments; private: QString qml; diff --git a/libraries/ui/src/QmlWebWindowClass.cpp b/libraries/ui/src/QmlWebWindowClass.cpp index c7851d416fe..a908e057801 100644 --- a/libraries/ui/src/QmlWebWindowClass.cpp +++ b/libraries/ui/src/QmlWebWindowClass.cpp @@ -10,8 +10,8 @@ #include -#include -#include +#include +#include #include @@ -19,7 +19,7 @@ static const char* const URL_PROPERTY = "source"; static const char* const SCRIPT_PROPERTY = "scriptUrl"; // Method called by Qt scripts to create a new web window in the overlay -QScriptValue QmlWebWindowClass::internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted) { +ScriptValuePointer QmlWebWindowClass::internal_constructor(ScriptContext* context, ScriptEngine* engine, bool restricted) { auto properties = parseArguments(context); QmlWebWindowClass* retVal = new QmlWebWindowClass(restricted); Q_ASSERT(retVal); @@ -29,7 +29,7 @@ QScriptValue QmlWebWindowClass::internal_constructor(QScriptContext* context, QS } else { retVal->initQml(properties); } - connect(engine, &QScriptEngine::destroyed, retVal, &QmlWindowClass::deleteLater); + connect(engine, &ScriptEngine::destroyed, retVal, &QmlWindowClass::deleteLater); return engine->newQObject(retVal); } diff --git a/libraries/ui/src/QmlWebWindowClass.h b/libraries/ui/src/QmlWebWindowClass.h index feca1486140..4837f792a02 100644 --- a/libraries/ui/src/QmlWebWindowClass.h +++ b/libraries/ui/src/QmlWebWindowClass.h @@ -9,8 +9,15 @@ #ifndef hifi_ui_QmlWebWindowClass_h #define hifi_ui_QmlWebWindowClass_h +#include + #include "QmlWindowClass.h" +class ScriptContext; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; + /**jsdoc * A OverlayWebWindow displays an HTML window inside Interface. * @@ -142,15 +149,15 @@ class QmlWebWindowClass : public QmlWindowClass { Q_PROPERTY(QString url READ getURL CONSTANT) private: - static QScriptValue internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted); + static ScriptValuePointer internal_constructor(ScriptContext* context, ScriptEngine* engine, bool restricted); public: QmlWebWindowClass(bool restricted) : QmlWindowClass(restricted) {} - static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine) { + static ScriptValuePointer constructor(ScriptContext* context, ScriptEngine* engine) { return internal_constructor(context, engine, false); } - static QScriptValue restricted_constructor(QScriptContext* context, QScriptEngine* engine ){ + static ScriptValuePointer restricted_constructor(ScriptContext* context, ScriptEngine* engine ){ return internal_constructor(context, engine, true); } diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index ae2292dc09a..23309d0f362 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -11,8 +11,6 @@ #include #include -#include -#include #include #include @@ -27,6 +25,9 @@ #include "OffscreenUi.h" #include "ui/types/HFWebEngineProfile.h" #include "ui/types/FileTypeProfile.h" +#include +#include +#include static const char* const SOURCE_PROPERTY = "source"; static const char* const TITLE_PROPERTY = "title"; @@ -37,24 +38,24 @@ static const char* const VISIBILE_PROPERTY = "visible"; static const uvec2 MAX_QML_WINDOW_SIZE { 1280, 720 }; static const uvec2 MIN_QML_WINDOW_SIZE { 120, 80 }; -QVariantMap QmlWindowClass::parseArguments(QScriptContext* context) { +QVariantMap QmlWindowClass::parseArguments(ScriptContext* context) { const auto argumentCount = context->argumentCount(); QVariantMap properties; if (argumentCount > 1) { - if (!context->argument(0).isUndefined()) { - properties[TITLE_PROPERTY] = context->argument(0).toString(); + if (!context->argument(0)->isUndefined()) { + properties[TITLE_PROPERTY] = context->argument(0)->toString(); } - if (!context->argument(1).isUndefined()) { - properties[SOURCE_PROPERTY] = context->argument(1).toString(); + if (!context->argument(1)->isUndefined()) { + properties[SOURCE_PROPERTY] = context->argument(1)->toString(); } - if (context->argument(2).isNumber()) { - properties[WIDTH_PROPERTY] = context->argument(2).toInt32(); + if (context->argument(2)->isNumber()) { + properties[WIDTH_PROPERTY] = context->argument(2)->toInt32(); } - if (context->argument(3).isNumber()) { - properties[HEIGHT_PROPERTY] = context->argument(3).toInt32(); + if (context->argument(3)->isNumber()) { + properties[HEIGHT_PROPERTY] = context->argument(3)->toInt32(); } } else { - properties = context->argument(0).toVariant().toMap(); + properties = context->argument(0)->toVariant().toMap(); } QUrl url { properties[SOURCE_PROPERTY].toString() }; @@ -70,7 +71,7 @@ QVariantMap QmlWindowClass::parseArguments(QScriptContext* context) { // Method called by Qt scripts to create a new web window in the overlay -QScriptValue QmlWindowClass::internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted) { +ScriptValuePointer QmlWindowClass::internal_constructor(ScriptContext* context, ScriptEngine* engine, bool restricted) { auto properties = parseArguments(context); QmlWindowClass* retVal = new QmlWindowClass(restricted); Q_ASSERT(retVal); @@ -80,7 +81,7 @@ QScriptValue QmlWindowClass::internal_constructor(QScriptContext* context, QScri } else { retVal->initQml(properties); } - connect(engine, &QScriptEngine::destroyed, retVal, &QmlWindowClass::deleteLater); + connect(engine, &ScriptEngine::destroyed, retVal, &QmlWindowClass::deleteLater); return engine->newQObject(retVal); } diff --git a/libraries/ui/src/QmlWindowClass.h b/libraries/ui/src/QmlWindowClass.h index 3a2f202bd39..129caef9519 100644 --- a/libraries/ui/src/QmlWindowClass.h +++ b/libraries/ui/src/QmlWindowClass.h @@ -11,13 +11,15 @@ #include #include -#include +#include #include #include -class QScriptEngine; -class QScriptContext; +class ScriptContext; +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; /**jsdoc * A OverlayWindow displays a QML window inside Interface. @@ -53,13 +55,13 @@ class QmlWindowClass : public QObject { Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged) private: - static QScriptValue internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted); + static ScriptValuePointer internal_constructor(ScriptContext* context, ScriptEngine* engine, bool restricted); public: - static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine) { + static ScriptValuePointer constructor(ScriptContext* context, ScriptEngine* engine) { return internal_constructor(context, engine, false); } - static QScriptValue restricted_constructor(QScriptContext* context, QScriptEngine* engine ){ + static ScriptValuePointer restricted_constructor(ScriptContext* context, ScriptEngine* engine ){ return internal_constructor(context, engine, true); } @@ -345,8 +347,8 @@ protected slots: void qmlToScript(const QVariant& message); protected: - static QVariantMap parseArguments(QScriptContext* context); - static QScriptValue internalConstructor(QScriptContext* context, QScriptEngine* engine, + static QVariantMap parseArguments(ScriptContext* context); + static ScriptValuePointer internalConstructor(ScriptContext* context, ScriptEngine* engine, std::function function); virtual QString qmlSource() const { return "QmlWindow.qml"; } diff --git a/libraries/ui/src/ui/QmlWrapper.h b/libraries/ui/src/ui/QmlWrapper.h index d77e45c9dc0..b768f3c262d 100644 --- a/libraries/ui/src/ui/QmlWrapper.h +++ b/libraries/ui/src/ui/QmlWrapper.h @@ -11,8 +11,11 @@ #include #include -#include -#include +#include + +class ScriptEngine; +class ScriptValue; +using ScriptValuePointer = QSharedPointer; class QmlWrapper : public QObject { Q_OBJECT @@ -29,16 +32,16 @@ class QmlWrapper : public QObject { }; template -QScriptValue wrapperToScriptValue(QScriptEngine* engine, T* const &in) { +ScriptValuePointer wrapperToScriptValue(ScriptEngine* engine, T* const &in) { if (!in) { return engine->undefinedValue(); } - return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); + return engine->newQObject(in, ScriptEngine::QtOwnership, ScriptEngine::ExcludeDeleteLater | ScriptEngine::ExcludeChildObjects); } template -void wrapperFromScriptValue(const QScriptValue& value, T* &out) { - out = qobject_cast(value.toQObject()); +void wrapperFromScriptValue(const ScriptValuePointer& value, T* &out) { + out = qobject_cast(value->toQObject()); } #endif \ No newline at end of file diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index 5468ee18c30..9b7c0ebe703 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -18,10 +18,6 @@ #include #include -#include -#include -#include - #include #include diff --git a/libraries/ui/src/ui/ToolbarScriptingInterface.cpp b/libraries/ui/src/ui/ToolbarScriptingInterface.cpp index d01b538004a..05b1804f87b 100644 --- a/libraries/ui/src/ui/ToolbarScriptingInterface.cpp +++ b/libraries/ui/src/ui/ToolbarScriptingInterface.cpp @@ -10,32 +10,32 @@ #include #include -#include -#include +#include +#include #include #include "../OffscreenUi.h" -QScriptValue toolbarToScriptValue(QScriptEngine* engine, ToolbarProxy* const &in) { +ScriptValuePointer toolbarToScriptValue(ScriptEngine* engine, ToolbarProxy* const &in) { if (!in) { return engine->undefinedValue(); } - return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); + return engine->newQObject(in, ScriptEngine::QtOwnership, ScriptEngine::ExcludeDeleteLater | ScriptEngine::ExcludeChildObjects); } -void toolbarFromScriptValue(const QScriptValue& value, ToolbarProxy* &out) { - out = qobject_cast(value.toQObject()); +void toolbarFromScriptValue(const ScriptValuePointer& value, ToolbarProxy* &out) { + out = qobject_cast(value->toQObject()); } -QScriptValue toolbarButtonToScriptValue(QScriptEngine* engine, ToolbarButtonProxy* const &in) { +ScriptValuePointer toolbarButtonToScriptValue(ScriptEngine* engine, ToolbarButtonProxy* const &in) { if (!in) { return engine->undefinedValue(); } - return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); + return engine->newQObject(in, ScriptEngine::QtOwnership, ScriptEngine::ExcludeDeleteLater | ScriptEngine::ExcludeChildObjects); } -void toolbarButtonFromScriptValue(const QScriptValue& value, ToolbarButtonProxy* &out) { - out = qobject_cast(value.toQObject()); +void toolbarButtonFromScriptValue(const ScriptValuePointer& value, ToolbarButtonProxy* &out) { + out = qobject_cast(value->toQObject()); } diff --git a/libraries/ui/src/ui/ToolbarScriptingInterface.h b/libraries/ui/src/ui/ToolbarScriptingInterface.h index 746ba2894e8..39695aca2ee 100644 --- a/libraries/ui/src/ui/ToolbarScriptingInterface.h +++ b/libraries/ui/src/ui/ToolbarScriptingInterface.h @@ -12,7 +12,6 @@ #include #include -#include #include #include "QmlWrapper.h" diff --git a/plugins/JSAPIExample/CMakeLists.txt b/plugins/JSAPIExample/CMakeLists.txt index a8fa0a1fd6a..b4f79afc254 100644 --- a/plugins/JSAPIExample/CMakeLists.txt +++ b/plugins/JSAPIExample/CMakeLists.txt @@ -1,3 +1,4 @@ set(TARGET_NAME JSAPIExample) setup_hifi_client_server_plugin(scripting) link_hifi_libraries(shared plugins) +include_hifi_library_headers(script-engine) diff --git a/plugins/JSAPIExample/src/JSAPIExample.cpp b/plugins/JSAPIExample/src/JSAPIExample.cpp index 91aa8bd32a5..36907387bc5 100644 --- a/plugins/JSAPIExample/src/JSAPIExample.cpp +++ b/plugins/JSAPIExample/src/JSAPIExample.cpp @@ -18,12 +18,14 @@ #include #include #include -#include -#include #include // for ::settingsFilename() #include // for ::usecTimestampNow() #include +#include +#include +#include +#include // NOTE: replace this with your own namespace when starting a new plugin (to avoid .so/.dll symbol clashes) namespace REPLACE_ME_WITH_UNIQUE_NAME { @@ -33,9 +35,9 @@ namespace REPLACE_ME_WITH_UNIQUE_NAME { QLoggingCategory logger { "jsapiexample" }; - inline QVariant raiseScriptingError(QScriptContext* context, const QString& message, const QVariant& returnValue = QVariant()) { + inline QVariant raiseScriptingError(ScriptContext* context, const QString& message, const QVariant& returnValue = QVariant()) { if (context) { - // when a QScriptContext is available throw an actual JS Exception (which can be caught using try/catch on JS side) + // when a ScriptContext is available throw an actual JS Exception (which can be caught using try/catch on JS side) context->throwError(message); } else { // otherwise just log the error @@ -46,7 +48,7 @@ namespace REPLACE_ME_WITH_UNIQUE_NAME { QObject* createScopedSettings(const QString& scope, QObject* parent, QString& error); - class JSAPIExample : public QObject, public QScriptable { + class JSAPIExample : public QObject, public Scriptable { Q_OBJECT Q_PLUGIN_METADATA(IID "JSAPIExample" FILE "plugin.json") Q_PROPERTY(QString version MEMBER _version CONSTANT) @@ -59,9 +61,9 @@ namespace REPLACE_ME_WITH_UNIQUE_NAME { return; } qCWarning(logger) << "registering w/ScriptInitializerMixin..." << scriptInit.data(); - scriptInit->registerScriptInitializer([this](QScriptEngine* engine) { - auto value = engine->newQObject(this, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater); - engine->globalObject().setProperty(objectName(), value); + scriptInit->registerScriptInitializer([this](ScriptEngine* engine) { + auto value = engine->newQObject(this, ScriptEngine::QtOwnership, ScriptEngine::ExcludeDeleteLater); + engine->globalObject()->setProperty(objectName(), value); // qCDebug(logger) << "setGlobalInstance" << objectName() << engine->property("fileName"); }); // qCInfo(logger) << "plugin loaded" << qApp << toString() << QThread::currentThread(); @@ -69,7 +71,7 @@ namespace REPLACE_ME_WITH_UNIQUE_NAME { // NOTES: everything within the "public slots:" section below will be available from JS via overall plugin QObject // also, to demonstrate future-proofing JS API code, QVariant's are used throughout most of these examples -- - // which still makes them very Qt-specific, but avoids depending directly on deprecated QtScript/QScriptValue APIs. + // which still makes them very Qt-specific, but avoids depending directly on deprecated ScriptValuePointer APIs. // (as such this plugin class and its methods remain forward-compatible with other engines like QML's QJSEngine) public slots: @@ -145,7 +147,7 @@ namespace REPLACE_ME_WITH_UNIQUE_NAME { /** * Example of exposing a custom "managed" C++ QObject to JS - * The lifecycle of the created QObject* instance becomes managed by the invoking QScriptEngine -- + * The lifecycle of the created QObject* instance becomes managed by the invoking ScriptEngine -- * it will be automatically cleaned up once no longer reachable from any JS variables/closures. * @example access persistent settings stored in separate .json files * var settings = JSAPIExample.getScopedSettings("example"); @@ -156,18 +158,18 @@ namespace REPLACE_ME_WITH_UNIQUE_NAME { * print("all example::* keys", settings.allKeys()); * settings = null; // optional best pratice; allows the object to be reclaimed ASAP by the JS garbage collector */ - QScriptValue getScopedSettings(const QString& scope) { - auto engine = QScriptable::engine(); + ScriptValuePointer getScopedSettings(const QString& scope) { + auto engine = Scriptable::engine(); if (!engine) { - return QScriptValue::NullValue; + return ScriptValuePointer(); } QString error; auto cppValue = createScopedSettings(scope, engine, error); if (!cppValue) { raiseScriptingError(context(), "error creating scoped settings instance: " + error); - return QScriptValue::NullValue; + return engine->nullValue(); } - return engine->newQObject(cppValue, QScriptEngine::ScriptOwnership, QScriptEngine::ExcludeDeleteLater); + return engine->newQObject(cppValue, ScriptEngine::ScriptOwnership, ScriptEngine::ExcludeDeleteLater); } private: diff --git a/scripts/system/request-service.js b/scripts/system/request-service.js index b57f2d4cd77..f51931bb3a7 100644 --- a/scripts/system/request-service.js +++ b/scripts/system/request-service.js @@ -14,7 +14,7 @@ // QML has its own XMLHttpRequest, but: // - npm request is easier to use. // - It is not easy to hack QML's XMLHttpRequest to use our MetaverseServer, and to supply the user's auth when contacting it. - // a. Our custom XMLHttpRequestClass object only works with QScriptEngine, not QML's javascript. + // a. Our custom XMLHttpRequestClass object only works with ScriptEngine, not QML's javascript. // b. We have hacked profiles that intercept requests to our MetavserseServer (providing the correct auth), but those // only work in QML WebEngineView. Setting up communication between ordinary QML and a hiddent WebEngineView is // tantamount to the following anyway, and would still have to duplicate the code from request.js. diff --git a/tests-manual/controllers/CMakeLists.txt b/tests-manual/controllers/CMakeLists.txt index 932826c8de1..c200e2fb9ca 100644 --- a/tests-manual/controllers/CMakeLists.txt +++ b/tests-manual/controllers/CMakeLists.txt @@ -3,7 +3,7 @@ if (NOT APPLE) set(TARGET_NAME controllers-test) # This is not a testcase -- just set it up as a regular hifi project -setup_hifi_project(Script Qml) +setup_hifi_project(Qml) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") setup_memory_debugger() diff --git a/tests-manual/entities/CMakeLists.txt b/tests-manual/entities/CMakeLists.txt index a6eed4f2344..1aede6ad6b0 100644 --- a/tests-manual/entities/CMakeLists.txt +++ b/tests-manual/entities/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET_NAME "entities-test") # This is not a testcase -- just set it up as a regular hifi project -setup_hifi_project(Network Script) +setup_hifi_project(Network) setup_memory_debugger() setup_thread_debugger() set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") diff --git a/tests-manual/gpu-textures/CMakeLists.txt b/tests-manual/gpu-textures/CMakeLists.txt index d148b0cd21e..fcade688f95 100644 --- a/tests-manual/gpu-textures/CMakeLists.txt +++ b/tests-manual/gpu-textures/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME gpu-textures-tests) # This is not a testcase -- just set it up as a regular hifi project -setup_hifi_project(Quick Gui Script) +setup_hifi_project(Quick Gui) setup_memory_debugger() setup_thread_debugger() set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") diff --git a/tests-manual/gpu/CMakeLists.txt b/tests-manual/gpu/CMakeLists.txt index dc7bfbe75e0..e72d1672942 100644 --- a/tests-manual/gpu/CMakeLists.txt +++ b/tests-manual/gpu/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME gpu-test) # This is not a testcase -- just set it up as a regular hifi project -setup_hifi_project(Quick Gui Script) +setup_hifi_project(Quick Gui) setup_memory_debugger() setup_thread_debugger() set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") diff --git a/tests/octree/CMakeLists.txt b/tests/octree/CMakeLists.txt index 287a3b73d87..49a031887d2 100644 --- a/tests/octree/CMakeLists.txt +++ b/tests/octree/CMakeLists.txt @@ -7,4 +7,4 @@ macro (setup_testcase_dependencies) package_libraries_for_deployment() endmacro () -setup_hifi_testcase(Script Network) +setup_hifi_testcase(Network) diff --git a/tests/physics/CMakeLists.txt b/tests/physics/CMakeLists.txt index 87e5350c8f8..f47e27226b4 100644 --- a/tests/physics/CMakeLists.txt +++ b/tests/physics/CMakeLists.txt @@ -6,4 +6,4 @@ macro (SETUP_TESTCASE_DEPENDENCIES) package_libraries_for_deployment() endmacro () -setup_hifi_testcase(Script) +setup_hifi_testcase() diff --git a/tools/oven/CMakeLists.txt b/tools/oven/CMakeLists.txt index 4f48570428b..95206d1ced6 100644 --- a/tools/oven/CMakeLists.txt +++ b/tools/oven/CMakeLists.txt @@ -3,6 +3,7 @@ set(TARGET_NAME oven) setup_hifi_project(Widgets Gui Concurrent) link_hifi_libraries(shared shaders image gpu ktx model-serializers hfm baking graphics networking procedural material-networking model-baker task) +include_hifi_library_headers(script-engine) setup_memory_debugger() setup_thread_debugger()