diff --git a/cpp/WKTJsRuntimeFactory.h b/cpp/WKTJsRuntimeFactory.h index 4f8cda8..0ae3fc8 100644 --- a/cpp/WKTJsRuntimeFactory.h +++ b/cpp/WKTJsRuntimeFactory.h @@ -11,10 +11,10 @@ // Hermes #include #elif __has_include() - // JSC - #include +// JSC +#include #else - #include +#include #endif namespace RNWorklet { diff --git a/cpp/WKTJsiWorklet.h b/cpp/WKTJsiWorklet.h index 48524b4..1c8b51c 100644 --- a/cpp/WKTJsiWorklet.h +++ b/cpp/WKTJsiWorklet.h @@ -237,9 +237,7 @@ class JsiWorklet : public JsiHostObject, /** Returns true if the character is a whitespace character */ - static bool isWhitespace(unsigned char c) { - return std::isspace(c); - } + static bool isWhitespace(unsigned char c) { return std::isspace(c); } private: /** @@ -309,14 +307,19 @@ class JsiWorklet : public JsiHostObject, .asString(runtime) .utf8(runtime); } - + // Double-check if the code property is valid. bool isCodeEmpty = std::all_of(_code.begin(), _code.end(), isWhitespace); if (isCodeEmpty) { - std::string error = "Failed to create Worklet, the provided code is empty. Tips:\n" - "* Is the babel plugin correctly installed?\n" - "* If you are using react-native-reanimated, make sure the react-native-reanimated plugin does not override the react-native-worklets-core/plugin.\n" - "* Make sure the JS Worklet contains a \"" + std::string(PropNameWorkletInitDataCode) + "\" property with the function's code."; + std::string error = + "Failed to create Worklet, the provided code is empty. Tips:\n" + "* Is the babel plugin correctly installed?\n" + "* If you are using react-native-reanimated, make sure the " + "react-native-reanimated plugin does not override the " + "react-native-worklets-core/plugin.\n" + "* Make sure the JS Worklet contains a \"" + + std::string(PropNameWorkletInitDataCode) + + "\" property with the function's code."; throw jsi::JSError(runtime, error); } @@ -360,10 +363,11 @@ class WorkletInvoker { jsi::Value call(jsi::Runtime &runtime, const jsi::Value &thisValue, const jsi::Value *arguments, size_t count) { if (_workletFunction.get(runtime) == nullptr) { - _workletFunction.get(runtime) = _worklet->createWorkletJsFunction(runtime); + _workletFunction.get(runtime) = + _worklet->createWorkletJsFunction(runtime); } - return _worklet->call(_workletFunction.get(runtime), runtime, thisValue, arguments, - count); + return _worklet->call(_workletFunction.get(runtime), runtime, thisValue, + arguments, count); } private: diff --git a/cpp/WKTJsiWorkletApi.h b/cpp/WKTJsiWorkletApi.h index 874e116..659d650 100644 --- a/cpp/WKTJsiWorkletApi.h +++ b/cpp/WKTJsiWorkletApi.h @@ -120,10 +120,39 @@ class JsiWorkletApi : public JsiHostObject { }); } + JSI_HOST_FUNCTION(__jsi_is_array) { + if (count == 0) { + throw jsi::JSError(runtime, "__getTypeIsArray expects one parameter."); + } + + if (arguments[0].isObject()) { + auto obj = arguments[0].asObject(runtime); + arguments[0].asObject(runtime).isArray(runtime); + auto isArray = obj.isArray(runtime); + return isArray; + } + + return false; + } + + JSI_HOST_FUNCTION(__jsi_is_object) { + if (count == 0) { + throw jsi::JSError(runtime, "__getTypeIsObject expects one parameter."); + } + + if (arguments[0].isObject()) { + return true; + } + + return false; + } + JSI_EXPORT_FUNCTIONS(JSI_EXPORT_FUNC(JsiWorkletApi, createSharedValue), JSI_EXPORT_FUNC(JsiWorkletApi, createContext), JSI_EXPORT_FUNC(JsiWorkletApi, createRunInContextFn), - JSI_EXPORT_FUNC(JsiWorkletApi, createRunInJsFn)) + JSI_EXPORT_FUNC(JsiWorkletApi, createRunInJsFn), + JSI_EXPORT_FUNC(JsiWorkletApi, __jsi_is_array), + JSI_EXPORT_FUNC(JsiWorkletApi, __jsi_is_object)) JSI_PROPERTY_GET(defaultContext) { return jsi::Object::createFromHostObject( diff --git a/cpp/WKTJsiWorkletContext.cpp b/cpp/WKTJsiWorkletContext.cpp index 6fe4437..9ac1209 100644 --- a/cpp/WKTJsiWorkletContext.cpp +++ b/cpp/WKTJsiWorkletContext.cpp @@ -74,7 +74,7 @@ void JsiWorkletContext::initialize( // Register main runtime BaseRuntimeAwareCache::setMainJsRuntime(jsRuntime); - + _name = name; _jsRuntime = jsRuntime; _jsCallInvoker = jsCallInvoker; diff --git a/cpp/WKTJsiWorkletContext.h b/cpp/WKTJsiWorkletContext.h index 1457346..dcec8ad 100644 --- a/cpp/WKTJsiWorkletContext.h +++ b/cpp/WKTJsiWorkletContext.h @@ -101,11 +101,11 @@ class JsiWorkletContext return defaultInstance; } -/** - * @brief Get the Default Instance object - * - * @return JsiWorkletContext* - */ + /** + * @brief Get the Default Instance object + * + * @return JsiWorkletContext* + */ static JsiWorkletContext *getDefaultInstance() { return JsiWorkletContext::getDefaultInstanceAsShared().get(); } diff --git a/cpp/sharedvalues/WKTJsiSharedValue.h b/cpp/sharedvalues/WKTJsiSharedValue.h index ae06a92..297b731 100644 --- a/cpp/sharedvalues/WKTJsiSharedValue.h +++ b/cpp/sharedvalues/WKTJsiSharedValue.h @@ -20,8 +20,10 @@ class JsiSharedValue : public JsiHostObject { Constructs a shared value - which is a wrapped value that can be accessed in a thread safe across two javascript runtimes. */ - JsiSharedValue(const jsi::Value &value) - : _valueWrapper(JsiWrapper::wrap(*JsiWorkletContext::getDefaultInstance()->getJsRuntime(), value)) {} + explicit JsiSharedValue(const jsi::Value &value) + : _valueWrapper(JsiWrapper::wrap( + *JsiWorkletContext::getDefaultInstance()->getJsRuntime(), value, + nullptr, true)) {} /** Destructor @@ -33,15 +35,13 @@ class JsiSharedValue : public JsiHostObject { _valueWrapper->toString(runtime)); } - JSI_PROPERTY_GET(value) { - return _valueWrapper->unwrapAsProxyOrValue(runtime); - } + JSI_PROPERTY_GET(value) { return _valueWrapper->unwrap(runtime); } JSI_PROPERTY_SET(value) { if (_valueWrapper->canUpdateValue(runtime, value)) { _valueWrapper->updateValue(runtime, value); } else { - _valueWrapper = JsiWrapper::wrap(runtime, value); + _valueWrapper = JsiWrapper::wrap(runtime, value, nullptr, true); } } @@ -72,14 +72,16 @@ class JsiSharedValue : public JsiHostObject { }; // Do not Wrap this Value - replace with undefined - auto thisValuePtr = JsiWrapper::wrap(runtime, jsi::Value::undefined()); + auto thisValuePtr = + JsiWrapper::wrap(runtime, jsi::Value::undefined(), nullptr, true); auto dispatcher = JsiDispatcher::createDispatcher( runtime, thisValuePtr, functionPtr, nullptr, [&runtime, this](const char *err) { - JsiWorkletContext::getCurrent(runtime)->invokeOnJsThread([err](jsi::Runtime &runtime) { - throw jsi::JSError(runtime, err); - }); + JsiWorkletContext::getCurrent(runtime)->invokeOnJsThread( + [err](jsi::Runtime &runtime) { + throw jsi::JSError(runtime, err); + }); }); // Set up the callback to run on the correct runtime thread. @@ -121,6 +123,6 @@ class JsiSharedValue : public JsiHostObject { } private: - std::shared_ptr _valueWrapper; + std::shared_ptr _valueWrapper; }; } // namespace RNWorklet diff --git a/cpp/wrappers/WKTJsiArrayWrapper.h b/cpp/wrappers/WKTJsiArrayWrapper.h index 1d63aaa..10dcb48 100644 --- a/cpp/wrappers/WKTJsiArrayWrapper.h +++ b/cpp/wrappers/WKTJsiArrayWrapper.h @@ -24,13 +24,11 @@ class JsiArrayWrapper : public JsiHostObject, public: /** * Constructs a new array wrapper - * @param runtime In runtime - * @param value Value to wrap * @param parent Parent wrapper object + * @param useProxiesForUnwrapping use proxies when unwrapping */ - JsiArrayWrapper(jsi::Runtime &runtime, const jsi::Value &value, - JsiWrapper *parent) - : JsiWrapper(runtime, value, parent, JsiWrapperType::Array) {} + JsiArrayWrapper(JsiWrapper *parent, bool useProxiesForUnwrapping) + : JsiWrapper(parent, useProxiesForUnwrapping, JsiWrapperType::Array) {} JSI_HOST_FUNCTION(toStringImpl) { return jsi::String::createFromUtf8(runtime, toString(runtime)); @@ -46,8 +44,7 @@ class JsiArrayWrapper : public JsiHostObject, const jsi::Value *arguments, size_t count) mutable { auto retVal = jsi::Object(runtime); if (index < _array.size()) { - retVal.setProperty(runtime, "value", - _array[index]->unwrapAsProxyOrValue(runtime)); + retVal.setProperty(runtime, "value", _array[index]->unwrap(runtime)); retVal.setProperty(runtime, "done", false); index++; } else { @@ -67,7 +64,8 @@ class JsiArrayWrapper : public JsiHostObject, JSI_HOST_FUNCTION(push) { // Push all arguments to the array end for (size_t i = 0; i < count; i++) { - _array.push_back(JsiWrapper::wrap(runtime, arguments[i], this)); + _array.push_back(JsiWrapper::wrap(runtime, arguments[i], this, + getUseProxiesForUnwrapping())); } notify(); return static_cast(_array.size()); @@ -76,7 +74,9 @@ class JsiArrayWrapper : public JsiHostObject, JSI_HOST_FUNCTION(unshift) { // Insert all arguments to the array beginning for (size_t i = 0; i < count; i++) { - _array.insert(_array.begin(), JsiWrapper::wrap(runtime, arguments[i], this)); + _array.insert(_array.begin(), + JsiWrapper::wrap(runtime, arguments[i], this, + getUseProxiesForUnwrapping())); } notify(); return static_cast(_array.size()); @@ -90,7 +90,7 @@ class JsiArrayWrapper : public JsiHostObject, auto lastEl = _array.at(_array.size() - 1); _array.pop_back(); notify(); - return lastEl->unwrapAsProxyOrValue(runtime); + return lastEl->unwrap(runtime); }; JSI_HOST_FUNCTION(shift) { @@ -101,13 +101,13 @@ class JsiArrayWrapper : public JsiHostObject, auto firstEl = _array.at(0); _array.erase(_array.begin()); notify(); - return firstEl->unwrapAsProxyOrValue(runtime); + return firstEl->unwrap(runtime); }; JSI_HOST_FUNCTION(forEach) { auto callbackFn = arguments[0].asObject(runtime).asFunction(runtime); for (size_t i = 0; i < _array.size(); i++) { - auto arg = _array.at(i)->unwrapAsProxyOrValue(runtime); + auto arg = _array.at(i)->unwrap(runtime); callFunction(runtime, callbackFn, thisValue, &arg, 1); } return jsi::Value::undefined(); @@ -117,7 +117,7 @@ class JsiArrayWrapper : public JsiHostObject, auto callbackFn = arguments[0].asObject(runtime).asFunction(runtime); auto result = jsi::Array(runtime, _array.size()); for (size_t i = 0; i < _array.size(); i++) { - auto arg = _array.at(i)->unwrapAsProxyOrValue(runtime); + auto arg = _array.at(i)->unwrap(runtime); auto retVal = callFunction(runtime, callbackFn, thisValue, &arg, 1); result.setValueAtIndex(runtime, i, retVal); } @@ -129,7 +129,7 @@ class JsiArrayWrapper : public JsiHostObject, std::vector> result; for (size_t i = 0; i < _array.size(); i++) { - auto arg = _array.at(i)->unwrapAsProxyOrValue(runtime); + auto arg = _array.at(i)->unwrap(runtime); auto retVal = callFunction(runtime, callbackFn, thisValue, &arg, 1); if (retVal.getBool() == true) { result.push_back(_array.at(i)); @@ -137,8 +137,7 @@ class JsiArrayWrapper : public JsiHostObject, } auto returnValue = jsi::Array(runtime, result.size()); for (size_t i = 0; i < result.size(); i++) { - returnValue.setValueAtIndex(runtime, i, - result.at(i)->unwrapAsProxyOrValue(runtime)); + returnValue.setValueAtIndex(runtime, i, result.at(i)->unwrap(runtime)); } return returnValue; }; @@ -146,7 +145,7 @@ class JsiArrayWrapper : public JsiHostObject, JSI_HOST_FUNCTION(find) { auto callbackFn = arguments[0].asObject(runtime).asFunction(runtime); for (size_t i = 0; i < _array.size(); i++) { - auto arg = _array.at(i)->unwrapAsProxyOrValue(runtime); + auto arg = _array.at(i)->unwrap(runtime); auto retVal = callFunction(runtime, callbackFn, thisValue, &arg, 1); if (retVal.getBool() == true) { return arg; @@ -158,7 +157,7 @@ class JsiArrayWrapper : public JsiHostObject, JSI_HOST_FUNCTION(every) { auto callbackFn = arguments[0].asObject(runtime).asFunction(runtime); for (size_t i = 0; i < _array.size(); i++) { - auto arg = JsiWrapper::unwrapAsProxyOrValue(runtime, _array.at(i)); + auto arg = JsiWrapper::unwrap(runtime, _array.at(i)); auto retVal = callFunction(runtime, callbackFn, thisValue, &arg, 1); if (retVal.getBool() == false) { return false; @@ -170,7 +169,7 @@ class JsiArrayWrapper : public JsiHostObject, JSI_HOST_FUNCTION(findIndex) { auto callbackFn = arguments[0].asObject(runtime).asFunction(runtime); for (size_t i = 0; i < _array.size(); i++) { - auto arg = JsiWrapper::unwrapAsProxyOrValue(runtime, _array.at(i)); + auto arg = JsiWrapper::unwrap(runtime, _array.at(i)); auto retVal = callFunction(runtime, callbackFn, thisValue, &arg, 1); if (retVal.getBool() == true) { return static_cast(i); @@ -180,7 +179,8 @@ class JsiArrayWrapper : public JsiHostObject, }; JSI_HOST_FUNCTION(indexOf) { - auto wrappedArg = JsiWrapper::wrap(runtime, arguments[0]); + auto wrappedArg = JsiWrapper::wrap(runtime, arguments[0], nullptr, + getUseProxiesForUnwrapping()); for (size_t i = 0; i < _array.size(); i++) { // TODO: Add == operator to JsiWrapper if (wrappedArg->getType() == _array[i]->getType()) { @@ -219,14 +219,15 @@ class JsiArrayWrapper : public JsiHostObject, flat_internal(depth, _array); auto returnValue = jsi::Array(runtime, result.size()); for (size_t i = 0; i < result.size(); i++) { - returnValue.setValueAtIndex( - runtime, i, JsiWrapper::unwrapAsProxyOrValue(runtime, result.at(i))); + returnValue.setValueAtIndex(runtime, i, + JsiWrapper::unwrap(runtime, result.at(i))); } return returnValue; }; JSI_HOST_FUNCTION(includes) { - auto wrappedArg = JsiWrapper::wrap(runtime, arguments[0]); + auto wrappedArg = JsiWrapper::wrap(runtime, arguments[0], nullptr, + getUseProxiesForUnwrapping()); for (size_t i = 0; i < _array.size(); i++) { // TODO: Add == operator to JsiWrapper!!! if (wrappedArg->getType() == _array[i]->getType()) { @@ -243,8 +244,8 @@ class JsiArrayWrapper : public JsiHostObject, auto results = jsi::Array( runtime, static_cast(_array.size() + nextArray.size(runtime))); for (size_t i = 0; i < _array.size(); i++) { - results.setValueAtIndex( - runtime, i, JsiWrapper::unwrapAsProxyOrValue(runtime, _array[i])); + results.setValueAtIndex(runtime, i, + JsiWrapper::unwrap(runtime, _array[i])); } auto startIndex = std::max(0, _array.size() - 1); for (size_t i = 0; i < nextArray.size(runtime); i++) { @@ -259,7 +260,7 @@ class JsiArrayWrapper : public JsiHostObject, count > 0 ? arguments[0].asString(runtime).utf8(runtime) : ","; auto result = std::string(""); for (size_t i = 0; i < _array.size(); i++) { - auto arg = _array.at(i)->unwrapAsProxyOrValue(runtime); + auto arg = _array.at(i)->unwrap(runtime); result += arg.toString(runtime).utf8(runtime); if (i < _array.size() - 1) { result += separator; @@ -271,21 +272,24 @@ class JsiArrayWrapper : public JsiHostObject, JSI_HOST_FUNCTION(reduce) { auto callbackFn = arguments[0].asObject(runtime).asFunction(runtime); std::shared_ptr acc = - JsiWrapper::wrap(runtime, jsi::Value::undefined()); + JsiWrapper::wrap(runtime, jsi::Value::undefined(), nullptr, + getUseProxiesForUnwrapping()); if (count > 1) { - acc = JsiWrapper::wrap(runtime, arguments[1]); + acc = JsiWrapper::wrap(runtime, arguments[1], nullptr, + getUseProxiesForUnwrapping()); } for (size_t i = 0; i < _array.size(); i++) { std::vector args(3); - args[0] = acc->unwrapAsProxyOrValue(runtime); - args[1] = _array.at(i)->unwrapAsProxyOrValue(runtime); + args[0] = acc->unwrap(runtime); + args[1] = _array.at(i)->unwrap(runtime); args[2] = jsi::Value(static_cast(i)); acc = JsiWrapper::wrap( runtime, callFunction(runtime, callbackFn, thisValue, - static_cast(args.data()), 3)); + static_cast(args.data()), 3), + nullptr, getUseProxiesForUnwrapping()); } - return JsiWrapper::unwrapAsProxyOrValue(runtime, acc); + return JsiWrapper::unwrap(runtime, acc); } JSI_EXPORT_PROPERTY_GETTERS(JSI_EXPORT_PROP_GET(JsiArrayWrapper, length)) @@ -318,7 +322,16 @@ class JsiArrayWrapper : public JsiHostObject, * @return jsi::Value representing this array */ jsi::Value getValue(jsi::Runtime &runtime) override { - return getArrayProxy(runtime, shared_from_this()); + if (getUseProxiesForUnwrapping()) { + return getArrayProxy(runtime, shared_from_this()); + } + + // Copy array if we're not using proxies (shared values) + auto result = jsi::Array(runtime, _array.size()); + for (size_t i = 0; i < _array.size(); i++) { + result.setValueAtIndex(runtime, i, _array.at(i)->unwrap(runtime)); + } + return result; } bool canUpdateValue(jsi::Runtime &runtime, const jsi::Value &value) override { @@ -340,28 +353,9 @@ class JsiArrayWrapper : public JsiHostObject, _array.resize(size); for (size_t i = 0; i < size; i++) { - _array[i] = - JsiWrapper::wrap(runtime, array.getValueAtIndex(runtime, i), this); + _array[i] = JsiWrapper::wrap(runtime, array.getValueAtIndex(runtime, i), + this, getUseProxiesForUnwrapping()); } - - /* / Update prototype - auto objectCtor = runtime.global().getProperty(runtime, "Object"); - if (!objectCtor.isUndefined()) { - // Get setPrototypeOf - auto setPrototypeOf = - objectCtor.asObject(runtime).getProperty(runtime, "setPrototypeOf"); - if (!setPrototypeOf.isUndefined()) { - auto array = runtime.global().getProperty(runtime, "Array"); - if (!array.isUndefined()) { - auto arrayPrototype = - array.asObject(runtime).getProperty(runtime, "prototype"); - auto selfObject = - jsi::Object::createFromHostObject(runtime, shared_from_this()); - setPrototypeOf.asObject(runtime).asFunction(runtime).call( - runtime, selfObject, arrayPrototype); - } - } - }*/ } /** @@ -377,7 +371,13 @@ class JsiArrayWrapper : public JsiHostObject, std::all_of(nameStr.begin(), nameStr.end(), ::isdigit)) { // Return property by index auto index = std::stoi(nameStr.c_str()); - _array[index] = JsiWrapper::wrap(runtime, value); + // Ensure we have the required length + if (index >= _array.size()) { + _array.resize(index + 1); + } + // Set value + _array[index] = JsiWrapper::wrap(runtime, value, nullptr, + getUseProxiesForUnwrapping()); notify(); } else { // This is an edge case where the array is used as a @@ -401,7 +401,7 @@ class JsiArrayWrapper : public JsiHostObject, // Return property by index auto index = std::stoi(nameStr.c_str()); auto prop = _array[index]; - return JsiWrapper::unwrapAsProxyOrValue(runtime, prop); + return JsiWrapper::unwrap(runtime, prop); } // Return super JsiHostObject's get return JsiHostObject::get(runtime, name); @@ -440,45 +440,58 @@ class JsiArrayWrapper : public JsiHostObject, */ jsi::Value getArrayProxy(jsi::Runtime &runtime, std::shared_ptr hostObj) { - // return jsi::Object::createFromHostObject(runtime, hostObj); - auto createArrayProxy = runtime.global().getProperty(runtime, WorkletArrayProxyName); + + // Install factory for creating an array proxy if (createArrayProxy.isUndefined()) { // Install worklet proxy helper into runtime static std::string code = - "function (obj) {" - "return new Proxy(obj, {" - " ownKeys: function (target) {" - " return Reflect.ownKeys(target).concat(['length']);" - " }," - " getPrototypeOf: function () {" - " return Reflect.getPrototypeOf([]);" - " }," - " getOwnPropertyDescriptor: function (_, prop) {" - " return {" - " configurable: true," - " writable: true," - " enumerable: prop !== 'length'," - " };" - " }," - " set: function(target, prop, value) { return " - "Reflect.set(target,prop,value); }," - " get: function(target, prop) { return Reflect.get(target, prop); " - "}" - " })" - "}"; - + "function (target) {" + " const dummy = [];" + " return new Proxy(dummy, {" + " ownKeys: function (_target) {" + " return Reflect.ownKeys(target).concat(['length']);" + " }," + " getOwnPropertyDescriptor: function (_target, prop) {" + " return {" + " ...Reflect.getOwnPropertyDescriptor(target, prop)," + " configurable: prop !== 'length'," + " writable: true," + " enumerable: prop !== 'length'," + " };" + " }," + " set: function (_target, prop, value, _receiver) {" + " return Reflect.set(target, prop, value, target);" + " }," + " get: function (_target, prop, receiver) {" + " if (prop === 'length') return " + "Object.keys(target).length;" + " if (prop === 'keys') return Object.keys(target);" + " if (prop === 'values') return Object.values(target);" + " return Reflect.get(target, prop, receiver);" + " }," + " });" + " }"; + + // Format code as an installable function auto codeBuffer = std::make_shared("(" + code + "\n)"); + + // Create function createArrayProxy = runtime.evaluateJavaScript(codeBuffer, WorkletArrayProxyName); + + // Set in runtime runtime.global().setProperty(runtime, WorkletArrayProxyName, createArrayProxy); } + // Get the create proxy factory function auto createProxyFunc = createArrayProxy.asObject(runtime).asFunction(runtime); + + // Create the proxy that converts the HostObject to an Array return createProxyFunc.call( runtime, jsi::Object::createFromHostObject(runtime, hostObj)); } diff --git a/cpp/wrappers/WKTJsiObjectWrapper.h b/cpp/wrappers/WKTJsiObjectWrapper.h index ac72d7a..931bd36 100644 --- a/cpp/wrappers/WKTJsiObjectWrapper.h +++ b/cpp/wrappers/WKTJsiObjectWrapper.h @@ -22,13 +22,11 @@ class JsiObjectWrapper : public JsiHostObject, public: /** * Constructor - * @param runtime Calling runtie - * @param value value to wrap * @param parent optional parent wrapper + * @param useProxiesForUnwrapping unwraps using proxies */ - JsiObjectWrapper(jsi::Runtime &runtime, const jsi::Value &value, - JsiWrapper *parent) - : JsiWrapper(runtime, value, parent) {} + JsiObjectWrapper(JsiWrapper *parent, bool useProxiesForUnwrapping) + : JsiWrapper(parent, useProxiesForUnwrapping) {} JSI_HOST_FUNCTION(toStringImpl) { return jsi::String::createFromUtf8(runtime, toString(runtime)); @@ -64,14 +62,14 @@ class JsiObjectWrapper : public JsiHostObject, } updateNativeState(runtime, object); } - - void updateNativeState(jsi::Runtime& runtime, jsi::Object& obj) { - if (obj.hasNativeState(runtime)) { - _nativeState = obj.getNativeState(runtime); - } else { - _nativeState = nullptr; - } - } + + void updateNativeState(jsi::Runtime &runtime, jsi::Object &obj) { + if (obj.hasNativeState(runtime)) { + _nativeState = obj.getNativeState(runtime); + } else { + _nativeState = nullptr; + } + } /** * Overridden get value where we convert from the internal representation to @@ -80,29 +78,37 @@ class JsiObjectWrapper : public JsiHostObject, * @return Value converted to a jsi::Value */ jsi::Value getValue(jsi::Runtime &runtime) override { + if (getUseProxiesForUnwrapping()) { + if (getType() == JsiWrapperType::Object) { + return getObjectAsProxy(runtime, shared_from_this()); + } else if (getType() == JsiWrapperType::HostObject) { + return jsi::Object::createFromHostObject(runtime, _hostObject); + } + } + jsi::Object obj = getObject(runtime); if (_nativeState != nullptr) { obj.setNativeState(runtime, _nativeState); } return obj; } - - jsi::Object getObject(jsi::Runtime& runtime) { - switch (getType()) { - case JsiWrapperType::HostObject: - return jsi::Object::createFromHostObject(runtime, _hostObject); - case JsiWrapperType::HostFunction: - return jsi::Function::createFromHostFunction( - runtime, jsi::PropNameID::forUtf8(runtime, "fn"), 0, - *_hostFunction.get()); - case JsiWrapperType::Object: - return jsi::Object::createFromHostObject(runtime, shared_from_this()); - case JsiWrapperType::Promise: - throw jsi::JSError(runtime, "Promise type not supported."); - default: - throw jsi::JSError(runtime, "Value type not supported."); - } - } + + jsi::Object getObject(jsi::Runtime &runtime) { + switch (getType()) { + case JsiWrapperType::HostObject: + return jsi::Object::createFromHostObject(runtime, _hostObject); + case JsiWrapperType::HostFunction: + return jsi::Function::createFromHostFunction( + runtime, jsi::PropNameID::forUtf8(runtime, "fn"), 0, + *_hostFunction.get()); + case JsiWrapperType::Object: + return jsi::Object::createFromHostObject(runtime, shared_from_this()); + case JsiWrapperType::Promise: + throw jsi::JSError(runtime, "Promise type not supported."); + default: + throw jsi::JSError(runtime, "Value type not supported."); + } + } /** * jsi::HostObject's overridden set function @@ -114,7 +120,9 @@ class JsiObjectWrapper : public JsiHostObject, const jsi::Value &value) override { auto nameStr = name.utf8(runtime); if (_properties.count(nameStr) == 0) { - _properties.emplace(nameStr, JsiWrapper::wrap(runtime, value, this)); + _properties.emplace( + nameStr, + JsiWrapper::wrap(runtime, value, this, getUseProxiesForUnwrapping())); } else { _properties.at(nameStr)->updateValue(runtime, value); } @@ -172,16 +180,6 @@ class JsiObjectWrapper : public JsiHostObject, } } -protected: - jsi::Value getAsProxyOrValue(jsi::Runtime &runtime) override { - if (getType() == JsiWrapperType::Object) { - return getObjectAsProxy(runtime, shared_from_this()); - } else if (getType() == JsiWrapperType::HostObject) { - return jsi::Object::createFromHostObject(runtime, _hostObject); - } - return JsiWrapper::getAsProxyOrValue(runtime); - } - private: void setArrayBufferValue(jsi::Runtime &runtime, jsi::Object &obj) { throw jsi::JSError(runtime, @@ -197,7 +195,9 @@ class JsiObjectWrapper : public JsiHostObject, propNames.getValueAtIndex(runtime, i).asString(runtime).utf8(runtime); auto value = obj.getProperty(runtime, nameString.c_str()); - _properties.emplace(nameString, JsiWrapper::wrap(runtime, value, this)); + _properties.emplace( + nameString, + JsiWrapper::wrap(runtime, value, this, getUseProxiesForUnwrapping())); } } diff --git a/cpp/wrappers/WKTJsiPromiseWrapper.cpp b/cpp/wrappers/WKTJsiPromiseWrapper.cpp index 4e57a87..07c4484 100644 --- a/cpp/wrappers/WKTJsiPromiseWrapper.cpp +++ b/cpp/wrappers/WKTJsiPromiseWrapper.cpp @@ -10,13 +10,11 @@ namespace RNWorklet { namespace jsi = facebook::jsi; -size_t JsiPromiseWrapper::Counter = 1000; - std::shared_ptr JsiPromiseWrapper::createPromiseWrapper( jsi::Runtime &runtime, PromiseComputationFunction computation) { // Create promise wrapper - auto result = std::make_shared(runtime); + auto result = std::make_shared(nullptr, false); result->runComputation(runtime, computation); return result; } @@ -52,16 +50,6 @@ void JsiPromiseWrapper::runComputation(jsi::Runtime &runtime, } } -JsiPromiseWrapper::JsiPromiseWrapper(jsi::Runtime &runtime) - : JsiWrapper(runtime, jsi::Value::undefined(), nullptr) { - - _counter = ++Counter; - // printf("promise: CTOR JsiPromiseWrapper %zu\n", _counter); - - // Set type - setType(JsiWrapperType::Promise); -} - /** Returns true if the object is a thenable object - ie. an object with a then function. Which is basically what a promise is. @@ -95,8 +83,7 @@ bool JsiPromiseWrapper::isThenable(jsi::Runtime &runtime, jsi::Value &value) { std::shared_ptr JsiPromiseWrapper::resolve(jsi::Runtime &runtime, std::shared_ptr value) { - auto retVal = std::make_shared( - runtime, jsi::Value::undefined(), nullptr); + auto retVal = std::make_shared(nullptr, false); retVal->setType(JsiWrapperType::Promise); retVal->onFulfilled(runtime, value->unwrap(runtime)); return retVal; @@ -108,8 +95,7 @@ JsiPromiseWrapper::resolve(jsi::Runtime &runtime, std::shared_ptr JsiPromiseWrapper::reject(jsi::Runtime &runtime, std::shared_ptr reason) { - auto retVal = std::make_shared( - runtime, jsi::Value::undefined(), nullptr); + auto retVal = std::make_shared(nullptr, false); retVal->setType(JsiWrapperType::Promise); retVal->onRejected(runtime, reason->unwrap(runtime)); return retVal; @@ -126,7 +112,9 @@ jsi::Value JsiPromiseWrapper::then(jsi::Runtime &runtime, thenHostFn = JsiWorkletContext::createInvoker(runtime, thenFn); } else { thenHostFn = JSI_HOST_FUNCTION_LAMBDA { - return JsiWrapper::wrap(runtime, arguments[0])->unwrap(runtime); + return JsiWrapper::wrap(runtime, arguments[0], nullptr, + getUseProxiesForUnwrapping()) + ->unwrap(runtime); }; } @@ -136,7 +124,8 @@ jsi::Value JsiPromiseWrapper::then(jsi::Runtime &runtime, catchHostFn = JsiWorkletContext::createInvoker(runtime, catchFn); } - auto thisWrapper = JsiWrapper::wrap(runtime, thisValue); + auto thisWrapper = JsiWrapper::wrap(runtime, thisValue, nullptr, + getUseProxiesForUnwrapping()); return jsi::Object::createFromHostObject( runtime, then(runtime, std::move(thisWrapper), std::move(thenHostFn), std::move(catchHostFn))); @@ -146,7 +135,8 @@ std::shared_ptr JsiPromiseWrapper::then( jsi::Runtime &runtime, std::shared_ptr thisValue, const jsi::HostFunctionType &thenFn, const jsi::HostFunctionType &catchFn) { - auto controlledPromise = std::make_shared(runtime, this); + auto controlledPromise = + std::make_shared(this, getUseProxiesForUnwrapping()); _thenQueue.push_back({ .controlledPromise = controlledPromise, @@ -184,7 +174,8 @@ jsi::Value JsiPromiseWrapper::finally(jsi::Runtime &runtime, : JsiPromiseWrapper::reject(runtime, _reason)->unwrap(runtime); } - auto controlledPromise = std::make_shared(runtime, this); + auto controlledPromise = + std::make_shared(this, getUseProxiesForUnwrapping()); _finallyQueue.push_back({ .controlledPromise = controlledPromise, @@ -224,12 +215,16 @@ void JsiPromiseWrapper::setValue(jsi::Runtime &runtime, maybeCatchFunc.asObject(runtime).isFunction(runtime)) { // We have catch and then auto catchFn = callingContext->createCallInContext(runtime, maybeCatchFunc); - then(runtime, JsiWrapper::wrap(runtime, jsi::Value::undefined()), thenFn, - catchFn); + then(runtime, + JsiWrapper::wrap(runtime, jsi::Value::undefined(), nullptr, + getUseProxiesForUnwrapping()), + thenFn, catchFn); } else { // Only have then function - then(runtime, JsiWrapper::wrap(runtime, jsi::Value::undefined()), thenFn, - nullptr); + then(runtime, + JsiWrapper::wrap(runtime, jsi::Value::undefined(), nullptr, + getUseProxiesForUnwrapping()), + thenFn, nullptr); } } @@ -345,7 +340,8 @@ void JsiPromiseWrapper::onFulfilled(jsi::Runtime &runtime, const jsi::Value &val) { if (_state == PromiseState::Pending) { _state = PromiseState::Fulfilled; - _value = JsiWrapper::wrap(runtime, val); + _value = + JsiWrapper::wrap(runtime, val, nullptr, getUseProxiesForUnwrapping()); // printf("promise %zu: onFulfilled: %s\n", _counter, // _value->toString(runtime).c_str()); propagateFulfilled(runtime); @@ -356,7 +352,8 @@ void JsiPromiseWrapper::onRejected(jsi::Runtime &runtime, const jsi::Value &reason) { if (_state == PromiseState::Pending) { _state = PromiseState::Rejected; - _reason = JsiWrapper::wrap(runtime, reason); + _reason = JsiWrapper::wrap(runtime, reason, nullptr, + getUseProxiesForUnwrapping()); // printf("promise %zu: onRejected: %s\n", _counter, // _reason->toString(runtime).c_str()); propagateRejected(runtime); diff --git a/cpp/wrappers/WKTJsiPromiseWrapper.h b/cpp/wrappers/WKTJsiPromiseWrapper.h index 0bbca70..e4af723 100644 --- a/cpp/wrappers/WKTJsiPromiseWrapper.h +++ b/cpp/wrappers/WKTJsiPromiseWrapper.h @@ -61,22 +61,8 @@ class JsiPromiseWrapper createPromiseWrapper(jsi::Runtime &runtime, PromiseComputationFunction computation); - JsiPromiseWrapper(jsi::Runtime &runtime, const jsi::Value &value, - JsiWrapper *parent) - : JsiWrapper(runtime, value, parent) { - _counter = ++Counter; - setType(JsiWrapperType::Promise); - // printf("promise: CTOR JsiPromiseWrapper %zu\n", _counter); - } - - JsiPromiseWrapper(jsi::Runtime &runtime, JsiWrapper *parent) - : JsiWrapper(runtime, jsi::Value::undefined(), parent) { - setType(JsiWrapperType::Promise); - _counter = ++Counter; - // printf("promise: CTOR JsiPromiseWrapper %zu\n", _counter); - } - - explicit JsiPromiseWrapper(jsi::Runtime &runtime); + JsiPromiseWrapper(JsiWrapper *parent, bool useProxiesForUnwrapping) + : JsiWrapper(parent, useProxiesForUnwrapping, JsiWrapperType::Promise) {} ~JsiPromiseWrapper() {} /** @@ -126,9 +112,6 @@ class JsiPromiseWrapper void onFulfilled(jsi::Runtime &runtime, const jsi::Value &val); void onRejected(jsi::Runtime &runtime, const jsi::Value &reason); - static size_t Counter; - size_t _counter; - void resolve(jsi::Runtime &runtime, const jsi::Value &value) override { onFulfilled(runtime, value); } diff --git a/cpp/wrappers/WKTJsiWrapper.cpp b/cpp/wrappers/WKTJsiWrapper.cpp index 2199315..85d3c0c 100644 --- a/cpp/wrappers/WKTJsiWrapper.cpp +++ b/cpp/wrappers/WKTJsiWrapper.cpp @@ -28,21 +28,25 @@ jsi::Value JsiWrapper::getValue(jsi::Runtime &runtime) { std::shared_ptr JsiWrapper::wrap(jsi::Runtime &runtime, const jsi::Value &value, - JsiWrapper *parent) { + JsiWrapper *parent, + bool useProxiesForUnwrapping) { std::shared_ptr retVal = nullptr; if (value.isUndefined() || value.isNull() || value.isBool() || value.isNumber() || value.isString()) { - retVal = std::make_shared(runtime, value, parent); + retVal = std::make_shared(parent, useProxiesForUnwrapping); } else if (value.isObject()) { auto obj = value.asObject(runtime); if (obj.isArray(runtime)) { - retVal = std::make_shared(runtime, value, parent); + retVal = + std::make_shared(parent, useProxiesForUnwrapping); } else if (!obj.isHostObject(runtime) && JsiPromiseWrapper::isThenable(runtime, obj)) { - retVal = std::make_shared(runtime, value, parent); + retVal = + std::make_shared(parent, useProxiesForUnwrapping); } else { - retVal = std::make_shared(runtime, value, parent); + retVal = + std::make_shared(parent, useProxiesForUnwrapping); } } diff --git a/cpp/wrappers/WKTJsiWrapper.h b/cpp/wrappers/WKTJsiWrapper.h index 7210ef7..230c08a 100644 --- a/cpp/wrappers/WKTJsiWrapper.h +++ b/cpp/wrappers/WKTJsiWrapper.h @@ -30,36 +30,46 @@ enum JsiWrapperType { class JsiWrapper { public: /** - * Constructor - called from static members - * @param runtime Calling runtime - * @param value Value to wrap - * @param parent Optional parent wrapper - * @paran type Type of wrapper + * Base Constructor + * @param parent Parent wrapper + * @param useProxiesForUnwrapping Uses proxies when unwrapping + */ + explicit JsiWrapper(JsiWrapper *parent, bool useProxiesForUnwrapping) + : _parent(parent), _useProxiesForUnwrapping(useProxiesForUnwrapping) {} + + /** + * Constructor + * @param parent Parent Wrapper + * @param useProxiesForUnwrapping Uses proxies when unwrapping + * @param type Type of wrapper */ - JsiWrapper(jsi::Runtime &runtime, const jsi::Value &value, JsiWrapper *parent, + JsiWrapper(JsiWrapper *parent, bool useProxiesForUnwrapping, JsiWrapperType type) - : JsiWrapper(parent) { + : JsiWrapper(parent, useProxiesForUnwrapping) { _type = type; } /** - * Constructor - called from static members - * @param runtime Calling runtime + * Returns a wrapper for the a jsi value + * @param runtime Runtime to wrap value in * @param value Value to wrap - * @param parent Optional parent wrapper + * @param useProxiesForUnwrapping Uses proxies when unwrapping + * @return A new JsiWrapper */ - JsiWrapper(jsi::Runtime &runtime, const jsi::Value &value, JsiWrapper *parent) - : JsiWrapper(parent) {} + static std::shared_ptr wrap(jsi::Runtime &runtime, + const jsi::Value &value, + JsiWrapper *parent, + bool useProxiesForUnwrapping); /** - * Returns a wrapper for the a jsi value + * Returns a wrapper for the a jsi value without a partner and with + * useProxiesForUnwrapping set to false * @param runtime Runtime to wrap value in * @param value Value to wrap - * @return A new JsiWrapper */ static std::shared_ptr wrap(jsi::Runtime &runtime, const jsi::Value &value) { - return JsiWrapper::wrap(runtime, value, nullptr); + return JsiWrapper::wrap(runtime, value, nullptr, false); } /** @@ -74,30 +84,11 @@ class JsiWrapper { } /** - Non-static variant of unwrapAsProxyOrValue + Non-static variant of unwrap @param runtime Runtime */ jsi::Value unwrap(jsi::Runtime &runtime) { return this->getValue(runtime); } - /** - * Returns the value as a javascript proxy or value depending on need on the - * provided runtime - * @param runtime Runtime - * @param wrapper Wrapper to get value for - * @return A new js value in the provided runtime with the wrapped value - */ - static jsi::Value unwrapAsProxyOrValue(jsi::Runtime &runtime, - std::shared_ptr wrapper) { - return wrapper->getAsProxyOrValue(runtime); - } - - /** - Unwraps to a proxy if needed, to value if not. - */ - jsi::Value unwrapAsProxyOrValue(jsi::Runtime &runtime) { - return getAsProxyOrValue(runtime); - } - /** * Updates the value from a JS value * @param runtime runtime for the value @@ -138,16 +129,6 @@ class JsiWrapper { void removeListener(size_t listenerId) { _listeners.erase(listenerId); } protected: - /** - * Returns a wrapper for the value - * @param runtime Runtime to wrap value in - * @param value Value to wrap - * @param parent Parent wrapper (for nested hierarchies) - * @return A new JsiWrapper - */ - static std::shared_ptr - wrap(jsi::Runtime &runtime, const jsi::Value &value, JsiWrapper *parent); - /** * Call to notify parent that something has changed */ @@ -158,14 +139,6 @@ class JsiWrapper { notifyListeners(); } - /** - Returns self as a proxy object or a regular value, depending on wether the - value needs to be a proxy. - */ - virtual jsi::Value getAsProxyOrValue(jsi::Runtime &runtime) { - return getValue(runtime); - } - /** * Update the type * @param type Type to set @@ -177,6 +150,11 @@ class JsiWrapper { */ JsiWrapper *getParent() { return _parent; } + /** + Returns true if proxies should be used when unwrapping + */ + bool getUseProxiesForUnwrapping() { return _useProxiesForUnwrapping; } + /** Calls the Function and returns its value. This function will call the correct overload based on the this value @@ -248,13 +226,8 @@ class JsiWrapper { } } - /** - * Base Constructor - * @param parent Parent wrapper - */ - explicit JsiWrapper(JsiWrapper *parent) : _parent(parent) {} - std::mutex _readWriteMutex; + JsiWrapper *_parent; JsiWrapperType _type; @@ -265,6 +238,8 @@ class JsiWrapper { size_t _listenerId = 1000; std::map>> _listeners; + + bool _useProxiesForUnwrapping; }; } // namespace RNWorklet diff --git a/example/Tests/sharedvalue-tests.ts b/example/Tests/sharedvalue-tests.ts index e4f666a..75f0fa3 100644 --- a/example/Tests/sharedvalue-tests.ts +++ b/example/Tests/sharedvalue-tests.ts @@ -68,7 +68,7 @@ export const sharedvalue_tests = { return ExpectValue(sharedValue.value, 100); }, - box_string_to_array_FAILS: () => { + box_string_to_array: () => { const sharedValue = Worklets.createSharedValue("100"); // @ts-ignore sharedValue.value = [100, 200]; @@ -89,7 +89,7 @@ export const sharedvalue_tests = { return ExpectValue(sharedValue.value, { a: 100, b: 200 }); }, - box_object_to_array_FAILS: () => { + box_object_to_array: () => { const sharedValue = Worklets.createSharedValue({ a: 100, b: 200 }); // @ts-ignore sharedValue.value = [100.34, 200]; @@ -159,7 +159,7 @@ export const sharedvalue_tests = { ); }, - set_function_fails_when_calling_function: () => { + set_function_when_calling_function: () => { return ExpectException(() => { Worklets.createSharedValue(() => {}).value(); }); diff --git a/example/Tests/worklet-tests.ts b/example/Tests/worklet-tests.ts index 2402935..18471e2 100644 --- a/example/Tests/worklet-tests.ts +++ b/example/Tests/worklet-tests.ts @@ -109,4 +109,58 @@ export const worklet_tests = { undefined ); }, + check_pure_array_is_passed_as_array: () => { + const array = [0, 1, 2, 3, 4]; + const f = () => { + "worklet"; + return Array.isArray(array); + }; + const w = Worklets.createRunInContextFn(f); + return ExpectValue(w(), true); + }, + check_pure_array_is_passed_as_jsi_array: () => { + const array = [0, 1, 2, 3, 4]; + const f = () => { + "worklet"; + return Worklets.__jsi_is_array(array); + }; + const w = Worklets.createRunInContextFn(f); + return ExpectValue(w(), true); + }, + check_pure_array_inside_object_is_passed_as_jsi_array: () => { + const obj = { a: [0, 1, 2, 3, 4] }; + const f = () => { + "worklet"; + return Worklets.__jsi_is_array(obj.a); + }; + const w = Worklets.createRunInContextFn(f); + return ExpectValue(w(), true); + }, + check_pure_array_nested_argument_is_passed_as_jsi_array: () => { + const obj = { a: [0, 1, 2, 3, 4] }; + const f = (t: typeof obj) => { + "worklet"; + return Worklets.__jsi_is_array(t.a); + }; + const w = Worklets.createRunInContextFn(f); + return ExpectValue(w(obj), true); + }, + check_shared_value_array_is_not_passed_as_jsi_array: () => { + const obj = Worklets.createSharedValue([0, 1, 2, 3, 4]); + const f = () => { + "worklet"; + return Worklets.__jsi_is_array(obj.value); + }; + const w = Worklets.createRunInContextFn(f); + return ExpectValue(w(), false); + }, + check_shared_value_nested_array_is_not_passed_as_jsi_array: () => { + const obj = Worklets.createSharedValue({ a: [0, 1, 2, 3, 4] }); + const f = () => { + "worklet"; + return Worklets.__jsi_is_array(obj.value.a); + }; + const w = Worklets.createRunInContextFn(f); + return ExpectValue(w(), false); + }, }; diff --git a/example/Tests/wrapper-tests.ts b/example/Tests/wrapper-tests.ts index b684754..d972bdd 100644 --- a/example/Tests/wrapper-tests.ts +++ b/example/Tests/wrapper-tests.ts @@ -16,7 +16,7 @@ export const wrapper_tests = { convert_string: convert("abc"), convert_boolean: convert(true), convert_object: convert({ a: 123, b: "abc", child: { x: 5, y: 23 } }), - convert_object_with_children_FAILS: convert({ + convert_object_with_children: convert({ a: 123, b: "abc", children: [ @@ -26,8 +26,11 @@ export const wrapper_tests = { ], }), - array_is_array_FAILS: () => { - return ExpectValue(Array.isArray(Worklets.createSharedValue([])), true); + array_is_array: () => { + return ExpectValue( + Array.isArray(Worklets.createSharedValue([]).value), + true + ); }, array_instanceof_array: () => { @@ -239,8 +242,8 @@ export const wrapper_tests = { return ExpectValue(sum, 300); }, - convert_array_FAILS: convert([123, "abc"]), - convert_array_of_objects_FAILS: convert([ + convert_array: convert([123, "abc"]), + convert_array_of_objects: convert([ { x: 1, y: 2 }, { x: 5, y: 12 }, ]), diff --git a/src/types.ts b/src/types.ts index 8a07077..2df0e4a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -90,6 +90,14 @@ export interface IWorkletNativeApi { * Get the current Worklet context, or `undefined` if called in main React JS context. */ currentContext: IWorkletContext; + /** + * Returns true if jsi/cpp believes that the passed value is an array. + */ + __jsi_is_array: (value: T) => boolean; + /** + * Returns true if jsi/cpp believes that the passed value is an object. + */ + __jsi_is_object: (value: T) => boolean; } declare global { var Worklets: IWorkletNativeApi;