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