Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restructure RuntimeDecorator #1860

Merged
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 11 additions & 21 deletions Common/cpp/NativeModules/NativeReanimatedModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,35 +63,25 @@ NativeReanimatedModule::NativeReanimatedModule(std::shared_ptr<CallInvoker> jsIn
std::function<jsi::Value(jsi::Runtime &, const int, const jsi::String &)> propObtainer,
PlatformDepMethodsHolder platformDepMethodsHolder) : NativeReanimatedModuleSpec(jsInvoker),
runtime(std::move(rt)),
mapperRegistry(new MapperRegistry()),
eventHandlerRegistry(new EventHandlerRegistry()),
mapperRegistry(std::make_shared<MapperRegistry>()),
eventHandlerRegistry(std::make_shared<EventHandlerRegistry>()),
requestRender(platformDepMethodsHolder.requestRender),
propObtainer(propObtainer),
errorHandler(errorHandler),
workletsCache(new WorkletsCache()),
workletsCache(std::make_shared<WorkletsCache>()),
scheduler(scheduler)
{
auto requestAnimationFrame = [=](FrameCallback callback) {
frameCallbacks.push_back(callback);
maybeRequestRender();
};

RuntimeDecorator::addNativeObjects(*runtime,
platformDepMethodsHolder.updaterFunction,
requestAnimationFrame,
platformDepMethodsHolder.scrollToFunction,
platformDepMethodsHolder.measuringFunction,
platformDepMethodsHolder.getCurrentTime);
}

bool NativeReanimatedModule::isUIRuntime(jsi::Runtime &rt)
{
return runtime.get() == &rt;
}

bool NativeReanimatedModule::isHostRuntime(jsi::Runtime &rt)
{
return !isUIRuntime(rt);

RuntimeDecorator::decorateUIRuntime(*runtime,
platformDepMethodsHolder.updaterFunction,
requestAnimationFrame,
platformDepMethodsHolder.scrollToFunction,
platformDepMethodsHolder.measuringFunction,
platformDepMethodsHolder.getCurrentTime);
}

void NativeReanimatedModule::installCoreFunctions(jsi::Runtime &rt, const jsi::Value &valueSetter)
Expand Down Expand Up @@ -174,7 +164,7 @@ jsi::Value NativeReanimatedModule::getViewProp(jsi::Runtime &rt, const jsi::Valu
const int viewTagInt = (int)viewTag.asNumber();
std::string propNameStr = propName.asString(rt).utf8(rt);
jsi::Function fun = callback.getObject(rt).asFunction(rt);
std::shared_ptr<jsi::Function> funPtr(new jsi::Function(std::move(fun)));
std::shared_ptr<jsi::Function> funPtr = std::make_shared<jsi::Function>(std::move(fun));

scheduler->scheduleOnUI([&rt, viewTagInt, funPtr, this, propNameStr]() {
const jsi::String propNameValue = jsi::String::createFromUtf8(rt, propNameStr);
Expand Down
2 changes: 1 addition & 1 deletion Common/cpp/Registries/WorkletsCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ std::shared_ptr<jsi::Function> WorkletsCache::getFunction(jsi::Runtime &rt, std:
rt,
ValueWrapper::asString(frozenObj->map["asString"]->valueContainer)
);
std::shared_ptr<jsi::Function> funPtr(new jsi::Function(std::move(fun)));
std::shared_ptr<jsi::Function> funPtr = std::make_shared<jsi::Function>(std::move(fun));
worklets[workletHash] = funPtr;
}
return worklets[workletHash];
Expand Down
6 changes: 3 additions & 3 deletions Common/cpp/SharedItems/MutableValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ void MutableValue::setValue(jsi::Runtime &rt, const jsi::Value &newValue) {
}
};

if (module->isUIRuntime(rt)) {
if (RuntimeDecorator::isWorkletRuntime(rt)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that RuntimeDecorator should have such methods. Maybe it will fit better with RuntimeManager?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RuntimeDecorator is the one responsible for marking a runtime a "worklet runtime", why shouldn't it also be exposing a method that can determine this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have convinced me.

notifyListeners();
} else {
module->scheduler->scheduleOnUI([notifyListeners] {
Expand All @@ -33,7 +33,7 @@ jsi::Value MutableValue::getValue(jsi::Runtime &rt) {
void MutableValue::set(jsi::Runtime &rt, const jsi::PropNameID &name, const jsi::Value &newValue) {
auto propName = name.utf8(rt);

if (module->isHostRuntime(rt)) {
if (RuntimeDecorator::isReactRuntime(rt)) {
if (propName == "value") {
auto shareable = ShareableValue::adapt(rt, newValue, module);
module->scheduler->scheduleOnUI([this, shareable] {
Expand Down Expand Up @@ -72,7 +72,7 @@ jsi::Value MutableValue::get(jsi::Runtime &rt, const jsi::PropNameID &name) {
return getValue(rt);
}

if (module->isUIRuntime(rt)) {
if (RuntimeDecorator::isWorkletRuntime(rt)) {
// _value and _animation should be accessed from UI only
if (propName == "_value") {
return getValue(rt);
Expand Down
7 changes: 4 additions & 3 deletions Common/cpp/SharedItems/RemoteObject.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
#include "RemoteObject.h"
#include "SharedParent.h"
#include "NativeReanimatedModule.h"
#include "RuntimeDecorator.h"
#include <jsi/jsi.h>

using namespace facebook;

namespace reanimated {

void RemoteObject::maybeInitializeOnUIRuntime(jsi::Runtime &rt) {
void RemoteObject::maybeInitializeOnWorkletRuntime(jsi::Runtime &rt) {
if (initializer.get() != nullptr) {
backing = getWeakRef(rt);
*backing.lock() = initializer->shallowClone(rt);
Expand All @@ -16,14 +17,14 @@ void RemoteObject::maybeInitializeOnUIRuntime(jsi::Runtime &rt) {
}

jsi::Value RemoteObject::get(jsi::Runtime &rt, const jsi::PropNameID &name) {
if (module->isUIRuntime(rt)) {
if (RuntimeDecorator::isWorkletRuntime(rt)) {
return backing.lock()->getObject(rt).getProperty(rt, name);
}
return jsi::Value::undefined();
}

void RemoteObject::set(jsi::Runtime &rt, const jsi::PropNameID &name, const jsi::Value &value) {
if (module->isUIRuntime(rt)) {
if (RuntimeDecorator::isWorkletRuntime(rt)) {
backing.lock()->getObject(rt).setProperty(rt, name, value);
}
// TODO: we should throw if trying to update remote from host runtime
Expand Down
58 changes: 29 additions & 29 deletions Common/cpp/SharedItems/ShareableValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ std::string CALLBACK_ERROR_SUFFIX = R"(
Possible solutions are:
a) If you want to synchronously execute this method, mark it as a Worklet
b) If you want to execute this method on the JS thread, wrap it using runOnJS )";

void addHiddenProperty(jsi::Runtime &rt,
jsi::Value &&value,
jsi::Object &obj,
Expand All @@ -40,7 +40,7 @@ void freeze(jsi::Runtime &rt, jsi::Object &obj) {
void ShareableValue::adaptCache(jsi::Runtime &rt, const jsi::Value &value) {
// when adapting from host object we can assign cached value immediately such that we avoid
// running `toJSValue` in the future when given object is accessed
if (module->isUIRuntime(rt)) {
if (RuntimeDecorator::isWorkletRuntime(rt)) {
if (remoteValue.expired()) {
remoteValue = getWeakRef(rt);
}
Expand All @@ -51,7 +51,7 @@ void ShareableValue::adaptCache(jsi::Runtime &rt, const jsi::Value &value) {
}

void ShareableValue::adapt(jsi::Runtime &rt, const jsi::Value &value, ValueType objectType) {
bool isRNRuntime = !(module->isUIRuntime(rt));
bool isRNRuntime = RuntimeDecorator::isReactRuntime(rt);
if (value.isObject()) {
jsi::Object object = value.asObject(rt);
jsi::Value hiddenValue = object.getProperty(rt, HIDDEN_HOST_OBJECT_PROP);
Expand All @@ -72,7 +72,7 @@ void ShareableValue::adapt(jsi::Runtime &rt, const jsi::Value &value, ValueType
}
}
}

if (objectType == ValueType::MutableValueType) {
type = ValueType::MutableValueType;
valueContainer = std::make_unique<MutableValueWrapper>(
Expand All @@ -98,7 +98,7 @@ void ShareableValue::adapt(jsi::Runtime &rt, const jsi::Value &value, ValueType
// not a worklet, we treat this as a host function
type = ValueType::HostFunctionType;
containsHostFunction = true;

//Check if it's a hostFunction wrapper
jsi::Value primalFunction = object.getProperty(rt, PRIMAL_FUNCTION);
if (!primalFunction.isUndefined()) {
Expand All @@ -109,11 +109,11 @@ void ShareableValue::adapt(jsi::Runtime &rt, const jsi::Value &value, ValueType
valueContainer = std::make_unique<HostFunctionWrapper>(
std::make_shared<HostFunctionHandler>(std::make_shared<jsi::Function>(object.asFunction(rt)), rt));
}

} else {
// a worklet
type = ValueType::WorkletFunctionType;

valueContainer = std::make_unique<FrozenObjectWrapper>(std::make_shared<FrozenObject>(rt, object, module));
auto& frozenObject = ValueWrapper::asFrozenObject(valueContainer);
containsHostFunction |= frozenObject->containsHostFunction;
Expand Down Expand Up @@ -177,12 +177,12 @@ std::shared_ptr<ShareableValue> ShareableValue::adapt(jsi::Runtime &rt, const js

jsi::Value ShareableValue::getValue(jsi::Runtime &rt) {
// TODO: maybe we can cache toJSValue results on a per-runtime basis, need to avoid ref loops
if (module->isUIRuntime(rt)) {
if (RuntimeDecorator::isWorkletRuntime(rt)) {
if (remoteValue.expired()) {
auto ref = getWeakRef(rt);
remoteValue = ref;
}

if (remoteValue.lock()->isUndefined()) {
(*remoteValue.lock()) = jsi::Value(rt, toJSValue(rt));
}
Expand Down Expand Up @@ -239,8 +239,8 @@ jsi::Value ShareableValue::toJSValue(jsi::Runtime &rt) {
}
case ValueType::RemoteObjectType: {
auto& remoteObject = ValueWrapper::asRemoteObject(valueContainer);
if (module->isUIRuntime(rt)) {
remoteObject->maybeInitializeOnUIRuntime(rt);
if (RuntimeDecorator::isWorkletRuntime(rt)) {
remoteObject->maybeInitializeOnWorkletRuntime(rt);
}
return createHost(rt, remoteObject);
}
Expand All @@ -253,22 +253,22 @@ jsi::Value ShareableValue::toJSValue(jsi::Runtime &rt) {
auto& hostRuntime = hostFunctionWrapper->value->hostRuntime;
if (hostRuntime == &rt) {
// function is accessed from the same runtime it was crated, we just return same function obj

return jsi::Value(rt, *hostFunctionWrapper->value->getPureFunction().get());
} else {
// function is accessed from a different runtime, we wrap function in host func that'd enqueue
// call on an appropriate thread

auto module = this->module;
auto hostFunction = hostFunctionWrapper->value;

auto warnFunction = [module, hostFunction](
jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count
) -> jsi::Value {

jsi::Value jsThis = rt.global().getProperty(rt, "jsThis");
std::string workletLocation = jsThis.asObject(rt).getProperty(rt, "__location").toString(rt).utf8(rt);
std::string exceptionMessage = "Tried to synchronously call ";
Expand All @@ -282,10 +282,10 @@ jsi::Value ShareableValue::toJSValue(jsi::Runtime &rt) {
exceptionMessage += CALLBACK_ERROR_SUFFIX;
module->errorHandler->setError(exceptionMessage);
module->errorHandler->raise();

return jsi::Value::undefined();
};

auto clb = [module, hostFunction, hostRuntime](
jsi::Runtime &rt,
const jsi::Value &thisValue,
Expand All @@ -294,26 +294,26 @@ jsi::Value ShareableValue::toJSValue(jsi::Runtime &rt) {
) -> jsi::Value {
// TODO: we should find thread based on runtime such that we could also call UI methods
// from RN and not only RN methods from UI

std::vector<std::shared_ptr<ShareableValue>> params;
for (int i = 0; i < count; ++i) {
params.push_back(ShareableValue::adapt(rt, args[i], module));
}

std::function<void()> job = [hostFunction, hostRuntime, params] {
jsi::Value * args = new jsi::Value[params.size()];
for (int i = 0; i < params.size(); ++i) {
args[i] = params[i]->getValue(*hostRuntime);
}

jsi::Value returnedValue = hostFunction->getPureFunction().get()->call(*hostRuntime,
static_cast<const jsi::Value*>(args),
(size_t)params.size());

delete [] args;
// ToDo use returned value to return promise
};

module->scheduler->scheduleOnJS(job);
return jsi::Value::undefined();
};
Expand All @@ -328,7 +328,7 @@ jsi::Value ShareableValue::toJSValue(jsi::Runtime &rt) {
case ValueType::WorkletFunctionType: {
auto module = this->module;
auto& frozenObject = ValueWrapper::asFrozenObject(this->valueContainer);
if (module->isUIRuntime(rt)) {
if (RuntimeDecorator::isWorkletRuntime(rt)) {
// when running on UI thread we prep a function

auto jsThis = std::make_shared<jsi::Object>(frozenObject->shallowClone(*module->runtime));
Expand Down Expand Up @@ -383,27 +383,27 @@ jsi::Value ShareableValue::toJSValue(jsi::Runtime &rt) {
for (int i = 0; i < count; ++i) {
params.push_back(ShareableValue::adapt(rt, args[i], module));
}

module->scheduler->scheduleOnUI([=] {
jsi::Runtime &rt = *module->runtime.get();
auto jsThis = createFrozenWrapper(rt, frozenObject).getObject(rt);
auto code = jsThis.getProperty(rt, "asString").asString(rt).utf8(rt);
std::shared_ptr<jsi::Function> funPtr(module->workletsCache->getFunction(rt, frozenObject));

jsi::Value * args = new jsi::Value[params.size()];
for (int i = 0; i < params.size(); ++i) {
args[i] = params[i]->getValue(rt);
}

jsi::Value returnedValue;

jsi::Value oldJSThis = rt.global().getProperty(rt, "jsThis");
rt.global().setProperty(rt, "jsThis", jsThis); //set jsThis
try {
returnedValue = funPtr->call(rt,
static_cast<const jsi::Value*>(args),
(size_t)params.size());

} catch(std::exception &e) {
std::string str = e.what();
module->errorHandler->setError(str);
Expand All @@ -419,7 +419,7 @@ jsi::Value ShareableValue::toJSValue(jsi::Runtime &rt) {
module->errorHandler->raise();
}
rt.global().setProperty(rt, "jsThis", oldJSThis); //clean jsThis

delete [] args;
// ToDo use returned value to return promise
});
Expand Down
Loading