Move some classes to new bridging library (#33413)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/33413

This moves `CallbackWrapper` and `LongLivedObject` into a new "bridging" library. This library is mostly intended for use by the native module system, but can also be used separately to "bridge" native and JS interfaces through higher-level (and safer) abstractions than relying JSI alone.

Changelog:
Internal

Reviewed By: christophpurrer

Differential Revision: D34723341

fbshipit-source-id: 7ca8fa815537152f8163920513b90313540477e3
This commit is contained in:
Scott Kyle
2022-03-11 12:47:51 -08:00
committed by Facebook GitHub Bot
parent b676ca560d
commit 6a9497dbbb
14 changed files with 268 additions and 134 deletions
+1
View File
@@ -82,6 +82,7 @@ LOCAL_MODULE := react_nativemodule_core
LOCAL_SRC_FILES := $(REACT_NDK_EXPORT_DIR)/$(TARGET_ARCH_ABI)/libreact_nativemodule_core.so
LOCAL_EXPORT_C_INCLUDES := \
$(REACT_ANDROID_SRC_DIR)/jni \
$(REACT_COMMON_DIR) \
$(REACT_COMMON_DIR)/callinvoker \
$(REACT_COMMON_DIR)/jsi \
$(REACT_COMMON_DIR)/react/nativemodule/core \
+2 -1
View File
@@ -32,13 +32,14 @@ Pod::Spec.new do |s|
s.source = source
s.header_dir = "ReactCommon" # Use global header_dir for all subspecs for use_frameworks! compatibility
s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags
s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/React-Core\"",
s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/React-Core\" \"$(PODS_ROOT)/Headers/Private/React-bridging\"",
"USE_HEADERMAP" => "YES",
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17" }
# TODO (T48588859): Restructure this target to align with dir structure: "react/nativemodule/..."
# Note: Update this only when ready to minimize breaking changes.
s.subspec "turbomodule" do |ss|
ss.dependency "React-bridging", version
ss.dependency "React-callinvoker", version
ss.dependency "React-perflogger", version
ss.dependency "React-Core", version
+27
View File
@@ -0,0 +1,27 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := react_bridging
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp)
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../
LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall
LOCAL_STATIC_LIBRARIES :=
LOCAL_SHARED_LIBRARIES := jsi
include $(BUILD_STATIC_LIBRARY)
$(call import-module,butter)
$(call import-module,folly)
$(call import-module,jsi)
+19
View File
@@ -0,0 +1,19 @@
load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "APPLE", "CXX", "react_native_xplat_shared_library_target", "react_native_xplat_target", "rn_xplat_cxx_library")
rn_xplat_cxx_library(
name = "bridging",
srcs = glob(["*.cpp"]),
header_namespace = "react/bridging",
exported_headers = glob(["*.h"]),
labels = ["supermodule:xplat/default/public.react_native.infra"],
platforms = (ANDROID, APPLE, CXX),
visibility = ["PUBLIC"],
deps = [
"//xplat/folly:headers_only",
],
exported_deps = [
react_native_xplat_target("butter:butter"),
react_native_xplat_target("callinvoker:callinvoker"),
react_native_xplat_shared_library_target("jsi:jsi"),
],
)
@@ -0,0 +1,99 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <jsi/jsi.h>
#include <react/bridging/LongLivedObject.h>
#include <memory>
namespace facebook {
namespace react {
// Helper for passing jsi::Function arg to other methods.
class CallbackWrapper : public LongLivedObject {
private:
CallbackWrapper(
jsi::Function &&callback,
jsi::Runtime &runtime,
std::shared_ptr<CallInvoker> jsInvoker)
: longLivedObjectCollection_(),
callback_(std::move(callback)),
runtime_(runtime),
jsInvoker_(std::move(jsInvoker)) {}
CallbackWrapper(
std::shared_ptr<LongLivedObjectCollection> longLivedObjectCollection,
jsi::Function &&callback,
jsi::Runtime &runtime,
std::shared_ptr<CallInvoker> jsInvoker)
: longLivedObjectCollection_(longLivedObjectCollection),
callback_(std::move(callback)),
runtime_(runtime),
jsInvoker_(std::move(jsInvoker)) {}
// Use a weak_ptr to avoid a retain cycle: LongLivedObjectCollection owns all
// CallbackWrappers. So, CallbackWrapper cannot own its
// LongLivedObjectCollection.
std::weak_ptr<LongLivedObjectCollection> longLivedObjectCollection_;
jsi::Function callback_;
jsi::Runtime &runtime_;
std::shared_ptr<CallInvoker> jsInvoker_;
public:
static std::weak_ptr<CallbackWrapper> createWeak(
jsi::Function &&callback,
jsi::Runtime &runtime,
std::shared_ptr<CallInvoker> jsInvoker) {
auto wrapper = std::shared_ptr<CallbackWrapper>(
new CallbackWrapper(std::move(callback), runtime, jsInvoker));
LongLivedObjectCollection::get().add(wrapper);
return wrapper;
}
static std::weak_ptr<CallbackWrapper> createWeak(
std::shared_ptr<LongLivedObjectCollection> longLivedObjectCollection,
jsi::Function &&callback,
jsi::Runtime &runtime,
std::shared_ptr<CallInvoker> jsInvoker) {
auto wrapper = std::shared_ptr<CallbackWrapper>(new CallbackWrapper(
longLivedObjectCollection, std::move(callback), runtime, jsInvoker));
longLivedObjectCollection->add(wrapper);
return wrapper;
}
// Delete the enclosed jsi::Function
void destroy() {
allowRelease();
}
jsi::Function &callback() {
return callback_;
}
jsi::Runtime &runtime() {
return runtime_;
}
CallInvoker &jsInvoker() {
return *(jsInvoker_);
}
void allowRelease() override {
if (auto longLivedObjectCollection = longLivedObjectCollection_.lock()) {
if (longLivedObjectCollection != nullptr) {
longLivedObjectCollection->remove(this);
return;
}
}
LongLivedObject::allowRelease();
}
};
} // namespace react
} // namespace facebook
@@ -0,0 +1,57 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <memory>
#include <mutex>
#include <unordered_set>
namespace facebook {
namespace react {
/**
* A simple wrapper class that can be registered to a collection that keep it
* alive for extended period of time. This object can be removed from the
* collection when needed.
*
* The subclass of this class must be created using std::make_shared<T>().
* After creation, add it to the `LongLivedObjectCollection`.
* When done with the object, call `allowRelease()` to allow the OS to release
* it.
*/
class LongLivedObject {
public:
virtual void allowRelease();
protected:
LongLivedObject();
virtual ~LongLivedObject();
};
/**
* A singleton, thread-safe, write-only collection for the `LongLivedObject`s.
*/
class LongLivedObjectCollection {
public:
static LongLivedObjectCollection &get();
LongLivedObjectCollection();
LongLivedObjectCollection(LongLivedObjectCollection const &) = delete;
void operator=(LongLivedObjectCollection const &) = delete;
void add(std::shared_ptr<LongLivedObject> o) const;
void remove(const LongLivedObject *o) const;
void clear() const;
private:
mutable std::unordered_set<std::shared_ptr<LongLivedObject>> collection_;
mutable std::mutex collectionMutex_;
};
} // namespace react
} // namespace facebook
@@ -0,0 +1,41 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
require "json"
package = JSON.parse(File.read(File.join(__dir__, "..", "..", "..", "package.json")))
version = package['version']
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which were presumably in.
source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1")
else
source[:tag] = "v#{version}"
end
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
folly_version = '2021.06.28.00-v2'
Pod::Spec.new do |s|
s.name = "React-bridging"
s.version = version
s.summary = "-" # TODO
s.homepage = "https://reactnative.dev/"
s.license = package["license"]
s.author = "Facebook, Inc. and its affiliates"
s.platforms = { :ios => "11.0" }
s.source = source
s.source_files = "**/*.{cpp,h}"
s.exclude_files = "tests"
s.header_dir = "react/bridging"
s.compiler_flags = folly_compiler_flags
s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/RCT-Folly\"",
"USE_HEADERMAP" => "YES",
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17" }
s.dependency "RCT-Folly", folly_version
s.dependency "React-jsi", version
end
@@ -9,11 +9,11 @@ include $(CLEAR_VARS)
LOCAL_MODULE := react_nativemodule_core
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../ $(LOCAL_PATH)/ReactCommon $(LOCAL_PATH)/platform/android/ReactCommon
LOCAL_C_INCLUDES := $(LOCAL_PATH)/ReactCommon $(LOCAL_PATH)/platform/android/ReactCommon
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/ReactCommon/*.cpp) $(wildcard $(LOCAL_PATH)/platform/android/ReactCommon/*.cpp)
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/platform/android/
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../ $(LOCAL_PATH) $(LOCAL_PATH)/platform/android/
LOCAL_SHARED_LIBRARIES := \
libfbjni \
@@ -22,7 +22,9 @@ LOCAL_SHARED_LIBRARIES := \
libreact_debug \
libreactnativejni
LOCAL_STATIC_LIBRARIES := libreactperflogger
LOCAL_STATIC_LIBRARIES := \
libreact_bridging \
libreactperflogger
LOCAL_CFLAGS := \
-DLOG_TAG=\"ReactNative\"
@@ -34,3 +36,4 @@ include $(BUILD_SHARED_LIBRARY)
$(call import-module,folly)
$(call import-module,jsi)
$(call import-module,reactperflogger)
$(call import-module,react/bridging)
+1
View File
@@ -78,6 +78,7 @@ rn_xplat_cxx_library(
react_native_xplat_target("reactperflogger:reactperflogger"),
],
exported_deps = [
react_native_xplat_target("react/bridging:bridging"),
react_native_xplat_shared_library_target("jsi:jsi"),
],
)
@@ -7,51 +7,5 @@
#pragma once
#include <memory>
#include <mutex>
#include <unordered_set>
namespace facebook {
namespace react {
/**
* A simple wrapper class that can be registered to a collection that keep it
* alive for extended period of time. This object can be removed from the
* collection when needed.
*
* The subclass of this class must be created using std::make_shared<T>().
* After creation, add it to the `LongLivedObjectCollection`.
* When done with the object, call `allowRelease()` to allow the OS to release
* it.
*/
class LongLivedObject {
public:
virtual void allowRelease();
protected:
LongLivedObject();
virtual ~LongLivedObject();
};
/**
* A singleton, thread-safe, write-only collection for the `LongLivedObject`s.
*/
class LongLivedObjectCollection {
public:
static LongLivedObjectCollection &get();
LongLivedObjectCollection();
LongLivedObjectCollection(LongLivedObjectCollection const &) = delete;
void operator=(LongLivedObjectCollection const &) = delete;
void add(std::shared_ptr<LongLivedObject> o) const;
void remove(const LongLivedObject *o) const;
void clear() const;
private:
mutable std::unordered_set<std::shared_ptr<LongLivedObject>> collection_;
mutable std::mutex collectionMutex_;
};
} // namespace react
} // namespace facebook
// This header is left here for compatibility reasons.
#include <react/bridging/LongLivedObject.h>
@@ -13,7 +13,8 @@
#include <jsi/jsi.h>
#include <ReactCommon/CallInvoker.h>
#include <ReactCommon/LongLivedObject.h>
#include <react/bridging/CallbackWrapper.h>
#include <react/bridging/LongLivedObject.h>
namespace facebook {
namespace react {
@@ -38,86 +39,6 @@ jsi::Value createPromiseAsJSIValue(
jsi::Runtime &rt,
const PromiseSetupFunctionType func);
// Helper for passing jsi::Function arg to other methods.
class CallbackWrapper : public LongLivedObject {
private:
CallbackWrapper(
jsi::Function &&callback,
jsi::Runtime &runtime,
std::shared_ptr<CallInvoker> jsInvoker)
: longLivedObjectCollection_(),
callback_(std::move(callback)),
runtime_(runtime),
jsInvoker_(std::move(jsInvoker)) {}
CallbackWrapper(
std::shared_ptr<LongLivedObjectCollection> longLivedObjectCollection,
jsi::Function &&callback,
jsi::Runtime &runtime,
std::shared_ptr<CallInvoker> jsInvoker)
: longLivedObjectCollection_(longLivedObjectCollection),
callback_(std::move(callback)),
runtime_(runtime),
jsInvoker_(std::move(jsInvoker)) {}
// Use a weak_ptr to avoid a retain cycle: LongLivedObjectCollection owns all
// CallbackWrappers. So, CallbackWrapper cannot own its
// LongLivedObjectCollection.
std::weak_ptr<LongLivedObjectCollection> longLivedObjectCollection_;
jsi::Function callback_;
jsi::Runtime &runtime_;
std::shared_ptr<CallInvoker> jsInvoker_;
public:
static std::weak_ptr<CallbackWrapper> createWeak(
jsi::Function &&callback,
jsi::Runtime &runtime,
std::shared_ptr<CallInvoker> jsInvoker) {
auto wrapper = std::shared_ptr<CallbackWrapper>(
new CallbackWrapper(std::move(callback), runtime, jsInvoker));
LongLivedObjectCollection::get().add(wrapper);
return wrapper;
}
static std::weak_ptr<CallbackWrapper> createWeak(
std::shared_ptr<LongLivedObjectCollection> longLivedObjectCollection,
jsi::Function &&callback,
jsi::Runtime &runtime,
std::shared_ptr<CallInvoker> jsInvoker) {
auto wrapper = std::shared_ptr<CallbackWrapper>(new CallbackWrapper(
longLivedObjectCollection, std::move(callback), runtime, jsInvoker));
longLivedObjectCollection->add(wrapper);
return wrapper;
}
// Delete the enclosed jsi::Function
void destroy() {
allowRelease();
}
jsi::Function &callback() {
return callback_;
}
jsi::Runtime &runtime() {
return runtime_;
}
CallInvoker &jsInvoker() {
return *(jsInvoker_);
}
void allowRelease() override {
if (auto longLivedObjectCollection = longLivedObjectCollection_.lock()) {
if (longLivedObjectCollection != nullptr) {
longLivedObjectCollection->remove(this);
return;
}
}
LongLivedObject::allowRelease();
}
};
class RAIICallbackWrapperDestroyer {
public:
RAIICallbackWrapperDestroyer(std::weak_ptr<CallbackWrapper> callbackWrapper)
+10 -1
View File
@@ -110,6 +110,9 @@ PODS:
- React-RCTSettings (= 1000.0.0)
- React-RCTText (= 1000.0.0)
- React-RCTVibration (= 1000.0.0)
- React-bridging (1000.0.0):
- RCT-Folly (= 2021.06.28.00-v2)
- React-jsi (= 1000.0.0)
- React-callinvoker (1000.0.0)
- React-Codegen (1000.0.0):
- FBReactNativeSpec (= 1000.0.0)
@@ -691,6 +694,7 @@ PODS:
- DoubleConversion
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-bridging (= 1000.0.0)
- React-callinvoker (= 1000.0.0)
- React-Core (= 1000.0.0)
- React-cxxreact (= 1000.0.0)
@@ -701,6 +705,7 @@ PODS:
- DoubleConversion
- glog
- RCT-Folly (= 2021.06.28.00-v2)
- React-bridging (= 1000.0.0)
- React-callinvoker (= 1000.0.0)
- React-Core (= 1000.0.0)
- React-cxxreact (= 1000.0.0)
@@ -749,6 +754,7 @@ DEPENDENCIES:
- RCTRequired (from `../../Libraries/RCTRequired`)
- RCTTypeSafety (from `../../Libraries/TypeSafety`)
- React (from `../../`)
- React-bridging (from `../../ReactCommon/react/bridging`)
- React-callinvoker (from `../../ReactCommon/callinvoker`)
- React-Codegen (from `build/generated/ios`)
- React-Core (from `../../`)
@@ -820,6 +826,8 @@ EXTERNAL SOURCES:
:path: "../../Libraries/TypeSafety"
React:
:path: "../../"
React-bridging:
:path: "../../ReactCommon/react/bridging"
React-callinvoker:
:path: "../../ReactCommon/callinvoker"
React-Codegen:
@@ -902,6 +910,7 @@ SPEC CHECKSUMS:
RCTRequired: af2d6080a4b9ba0885b28ca78879a92066c71cab
RCTTypeSafety: 2d6c5149fd3b8f2ba89987b47169423efb740300
React: f64c9f6db5428717922a3292ba6a448615a2e143
React-bridging: a86401f93edb5564d437595060e55306c877462d
React-callinvoker: c5d61e29df57793f0dc10ec2bc01c846f863e51f
React-Codegen: c4ae835565487e494740b756ce953d8f635a76df
React-Core: 30784d0eacbd709a3a6a5ae7c02c928634a6247e
@@ -928,7 +937,7 @@ SPEC CHECKSUMS:
React-RCTVibration: 9a73a3550a0ee184530341f91f9b85ad9cf3465a
React-rncore: b6e9b49eb1c7c045b98c77b1f94932e3c0be7cec
React-runtimeexecutor: 4b0c6eb341c7d3ceb5e2385cb0fdb9bf701024f3
ReactCommon: 9607f2246289d573adcbd144c02c19ae5246d6a5
ReactCommon: f5fd5011a78235afb572a4618bd04a672b41da9d
ScreenshotManager: 8a08e488cb533b83ebe069ad6109d9c1df9cea79
SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
Yoga: faebdb8710c7e742c6ab99e14ed4270b8da27f1f
+1
View File
@@ -63,6 +63,7 @@ def use_react_native! (options={})
pod 'React-Core/DevSupport', :path => "#{prefix}/"
end
pod 'React-bridging', :path => "#{prefix}/ReactCommon/react/bridging"
pod 'React-cxxreact', :path => "#{prefix}/ReactCommon/cxxreact"
pod 'React-jsi', :path => "#{prefix}/ReactCommon/jsi"
pod 'React-jsiexecutor', :path => "#{prefix}/ReactCommon/jsiexecutor"