1*fe5cfb17STomasz Sapeta #include <cxxabi.h>
2*fe5cfb17STomasz Sapeta #include <utility>
3*fe5cfb17STomasz Sapeta 
4*fe5cfb17STomasz Sapeta #include "FrozenObject.h"
5*fe5cfb17STomasz Sapeta #include "MutableValue.h"
6*fe5cfb17STomasz Sapeta #include "MutableValueSetterProxy.h"
7*fe5cfb17STomasz Sapeta #include "RemoteObject.h"
8*fe5cfb17STomasz Sapeta #include "RuntimeDecorator.h"
9*fe5cfb17STomasz Sapeta #include "RuntimeManager.h"
10*fe5cfb17STomasz Sapeta #include "ShareableValue.h"
11*fe5cfb17STomasz Sapeta #include "SharedParent.h"
12*fe5cfb17STomasz Sapeta 
13*fe5cfb17STomasz Sapeta namespace ABI48_0_0reanimated {
14*fe5cfb17STomasz Sapeta class ShareableValue;
15*fe5cfb17STomasz Sapeta const char *HIDDEN_HOST_OBJECT_PROP = "__reanimatedHostObjectRef";
16*fe5cfb17STomasz Sapeta const char *ALREADY_CONVERTED = "__alreadyConverted";
17*fe5cfb17STomasz Sapeta const char *CALL_ASYNC = "__callAsync";
18*fe5cfb17STomasz Sapeta const char *PRIMAL_FUNCTION = "__primalFunction";
19*fe5cfb17STomasz Sapeta const char *CALLBACK_ERROR_SUFFIX =
20*fe5cfb17STomasz Sapeta     "\n\nPossible solutions are:\n"
21*fe5cfb17STomasz Sapeta     "a) If you want to synchronously execute this method, mark it as a Worklet\n"
22*fe5cfb17STomasz Sapeta     "b) If you want to execute this method on the JS thread, wrap it using runOnJS";
23*fe5cfb17STomasz Sapeta 
addHiddenProperty(jsi::Runtime & rt,jsi::Value && value,const jsi::Object & obj,const char * name)24*fe5cfb17STomasz Sapeta void addHiddenProperty(
25*fe5cfb17STomasz Sapeta     jsi::Runtime &rt,
26*fe5cfb17STomasz Sapeta     jsi::Value &&value,
27*fe5cfb17STomasz Sapeta     const jsi::Object &obj,
28*fe5cfb17STomasz Sapeta     const char *name) {
29*fe5cfb17STomasz Sapeta   jsi::Object globalObject = rt.global().getPropertyAsObject(rt, "Object");
30*fe5cfb17STomasz Sapeta   jsi::Function defineProperty =
31*fe5cfb17STomasz Sapeta       globalObject.getPropertyAsFunction(rt, "defineProperty");
32*fe5cfb17STomasz Sapeta   jsi::String internalPropName = jsi::String::createFromUtf8(rt, name);
33*fe5cfb17STomasz Sapeta   jsi::Object paramForDefineProperty(rt);
34*fe5cfb17STomasz Sapeta   paramForDefineProperty.setProperty(rt, "enumerable", false);
35*fe5cfb17STomasz Sapeta   paramForDefineProperty.setProperty(rt, "value", value);
36*fe5cfb17STomasz Sapeta   defineProperty.call(rt, obj, internalPropName, paramForDefineProperty);
37*fe5cfb17STomasz Sapeta }
38*fe5cfb17STomasz Sapeta 
freeze(jsi::Runtime & rt,const jsi::Object & obj)39*fe5cfb17STomasz Sapeta void freeze(jsi::Runtime &rt, const jsi::Object &obj) {
40*fe5cfb17STomasz Sapeta   jsi::Object globalObject = rt.global().getPropertyAsObject(rt, "Object");
41*fe5cfb17STomasz Sapeta   jsi::Function freeze = globalObject.getPropertyAsFunction(rt, "freeze");
42*fe5cfb17STomasz Sapeta   freeze.call(rt, obj);
43*fe5cfb17STomasz Sapeta }
44*fe5cfb17STomasz Sapeta 
adaptCache(jsi::Runtime & rt,const jsi::Value & value)45*fe5cfb17STomasz Sapeta void ShareableValue::adaptCache(jsi::Runtime &rt, const jsi::Value &value) {
46*fe5cfb17STomasz Sapeta   // when adapting from host object we can assign cached value immediately such
47*fe5cfb17STomasz Sapeta   // that we avoid running `toJSValue` in the future when given object is
48*fe5cfb17STomasz Sapeta   // accessed
49*fe5cfb17STomasz Sapeta   if (RuntimeDecorator::isWorkletRuntime(rt)) {
50*fe5cfb17STomasz Sapeta     if (remoteValue.expired()) {
51*fe5cfb17STomasz Sapeta       remoteValue = getWeakRef(rt);
52*fe5cfb17STomasz Sapeta     }
53*fe5cfb17STomasz Sapeta     (*remoteValue.lock()) = jsi::Value(rt, value);
54*fe5cfb17STomasz Sapeta   } else {
55*fe5cfb17STomasz Sapeta     hostValue = std::make_unique<jsi::Value>(rt, value);
56*fe5cfb17STomasz Sapeta   }
57*fe5cfb17STomasz Sapeta }
58*fe5cfb17STomasz Sapeta 
adapt(jsi::Runtime & rt,const jsi::Value & value,ValueType objectType)59*fe5cfb17STomasz Sapeta void ShareableValue::adapt(
60*fe5cfb17STomasz Sapeta     jsi::Runtime &rt,
61*fe5cfb17STomasz Sapeta     const jsi::Value &value,
62*fe5cfb17STomasz Sapeta     ValueType objectType) {
63*fe5cfb17STomasz Sapeta   if (value.isObject()) {
64*fe5cfb17STomasz Sapeta     jsi::Object object = value.asObject(rt);
65*fe5cfb17STomasz Sapeta     jsi::Value hiddenValue = object.getProperty(rt, HIDDEN_HOST_OBJECT_PROP);
66*fe5cfb17STomasz Sapeta     if (!(hiddenValue.isUndefined())) {
67*fe5cfb17STomasz Sapeta       jsi::Object hiddenProperty = hiddenValue.asObject(rt);
68*fe5cfb17STomasz Sapeta       if (hiddenProperty.isHostObject<FrozenObject>(rt)) {
69*fe5cfb17STomasz Sapeta         type = ValueType::FrozenObjectType;
70*fe5cfb17STomasz Sapeta         if (object.hasProperty(rt, "__workletHash") && object.isFunction(rt)) {
71*fe5cfb17STomasz Sapeta           type = ValueType::WorkletFunctionType;
72*fe5cfb17STomasz Sapeta         }
73*fe5cfb17STomasz Sapeta         valueContainer = std::make_unique<FrozenObjectWrapper>(
74*fe5cfb17STomasz Sapeta             hiddenProperty.getHostObject<FrozenObject>(rt));
75*fe5cfb17STomasz Sapeta         if (object.hasProperty(rt, ALREADY_CONVERTED)) {
76*fe5cfb17STomasz Sapeta           adaptCache(rt, value);
77*fe5cfb17STomasz Sapeta         }
78*fe5cfb17STomasz Sapeta         return;
79*fe5cfb17STomasz Sapeta       }
80*fe5cfb17STomasz Sapeta     }
81*fe5cfb17STomasz Sapeta   }
82*fe5cfb17STomasz Sapeta 
83*fe5cfb17STomasz Sapeta   if (objectType == ValueType::MutableValueType) {
84*fe5cfb17STomasz Sapeta     type = ValueType::MutableValueType;
85*fe5cfb17STomasz Sapeta     valueContainer =
86*fe5cfb17STomasz Sapeta         std::make_unique<MutableValueWrapper>(std::make_shared<MutableValue>(
87*fe5cfb17STomasz Sapeta             rt, value, runtimeManager, runtimeManager->scheduler));
88*fe5cfb17STomasz Sapeta   } else if (value.isUndefined()) {
89*fe5cfb17STomasz Sapeta     type = ValueType::UndefinedType;
90*fe5cfb17STomasz Sapeta   } else if (value.isNull()) {
91*fe5cfb17STomasz Sapeta     type = ValueType::NullType;
92*fe5cfb17STomasz Sapeta   } else if (value.isBool()) {
93*fe5cfb17STomasz Sapeta     type = ValueType::BoolType;
94*fe5cfb17STomasz Sapeta     valueContainer = std::make_unique<BooleanValueWrapper>(value.getBool());
95*fe5cfb17STomasz Sapeta   } else if (value.isNumber()) {
96*fe5cfb17STomasz Sapeta     type = ValueType::NumberType;
97*fe5cfb17STomasz Sapeta     valueContainer = std::make_unique<NumberValueWrapper>(value.asNumber());
98*fe5cfb17STomasz Sapeta   } else if (value.isString()) {
99*fe5cfb17STomasz Sapeta     type = ValueType::StringType;
100*fe5cfb17STomasz Sapeta     valueContainer =
101*fe5cfb17STomasz Sapeta         std::make_unique<StringValueWrapper>(value.asString(rt).utf8(rt));
102*fe5cfb17STomasz Sapeta   } else if (value.isObject()) {
103*fe5cfb17STomasz Sapeta     auto object = value.asObject(rt);
104*fe5cfb17STomasz Sapeta     if (object.isFunction(rt)) {
105*fe5cfb17STomasz Sapeta       if (object.getProperty(rt, "__workletHash").isUndefined()) {
106*fe5cfb17STomasz Sapeta         // not a worklet, we treat this as a host function
107*fe5cfb17STomasz Sapeta         type = ValueType::HostFunctionType;
108*fe5cfb17STomasz Sapeta         containsHostFunction = true;
109*fe5cfb17STomasz Sapeta 
110*fe5cfb17STomasz Sapeta         // Check if it's a hostFunction wrapper
111*fe5cfb17STomasz Sapeta         jsi::Value primalFunction = object.getProperty(rt, PRIMAL_FUNCTION);
112*fe5cfb17STomasz Sapeta         if (!primalFunction.isUndefined()) {
113*fe5cfb17STomasz Sapeta           jsi::Object handlerAsObject = primalFunction.asObject(rt);
114*fe5cfb17STomasz Sapeta           std::shared_ptr<HostFunctionHandler> handler =
115*fe5cfb17STomasz Sapeta               handlerAsObject.getHostObject<HostFunctionHandler>(rt);
116*fe5cfb17STomasz Sapeta           valueContainer = std::make_unique<HostFunctionWrapper>(handler);
117*fe5cfb17STomasz Sapeta         } else {
118*fe5cfb17STomasz Sapeta           valueContainer = std::make_unique<HostFunctionWrapper>(
119*fe5cfb17STomasz Sapeta               std::make_shared<HostFunctionHandler>(
120*fe5cfb17STomasz Sapeta                   std::make_shared<jsi::Function>(object.asFunction(rt)), rt));
121*fe5cfb17STomasz Sapeta         }
122*fe5cfb17STomasz Sapeta 
123*fe5cfb17STomasz Sapeta       } else {
124*fe5cfb17STomasz Sapeta         // a worklet
125*fe5cfb17STomasz Sapeta         type = ValueType::WorkletFunctionType;
126*fe5cfb17STomasz Sapeta         valueContainer = std::make_unique<FrozenObjectWrapper>(
127*fe5cfb17STomasz Sapeta             std::make_shared<FrozenObject>(rt, object, runtimeManager));
128*fe5cfb17STomasz Sapeta         auto &frozenObject = ValueWrapper::asFrozenObject(valueContainer);
129*fe5cfb17STomasz Sapeta         containsHostFunction |= frozenObject->containsHostFunction;
130*fe5cfb17STomasz Sapeta         if (RuntimeDecorator::isReactRuntime(rt) && !containsHostFunction) {
131*fe5cfb17STomasz Sapeta           addHiddenProperty(
132*fe5cfb17STomasz Sapeta               rt,
133*fe5cfb17STomasz Sapeta               createHost(rt, frozenObject),
134*fe5cfb17STomasz Sapeta               object,
135*fe5cfb17STomasz Sapeta               HIDDEN_HOST_OBJECT_PROP);
136*fe5cfb17STomasz Sapeta         }
137*fe5cfb17STomasz Sapeta       }
138*fe5cfb17STomasz Sapeta     } else if (object.isArray(rt)) {
139*fe5cfb17STomasz Sapeta       type = ValueType::FrozenArrayType;
140*fe5cfb17STomasz Sapeta       auto array = object.asArray(rt);
141*fe5cfb17STomasz Sapeta       valueContainer = std::make_unique<FrozenArrayWrapper>();
142*fe5cfb17STomasz Sapeta       auto &frozenArray = ValueWrapper::asFrozenArray(valueContainer);
143*fe5cfb17STomasz Sapeta       for (size_t i = 0, size = array.size(rt); i < size; i++) {
144*fe5cfb17STomasz Sapeta         auto sv = adapt(rt, array.getValueAtIndex(rt, i), runtimeManager);
145*fe5cfb17STomasz Sapeta         containsHostFunction |= sv->containsHostFunction;
146*fe5cfb17STomasz Sapeta         frozenArray.push_back(sv);
147*fe5cfb17STomasz Sapeta       }
148*fe5cfb17STomasz Sapeta     } else if (object.isHostObject<MutableValue>(rt)) {
149*fe5cfb17STomasz Sapeta       type = ValueType::MutableValueType;
150*fe5cfb17STomasz Sapeta       valueContainer = std::make_unique<MutableValueWrapper>(
151*fe5cfb17STomasz Sapeta           object.getHostObject<MutableValue>(rt));
152*fe5cfb17STomasz Sapeta       adaptCache(rt, value);
153*fe5cfb17STomasz Sapeta     } else if (object.isHostObject<RemoteObject>(rt)) {
154*fe5cfb17STomasz Sapeta       type = ValueType::RemoteObjectType;
155*fe5cfb17STomasz Sapeta       valueContainer = std::make_unique<RemoteObjectWrapper>(
156*fe5cfb17STomasz Sapeta           object.getHostObject<RemoteObject>(rt));
157*fe5cfb17STomasz Sapeta       adaptCache(rt, value);
158*fe5cfb17STomasz Sapeta     } else if (objectType == ValueType::RemoteObjectType) {
159*fe5cfb17STomasz Sapeta       type = ValueType::RemoteObjectType;
160*fe5cfb17STomasz Sapeta       valueContainer =
161*fe5cfb17STomasz Sapeta           std::make_unique<RemoteObjectWrapper>(std::make_shared<RemoteObject>(
162*fe5cfb17STomasz Sapeta               rt, object, runtimeManager, runtimeManager->scheduler));
163*fe5cfb17STomasz Sapeta     } else {
164*fe5cfb17STomasz Sapeta       // create frozen object based on a copy of a given object
165*fe5cfb17STomasz Sapeta       type = ValueType::FrozenObjectType;
166*fe5cfb17STomasz Sapeta       valueContainer = std::make_unique<FrozenObjectWrapper>(
167*fe5cfb17STomasz Sapeta           std::make_shared<FrozenObject>(rt, object, runtimeManager));
168*fe5cfb17STomasz Sapeta       auto &frozenObject = ValueWrapper::asFrozenObject(valueContainer);
169*fe5cfb17STomasz Sapeta       containsHostFunction |= frozenObject->containsHostFunction;
170*fe5cfb17STomasz Sapeta       if (RuntimeDecorator::isReactRuntime(rt)) {
171*fe5cfb17STomasz Sapeta         if (!containsHostFunction) {
172*fe5cfb17STomasz Sapeta           addHiddenProperty(
173*fe5cfb17STomasz Sapeta               rt,
174*fe5cfb17STomasz Sapeta               createHost(rt, frozenObject),
175*fe5cfb17STomasz Sapeta               object,
176*fe5cfb17STomasz Sapeta               HIDDEN_HOST_OBJECT_PROP);
177*fe5cfb17STomasz Sapeta         }
178*fe5cfb17STomasz Sapeta         freeze(rt, object);
179*fe5cfb17STomasz Sapeta       }
180*fe5cfb17STomasz Sapeta     }
181*fe5cfb17STomasz Sapeta   } else if (value.isSymbol()) {
182*fe5cfb17STomasz Sapeta     type = ValueType::StringType;
183*fe5cfb17STomasz Sapeta     valueContainer =
184*fe5cfb17STomasz Sapeta         std::make_unique<StringValueWrapper>(value.asSymbol(rt).toString(rt));
185*fe5cfb17STomasz Sapeta   } else {
186*fe5cfb17STomasz Sapeta     throw "Invalid value type";
187*fe5cfb17STomasz Sapeta   }
188*fe5cfb17STomasz Sapeta }
189*fe5cfb17STomasz Sapeta 
adapt(jsi::Runtime & rt,const jsi::Value & value,RuntimeManager * runtimeManager,ValueType valueType)190*fe5cfb17STomasz Sapeta std::shared_ptr<ShareableValue> ShareableValue::adapt(
191*fe5cfb17STomasz Sapeta     jsi::Runtime &rt,
192*fe5cfb17STomasz Sapeta     const jsi::Value &value,
193*fe5cfb17STomasz Sapeta     RuntimeManager *runtimeManager,
194*fe5cfb17STomasz Sapeta     ValueType valueType) {
195*fe5cfb17STomasz Sapeta   auto sv = std::shared_ptr<ShareableValue>(
196*fe5cfb17STomasz Sapeta       new ShareableValue(runtimeManager, runtimeManager->scheduler));
197*fe5cfb17STomasz Sapeta   sv->adapt(rt, value, valueType);
198*fe5cfb17STomasz Sapeta   return sv;
199*fe5cfb17STomasz Sapeta }
200*fe5cfb17STomasz Sapeta 
getValue(jsi::Runtime & rt)201*fe5cfb17STomasz Sapeta jsi::Value ShareableValue::getValue(jsi::Runtime &rt) {
202*fe5cfb17STomasz Sapeta   // TODO: maybe we can cache toJSValue results on a per-runtime basis, need to
203*fe5cfb17STomasz Sapeta   // avoid ref loops
204*fe5cfb17STomasz Sapeta   if (&rt == runtimeManager->runtime.get()) {
205*fe5cfb17STomasz Sapeta     // Getting value on the same runtime where it was created, prepare
206*fe5cfb17STomasz Sapeta     // remoteValue
207*fe5cfb17STomasz Sapeta     if (remoteValue.expired()) {
208*fe5cfb17STomasz Sapeta       remoteValue = getWeakRef(rt);
209*fe5cfb17STomasz Sapeta     }
210*fe5cfb17STomasz Sapeta 
211*fe5cfb17STomasz Sapeta     if (remoteValue.lock()->isUndefined()) {
212*fe5cfb17STomasz Sapeta       (*remoteValue.lock()) = toJSValue(rt);
213*fe5cfb17STomasz Sapeta     }
214*fe5cfb17STomasz Sapeta     return jsi::Value(rt, *remoteValue.lock());
215*fe5cfb17STomasz Sapeta   } else {
216*fe5cfb17STomasz Sapeta     // Getting value on a different runtime than where it was created from,
217*fe5cfb17STomasz Sapeta     // prepare hostValue
218*fe5cfb17STomasz Sapeta     if (hostValue.get() == nullptr) {
219*fe5cfb17STomasz Sapeta       hostValue = std::make_unique<jsi::Value>(toJSValue(rt));
220*fe5cfb17STomasz Sapeta     }
221*fe5cfb17STomasz Sapeta     return jsi::Value(rt, *hostValue);
222*fe5cfb17STomasz Sapeta   }
223*fe5cfb17STomasz Sapeta }
224*fe5cfb17STomasz Sapeta 
createHost(jsi::Runtime & rt,std::shared_ptr<jsi::HostObject> host)225*fe5cfb17STomasz Sapeta jsi::Object ShareableValue::createHost(
226*fe5cfb17STomasz Sapeta     jsi::Runtime &rt,
227*fe5cfb17STomasz Sapeta     std::shared_ptr<jsi::HostObject> host) {
228*fe5cfb17STomasz Sapeta   return jsi::Object::createFromHostObject(rt, host);
229*fe5cfb17STomasz Sapeta }
230*fe5cfb17STomasz Sapeta 
createFrozenWrapper(jsi::Runtime & rt,std::shared_ptr<FrozenObject> frozenObject)231*fe5cfb17STomasz Sapeta jsi::Value createFrozenWrapper(
232*fe5cfb17STomasz Sapeta     jsi::Runtime &rt,
233*fe5cfb17STomasz Sapeta     std::shared_ptr<FrozenObject> frozenObject) {
234*fe5cfb17STomasz Sapeta   jsi::Object __reanimatedHiddenHost =
235*fe5cfb17STomasz Sapeta       jsi::Object::createFromHostObject(rt, frozenObject);
236*fe5cfb17STomasz Sapeta   jsi::Object obj = frozenObject->shallowClone(rt);
237*fe5cfb17STomasz Sapeta   jsi::Object globalObject = rt.global().getPropertyAsObject(rt, "Object");
238*fe5cfb17STomasz Sapeta   jsi::Function freeze = globalObject.getPropertyAsFunction(rt, "freeze");
239*fe5cfb17STomasz Sapeta   if (!frozenObject->containsHostFunction) {
240*fe5cfb17STomasz Sapeta     addHiddenProperty(
241*fe5cfb17STomasz Sapeta         rt, std::move(__reanimatedHiddenHost), obj, HIDDEN_HOST_OBJECT_PROP);
242*fe5cfb17STomasz Sapeta     addHiddenProperty(rt, true, obj, ALREADY_CONVERTED);
243*fe5cfb17STomasz Sapeta   }
244*fe5cfb17STomasz Sapeta   return freeze.call(rt, obj);
245*fe5cfb17STomasz Sapeta }
246*fe5cfb17STomasz Sapeta 
toJSValue(jsi::Runtime & rt)247*fe5cfb17STomasz Sapeta jsi::Value ShareableValue::toJSValue(jsi::Runtime &rt) {
248*fe5cfb17STomasz Sapeta   switch (type) {
249*fe5cfb17STomasz Sapeta     case ValueType::UndefinedType:
250*fe5cfb17STomasz Sapeta       return jsi::Value::undefined();
251*fe5cfb17STomasz Sapeta     case ValueType::NullType:
252*fe5cfb17STomasz Sapeta       return jsi::Value::null();
253*fe5cfb17STomasz Sapeta     case ValueType::BoolType:
254*fe5cfb17STomasz Sapeta       return jsi::Value(ValueWrapper::asBoolean(valueContainer));
255*fe5cfb17STomasz Sapeta     case ValueType::NumberType:
256*fe5cfb17STomasz Sapeta       return jsi::Value(ValueWrapper::asNumber(valueContainer));
257*fe5cfb17STomasz Sapeta     case ValueType::StringType: {
258*fe5cfb17STomasz Sapeta       auto &stringValue = ValueWrapper::asString(valueContainer);
259*fe5cfb17STomasz Sapeta       return jsi::Value(rt, jsi::String::createFromUtf8(rt, stringValue));
260*fe5cfb17STomasz Sapeta     }
261*fe5cfb17STomasz Sapeta     case ValueType::FrozenObjectType: {
262*fe5cfb17STomasz Sapeta       auto &frozenObject = ValueWrapper::asFrozenObject(valueContainer);
263*fe5cfb17STomasz Sapeta       return createFrozenWrapper(rt, frozenObject);
264*fe5cfb17STomasz Sapeta     }
265*fe5cfb17STomasz Sapeta     case ValueType::FrozenArrayType: {
266*fe5cfb17STomasz Sapeta       auto &frozenArray = ValueWrapper::asFrozenArray(valueContainer);
267*fe5cfb17STomasz Sapeta       jsi::Array array(rt, frozenArray.size());
268*fe5cfb17STomasz Sapeta       for (size_t i = 0; i < frozenArray.size(); i++) {
269*fe5cfb17STomasz Sapeta         array.setValueAtIndex(rt, i, frozenArray[i]->toJSValue(rt));
270*fe5cfb17STomasz Sapeta       }
271*fe5cfb17STomasz Sapeta       return array;
272*fe5cfb17STomasz Sapeta     }
273*fe5cfb17STomasz Sapeta     case ValueType::RemoteObjectType: {
274*fe5cfb17STomasz Sapeta       auto &remoteObject = ValueWrapper::asRemoteObject(valueContainer);
275*fe5cfb17STomasz Sapeta       if (RuntimeDecorator::isWorkletRuntime(rt)) {
276*fe5cfb17STomasz Sapeta         remoteObject->maybeInitializeOnWorkletRuntime(rt);
277*fe5cfb17STomasz Sapeta       }
278*fe5cfb17STomasz Sapeta       return createHost(rt, remoteObject);
279*fe5cfb17STomasz Sapeta     }
280*fe5cfb17STomasz Sapeta     case ValueType::MutableValueType: {
281*fe5cfb17STomasz Sapeta       auto &mutableObject = ValueWrapper::asMutableValue(valueContainer);
282*fe5cfb17STomasz Sapeta       return createHost(rt, mutableObject);
283*fe5cfb17STomasz Sapeta     }
284*fe5cfb17STomasz Sapeta     case ValueType::HostFunctionType: {
285*fe5cfb17STomasz Sapeta       auto hostFunctionWrapper =
286*fe5cfb17STomasz Sapeta           ValueWrapper::asHostFunctionWrapper(valueContainer);
287*fe5cfb17STomasz Sapeta       auto &hostRuntime = hostFunctionWrapper->value->hostRuntime;
288*fe5cfb17STomasz Sapeta       if (hostRuntime == &rt) {
289*fe5cfb17STomasz Sapeta         // function is accessed from the same runtime it was crated, we just
290*fe5cfb17STomasz Sapeta         // return same function obj
291*fe5cfb17STomasz Sapeta         return jsi::Value(
292*fe5cfb17STomasz Sapeta             rt, *hostFunctionWrapper->value->getPureFunction().get());
293*fe5cfb17STomasz Sapeta       } else {
294*fe5cfb17STomasz Sapeta         // function is accessed from a different runtime, we wrap function in
295*fe5cfb17STomasz Sapeta         // host func that'd enqueue call on an appropriate thread
296*fe5cfb17STomasz Sapeta 
297*fe5cfb17STomasz Sapeta         auto runtimeManager = this->runtimeManager;
298*fe5cfb17STomasz Sapeta         auto hostFunction = hostFunctionWrapper->value;
299*fe5cfb17STomasz Sapeta 
300*fe5cfb17STomasz Sapeta         auto warnFunction = [runtimeManager, hostFunction](
301*fe5cfb17STomasz Sapeta                                 jsi::Runtime &rt,
302*fe5cfb17STomasz Sapeta                                 const jsi::Value &thisValue,
303*fe5cfb17STomasz Sapeta                                 const jsi::Value *args,
304*fe5cfb17STomasz Sapeta                                 size_t count) -> jsi::Value {
305*fe5cfb17STomasz Sapeta           jsi::Value jsThis = rt.global().getProperty(rt, "jsThis");
306*fe5cfb17STomasz Sapeta           std::string workletLocation = jsThis.asObject(rt)
307*fe5cfb17STomasz Sapeta                                             .getProperty(rt, "__location")
308*fe5cfb17STomasz Sapeta                                             .toString(rt)
309*fe5cfb17STomasz Sapeta                                             .utf8(rt);
310*fe5cfb17STomasz Sapeta           std::string exceptionMessage = "Tried to synchronously call ";
311*fe5cfb17STomasz Sapeta           if (hostFunction->functionName.empty()) {
312*fe5cfb17STomasz Sapeta             exceptionMessage += "anonymous function";
313*fe5cfb17STomasz Sapeta           } else {
314*fe5cfb17STomasz Sapeta             exceptionMessage += "function {" + hostFunction->functionName + "}";
315*fe5cfb17STomasz Sapeta           }
316*fe5cfb17STomasz Sapeta           exceptionMessage +=
317*fe5cfb17STomasz Sapeta               " from a different thread.\n\nOccurred in worklet location: ";
318*fe5cfb17STomasz Sapeta           exceptionMessage += workletLocation;
319*fe5cfb17STomasz Sapeta           exceptionMessage += CALLBACK_ERROR_SUFFIX;
320*fe5cfb17STomasz Sapeta           runtimeManager->errorHandler->setError(exceptionMessage);
321*fe5cfb17STomasz Sapeta           runtimeManager->errorHandler->raise();
322*fe5cfb17STomasz Sapeta 
323*fe5cfb17STomasz Sapeta           return jsi::Value::undefined();
324*fe5cfb17STomasz Sapeta         };
325*fe5cfb17STomasz Sapeta 
326*fe5cfb17STomasz Sapeta         auto clb = [runtimeManager, hostFunction, hostRuntime](
327*fe5cfb17STomasz Sapeta                        jsi::Runtime &rt,
328*fe5cfb17STomasz Sapeta                        const jsi::Value &thisValue,
329*fe5cfb17STomasz Sapeta                        const jsi::Value *args,
330*fe5cfb17STomasz Sapeta                        size_t count) -> jsi::Value {
331*fe5cfb17STomasz Sapeta           // TODO: we should find thread based on runtime such that we could
332*fe5cfb17STomasz Sapeta           // also call UI methods from RN and not only RN methods from UI
333*fe5cfb17STomasz Sapeta 
334*fe5cfb17STomasz Sapeta           std::vector<std::shared_ptr<ShareableValue>> params;
335*fe5cfb17STomasz Sapeta           for (int i = 0; i < count; ++i) {
336*fe5cfb17STomasz Sapeta             params.push_back(
337*fe5cfb17STomasz Sapeta                 ShareableValue::adapt(rt, args[i], runtimeManager));
338*fe5cfb17STomasz Sapeta           }
339*fe5cfb17STomasz Sapeta 
340*fe5cfb17STomasz Sapeta           std::function<void()> job = [hostFunction, hostRuntime, params] {
341*fe5cfb17STomasz Sapeta             jsi::Value *args = new jsi::Value[params.size()];
342*fe5cfb17STomasz Sapeta             for (int i = 0; i < params.size(); ++i) {
343*fe5cfb17STomasz Sapeta               args[i] = params[i]->getValue(*hostRuntime);
344*fe5cfb17STomasz Sapeta             }
345*fe5cfb17STomasz Sapeta             jsi::Value returnedValue =
346*fe5cfb17STomasz Sapeta                 hostFunction->getPureFunction().get()->call(
347*fe5cfb17STomasz Sapeta                     *hostRuntime,
348*fe5cfb17STomasz Sapeta                     static_cast<const jsi::Value *>(args),
349*fe5cfb17STomasz Sapeta                     static_cast<size_t>(params.size()));
350*fe5cfb17STomasz Sapeta 
351*fe5cfb17STomasz Sapeta             delete[] args;
352*fe5cfb17STomasz Sapeta             // ToDo use returned value to return promise
353*fe5cfb17STomasz Sapeta           };
354*fe5cfb17STomasz Sapeta 
355*fe5cfb17STomasz Sapeta           runtimeManager->scheduler->scheduleOnJS(job);
356*fe5cfb17STomasz Sapeta           return jsi::Value::undefined();
357*fe5cfb17STomasz Sapeta         };
358*fe5cfb17STomasz Sapeta         jsi::Function wrapperFunction = jsi::Function::createFromHostFunction(
359*fe5cfb17STomasz Sapeta             rt, jsi::PropNameID::forAscii(rt, "hostFunction"), 0, warnFunction);
360*fe5cfb17STomasz Sapeta         jsi::Function res = jsi::Function::createFromHostFunction(
361*fe5cfb17STomasz Sapeta             rt, jsi::PropNameID::forAscii(rt, "hostFunction"), 0, clb);
362*fe5cfb17STomasz Sapeta         addHiddenProperty(rt, std::move(res), wrapperFunction, CALL_ASYNC);
363*fe5cfb17STomasz Sapeta         jsi::Object functionHandler =
364*fe5cfb17STomasz Sapeta             createHost(rt, hostFunctionWrapper->value);
365*fe5cfb17STomasz Sapeta         addHiddenProperty(
366*fe5cfb17STomasz Sapeta             rt, std::move(functionHandler), wrapperFunction, PRIMAL_FUNCTION);
367*fe5cfb17STomasz Sapeta         return wrapperFunction;
368*fe5cfb17STomasz Sapeta       }
369*fe5cfb17STomasz Sapeta     }
370*fe5cfb17STomasz Sapeta     case ValueType::WorkletFunctionType: {
371*fe5cfb17STomasz Sapeta       auto runtimeManager = this->runtimeManager;
372*fe5cfb17STomasz Sapeta       auto &frozenObject = ValueWrapper::asFrozenObject(this->valueContainer);
373*fe5cfb17STomasz Sapeta       if (RuntimeDecorator::isWorkletRuntime(rt)) {
374*fe5cfb17STomasz Sapeta         // when running on worklet thread we prep a function
375*fe5cfb17STomasz Sapeta 
376*fe5cfb17STomasz Sapeta         auto jsThis = std::make_shared<jsi::Object>(
377*fe5cfb17STomasz Sapeta             frozenObject->shallowClone(*runtimeManager->runtime));
378*fe5cfb17STomasz Sapeta         std::shared_ptr<jsi::Function> funPtr(
379*fe5cfb17STomasz Sapeta             runtimeManager->workletsCache->getFunction(rt, frozenObject));
380*fe5cfb17STomasz Sapeta         auto name = funPtr->getProperty(rt, "name").asString(rt).utf8(rt);
381*fe5cfb17STomasz Sapeta 
382*fe5cfb17STomasz Sapeta         auto clb = [=](jsi::Runtime &rt,
383*fe5cfb17STomasz Sapeta                        const jsi::Value &thisValue,
384*fe5cfb17STomasz Sapeta                        const jsi::Value *args,
385*fe5cfb17STomasz Sapeta                        size_t count) mutable -> jsi::Value {
386*fe5cfb17STomasz Sapeta           const jsi::String jsThisName =
387*fe5cfb17STomasz Sapeta               jsi::String::createFromAscii(rt, "jsThis");
388*fe5cfb17STomasz Sapeta           jsi::Object global = rt.global();
389*fe5cfb17STomasz Sapeta           jsi::Value oldJSThis = global.getProperty(rt, jsThisName);
390*fe5cfb17STomasz Sapeta           global.setProperty(rt, jsThisName, *jsThis); // set jsThis
391*fe5cfb17STomasz Sapeta 
392*fe5cfb17STomasz Sapeta           jsi::Value res = jsi::Value::undefined();
393*fe5cfb17STomasz Sapeta           try {
394*fe5cfb17STomasz Sapeta             if (thisValue.isObject()) {
395*fe5cfb17STomasz Sapeta               res =
396*fe5cfb17STomasz Sapeta                   funPtr->callWithThis(rt, thisValue.asObject(rt), args, count);
397*fe5cfb17STomasz Sapeta             } else {
398*fe5cfb17STomasz Sapeta               res = funPtr->call(rt, args, count);
399*fe5cfb17STomasz Sapeta             }
400*fe5cfb17STomasz Sapeta           } catch (std::exception &e) {
401*fe5cfb17STomasz Sapeta             std::string str = e.what();
402*fe5cfb17STomasz Sapeta             runtimeManager->errorHandler->setError(str);
403*fe5cfb17STomasz Sapeta             runtimeManager->errorHandler->raise();
404*fe5cfb17STomasz Sapeta           } catch (...) {
405*fe5cfb17STomasz Sapeta             if (demangleExceptionName(
406*fe5cfb17STomasz Sapeta                     abi::__cxa_current_exception_type()->name()) ==
407*fe5cfb17STomasz Sapeta                 "ABI48_0_0facebook::jsi::JSError") {
408*fe5cfb17STomasz Sapeta               throw jsi::JSError(rt, "Javascript worklet error");
409*fe5cfb17STomasz Sapeta             }
410*fe5cfb17STomasz Sapeta             // TODO find out a way to get the error's message on hermes
411*fe5cfb17STomasz Sapeta             jsi::Value location = jsThis->getProperty(rt, "__location");
412*fe5cfb17STomasz Sapeta             std::string str = "Javascript worklet error";
413*fe5cfb17STomasz Sapeta             if (location.isString()) {
414*fe5cfb17STomasz Sapeta               str += "\nIn file: " + location.asString(rt).utf8(rt);
415*fe5cfb17STomasz Sapeta             }
416*fe5cfb17STomasz Sapeta             runtimeManager->errorHandler->setError(str);
417*fe5cfb17STomasz Sapeta             runtimeManager->errorHandler->raise();
418*fe5cfb17STomasz Sapeta           }
419*fe5cfb17STomasz Sapeta           global.setProperty(rt, jsThisName, oldJSThis); // clean jsThis
420*fe5cfb17STomasz Sapeta           return res;
421*fe5cfb17STomasz Sapeta         };
422*fe5cfb17STomasz Sapeta         return jsi::Function::createFromHostFunction(
423*fe5cfb17STomasz Sapeta             rt, jsi::PropNameID::forAscii(rt, name.c_str()), 0, clb);
424*fe5cfb17STomasz Sapeta       } else {
425*fe5cfb17STomasz Sapeta         // when run outside of UI thread we enqueue a call on the UI thread
426*fe5cfb17STomasz Sapeta         auto clb = [=](jsi::Runtime &rt,
427*fe5cfb17STomasz Sapeta                        const jsi::Value &thisValue,
428*fe5cfb17STomasz Sapeta                        const jsi::Value *args,
429*fe5cfb17STomasz Sapeta                        size_t count) -> jsi::Value {
430*fe5cfb17STomasz Sapeta           // TODO: we should find thread based on runtime such that we could
431*fe5cfb17STomasz Sapeta           // also call UI methods from RN and not only RN methods from UI
432*fe5cfb17STomasz Sapeta 
433*fe5cfb17STomasz Sapeta           std::vector<std::shared_ptr<ShareableValue>> params;
434*fe5cfb17STomasz Sapeta           for (int i = 0; i < count; ++i) {
435*fe5cfb17STomasz Sapeta             params.push_back(
436*fe5cfb17STomasz Sapeta                 ShareableValue::adapt(rt, args[i], runtimeManager));
437*fe5cfb17STomasz Sapeta           }
438*fe5cfb17STomasz Sapeta 
439*fe5cfb17STomasz Sapeta           runtimeManager->scheduler->scheduleOnUI([=] {
440*fe5cfb17STomasz Sapeta             jsi::Runtime &rt = *runtimeManager->runtime.get();
441*fe5cfb17STomasz Sapeta             auto jsThis = createFrozenWrapper(rt, frozenObject).getObject(rt);
442*fe5cfb17STomasz Sapeta             auto code =
443*fe5cfb17STomasz Sapeta                 jsThis.getProperty(rt, "asString").asString(rt).utf8(rt);
444*fe5cfb17STomasz Sapeta             std::shared_ptr<jsi::Function> funPtr(
445*fe5cfb17STomasz Sapeta                 runtimeManager->workletsCache->getFunction(rt, frozenObject));
446*fe5cfb17STomasz Sapeta 
447*fe5cfb17STomasz Sapeta             jsi::Value *args = new jsi::Value[params.size()];
448*fe5cfb17STomasz Sapeta             for (int i = 0; i < params.size(); ++i) {
449*fe5cfb17STomasz Sapeta               args[i] = params[i]->getValue(rt);
450*fe5cfb17STomasz Sapeta             }
451*fe5cfb17STomasz Sapeta 
452*fe5cfb17STomasz Sapeta             jsi::Value returnedValue;
453*fe5cfb17STomasz Sapeta             const jsi::String jsThisName =
454*fe5cfb17STomasz Sapeta                 jsi::String::createFromAscii(rt, "jsThis");
455*fe5cfb17STomasz Sapeta             jsi::Object global = rt.global();
456*fe5cfb17STomasz Sapeta             jsi::Value oldJSThis = global.getProperty(rt, jsThisName);
457*fe5cfb17STomasz Sapeta             global.setProperty(rt, jsThisName, jsThis); // set jsThis
458*fe5cfb17STomasz Sapeta             try {
459*fe5cfb17STomasz Sapeta               returnedValue = funPtr->call(
460*fe5cfb17STomasz Sapeta                   rt,
461*fe5cfb17STomasz Sapeta                   static_cast<const jsi::Value *>(args),
462*fe5cfb17STomasz Sapeta                   static_cast<size_t>(params.size()));
463*fe5cfb17STomasz Sapeta             } catch (std::exception &e) {
464*fe5cfb17STomasz Sapeta               std::string str = e.what();
465*fe5cfb17STomasz Sapeta               runtimeManager->errorHandler->setError(str);
466*fe5cfb17STomasz Sapeta               runtimeManager->errorHandler->raise();
467*fe5cfb17STomasz Sapeta             } catch (...) {
468*fe5cfb17STomasz Sapeta               if (demangleExceptionName(
469*fe5cfb17STomasz Sapeta                       abi::__cxa_current_exception_type()->name()) ==
470*fe5cfb17STomasz Sapeta                   "ABI48_0_0facebook::jsi::JSError") {
471*fe5cfb17STomasz Sapeta                 throw jsi::JSError(rt, "Javascript worklet error");
472*fe5cfb17STomasz Sapeta               }
473*fe5cfb17STomasz Sapeta               // TODO find out a way to get the error's message on hermes
474*fe5cfb17STomasz Sapeta               jsi::Value location = jsThis.getProperty(rt, "__location");
475*fe5cfb17STomasz Sapeta               std::string str = "Javascript worklet error";
476*fe5cfb17STomasz Sapeta               if (location.isString()) {
477*fe5cfb17STomasz Sapeta                 str += "\nIn file: " + location.asString(rt).utf8(rt);
478*fe5cfb17STomasz Sapeta               }
479*fe5cfb17STomasz Sapeta               runtimeManager->errorHandler->setError(str);
480*fe5cfb17STomasz Sapeta               runtimeManager->errorHandler->raise();
481*fe5cfb17STomasz Sapeta             }
482*fe5cfb17STomasz Sapeta             global.setProperty(rt, jsThisName, oldJSThis); // clean jsThis
483*fe5cfb17STomasz Sapeta 
484*fe5cfb17STomasz Sapeta             delete[] args;
485*fe5cfb17STomasz Sapeta             // ToDo use returned value to return promise
486*fe5cfb17STomasz Sapeta           });
487*fe5cfb17STomasz Sapeta           return jsi::Value::undefined();
488*fe5cfb17STomasz Sapeta         };
489*fe5cfb17STomasz Sapeta         return jsi::Function::createFromHostFunction(
490*fe5cfb17STomasz Sapeta             rt, jsi::PropNameID::forAscii(rt, "_workletFunction"), 0, clb);
491*fe5cfb17STomasz Sapeta       }
492*fe5cfb17STomasz Sapeta     }
493*fe5cfb17STomasz Sapeta     default: {
494*fe5cfb17STomasz Sapeta       throw "Unable to find conversion method for this type";
495*fe5cfb17STomasz Sapeta     }
496*fe5cfb17STomasz Sapeta   }
497*fe5cfb17STomasz Sapeta   throw "convert error";
498*fe5cfb17STomasz Sapeta }
499*fe5cfb17STomasz Sapeta 
demangleExceptionName(std::string toDemangle)500*fe5cfb17STomasz Sapeta std::string ShareableValue::demangleExceptionName(std::string toDemangle) {
501*fe5cfb17STomasz Sapeta   int status = 0;
502*fe5cfb17STomasz Sapeta   char *buff =
503*fe5cfb17STomasz Sapeta       __cxxabiv1::__cxa_demangle(toDemangle.c_str(), nullptr, nullptr, &status);
504*fe5cfb17STomasz Sapeta   if (!buff) {
505*fe5cfb17STomasz Sapeta     return toDemangle;
506*fe5cfb17STomasz Sapeta   }
507*fe5cfb17STomasz Sapeta   std::string demangled = buff;
508*fe5cfb17STomasz Sapeta   std::free(buff);
509*fe5cfb17STomasz Sapeta   return demangled;
510*fe5cfb17STomasz Sapeta }
511*fe5cfb17STomasz Sapeta 
512*fe5cfb17STomasz Sapeta } // namespace reanimated
513