From bc6eaf25dc5a4164fa4fe3b997f7af0bd0f0223a Mon Sep 17 00:00:00 2001 From: Heather Anderson Date: Tue, 14 Dec 2021 00:30:08 -0800 Subject: [PATCH] first pass creating Qt <-> QtScript interface --- interface/src/LODManager.cpp | 3 +- interface/src/LODManager.h | 2 +- interface/src/avatar/MyAvatar.cpp | 6 +- interface/src/avatar/MyAvatar.h | 4 +- .../src/raypick/PickScriptingInterface.cpp | 3 +- .../AccountServicesScriptingInterface.cpp | 3 +- .../AccountServicesScriptingInterface.h | 2 +- interface/src/ui/InteractiveWindow.cpp | 3 +- interface/src/ui/InteractiveWindow.h | 2 +- interface/src/ui/overlays/Overlays.cpp | 3 +- interface/src/ui/overlays/Overlays.h | 2 +- libraries/audio/src/AudioInjectorOptions.cpp | 5 +- libraries/audio/src/AudioInjectorOptions.h | 2 +- libraries/audio/src/ScriptAudioInjector.cpp | 3 +- libraries/audio/src/ScriptAudioInjector.h | 2 +- libraries/audio/src/Sound.cpp | 3 +- libraries/audio/src/Sound.h | 2 +- libraries/avatars/src/AvatarData.cpp | 6 +- libraries/avatars/src/AvatarData.h | 4 +- libraries/avatars/src/ScriptAvatarData.cpp | 3 +- .../controllers/src/controllers/Pose.cpp | 3 +- libraries/controllers/src/controllers/Pose.h | 2 +- .../src/controllers/ScriptingInterface.cpp | 3 +- .../src/controllers/UserInputMapper.cpp | 20 +- .../entities/src/EntityItemProperties.cpp | 16 +- libraries/entities/src/EntityItemProperties.h | 10 +- .../entities/src/EntityScriptingInterface.cpp | 3 +- .../entities/src/EntityScriptingInterface.h | 2 +- .../GraphicsScriptingInterface.cpp | 22 +- libraries/script-engine/src/KeyEvent.cpp | 3 +- libraries/script-engine/src/KeyEvent.h | 2 +- libraries/script-engine/src/MIDIEvent.cpp | 3 +- libraries/script-engine/src/MIDIEvent.h | 2 +- .../script-engine/src/MenuItemProperties.cpp | 3 +- .../script-engine/src/MenuItemProperties.h | 2 +- libraries/script-engine/src/MouseEvent.cpp | 3 +- libraries/script-engine/src/MouseEvent.h | 2 +- libraries/script-engine/src/PointerEvent.cpp | 3 +- libraries/script-engine/src/PointerEvent.h | 2 +- libraries/script-engine/src/ScriptContext.cpp | 23 + libraries/script-engine/src/ScriptContext.h | 9 + libraries/script-engine/src/ScriptEngine.h | 6 +- .../script-engine/src/ScriptEngineCast.h | 22 +- libraries/script-engine/src/ScriptManager.cpp | 11 +- libraries/script-engine/src/ScriptManager.h | 9 + .../script-engine/src/ScriptValueUtils.cpp | 91 ++- .../script-engine/src/ScriptValueUtils.h | 58 +- libraries/script-engine/src/SpatialEvent.cpp | 3 +- libraries/script-engine/src/SpatialEvent.h | 2 +- libraries/script-engine/src/TouchEvent.cpp | 3 +- libraries/script-engine/src/TouchEvent.h | 2 +- .../script-engine/src/WebSocketClass.cpp | 9 +- libraries/script-engine/src/WebSocketClass.h | 6 +- libraries/script-engine/src/WheelEvent.cpp | 3 +- libraries/script-engine/src/WheelEvent.h | 2 +- .../src/qtscript/ArrayBufferClass.cpp | 3 +- .../src/qtscript/ScriptEngineQtScript.cpp | 157 +---- .../src/qtscript/ScriptEngineQtScript.h | 21 +- .../qtscript/ScriptEngineQtScript_cast.cpp | 538 +++++++++++++++ .../src/qtscript/ScriptObjectQtProxy.cpp | 622 ++++++++++++++++++ .../src/qtscript/ScriptObjectQtProxy.h | 205 ++++++ .../src/qtscript/ScriptValueQtWrapper.cpp | 32 +- libraries/ui/src/ui/QmlWrapper.h | 3 +- 63 files changed, 1711 insertions(+), 298 deletions(-) create mode 100644 libraries/script-engine/src/ScriptContext.cpp create mode 100644 libraries/script-engine/src/qtscript/ScriptEngineQtScript_cast.cpp create mode 100644 libraries/script-engine/src/qtscript/ScriptObjectQtProxy.cpp create mode 100644 libraries/script-engine/src/qtscript/ScriptObjectQtProxy.h diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index 9a80c5478b..3acd4f4781 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -430,9 +430,10 @@ ScriptValue worldDetailQualityToScriptValue(ScriptEngine* engine, const WorldDet return engine->newValue(worldDetailQuality); } -void worldDetailQualityFromScriptValue(const ScriptValue& object, WorldDetailQuality& worldDetailQuality) { +bool worldDetailQualityFromScriptValue(const ScriptValue& object, WorldDetailQuality& worldDetailQuality) { worldDetailQuality = static_cast(std::min(std::max(object.toInt32(), (int)WORLD_DETAIL_LOW), (int)WORLD_DETAIL_HIGH)); + return true; } void LODManager::setLODQualityLevel(float quality) { diff --git a/interface/src/LODManager.h b/interface/src/LODManager.h index 1ab64b2dc1..afebe9d774 100644 --- a/interface/src/LODManager.h +++ b/interface/src/LODManager.h @@ -383,6 +383,6 @@ private: }; ScriptValue worldDetailQualityToScriptValue(ScriptEngine* engine, const WorldDetailQuality& worldDetailQuality); -void worldDetailQualityFromScriptValue(const ScriptValue& object, WorldDetailQuality& worldDetailQuality); +bool worldDetailQualityFromScriptValue(const ScriptValue& object, WorldDetailQuality& worldDetailQuality); #endif // hifi_LODManager_h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 59883cd1df..f6c2deb4e3 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -5707,16 +5707,18 @@ ScriptValue audioListenModeToScriptValue(ScriptEngine* engine, const AudioListen return engine->newValue(audioListenerMode); } -void audioListenModeFromScriptValue(const ScriptValue& object, AudioListenerMode& audioListenerMode) { +bool audioListenModeFromScriptValue(const ScriptValue& object, AudioListenerMode& audioListenerMode) { audioListenerMode = static_cast(object.toUInt16()); + return true; } ScriptValue driveKeysToScriptValue(ScriptEngine* engine, const MyAvatar::DriveKeys& driveKeys) { return engine->newValue(driveKeys); } -void driveKeysFromScriptValue(const ScriptValue& object, MyAvatar::DriveKeys& driveKeys) { +bool driveKeysFromScriptValue(const ScriptValue& object, MyAvatar::DriveKeys& driveKeys) { driveKeys = static_cast(object.toUInt16()); + return true; } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index d71bd4dc69..8e4b32c5d3 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -3120,10 +3120,10 @@ private: }; ScriptValue audioListenModeToScriptValue(ScriptEngine* engine, const AudioListenerMode& audioListenerMode); -void audioListenModeFromScriptValue(const ScriptValue& object, AudioListenerMode& audioListenerMode); +bool audioListenModeFromScriptValue(const ScriptValue& object, AudioListenerMode& audioListenerMode); ScriptValue driveKeysToScriptValue(ScriptEngine* engine, const MyAvatar::DriveKeys& driveKeys); -void driveKeysFromScriptValue(const ScriptValue& object, MyAvatar::DriveKeys& driveKeys); +bool driveKeysFromScriptValue(const ScriptValue& object, MyAvatar::DriveKeys& driveKeys); bool isWearableEntity(const EntityItemPointer& entity); diff --git a/interface/src/raypick/PickScriptingInterface.cpp b/interface/src/raypick/PickScriptingInterface.cpp index eba2dec19e..b5ef887f62 100644 --- a/interface/src/raypick/PickScriptingInterface.cpp +++ b/interface/src/raypick/PickScriptingInterface.cpp @@ -451,8 +451,9 @@ ScriptValue pickTypesToScriptValue(ScriptEngine* engine, const PickQuery::PickTy return engine->newValue(pickType); } -void pickTypesFromScriptValue(const ScriptValue& object, PickQuery::PickType& pickType) { +bool pickTypesFromScriptValue(const ScriptValue& object, PickQuery::PickType& pickType) { pickType = static_cast(object.toUInt16()); + return true; } void PickScriptingInterface::registerMetaTypes(ScriptEngine* engine) { diff --git a/interface/src/scripting/AccountServicesScriptingInterface.cpp b/interface/src/scripting/AccountServicesScriptingInterface.cpp index 55d5b25acf..042cb25978 100644 --- a/interface/src/scripting/AccountServicesScriptingInterface.cpp +++ b/interface/src/scripting/AccountServicesScriptingInterface.cpp @@ -136,7 +136,7 @@ ScriptValue DownloadInfoResultToScriptValue(ScriptEngine* engine, const Download return object; } -void DownloadInfoResultFromScriptValue(const ScriptValue& object, DownloadInfoResult& result) { +bool DownloadInfoResultFromScriptValue(const ScriptValue& object, DownloadInfoResult& result) { QList downloading = object.property("downloading").toVariant().toList(); result.downloading.clear(); for (int i = 0; i < downloading.count(); i += 1) { @@ -144,6 +144,7 @@ void DownloadInfoResultFromScriptValue(const ScriptValue& object, DownloadInfoRe } result.pending = object.property("pending").toVariant().toFloat(); + return true; } DownloadInfoResult AccountServicesScriptingInterface::getDownloadInfo() { diff --git a/interface/src/scripting/AccountServicesScriptingInterface.h b/interface/src/scripting/AccountServicesScriptingInterface.h index 241db84c0d..b65d41b3b1 100644 --- a/interface/src/scripting/AccountServicesScriptingInterface.h +++ b/interface/src/scripting/AccountServicesScriptingInterface.h @@ -32,7 +32,7 @@ public: Q_DECLARE_METATYPE(DownloadInfoResult) ScriptValue DownloadInfoResultToScriptValue(ScriptEngine* engine, const DownloadInfoResult& result); -void DownloadInfoResultFromScriptValue(const ScriptValue& object, DownloadInfoResult& result); +bool DownloadInfoResultFromScriptValue(const ScriptValue& object, DownloadInfoResult& result); class AccountServicesScriptingInterface : public QObject { Q_OBJECT diff --git a/interface/src/ui/InteractiveWindow.cpp b/interface/src/ui/InteractiveWindow.cpp index f74c61dea3..cda66c5cf2 100644 --- a/interface/src/ui/InteractiveWindow.cpp +++ b/interface/src/ui/InteractiveWindow.cpp @@ -101,10 +101,11 @@ ScriptValue interactiveWindowPointerToScriptValue(ScriptEngine* engine, const In return engine->newQObject(in, ScriptEngine::ScriptOwnership); } -void interactiveWindowPointerFromScriptValue(const ScriptValue& object, InteractiveWindowPointer& out) { +bool interactiveWindowPointerFromScriptValue(const ScriptValue& object, InteractiveWindowPointer& out) { if (const auto interactiveWindow = qobject_cast(object.toQObject())) { out = interactiveWindow; } + return true; } void InteractiveWindow::forwardKeyPressEvent(int key, int modifiers) { diff --git a/interface/src/ui/InteractiveWindow.h b/interface/src/ui/InteractiveWindow.h index f11140b3c3..9cce6944cb 100644 --- a/interface/src/ui/InteractiveWindow.h +++ b/interface/src/ui/InteractiveWindow.h @@ -411,7 +411,7 @@ private: typedef InteractiveWindow* InteractiveWindowPointer; ScriptValue interactiveWindowPointerToScriptValue(ScriptEngine* engine, const InteractiveWindowPointer& in); -void interactiveWindowPointerFromScriptValue(const ScriptValue& object, InteractiveWindowPointer& out); +bool interactiveWindowPointerFromScriptValue(const ScriptValue& object, InteractiveWindowPointer& out); void registerInteractiveWindowMetaType(ScriptEngine* engine); diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 7345e72b37..89a5dae049 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -1126,7 +1126,7 @@ ScriptValue RayToOverlayIntersectionResultToScriptValue(ScriptEngine* engine, co return obj; } -void RayToOverlayIntersectionResultFromScriptValue(const ScriptValue& object, RayToOverlayIntersectionResult& value) { +bool RayToOverlayIntersectionResultFromScriptValue(const ScriptValue& object, RayToOverlayIntersectionResult& value) { value.intersects = object.property("intersects").toVariant().toBool(); ScriptValue overlayIDValue = object.property("overlayID"); quuidFromScriptValue(overlayIDValue, value.overlayID); @@ -1142,6 +1142,7 @@ void RayToOverlayIntersectionResultFromScriptValue(const ScriptValue& object, Ra vec3FromScriptValue(surfaceNormal, value.surfaceNormal); } value.extraInfo = object.property("extraInfo").toVariant().toMap(); + return true; } bool Overlays::isLoaded(const QUuid& id) { diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 167a0e1d7d..d13e2f821c 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -54,7 +54,7 @@ public: }; Q_DECLARE_METATYPE(RayToOverlayIntersectionResult); ScriptValue RayToOverlayIntersectionResultToScriptValue(ScriptEngine* engine, const RayToOverlayIntersectionResult& value); -void RayToOverlayIntersectionResultFromScriptValue(const ScriptValue& object, RayToOverlayIntersectionResult& value); +bool RayToOverlayIntersectionResultFromScriptValue(const ScriptValue& object, RayToOverlayIntersectionResult& value); class ParabolaToOverlayIntersectionResult { public: diff --git a/libraries/audio/src/AudioInjectorOptions.cpp b/libraries/audio/src/AudioInjectorOptions.cpp index 94597e4757..804d935a46 100644 --- a/libraries/audio/src/AudioInjectorOptions.cpp +++ b/libraries/audio/src/AudioInjectorOptions.cpp @@ -66,10 +66,10 @@ ScriptValue injectorOptionsToScriptValue(ScriptEngine* engine, const AudioInject * @property {boolean} ignorePenumbra=false -

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

*/ -void injectorOptionsFromScriptValue(const ScriptValue& object, AudioInjectorOptions& injectorOptions) { +bool injectorOptionsFromScriptValue(const ScriptValue& object, AudioInjectorOptions& injectorOptions) { if (!object.isObject()) { qWarning() << "Audio injector options is not an object."; - return; + return false; } if (injectorOptions.positionSet == false) { @@ -126,4 +126,5 @@ void injectorOptionsFromScriptValue(const ScriptValue& object, AudioInjectorOpti qCWarning(audio) << "Unknown audio injector option:" << it->name(); } } + return true; } diff --git a/libraries/audio/src/AudioInjectorOptions.h b/libraries/audio/src/AudioInjectorOptions.h index 0dd3ab1cb1..fda05f0117 100644 --- a/libraries/audio/src/AudioInjectorOptions.h +++ b/libraries/audio/src/AudioInjectorOptions.h @@ -37,6 +37,6 @@ public: Q_DECLARE_METATYPE(AudioInjectorOptions); ScriptValue injectorOptionsToScriptValue(ScriptEngine* engine, const AudioInjectorOptions& injectorOptions); -void injectorOptionsFromScriptValue(const ScriptValue& object, AudioInjectorOptions& injectorOptions); +bool injectorOptionsFromScriptValue(const ScriptValue& object, AudioInjectorOptions& injectorOptions); #endif // hifi_AudioInjectorOptions_h diff --git a/libraries/audio/src/ScriptAudioInjector.cpp b/libraries/audio/src/ScriptAudioInjector.cpp index 09f24c7207..28af8b849a 100644 --- a/libraries/audio/src/ScriptAudioInjector.cpp +++ b/libraries/audio/src/ScriptAudioInjector.cpp @@ -32,8 +32,9 @@ ScriptValue injectorToScriptValue(ScriptEngine* engine, ScriptAudioInjector* con return engine->newQObject(in, ScriptEngine::ScriptOwnership); } -void injectorFromScriptValue(const ScriptValue& object, ScriptAudioInjector*& out) { +bool injectorFromScriptValue(const ScriptValue& object, ScriptAudioInjector*& out) { out = qobject_cast(object.toQObject()); + return true; } ScriptAudioInjector::ScriptAudioInjector(const AudioInjectorPointer& injector) : diff --git a/libraries/audio/src/ScriptAudioInjector.h b/libraries/audio/src/ScriptAudioInjector.h index 382ce16772..431f59241e 100644 --- a/libraries/audio/src/ScriptAudioInjector.h +++ b/libraries/audio/src/ScriptAudioInjector.h @@ -152,7 +152,7 @@ private: Q_DECLARE_METATYPE(ScriptAudioInjector*) ScriptValue injectorToScriptValue(ScriptEngine* engine, ScriptAudioInjector* const& in); -void injectorFromScriptValue(const ScriptValue& object, ScriptAudioInjector*& out); +bool injectorFromScriptValue(const ScriptValue& object, ScriptAudioInjector*& out); #endif // hifi_ScriptAudioInjector_h diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 6329ade515..2f244ca539 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -428,10 +428,11 @@ ScriptValue soundSharedPointerToScriptValue(ScriptEngine* engine, const SharedSo return engine->newQObject(new SoundScriptingInterface(in), ScriptEngine::ScriptOwnership); } -void soundSharedPointerFromScriptValue(const ScriptValue& object, SharedSoundPointer& out) { +bool soundSharedPointerFromScriptValue(const ScriptValue& object, SharedSoundPointer& out) { if (auto soundInterface = qobject_cast(object.toQObject())) { out = soundInterface->getSound(); } + return true; } SoundScriptingInterface::SoundScriptingInterface(const SharedSoundPointer& sound) : _sound(sound) { diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h index f98822249f..f38d2991ce 100644 --- a/libraries/audio/src/Sound.h +++ b/libraries/audio/src/Sound.h @@ -172,6 +172,6 @@ private: Q_DECLARE_METATYPE(SharedSoundPointer) ScriptValue soundSharedPointerToScriptValue(ScriptEngine* engine, const SharedSoundPointer& in); -void soundSharedPointerFromScriptValue(const ScriptValue& object, SharedSoundPointer& out); +bool soundSharedPointerFromScriptValue(const ScriptValue& object, SharedSoundPointer& out); #endif // hifi_Sound_h diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 348d42b5af..eaa264a669 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -3176,7 +3176,7 @@ ScriptValue RayToAvatarIntersectionResultToScriptValue(ScriptEngine* engine, con return obj; } -void RayToAvatarIntersectionResultFromScriptValue(const ScriptValue& object, RayToAvatarIntersectionResult& value) { +bool RayToAvatarIntersectionResultFromScriptValue(const ScriptValue& object, RayToAvatarIntersectionResult& value) { value.intersects = object.property("intersects").toVariant().toBool(); ScriptValue avatarIDValue = object.property("avatarID"); quuidFromScriptValue(avatarIDValue, value.avatarID); @@ -3193,6 +3193,7 @@ void RayToAvatarIntersectionResultFromScriptValue(const ScriptValue& object, Ray } value.jointIndex = object.property("jointIndex").toInt32(); value.extraInfo = object.property("extraInfo").toVariant().toMap(); + return true; } // these coefficients can be changed via JS for experimental tuning @@ -3226,7 +3227,7 @@ ScriptValue AvatarEntityMapToScriptValue(ScriptEngine* engine, const AvatarEntit return obj; } -void AvatarEntityMapFromScriptValue(const ScriptValue& object, AvatarEntityMap& value) { +bool AvatarEntityMapFromScriptValue(const ScriptValue& object, AvatarEntityMap& value) { ScriptValueIteratorPointer itr(object.newIterator()); while (itr->hasNext()) { itr->next(); @@ -3240,6 +3241,7 @@ void AvatarEntityMapFromScriptValue(const ScriptValue& object, AvatarEntityMap& OVERTE_IGNORE_DEPRECATED_END value[EntityID] = binaryEntityProperties; } + return true; } const float AvatarData::DEFAULT_BUBBLE_SCALE = 2.4f; // magic number determined empirically diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index b2e2b06b87..9e08da6d46 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1972,7 +1972,7 @@ public: }; Q_DECLARE_METATYPE(RayToAvatarIntersectionResult) ScriptValue RayToAvatarIntersectionResultToScriptValue(ScriptEngine* engine, const RayToAvatarIntersectionResult& results); -void RayToAvatarIntersectionResultFromScriptValue(const ScriptValue& object, RayToAvatarIntersectionResult& results); +bool RayToAvatarIntersectionResultFromScriptValue(const ScriptValue& object, RayToAvatarIntersectionResult& results); // No JSDoc because it's not provided as a type to the script engine. class ParabolaToAvatarIntersectionResult { @@ -1990,7 +1990,7 @@ public: Q_DECLARE_METATYPE(AvatarEntityMap) ScriptValue AvatarEntityMapToScriptValue(ScriptEngine* engine, const AvatarEntityMap& value); -void AvatarEntityMapFromScriptValue(const ScriptValue& object, AvatarEntityMap& value); +bool AvatarEntityMapFromScriptValue(const ScriptValue& object, AvatarEntityMap& value); // faux joint indexes (-1 means invalid) const int NO_JOINT_INDEX = 65535; // -1 diff --git a/libraries/avatars/src/ScriptAvatarData.cpp b/libraries/avatars/src/ScriptAvatarData.cpp index 9f23fee84a..5335f8ab5b 100644 --- a/libraries/avatars/src/ScriptAvatarData.cpp +++ b/libraries/avatars/src/ScriptAvatarData.cpp @@ -18,10 +18,11 @@ ScriptValue avatarDataToScriptValue(ScriptEngine* engine, ScriptAvatarData* cons return engine->newQObject(in, ScriptEngine::ScriptOwnership); } -void avatarDataFromScriptValue(const ScriptValue& object, ScriptAvatarData*& out) { +bool avatarDataFromScriptValue(const ScriptValue& object, ScriptAvatarData*& out) { // This is not implemented because there are no slots/properties that take an AvatarSharedPointer from a script assert(false); out = nullptr; + return false; } STATIC_SCRIPT_INITIALIZER(+[](ScriptManager* manager) { diff --git a/libraries/controllers/src/controllers/Pose.cpp b/libraries/controllers/src/controllers/Pose.cpp index bfb6f6afc4..1fad0b9c42 100644 --- a/libraries/controllers/src/controllers/Pose.cpp +++ b/libraries/controllers/src/controllers/Pose.cpp @@ -50,7 +50,7 @@ namespace controller { return obj; } - void Pose::fromScriptValue(const ScriptValue& object, Pose& pose) { + bool Pose::fromScriptValue(const ScriptValue& object, Pose& pose) { auto translation = object.property("translation"); auto rotation = object.property("rotation"); auto velocity = object.property("velocity"); @@ -67,6 +67,7 @@ namespace controller { } else { pose.valid = false; } + return true; } Pose Pose::transform(const glm::mat4& mat) const { diff --git a/libraries/controllers/src/controllers/Pose.h b/libraries/controllers/src/controllers/Pose.h index 7a71c7c5bb..a1bcbffd2d 100644 --- a/libraries/controllers/src/controllers/Pose.h +++ b/libraries/controllers/src/controllers/Pose.h @@ -45,7 +45,7 @@ namespace controller { Pose postTransform(const glm::mat4& mat) const; static ScriptValue toScriptValue(ScriptEngine* engine, const Pose& event); - static void fromScriptValue(const ScriptValue& object, Pose& event); + static bool fromScriptValue(const ScriptValue& object, Pose& event); }; } diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index b6e57a0c4e..6f84255b4b 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -37,8 +37,9 @@ ScriptValue inputControllerToScriptValue(ScriptEngine* engine, controller::Input return engine->newQObject(in, ScriptEngine::QtOwnership); } -void inputControllerFromScriptValue(const ScriptValue& object, controller::InputController*& out) { +bool inputControllerFromScriptValue(const ScriptValue& object, controller::InputController*& out) { out = qobject_cast(object.toQObject()); + return true; } STATIC_SCRIPT_INITIALIZER(+[](ScriptManager* manager) { diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 59427671eb..cc62f584fa 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -393,13 +393,13 @@ int poseMetaTypeId = qRegisterMetaType("Pose"); int handMetaTypeId = qRegisterMetaType(); ScriptValue inputToScriptValue(ScriptEngine* engine, const Input& input); -void inputFromScriptValue(const ScriptValue& object, Input& input); +bool inputFromScriptValue(const ScriptValue& object, Input& input); ScriptValue actionToScriptValue(ScriptEngine* engine, const Action& action); -void actionFromScriptValue(const ScriptValue& object, Action& action); +bool actionFromScriptValue(const ScriptValue& object, Action& action); ScriptValue inputPairToScriptValue(ScriptEngine* engine, const Input::NamedPair& inputPair); -void inputPairFromScriptValue(const ScriptValue& object, Input::NamedPair& inputPair); +bool inputPairFromScriptValue(const ScriptValue& object, Input::NamedPair& inputPair); ScriptValue handToScriptValue(ScriptEngine* engine, const controller::Hand& hand); -void handFromScriptValue(const ScriptValue& object, controller::Hand& hand); +bool handFromScriptValue(const ScriptValue& object, controller::Hand& hand); ScriptValue inputToScriptValue(ScriptEngine* engine, const Input& input) { ScriptValue obj = engine->newObject(); @@ -410,8 +410,9 @@ ScriptValue inputToScriptValue(ScriptEngine* engine, const Input& input) { return obj; } -void inputFromScriptValue(const ScriptValue& object, Input& input) { +bool inputFromScriptValue(const ScriptValue& object, Input& input) { input.id = object.property("id").toInt32(); + return true; } ScriptValue actionToScriptValue(ScriptEngine* engine, const Action& action) { @@ -422,8 +423,9 @@ ScriptValue actionToScriptValue(ScriptEngine* engine, const Action& action) { return obj; } -void actionFromScriptValue(const ScriptValue& object, Action& action) { +bool actionFromScriptValue(const ScriptValue& object, Action& action) { action = Action(object.property("action").toVariant().toInt()); + return true; } ScriptValue inputPairToScriptValue(ScriptEngine* engine, const Input::NamedPair& inputPair) { @@ -433,17 +435,19 @@ ScriptValue inputPairToScriptValue(ScriptEngine* engine, const Input::NamedPair& return obj; } -void inputPairFromScriptValue(const ScriptValue& object, Input::NamedPair& inputPair) { +bool inputPairFromScriptValue(const ScriptValue& object, Input::NamedPair& inputPair) { inputFromScriptValue(object.property("input"), inputPair.first); inputPair.second = QString(object.property("inputName").toVariant().toString()); + return true; } ScriptValue handToScriptValue(ScriptEngine* engine, const controller::Hand& hand) { return engine->newValue((int)hand); } -void handFromScriptValue(const ScriptValue& object, controller::Hand& hand) { +bool handFromScriptValue(const ScriptValue& object, controller::Hand& hand) { hand = Hand(object.toVariant().toInt()); + return true; } void UserInputMapper::registerControllerTypes(ScriptEngine* engine) { diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index c3a36bbbd8..9126876da4 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -2588,20 +2588,22 @@ ScriptValue EntityItemNonDefaultPropertiesToScriptValue(ScriptEngine* engine, co return properties.copyToScriptValue(engine, true); } -void EntityItemPropertiesFromScriptValueIgnoreReadOnly(const ScriptValue &object, EntityItemProperties& properties) { +bool EntityItemPropertiesFromScriptValueIgnoreReadOnly(const ScriptValue &object, EntityItemProperties& properties) { properties.copyFromScriptValue(object, false); + return true; } -void EntityItemPropertiesFromScriptValueHonorReadOnly(const ScriptValue &object, EntityItemProperties& properties) { +bool EntityItemPropertiesFromScriptValueHonorReadOnly(const ScriptValue &object, EntityItemProperties& properties) { properties.copyFromScriptValue(object, true); + return true; } ScriptValue EntityPropertyFlagsToScriptValue(ScriptEngine* engine, const EntityPropertyFlags& flags) { return EntityItemProperties::entityPropertyFlagsToScriptValue(engine, flags); } -void EntityPropertyFlagsFromScriptValue(const ScriptValue& object, EntityPropertyFlags& flags) { - EntityItemProperties::entityPropertyFlagsFromScriptValue(object, flags); +bool EntityPropertyFlagsFromScriptValue(const ScriptValue& object, EntityPropertyFlags& flags) { + return EntityItemProperties::entityPropertyFlagsFromScriptValue(object, flags); } @@ -2610,7 +2612,7 @@ ScriptValue EntityItemProperties::entityPropertyFlagsToScriptValue(ScriptEngine* return result; } -void EntityItemProperties::entityPropertyFlagsFromScriptValue(const ScriptValue& object, EntityPropertyFlags& flags) { +bool EntityItemProperties::entityPropertyFlagsFromScriptValue(const ScriptValue& object, EntityPropertyFlags& flags) { if (object.isString()) { EntityPropertyInfo propertyInfo; if (getPropertyInfo(object.toString(), propertyInfo)) { @@ -2627,6 +2629,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const ScriptValue& } } } + return true; } static QHash _propertyInfos; @@ -3027,10 +3030,11 @@ ScriptValue EntityPropertyInfoToScriptValue(ScriptEngine* engine, const EntityPr return obj; } -void EntityPropertyInfoFromScriptValue(const ScriptValue& object, EntityPropertyInfo& propertyInfo) { +bool EntityPropertyInfoFromScriptValue(const ScriptValue& object, EntityPropertyInfo& propertyInfo) { propertyInfo.propertyEnum = (EntityPropertyList)object.property("propertyEnum").toVariant().toUInt(); propertyInfo.minimum = object.property("minimum").toVariant(); propertyInfo.maximum = object.property("maximum").toVariant(); + return true; } // TODO: Implement support for edit packets that can span an MTU sized buffer. We need to implement a mechanism for the diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index c8016461d7..678bd9680a 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -141,7 +141,7 @@ public: void copyFromJSONString(ScriptEngine& scriptEngine, const QString& jsonString); static ScriptValue entityPropertyFlagsToScriptValue(ScriptEngine* engine, const EntityPropertyFlags& flags); - static void entityPropertyFlagsFromScriptValue(const ScriptValue& object, EntityPropertyFlags& flags); + static bool entityPropertyFlagsFromScriptValue(const ScriptValue& object, EntityPropertyFlags& flags); static bool getPropertyInfo(const QString& propertyName, EntityPropertyInfo& propertyInfo); @@ -543,16 +543,16 @@ private: Q_DECLARE_METATYPE(EntityItemProperties); ScriptValue EntityItemPropertiesToScriptValue(ScriptEngine* engine, const EntityItemProperties& properties); ScriptValue EntityItemNonDefaultPropertiesToScriptValue(ScriptEngine* engine, const EntityItemProperties& properties); -void EntityItemPropertiesFromScriptValueIgnoreReadOnly(const ScriptValue& object, EntityItemProperties& properties); -void EntityItemPropertiesFromScriptValueHonorReadOnly(const ScriptValue& object, EntityItemProperties& properties); +bool EntityItemPropertiesFromScriptValueIgnoreReadOnly(const ScriptValue& object, EntityItemProperties& properties); +bool EntityItemPropertiesFromScriptValueHonorReadOnly(const ScriptValue& object, EntityItemProperties& properties); Q_DECLARE_METATYPE(EntityPropertyFlags); ScriptValue EntityPropertyFlagsToScriptValue(ScriptEngine* engine, const EntityPropertyFlags& flags); -void EntityPropertyFlagsFromScriptValue(const ScriptValue& object, EntityPropertyFlags& flags); +bool EntityPropertyFlagsFromScriptValue(const ScriptValue& object, EntityPropertyFlags& flags); Q_DECLARE_METATYPE(EntityPropertyInfo); ScriptValue EntityPropertyInfoToScriptValue(ScriptEngine* engine, const EntityPropertyInfo& propertyInfo); -void EntityPropertyInfoFromScriptValue(const ScriptValue& object, EntityPropertyInfo& propertyInfo); +bool EntityPropertyInfoFromScriptValue(const ScriptValue& 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/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 6efb4f4671..f93ad4ef97 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1690,7 +1690,7 @@ ScriptValue RayToEntityIntersectionResultToScriptValue(ScriptEngine* engine, con return obj; } -void RayToEntityIntersectionResultFromScriptValue(const ScriptValue& object, RayToEntityIntersectionResult& value) { +bool RayToEntityIntersectionResultFromScriptValue(const ScriptValue& object, RayToEntityIntersectionResult& value) { value.intersects = object.property("intersects").toVariant().toBool(); value.accurate = object.property("accurate").toVariant().toBool(); ScriptValue entityIDValue = object.property("entityID"); @@ -1707,6 +1707,7 @@ void RayToEntityIntersectionResultFromScriptValue(const ScriptValue& object, Ray vec3FromScriptValue(surfaceNormal, value.surfaceNormal); } value.extraInfo = object.property("extraInfo").toVariant().toMap(); + return true; } bool EntityScriptingInterface::polyVoxWorker(QUuid entityID, std::function actor) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 730feba981..46208966f1 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -92,7 +92,7 @@ public: }; Q_DECLARE_METATYPE(RayToEntityIntersectionResult) ScriptValue RayToEntityIntersectionResultToScriptValue(ScriptEngine* engine, const RayToEntityIntersectionResult& results); -void RayToEntityIntersectionResultFromScriptValue(const ScriptValue& object, RayToEntityIntersectionResult& results); +bool RayToEntityIntersectionResultFromScriptValue(const ScriptValue& object, RayToEntityIntersectionResult& results); class ParabolaToEntityIntersectionResult { public: diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index 1bf6c52ca0..aa59a8f445 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -325,14 +325,14 @@ namespace scriptable { } return engine->newQObject(object, ScriptEngine::QtOwnership, ScriptEngine::AutoCreateDynamicProperties); }, - [](const ScriptValue& value, QPointer& out) { + [](const ScriptValue& value, QPointer& out) -> bool { auto obj = value.toQObject(); #ifdef SCRIPTABLE_MESH_DEBUG qCInfo(graphics_scripting) << "qpointer_qobject_cast" << obj << value.toString(); #endif if (auto tmp = qobject_cast(obj)) { out = QPointer(tmp); - return; + return true; } #if 0 if (auto tmp = static_cast(obj)) { @@ -340,10 +340,11 @@ namespace scriptable { qCInfo(graphics_scripting) << "qpointer_qobject_cast -- via static_cast" << obj << tmp << value.toString(); #endif out = QPointer(tmp); - return; + return true; } #endif out = nullptr; + return false; } ); } @@ -352,8 +353,9 @@ namespace scriptable { return scriptValueFromSequence(engine, vector); } - void qVectorScriptableMaterialLayerFromScriptValue(const ScriptValue& array, QVector& result) { + bool qVectorScriptableMaterialLayerFromScriptValue(const ScriptValue& array, QVector& result) { scriptValueToSequence(array, result); + return true; } /*@jsdoc @@ -625,8 +627,9 @@ namespace scriptable { return obj; } - void scriptableMaterialFromScriptValue(const ScriptValue &object, scriptable::ScriptableMaterial& material) { + bool scriptableMaterialFromScriptValue(const ScriptValue& object, scriptable::ScriptableMaterial& material) { // No need to convert from ScriptValue to ScriptableMaterial + return false; } ScriptValue scriptableMaterialLayerToScriptValue(ScriptEngine* engine, const scriptable::ScriptableMaterialLayer &materialLayer) { @@ -636,8 +639,9 @@ namespace scriptable { return obj; } - void scriptableMaterialLayerFromScriptValue(const ScriptValue &object, scriptable::ScriptableMaterialLayer& materialLayer) { + bool scriptableMaterialLayerFromScriptValue(const ScriptValue& object, scriptable::ScriptableMaterialLayer& materialLayer) { // No need to convert from ScriptValue to ScriptableMaterialLayer + return false; } ScriptValue multiMaterialMapToScriptValue(ScriptEngine* engine, const scriptable::MultiMaterialMap& map) { @@ -648,8 +652,9 @@ namespace scriptable { return obj; } - void multiMaterialMapFromScriptValue(const ScriptValue& map, scriptable::MultiMaterialMap& result) { + bool multiMaterialMapFromScriptValue(const ScriptValue& map, scriptable::MultiMaterialMap& result) { // No need to convert from ScriptValue to MultiMaterialMap + return false; } template int registerDebugEnum(ScriptEngine* engine, const DebugEnums& debugEnums) { @@ -659,8 +664,9 @@ namespace scriptable { [](ScriptEngine* engine, const T& topology) -> ScriptValue { return engine->newValue(instance.value(topology)); }, - [](const ScriptValue& value, T& topology) { + [](const ScriptValue& value, T& topology) -> bool { topology = instance.key(value.toString()); + return true; } ); } diff --git a/libraries/script-engine/src/KeyEvent.cpp b/libraries/script-engine/src/KeyEvent.cpp index a6769c41c1..1c60ba7e1e 100644 --- a/libraries/script-engine/src/KeyEvent.cpp +++ b/libraries/script-engine/src/KeyEvent.cpp @@ -187,7 +187,7 @@ ScriptValue KeyEvent::toScriptValue(ScriptEngine* engine, const KeyEvent& event) return obj; } -void KeyEvent::fromScriptValue(const ScriptValue& object, KeyEvent& event) { +bool KeyEvent::fromScriptValue(const ScriptValue& object, KeyEvent& event) { event.isValid = false; // assume the worst event.isMeta = object.property("isMeta").toVariant().toBool(); @@ -281,6 +281,7 @@ void KeyEvent::fromScriptValue(const ScriptValue& object, KeyEvent& event) { } event.isValid = true; } + return true; } ScriptValue isShifted = object.property("isShifted"); diff --git a/libraries/script-engine/src/KeyEvent.h b/libraries/script-engine/src/KeyEvent.h index 95cac8bc9e..658107e152 100644 --- a/libraries/script-engine/src/KeyEvent.h +++ b/libraries/script-engine/src/KeyEvent.h @@ -30,7 +30,7 @@ public: operator QKeySequence() const; static ScriptValue toScriptValue(ScriptEngine* engine, const KeyEvent& event); - static void fromScriptValue(const ScriptValue& object, KeyEvent& event); + static bool fromScriptValue(const ScriptValue& object, KeyEvent& event); int key; QString text; diff --git a/libraries/script-engine/src/MIDIEvent.cpp b/libraries/script-engine/src/MIDIEvent.cpp index d25ddc7e63..2070e7bbc9 100644 --- a/libraries/script-engine/src/MIDIEvent.cpp +++ b/libraries/script-engine/src/MIDIEvent.cpp @@ -35,9 +35,10 @@ ScriptValue midiEventToScriptValue(ScriptEngine* engine, const MIDIEvent& event) return obj; } -void midiEventFromScriptValue(const ScriptValue &object, MIDIEvent& event) { +bool midiEventFromScriptValue(const ScriptValue &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(); + return true; } \ No newline at end of file diff --git a/libraries/script-engine/src/MIDIEvent.h b/libraries/script-engine/src/MIDIEvent.h index 1b2007cf3b..46b1ffb8ee 100644 --- a/libraries/script-engine/src/MIDIEvent.h +++ b/libraries/script-engine/src/MIDIEvent.h @@ -33,7 +33,7 @@ Q_DECLARE_METATYPE(MIDIEvent) void registerMIDIMetaTypes(ScriptEngine* engine); ScriptValue midiEventToScriptValue(ScriptEngine* engine, const MIDIEvent& event); -void midiEventFromScriptValue(const ScriptValue &object, MIDIEvent& event); +bool midiEventFromScriptValue(const ScriptValue &object, MIDIEvent& event); #endif // hifi_MIDIEvent_h diff --git a/libraries/script-engine/src/MenuItemProperties.cpp b/libraries/script-engine/src/MenuItemProperties.cpp index 9864d7dd7f..f25d05ddf9 100644 --- a/libraries/script-engine/src/MenuItemProperties.cpp +++ b/libraries/script-engine/src/MenuItemProperties.cpp @@ -73,7 +73,7 @@ ScriptValue menuItemPropertiesToScriptValue(ScriptEngine* engine, const MenuItem * @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 ScriptValue& object, MenuItemProperties& properties) { +bool menuItemPropertiesFromScriptValue(const ScriptValue& object, MenuItemProperties& properties) { properties.menuName = object.property("menuName").toVariant().toString(); properties.menuItemName = object.property("menuItemName").toVariant().toString(); properties.isCheckable = object.property("isCheckable").toVariant().toBool(); @@ -99,6 +99,7 @@ void menuItemPropertiesFromScriptValue(const ScriptValue& object, MenuItemProper properties.beforeItem = object.property("beforeItem").toVariant().toString(); properties.afterItem = object.property("afterItem").toVariant().toString(); properties.grouping = object.property("grouping").toVariant().toString(); + return true; } diff --git a/libraries/script-engine/src/MenuItemProperties.h b/libraries/script-engine/src/MenuItemProperties.h index 2ec69b7ff9..9bc7ed4e0e 100644 --- a/libraries/script-engine/src/MenuItemProperties.h +++ b/libraries/script-engine/src/MenuItemProperties.h @@ -55,7 +55,7 @@ private: }; Q_DECLARE_METATYPE(MenuItemProperties) ScriptValue menuItemPropertiesToScriptValue(ScriptEngine* engine, const MenuItemProperties& props); -void menuItemPropertiesFromScriptValue(const ScriptValue& object, MenuItemProperties& props); +bool menuItemPropertiesFromScriptValue(const ScriptValue& object, MenuItemProperties& props); void registerMenuItemProperties(ScriptEngine* engine); diff --git a/libraries/script-engine/src/MouseEvent.cpp b/libraries/script-engine/src/MouseEvent.cpp index c436cb6f35..a0726e0aed 100644 --- a/libraries/script-engine/src/MouseEvent.cpp +++ b/libraries/script-engine/src/MouseEvent.cpp @@ -102,6 +102,7 @@ ScriptValue MouseEvent::toScriptValue(ScriptEngine* engine, const MouseEvent& ev return obj; } -void MouseEvent::fromScriptValue(const ScriptValue& object, MouseEvent& event) { +bool MouseEvent::fromScriptValue(const ScriptValue& object, MouseEvent& event) { // nothing for now... + return false; } diff --git a/libraries/script-engine/src/MouseEvent.h b/libraries/script-engine/src/MouseEvent.h index 2abc8085c0..4976e6e30d 100644 --- a/libraries/script-engine/src/MouseEvent.h +++ b/libraries/script-engine/src/MouseEvent.h @@ -28,7 +28,7 @@ public: MouseEvent(const QMouseEvent& event); static ScriptValue toScriptValue(ScriptEngine* engine, const MouseEvent& event); - static void fromScriptValue(const ScriptValue& object, MouseEvent& event); + static bool fromScriptValue(const ScriptValue& object, MouseEvent& event); ScriptValue toScriptValue(ScriptEngine* engine) const { return MouseEvent::toScriptValue(engine, *this); } diff --git a/libraries/script-engine/src/PointerEvent.cpp b/libraries/script-engine/src/PointerEvent.cpp index 87cb95fa95..8107640a87 100644 --- a/libraries/script-engine/src/PointerEvent.cpp +++ b/libraries/script-engine/src/PointerEvent.cpp @@ -212,7 +212,7 @@ ScriptValue PointerEvent::toScriptValue(ScriptEngine* engine, const PointerEvent return obj; } -void PointerEvent::fromScriptValue(const ScriptValue& object, PointerEvent& event) { +bool PointerEvent::fromScriptValue(const ScriptValue& object, PointerEvent& event) { if (object.isObject()) { ScriptValue type = object.property("type"); QString typeStr = type.isString() ? type.toString() : "Move"; @@ -263,6 +263,7 @@ void PointerEvent::fromScriptValue(const ScriptValue& object, PointerEvent& even event._keyboardModifiers = (Qt::KeyboardModifiers)(object.property("keyboardModifiers").toUInt32()); } + return true; } static const char* typeToStringMap[PointerEvent::NumEventTypes] = { "Press", "DoublePress", "Release", "Move" }; diff --git a/libraries/script-engine/src/PointerEvent.h b/libraries/script-engine/src/PointerEvent.h index fb4617ba6e..258cdd22e2 100644 --- a/libraries/script-engine/src/PointerEvent.h +++ b/libraries/script-engine/src/PointerEvent.h @@ -52,7 +52,7 @@ public: Button button = NoButtons, uint32_t buttons = NoButtons, Qt::KeyboardModifiers keyboardModifiers = Qt::NoModifier); static ScriptValue toScriptValue(ScriptEngine* engine, const PointerEvent& event); - static void fromScriptValue(const ScriptValue& object, PointerEvent& event); + static bool fromScriptValue(const ScriptValue& object, PointerEvent& event); ScriptValue toScriptValue(ScriptEngine* engine) const { return PointerEvent::toScriptValue(engine, *this); } diff --git a/libraries/script-engine/src/ScriptContext.cpp b/libraries/script-engine/src/ScriptContext.cpp new file mode 100644 index 0000000000..16b49fe705 --- /dev/null +++ b/libraries/script-engine/src/ScriptContext.cpp @@ -0,0 +1,23 @@ +// +// ScriptContext.h +// libraries/script-engine/src +// +// Created by Heather Anderson on 12/5/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 +// + +#include "ScriptContext.h" + +#include "Scriptable.h" + +ScriptContextGuard::ScriptContextGuard(ScriptContext* context) { + _oldContext = Scriptable::context(); + Scriptable::setContext(context); +} + +ScriptContextGuard::~ScriptContextGuard() { + Scriptable::setContext(_oldContext); +} diff --git a/libraries/script-engine/src/ScriptContext.h b/libraries/script-engine/src/ScriptContext.h index e046fe88ca..33c227b9a3 100644 --- a/libraries/script-engine/src/ScriptContext.h +++ b/libraries/script-engine/src/ScriptContext.h @@ -67,6 +67,15 @@ protected: ~ScriptContext() {} // prevent explicit deletion of base class }; +class ScriptContextGuard { +public: + ScriptContextGuard(ScriptContext* context); + ~ScriptContextGuard(); + +private: + ScriptContext* _oldContext; +}; + #endif // hifi_ScriptContext_h /// @} diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index d611e37559..9708babf83 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -49,7 +49,7 @@ class ScriptEngine { public: typedef ScriptValue (*FunctionSignature)(ScriptContext*, ScriptEngine*); typedef ScriptValue (*MarshalFunction)(ScriptEngine*, const void*); - typedef void (*DemarshalFunction)(const ScriptValue&, void*); + typedef bool (*DemarshalFunction)(const ScriptValue&, void*); enum ValueOwnership { QtOwnership = 0, @@ -136,8 +136,8 @@ public: public: // not for public use, but I don't like how Qt strings this along with private friend functions virtual ScriptValue create(int type, const void* ptr) = 0; - virtual bool convert(const ScriptValue& value, int type, void* ptr) = 0; - virtual void registerCustomType(int type, MarshalFunction mf, DemarshalFunction df, const ScriptValue& prototype) = 0; + virtual QVariant convert(const ScriptValue& value, int type) = 0; + virtual void registerCustomType(int type, MarshalFunction mf, DemarshalFunction df) = 0; protected: ~ScriptEngine() {} // prevent explicit deletion of base class diff --git a/libraries/script-engine/src/ScriptEngineCast.h b/libraries/script-engine/src/ScriptEngineCast.h index 475c869584..7ab6b02f38 100644 --- a/libraries/script-engine/src/ScriptEngineCast.h +++ b/libraries/script-engine/src/ScriptEngineCast.h @@ -42,13 +42,16 @@ inline ScriptValue scriptValueFromValue(ScriptEngine* engine, const QV template inline T scriptvalue_cast(const ScriptValue& value) { - T t; const int id = qMetaTypeId(); auto engine = value.engine(); - if (engine && engine->convert(value, id, &t)) { - return t; - } else if (value.isVariant()) { + if (engine) { + QVariant varValue = engine->convert(value, id); + if (varValue.isValid()) { + return varValue.value(); + } + } + if (value.isVariant()) { return qvariant_cast(value.toVariant()); } @@ -63,13 +66,12 @@ inline QVariant scriptvalue_cast(const ScriptValue& value) { template int scriptRegisterMetaType(ScriptEngine* eng, ScriptValue (*toScriptValue)(ScriptEngine*, const T& t), - void (*fromScriptValue)(const ScriptValue&, T& t), - const ScriptValue& prototype = ScriptValue(), + bool (*fromScriptValue)(const ScriptValue&, T& t), T* = 0) { const int id = qRegisterMetaType(); // make sure it's registered eng->registerCustomType(id, reinterpret_cast(toScriptValue), - reinterpret_cast(fromScriptValue), prototype); + reinterpret_cast(fromScriptValue)); return id; } @@ -87,19 +89,19 @@ ScriptValue scriptValueFromSequence(ScriptEngine* eng, const Container& cont) { } template -void scriptValueToSequence(const ScriptValue& value, Container& cont) { +bool scriptValueToSequence(const ScriptValue& value, Container& cont) { quint32 len = value.property(QLatin1String("length")).toUInt32(); for (quint32 i = 0; i < len; ++i) { ScriptValue item = value.property(i); cont.push_back(scriptvalue_cast(item)); } + return true; } template int scriptRegisterSequenceMetaType(ScriptEngine* engine, - const ScriptValue& prototype = ScriptValue(), T* = 0) { - return scriptRegisterMetaType(engine, scriptValueFromSequence, scriptValueToSequence, prototype); + return scriptRegisterMetaType(engine, scriptValueFromSequence, scriptValueToSequence); } #endif // hifi_ScriptEngineCast_h diff --git a/libraries/script-engine/src/ScriptManager.cpp b/libraries/script-engine/src/ScriptManager.cpp index b523c7d059..258d8737a9 100644 --- a/libraries/script-engine/src/ScriptManager.cpp +++ b/libraries/script-engine/src/ScriptManager.cpp @@ -538,8 +538,9 @@ static ScriptValue scriptableResourceToScriptValue(ScriptEngine* engine, return object; } -static void scriptableResourceFromScriptValue(const ScriptValue& value, ScriptableResourceRawPtr& resource) { +static bool scriptableResourceFromScriptValue(const ScriptValue& value, ScriptableResourceRawPtr& resource) { resource = static_cast(value.toQObject()); + return true; } /*@jsdoc @@ -578,8 +579,9 @@ ScriptValue externalResourceBucketToScriptValue(ScriptEngine* engine, ExternalRe return engine->newValue((int)in); } -void externalResourceBucketFromScriptValue(const ScriptValue& object, ExternalResource::Bucket& out) { +bool externalResourceBucketFromScriptValue(const ScriptValue& object, ExternalResource::Bucket& out) { out = static_cast(object.toInt32()); + return true; } void ScriptManager::resetModuleCache(bool deleteScriptCache) { @@ -722,6 +724,11 @@ void ScriptManager::init() { })); } +// registers a global object by name +void ScriptManager::registerValue(const QString& valueName, ScriptValue value) { + _engine->globalObject().setProperty(valueName, value); +} + // Unregister the handlers for this eventName and entityID. void ScriptManager::removeEventHandler(const EntityItemID& entityID, const QString& eventName, const ScriptValue& handler) { if (QThread::currentThread() != thread()) { diff --git a/libraries/script-engine/src/ScriptManager.h b/libraries/script-engine/src/ScriptManager.h index 8e94b0ed88..58db2bef69 100644 --- a/libraries/script-engine/src/ScriptManager.h +++ b/libraries/script-engine/src/ScriptManager.h @@ -275,6 +275,15 @@ public: */ Q_INVOKABLE bool isAgentScript() const { return _context == AGENT_SCRIPT; } + /*@jsdoc + * registers a global object by name. + * @function Script.registerValue + * @param {string} valueName + * @param {value} value + */ + /// registers a global object by name + Q_INVOKABLE void registerValue(const QString& valueName, ScriptValue value); + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // NOTE - these are intended to be public interfaces available to scripts diff --git a/libraries/script-engine/src/ScriptValueUtils.cpp b/libraries/script-engine/src/ScriptValueUtils.cpp index d183606e86..e428c06bff 100644 --- a/libraries/script-engine/src/ScriptValueUtils.cpp +++ b/libraries/script-engine/src/ScriptValueUtils.cpp @@ -99,7 +99,7 @@ ScriptValue vec2ToScriptValue(ScriptEngine* engine, const glm::vec2& vec2) { return value; } -void vec2FromScriptValue(const ScriptValue& object, glm::vec2& vec2) { +bool vec2FromScriptValue(const ScriptValue& object, glm::vec2& vec2) { if (object.isNumber()) { vec2 = glm::vec2(object.toVariant().toFloat()); } else if (object.isArray()) { @@ -122,6 +122,7 @@ void vec2FromScriptValue(const ScriptValue& object, glm::vec2& vec2) { vec2.x = x.toVariant().toFloat(); vec2.y = y.toVariant().toFloat(); } + return true; } ScriptValue vec3ToScriptValue(ScriptEngine* engine, const glm::vec3& vec3) { @@ -174,7 +175,7 @@ ScriptValue vec3ColorToScriptValue(ScriptEngine* engine, const glm::vec3& vec3) return value; } -void vec3FromScriptValue(const ScriptValue& object, glm::vec3& vec3) { +bool vec3FromScriptValue(const ScriptValue& object, glm::vec3& vec3) { if (object.isNumber()) { vec3 = glm::vec3(object.toVariant().toFloat()); } else if (object.isString()) { @@ -220,6 +221,7 @@ void vec3FromScriptValue(const ScriptValue& object, glm::vec3& vec3) { vec3.y = y.toVariant().toFloat(); vec3.z = z.toVariant().toFloat(); } + return true; } ScriptValue u8vec3ToScriptValue(ScriptEngine* engine, const glm::u8vec3& vec3) { @@ -272,7 +274,7 @@ ScriptValue u8vec3ColorToScriptValue(ScriptEngine* engine, const glm::u8vec3& ve return value; } -void u8vec3FromScriptValue(const ScriptValue& object, glm::u8vec3& vec3) { +bool u8vec3FromScriptValue(const ScriptValue& object, glm::u8vec3& vec3) { if (object.isNumber()) { vec3 = glm::vec3(object.toVariant().toUInt()); } else if (object.isString()) { @@ -318,6 +320,7 @@ void u8vec3FromScriptValue(const ScriptValue& object, glm::u8vec3& vec3) { vec3.y = y.toVariant().toUInt(); vec3.z = z.toVariant().toUInt(); } + return true; } ScriptValue vec4toScriptValue(ScriptEngine* engine, const glm::vec4& vec4) { @@ -329,11 +332,12 @@ ScriptValue vec4toScriptValue(ScriptEngine* engine, const glm::vec4& vec4) { return obj; } -void vec4FromScriptValue(const ScriptValue& object, glm::vec4& vec4) { +bool vec4FromScriptValue(const ScriptValue& 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(); + return true; } ScriptValue mat4toScriptValue(ScriptEngine* engine, const glm::mat4& mat4) { @@ -357,7 +361,7 @@ ScriptValue mat4toScriptValue(ScriptEngine* engine, const glm::mat4& mat4) { return obj; } -void mat4FromScriptValue(const ScriptValue& object, glm::mat4& mat4) { +bool mat4FromScriptValue(const ScriptValue& 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(); @@ -374,6 +378,7 @@ void mat4FromScriptValue(const ScriptValue& object, glm::mat4& mat4) { mat4[3][1] = object.property("r1c3").toVariant().toFloat(); mat4[3][2] = object.property("r2c3").toVariant().toFloat(); mat4[3][3] = object.property("r3c3").toVariant().toFloat(); + return true; } ScriptValue qVectorVec3ColorToScriptValue(ScriptEngine* engine, const QVector& vector) { @@ -404,7 +409,7 @@ QVector qVectorVec3FromScriptValue(const ScriptValue& array) { return newVector; } -void qVectorVec3FromScriptValue(const ScriptValue& array, QVector& vector) { +bool qVectorVec3FromScriptValue(const ScriptValue& array, QVector& vector) { int length = array.property("length").toInteger(); for (int i = 0; i < length; i++) { @@ -412,6 +417,7 @@ void qVectorVec3FromScriptValue(const ScriptValue& array, QVector& ve vec3FromScriptValue(array.property(i), newVec3); vector << newVec3; } + return true; } ScriptValue quatToScriptValue(ScriptEngine* engine, const glm::quat& quat) { @@ -427,7 +433,7 @@ ScriptValue quatToScriptValue(ScriptEngine* engine, const glm::quat& quat) { return obj; } -void quatFromScriptValue(const ScriptValue& object, glm::quat& quat) { +bool quatFromScriptValue(const ScriptValue& 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(); @@ -440,6 +446,7 @@ void quatFromScriptValue(const ScriptValue& object, glm::quat& quat) { } else { quat = glm::quat(); } + return true; } ScriptValue qVectorQuatToScriptValue(ScriptEngine* engine, const QVector& vector) { @@ -482,12 +489,13 @@ ScriptValue qVectorQUuidToScriptValue(ScriptEngine* engine, const QVector return array; } -void qVectorQUuidFromScriptValue(const ScriptValue& array, QVector& vector) { +bool qVectorQUuidFromScriptValue(const ScriptValue& array, QVector& vector) { int length = array.property("length").toInteger(); for (int i = 0; i < length; i++) { vector << array.property(i).toVariant().toUuid(); } + return true; } QVector qVectorQUuidFromScriptValue(const ScriptValue& array) { @@ -523,20 +531,22 @@ ScriptValue qVectorIntToScriptValue(ScriptEngine* engine, const QVector& vector) { +bool qVectorFloatFromScriptValue(const ScriptValue& array, QVector& vector) { int length = array.property("length").toInteger(); for (int i = 0; i < length; i++) { vector << array.property(i).toVariant().toFloat(); } + return true; } -void qVectorIntFromScriptValue(const ScriptValue& array, QVector& vector) { +bool qVectorIntFromScriptValue(const ScriptValue& array, QVector& vector) { int length = array.property("length").toInteger(); for (int i = 0; i < length; i++) { vector << array.property(i).toVariant().toInt(); } + return true; } QVector qVectorQuatFromScriptValue(const ScriptValue& array) { @@ -551,7 +561,7 @@ QVector qVectorQuatFromScriptValue(const ScriptValue& array) { return newVector; } -void qVectorQuatFromScriptValue(const ScriptValue& array, QVector& vector) { +bool qVectorQuatFromScriptValue(const ScriptValue& array, QVector& vector) { int length = array.property("length").toInteger(); for (int i = 0; i < length; i++) { @@ -559,6 +569,7 @@ void qVectorQuatFromScriptValue(const ScriptValue& array, QVector& ve quatFromScriptValue(array.property(i), newQuat); vector << newQuat; } + return true; } QVector qVectorBoolFromScriptValue(const ScriptValue& array) { @@ -571,12 +582,13 @@ QVector qVectorBoolFromScriptValue(const ScriptValue& array) { return newVector; } -void qVectorBoolFromScriptValue(const ScriptValue& array, QVector& vector) { +bool qVectorBoolFromScriptValue(const ScriptValue& array, QVector& vector) { int length = array.property("length").toInteger(); for (int i = 0; i < length; i++) { vector << array.property(i).toBool(); } + return true; } ScriptValue qRectToScriptValue(ScriptEngine* engine, const QRect& rect) { @@ -588,11 +600,12 @@ ScriptValue qRectToScriptValue(ScriptEngine* engine, const QRect& rect) { return obj; } -void qRectFromScriptValue(const ScriptValue& object, QRect& rect) { +bool qRectFromScriptValue(const ScriptValue& 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()); + return true; } ScriptValue qRectFToScriptValue(ScriptEngine* engine, const QRectF& rect) { @@ -604,11 +617,12 @@ ScriptValue qRectFToScriptValue(ScriptEngine* engine, const QRectF& rect) { return obj; } -void qRectFFromScriptValue(const ScriptValue& object, QRectF& rect) { +bool qRectFFromScriptValue(const ScriptValue& 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()); + return true; } ScriptValue qColorToScriptValue(ScriptEngine* engine, const QColor& color) { @@ -639,7 +653,7 @@ ScriptValue aaCubeToScriptValue(ScriptEngine* engine, const AACube& aaCube) { return obj; } -void aaCubeFromScriptValue(const ScriptValue& object, AACube& aaCube) { +bool aaCubeFromScriptValue(const ScriptValue& object, AACube& aaCube) { glm::vec3 corner; corner.x = object.property("x").toVariant().toFloat(); corner.y = object.property("y").toVariant().toFloat(); @@ -647,9 +661,10 @@ void aaCubeFromScriptValue(const ScriptValue& object, AACube& aaCube) { float scale = object.property("scale").toVariant().toFloat(); aaCube.setBox(corner, scale); + return true; } -void qColorFromScriptValue(const ScriptValue& object, QColor& color) { +bool qColorFromScriptValue(const ScriptValue& object, QColor& color) { if (object.isNumber()) { color.setRgb(object.toUInt32()); @@ -661,14 +676,16 @@ void qColorFromScriptValue(const ScriptValue& object, QColor& color) { color.setRgb(object.property("red").toInt32(), object.property("green").toInt32(), object.property("blue").toInt32(), alphaValue.isNumber() ? alphaValue.toInt32() : 255); } + return true; } ScriptValue qURLToScriptValue(ScriptEngine* engine, const QUrl& url) { return engine->newValue(url.toString()); } -void qURLFromScriptValue(const ScriptValue& object, QUrl& url) { +bool qURLFromScriptValue(const ScriptValue& object, QUrl& url) { url = object.toString(); + return true; } ScriptValue pickRayToScriptValue(ScriptEngine* engine, const PickRay& pickRay) { @@ -680,7 +697,7 @@ ScriptValue pickRayToScriptValue(ScriptEngine* engine, const PickRay& pickRay) { return obj; } -void pickRayFromScriptValue(const ScriptValue& object, PickRay& pickRay) { +bool pickRayFromScriptValue(const ScriptValue& object, PickRay& pickRay) { ScriptValue originValue = object.property("origin"); if (originValue.isValid()) { auto x = originValue.property("x"); @@ -703,6 +720,7 @@ void pickRayFromScriptValue(const ScriptValue& object, PickRay& pickRay) { pickRay.direction.z = z.toVariant().toFloat(); } } + return true; } /**jsdoc @@ -726,8 +744,9 @@ ScriptValue collisionToScriptValue(ScriptEngine* engine, const Collision& collis return obj; } -void collisionFromScriptValue(const ScriptValue& object, Collision& collision) { +bool collisionFromScriptValue(const ScriptValue& object, Collision& collision) { // TODO: implement this when we know what it means to accept collision events from JS + return false; } ScriptValue quuidToScriptValue(ScriptEngine* engine, const QUuid& uuid) { @@ -738,14 +757,15 @@ ScriptValue quuidToScriptValue(ScriptEngine* engine, const QUuid& uuid) { return obj; } -void quuidFromScriptValue(const ScriptValue& object, QUuid& uuid) { +bool quuidFromScriptValue(const ScriptValue& object, QUuid& uuid) { if (object.isNull()) { uuid = QUuid(); - return; + return true; } QString uuidAsString = object.toVariant().toString(); QUuid fromString(uuidAsString); uuid = fromString; + return true; } /**jsdoc @@ -761,9 +781,10 @@ ScriptValue qSizeFToScriptValue(ScriptEngine* engine, const QSizeF& qSizeF) { return obj; } -void qSizeFFromScriptValue(const ScriptValue& object, QSizeF& qSizeF) { +bool qSizeFFromScriptValue(const ScriptValue& object, QSizeF& qSizeF) { qSizeF.setWidth(object.property("width").toVariant().toFloat()); qSizeF.setHeight(object.property("height").toVariant().toFloat()); + return true; } /**jsdoc @@ -802,16 +823,18 @@ ScriptValue animationDetailsToScriptValue(ScriptEngine* engine, const AnimationD return obj; } -void animationDetailsFromScriptValue(const ScriptValue& object, AnimationDetails& details) { +bool animationDetailsFromScriptValue(const ScriptValue& object, AnimationDetails& details) { // nothing for now... + return false; } ScriptValue meshToScriptValue(ScriptEngine* engine, MeshProxy* const& in) { return engine->newQObject(in, ScriptEngine::QtOwnership); } -void meshFromScriptValue(const ScriptValue& value, MeshProxy*& out) { +bool meshFromScriptValue(const ScriptValue& value, MeshProxy*& out) { out = qobject_cast(value.toQObject()); + return true; } ScriptValue meshesToScriptValue(ScriptEngine* engine, const MeshProxyList& in) { @@ -822,7 +845,7 @@ ScriptValue meshesToScriptValue(ScriptEngine* engine, const MeshProxyList& in) { return result; } -void meshesFromScriptValue(const ScriptValue& value, MeshProxyList& out) { +bool meshesFromScriptValue(const ScriptValue& value, MeshProxyList& out) { ScriptValueIteratorPointer itr(value.newIterator()); qDebug() << "in meshesFromScriptValue, value.length =" << value.property("length").toInt32(); @@ -836,6 +859,7 @@ void meshesFromScriptValue(const ScriptValue& value, MeshProxyList& out) { qDebug() << "null meshProxy"; } } + return true; } /**jsdoc @@ -849,8 +873,8 @@ ScriptValue meshFaceToScriptValue(ScriptEngine* engine, const MeshFace& meshFace return obj; } -void meshFaceFromScriptValue(const ScriptValue& object, MeshFace& meshFaceResult) { - qVectorIntFromScriptValue(object.property("vertices"), meshFaceResult.vertexIndices); +bool meshFaceFromScriptValue(const ScriptValue& object, MeshFace& meshFaceResult) { + return qVectorIntFromScriptValue(object.property("vertices"), meshFaceResult.vertexIndices); } ScriptValue qVectorMeshFaceToScriptValue(ScriptEngine* engine, const QVector& vector) { @@ -861,7 +885,7 @@ ScriptValue qVectorMeshFaceToScriptValue(ScriptEngine* engine, const QVector& result) { +bool qVectorMeshFaceFromScriptValue(const ScriptValue& array, QVector& result) { int length = array.property("length").toInteger(); result.clear(); @@ -870,18 +894,21 @@ void qVectorMeshFaceFromScriptValue(const ScriptValue& array, QVector& meshFaceFromScriptValue(array.property(i), meshFace); result << meshFace; } + return true; } ScriptValue stencilMaskModeToScriptValue(ScriptEngine* engine, const StencilMaskMode& stencilMode) { return engine->newValue((int)stencilMode); } -void stencilMaskModeFromScriptValue(const ScriptValue& object, StencilMaskMode& stencilMode) { +bool stencilMaskModeFromScriptValue(const ScriptValue& object, StencilMaskMode& stencilMode) { stencilMode = StencilMaskMode(object.toVariant().toInt()); + return true; } -void promiseFromScriptValue(const ScriptValue& object, std::shared_ptr& promise) { +bool promiseFromScriptValue(const ScriptValue& object, std::shared_ptr& promise) { Q_ASSERT(false); + return false; } ScriptValue promiseToScriptValue(ScriptEngine* engine, const std::shared_ptr& promise) { return engine->newQObject(promise.get()); @@ -891,8 +918,8 @@ ScriptValue EntityItemIDtoScriptValue(ScriptEngine* engine, const EntityItemID& return quuidToScriptValue(engine, id); } -void EntityItemIDfromScriptValue(const ScriptValue &object, EntityItemID& id) { - quuidFromScriptValue(object, id); +bool EntityItemIDfromScriptValue(const ScriptValue& object, EntityItemID& id) { + return quuidFromScriptValue(object, id); } QVector qVectorEntityItemIDFromScriptValue(const ScriptValue& array) { diff --git a/libraries/script-engine/src/ScriptValueUtils.h b/libraries/script-engine/src/ScriptValueUtils.h index b99b014f45..b1e20311b6 100644 --- a/libraries/script-engine/src/ScriptValueUtils.h +++ b/libraries/script-engine/src/ScriptValueUtils.h @@ -52,7 +52,7 @@ void registerMetaTypes(ScriptEngine* engine); * @property {number} r3c3 - Row 3, column 3 value. */ ScriptValue mat4toScriptValue(ScriptEngine* engine, const glm::mat4& mat4); -void mat4FromScriptValue(const ScriptValue& object, glm::mat4& mat4); +bool mat4FromScriptValue(const ScriptValue& object, glm::mat4& mat4); /**jsdoc * A 2-dimensional vector. @@ -69,7 +69,7 @@ void mat4FromScriptValue(const ScriptValue& object, glm::mat4& mat4); * color.v = 0.8; // { x: 0.7, y: 0.8 } */ ScriptValue vec2ToScriptValue(ScriptEngine* engine, const glm::vec2& vec2); -void vec2FromScriptValue(const ScriptValue& object, glm::vec2& vec2); +bool vec2FromScriptValue(const ScriptValue& object, glm::vec2& vec2); /**jsdoc * A 3-dimensional vector. See also the {@link Vec3(0)|Vec3} object. @@ -93,7 +93,7 @@ void vec2FromScriptValue(const ScriptValue& object, glm::vec2& vec2); */ ScriptValue vec3ToScriptValue(ScriptEngine* engine, const glm::vec3& vec3); ScriptValue vec3ColorToScriptValue(ScriptEngine* engine, const glm::vec3& vec3); -void vec3FromScriptValue(const ScriptValue& object, glm::vec3& vec3); +bool vec3FromScriptValue(const ScriptValue& object, glm::vec3& vec3); /**jsdoc * A color vector. See also the {@link Vec3(0)|Vec3} object. @@ -135,7 +135,7 @@ void vec3FromScriptValue(const ScriptValue& object, glm::vec3& vec3); */ ScriptValue u8vec3ToScriptValue(ScriptEngine* engine, const glm::u8vec3& vec3); ScriptValue u8vec3ColorToScriptValue(ScriptEngine* engine, const glm::u8vec3& vec3); -void u8vec3FromScriptValue(const ScriptValue& object, glm::u8vec3& vec3); +bool u8vec3FromScriptValue(const ScriptValue& object, glm::u8vec3& vec3); /**jsdoc * A 4-dimensional vector. @@ -147,11 +147,11 @@ void u8vec3FromScriptValue(const ScriptValue& object, glm::u8vec3& vec3); * @property {number} w - W-coordinate of the vector. */ ScriptValue vec4toScriptValue(ScriptEngine* engine, const glm::vec4& vec4); -void vec4FromScriptValue(const ScriptValue& object, glm::vec4& vec4); +bool vec4FromScriptValue(const ScriptValue& object, glm::vec4& vec4); // Quaternions ScriptValue quatToScriptValue(ScriptEngine* engine, const glm::quat& quat); -void quatFromScriptValue(const ScriptValue& object, glm::quat& quat); +bool quatFromScriptValue(const ScriptValue& object, glm::quat& quat); /**jsdoc * Defines a rectangular portion of an image or screen, or similar. @@ -163,63 +163,63 @@ void quatFromScriptValue(const ScriptValue& object, glm::quat& quat); */ class QRect; ScriptValue qRectToScriptValue(ScriptEngine* engine, const QRect& rect); -void qRectFromScriptValue(const ScriptValue& object, QRect& rect); +bool qRectFromScriptValue(const ScriptValue& object, QRect& rect); class QRectF; ScriptValue qRectFToScriptValue(ScriptEngine* engine, const QRectF& rect); -void qRectFFromScriptValue(const ScriptValue& object, QRectF& rect); +bool qRectFFromScriptValue(const ScriptValue& object, QRectF& rect); // QColor class QColor; ScriptValue qColorToScriptValue(ScriptEngine* engine, const QColor& color); -void qColorFromScriptValue(const ScriptValue& object, QColor& color); +bool qColorFromScriptValue(const ScriptValue& object, QColor& color); class QUrl; ScriptValue qURLToScriptValue(ScriptEngine* engine, const QUrl& url); -void qURLFromScriptValue(const ScriptValue& object, QUrl& url); +bool qURLFromScriptValue(const ScriptValue& object, QUrl& url); // vector Q_DECLARE_METATYPE(QVector) ScriptValue qVectorVec3ToScriptValue(ScriptEngine* engine, const QVector& vector); ScriptValue qVectorVec3ColorToScriptValue(ScriptEngine* engine, const QVector& vector); -void qVectorVec3FromScriptValue(const ScriptValue& array, QVector& vector); +bool qVectorVec3FromScriptValue(const ScriptValue& array, QVector& vector); QVector qVectorVec3FromScriptValue(const ScriptValue& array); // vector Q_DECLARE_METATYPE(QVector) ScriptValue qVectorQuatToScriptValue(ScriptEngine* engine, const QVector& vector); -void qVectorQuatFromScriptValue(const ScriptValue& array, QVector& vector); +bool qVectorQuatFromScriptValue(const ScriptValue& array, QVector& vector); QVector qVectorQuatFromScriptValue(const ScriptValue& array); // vector ScriptValue qVectorBoolToScriptValue(ScriptEngine* engine, const QVector& vector); -void qVectorBoolFromScriptValue(const ScriptValue& array, QVector& vector); +bool qVectorBoolFromScriptValue(const ScriptValue& array, QVector& vector); QVector qVectorBoolFromScriptValue(const ScriptValue& array); // vector ScriptValue qVectorFloatToScriptValue(ScriptEngine* engine, const QVector& vector); -void qVectorFloatFromScriptValue(const ScriptValue& array, QVector& vector); +bool qVectorFloatFromScriptValue(const ScriptValue& array, QVector& vector); QVector qVectorFloatFromScriptValue(const ScriptValue& array); // vector ScriptValue qVectorIntToScriptValue(ScriptEngine* engine, const QVector& vector); -void qVectorIntFromScriptValue(const ScriptValue& array, QVector& vector); +bool qVectorIntFromScriptValue(const ScriptValue& array, QVector& vector); ScriptValue qVectorQUuidToScriptValue(ScriptEngine* engine, const QVector& vector); -void qVectorQUuidFromScriptValue(const ScriptValue& array, QVector& vector); +bool qVectorQUuidFromScriptValue(const ScriptValue& array, QVector& vector); QVector qVectorQUuidFromScriptValue(const ScriptValue& array); class AACube; ScriptValue aaCubeToScriptValue(ScriptEngine* engine, const AACube& aaCube); -void aaCubeFromScriptValue(const ScriptValue& object, AACube& aaCube); +bool aaCubeFromScriptValue(const ScriptValue& object, AACube& aaCube); class PickRay; ScriptValue pickRayToScriptValue(ScriptEngine* engine, const PickRay& pickRay); -void pickRayFromScriptValue(const ScriptValue& object, PickRay& pickRay); +bool pickRayFromScriptValue(const ScriptValue& object, PickRay& pickRay); class Collision; ScriptValue collisionToScriptValue(ScriptEngine* engine, const Collision& collision); -void collisionFromScriptValue(const ScriptValue& object, Collision& collision); +bool collisionFromScriptValue(const ScriptValue& object, Collision& collision); /**jsdoc * UUIDs (Universally Unique IDentifiers) are used to uniquely identify entities, avatars, and the like. They are represented @@ -229,42 +229,42 @@ void collisionFromScriptValue(const ScriptValue& object, Collision& collision); */ //Q_DECLARE_METATYPE(QUuid) // don't need to do this for QUuid since it's already a meta type ScriptValue quuidToScriptValue(ScriptEngine* engine, const QUuid& uuid); -void quuidFromScriptValue(const ScriptValue& object, QUuid& uuid); +bool quuidFromScriptValue(const ScriptValue& object, QUuid& uuid); //Q_DECLARE_METATYPE(QSizeF) // Don't need to to this becase it's arleady a meta type class QSizeF; ScriptValue qSizeFToScriptValue(ScriptEngine* engine, const QSizeF& qSizeF); -void qSizeFFromScriptValue(const ScriptValue& object, QSizeF& qSizeF); +bool qSizeFFromScriptValue(const ScriptValue& object, QSizeF& qSizeF); class AnimationDetails; ScriptValue animationDetailsToScriptValue(ScriptEngine* engine, const AnimationDetails& event); -void animationDetailsFromScriptValue(const ScriptValue& object, AnimationDetails& event); +bool animationDetailsFromScriptValue(const ScriptValue& object, AnimationDetails& event); class MeshProxy; ScriptValue meshToScriptValue(ScriptEngine* engine, MeshProxy* const& in); -void meshFromScriptValue(const ScriptValue& value, MeshProxy*& out); +bool meshFromScriptValue(const ScriptValue& value, MeshProxy*& out); class MeshProxyList; ScriptValue meshesToScriptValue(ScriptEngine* engine, const MeshProxyList& in); -void meshesFromScriptValue(const ScriptValue& value, MeshProxyList& out); +bool meshesFromScriptValue(const ScriptValue& value, MeshProxyList& out); class MeshFace; ScriptValue meshFaceToScriptValue(ScriptEngine* engine, const MeshFace& meshFace); -void meshFaceFromScriptValue(const ScriptValue& object, MeshFace& meshFaceResult); +bool meshFaceFromScriptValue(const ScriptValue& object, MeshFace& meshFaceResult); ScriptValue qVectorMeshFaceToScriptValue(ScriptEngine* engine, const QVector& vector); -void qVectorMeshFaceFromScriptValue(const ScriptValue& array, QVector& result); +bool qVectorMeshFaceFromScriptValue(const ScriptValue& array, QVector& result); enum class StencilMaskMode; ScriptValue stencilMaskModeToScriptValue(ScriptEngine* engine, const StencilMaskMode& stencilMode); -void stencilMaskModeFromScriptValue(const ScriptValue& object, StencilMaskMode& stencilMode); +bool stencilMaskModeFromScriptValue(const ScriptValue& object, StencilMaskMode& stencilMode); class MiniPromise; -void promiseFromScriptValue(const ScriptValue& object, std::shared_ptr& promise); +bool promiseFromScriptValue(const ScriptValue& object, std::shared_ptr& promise); ScriptValue promiseToScriptValue(ScriptEngine* engine, const std::shared_ptr& promise); class EntityItemID; ScriptValue EntityItemIDtoScriptValue(ScriptEngine* engine, const EntityItemID& properties); -void EntityItemIDfromScriptValue(const ScriptValue& object, EntityItemID& properties); +bool EntityItemIDfromScriptValue(const ScriptValue& object, EntityItemID& properties); QVector qVectorEntityItemIDFromScriptValue(const ScriptValue& array); #endif // #define hifi_ScriptValueUtils_h diff --git a/libraries/script-engine/src/SpatialEvent.cpp b/libraries/script-engine/src/SpatialEvent.cpp index bada11b66e..d663fae306 100644 --- a/libraries/script-engine/src/SpatialEvent.cpp +++ b/libraries/script-engine/src/SpatialEvent.cpp @@ -44,6 +44,7 @@ ScriptValue SpatialEvent::toScriptValue(ScriptEngine* engine, const SpatialEvent return obj; } -void SpatialEvent::fromScriptValue(const ScriptValue& object,SpatialEvent& event) { +bool SpatialEvent::fromScriptValue(const ScriptValue& object, SpatialEvent& event) { // nothing for now... + return false; } diff --git a/libraries/script-engine/src/SpatialEvent.h b/libraries/script-engine/src/SpatialEvent.h index be7e5d3320..bfd9142b93 100644 --- a/libraries/script-engine/src/SpatialEvent.h +++ b/libraries/script-engine/src/SpatialEvent.h @@ -29,7 +29,7 @@ public: SpatialEvent(const SpatialEvent& other); static ScriptValue toScriptValue(ScriptEngine* engine, const SpatialEvent& event); - static void fromScriptValue(const ScriptValue& object, SpatialEvent& event); + static bool fromScriptValue(const ScriptValue& 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 593eb326fc..d65b585b0a 100644 --- a/libraries/script-engine/src/TouchEvent.cpp +++ b/libraries/script-engine/src/TouchEvent.cpp @@ -245,6 +245,7 @@ ScriptValue TouchEvent::toScriptValue(ScriptEngine* engine, const TouchEvent& ev return obj; } -void TouchEvent::fromScriptValue(const ScriptValue& object, TouchEvent& event) { +bool TouchEvent::fromScriptValue(const ScriptValue& object, TouchEvent& event) { // nothing for now... + return false; } diff --git a/libraries/script-engine/src/TouchEvent.h b/libraries/script-engine/src/TouchEvent.h index 80edf53dd2..bca52a5ac2 100644 --- a/libraries/script-engine/src/TouchEvent.h +++ b/libraries/script-engine/src/TouchEvent.h @@ -32,7 +32,7 @@ public: TouchEvent(const QTouchEvent& event, const TouchEvent& other); static ScriptValue toScriptValue(ScriptEngine* engine, const TouchEvent& event); - static void fromScriptValue(const ScriptValue& object, TouchEvent& event); + static bool fromScriptValue(const ScriptValue& object, TouchEvent& event); float x; float y; diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 8bb6e63082..fbe2b7fc06 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -208,22 +208,25 @@ ScriptValue qWSCloseCodeToScriptValue(ScriptEngine* engine, const QWebSocketProt return engine->newValue(closeCode); } -void qWSCloseCodeFromScriptValue(const ScriptValue &object, QWebSocketProtocol::CloseCode &closeCode) { +bool qWSCloseCodeFromScriptValue(const ScriptValue &object, QWebSocketProtocol::CloseCode &closeCode) { closeCode = (QWebSocketProtocol::CloseCode)object.toUInt16(); + return true; } ScriptValue webSocketToScriptValue(ScriptEngine* engine, WebSocketClass* const &in) { return engine->newQObject(in, ScriptEngine::ScriptOwnership); } -void webSocketFromScriptValue(const ScriptValue &object, WebSocketClass* &out) { +bool webSocketFromScriptValue(const ScriptValue &object, WebSocketClass* &out) { out = qobject_cast(object.toQObject()); + return true; } ScriptValue wscReadyStateToScriptValue(ScriptEngine* engine, const WebSocketClass::ReadyState& readyState) { return engine->newValue(readyState); } -void wscReadyStateFromScriptValue(const ScriptValue& object, WebSocketClass::ReadyState& readyState) { +bool wscReadyStateFromScriptValue(const ScriptValue& object, WebSocketClass::ReadyState& readyState) { readyState = (WebSocketClass::ReadyState)object.toUInt16(); + return true; } diff --git a/libraries/script-engine/src/WebSocketClass.h b/libraries/script-engine/src/WebSocketClass.h index 32c5db87f6..18efefe29c 100644 --- a/libraries/script-engine/src/WebSocketClass.h +++ b/libraries/script-engine/src/WebSocketClass.h @@ -253,13 +253,13 @@ Q_DECLARE_METATYPE(QWebSocketProtocol::CloseCode); Q_DECLARE_METATYPE(WebSocketClass::ReadyState); ScriptValue qWSCloseCodeToScriptValue(ScriptEngine* engine, const QWebSocketProtocol::CloseCode& closeCode); -void qWSCloseCodeFromScriptValue(const ScriptValue& object, QWebSocketProtocol::CloseCode& closeCode); +bool qWSCloseCodeFromScriptValue(const ScriptValue& object, QWebSocketProtocol::CloseCode& closeCode); ScriptValue webSocketToScriptValue(ScriptEngine* engine, WebSocketClass* const &in); -void webSocketFromScriptValue(const ScriptValue &object, WebSocketClass* &out); +bool webSocketFromScriptValue(const ScriptValue& object, WebSocketClass*& out); ScriptValue wscReadyStateToScriptValue(ScriptEngine* engine, const WebSocketClass::ReadyState& readyState); -void wscReadyStateFromScriptValue(const ScriptValue& object, WebSocketClass::ReadyState& readyState); +bool wscReadyStateFromScriptValue(const ScriptValue& object, WebSocketClass::ReadyState& readyState); #endif // hifi_WebSocketClass_h diff --git a/libraries/script-engine/src/WheelEvent.cpp b/libraries/script-engine/src/WheelEvent.cpp index ee2be92bce..808a008811 100644 --- a/libraries/script-engine/src/WheelEvent.cpp +++ b/libraries/script-engine/src/WheelEvent.cpp @@ -96,6 +96,7 @@ ScriptValue WheelEvent::toScriptValue(ScriptEngine* engine, const WheelEvent& ev return obj; } -void WheelEvent::fromScriptValue(const ScriptValue& object, WheelEvent& event) { +bool WheelEvent::fromScriptValue(const ScriptValue& object, WheelEvent& event) { // nothing for now... + return false; } diff --git a/libraries/script-engine/src/WheelEvent.h b/libraries/script-engine/src/WheelEvent.h index 9631c8d627..57822eda12 100644 --- a/libraries/script-engine/src/WheelEvent.h +++ b/libraries/script-engine/src/WheelEvent.h @@ -29,7 +29,7 @@ public: WheelEvent(const QWheelEvent& event); static ScriptValue toScriptValue(ScriptEngine* engine, const WheelEvent& event); - static void fromScriptValue(const ScriptValue& object, WheelEvent& event); + static bool fromScriptValue(const ScriptValue& object, WheelEvent& event); int x; int y; diff --git a/libraries/script-engine/src/qtscript/ArrayBufferClass.cpp b/libraries/script-engine/src/qtscript/ArrayBufferClass.cpp index 3902096e52..85564bc114 100644 --- a/libraries/script-engine/src/qtscript/ArrayBufferClass.cpp +++ b/libraries/script-engine/src/qtscript/ArrayBufferClass.cpp @@ -87,7 +87,8 @@ QScriptValue ArrayBufferClass::newInstance(qint32 size) { } QScriptValue ArrayBufferClass::newInstance(const QByteArray& ba) { - QScriptValue data = engine()->newVariant(QVariant::fromValue(ba)); + QScriptEngine* eng = engine(); + QScriptValue data = eng->newVariant(QVariant::fromValue(ba)); return engine()->newObject(this, data); } diff --git a/libraries/script-engine/src/qtscript/ScriptEngineQtScript.cpp b/libraries/script-engine/src/qtscript/ScriptEngineQtScript.cpp index c2c25f9bb8..e5ce15898b 100644 --- a/libraries/script-engine/src/qtscript/ScriptEngineQtScript.cpp +++ b/libraries/script-engine/src/qtscript/ScriptEngineQtScript.cpp @@ -49,17 +49,13 @@ #include "../ScriptValue.h" #include "ScriptContextQtWrapper.h" +#include "ScriptObjectQtProxy.h" #include "ScriptProgramQtWrapper.h" #include "ScriptValueQtWrapper.h" #include "ScriptContextQtAgent.h" static const int MAX_DEBUG_VALUE_LENGTH { 80 }; -static const QScriptEngine::QObjectWrapOptions DEFAULT_QOBJECT_WRAP_OPTIONS = - QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects; - -Q_DECLARE_METATYPE(ScriptValue); - Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature) int qfunctionSignatureMetaID = qRegisterMetaType(); @@ -311,21 +307,12 @@ void ScriptEngineQtScript::_debugDump(const QString& header, const QScriptValue& } #endif -static QScriptValue ScriptValueToQScriptValue(QScriptEngine* engine, const ScriptValue& src) { - return ScriptValueQtWrapper::fullUnwrap(static_cast(engine), src); -} - -static void ScriptValueFromQScriptValue(const QScriptValue& src, ScriptValue& dest) { - ScriptEngineQtScript* engine = static_cast(src.engine()); - dest = ScriptValue(new ScriptValueQtWrapper(engine, src)); -} - ScriptEngineQtScript::ScriptEngineQtScript(ScriptManager* scriptManager) : QScriptEngine(), _scriptManager(scriptManager), _arrayBufferClass(new ArrayBufferClass(this)) { - qScriptRegisterMetaType(this, ScriptValueToQScriptValue, ScriptValueFromQScriptValue); + registerSystemTypes(); if (_scriptManager) { connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) { @@ -343,10 +330,10 @@ ScriptEngineQtScript::ScriptEngineQtScript(ScriptManager* scriptManager) : } QScriptValue null = QScriptEngine::nullValue(); - _nullValue = ScriptValue(new ScriptValueQtWrapper(const_cast(this), std::move(null))); + _nullValue = ScriptValue(new ScriptValueQtWrapper(this, std::move(null))); QScriptValue undefined = QScriptEngine::undefinedValue(); - _undefinedValue = ScriptValue(new ScriptValueQtWrapper(const_cast(this), std::move(undefined))); + _undefinedValue = ScriptValue(new ScriptValueQtWrapper(this, std::move(undefined))); QScriptEngine::setProcessEventsInterval(MSECS_PER_SECOND); @@ -417,7 +404,7 @@ void ScriptEngineQtScript::registerGlobalObject(const QString& name, QObject* ob if (!QScriptEngine::globalObject().property(name).isValid()) { if (object) { - QScriptValue value = QScriptEngine::newQObject(object, QScriptEngine::QtOwnership, DEFAULT_QOBJECT_WRAP_OPTIONS); + QScriptValue value = ScriptObjectQtProxy::newQObject(this, object, ScriptEngine::QtOwnership); QScriptEngine::globalObject().setProperty(name, value); } else { QScriptEngine::globalObject().setProperty(name, QScriptValue()); @@ -747,7 +734,7 @@ void ScriptEngineQtScript::updateMemoryCost(const qint64& deltaSize) { ScriptValue ScriptEngineQtScript::globalObject() const { QScriptValue global = QScriptEngine::globalObject(); // can't cache the value as it may change - return ScriptValue(new ScriptValueQtWrapper(const_cast < ScriptEngineQtScript*>(this), std::move(global))); + return ScriptValue(new ScriptValueQtWrapper(const_cast(this), std::move(global))); } ScriptManager* ScriptEngineQtScript::manager() const { @@ -783,8 +770,7 @@ ScriptProgramPointer ScriptEngineQtScript::newProgram(const QString& sourceCode, ScriptValue ScriptEngineQtScript::newQObject(QObject* object, ScriptEngine::ValueOwnership ownership, const ScriptEngine::QObjectWrapOptions& options) { - QScriptValue result = QScriptEngine::newQObject(object, static_cast(ownership), - (QScriptEngine::QObjectWrapOptions)((int)options | DEFAULT_QOBJECT_WRAP_OPTIONS)); + QScriptValue result = ScriptObjectQtProxy::newQObject(this, object, ownership, options); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result))); } @@ -824,7 +810,7 @@ ScriptValue ScriptEngineQtScript::newValue(const char* value) { } ScriptValue ScriptEngineQtScript::newVariant(const QVariant& value) { - QScriptValue result = QScriptEngine::newVariant(value); + QScriptValue result = castVariantToValue(value); return ScriptValue(new ScriptValueQtWrapper(this, std::move(result))); } @@ -832,13 +818,6 @@ ScriptValue ScriptEngineQtScript::nullValue() { return _nullValue; } -void ScriptEngineQtScript::setDefaultPrototype(int metaTypeId, const ScriptValue& prototype){ - ScriptValueQtWrapper* unwrappedPrototype = ScriptValueQtWrapper::unwrap(prototype); - if (unwrappedPrototype) { - QScriptEngine::setDefaultPrototype(metaTypeId, unwrappedPrototype->toQtValue()); - } -} - ScriptValue ScriptEngineQtScript::undefinedValue() { return _undefinedValue; } @@ -928,112 +907,26 @@ bool ScriptEngineQtScript::raiseException(const ScriptValue& exception) { } ScriptValue ScriptEngineQtScript::create(int type, const void* ptr) { - QScriptValue result = qScriptValueFromValue_helper(this, type, ptr); - return ScriptValue(new ScriptValueQtWrapper(const_cast(this), std::move(result))); + QVariant variant(type, ptr); + QScriptValue scriptValue = castVariantToValue(variant); + return ScriptValue(new ScriptValueQtWrapper(this, std::move(scriptValue))); } -bool ScriptEngineQtScript::convert(const ScriptValue& value, int type, void* ptr) { +QVariant ScriptEngineQtScript::convert(const ScriptValue& value, int type) { ScriptValueQtWrapper* unwrapped = ScriptValueQtWrapper::unwrap(value); if (unwrapped == nullptr) { - return false; + return QVariant(); } - return qscriptvalue_cast_helper(unwrapped->toQtValue(), type, ptr); -} - -template -class CustomTypeInstance { -public: - static ScriptEngine::MarshalFunction marshalFunc; - static ScriptEngine::DemarshalFunction demarshalFunc; - - static QScriptValue internalMarshalFunc(QScriptEngine* engine, const void* src) { - ScriptEngineQtScript* unwrappedEngine = static_cast(engine); - ScriptValue dest = marshalFunc(unwrappedEngine, src); - return ScriptValueQtWrapper::fullUnwrap(unwrappedEngine, dest); - } - - static void internalDemarshalFunc(const QScriptValue& src, void* dest) { - ScriptEngineQtScript* unwrappedEngine = static_cast(src.engine()); - ScriptValue wrappedSrc(new ScriptValueQtWrapper(unwrappedEngine, src)); - demarshalFunc(wrappedSrc, dest); - } -}; -template -ScriptEngine::MarshalFunction CustomTypeInstance::marshalFunc; -template -ScriptEngine::DemarshalFunction CustomTypeInstance::demarshalFunc; - -// I would *LOVE* it if there was a different way to do this, jeez! -// Qt requires two functions that have no parameters that give any context, -// one of the must return a QScriptValue (so we can't void* them into generics and stick them in the templates). -// This *has* to be done via templates but the whole point of this is to avoid leaking types into the rest of -// the system that would require anyone other than us to have a dependency on QtScript -#define CUSTOM_TYPE_ENTRY(idx) \ - case idx: \ - CustomTypeInstance::marshalFunc = marshalFunc; \ - CustomTypeInstance::demarshalFunc = demarshalFunc; \ - internalMarshalFunc = CustomTypeInstance::internalMarshalFunc; \ - internalDemarshalFunc = CustomTypeInstance::internalDemarshalFunc; \ - break; -#define CUSTOM_TYPE_ENTRY_10(idx) \ - CUSTOM_TYPE_ENTRY((idx * 10)); \ - CUSTOM_TYPE_ENTRY((idx * 10) + 1); \ - CUSTOM_TYPE_ENTRY((idx * 10) + 2); \ - CUSTOM_TYPE_ENTRY((idx * 10) + 3); \ - CUSTOM_TYPE_ENTRY((idx * 10) + 4); \ - CUSTOM_TYPE_ENTRY((idx * 10) + 5); \ - CUSTOM_TYPE_ENTRY((idx * 10) + 6); \ - CUSTOM_TYPE_ENTRY((idx * 10) + 7); \ - CUSTOM_TYPE_ENTRY((idx * 10) + 8); \ - CUSTOM_TYPE_ENTRY((idx * 10) + 9); - -void ScriptEngineQtScript::registerCustomType(int type, - ScriptEngine::MarshalFunction marshalFunc, - ScriptEngine::DemarshalFunction demarshalFunc, - const ScriptValue& prototype) -{ - QScriptValue unwrapped = ScriptValueQtWrapper::fullUnwrap(this, prototype); - QScriptEngine::MarshalFunction internalMarshalFunc; - QScriptEngine::DemarshalFunction internalDemarshalFunc; - - if (_nextCustomType >= 300) { // have we ran out of translators? - Q_ASSERT(false); - return; - } - - switch (_nextCustomType++) { - CUSTOM_TYPE_ENTRY_10(0); - CUSTOM_TYPE_ENTRY_10(1); - CUSTOM_TYPE_ENTRY_10(2); - CUSTOM_TYPE_ENTRY_10(3); - CUSTOM_TYPE_ENTRY_10(4); - CUSTOM_TYPE_ENTRY_10(5); - CUSTOM_TYPE_ENTRY_10(6); - CUSTOM_TYPE_ENTRY_10(7); - CUSTOM_TYPE_ENTRY_10(8); - CUSTOM_TYPE_ENTRY_10(9); - CUSTOM_TYPE_ENTRY_10(10); - CUSTOM_TYPE_ENTRY_10(11); - CUSTOM_TYPE_ENTRY_10(12); - CUSTOM_TYPE_ENTRY_10(13); - CUSTOM_TYPE_ENTRY_10(14); - CUSTOM_TYPE_ENTRY_10(15); - CUSTOM_TYPE_ENTRY_10(16); - CUSTOM_TYPE_ENTRY_10(17); - CUSTOM_TYPE_ENTRY_10(18); - CUSTOM_TYPE_ENTRY_10(19); - CUSTOM_TYPE_ENTRY_10(20); - CUSTOM_TYPE_ENTRY_10(21); - CUSTOM_TYPE_ENTRY_10(22); - CUSTOM_TYPE_ENTRY_10(23); - CUSTOM_TYPE_ENTRY_10(24); - CUSTOM_TYPE_ENTRY_10(25); - CUSTOM_TYPE_ENTRY_10(26); - CUSTOM_TYPE_ENTRY_10(27); - CUSTOM_TYPE_ENTRY_10(28); - CUSTOM_TYPE_ENTRY_10(29); - CUSTOM_TYPE_ENTRY_10(30); - } - - qScriptRegisterMetaType_helper(this, type, internalMarshalFunc, internalDemarshalFunc, unwrapped); + + QVariant var; + if (!castValueToVariant(unwrapped->toQtValue(), var, type)) { + return QVariant(); + } + + int destType = var.userType(); + if (destType != type) { + var.convert(type); // if conversion fails then var is set to QVariant() + } + + return var; } diff --git a/libraries/script-engine/src/qtscript/ScriptEngineQtScript.h b/libraries/script-engine/src/qtscript/ScriptEngineQtScript.h index 9d1e54d1b3..5dd685292f 100644 --- a/libraries/script-engine/src/qtscript/ScriptEngineQtScript.h +++ b/libraries/script-engine/src/qtscript/ScriptEngineQtScript.h @@ -19,7 +19,9 @@ #include #include +#include #include +#include #include #include #include @@ -148,10 +150,11 @@ public: // public non-interface methods for other QtScript-specific classes to u public: // not for public use, but I don't like how Qt strings this along with private friend functions virtual ScriptValue create(int type, const void* ptr) override; - virtual bool convert(const ScriptValue& value, int type, void* ptr) override; + virtual QVariant convert(const ScriptValue& value, int type) override; virtual void registerCustomType(int type, ScriptEngine::MarshalFunction marshalFunc, - ScriptEngine::DemarshalFunction demarshalFunc, - const ScriptValue& prototype) override; + ScriptEngine::DemarshalFunction demarshalFunc) override; + bool castValueToVariant(const QScriptValue& val, QVariant& dest, int destType); + QScriptValue castVariantToValue(const QVariant& val); protected: // like `newFunction`, but allows mapping inline C++ lambdas with captures as callable QScriptValues @@ -162,9 +165,21 @@ protected: const QScriptValue& data = QScriptValue(), const QScriptEngine::ValueOwnership& ownership = QScriptEngine::AutoOwnership); + void registerSystemTypes(); + protected: + struct CustomMarshal { + ScriptEngine::MarshalFunction marshalFunc; + ScriptEngine::DemarshalFunction demarshalFunc; + }; + using CustomMarshalMap = QHash; + using CustomPrototypeMap = QHash; + QPointer _scriptManager; + mutable QMutex _customTypeProtect; + CustomMarshalMap _customTypes; + CustomPrototypeMap _customPrototypes; int _nextCustomType = 0; ScriptValue _nullValue; ScriptValue _undefinedValue; diff --git a/libraries/script-engine/src/qtscript/ScriptEngineQtScript_cast.cpp b/libraries/script-engine/src/qtscript/ScriptEngineQtScript_cast.cpp new file mode 100644 index 0000000000..69125ec607 --- /dev/null +++ b/libraries/script-engine/src/qtscript/ScriptEngineQtScript_cast.cpp @@ -0,0 +1,538 @@ +// +// ScriptEngineQtScript_cast.cpp +// libraries/script-engine/src/qtscript +// +// Created by Heather Anderson 12/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 +// + +#include "ScriptEngineQtScript.h" + +#include +#include +#include +#include + +#include "../ScriptValueIterator.h" + +#include "ScriptObjectQtProxy.h" +#include "ScriptValueQtWrapper.h" + +template +class CustomTypeInstance { +public: + static ScriptEngine::MarshalFunction marshalFunc; + static ScriptEngine::DemarshalFunction demarshalFunc; + + static QScriptValue internalMarshalFunc(QScriptEngine* engine, const void* src) { + ScriptEngineQtScript* unwrappedEngine = static_cast(engine); + ScriptValue dest = marshalFunc(unwrappedEngine, src); + return ScriptValueQtWrapper::fullUnwrap(unwrappedEngine, dest); + } + + static void internalDemarshalFunc(const QScriptValue& src, void* dest) { + ScriptEngineQtScript* unwrappedEngine = static_cast(src.engine()); + ScriptValue wrappedSrc(new ScriptValueQtWrapper(unwrappedEngine, src)); + demarshalFunc(wrappedSrc, dest); + } +}; +template +ScriptEngine::MarshalFunction CustomTypeInstance::marshalFunc; +template +ScriptEngine::DemarshalFunction CustomTypeInstance::demarshalFunc; + +// I would *LOVE* it if there was a different way to do this, jeez! +// Qt requires two functions that have no parameters that give any context, +// one of the must return a QScriptValue (so we can't void* them into generics and stick them in the templates). +// This *has* to be done via templates but the whole point of this is to avoid leaking types into the rest of +// the system that would require anyone other than us to have a dependency on QtScript +#define CUSTOM_TYPE_ENTRY(idx) \ + case idx: \ + CustomTypeInstance::marshalFunc = marshalFunc; \ + CustomTypeInstance::demarshalFunc = demarshalFunc; \ + internalMarshalFunc = CustomTypeInstance::internalMarshalFunc; \ + internalDemarshalFunc = CustomTypeInstance::internalDemarshalFunc; \ + break; +#define CUSTOM_TYPE_ENTRY_10(idx) \ + CUSTOM_TYPE_ENTRY((idx * 10)); \ + CUSTOM_TYPE_ENTRY((idx * 10) + 1); \ + CUSTOM_TYPE_ENTRY((idx * 10) + 2); \ + CUSTOM_TYPE_ENTRY((idx * 10) + 3); \ + CUSTOM_TYPE_ENTRY((idx * 10) + 4); \ + CUSTOM_TYPE_ENTRY((idx * 10) + 5); \ + CUSTOM_TYPE_ENTRY((idx * 10) + 6); \ + CUSTOM_TYPE_ENTRY((idx * 10) + 7); \ + CUSTOM_TYPE_ENTRY((idx * 10) + 8); \ + CUSTOM_TYPE_ENTRY((idx * 10) + 9); + +void ScriptEngineQtScript::setDefaultPrototype(int metaTypeId, const ScriptValue& prototype) { + ScriptValueQtWrapper* unwrappedPrototype = ScriptValueQtWrapper::unwrap(prototype); + if (unwrappedPrototype) { + const QScriptValue& scriptPrototype = unwrappedPrototype->toQtValue(); + QScriptEngine::setDefaultPrototype(metaTypeId, scriptPrototype); + + QMutexLocker guard(&_customTypeProtect); + _customPrototypes.insert(metaTypeId, scriptPrototype); + } +} + +void ScriptEngineQtScript::registerCustomType(int type, + ScriptEngine::MarshalFunction marshalFunc, + ScriptEngine::DemarshalFunction demarshalFunc) +{ + QScriptEngine::MarshalFunction internalMarshalFunc; + QScriptEngine::DemarshalFunction internalDemarshalFunc; + + { + QMutexLocker guard(&_customTypeProtect); + + // storing it in a map for our own benefit + CustomMarshal& customType = _customTypes.insert(type, CustomMarshal()).value(); + customType.demarshalFunc = demarshalFunc; + customType.marshalFunc = marshalFunc; + + // creating a conversion for QtScript's benefit + if (_nextCustomType >= 300) { // have we ran out of translators? + Q_ASSERT(false); + return; + } + + switch (_nextCustomType++) { + CUSTOM_TYPE_ENTRY_10(0); + CUSTOM_TYPE_ENTRY_10(1); + CUSTOM_TYPE_ENTRY_10(2); + CUSTOM_TYPE_ENTRY_10(3); + CUSTOM_TYPE_ENTRY_10(4); + CUSTOM_TYPE_ENTRY_10(5); + CUSTOM_TYPE_ENTRY_10(6); + CUSTOM_TYPE_ENTRY_10(7); + CUSTOM_TYPE_ENTRY_10(8); + CUSTOM_TYPE_ENTRY_10(9); + CUSTOM_TYPE_ENTRY_10(10); + CUSTOM_TYPE_ENTRY_10(11); + CUSTOM_TYPE_ENTRY_10(12); + CUSTOM_TYPE_ENTRY_10(13); + CUSTOM_TYPE_ENTRY_10(14); + CUSTOM_TYPE_ENTRY_10(15); + CUSTOM_TYPE_ENTRY_10(16); + CUSTOM_TYPE_ENTRY_10(17); + CUSTOM_TYPE_ENTRY_10(18); + CUSTOM_TYPE_ENTRY_10(19); + CUSTOM_TYPE_ENTRY_10(20); + CUSTOM_TYPE_ENTRY_10(21); + CUSTOM_TYPE_ENTRY_10(22); + CUSTOM_TYPE_ENTRY_10(23); + CUSTOM_TYPE_ENTRY_10(24); + CUSTOM_TYPE_ENTRY_10(25); + CUSTOM_TYPE_ENTRY_10(26); + CUSTOM_TYPE_ENTRY_10(27); + CUSTOM_TYPE_ENTRY_10(28); + CUSTOM_TYPE_ENTRY_10(29); + CUSTOM_TYPE_ENTRY_10(30); + } + } + + qScriptRegisterMetaType_helper(this, type, internalMarshalFunc, internalDemarshalFunc, QScriptValue()); +} + +Q_DECLARE_METATYPE(ScriptValue); + +static QScriptValue ScriptValueToQScriptValue(QScriptEngine* engine, const ScriptValue& src) { + return ScriptValueQtWrapper::fullUnwrap(static_cast(engine), src); +} + +static void ScriptValueFromQScriptValue(const QScriptValue& src, ScriptValue& dest) { + ScriptEngineQtScript* engine = static_cast(src.engine()); + dest = ScriptValue(new ScriptValueQtWrapper(engine, src)); +} + +static ScriptValue StringListToScriptValue(ScriptEngine* engine, const void* pSrc) { + const QStringList& src = *reinterpret_cast(pSrc); + int len = src.length(); + ScriptValue dest = engine->newArray(len); + for (int idx = 0; idx < len; ++idx) { + dest.setProperty(idx, engine->newValue(src.at(idx))); + } + return dest; +} + +static bool StringListFromScriptValue(const ScriptValue& src, void* pDest) { + if(!src.isArray()) return false; + QStringList& dest = *reinterpret_cast(pDest); + int len = src.property("length").toInteger(); + dest.clear(); + for (int idx = 0; idx < len; ++idx) { + dest.append(src.property(idx).toString()); + } + return true; +} + +static ScriptValue VariantListToScriptValue(ScriptEngine* engine, const void* pSrc) { + const QVariantList& src = *reinterpret_cast(pSrc); + int len = src.length(); + ScriptValue dest = engine->newArray(len); + for (int idx = 0; idx < len; ++idx) { + dest.setProperty(idx, engine->newVariant(src.at(idx))); + } + return dest; +} + +static bool VariantListFromScriptValue(const ScriptValue& src, void* pDest) { + if(!src.isArray()) return false; + QVariantList& dest = *reinterpret_cast(pDest); + int len = src.property("length").toInteger(); + dest.clear(); + for (int idx = 0; idx < len; ++idx) { + dest.append(src.property(idx).toVariant()); + } + return true; +} + +static ScriptValue VariantMapToScriptValue(ScriptEngine* engine, const void* pSrc) { + const QVariantMap& src = *reinterpret_cast(pSrc); + ScriptValue dest = engine->newObject(); + for (QVariantMap::const_iterator iter = src.cbegin(); iter != src.cend(); ++iter) { + dest.setProperty(iter.key(), engine->newVariant(iter.value())); + } + return dest; +} + +static bool VariantMapFromScriptValue(const ScriptValue& src, void* pDest) { + QVariantMap& dest = *reinterpret_cast(pDest); + dest.clear(); + ScriptValueIteratorPointer iter = src.newIterator(); + while (iter->hasNext()) { + iter->next(); + dest.insert(iter->name(), iter->value().toVariant()); + } + return true; +} + +static ScriptValue VariantHashToScriptValue(ScriptEngine* engine, const void* pSrc) { + const QVariantHash& src = *reinterpret_cast(pSrc); + ScriptValue dest = engine->newObject(); + for (QVariantHash::const_iterator iter = src.cbegin(); iter != src.cend(); ++iter) { + dest.setProperty(iter.key(), engine->newVariant(iter.value())); + } + return dest; +} + +static bool VariantHashFromScriptValue(const ScriptValue& src, void* pDest) { + QVariantHash& dest = *reinterpret_cast(pDest); + dest.clear(); + ScriptValueIteratorPointer iter = src.newIterator(); + while (iter->hasNext()) { + iter->next(); + dest.insert(iter->name(), iter->value().toVariant()); + } + return true; +} + +static ScriptValue JsonValueToScriptValue(ScriptEngine* engine, const void* pSrc) { + const QJsonValue& src = *reinterpret_cast(pSrc); + return engine->newVariant(src.toVariant()); +} + +static bool JsonValueFromScriptValue(const ScriptValue& src, void* pDest) { + QJsonValue& dest = *reinterpret_cast(pDest); + dest = QJsonValue::fromVariant(src.toVariant()); + return true; +} + +static ScriptValue JsonObjectToScriptValue(ScriptEngine* engine, const void* pSrc) { + const QJsonObject& src = *reinterpret_cast(pSrc); + QVariantMap map = src.toVariantMap(); + ScriptValue dest = engine->newObject(); + for (QVariantMap::const_iterator iter = map.cbegin(); iter != map.cend(); ++iter) { + dest.setProperty(iter.key(), engine->newVariant(iter.value())); + } + return dest; +} + +static bool JsonObjectFromScriptValue(const ScriptValue& src, void* pDest) { + QJsonObject& dest = *reinterpret_cast(pDest); + QVariantMap map; + ScriptValueIteratorPointer iter = src.newIterator(); + while (iter->hasNext()) { + iter->next(); + map.insert(iter->name(), iter->value().toVariant()); + } + dest = QJsonObject::fromVariantMap(map); + return true; +} + +static ScriptValue JsonArrayToScriptValue(ScriptEngine* engine, const void* pSrc) { + const QJsonArray& src = *reinterpret_cast(pSrc); + QVariantList list = src.toVariantList(); + int len = list.length(); + ScriptValue dest = engine->newArray(len); + for (int idx = 0; idx < len; ++idx) { + dest.setProperty(idx, engine->newVariant(list.at(idx))); + } + return dest; +} + +static bool JsonArrayFromScriptValue(const ScriptValue& src, void* pDest) { + if(!src.isArray()) return false; + QJsonArray& dest = *reinterpret_cast(pDest); + QVariantList list; + int len = src.property("length").toInteger(); + for (int idx = 0; idx < len; ++idx) { + list.append(src.property(idx).toVariant()); + } + dest = QJsonArray::fromVariantList(list); + return true; +} + +// QMetaType::QJsonArray + +void ScriptEngineQtScript::registerSystemTypes() { + qScriptRegisterMetaType(this, ScriptValueToQScriptValue, ScriptValueFromQScriptValue); + + QMutexLocker guard(&_customTypeProtect); + + { + CustomMarshal& customType = _customTypes.insert(QMetaType::QStringList, CustomMarshal()).value(); + customType.demarshalFunc = StringListFromScriptValue; + customType.marshalFunc = StringListToScriptValue; + } + { + CustomMarshal& customType = _customTypes.insert(QMetaType::QVariantList, CustomMarshal()).value(); + customType.demarshalFunc = VariantListFromScriptValue; + customType.marshalFunc = VariantListToScriptValue; + } + { + CustomMarshal& customType = _customTypes.insert(QMetaType::QVariantMap, CustomMarshal()).value(); + customType.demarshalFunc = VariantMapFromScriptValue; + customType.marshalFunc = VariantMapToScriptValue; + } + { + CustomMarshal& customType = _customTypes.insert(QMetaType::QVariantHash, CustomMarshal()).value(); + customType.demarshalFunc = VariantHashFromScriptValue; + customType.marshalFunc = VariantHashToScriptValue; + } + { + CustomMarshal& customType = _customTypes.insert(QMetaType::QJsonValue, CustomMarshal()).value(); + customType.demarshalFunc = JsonValueFromScriptValue; + customType.marshalFunc = JsonValueToScriptValue; + } + { + CustomMarshal& customType = _customTypes.insert(QMetaType::QJsonObject, CustomMarshal()).value(); + customType.demarshalFunc = JsonObjectFromScriptValue; + customType.marshalFunc = JsonObjectToScriptValue; + } + { + CustomMarshal& customType = _customTypes.insert(QMetaType::QJsonArray, CustomMarshal()).value(); + customType.demarshalFunc = JsonArrayFromScriptValue; + customType.marshalFunc = JsonArrayToScriptValue; + } +} + +bool ScriptEngineQtScript::castValueToVariant(const QScriptValue& val, QVariant& dest, int destType) { + + // if we're not particularly interested in a specific type, try to detect if we're dealing with a registered type + if (destType == QMetaType::UnknownType) { + QObject* obj = ScriptObjectQtProxy::unwrap(val); + if (obj) { + for (const QMetaObject* metaObject = obj->metaObject(); metaObject; metaObject = metaObject->superClass()) { + QByteArray typeName = QByteArray(metaObject->className()) + "*"; + int typeId = QMetaType::type(typeName.constData()); + if (typeId != QMetaType::UnknownType) { + destType = typeId; + break; + } + } + } + } + + if (destType == qMetaTypeId()) { + dest = QVariant::fromValue(ScriptValue(new ScriptValueQtWrapper(this, val))); + return true; + } + + // do we have a registered handler for this type? + ScriptEngine::DemarshalFunction demarshalFunc = nullptr; + { + QMutexLocker guard(&_customTypeProtect); + CustomMarshalMap::const_iterator lookup = _customTypes.find(destType); + if (lookup != _customTypes.cend()) { + demarshalFunc = lookup.value().demarshalFunc; + } + } + if (demarshalFunc) { + void* destStorage = QMetaType::create(destType); + ScriptValue wrappedVal(new ScriptValueQtWrapper(this, val)); + bool success = demarshalFunc(wrappedVal, destStorage); + dest = success ? QVariant(destType, destStorage) : QVariant(); + QMetaType::destroy(destType, destStorage); + return success; + } else { + switch (destType) { + case QMetaType::UnknownType: + if (val.isUndefined()) { + dest = QVariant(); + break; + } + if (val.isNull()) { + dest = QVariant::fromValue(nullptr); + break; + } + if (val.isBool()) { + dest = QVariant::fromValue(val.toBool()); + break; + } + if (val.isString()) { + dest = QVariant::fromValue(val.toString()); + break; + } + if (val.isNumber()) { + dest = QVariant::fromValue(val.toNumber()); + break; + } + { + QObject* obj = ScriptObjectQtProxy::unwrap(val); + if (obj) { + dest = QVariant::fromValue(obj); + break; + } + } + { + QVariant var = ScriptVariantQtProxy::unwrap(val); + if (var.isValid()) { + dest = var; + break; + } + } + dest = val.toVariant(); + break; + case QMetaType::Bool: + dest = QVariant::fromValue(val.toBool()); + break; + case QMetaType::QDateTime: + case QMetaType::QDate: + Q_ASSERT(val.isDate()); + dest = QVariant::fromValue(val.toDateTime()); + break; + case QMetaType::UInt: + case QMetaType::ULong: + dest = QVariant::fromValue(val.toUInt32()); + break; + case QMetaType::Int: + case QMetaType::Long: + case QMetaType::Short: + dest = QVariant::fromValue(val.toInt32()); + break; + case QMetaType::Double: + case QMetaType::Float: + case QMetaType::ULongLong: + case QMetaType::LongLong: + dest = QVariant::fromValue(val.toNumber()); + break; + case QMetaType::QString: + case QMetaType::QByteArray: + dest = QVariant::fromValue(val.toString()); + break; + case QMetaType::UShort: + dest = QVariant::fromValue(val.toUInt16()); + break; + case QMetaType::QObjectStar: + dest = QVariant::fromValue(ScriptObjectQtProxy::unwrap(val)); + break; + default: + // check to see if this is a pointer to a QObject-derived object + if (QMetaType::typeFlags(destType) & QMetaType::PointerToQObject) { + dest = QVariant::fromValue(ScriptObjectQtProxy::unwrap(val)); + break; + } + // check to see if we have a registered prototype + { + QVariant var = ScriptVariantQtProxy::unwrap(val); + if (var.isValid()) { + dest = var; + break; + } + } + // last chance, just convert it to a variant + dest = val.toVariant(); + break; + } + } + + return destType == QMetaType::UnknownType || dest.userType() == destType || dest.convert(destType); +} + +QScriptValue ScriptEngineQtScript::castVariantToValue(const QVariant& val) { + int valTypeId = val.userType(); + + if (valTypeId == qMetaTypeId()) { + // this is a wrapped ScriptValue, so just unwrap it and call it good + ScriptValue innerVal = val.value(); + return ScriptValueQtWrapper::fullUnwrap(this, innerVal); + } + + // do we have a registered handler for this type? + ScriptEngine::MarshalFunction marshalFunc = nullptr; + { + QMutexLocker guard(&_customTypeProtect); + CustomMarshalMap::const_iterator lookup = _customTypes.find(valTypeId); + if (lookup != _customTypes.cend()) { + marshalFunc = lookup.value().marshalFunc; + } + } + if (marshalFunc) { + ScriptValue wrappedVal = marshalFunc(this, val.constData()); + return ScriptValueQtWrapper::fullUnwrap(this, wrappedVal); + } + + switch (valTypeId) { + case QMetaType::UnknownType: + case QMetaType::Void: + return QScriptValue(this, QScriptValue::UndefinedValue); + case QMetaType::Nullptr: + return QScriptValue(this, QScriptValue::NullValue); + case QMetaType::Bool: + return QScriptValue(this, val.toBool()); + case QMetaType::Int: + case QMetaType::Long: + case QMetaType::Short: + return QScriptValue(this, val.toInt()); + case QMetaType::UInt: + case QMetaType::ULong: + case QMetaType::UShort: + return QScriptValue(this, val.toUInt()); + case QMetaType::Float: + case QMetaType::LongLong: + case QMetaType::ULongLong: + case QMetaType::Double: + return QScriptValue(this, val.toFloat()); + case QMetaType::QString: + case QMetaType::QByteArray: + return QScriptValue(this, val.toString()); + case QMetaType::QVariant: + return castVariantToValue(val.value()); + case QMetaType::QObjectStar: + return ScriptObjectQtProxy::newQObject(this, val.value(), ScriptEngine::QtOwnership); + case QMetaType::QDateTime: + return static_cast(this)->newDate(val.value()); + case QMetaType::QDate: + return static_cast(this)->newDate(val.value().startOfDay()); + default: + // check to see if this is a pointer to a QObject-derived object + if (QMetaType::typeFlags(valTypeId) & QMetaType::PointerToQObject) { + return ScriptObjectQtProxy::newQObject(this, val.value(), ScriptEngine::QtOwnership); + } + // have we set a prototype'd variant? + { + QMutexLocker guard(&_customTypeProtect); + CustomPrototypeMap::const_iterator lookup = _customPrototypes.find(valTypeId); + if (lookup != _customPrototypes.cend()) { + return ScriptVariantQtProxy::newVariant(this, val, lookup.value()); + } + } + // just do a generic variant + return QScriptEngine::newVariant(val); + } +} \ No newline at end of file diff --git a/libraries/script-engine/src/qtscript/ScriptObjectQtProxy.cpp b/libraries/script-engine/src/qtscript/ScriptObjectQtProxy.cpp new file mode 100644 index 0000000000..f981534b07 --- /dev/null +++ b/libraries/script-engine/src/qtscript/ScriptObjectQtProxy.cpp @@ -0,0 +1,622 @@ +// +// ScriptObjectQtProxy.cpp +// libraries/script-engine/src/qtscript +// +// Created by Heather Anderson on 12/5/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 +// + +#include "ScriptObjectQtProxy.h" + +#include +#include + +#include + +#include "ScriptContextQtWrapper.h" +#include "ScriptValueQtWrapper.h" + +Q_DECLARE_METATYPE(QScriptContext*) +Q_DECLARE_METATYPE(ScriptValue) +Q_DECLARE_METATYPE(QScriptValue) + +Q_DECLARE_METATYPE(QSharedPointer) +Q_DECLARE_METATYPE(QSharedPointer) + +// Used strictly to replace the "this" object value for property access. May expand to a full context element +// if we find it necessary to, but hopefully not needed +class ScriptPropertyContextQtWrapper final : public ScriptContext { +public: // construction + inline ScriptPropertyContextQtWrapper(const ScriptValue& object, ScriptContext* parentContext) : + _parent(parentContext), _object(object) {} + +public: // ScriptContext implementation + virtual int argumentCount() const override { return _parent->argumentCount(); } + virtual ScriptValue argument(int index) const override { return _parent->argument(index); } + virtual QStringList backtrace() const override { return _parent->backtrace(); } + virtual ScriptValue callee() const override { return _parent->callee(); } + virtual ScriptEnginePointer engine() const override { return _parent->engine(); } + virtual ScriptFunctionContextPointer functionContext() const override { return _parent->functionContext(); } + virtual ScriptContextPointer parentContext() const override { return _parent->parentContext(); } + virtual ScriptValue thisObject() const override { return _object; } + virtual ScriptValue throwError(const QString& text) override { return _parent->throwError(text); } + virtual ScriptValue throwValue(const ScriptValue& value) override { return _parent->throwValue(value); } + +private: // storage + ScriptContext* _parent; + const ScriptValue& _object; +}; + +QScriptValue ScriptObjectQtProxy::newQObject(ScriptEngineQtScript* engine, QObject* object, + ScriptEngine::ValueOwnership ownership, + const ScriptEngine::QObjectWrapOptions& options) { + bool ownsObject; + switch (ownership) { + case ScriptEngine::QtOwnership: + ownsObject = false; + break; + case ScriptEngine::ScriptOwnership: + ownsObject = true; + break; + case ScriptEngine::AutoOwnership: + ownsObject = !object->parent(); + break; + } + QScriptEngine* qengine = static_cast(engine); + auto proxy = QSharedPointer::create(engine, object, ownsObject, options); + return static_cast(engine)->newObject(proxy.get(), qengine->newVariant(QVariant::fromValue(proxy))); +} + +ScriptObjectQtProxy* ScriptObjectQtProxy::unwrapProxy(const QScriptValue& val) { + QScriptClass* scriptClass = val.scriptClass(); + return scriptClass ? dynamic_cast(scriptClass) : nullptr; +} + +QObject* ScriptObjectQtProxy::unwrap(const QScriptValue& val) { + if (val.isQObject()) { + return val.toQObject(); + } + ScriptObjectQtProxy* proxy = unwrapProxy(val); + return proxy ? proxy->toQtValue() : nullptr; +} + +ScriptObjectQtProxy::~ScriptObjectQtProxy() { + if (_ownsObject) { + QObject* qobject = _object; + if(qobject) qobject->deleteLater(); + } +} + +void ScriptObjectQtProxy::investigate() { + QObject* qobject = _object; + Q_ASSERT(qobject); + if (!qobject) return; + + const QMetaObject* metaObject = qobject->metaObject(); + _name = QString::fromLatin1(metaObject->className()); + + // discover properties + int startIdx = _wrapOptions & ScriptEngine::ExcludeSuperClassProperties ? metaObject->propertyOffset() : 0; + int num = metaObject->propertyCount(); + for (int idx = startIdx; idx < num; ++idx) { + QMetaProperty prop = metaObject->property(idx); + if (!prop.isScriptable()) continue; + + // always exclude child objects (at least until we decide otherwise) + int metaTypeId = prop.userType(); + if (metaTypeId != QMetaType::UnknownType) { + QMetaType metaType(metaTypeId); + if (metaType.flags() & QMetaType::PointerToQObject) { + continue; + } + } + + PropertyDef& propDef = _props.insert(idx, PropertyDef()).value(); + propDef.name = _engine->toStringHandle(QString::fromLatin1(prop.name())); + propDef.flags = QScriptValue::Undeletable | QScriptValue::PropertyGetter | QScriptValue::PropertySetter | + QScriptValue::QObjectMember; + if (prop.isConstant()) propDef.flags |= QScriptValue::ReadOnly; + } + + // discover methods + startIdx = _wrapOptions & ScriptEngine::ExcludeSuperClassMethods ? metaObject->methodCount() : 0; + num = metaObject->methodCount(); + QHash methodNames; + for (int idx = startIdx; idx < num; ++idx) { + QMetaMethod method = metaObject->method(idx); + + // perhaps keep this comment? Calls (like AudioScriptingInterface::playSound) seem to expect non-public methods to be script-accessible + /* if (method.access() != QMetaMethod::Public) continue;*/ + + bool isSignal = false; + QByteArray szName = method.name(); + + switch (method.methodType()) { + case QMetaMethod::Constructor: + continue; + case QMetaMethod::Signal: + isSignal = true; + break; + case QMetaMethod::Slot: + if (_wrapOptions & ScriptEngine::ExcludeSlots) { + continue; + } + if (szName == "deleteLater") { + continue; + } + break; + } + + QScriptString name = _engine->toStringHandle(QString::fromLatin1(szName)); + auto nameLookup = methodNames.find(name); + if (isSignal) { + if (nameLookup == methodNames.end()) { + SignalDef& signalDef = _signals.insert(idx, SignalDef()).value(); + signalDef.name = name; + signalDef.signal = method; + methodNames.insert(name, idx); + } else { + int originalMethodId = nameLookup.value(); + SignalDefMap::iterator signalLookup = _signals.find(originalMethodId); + Q_ASSERT(signalLookup != _signals.end()); + SignalDef& signalDef = signalLookup.value(); + Q_ASSERT(signalDef.signal.parameterCount() != method.parameterCount()); + if (signalDef.signal.parameterCount() < method.parameterCount()) { + signalDef.signal = method; + } + } + } else { + if (nameLookup == methodNames.end()) { + MethodDef& methodDef = _methods.insert(idx, MethodDef()).value(); + methodDef.name = name; + methodDef.methods.append(method); + methodNames.insert(name, idx); + } else { + int originalMethodId = nameLookup.value(); + MethodDefMap::iterator methodLookup = _methods.find(originalMethodId); + Q_ASSERT(methodLookup != _methods.end()); + MethodDef& methodDef = methodLookup.value(); + methodDef.methods.append(method); + } + } + } +} + +QScriptClass::QueryFlags ScriptObjectQtProxy::queryProperty(const QScriptValue& object, const QScriptString& name, QueryFlags flags, uint* id) { + // check for properties + for (PropertyDefMap::const_iterator trans = _props.cbegin(); trans != _props.cend(); ++trans) { + const PropertyDef& propDef = trans.value(); + if (propDef.name != name) continue; + *id = trans.key() | PROPERTY_TYPE; + return flags & (HandlesReadAccess | HandlesWriteAccess); + } + + // check for methods + for (MethodDefMap::const_iterator trans = _methods.cbegin(); trans != _methods.cend(); ++trans) { + if (trans.value().name != name) continue; + *id = trans.key() | METHOD_TYPE; + return flags & (HandlesReadAccess | HandlesWriteAccess); + } + + // check for signals + for (SignalDefMap::const_iterator trans = _signals.cbegin(); trans != _signals.cend(); ++trans) { + if (trans.value().name != name) continue; + *id = trans.key() | SIGNAL_TYPE; + return flags & (HandlesReadAccess | HandlesWriteAccess); + } + + return QueryFlags(); +} + +QScriptValue::PropertyFlags ScriptObjectQtProxy::propertyFlags(const QScriptValue& object, const QScriptString& name, uint id) { + QObject* qobject = _object; + if (!qobject) { + return QScriptValue::PropertyFlags(); + } + + switch (id & TYPE_MASK) { + case PROPERTY_TYPE: { + PropertyDefMap::const_iterator lookup = _props.find(id & ~TYPE_MASK); + if (lookup == _props.cend()) return QScriptValue::PropertyFlags(); + const PropertyDef& propDef = lookup.value(); + return propDef.flags; + } + case METHOD_TYPE: { + MethodDefMap::const_iterator lookup = _methods.find(id & ~TYPE_MASK); + if (lookup == _methods.cend()) return QScriptValue::PropertyFlags(); + return QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::QObjectMember; + } + case SIGNAL_TYPE: { + SignalDefMap::const_iterator lookup = _signals.find(id & ~TYPE_MASK); + if (lookup == _signals.cend()) return QScriptValue::PropertyFlags(); + return QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::QObjectMember; + } + } + return QScriptValue::PropertyFlags(); +} + +QScriptValue ScriptObjectQtProxy::property(const QScriptValue& object, const QScriptString& name, uint id) { + QObject* qobject = _object; + if (!qobject) { + QScriptContext* currentContext = static_cast(_engine)->currentContext(); + currentContext->throwError(QScriptContext::ReferenceError, "Referencing deleted native object"); + return QScriptValue(); + } + + const QMetaObject* metaObject = qobject->metaObject(); + + switch (id & TYPE_MASK) { + case PROPERTY_TYPE: { + int propId = id & ~TYPE_MASK; + PropertyDefMap::const_iterator lookup = _props.find(propId); + if (lookup == _props.cend()) return QScriptValue(); + const PropertyDef& propDef = lookup.value(); + + QMetaProperty prop = metaObject->property(propId); + ScriptValue scriptThis = ScriptValue(new ScriptValueQtWrapper(_engine, object)); + ScriptPropertyContextQtWrapper ourContext(scriptThis, _engine->currentContext()); + ScriptContextGuard guard(&ourContext); + + QVariant varValue = prop.read(qobject); + return _engine->castVariantToValue(varValue); + } + case METHOD_TYPE: { + int methodId = id & ~TYPE_MASK; + MethodDefMap::const_iterator lookup = _methods.find(methodId); + if (lookup == _methods.cend()) return QScriptValue(); + return static_cast(_engine)->newObject( + new ScriptMethodQtProxy(_engine, qobject, object, name, lookup.value().methods)); + } + case SIGNAL_TYPE: { + int signalId = id & ~TYPE_MASK; + SignalDefMap::const_iterator defLookup = _signals.find(signalId); + if (defLookup == _signals.cend()) return QScriptValue(); + + InstanceMap::const_iterator instLookup = _signalInstances.find(signalId); + if (instLookup == _signalInstances.cend() || instLookup.value().isNull()) { + instLookup = _signalInstances.insert(signalId, + new ScriptSignalQtProxy(_engine, qobject, object, name, defLookup.value().signal)); + Q_ASSERT(instLookup != _signalInstances.cend()); + } + ScriptSignalQtProxy* proxy = instLookup.value(); + + QScriptEngine::QObjectWrapOptions options = QScriptEngine::ExcludeSuperClassContents | + QScriptEngine::ExcludeDeleteLater | + QScriptEngine::PreferExistingWrapperObject; + return static_cast(_engine)->newQObject(proxy, QScriptEngine::ScriptOwnership, options); + } + } + return QScriptValue(); +} + +void ScriptObjectQtProxy::setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) { + if (!(id & PROPERTY_TYPE)) return; + QObject* qobject = _object; + if (!qobject) { + QScriptContext* currentContext = static_cast(_engine)->currentContext(); + currentContext->throwError(QScriptContext::ReferenceError, "Referencing deleted native object"); + return; + } + + int propId = id & ~TYPE_MASK; + PropertyDefMap::const_iterator lookup = _props.find(propId); + if (lookup == _props.cend()) return; + const PropertyDef& propDef = lookup.value(); + if (propDef.flags & QScriptValue::ReadOnly) return; + + const QMetaObject* metaObject = qobject->metaObject(); + QMetaProperty prop = metaObject->property(propId); + + ScriptValue scriptThis = ScriptValue(new ScriptValueQtWrapper(_engine, object)); + ScriptPropertyContextQtWrapper ourContext(scriptThis, _engine->currentContext()); + ScriptContextGuard guard(&ourContext); + + int propTypeId = prop.userType(); + QVariant varValue; + if(!_engine->castValueToVariant(value, varValue, propTypeId)) { + QByteArray propTypeName = QMetaType(propTypeId).name(); + QByteArray valTypeName = value.toVariant().typeName(); + QScriptContext* currentContext = static_cast(_engine)->currentContext(); + currentContext->throwError(QScriptContext::TypeError, QString("Cannot convert %1 to %2").arg(valTypeName, propTypeName)); + return; + } + prop.write(qobject, varValue); +} + +ScriptVariantQtProxy::ScriptVariantQtProxy(ScriptEngineQtScript* engine, const QVariant& variant, QScriptValue scriptProto, ScriptObjectQtProxy* proto) : + QScriptClass(engine), _engine(engine), _variant(variant), _scriptProto(scriptProto), _proto(proto) { + _name = QString::fromLatin1(variant.typeName()); +} + +QScriptValue ScriptVariantQtProxy::newVariant(ScriptEngineQtScript* engine, const QVariant& variant, QScriptValue proto) { + QScriptEngine* qengine = static_cast(engine); + ScriptObjectQtProxy* protoProxy = ScriptObjectQtProxy::unwrapProxy(proto); + if (!protoProxy) { + Q_ASSERT(protoProxy); + return qengine->newVariant(variant); + } + auto proxy = QSharedPointer::create(engine, variant, proto, protoProxy); + return static_cast(engine)->newObject(proxy.get(), qengine->newVariant(QVariant::fromValue(proxy))); +} + +ScriptVariantQtProxy* ScriptVariantQtProxy::unwrapProxy(const QScriptValue& val) { + QScriptClass* scriptClass = val.scriptClass(); + return scriptClass ? dynamic_cast(scriptClass) : nullptr; +} + +QVariant ScriptVariantQtProxy::unwrap(const QScriptValue& val) { + ScriptVariantQtProxy* proxy = unwrapProxy(val); + return proxy ? proxy->toQtValue() : QVariant(); +} + +bool ScriptMethodQtProxy::supportsExtension(Extension extension) const { + switch (extension) { + case Callable: + return true; + default: + return false; + } +} + +QVariant ScriptMethodQtProxy::extension(Extension extension, const QVariant& argument) { + if (extension != Callable) return QVariant(); + QScriptContext* context = qvariant_cast(argument); + + QObject* qobject = _object; + if (!qobject) { + context->throwError(QScriptContext::ReferenceError, "Referencing deleted native object"); + return QVariant(); + } + + int scriptNumArgs = context->argumentCount(); + Q_ASSERT(scriptNumArgs < 10); + int numArgs = std::min(scriptNumArgs, 10); + + const int scriptValueTypeId = qMetaTypeId(); + + for (auto iter = _metas.cbegin(); iter != _metas.end(); ++iter) { + const QMetaMethod& meta = *iter; + int methodNumArgs = meta.parameterCount(); + if (methodNumArgs != numArgs) { + continue; + } + + QList qScriptArgList; + QList qVarArgList; + QGenericArgument qGenArgs[10]; + int arg; + bool conversionFailed = false; + for (arg = 0; arg < numArgs && !conversionFailed; ++arg) { + int methodArgTypeId = meta.parameterType(arg); + QScriptValue argVal = context->argument(arg); + if (methodArgTypeId == scriptValueTypeId) { + qScriptArgList.append(ScriptValue(new ScriptValueQtWrapper(_engine, argVal))); + qGenArgs[arg] = Q_ARG(ScriptValue, qScriptArgList.back()); + } else if (methodArgTypeId == QMetaType::QVariant) { + qVarArgList.append(argVal.toVariant()); + qGenArgs[arg] = Q_ARG(QVariant, qVarArgList.back()); + } else { + QVariant varArgVal; + if (!_engine->castValueToVariant(argVal, varArgVal, methodArgTypeId)) { + conversionFailed = true; + break; + /*QByteArray methodTypeName = QMetaType(methodArgTypeId).name(); + QByteArray argTypeName = argVal.toVariant().typeName(); + context->throwError(QScriptContext::TypeError, + QString("Cannot convert %1 to %2").arg(argTypeName, methodTypeName)); + return QVariant();*/ + } + qVarArgList.append(varArgVal); + const QVariant& converted = qVarArgList.back(); + + // a lot of type conversion assistance thanks to https://stackoverflow.com/questions/28457819/qt-invoke-method-with-qvariant + // A const_cast is needed because calling data() would detach the QVariant. + qGenArgs[arg] = + QGenericArgument(QMetaType::typeName(converted.userType()), const_cast(converted.constData())); + } + } + if (conversionFailed) { + continue; + } + + ScriptContextQtWrapper ourContext(_engine, context); + ScriptContextGuard guard(&ourContext); + + int returnTypeId = meta.returnType(); + if (returnTypeId == QMetaType::Void) { + bool success = meta.invoke(qobject, Qt::DirectConnection, qGenArgs[0], qGenArgs[1], qGenArgs[2], qGenArgs[3], + qGenArgs[4], qGenArgs[5], qGenArgs[6], qGenArgs[7], qGenArgs[8], qGenArgs[9]); + if (!success) { + context->throwError("Native call failed"); + } + return QVariant(); + } else if (returnTypeId == scriptValueTypeId) { + ScriptValue result; + bool success = meta.invoke(qobject, Qt::DirectConnection, Q_RETURN_ARG(ScriptValue, result), qGenArgs[0], + qGenArgs[1], qGenArgs[2], qGenArgs[3], qGenArgs[4], qGenArgs[5], qGenArgs[6], + qGenArgs[7], qGenArgs[8], qGenArgs[9]); + if (!success) { + context->throwError("Native call failed"); + return QVariant(); + } + QScriptValue qResult = ScriptValueQtWrapper::fullUnwrap(_engine, result); + return QVariant::fromValue(qResult); + } else { + // a lot of type conversion assistance thanks to https://stackoverflow.com/questions/28457819/qt-invoke-method-with-qvariant + const char* typeName = meta.typeName(); + QVariant qRetVal(returnTypeId, static_cast(NULL)); + QGenericReturnArgument sRetVal(typeName, const_cast(qRetVal.constData())); + + bool success = + meta.invoke(qobject, Qt::DirectConnection, sRetVal, qGenArgs[0], qGenArgs[1], qGenArgs[2], qGenArgs[3], + qGenArgs[4], qGenArgs[5], qGenArgs[6], qGenArgs[7], qGenArgs[8], qGenArgs[9]); + if (!success) { + context->throwError("Native call failed"); + return QVariant(); + } + QScriptValue qResult = _engine->castVariantToValue(qRetVal); + return QVariant::fromValue(qResult); + } + } + context->throwError("Native call failed: could not locate an overload with the requested arguments"); + return QVariant(); +} + +// Adapted from https://doc.qt.io/archives/qq/qq16-dynamicqobject.html, for connecting to a signal without a compile-time definition for it +int ScriptSignalQtProxy::qt_metacall(QMetaObject::Call call, int id, void** arguments) { + id = ScriptSignalQtProxyBase::qt_metacall(call, id, arguments); + if (id != 0 || call != QMetaObject::InvokeMetaMethod) { + return id; + } + + QScriptValueList args; + int numArgs = _meta.parameterCount(); + for (int arg = 0; arg < numArgs; ++arg) { + int methodArgTypeId = _meta.parameterType(arg); + QVariant argValue(methodArgTypeId, arguments[arg+1]); + args.append(_engine->castVariantToValue(argValue)); + } + + for (ConnectionList::iterator iter = _connections.begin(); iter != _connections.end(); ++iter) { + Connection& conn = *iter; + conn.callback.call(conn.thisValue, args); + } + + return -1; +} + +int ScriptSignalQtProxy::discoverMetaCallIdx() { + const QMetaObject* ourMeta = metaObject(); + return ourMeta->methodCount(); +} + +ScriptSignalQtProxy::ConnectionList::iterator ScriptSignalQtProxy::findConnection(QScriptValue thisObject, + QScriptValue callback) { + for (ConnectionList::iterator iter = _connections.begin(); iter != _connections.end(); ++iter) { + Connection& conn = *iter; + if (conn.callback.strictlyEquals(callback) && conn.thisValue.strictlyEquals(thisObject)) { + return iter; + } + } + return _connections.end(); +} + + +void ScriptSignalQtProxy::connect(QScriptValue arg0, QScriptValue arg1) { + QObject* qobject = _object; + if (!qobject) { + QScriptContext* currentContext = static_cast(_engine)->currentContext(); + currentContext->throwError(QScriptContext::ReferenceError, "Referencing deleted native object"); + return; + } + + // untangle the arguments + QScriptValue callback; + QScriptValue callbackThis; + if (arg1.isFunction()) { + callbackThis = arg0; + callback = arg1; + } else { + callback = arg0; + } + if (!callback.isFunction()) { + QScriptContext* currentContext = static_cast(_engine)->currentContext(); + currentContext->throwError(QScriptContext::TypeError, "Function expected as argument to 'connect'"); + return; + } + + // are we already connected? + ConnectionList::iterator lookup = findConnection(callbackThis, callback); + if (lookup != _connections.end()) { + return; // already exists + } + + // add a reference to ourselves to the destination callback + QScriptValue destData = callback.data(); + Q_ASSERT(!destData.isValid() || destData.isArray()); + if (!destData.isArray()) { + destData = static_cast(_engine)->newArray(); + } + { + QScriptValueList args; + args << thisObject(); + destData.property("push").call(destData, args); + } + callback.setData(destData); + + // add this to our internal list of connections + Connection newConn; + newConn.callback = callback; + newConn.thisValue = callbackThis; + _connections.append(newConn); + + // inform Qt that we're connecting to this signal + if (!_isConnected) { + auto result = QMetaObject::connect(qobject, _meta.methodIndex(), this, _metaCallId); + Q_ASSERT(result); + _isConnected = true; + } +} + +void ScriptSignalQtProxy::disconnect(QScriptValue arg0, QScriptValue arg1) { + QObject* qobject = _object; + if (!qobject) { + QScriptContext* currentContext = static_cast(_engine)->currentContext(); + currentContext->throwError(QScriptContext::ReferenceError, "Referencing deleted native object"); + return; + } + + // untangle the arguments + QScriptValue callback; + QScriptValue callbackThis; + if (arg1.isFunction()) { + callbackThis = arg0; + callback = arg1; + } else { + callback = arg0; + } + if (!callback.isFunction()) { + QScriptContext* currentContext = static_cast(_engine)->currentContext(); + currentContext->throwError(QScriptContext::TypeError, "Function expected as argument to 'disconnect'"); + return; + } + + // locate this connection in our list of connections + ConnectionList::iterator lookup = findConnection(callbackThis, callback); + if (lookup == _connections.end()) { + return; // not here + } + + // remove it from our internal list of connections + _connections.erase(lookup); + + // remove a reference to ourselves from the destination callback + QScriptValue destData = callback.data(); + Q_ASSERT(destData.isArray()); + if (destData.isArray()) { + QScriptValue qThis = thisObject(); + int len = destData.property("length").toInteger(); + bool foundIt = false; + for (int idx = 0; idx < len && !foundIt; ++idx) { + QScriptValue entry = destData.property(idx); + if (entry.strictlyEquals(qThis)) { + foundIt = true; + QScriptValueList args; + args << idx << 1; + destData.property("splice").call(destData, args); + } + } + Q_ASSERT(foundIt); + } + + // inform Qt that we're no longer connected to this signal + if (_connections.empty()) { + Q_ASSERT(_isConnected); + bool result = QMetaObject::disconnect(qobject, _meta.methodIndex(), this, _metaCallId); + Q_ASSERT(result); + _isConnected = false; + } +} diff --git a/libraries/script-engine/src/qtscript/ScriptObjectQtProxy.h b/libraries/script-engine/src/qtscript/ScriptObjectQtProxy.h new file mode 100644 index 0000000000..efd9070bb3 --- /dev/null +++ b/libraries/script-engine/src/qtscript/ScriptObjectQtProxy.h @@ -0,0 +1,205 @@ +// +// ScriptObjectQtProxy.h +// libraries/script-engine/src/qtscript +// +// Created by Heather Anderson on 12/5/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 +// + +/// @addtogroup ScriptEngine +/// @{ + +#ifndef hifi_ScriptObjectQtProxy_h +#define hifi_ScriptObjectQtProxy_h + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../ScriptEngine.h" +#include "ScriptEngineQtScript.h" + +class ScriptEngineQtScript; +class ScriptSignalQtProxy; + +/// [QtScript] (re-)implements the translation layer between ScriptValue and QObject. This object +/// will focus exclusively on property get/set until function calls appear to be a problem +class ScriptObjectQtProxy final : public QScriptClass { +private: // implementation + struct PropertyDef { + QScriptString name; + QScriptValue::PropertyFlags flags; + }; + struct MethodDef { + QScriptString name; + QList methods; + }; + struct SignalDef { + QScriptString name; + QMetaMethod signal; + }; + using PropertyDefMap = QHash; + using MethodDefMap = QHash; + using SignalDefMap = QHash; + using InstanceMap = QHash >; + + static constexpr uint PROPERTY_TYPE = 0x1000; + static constexpr uint METHOD_TYPE = 0x2000; + static constexpr uint SIGNAL_TYPE = 0x3000; + static constexpr uint TYPE_MASK = 0xF000; + +public: // construction + inline ScriptObjectQtProxy(ScriptEngineQtScript* engine, QObject* object, bool ownsObject, const ScriptEngine::QObjectWrapOptions& options) : + QScriptClass(engine), _engine(engine), _object(object), _wrapOptions(options), _ownsObject(ownsObject) { + investigate(); + } + virtual ~ScriptObjectQtProxy(); + + static QScriptValue newQObject(ScriptEngineQtScript* engine, + QObject* object, + ScriptEngine::ValueOwnership ownership = ScriptEngine::QtOwnership, + const ScriptEngine::QObjectWrapOptions& options = ScriptEngine::QObjectWrapOptions()); + static ScriptObjectQtProxy* unwrapProxy(const QScriptValue& val); + static QObject* unwrap(const QScriptValue& val); + inline QObject* toQtValue() const { return _object; } + +public: // QScriptClass implementation + virtual QString name() const override { return _name; } + + virtual QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; + virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, const QScriptString& name, uint id) override; + virtual QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, QueryFlags flags, uint* id) override; + virtual void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; + +private: // implementation + void investigate(); + +private: // storage + ScriptEngineQtScript* _engine; + const ScriptEngine::QObjectWrapOptions _wrapOptions; + PropertyDefMap _props; + MethodDefMap _methods; + SignalDefMap _signals; + InstanceMap _signalInstances; + const bool _ownsObject; + QPointer _object; + QString _name; + + Q_DISABLE_COPY(ScriptObjectQtProxy) +}; + +/// [QtScript] (re-)implements the translation layer between ScriptValue and QVariant where a prototype is set. +/// This object depends on a ScriptObjectQtProxy to provide the prototype's behavior +class ScriptVariantQtProxy final : public QScriptClass { +public: // construction + ScriptVariantQtProxy(ScriptEngineQtScript* engine, const QVariant& variant, QScriptValue scriptProto, ScriptObjectQtProxy* proto); + + static QScriptValue newVariant(ScriptEngineQtScript* engine, const QVariant& variant, QScriptValue proto); + static ScriptVariantQtProxy* unwrapProxy(const QScriptValue& val); + static QVariant unwrap(const QScriptValue& val); + inline QVariant toQtValue() const { return _variant; } + +public: // QScriptClass implementation + virtual QString name() const override { return _name; } + + virtual QScriptValue prototype() const override { return _scriptProto; } + + virtual QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override { + return _proto->property(object, name, id); + } + virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, const QScriptString& name, uint id) override { + return _proto->propertyFlags(object, name, id); + } + virtual QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, QueryFlags flags, uint* id) override { + return _proto->queryProperty(object, name, flags, id); + } + virtual void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override { + return _proto->setProperty(object, name, id, value); + } + +private: // storage + ScriptEngineQtScript* _engine; + QVariant _variant; + QScriptValue _scriptProto; + ScriptObjectQtProxy* _proto; + QString _name; + + Q_DISABLE_COPY(ScriptVariantQtProxy) +}; + +class ScriptMethodQtProxy final : public QScriptClass { +public: // construction + inline ScriptMethodQtProxy(ScriptEngineQtScript* engine, QObject* object, QScriptValue lifetime, QString name, const QList& metas) : + QScriptClass(engine), _engine(engine), _object(object), _objectLifetime(lifetime), _name(name), _metas(metas) {} + +public: // QScriptClass implementation + virtual QString name() const override { return _name; } + virtual bool supportsExtension(Extension extension) const override; + virtual QVariant extension(Extension extension, const QVariant& argument = QVariant()) override; + +private: // storage + ScriptEngineQtScript* _engine; + QPointer _object; + QScriptValue _objectLifetime; + const QString _name; + const QList _metas; + + Q_DISABLE_COPY(ScriptMethodQtProxy) +}; + +// This abstract base class serves solely to declare the Q_INVOKABLE methods for ScriptSignalQtProxy +// as we're overriding qt_metacall later for the signal callback yet still want to support +// metacalls for the connect/disconnect API +class ScriptSignalQtProxyBase : public QObject, protected QScriptable { + Q_OBJECT +public: // API + Q_INVOKABLE virtual void connect(QScriptValue arg0, QScriptValue arg1 = QScriptValue()) = 0; + Q_INVOKABLE virtual void disconnect(QScriptValue arg0, QScriptValue arg1 = QScriptValue()) = 0; +}; + +class ScriptSignalQtProxy final : public ScriptSignalQtProxyBase { +private: // storage + struct Connection { + QScriptValue thisValue; + QScriptValue callback; + }; + using ConnectionList = QList; + +public: // construction + inline ScriptSignalQtProxy(ScriptEngineQtScript* engine, QObject* object, QScriptValue lifetime, QString name, const QMetaMethod& meta) : + _engine(engine), _object(object), _objectLifetime(lifetime), _name(name), _meta(meta), _metaCallId(discoverMetaCallIdx()) {} + +private: // implementation + virtual int qt_metacall(QMetaObject::Call call, int id, void** arguments); + int discoverMetaCallIdx(); + ConnectionList::iterator findConnection(QScriptValue thisObject, QScriptValue callback); + +public: // API + virtual void connect(QScriptValue arg0, QScriptValue arg1 = QScriptValue()) override; + virtual void disconnect(QScriptValue arg0, QScriptValue arg1 = QScriptValue()) override; + +private: // storage + ScriptEngineQtScript* _engine; + QPointer _object; + QScriptValue _objectLifetime; + QString _name; + const QMetaMethod _meta; + const int _metaCallId; + ConnectionList _connections; + bool _isConnected{ false }; + + Q_DISABLE_COPY(ScriptSignalQtProxy) +}; + +#endif // hifi_ScriptObjectQtProxy_h + +/// @} diff --git a/libraries/script-engine/src/qtscript/ScriptValueQtWrapper.cpp b/libraries/script-engine/src/qtscript/ScriptValueQtWrapper.cpp index 4eb32476ea..cae3de30f8 100644 --- a/libraries/script-engine/src/qtscript/ScriptValueQtWrapper.cpp +++ b/libraries/script-engine/src/qtscript/ScriptValueQtWrapper.cpp @@ -28,19 +28,27 @@ ScriptValueQtWrapper* ScriptValueQtWrapper::unwrap(const ScriptValue& val) { QScriptValue ScriptValueQtWrapper::fullUnwrap(const ScriptValue& value) const { ScriptValueQtWrapper* unwrapped = unwrap(value); if (unwrapped) { - return unwrapped->toQtValue(); + if (unwrapped->engine().get() != _engine) { + return static_cast(_engine)->toScriptValue(unwrapped->toVariant()); + } else { + return unwrapped->toQtValue(); + } } QVariant varValue = value.toVariant(); - return static_cast(_engine)->newVariant(varValue); + return _engine->castVariantToValue(varValue); } QScriptValue ScriptValueQtWrapper::fullUnwrap(ScriptEngineQtScript* engine, const ScriptValue& value) { ScriptValueQtWrapper* unwrapped = unwrap(value); if (unwrapped) { - return unwrapped->toQtValue(); + if (unwrapped->engine().get() != engine) { + return static_cast(engine)->toScriptValue(unwrapped->toVariant()); + } else { + return unwrapped->toQtValue(); + } } QVariant varValue = value.toVariant(); - return static_cast(engine)->newVariant(varValue); + return engine->castVariantToValue(varValue); } ScriptValue ScriptValueQtWrapper::call(const ScriptValue& thisObject, const ScriptValueList& args) { @@ -157,11 +165,23 @@ quint32 ScriptValueQtWrapper::toUInt32() const { } QVariant ScriptValueQtWrapper::toVariant() const { - return _value.toVariant(); + QVariant dest; + if (_engine->castValueToVariant(_value, dest, QMetaType::UnknownType)) { + return dest; + } else { + Q_ASSERT(false); + return QVariant(); + } } QObject* ScriptValueQtWrapper::toQObject() const { - return _value.toQObject(); + QVariant dest; + if (_engine->castValueToVariant(_value, dest, QMetaType::QObjectStar)) { + return dest.value(); + } else { + Q_ASSERT(false); + return nullptr; + } } bool ScriptValueQtWrapper::equals(const ScriptValue& other) const { diff --git a/libraries/ui/src/ui/QmlWrapper.h b/libraries/ui/src/ui/QmlWrapper.h index 8e110ffee3..42091d5549 100644 --- a/libraries/ui/src/ui/QmlWrapper.h +++ b/libraries/ui/src/ui/QmlWrapper.h @@ -40,8 +40,9 @@ ScriptValue wrapperToScriptValue(ScriptEngine* engine, T* const &in) { } template -void wrapperFromScriptValue(const ScriptValue& value, T* &out) { +bool wrapperFromScriptValue(const ScriptValue& value, T* &out) { out = qobject_cast(value.toQObject()); + return !!out; } #endif \ No newline at end of file