This commit is contained in:
Yamozha
2021-04-02 02:24:13 +03:00
parent c23950b545
commit 7256d79e2c
31493 changed files with 3036630 additions and 0 deletions

View File

@ -0,0 +1,220 @@
# Copyright (c) Facebook, Inc. and its 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
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 = '2020.01.13.00'
folly_dep_name = 'Folly/Fabric'
boost_compiler_flags = '-Wno-documentation'
Pod::Spec.new do |s|
s.name = "React-Fabric"
s.version = version
s.summary = "Fabric for React Native."
s.homepage = "https://reactnative.dev/"
s.license = package["license"]
s.author = "Facebook, Inc. and its affiliates"
s.platforms = { :ios => "10.0", :tvos => "10.0" }
s.source = source
s.prepare_command = File.read("../scripts/generate-rncore.sh")
s.source_files = "dummyFile.cpp"
s.library = "stdc++"
s.pod_target_xcconfig = { "USE_HEADERMAP" => "YES",
"CLANG_CXX_LANGUAGE_STANDARD" => "c++14" }
s.dependency folly_dep_name, folly_version
s.dependency "React-graphics", version
s.dependency "React-jsiexecutor", version
s.dependency "RCTRequired", version
s.dependency "RCTTypeSafety", version
s.dependency "ReactCommon/turbomodule/core", version
s.dependency "React-jsi", version
s.subspec "attributedstring" do |ss|
ss.dependency folly_dep_name, folly_version
ss.compiler_flags = folly_compiler_flags
ss.source_files = "fabric/attributedstring/**/*.{m,mm,cpp,h}"
ss.exclude_files = "**/tests/*"
ss.header_dir = "react/attributedstring"
ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" }
end
s.subspec "better" do |ss|
ss.dependency folly_dep_name, folly_version
ss.compiler_flags = folly_compiler_flags
ss.source_files = "better/**/*.{m,mm,cpp,h}"
ss.exclude_files = "**/tests/*"
ss.header_dir = "better"
ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" }
end
s.subspec "config" do |ss|
ss.source_files = "config/*.{m,mm,cpp,h}"
ss.header_dir = "react/config"
ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\"" }
end
s.subspec "core" do |ss|
ss.dependency folly_dep_name, folly_version
ss.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags
ss.source_files = "fabric/core/**/*.{m,mm,cpp,h}"
ss.exclude_files = "**/tests/**/*"
ss.header_dir = "react/core"
ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" }
end
s.subspec "components" do |ss|
ss.subspec "activityindicator" do |sss|
sss.dependency folly_dep_name, folly_version
sss.compiler_flags = folly_compiler_flags
sss.source_files = "fabric/components/activityindicator/**/*.{m,mm,cpp,h}"
sss.exclude_files = "**/tests/*"
sss.header_dir = "react/components/activityindicator"
sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" }
end
ss.subspec "image" do |sss|
sss.dependency folly_dep_name, folly_version
sss.compiler_flags = folly_compiler_flags
sss.source_files = "fabric/components/image/**/*.{m,mm,cpp,h}"
sss.exclude_files = "**/tests/*"
sss.header_dir = "react/components/image"
sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" }
end
ss.subspec "modal" do |sss|
sss.dependency folly_dep_name, folly_version
sss.compiler_flags = folly_compiler_flags
sss.source_files = "fabric/components/modal/**/*.{m,mm,cpp,h}"
sss.exclude_files = "**/tests/*"
sss.header_dir = "react/components/modal"
sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" }
end
ss.subspec "rncore" do |sss|
sss.dependency folly_dep_name, folly_version
sss.compiler_flags = folly_compiler_flags
sss.source_files = "fabric/components/rncore/*.{m,mm,cpp,h}"
sss.exclude_files = "**/tests/*", "fabric/components/rncore/*Tests.{h,cpp}"
sss.header_dir = "react/components/rncore"
sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" }
end
ss.subspec "root" do |sss|
sss.dependency folly_dep_name, folly_version
sss.compiler_flags = folly_compiler_flags
sss.source_files = "fabric/components/root/**/*.{m,mm,cpp,h}"
sss.exclude_files = "**/tests/*"
sss.header_dir = "react/components/root"
sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" }
end
ss.subspec "scrollview" do |sss|
sss.dependency folly_dep_name, folly_version
sss.compiler_flags = folly_compiler_flags
sss.source_files = "fabric/components/scrollview/**/*.{m,mm,cpp,h}"
sss.exclude_files = "**/tests/*"
sss.header_dir = "react/components/scrollview"
sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" }
end
ss.subspec "slider" do |sss|
sss.dependency folly_dep_name, folly_version
sss.compiler_flags = folly_compiler_flags
sss.source_files = "fabric/components/slider/**/*.{m,mm,cpp,h}"
sss.exclude_files = "**/tests/*",
"**/android/*"
sss.header_dir = "react/components/slider"
sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" }
end
ss.subspec "text" do |sss|
sss.dependency folly_dep_name, folly_version
sss.compiler_flags = folly_compiler_flags
sss.source_files = "fabric/components/text/**/*.{m,mm,cpp,h}"
sss.exclude_files = "**/tests/*"
sss.header_dir = "react/components/text"
sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" }
end
ss.subspec "view" do |sss|
sss.dependency folly_dep_name, folly_version
sss.dependency "Yoga"
sss.compiler_flags = folly_compiler_flags
sss.source_files = "fabric/components/view/**/*.{m,mm,cpp,h}"
sss.exclude_files = "**/tests/*"
sss.header_dir = "react/components/view"
sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" }
end
end
s.subspec "debug" do |ss|
ss.dependency folly_dep_name, folly_version
ss.compiler_flags = folly_compiler_flags
ss.source_files = "fabric/debug/**/*.{m,mm,cpp,h}"
ss.exclude_files = "**/tests/*"
ss.header_dir = "react/debug"
ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" }
end
s.subspec "imagemanager" do |ss|
ss.dependency "React-RCTImage", version
ss.dependency folly_dep_name, folly_version
ss.compiler_flags = folly_compiler_flags
ss.source_files = "fabric/imagemanager/**/*.{m,mm,cpp,h}"
ss.exclude_files = "**/tests/*",
"**/android/*",
"**/cxx/*"
ss.header_dir = "react/imagemanager"
ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" }
end
s.subspec "mounting" do |ss|
ss.dependency folly_dep_name, folly_version
ss.compiler_flags = folly_compiler_flags
ss.source_files = "fabric/mounting/**/*.{m,mm,cpp,h}"
ss.exclude_files = "**/tests/*"
ss.header_dir = "react/mounting"
ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" }
end
s.subspec "textlayoutmanager" do |ss|
ss.dependency folly_dep_name, folly_version
ss.compiler_flags = folly_compiler_flags
ss.source_files = "fabric/textlayoutmanager/**/*.{m,mm,cpp,h}"
ss.exclude_files = "**/tests/*",
"**/android/*",
"**/cxx/*"
ss.header_dir = "react/textlayoutmanager"
ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" }
end
s.subspec "uimanager" do |ss|
ss.dependency folly_dep_name, folly_version
ss.compiler_flags = folly_compiler_flags
ss.source_files = "fabric/uimanager/**/*.{m,mm,cpp,h}"
ss.exclude_files = "**/tests/*",
ss.header_dir = "react/uimanager"
ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" }
end
s.subspec "utils" do |ss|
ss.source_files = "utils/*.{m,mm,cpp,h}"
ss.header_dir = "react/utils"
ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" }
end
end

View File

@ -0,0 +1,59 @@
# Copyright (c) Facebook, Inc. and its 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
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 = '2020.01.13.00'
boost_compiler_flags = '-Wno-documentation'
Pod::Spec.new do |s|
s.name = "ReactCommon"
s.module_name = "ReactCommon"
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 => "10.0", :tvos => "10.0" }
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-for-react-native\" \"$(PODS_ROOT)/Folly\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/React-Core\"",
"USE_HEADERMAP" => "YES",
"CLANG_CXX_LANGUAGE_STANDARD" => "c++14" }
s.subspec "turbomodule" do |ss|
ss.dependency "React-callinvoker", version
ss.dependency "React-Core", version
ss.dependency "React-cxxreact", version
ss.dependency "React-jsi", version
ss.dependency "Folly", folly_version
ss.dependency "DoubleConversion"
ss.dependency "glog"
ss.subspec "core" do |sss|
sss.source_files = "turbomodule/core/*.{cpp,h}",
"turbomodule/core/platform/ios/*.{mm,cpp,h}"
end
ss.subspec "samples" do |sss|
sss.source_files = "turbomodule/samples/*.{cpp,h}",
"turbomodule/samples/platform/ios/*.{mm,cpp,h}"
sss.dependency "ReactCommon/turbomodule/core", version
end
end
end

View File

@ -0,0 +1,5 @@
---
Checks: '>
clang-diagnostic-*,
'
...

57
node_modules/react-native/ReactCommon/better/BUCK generated vendored Normal file
View File

@ -0,0 +1,57 @@
load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode")
load(
"//tools/build_defs/oss:rn_defs.bzl",
"ANDROID",
"APPLE",
"CXX",
"get_apple_compiler_flags",
"get_apple_inspector_flags",
"rn_xplat_cxx_library",
"subdir_glob",
)
APPLE_COMPILER_FLAGS = get_apple_compiler_flags()
rn_xplat_cxx_library(
name = "better",
srcs = glob(
["**/*.cpp"],
exclude = glob(["tests/**/*.cpp"]),
),
headers = glob(
["**/*.h"],
exclude = glob(["tests/**/*.h"]),
),
header_namespace = "",
exported_headers = subdir_glob(
[
("", "*.h"),
],
prefix = "better",
),
compiler_flags = [
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wall",
],
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"],
fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(),
force_static = True,
macosx_tests_override = [],
platforms = (ANDROID, APPLE, CXX),
preprocessor_flags = [
"-DLOG_TAG=\"ReactNative\"",
"-DWITH_FBSYSTRACE=1",
],
tests = [],
visibility = ["PUBLIC"],
deps = [
"//xplat/fbsystrace:fbsystrace",
"//xplat/folly:headers_only",
"//xplat/folly:memory",
"//xplat/folly:molly",
"//xplat/third-party/glog:glog",
],
)

62
node_modules/react-native/ReactCommon/better/better.h generated vendored Normal file
View File

@ -0,0 +1,62 @@
/*
* Copyright (c) Facebook, Inc. and its 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
namespace facebook {
namespace better {
/*
* `Better` is a trivial collection of basic tools borrowed from other low-level
* general purpose libraries (like Folly, Abseil or Boost). The main goals of
* Better:
* - Make the codebase more portable;
* - Make the dependency list explicit (by decoupling it as a dependency list of
* Better);
* - Make relying on modern C++ patterns and tools in code simple and easy.
* - Make executing experiments with different dependencies easier.
*
* What should be part of Better and what should not? Should I add some piece of
* functionality in the Better? Here is a quick checklist.
*
* If one of the following is true, yes, go for it:
* - If some feature is already in some future C++ standard (possibly in draft
* stage) and it's already implemented in some 3rd party library.
* - If some standardized feature of C++ is implemented in the standard not in
* the most efficient way (because the standard enforces some tricky constraints
* (like always-valid iterators) which nobody uses and should use), but you have
* a library that does it right providing exact same interface.
*
* If one of the following is true, please do *NOT* do it (at least as part of
* the library):
* - You want to use some very fancy pattern that your favorite library (but
* nothing else) provides, and You want to make this pattern very command in the
* code base. Your hope is that this pattern will conquer the world and be
* a part of the C++ standard eventually.
* - You favorite library provides some general purpose container that 10x times
* faster than the standard one, so You want to use that in the code base. That
* container does not have compatible API though (because it's a clear trade-off
* with efficiency, of course).
*/
/*
* Configuration
*/
/*
* Enables using Folly containers instead of standard ones (such as map, vector,
* small_vector, optional and etc.)
* Custom containers are only enabled in release mode. Using custom stuff
* complicates debugging process because it breaks embedded into IDE
* introspections mechanisms.
*/
#ifndef DEBUG
#define BETTER_USE_FOLLY_CONTAINERS
#endif
} // namespace better
} // namespace facebook

45
node_modules/react-native/ReactCommon/better/map.h generated vendored Normal file
View File

@ -0,0 +1,45 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <better/better.h>
#ifdef BETTER_USE_FOLLY_CONTAINERS
#include <folly/container/F14Map.h>
#else
#include <unordered_map>
#endif
namespace facebook {
namespace better {
/*
* Note: In Better, `map` aliases to `unorderd_map` because everyone agrees that
* an *ordered* map is nonsense and was a huge mistake for standardization. If
* you need an *ordered* map, feel free to introduce that as
* `better::ordered_map`.
*/
#ifdef BETTER_USE_FOLLY_CONTAINERS
template <typename... Ts>
using map = folly::F14FastMap<Ts...>;
#else
template <typename... Ts>
using map = std::unordered_map<Ts...>;
#endif
} // namespace better
} // namespace facebook

20
node_modules/react-native/ReactCommon/better/mutex.h generated vendored Normal file
View File

@ -0,0 +1,20 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <folly/SharedMutex.h>
#include <mutex>
#include <shared_mutex>
namespace facebook {
namespace better {
using shared_mutex = folly::SharedMutex;
} // namespace better
} // namespace facebook

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <better/better.h>
#if defined(BETTER_USE_FOLLY_CONTAINERS) || __cplusplus < 202000L
#include <folly/Optional.h>
#else
#include <optional>
#endif
namespace facebook {
namespace better {
#if defined(BETTER_USE_FOLLY_CONTAINERS) || __cplusplus < 202000L
template <typename Value>
using optional = folly::Optional<Value>;
#else
template <typename Value>
using optional = std::optional<Value>;
#endif
} // namespace better
} // namespace facebook

38
node_modules/react-native/ReactCommon/better/set.h generated vendored Normal file
View File

@ -0,0 +1,38 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <better/better.h>
#ifdef BETTER_USE_FOLLY_CONTAINERS
#include <folly/container/F14Set.h>
#else
#include <unordered_set>
#endif
namespace facebook {
namespace better {
#ifdef BETTER_USE_FOLLY_CONTAINERS
template <typename... Ts>
using set = folly::F14FastSet<Ts...>;
#else
template <typename... Ts>
using set = std::unordered_set<Ts...>;
#endif
} // namespace better
} // namespace facebook

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <better/better.h>
// `folly::small_vector` is broken on some versions of Android.
#if defined(BETTER_USE_FOLLY_CONTAINERS) && !defined(ANDROID)
#include <folly/small_vector.h>
#else
#include <vector>
#endif
namespace facebook {
namespace better {
#if defined(BETTER_USE_FOLLY_CONTAINERS) && !defined(ANDROID)
template <typename T, std::size_t Size, typename... Ts>
using small_vector = folly::small_vector<T, Size, Ts...>;
#else
template <typename T, std::size_t Size, typename... Ts>
using small_vector = std::vector<T, Ts...>;
#endif
} // namespace better
} // namespace facebook

View File

@ -0,0 +1,5 @@
---
Checks: '>
clang-diagnostic-*,
'
...

View File

@ -0,0 +1,25 @@
# Copyright (c) Facebook, Inc. and its 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)
# Header search path for all source files in this module.
LOCAL_C_INCLUDES := $(LOCAL_PATH)/ReactCommon
# Header search path for modules that depend on this module
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall
# Name of this module.
LOCAL_MODULE := callinvoker
# Compile all local c++ files under ./ReactCommon
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/ReactCommon/*.cpp)
# Build the files in this directory as a shared library
include $(BUILD_STATIC_LIBRARY)

29
node_modules/react-native/ReactCommon/callinvoker/BUCK generated vendored Normal file
View File

@ -0,0 +1,29 @@
load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "APPLE", "rn_xplat_cxx_library", "subdir_glob")
rn_xplat_cxx_library(
name = "callinvoker",
srcs = glob(["**/*.cpp"]),
header_namespace = "",
exported_headers = subdir_glob(
[
("ReactCommon", "*.h"),
],
prefix = "ReactCommon",
),
compiler_flags = [
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wall",
],
fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"],
platforms = (ANDROID, APPLE),
preferred_linkage = "static",
preprocessor_flags = [
"-DLOG_TAG=\"ReactNative\"",
"-DWITH_FBSYSTRACE=1",
],
visibility = [
"PUBLIC",
],
)

View File

@ -0,0 +1,34 @@
# Copyright (c) Facebook, Inc. and its 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
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 = '2020.01.13.00'
boost_compiler_flags = '-Wno-documentation'
Pod::Spec.new do |s|
s.name = "React-callinvoker"
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 => "10.0", :tvos => "10.0" }
s.source = source
s.source_files = "**/*.{cpp,h}"
s.header_dir = "ReactCommon"
end

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <functional>
#include <memory>
namespace facebook {
namespace react {
/**
* An interface for a generic native-to-JS call invoker. See BridgeJSCallInvoker
* for an implementation.
*/
class CallInvoker {
public:
virtual void invokeAsync(std::function<void()> &&func) = 0;
virtual void invokeSync(std::function<void()> &&func) = 0;
virtual ~CallInvoker() {}
};
} // namespace react
} // namespace facebook

29
node_modules/react-native/ReactCommon/common.mk generated vendored Normal file
View File

@ -0,0 +1,29 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
##
# Returns the absolute path to the specified npm package, searching from the
# given base directory. This function uses Node's module resolution algorithm
# searching "node_modules" in the base directory and its ancestors. If no
# matching package is found, this function returns an empty string.
#
# The first argument to this function is the base directory from which to begin
# searching. The second argument is the name of the npm package.
#
# Ex: $(call find-node-module,$(LOCAL_PATH),hermes-engine)
###
define find-node-module
$(strip \
$(eval _base := $(strip $(1))) \
$(eval _package := $(strip $(2))) \
$(eval _candidate := $(abspath $(_base)/node_modules/$(_package))) \
$(if $(realpath $(_candidate)), \
$(_candidate), \
$(if $(_base), \
$(call find-node-module,$(patsubst %/,%,$(dir $(_base))),$(_package)) \
) \
) \
)
endef

View File

@ -0,0 +1,5 @@
---
Checks: '>
clang-diagnostic-*,
'
...

46
node_modules/react-native/ReactCommon/config/BUCK generated vendored Normal file
View File

@ -0,0 +1,46 @@
load("@fbsource//tools/build_defs:glob_defs.bzl", "subdir_glob")
load("@fbsource//tools/build_defs:platform_defs.bzl", "ANDROID", "APPLE", "CXX")
load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "flags", "get_preprocessor_flags_for_build_mode", "get_static_library_ios_flags")
load("@fbsource//tools/build_defs/oss:rn_defs.bzl", "get_apple_inspector_flags", "rn_xplat_cxx_library")
APPLE_COMPILER_FLAGS = flags.get_flag_value(
get_static_library_ios_flags(),
"compiler_flags",
)
rn_xplat_cxx_library(
name = "config",
srcs = glob(["**/*.cpp"]),
header_namespace = "",
exported_headers = subdir_glob(
[
("", "**/*.h"),
],
prefix = "react/config",
),
compiler_flags = [
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wall",
],
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"],
fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(),
force_static = True,
platforms = (ANDROID, APPLE, CXX),
preprocessor_flags = [
"-DLOG_TAG=\"ReactNative\"",
"-DWITH_FBSYSTRACE=1",
],
visibility = [
"PUBLIC",
],
deps = [
"//xplat/fbsystrace:fbsystrace",
"//xplat/folly:headers_only",
"//xplat/folly:memory",
"//xplat/folly:molly",
"//xplat/third-party/glog:glog",
],
)

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "ReactNativeConfig.h"
namespace facebook {
namespace react {
/**
* ReactNative configuration as provided by the hosting app.
* Provide a sub-class implementation to allow app specific customization.
*/
ReactNativeConfig::ReactNativeConfig() {}
ReactNativeConfig::~ReactNativeConfig() {}
EmptyReactNativeConfig::EmptyReactNativeConfig() {}
bool EmptyReactNativeConfig::getBool(const std::string &param) const {
return false;
}
std::string EmptyReactNativeConfig::getString(const std::string &param) const {
return "";
}
int64_t EmptyReactNativeConfig::getInt64(const std::string &param) const {
return 0;
}
double EmptyReactNativeConfig::getDouble(const std::string &param) const {
return 0.0;
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <string>
namespace facebook {
namespace react {
/**
* ReactNative configuration as provided by the hosting app.
* Provide a sub-class implementation to allow app specific customization.
*/
class ReactNativeConfig {
public:
ReactNativeConfig();
virtual ~ReactNativeConfig();
virtual bool getBool(const std::string &param) const = 0;
virtual std::string getString(const std::string &param) const = 0;
virtual int64_t getInt64(const std::string &param) const = 0;
virtual double getDouble(const std::string &param) const = 0;
};
/**
* Empty configuration that will always provide "falsy" values.
*/
class EmptyReactNativeConfig : public ReactNativeConfig {
public:
EmptyReactNativeConfig();
bool getBool(const std::string &param) const override;
std::string getString(const std::string &param) const override;
int64_t getInt64(const std::string &param) const override;
double getDouble(const std::string &param) const override;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,5 @@
---
Checks: '>
clang-diagnostic-*,
'
...

View File

@ -0,0 +1,34 @@
# Copyright (c) Facebook, Inc. and its 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 := reactnative
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/..
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_CFLAGS := \
-DLOG_TAG=\"ReactNative\"
LOCAL_CFLAGS += -fexceptions -frtti -Wno-unused-lambda-capture
LOCAL_STATIC_LIBRARIES := boost jsi callinvoker
LOCAL_SHARED_LIBRARIES := jsinspector libfolly_json glog
include $(BUILD_STATIC_LIBRARY)
$(call import-module,fb)
$(call import-module,folly)
$(call import-module,callinvoker)
$(call import-module,jsc)
$(call import-module,glog)
$(call import-module,jsi)
$(call import-module,jsinspector)
$(call import-module,hermes/inspector)

157
node_modules/react-native/ReactCommon/cxxreact/BUCK generated vendored Normal file
View File

@ -0,0 +1,157 @@
load("@fbsource//tools/build_defs:glob_defs.bzl", "subdir_glob")
load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "APPLE", "get_android_inspector_flags", "get_apple_compiler_flags", "get_apple_inspector_flags", "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library")
CXX_LIBRARY_COMPILER_FLAGS = [
"-std=c++14",
"-Wall",
]
rn_xplat_cxx_library(
name = "module",
header_namespace = "",
exported_headers = subdir_glob(
[
("", "CxxModule.h"),
("", "JsArgumentHelpers.h"),
("", "JsArgumentHelpers-inl.h"),
],
prefix = "cxxreact",
),
compiler_flags = CXX_LIBRARY_COMPILER_FLAGS,
fbobjc_compiler_flags = get_apple_compiler_flags(),
fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"],
force_static = True,
visibility = [
"PUBLIC",
],
deps = [
"//xplat/folly:molly",
],
)
rn_xplat_cxx_library(
name = "jsbigstring",
srcs = [
"JSBigString.cpp",
],
header_namespace = "",
exported_headers = subdir_glob(
[("", "JSBigString.h")],
prefix = "cxxreact",
),
compiler_flags = CXX_LIBRARY_COMPILER_FLAGS + [
"-fexceptions",
"-frtti",
],
fbobjc_compiler_flags = get_apple_compiler_flags(),
fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"],
force_static = True,
preprocessor_flags = [
"-DWITH_FBREMAP=1",
],
visibility = [
"PUBLIC",
],
deps = [
"//xplat/folly:memory",
"//xplat/folly:molly",
"//xplat/folly:scope_guard",
],
)
rn_xplat_cxx_library(
name = "samplemodule",
srcs = ["SampleCxxModule.cpp"],
header_namespace = "",
exported_headers = ["SampleCxxModule.h"],
compiler_flags = CXX_LIBRARY_COMPILER_FLAGS + [
"-fno-omit-frame-pointer",
"-fexceptions",
],
fbobjc_compiler_flags = get_apple_compiler_flags(),
soname = "libxplat_react_module_samplemodule.$(ext)",
visibility = [
"PUBLIC",
],
deps = [
":module",
"//xplat/folly:memory",
"//xplat/folly:molly",
"//xplat/third-party/glog:glog",
],
)
CXXREACT_PUBLIC_HEADERS = [
"CxxNativeModule.h",
"Instance.h",
"JSBundleType.h",
"JSDeltaBundleClient.h",
"JSExecutor.h",
"JSIndexedRAMBundle.h",
"JSModulesUnbundle.h",
"MessageQueueThread.h",
"MethodCall.h",
"ModuleRegistry.h",
"NativeModule.h",
"NativeToJsBridge.h",
"RAMBundleRegistry.h",
"ReactMarker.h",
"RecoverableError.h",
"SharedProxyCxxModule.h",
"SystraceSection.h",
]
rn_xplat_cxx_library(
name = "bridge",
srcs = glob(
["*.cpp"],
exclude = [
"JSBigString.cpp",
"SampleCxxModule.cpp",
],
),
headers = glob(
["*.h"],
exclude = CXXREACT_PUBLIC_HEADERS,
),
header_namespace = "",
exported_headers = dict([
(
"cxxreact/%s" % header,
header,
)
for header in CXXREACT_PUBLIC_HEADERS
]),
compiler_flags = CXX_LIBRARY_COMPILER_FLAGS + [
"-fexceptions",
"-frtti",
],
fbandroid_preprocessor_flags = get_android_inspector_flags(),
fbobjc_compiler_flags = get_apple_compiler_flags(),
fbobjc_force_static = True,
fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"],
fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(),
macosx_tests_override = [],
platforms = (ANDROID, APPLE),
preprocessor_flags = [
"-DLOG_TAG=\"ReactNative\"",
"-DWITH_FBSYSTRACE=1",
],
tests = [
react_native_xplat_target("cxxreact/tests:tests"),
],
visibility = ["PUBLIC"],
deps = [
":jsbigstring",
":module",
"//xplat/fbsystrace:fbsystrace",
"//xplat/folly:headers_only",
"//xplat/folly:memory",
"//xplat/folly:molly",
react_native_xplat_target("callinvoker:callinvoker"),
react_native_xplat_target("jsinspector:jsinspector"),
react_native_xplat_target("microprofiler:microprofiler"),
"//xplat/folly:optional",
"//xplat/third-party/glog:glog",
],
)

View File

@ -0,0 +1,258 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <functional>
#include <map>
#include <tuple>
#include <vector>
#include <folly/dynamic.h>
using namespace std::placeholders;
namespace facebook {
namespace react {
class Instance;
}
} // namespace facebook
namespace facebook {
namespace xplat {
namespace module {
/**
* Base class for Catalyst native modules whose implementations are
* written in C++. Native methods are represented by instances of the
* Method struct. Generally, a derived class will manage an instance
* which represents the data for the module, and non-Catalyst-specific
* methods can be wrapped in lambdas which convert between
* folly::dynamic and native C++ objects. The Callback arguments will
* pass through to js functions passed to the analogous javascript
* methods. At most two callbacks will be converted. Results should
* be passed to the first callback, and errors to the second callback.
* Exceptions thrown by a method will be converted to platform
* exceptions, and handled however they are handled on that platform.
* (TODO mhorowitz #7128529: this exception behavior is not yet
* implemented.)
*
* There are two sets of constructors here. The first set initializes
* a Method using a name and anything convertible to a std::function.
* This is most useful for registering a lambda as a RN method. There
* are overloads to support functions which take no arguments,
* arguments only, and zero, one, or two callbacks.
*
* The second set of methods is similar, but instead of taking a
* function, takes the method name, an object, and a pointer to a
* method on that object.
*/
class CxxModule {
class AsyncTagType {};
class SyncTagType {};
public:
typedef std::function<std::unique_ptr<CxxModule>()> Provider;
typedef std::function<void(std::vector<folly::dynamic>)> Callback;
constexpr static AsyncTagType AsyncTag = AsyncTagType();
constexpr static SyncTagType SyncTag = SyncTagType();
struct Method {
std::string name;
size_t callbacks;
bool isPromise;
std::function<void(folly::dynamic, Callback, Callback)> func;
std::function<folly::dynamic(folly::dynamic)> syncFunc;
const char *getType() {
assert(func || syncFunc);
return func ? (isPromise ? "promise" : "async") : "sync";
}
// std::function/lambda ctors
Method(std::string aname, std::function<void()> &&afunc)
: name(std::move(aname)),
callbacks(0),
isPromise(false),
func(std::bind(std::move(afunc))) {}
Method(std::string aname, std::function<void(folly::dynamic)> &&afunc)
: name(std::move(aname)),
callbacks(0),
isPromise(false),
func(std::bind(std::move(afunc), std::placeholders::_1)) {}
Method(
std::string aname,
std::function<void(folly::dynamic, Callback)> &&afunc)
: name(std::move(aname)),
callbacks(1),
isPromise(false),
func(std::bind(
std::move(afunc),
std::placeholders::_1,
std::placeholders::_2)) {}
Method(
std::string aname,
std::function<void(folly::dynamic, Callback, Callback)> &&afunc)
: name(std::move(aname)),
callbacks(2),
isPromise(true),
func(std::move(afunc)) {}
Method(
std::string aname,
std::function<void(folly::dynamic, Callback, Callback)> &&afunc,
AsyncTagType)
: name(std::move(aname)),
callbacks(2),
isPromise(false),
func(std::move(afunc)) {}
// method pointer ctors
template <typename T>
Method(std::string aname, T *t, void (T::*method)())
: name(std::move(aname)),
callbacks(0),
isPromise(false),
func(std::bind(method, t)) {}
template <typename T>
Method(std::string aname, T *t, void (T::*method)(folly::dynamic))
: name(std::move(aname)),
callbacks(0),
isPromise(false),
func(std::bind(method, t, std::placeholders::_1)) {}
template <typename T>
Method(std::string aname, T *t, void (T::*method)(folly::dynamic, Callback))
: name(std::move(aname)),
callbacks(1),
isPromise(false),
func(std::bind(
method,
t,
std::placeholders::_1,
std::placeholders::_2)) {}
template <typename T>
Method(
std::string aname,
T *t,
void (T::*method)(folly::dynamic, Callback, Callback))
: name(std::move(aname)),
callbacks(2),
isPromise(true),
func(std::bind(
method,
t,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3)) {}
template <typename T>
Method(
std::string aname,
T *t,
void (T::*method)(folly::dynamic, Callback, Callback),
AsyncTagType)
: name(std::move(aname)),
callbacks(2),
isPromise(false),
func(std::bind(
method,
t,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3)) {}
// sync std::function/lambda ctors
// Overloads for functions returning void give ambiguity errors.
// I am not sure if this is a runtime/compiler bug, or a
// limitation I do not understand.
Method(
std::string aname,
std::function<folly::dynamic()> &&afunc,
SyncTagType)
: name(std::move(aname)),
callbacks(0),
isPromise(false),
syncFunc([afunc = std::move(afunc)](const folly::dynamic &) {
return afunc();
}) {}
Method(
std::string aname,
std::function<folly::dynamic(folly::dynamic)> &&afunc,
SyncTagType)
: name(std::move(aname)),
callbacks(0),
isPromise(false),
syncFunc(std::move(afunc)) {}
};
/**
* This may block, if necessary to complete cleanup before the
* object is destroyed.
*/
virtual ~CxxModule() {}
/**
* @return the name of this module. This will be the name used to {@code
* require()} this module from javascript.
*/
virtual std::string getName() = 0;
/**
* Each entry in the map will be exported as a property to JS. The
* key is the property name, and the value can be anything.
*/
virtual auto getConstants() -> std::map<std::string, folly::dynamic> {
return {};
};
/**
* @return a list of methods this module exports to JS.
*/
virtual auto getMethods() -> std::vector<Method> = 0;
/**
* Called during the construction of CxxNativeModule.
*/
void setInstance(std::weak_ptr<react::Instance> instance) {
instance_ = instance;
}
/**
* @return a weak_ptr to the current instance of the bridge.
* When used with CxxNativeModule, this gives Cxx modules access to functions
* such as `callJSFunction`, allowing them to communicate back to JS outside
* of the regular callbacks.
*/
std::weak_ptr<react::Instance> getInstance() {
return instance_;
}
private:
std::weak_ptr<react::Instance> instance_;
};
} // namespace module
} // namespace xplat
} // namespace facebook

View File

@ -0,0 +1,213 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "CxxNativeModule.h"
#include "Instance.h"
#include <folly/json.h>
#include <glog/logging.h>
#include <iterator>
#include "JsArgumentHelpers.h"
#include "MessageQueueThread.h"
#include "SystraceSection.h"
using facebook::xplat::module::CxxModule;
namespace facebook {
namespace react {
std::function<void(folly::dynamic)> makeCallback(
std::weak_ptr<Instance> instance,
const folly::dynamic &callbackId) {
if (!callbackId.isNumber()) {
throw std::invalid_argument("Expected callback(s) as final argument");
}
auto id = callbackId.asInt();
return [winstance = std::move(instance), id](folly::dynamic args) {
if (auto instance = winstance.lock()) {
instance->callJSCallback(id, std::move(args));
}
};
}
namespace {
/**
* CxxModule::Callback accepts a vector<dynamic>, makeCallback returns
* a callback that accepts a dynamic, adapt the second into the first.
* TODO: Callback types should be made equal (preferably
* function<void(dynamic)>) to avoid the extra copy and indirect call.
*/
CxxModule::Callback convertCallback(
std::function<void(folly::dynamic)> callback) {
return [callback = std::move(callback)](std::vector<folly::dynamic> args) {
callback(folly::dynamic(
std::make_move_iterator(args.begin()),
std::make_move_iterator(args.end())));
};
}
} // namespace
std::string CxxNativeModule::getName() {
return name_;
}
std::vector<MethodDescriptor> CxxNativeModule::getMethods() {
lazyInit();
std::vector<MethodDescriptor> descs;
for (auto &method : methods_) {
descs.emplace_back(method.name, method.getType());
}
return descs;
}
folly::dynamic CxxNativeModule::getConstants() {
lazyInit();
if (!module_) {
return nullptr;
}
folly::dynamic constants = folly::dynamic::object();
for (auto &pair : module_->getConstants()) {
constants.insert(std::move(pair.first), std::move(pair.second));
}
return constants;
}
void CxxNativeModule::invoke(
unsigned int reactMethodId,
folly::dynamic &&params,
int callId) {
if (reactMethodId >= methods_.size()) {
throw std::invalid_argument(folly::to<std::string>(
"methodId ",
reactMethodId,
" out of range [0..",
methods_.size(),
"]"));
}
if (!params.isArray()) {
throw std::invalid_argument(folly::to<std::string>(
"method parameters should be array, but are ", params.typeName()));
}
CxxModule::Callback first;
CxxModule::Callback second;
const auto &method = methods_[reactMethodId];
if (!method.func) {
throw std::runtime_error(folly::to<std::string>(
"Method ", method.name, " is synchronous but invoked asynchronously"));
}
if (params.size() < method.callbacks) {
throw std::invalid_argument(folly::to<std::string>(
"Expected ",
method.callbacks,
" callbacks, but only ",
params.size(),
" parameters provided"));
}
if (method.callbacks == 1) {
first = convertCallback(makeCallback(instance_, params[params.size() - 1]));
} else if (method.callbacks == 2) {
first = convertCallback(makeCallback(instance_, params[params.size() - 2]));
second =
convertCallback(makeCallback(instance_, params[params.size() - 1]));
}
params.resize(params.size() - method.callbacks);
// I've got a few flawed options here. I can let the C++ exception
// propagate, and the registry will log/convert them to java exceptions.
// This lets all the java and red box handling work ok, but the only info I
// can capture about the C++ exception is the what() string, not the stack.
// I can std::terminate() the app. This causes the full, accurate C++
// stack trace to be added to logcat by debuggerd. The java state is lost,
// but in practice, the java stack is always the same in this case since
// the javascript stack is not visible, and the crash is unfriendly to js
// developers, but crucial to C++ developers. The what() value is also
// lost. Finally, I can catch, log the java stack, then rethrow the C++
// exception. In this case I get java and C++ stack data, but the C++
// stack is as of the rethrow, not the original throw, both the C++ and
// java stacks always look the same.
//
// I am going with option 2, since that seems like the most useful
// choice. It would be nice to be able to get what() and the C++
// stack. I'm told that will be possible in the future. TODO
// mhorowitz #7128529: convert C++ exceptions to Java
messageQueueThread_->runOnQueue(
[method, params = std::move(params), first, second, callId]() {
#ifdef WITH_FBSYSTRACE
if (callId != -1) {
fbsystrace_end_async_flow(TRACE_TAG_REACT_APPS, "native", callId);
}
#else
(void)(callId);
#endif
SystraceSection s(method.name.c_str());
try {
method.func(std::move(params), first, second);
} catch (const facebook::xplat::JsArgumentException &ex) {
throw;
} catch (std::exception &e) {
LOG(ERROR) << "std::exception. Method call " << method.name.c_str()
<< " failed: " << e.what();
std::terminate();
} catch (std::string &error) {
LOG(ERROR) << "std::string. Method call " << method.name.c_str()
<< " failed: " << error.c_str();
std::terminate();
} catch (...) {
LOG(ERROR) << "Method call " << method.name.c_str()
<< " failed. unknown error";
std::terminate();
}
});
}
MethodCallResult CxxNativeModule::callSerializableNativeHook(
unsigned int hookId,
folly::dynamic &&args) {
if (hookId >= methods_.size()) {
throw std::invalid_argument(folly::to<std::string>(
"methodId ", hookId, " out of range [0..", methods_.size(), "]"));
}
const auto &method = methods_[hookId];
if (!method.syncFunc) {
throw std::runtime_error(folly::to<std::string>(
"Method ", method.name, " is asynchronous but invoked synchronously"));
}
return method.syncFunc(std::move(args));
}
void CxxNativeModule::lazyInit() {
if (module_ || !provider_) {
return;
}
// TODO 17216751: providers should never return null modules
module_ = provider_();
provider_ = nullptr;
if (module_) {
methods_ = module_->getMethods();
module_->setInstance(instance_);
}
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,60 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <cxxreact/CxxModule.h>
#include <cxxreact/NativeModule.h>
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
namespace facebook {
namespace react {
class Instance;
class MessageQueueThread;
std::function<void(folly::dynamic)> makeCallback(
std::weak_ptr<Instance> instance,
const folly::dynamic &callbackId);
class RN_EXPORT CxxNativeModule : public NativeModule {
public:
CxxNativeModule(
std::weak_ptr<Instance> instance,
std::string name,
xplat::module::CxxModule::Provider provider,
std::shared_ptr<MessageQueueThread> messageQueueThread)
: instance_(instance),
name_(std::move(name)),
provider_(provider),
messageQueueThread_(messageQueueThread) {}
std::string getName() override;
std::vector<MethodDescriptor> getMethods() override;
folly::dynamic getConstants() override;
void invoke(unsigned int reactMethodId, folly::dynamic &&params, int callId)
override;
MethodCallResult callSerializableNativeHook(
unsigned int hookId,
folly::dynamic &&args) override;
private:
void lazyInit();
std::weak_ptr<Instance> instance_;
std::string name_;
xplat::module::CxxModule::Provider provider_;
std::shared_ptr<MessageQueueThread> messageQueueThread_;
std::unique_ptr<xplat::module::CxxModule> module_;
std::vector<xplat::module::CxxModule::Method> methods_;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,291 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "Instance.h"
#include "JSBigString.h"
#include "JSBundleType.h"
#include "JSExecutor.h"
#include "MessageQueueThread.h"
#include "MethodCall.h"
#include "NativeToJsBridge.h"
#include "RAMBundleRegistry.h"
#include "RecoverableError.h"
#include "SystraceSection.h"
#include <cxxreact/JSIndexedRAMBundle.h>
#include <folly/MoveWrapper.h>
#include <folly/json.h>
#include <glog/logging.h>
#include <condition_variable>
#include <exception>
#include <fstream>
#include <memory>
#include <mutex>
#include <string>
namespace facebook {
namespace react {
Instance::~Instance() {
if (nativeToJsBridge_) {
nativeToJsBridge_->destroy();
}
}
void Instance::initializeBridge(
std::unique_ptr<InstanceCallback> callback,
std::shared_ptr<JSExecutorFactory> jsef,
std::shared_ptr<MessageQueueThread> jsQueue,
std::shared_ptr<ModuleRegistry> moduleRegistry) {
callback_ = std::move(callback);
moduleRegistry_ = std::move(moduleRegistry);
jsQueue->runOnQueueSync([this, &jsef, jsQueue]() mutable {
nativeToJsBridge_ = std::make_shared<NativeToJsBridge>(
jsef.get(), moduleRegistry_, jsQueue, callback_);
nativeToJsBridge_->initializeRuntime();
/**
* After NativeToJsBridge is created, the jsi::Runtime should exist.
* Also, the JS message queue thread exists. So, it's safe to
* schedule all queued up js Calls.
*/
jsCallInvoker_->setNativeToJsBridgeAndFlushCalls(nativeToJsBridge_);
std::lock_guard<std::mutex> lock(m_syncMutex);
m_syncReady = true;
m_syncCV.notify_all();
});
CHECK(nativeToJsBridge_);
}
void Instance::loadBundle(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> string,
std::string sourceURL) {
callback_->incrementPendingJSCalls();
SystraceSection s("Instance::loadBundle", "sourceURL", sourceURL);
nativeToJsBridge_->loadBundle(
std::move(bundleRegistry), std::move(string), std::move(sourceURL));
}
void Instance::loadBundleSync(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> string,
std::string sourceURL) {
std::unique_lock<std::mutex> lock(m_syncMutex);
m_syncCV.wait(lock, [this] { return m_syncReady; });
SystraceSection s("Instance::loadBundleSync", "sourceURL", sourceURL);
nativeToJsBridge_->loadBundleSync(
std::move(bundleRegistry), std::move(string), std::move(sourceURL));
}
void Instance::setSourceURL(std::string sourceURL) {
callback_->incrementPendingJSCalls();
SystraceSection s("Instance::setSourceURL", "sourceURL", sourceURL);
nativeToJsBridge_->loadBundle(nullptr, nullptr, std::move(sourceURL));
}
void Instance::loadScriptFromString(
std::unique_ptr<const JSBigString> string,
std::string sourceURL,
bool loadSynchronously) {
SystraceSection s("Instance::loadScriptFromString", "sourceURL", sourceURL);
if (loadSynchronously) {
loadBundleSync(nullptr, std::move(string), std::move(sourceURL));
} else {
loadBundle(nullptr, std::move(string), std::move(sourceURL));
}
}
bool Instance::isIndexedRAMBundle(const char *sourcePath) {
std::ifstream bundle_stream(sourcePath, std::ios_base::in);
BundleHeader header;
if (!bundle_stream ||
!bundle_stream.read(reinterpret_cast<char *>(&header), sizeof(header))) {
return false;
}
return parseTypeFromHeader(header) == ScriptTag::RAMBundle;
}
bool Instance::isIndexedRAMBundle(std::unique_ptr<const JSBigString> *script) {
BundleHeader header;
strncpy(
reinterpret_cast<char *>(&header),
script->get()->c_str(),
sizeof(header));
return parseTypeFromHeader(header) == ScriptTag::RAMBundle;
}
void Instance::loadRAMBundleFromString(
std::unique_ptr<const JSBigString> script,
const std::string &sourceURL) {
auto bundle = std::make_unique<JSIndexedRAMBundle>(std::move(script));
auto startupScript = bundle->getStartupCode();
auto registry = RAMBundleRegistry::singleBundleRegistry(std::move(bundle));
loadRAMBundle(std::move(registry), std::move(startupScript), sourceURL, true);
}
void Instance::loadRAMBundleFromFile(
const std::string &sourcePath,
const std::string &sourceURL,
bool loadSynchronously) {
auto bundle = std::make_unique<JSIndexedRAMBundle>(sourcePath.c_str());
auto startupScript = bundle->getStartupCode();
auto registry = RAMBundleRegistry::multipleBundlesRegistry(
std::move(bundle), JSIndexedRAMBundle::buildFactory());
loadRAMBundle(
std::move(registry),
std::move(startupScript),
sourceURL,
loadSynchronously);
}
void Instance::loadRAMBundle(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL,
bool loadSynchronously) {
if (loadSynchronously) {
loadBundleSync(
std::move(bundleRegistry),
std::move(startupScript),
std::move(startupScriptSourceURL));
} else {
loadBundle(
std::move(bundleRegistry),
std::move(startupScript),
std::move(startupScriptSourceURL));
}
}
void Instance::setGlobalVariable(
std::string propName,
std::unique_ptr<const JSBigString> jsonValue) {
nativeToJsBridge_->setGlobalVariable(
std::move(propName), std::move(jsonValue));
}
void *Instance::getJavaScriptContext() {
return nativeToJsBridge_ ? nativeToJsBridge_->getJavaScriptContext()
: nullptr;
}
bool Instance::isInspectable() {
return nativeToJsBridge_ ? nativeToJsBridge_->isInspectable() : false;
}
bool Instance::isBatchActive() {
return nativeToJsBridge_ ? nativeToJsBridge_->isBatchActive() : false;
}
void Instance::callJSFunction(
std::string &&module,
std::string &&method,
folly::dynamic &&params) {
callback_->incrementPendingJSCalls();
nativeToJsBridge_->callFunction(
std::move(module), std::move(method), std::move(params));
}
void Instance::callJSCallback(uint64_t callbackId, folly::dynamic &&params) {
SystraceSection s("Instance::callJSCallback");
callback_->incrementPendingJSCalls();
nativeToJsBridge_->invokeCallback((double)callbackId, std::move(params));
}
void Instance::registerBundle(
uint32_t bundleId,
const std::string &bundlePath) {
nativeToJsBridge_->registerBundle(bundleId, bundlePath);
}
const ModuleRegistry &Instance::getModuleRegistry() const {
return *moduleRegistry_;
}
ModuleRegistry &Instance::getModuleRegistry() {
return *moduleRegistry_;
}
void Instance::handleMemoryPressure(int pressureLevel) {
nativeToJsBridge_->handleMemoryPressure(pressureLevel);
}
std::shared_ptr<CallInvoker> Instance::getJSCallInvoker() {
return std::static_pointer_cast<CallInvoker>(jsCallInvoker_);
}
std::shared_ptr<CallInvoker> Instance::getDecoratedNativeCallInvoker(
std::shared_ptr<CallInvoker> nativeInvoker) {
return nativeToJsBridge_->getDecoratedNativeCallInvoker(nativeInvoker);
}
void Instance::JSCallInvoker::setNativeToJsBridgeAndFlushCalls(
std::weak_ptr<NativeToJsBridge> nativeToJsBridge) {
std::lock_guard<std::mutex> guard(m_mutex);
m_shouldBuffer = false;
m_nativeToJsBridge = nativeToJsBridge;
while (m_workBuffer.size() > 0) {
scheduleAsync(std::move(m_workBuffer.front()));
m_workBuffer.pop_front();
}
}
void Instance::JSCallInvoker::invokeSync(std::function<void()> &&work) {
// TODO: Replace JS Callinvoker with RuntimeExecutor.
throw std::runtime_error(
"Synchronous native -> JS calls are currently not supported.");
}
void Instance::JSCallInvoker::invokeAsync(std::function<void()> &&work) {
std::lock_guard<std::mutex> guard(m_mutex);
/**
* Why is is necessary to queue up async work?
*
* 1. TurboModuleManager must be created synchronously after the Instance,
* before we load the source code. This is when the NativeModule system
* is initialized. RCTDevLoadingView shows bundle download progress.
* 2. TurboModuleManager requires a JS CallInvoker.
* 3. The JS CallInvoker requires the NativeToJsBridge, which is created on
* the JS thread in Instance::initializeBridge.
*
* Therefore, although we don't call invokeAsync before the JS bundle is
* executed, this buffering is implemented anyways to ensure that work
* isn't discarded.
*/
if (m_shouldBuffer) {
m_workBuffer.push_back(std::move(work));
return;
}
scheduleAsync(std::move(work));
}
void Instance::JSCallInvoker::scheduleAsync(std::function<void()> &&work) {
if (auto strongNativeToJsBridge = m_nativeToJsBridge.lock()) {
strongNativeToJsBridge->runOnExecutorQueue(
[work = std::move(work)](JSExecutor *executor) {
work();
executor->flush();
});
}
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,172 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <condition_variable>
#include <list>
#include <memory>
#include <mutex>
#include <cxxreact/NativeToJsBridge.h>
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
namespace folly {
struct dynamic;
}
namespace facebook {
namespace react {
class JSBigString;
class JSExecutorFactory;
class MessageQueueThread;
class ModuleRegistry;
class RAMBundleRegistry;
struct InstanceCallback {
virtual ~InstanceCallback() {}
virtual void onBatchComplete() {}
virtual void incrementPendingJSCalls() {}
virtual void decrementPendingJSCalls() {}
};
class RN_EXPORT Instance {
public:
~Instance();
void initializeBridge(
std::unique_ptr<InstanceCallback> callback,
std::shared_ptr<JSExecutorFactory> jsef,
std::shared_ptr<MessageQueueThread> jsQueue,
std::shared_ptr<ModuleRegistry> moduleRegistry);
void initializeRuntime();
void setSourceURL(std::string sourceURL);
void loadScriptFromString(
std::unique_ptr<const JSBigString> string,
std::string sourceURL,
bool loadSynchronously);
static bool isIndexedRAMBundle(const char *sourcePath);
static bool isIndexedRAMBundle(std::unique_ptr<const JSBigString> *string);
void loadRAMBundleFromString(
std::unique_ptr<const JSBigString> script,
const std::string &sourceURL);
void loadRAMBundleFromFile(
const std::string &sourcePath,
const std::string &sourceURL,
bool loadSynchronously);
void loadRAMBundle(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL,
bool loadSynchronously);
bool supportsProfiling();
void setGlobalVariable(
std::string propName,
std::unique_ptr<const JSBigString> jsonValue);
void *getJavaScriptContext();
bool isInspectable();
bool isBatchActive();
void callJSFunction(
std::string &&module,
std::string &&method,
folly::dynamic &&params);
void callJSCallback(uint64_t callbackId, folly::dynamic &&params);
// This method is experimental, and may be modified or removed.
void registerBundle(uint32_t bundleId, const std::string &bundlePath);
const ModuleRegistry &getModuleRegistry() const;
ModuleRegistry &getModuleRegistry();
void handleMemoryPressure(int pressureLevel);
/**
* JS CallInvoker is used by TurboModules to schedule work on the JS thread.
*
* Why is the bridge creating JS CallInvoker?
*
* - After every Native -> JS call in the TurboModule system, the bridge
* needs to flush all queued NativeModule method calls. The bridge must
* also dispatch onBatchComplete if the queue of NativeModule method calls
* was not empty.
*/
std::shared_ptr<CallInvoker> getJSCallInvoker();
/**
* Native CallInvoker is used by TurboModules to schedule work on the
* NativeModule thread(s).
*
* Why is the bridge decorating native CallInvoker?
*
* - The bridge must be informed of all TurboModule async method calls. Why?
* When all queued NativeModule method calls are flushed by a call from
* Native -> JS, if that queue was non-zero in size, JsToNativeBridge
* dispatches onBatchComplete. When we turn our NativeModules to
* TurboModuels, there will be less and less pending NativeModule method
* calls, so onBatchComplete will not fire as often. Therefore, the bridge
* needs to know how many TurboModule async method calls have been completed
* since the last time the bridge was flushed. If this number is non-zero,
* we fire onBatchComplete.
*
* Why can't we just create and return a new native CallInvoker?
*
* - On Android, we have one NativeModule thread. That thread is created and
* managed outisde of NativeToJsBridge. On iOS, we have one MethodQueue per
* module. Those MethodQueues are also created and managed outside of
* NativeToJsBridge. Therefore, we need to pass in a CallInvoker that
* schedules work on the respective thread.
*/
std::shared_ptr<CallInvoker> getDecoratedNativeCallInvoker(
std::shared_ptr<CallInvoker> nativeInvoker);
private:
void callNativeModules(folly::dynamic &&calls, bool isEndOfBatch);
void loadBundle(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL);
void loadBundleSync(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL);
std::shared_ptr<InstanceCallback> callback_;
std::shared_ptr<NativeToJsBridge> nativeToJsBridge_;
std::shared_ptr<ModuleRegistry> moduleRegistry_;
std::mutex m_syncMutex;
std::condition_variable m_syncCV;
bool m_syncReady = false;
class JSCallInvoker : public CallInvoker {
private:
std::weak_ptr<NativeToJsBridge> m_nativeToJsBridge;
std::mutex m_mutex;
bool m_shouldBuffer = true;
std::list<std::function<void()>> m_workBuffer;
void scheduleAsync(std::function<void()> &&work);
public:
void setNativeToJsBridgeAndFlushCalls(
std::weak_ptr<NativeToJsBridge> nativeToJsBridge);
void invokeAsync(std::function<void()> &&work) override;
void invokeSync(std::function<void()> &&work) override;
};
std::shared_ptr<JSCallInvoker> jsCallInvoker_ =
std::make_shared<JSCallInvoker>();
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,157 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "JSBigString.h"
#include <glog/logging.h>
#include <folly/Memory.h>
#include <folly/ScopeGuard.h>
#include <folly/portability/Fcntl.h>
#include <folly/portability/SysMman.h>
#include <folly/portability/SysStat.h>
#include <folly/portability/Unistd.h>
#include <memory>
namespace facebook {
namespace react {
JSBigFileString::JSBigFileString(int fd, size_t size, off_t offset /*= 0*/)
: m_fd{-1}, m_data{nullptr} {
folly::checkUnixError(m_fd = dup(fd), "Could not duplicate file descriptor");
// Offsets given to mmap must be page aligned. We abstract away that
// restriction by sending a page aligned offset to mmap, and keeping track
// of the offset within the page that we must alter the mmap pointer by to
// get the final desired offset.
if (offset != 0) {
const static auto ps = sysconf(_SC_PAGESIZE);
auto d = lldiv(offset, ps);
m_mapOff = d.quot;
m_pageOff = d.rem;
m_size = size + m_pageOff;
} else {
m_mapOff = 0;
m_pageOff = 0;
m_size = size;
}
}
JSBigFileString::~JSBigFileString() {
if (m_data) {
munmap((void *)m_data, m_size);
}
close(m_fd);
}
#ifdef WITH_FBREMAP
// Read and advance the pointer.
static uint16_t read16(char *&data) {
uint16_t result;
::memcpy(&result, data, sizeof(result));
data += sizeof(result);
return result;
}
// If the given file has a remapping table header, remap its pages accordingly
// and return the byte offset from the beginning to the unwrapped payload.
static off_t maybeRemap(char *data, size_t size, int fd) {
// A remapped file's size must be a multiple of its page size, so quickly
// filter out files with incorrect size, without touching any pages.
static const size_t kMinPageSize = 4096;
if (size < kMinPageSize || size % kMinPageSize != 0) {
return 0;
}
const auto begin = data;
static const uint8_t kRemapMagic[] = {
0xc6, 0x1f, 0xbc, 0x03, 0xc1, 0x03, 0x19, 0x1f, 0xa1, 0xd0, 0xeb, 0x73};
if (::memcmp(data, kRemapMagic, sizeof(kRemapMagic)) != 0) {
return 0;
}
data += sizeof(kRemapMagic);
const size_t filePS = static_cast<size_t>(1) << read16(data);
if (size & (filePS - 1)) {
return 0;
}
{
// System page size must be at least as granular as the remapping.
// TODO: Consider fallback that reads entire file into memory.
const size_t systemPS = sysconf(_SC_PAGESIZE);
CHECK(filePS >= systemPS)
<< "filePS: " << filePS << "systemPS: " << systemPS;
}
const off_t headerPages = read16(data);
uint16_t numMappings = read16(data);
size_t curFilePage = headerPages;
while (numMappings--) {
auto memPage = read16(data) + headerPages;
auto numPages = read16(data);
if (mmap(
begin + memPage * filePS,
numPages * filePS,
PROT_READ,
MAP_FILE | MAP_PRIVATE | MAP_FIXED,
fd,
curFilePage * filePS) == MAP_FAILED) {
CHECK(false) << " memPage: " << memPage << " numPages: " << numPages
<< " curFilePage: " << curFilePage << " size: " << size
<< " error: " << std::strerror(errno);
}
curFilePage += numPages;
}
return headerPages * filePS;
}
#endif // WITH_FBREMAP
const char *JSBigFileString::c_str() const {
if (m_size == 0) {
return "";
}
if (!m_data) {
m_data =
(const char *)mmap(0, m_size, PROT_READ, MAP_PRIVATE, m_fd, m_mapOff);
CHECK(m_data != MAP_FAILED)
<< " fd: " << m_fd << " size: " << m_size << " offset: " << m_mapOff
<< " error: " << std::strerror(errno);
#ifdef WITH_FBREMAP
// Remapping is only attempted when the entire file was requested.
if (m_mapOff == 0 && m_pageOff == 0) {
m_pageOff = maybeRemap(const_cast<char *>(m_data), m_size, m_fd);
}
#endif // WITH_FBREMAP
}
return m_data + m_pageOff;
}
size_t JSBigFileString::size() const {
// Ensure mapping has been initialized.
c_str();
return m_size - m_pageOff;
}
int JSBigFileString::fd() const {
return m_fd;
}
std::unique_ptr<const JSBigFileString> JSBigFileString::fromPath(
const std::string &sourceURL) {
int fd = ::open(sourceURL.c_str(), O_RDONLY);
folly::checkUnixError(fd, "Could not open file", sourceURL);
SCOPE_EXIT {
CHECK(::close(fd) == 0);
};
struct stat fileInfo;
folly::checkUnixError(::fstat(fd, &fileInfo), "fstat on bundle failed.");
return std::make_unique<const JSBigFileString>(fd, fileInfo.st_size);
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,137 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <folly/Exception.h>
#ifndef RN_EXPORT
#ifdef _MSC_VER
#define RN_EXPORT
#else
#define RN_EXPORT __attribute__((visibility("default")))
#endif
#endif
namespace facebook {
namespace react {
// JSExecutor functions sometimes take large strings, on the order of
// megabytes. Copying these can be expensive. Introducing a
// move-only, non-CopyConstructible type will let the compiler ensure
// that no copies occur. folly::MoveWrapper should be used when a
// large string needs to be curried into a std::function<>, which must
// by CopyConstructible.
class JSBigString {
public:
JSBigString() = default;
// Not copyable
JSBigString(const JSBigString &) = delete;
JSBigString &operator=(const JSBigString &) = delete;
virtual ~JSBigString() {}
virtual bool isAscii() const = 0;
// This needs to be a \0 terminated string
virtual const char *c_str() const = 0;
// Length of the c_str without the NULL byte.
virtual size_t size() const = 0;
};
// Concrete JSBigString implementation which holds a std::string
// instance.
class JSBigStdString : public JSBigString {
public:
JSBigStdString(std::string str, bool isAscii = false)
: m_isAscii(isAscii), m_str(std::move(str)) {}
bool isAscii() const override {
return m_isAscii;
}
const char *c_str() const override {
return m_str.c_str();
}
size_t size() const override {
return m_str.size();
}
private:
bool m_isAscii;
std::string m_str;
};
// Concrete JSBigString implementation which holds a heap-allocated
// buffer, and provides an accessor for writing to it. This can be
// used to construct a JSBigString in place, such as by reading from a
// file.
class RN_EXPORT JSBigBufferString : public JSBigString {
public:
JSBigBufferString(size_t size) : m_data(new char[size + 1]), m_size(size) {
// Guarantee nul-termination. The caller is responsible for
// filling in the rest of m_data.
m_data[m_size] = '\0';
}
~JSBigBufferString() {
delete[] m_data;
}
bool isAscii() const override {
return true;
}
const char *c_str() const override {
return m_data;
}
size_t size() const override {
return m_size;
}
char *data() {
return m_data;
}
private:
char *m_data;
size_t m_size;
};
// JSBigString interface implemented by a file-backed mmap region.
class RN_EXPORT JSBigFileString : public JSBigString {
public:
JSBigFileString(int fd, size_t size, off_t offset = 0);
~JSBigFileString();
bool isAscii() const override {
return true;
}
const char *c_str() const override;
size_t size() const override;
int fd() const;
static std::unique_ptr<const JSBigFileString> fromPath(
const std::string &sourceURL);
private:
int m_fd; // The file descriptor being mmaped
size_t m_size; // The size of the mmaped region
mutable off_t m_pageOff; // The offset in the mmaped region to the data.
off_t m_mapOff; // The offset in the file to the mmaped region.
mutable const char *m_data; // Pointer to the mmaped region.
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "JSBundleType.h"
#include <folly/Bits.h>
namespace facebook {
namespace react {
static uint32_t constexpr RAMBundleMagicNumber = 0xFB0BD1E5;
static uint32_t constexpr BCBundleMagicNumber = 0x6D657300;
ScriptTag parseTypeFromHeader(const BundleHeader &header) {
switch (folly::Endian::little(header.magic)) {
case RAMBundleMagicNumber:
return ScriptTag::RAMBundle;
case BCBundleMagicNumber:
return ScriptTag::BCBundle;
default:
return ScriptTag::String;
}
}
const char *stringForScriptTag(const ScriptTag &tag) {
switch (tag) {
case ScriptTag::String:
return "String";
case ScriptTag::RAMBundle:
return "RAM Bundle";
case ScriptTag::BCBundle:
return "BC Bundle";
}
return "";
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <folly/Portability.h>
#include <cstdint>
#include <cstring>
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
namespace facebook {
namespace react {
/*
* ScriptTag
*
* Scripts given to the JS Executors to run could be in any of the following
* formats. They are tagged so the executor knows how to run them.
*/
enum struct ScriptTag {
String = 0,
RAMBundle,
BCBundle,
};
/**
* BundleHeader
*
* RAM bundles and BC bundles begin with headers. For RAM bundles this is
* 4 bytes, for BC bundles this is 12 bytes. This structure holds the first 12
* bytes from a bundle in a way that gives access to that information.
*/
FOLLY_PACK_PUSH
struct FOLLY_PACK_ATTR BundleHeader {
BundleHeader() {
std::memset(this, 0, sizeof(BundleHeader));
}
uint32_t magic;
uint32_t reserved_;
uint32_t version;
};
FOLLY_PACK_POP
/**
* parseTypeFromHeader
*
* Takes the first 8 bytes of a bundle, and returns a tag describing the
* bundle's format.
*/
RN_EXPORT ScriptTag parseTypeFromHeader(const BundleHeader &header);
/**
* stringForScriptTag
*
* Convert an `ScriptTag` enum into a string, useful for emitting in errors
* and diagnostic messages.
*/
RN_EXPORT const char *stringForScriptTag(const ScriptTag &tag);
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,100 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "JSDeltaBundleClient.h"
#include <memory>
#include <sstream>
namespace facebook {
namespace react {
namespace {
std::string startupCode(const folly::dynamic *pre, const folly::dynamic *post) {
std::ostringstream startupCode;
for (auto section : {pre, post}) {
if (section != nullptr) {
startupCode << section->getString() << '\n';
}
}
return startupCode.str();
}
} // namespace
void JSDeltaBundleClient::patchModules(const folly::dynamic *modules) {
for (const folly::dynamic &pair : *modules) {
auto id = pair[0].getInt();
auto module = pair[1];
modules_[id] = std::move(module.getString());
}
}
void JSDeltaBundleClient::patch(const folly::dynamic &delta) {
auto const base = delta.get_ptr("base");
if (base != nullptr && base->asBool()) {
clear();
auto const pre = delta.get_ptr("pre");
auto const post = delta.get_ptr("post");
startupCode_ = startupCode(pre, post);
const folly::dynamic *modules = delta.get_ptr("modules");
if (modules != nullptr) {
patchModules(modules);
}
} else {
const folly::dynamic *deleted = delta.get_ptr("deleted");
if (deleted != nullptr) {
for (const folly::dynamic &id : *deleted) {
modules_.erase(id.getInt());
}
}
// TODO T37123645 This is deprecated but necessary in order to support older
// versions of the Metro server.
const folly::dynamic *modules = delta.get_ptr("modules");
if (modules != nullptr) {
patchModules(modules);
}
const folly::dynamic *added = delta.get_ptr("added");
if (added != nullptr) {
patchModules(added);
}
const folly::dynamic *modified = delta.get_ptr("modified");
if (modified != nullptr) {
patchModules(modified);
}
}
}
JSModulesUnbundle::Module JSDeltaBundleClient::getModule(
uint32_t moduleId) const {
auto search = modules_.find(moduleId);
if (search != modules_.end()) {
return {folly::to<std::string>(search->first, ".js"), search->second};
}
throw JSModulesUnbundle::ModuleNotFound(moduleId);
}
std::unique_ptr<const JSBigString> JSDeltaBundleClient::getStartupCode() const {
return std::make_unique<JSBigStdString>(startupCode_);
}
void JSDeltaBundleClient::clear() {
modules_.clear();
startupCode_.clear();
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <cstdint>
#include <memory>
#include <string>
#include <unordered_map>
#include <cxxreact/JSBigString.h>
#include <cxxreact/JSModulesUnbundle.h>
#include <folly/dynamic.h>
namespace facebook {
namespace react {
class JSDeltaBundleClient {
public:
void patch(const folly::dynamic &delta);
JSModulesUnbundle::Module getModule(uint32_t moduleId) const;
std::unique_ptr<const JSBigString> getStartupCode() const;
void clear();
private:
std::unordered_map<uint32_t, std::string> modules_;
std::string startupCode_;
void patchModules(const folly::dynamic *delta);
};
class JSDeltaBundleClientRAMBundle : public JSModulesUnbundle {
public:
JSDeltaBundleClientRAMBundle(
std::shared_ptr<const JSDeltaBundleClient> client)
: client_(client) {}
Module getModule(uint32_t moduleId) const override {
return client_->getModule(moduleId);
}
private:
const std::shared_ptr<const JSDeltaBundleClient> client_;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "JSExecutor.h"
#include "RAMBundleRegistry.h"
#include <folly/Conv.h>
namespace facebook {
namespace react {
std::string JSExecutor::getSyntheticBundlePath(
uint32_t bundleId,
const std::string &bundlePath) {
if (bundleId == RAMBundleRegistry::MAIN_BUNDLE_ID) {
return bundlePath;
}
return folly::to<std::string>("seg-", bundleId, ".js");
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,144 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <string>
#include <cxxreact/NativeModule.h>
#include <folly/dynamic.h>
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
namespace facebook {
namespace react {
class JSBigString;
class JSExecutor;
class JSModulesUnbundle;
class MessageQueueThread;
class ModuleRegistry;
class RAMBundleRegistry;
// This interface describes the delegate interface required by
// Executor implementations to call from JS into native code.
class ExecutorDelegate {
public:
virtual ~ExecutorDelegate() {}
virtual std::shared_ptr<ModuleRegistry> getModuleRegistry() = 0;
virtual void callNativeModules(
JSExecutor &executor,
folly::dynamic &&calls,
bool isEndOfBatch) = 0;
virtual MethodCallResult callSerializableNativeHook(
JSExecutor &executor,
unsigned int moduleId,
unsigned int methodId,
folly::dynamic &&args) = 0;
};
using NativeExtensionsProvider =
std::function<folly::dynamic(const std::string &)>;
class JSExecutorFactory {
public:
virtual std::unique_ptr<JSExecutor> createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> jsQueue) = 0;
virtual ~JSExecutorFactory() {}
};
class RN_EXPORT JSExecutor {
public:
/**
* Prepares the JS runtime for React Native by installing global variables.
* Called once before any JS is evaluated.
*/
virtual void initializeRuntime() = 0;
/**
* Execute an application script bundle in the JS context.
*/
virtual void loadBundle(
std::unique_ptr<const JSBigString> script,
std::string sourceURL) = 0;
/**
* Add an application "RAM" bundle registry
*/
virtual void setBundleRegistry(
std::unique_ptr<RAMBundleRegistry> bundleRegistry) = 0;
/**
* Register a file path for an additional "RAM" bundle
*/
virtual void registerBundle(
uint32_t bundleId,
const std::string &bundlePath) = 0;
/**
* Executes BatchedBridge.callFunctionReturnFlushedQueue with the module ID,
* method ID and optional additional arguments in JS. The executor is
* responsible for using Bridge->callNativeModules to invoke any necessary
* native modules methods.
*/
virtual void callFunction(
const std::string &moduleId,
const std::string &methodId,
const folly::dynamic &arguments) = 0;
/**
* Executes BatchedBridge.invokeCallbackAndReturnFlushedQueue with the cbID,
* and optional additional arguments in JS and returns the next queue. The
* executor is responsible for using Bridge->callNativeModules to invoke any
* necessary native modules methods.
*/
virtual void invokeCallback(
const double callbackId,
const folly::dynamic &arguments) = 0;
virtual void setGlobalVariable(
std::string propName,
std::unique_ptr<const JSBigString> jsonValue) = 0;
virtual void *getJavaScriptContext() {
return nullptr;
}
/**
* Returns whether or not the underlying executor supports debugging via the
* Chrome remote debugging protocol.
*/
virtual bool isInspectable() {
return false;
}
/**
* The description is displayed in the dev menu, if there is one in
* this build. There is a default, but if this method returns a
* non-empty string, it will be used instead.
*/
virtual std::string getDescription() = 0;
virtual void handleMemoryPressure(__unused int pressureLevel) {}
virtual void destroy() {}
virtual ~JSExecutor() {}
virtual void flush() {}
static std::string getSyntheticBundlePath(
uint32_t bundleId,
const std::string &bundlePath);
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,131 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "JSIndexedRAMBundle.h"
#include <glog/logging.h>
#include <fstream>
#include <memory>
#include <sstream>
namespace facebook {
namespace react {
std::function<std::unique_ptr<JSModulesUnbundle>(std::string)>
JSIndexedRAMBundle::buildFactory() {
return [](const std::string &bundlePath) {
return std::make_unique<JSIndexedRAMBundle>(bundlePath.c_str());
};
}
JSIndexedRAMBundle::JSIndexedRAMBundle(const char *sourcePath) {
m_bundle = std::make_unique<std::ifstream>(sourcePath, std::ifstream::binary);
if (!m_bundle) {
throw std::ios_base::failure(folly::to<std::string>(
"Bundle ", sourcePath, "cannot be opened: ", m_bundle->rdstate()));
}
init();
}
JSIndexedRAMBundle::JSIndexedRAMBundle(
std::unique_ptr<const JSBigString> script) {
// tmpStream is needed because m_bundle is std::istream type
// which has no member 'write'
std::unique_ptr<std::stringstream> tmpStream =
std::make_unique<std::stringstream>();
tmpStream->write(script->c_str(), script->size());
m_bundle = std::move(tmpStream);
if (!m_bundle) {
throw std::ios_base::failure(folly::to<std::string>(
"Bundle from string cannot be opened: ", m_bundle->rdstate()));
}
init();
}
void JSIndexedRAMBundle::init() {
// read in magic header, number of entries, and length of the startup section
uint32_t header[3];
static_assert(
sizeof(header) == 12,
"header size must exactly match the input file format");
readBundle(reinterpret_cast<char *>(header), sizeof(header));
const size_t numTableEntries = folly::Endian::little(header[1]);
const size_t startupCodeSize = folly::Endian::little(header[2]);
// allocate memory for meta data and lookup table.
m_table = ModuleTable(numTableEntries);
m_baseOffset = sizeof(header) + m_table.byteLength();
// read the lookup table from the file
readBundle(
reinterpret_cast<char *>(m_table.data.get()), m_table.byteLength());
// read the startup code
m_startupCode = std::unique_ptr<JSBigBufferString>(
new JSBigBufferString{startupCodeSize - 1});
readBundle(m_startupCode->data(), startupCodeSize - 1);
}
JSIndexedRAMBundle::Module JSIndexedRAMBundle::getModule(
uint32_t moduleId) const {
Module ret;
ret.name = folly::to<std::string>(moduleId, ".js");
ret.code = getModuleCode(moduleId);
return ret;
}
std::unique_ptr<const JSBigString> JSIndexedRAMBundle::getStartupCode() {
CHECK(m_startupCode)
<< "startup code for a RAM Bundle can only be retrieved once";
return std::move(m_startupCode);
}
std::string JSIndexedRAMBundle::getModuleCode(const uint32_t id) const {
const auto moduleData = id < m_table.numEntries ? &m_table.data[id] : nullptr;
// entries without associated code have offset = 0 and length = 0
const uint32_t length =
moduleData ? folly::Endian::little(moduleData->length) : 0;
if (length == 0) {
throw std::ios_base::failure(
folly::to<std::string>("Error loading module", id, "from RAM Bundle"));
}
std::string ret(length - 1, '\0');
readBundle(
&ret.front(),
length - 1,
m_baseOffset + folly::Endian::little(moduleData->offset));
return ret;
}
void JSIndexedRAMBundle::readBundle(char *buffer, const std::streamsize bytes)
const {
if (!m_bundle->read(buffer, bytes)) {
if (m_bundle->rdstate() & std::ios::eofbit) {
throw std::ios_base::failure("Unexpected end of RAM Bundle file");
}
throw std::ios_base::failure(folly::to<std::string>(
"Error reading RAM Bundle: ", m_bundle->rdstate()));
}
}
void JSIndexedRAMBundle::readBundle(
char *buffer,
const std::streamsize bytes,
const std::ifstream::pos_type position) const {
if (!m_bundle->seekg(position)) {
throw std::ios_base::failure(folly::to<std::string>(
"Error reading RAM Bundle: ", m_bundle->rdstate()));
}
readBundle(buffer, bytes);
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <istream>
#include <memory>
#include <cxxreact/JSBigString.h>
#include <cxxreact/JSModulesUnbundle.h>
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
namespace facebook {
namespace react {
class RN_EXPORT JSIndexedRAMBundle : public JSModulesUnbundle {
public:
static std::function<std::unique_ptr<JSModulesUnbundle>(std::string)>
buildFactory();
// Throws std::runtime_error on failure.
JSIndexedRAMBundle(const char *sourceURL);
JSIndexedRAMBundle(std::unique_ptr<const JSBigString> script);
// Throws std::runtime_error on failure.
std::unique_ptr<const JSBigString> getStartupCode();
// Throws std::runtime_error on failure.
Module getModule(uint32_t moduleId) const override;
private:
struct ModuleData {
uint32_t offset;
uint32_t length;
};
static_assert(
sizeof(ModuleData) == 8,
"ModuleData must not have any padding and use sizes matching input files");
struct ModuleTable {
size_t numEntries;
std::unique_ptr<ModuleData[]> data;
ModuleTable() : numEntries(0){};
ModuleTable(size_t entries)
: numEntries(entries),
data(std::unique_ptr<ModuleData[]>(new ModuleData[numEntries])){};
size_t byteLength() const {
return numEntries * sizeof(ModuleData);
}
};
void init();
std::string getModuleCode(const uint32_t id) const;
void readBundle(char *buffer, const std::streamsize bytes) const;
void readBundle(
char *buffer,
const std::streamsize bytes,
const std::istream::pos_type position) const;
mutable std::unique_ptr<std::istream> m_bundle;
ModuleTable m_table;
size_t m_baseOffset;
std::unique_ptr<JSBigBufferString> m_startupCode;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <cstdint>
#include <stdexcept>
#include <string>
#include <folly/Conv.h>
namespace facebook {
namespace react {
class JSModulesUnbundle {
/**
* Represents the set of JavaScript modules that the application consists of.
* The source code of each module can be retrieved by module ID.
*
* The class is non-copyable because copying instances might involve copying
* several megabytes of memory.
*/
public:
class ModuleNotFound : public std::out_of_range {
public:
using std::out_of_range::out_of_range;
ModuleNotFound(uint32_t moduleId)
: std::out_of_range::out_of_range(
folly::to<std::string>("Module not found: ", moduleId)) {}
};
struct Module {
std::string name;
std::string code;
};
JSModulesUnbundle() {}
virtual ~JSModulesUnbundle() {}
virtual Module getModule(uint32_t moduleId) const = 0;
private:
JSModulesUnbundle(const JSModulesUnbundle &) = delete;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,120 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <folly/dynamic.h>
namespace facebook {
namespace xplat {
namespace detail {
template <typename R, typename M, typename... T>
R jsArg1(const folly::dynamic &arg, M asFoo, const T &... desc) {
try {
return (arg.*asFoo)();
} catch (const folly::TypeError &ex) {
throw JsArgumentException(folly::to<std::string>(
"Error converting javascript arg ", desc..., " to C++: ", ex.what()));
} catch (const std::range_error &ex) {
throw JsArgumentException(folly::to<std::string>(
"Could not convert argument ",
desc...,
" to required type: ",
ex.what()));
}
}
} // namespace detail
template <typename R, typename... T>
R jsArg(
const folly::dynamic &arg,
R (folly::dynamic::*asFoo)() const,
const T &... desc) {
return detail::jsArg1<R>(arg, asFoo, desc...);
}
template <typename R, typename... T>
R jsArg(
const folly::dynamic &arg,
R (folly::dynamic::*asFoo)() const &,
const T &... desc) {
return detail::jsArg1<R>(arg, asFoo, desc...);
}
template <typename T>
// NOLINTNEXTLINE (T62192316)
typename detail::is_dynamic<T>::type &jsArgAsDynamic(T &&args, size_t n) {
try {
return args[n];
} catch (const std::out_of_range &ex) {
// Use 1-base counting for argument description.
throw JsArgumentException(folly::to<std::string>(
"JavaScript provided ",
args.size(),
" arguments for C++ method which references at least ",
n + 1,
" arguments: ",
ex.what()));
}
}
template <typename R>
R jsArgN(
const folly::dynamic &args,
size_t n,
R (folly::dynamic::*asFoo)() const) {
return jsArg(jsArgAsDynamic(args, n), asFoo, n);
}
template <typename R>
R jsArgN(
const folly::dynamic &args,
size_t n,
R (folly::dynamic::*asFoo)() const &) {
return jsArg(jsArgAsDynamic(args, n), asFoo, n);
}
namespace detail {
// This is a helper for jsArgAsArray and jsArgAsObject.
template <typename T>
typename detail::is_dynamic<T>::type &jsArgAsType(
T &&args,
size_t n,
const char *required,
bool (folly::dynamic::*isFoo)() const) {
T &ret = jsArgAsDynamic(args, n);
if ((ret.*isFoo)()) {
return ret;
}
// Use 1-base counting for argument description.
throw JsArgumentException(folly::to<std::string>(
"Argument ",
n + 1,
" of type ",
ret.typeName(),
" is not required type ",
required));
}
} // end namespace detail
template <typename T>
typename detail::is_dynamic<T>::type &jsArgAsArray(T &&args, size_t n) {
return detail::jsArgAsType(args, n, "Array", &folly::dynamic::isArray);
}
template <typename T>
typename detail::is_dynamic<T>::type &jsArgAsObject(T &&args, size_t n) {
return detail::jsArgAsType(args, n, "Object", &folly::dynamic::isObject);
}
} // namespace xplat
} // namespace facebook

View File

@ -0,0 +1,125 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <exception>
#include <string>
#include <folly/Conv.h>
#include <folly/dynamic.h>
// When building a cross-platform module for React Native, arguments passed
// from JS are represented as a folly::dynamic. This class provides helpers to
// extract arguments from the folly::dynamic to concrete types usable by
// cross-platform code, and converting exceptions to a JsArgumentException so
// they can be caught and reported to RN consistently. The goal is to make the
// jsArgAs... methods at the end simple to use should be most common, but any
// non-detail method can be used when needed.
namespace facebook {
namespace xplat {
class JsArgumentException : public std::logic_error {
public:
JsArgumentException(const std::string &msg) : std::logic_error(msg) {}
};
// This extracts a single argument by calling the given method pointer on it.
// If an exception is thrown, the additional arguments are passed to
// folly::to<> to be included in the exception string. This will be most
// commonly used when extracting values from non-scalar argument. The second
// overload accepts ref-qualified member functions.
template <typename R, typename... T>
R jsArg(
const folly::dynamic &arg,
R (folly::dynamic::*asFoo)() const,
const T &... desc);
template <typename R, typename... T>
R jsArg(
const folly::dynamic &arg,
R (folly::dynamic::*asFoo)() const &,
const T &... desc);
// This is like jsArg, but a operates on a dynamic representing an array of
// arguments. The argument n is used both to index the array and build the
// exception message, if any. It can be used directly, but will more often be
// used by the type-specific methods following.
template <typename R>
R jsArgN(
const folly::dynamic &args,
size_t n,
R (folly::dynamic::*asFoo)() const);
template <typename R>
R jsArgN(
const folly::dynamic &args,
size_t n,
R (folly::dynamic::*asFoo)() const &);
namespace detail {
// This is a type helper to implement functions which should work on both const
// and non-const folly::dynamic arguments, and return a type with the same
// constness. Basically, it causes the templates which use it to be defined
// only for types compatible with folly::dynamic.
template <typename T>
struct is_dynamic {
typedef typename std::
enable_if<std::is_assignable<folly::dynamic, T>::value, T>::type type;
};
} // end namespace detail
// Easy to use conversion helpers are here:
// Extract the n'th arg from the given dynamic, as a dynamic. Throws a
// JsArgumentException if there is no n'th arg in the input.
template <typename T>
typename detail::is_dynamic<T>::type &jsArgAsDynamic(T &&args, size_t n);
// Extract the n'th arg from the given dynamic, as a dynamic Array. Throws a
// JsArgumentException if there is no n'th arg in the input, or it is not an
// Array.
template <typename T>
typename detail::is_dynamic<T>::type &jsArgAsArray(T &&args, size_t n);
// Extract the n'th arg from the given dynamic, as a dynamic Object. Throws a
// JsArgumentException if there is no n'th arg in the input, or it is not an
// Object.
template <typename T>
typename detail::is_dynamic<T>::type &jsArgAsObject(T &&args, size_t n);
// Extract the n'th arg from the given dynamic, as a bool. Throws a
// JsArgumentException if this fails for some reason.
inline bool jsArgAsBool(const folly::dynamic &args, size_t n) {
return jsArgN(args, n, &folly::dynamic::asBool);
}
// Extract the n'th arg from the given dynamic, as an integer. Throws a
// JsArgumentException if this fails for some reason.
inline int64_t jsArgAsInt(const folly::dynamic &args, size_t n) {
return jsArgN(args, n, &folly::dynamic::asInt);
}
// Extract the n'th arg from the given dynamic, as a double. Throws a
// JsArgumentException if this fails for some reason.
inline double jsArgAsDouble(const folly::dynamic &args, size_t n) {
return jsArgN(args, n, &folly::dynamic::asDouble);
}
// Extract the n'th arg from the given dynamic, as a string. Throws a
// JsArgumentException if this fails for some reason.
inline std::string jsArgAsString(const folly::dynamic &args, size_t n) {
return jsArgN(args, n, &folly::dynamic::asString);
}
} // namespace xplat
} // namespace facebook
#include <cxxreact/JsArgumentHelpers-inl.h>

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <condition_variable>
#include <functional>
#include <mutex>
namespace facebook {
namespace react {
class MessageQueueThread {
public:
virtual ~MessageQueueThread() {}
virtual void runOnQueue(std::function<void()> &&) = 0;
// runOnQueueSync and quitSynchronous are dangerous. They should only be
// used for initialization and cleanup.
virtual void runOnQueueSync(std::function<void()> &&) = 0;
// Once quitSynchronous() returns, no further work should run on the queue.
virtual void quitSynchronous() = 0;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,89 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "MethodCall.h"
#include <folly/json.h>
#include <stdexcept>
namespace facebook {
namespace react {
#define REQUEST_MODULE_IDS 0
#define REQUEST_METHOD_IDS 1
#define REQUEST_PARAMSS 2
#define REQUEST_CALLID 3
static const char *errorPrefix = "Malformed calls from JS: ";
std::vector<MethodCall> parseMethodCalls(folly::dynamic &&jsonData) {
if (jsonData.isNull()) {
return {};
}
if (!jsonData.isArray()) {
throw std::invalid_argument(folly::to<std::string>(
errorPrefix, "input isn't array but ", jsonData.typeName()));
}
if (jsonData.size() < REQUEST_PARAMSS + 1) {
throw std::invalid_argument(
folly::to<std::string>(errorPrefix, "size == ", jsonData.size()));
}
auto &moduleIds = jsonData[REQUEST_MODULE_IDS];
auto &methodIds = jsonData[REQUEST_METHOD_IDS];
auto &params = jsonData[REQUEST_PARAMSS];
int callId = -1;
if (!moduleIds.isArray() || !methodIds.isArray() || !params.isArray()) {
throw std::invalid_argument(folly::to<std::string>(
errorPrefix,
"not all fields are arrays.\n\n",
folly::toJson(jsonData)));
}
if (moduleIds.size() != methodIds.size() ||
moduleIds.size() != params.size()) {
throw std::invalid_argument(folly::to<std::string>(
errorPrefix,
"field sizes are different.\n\n",
folly::toJson(jsonData)));
}
if (jsonData.size() > REQUEST_CALLID) {
if (!jsonData[REQUEST_CALLID].isNumber()) {
throw std::invalid_argument(folly::to<std::string>(
errorPrefix, "invalid callId", jsonData[REQUEST_CALLID].typeName()));
}
callId = (int)jsonData[REQUEST_CALLID].asInt();
}
std::vector<MethodCall> methodCalls;
for (size_t i = 0; i < moduleIds.size(); i++) {
if (!params[i].isArray()) {
throw std::invalid_argument(folly::to<std::string>(
errorPrefix,
"method arguments isn't array but ",
params[i].typeName()));
}
methodCalls.emplace_back(
moduleIds[i].asInt(),
methodIds[i].asInt(),
std::move(params[i]),
callId);
// only increment callid if contains valid callid as callid is optional
callId += (callId != -1) ? 1 : 0;
}
return methodCalls;
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <map>
#include <string>
#include <vector>
#include <folly/dynamic.h>
namespace facebook {
namespace react {
struct MethodCall {
int moduleId;
int methodId;
folly::dynamic arguments;
int callId;
MethodCall(int mod, int meth, folly::dynamic &&args, int cid)
: moduleId(mod),
methodId(meth),
arguments(std::move(args)),
callId(cid) {}
};
/// \throws std::invalid_argument
std::vector<MethodCall> parseMethodCalls(folly::dynamic &&calls);
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,186 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "ModuleRegistry.h"
#include <glog/logging.h>
#include "NativeModule.h"
#include "SystraceSection.h"
namespace facebook {
namespace react {
namespace {
std::string normalizeName(std::string name) {
// TODO mhorowitz #10487027: This is super ugly. We should just
// change iOS to emit normalized names, drop the "RK..." from
// names hardcoded in Android, and then delete this and the
// similar hacks in js.
if (name.compare(0, 3, "RCT") == 0) {
return name.substr(3);
} else if (name.compare(0, 2, "RK") == 0) {
return name.substr(2);
}
return name;
}
} // namespace
ModuleRegistry::ModuleRegistry(
std::vector<std::unique_ptr<NativeModule>> modules,
ModuleNotFoundCallback callback)
: modules_{std::move(modules)}, moduleNotFoundCallback_{callback} {}
void ModuleRegistry::updateModuleNamesFromIndex(size_t index) {
for (; index < modules_.size(); index++) {
std::string name = normalizeName(modules_[index]->getName());
modulesByName_[name] = index;
}
}
void ModuleRegistry::registerModules(
std::vector<std::unique_ptr<NativeModule>> modules) {
SystraceSection s_("ModuleRegistry::registerModules");
if (modules_.empty() && unknownModules_.empty()) {
modules_ = std::move(modules);
} else {
size_t modulesSize = modules_.size();
size_t addModulesSize = modules.size();
bool addToNames = !modulesByName_.empty();
modules_.reserve(modulesSize + addModulesSize);
std::move(modules.begin(), modules.end(), std::back_inserter(modules_));
if (!unknownModules_.empty()) {
for (size_t index = modulesSize; index < modulesSize + addModulesSize;
index++) {
std::string name = normalizeName(modules_[index]->getName());
auto it = unknownModules_.find(name);
if (it != unknownModules_.end()) {
throw std::runtime_error(folly::to<std::string>(
"module ",
name,
" was required without being registered and is now being registered."));
} else if (addToNames) {
modulesByName_[name] = index;
}
}
} else if (addToNames) {
updateModuleNamesFromIndex(modulesSize);
}
}
}
std::vector<std::string> ModuleRegistry::moduleNames() {
SystraceSection s_("ModuleRegistry::moduleNames");
std::vector<std::string> names;
for (size_t i = 0; i < modules_.size(); i++) {
std::string name = normalizeName(modules_[i]->getName());
modulesByName_[name] = i;
names.push_back(std::move(name));
}
return names;
}
folly::Optional<ModuleConfig> ModuleRegistry::getConfig(
const std::string &name) {
SystraceSection s("ModuleRegistry::getConfig", "module", name);
// Initialize modulesByName_
if (modulesByName_.empty() && !modules_.empty()) {
moduleNames();
}
auto it = modulesByName_.find(name);
if (it == modulesByName_.end()) {
if (unknownModules_.find(name) != unknownModules_.end()) {
return folly::none;
}
if (!moduleNotFoundCallback_ || !moduleNotFoundCallback_(name) ||
(it = modulesByName_.find(name)) == modulesByName_.end()) {
unknownModules_.insert(name);
return folly::none;
}
}
size_t index = it->second;
CHECK(index < modules_.size());
NativeModule *module = modules_[index].get();
// string name, object constants, array methodNames (methodId is index),
// [array promiseMethodIds], [array syncMethodIds]
folly::dynamic config = folly::dynamic::array(name);
{
SystraceSection s_("ModuleRegistry::getConstants", "module", name);
config.push_back(module->getConstants());
}
{
SystraceSection s_("ModuleRegistry::getMethods", "module", name);
std::vector<MethodDescriptor> methods = module->getMethods();
folly::dynamic methodNames = folly::dynamic::array;
folly::dynamic promiseMethodIds = folly::dynamic::array;
folly::dynamic syncMethodIds = folly::dynamic::array;
for (auto &descriptor : methods) {
// TODO: #10487027 compare tags instead of doing string comparison?
methodNames.push_back(std::move(descriptor.name));
if (descriptor.type == "promise") {
promiseMethodIds.push_back(methodNames.size() - 1);
} else if (descriptor.type == "sync") {
syncMethodIds.push_back(methodNames.size() - 1);
}
}
if (!methodNames.empty()) {
config.push_back(std::move(methodNames));
if (!promiseMethodIds.empty() || !syncMethodIds.empty()) {
config.push_back(std::move(promiseMethodIds));
if (!syncMethodIds.empty()) {
config.push_back(std::move(syncMethodIds));
}
}
}
}
if (config.size() == 2 && config[1].empty()) {
// no constants or methods
return folly::none;
} else {
return ModuleConfig{index, config};
}
}
void ModuleRegistry::callNativeMethod(
unsigned int moduleId,
unsigned int methodId,
folly::dynamic &&params,
int callId) {
if (moduleId >= modules_.size()) {
throw std::runtime_error(folly::to<std::string>(
"moduleId ", moduleId, " out of range [0..", modules_.size(), ")"));
}
modules_[moduleId]->invoke(methodId, std::move(params), callId);
}
MethodCallResult ModuleRegistry::callSerializableNativeHook(
unsigned int moduleId,
unsigned int methodId,
folly::dynamic &&params) {
if (moduleId >= modules_.size()) {
throw std::runtime_error(folly::to<std::string>(
"moduleId ", moduleId, "out of range [0..", modules_.size(), ")"));
}
return modules_[moduleId]->callSerializableNativeHook(
methodId, std::move(params));
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <unordered_set>
#include <vector>
#include <cxxreact/JSExecutor.h>
#include <folly/Optional.h>
#include <folly/dynamic.h>
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
namespace facebook {
namespace react {
class NativeModule;
struct ModuleConfig {
size_t index;
folly::dynamic config;
};
class RN_EXPORT ModuleRegistry {
public:
// not implemented:
// onBatchComplete: see
// https://our.intern.facebook.com/intern/tasks/?t=5279396 getModule: only
// used by views getAllModules: only used for cleanup; use RAII instead
// notifyCatalystInstanceInitialized: this is really only used by view-related
// code notifyCatalystInstanceDestroy: use RAII instead
using ModuleNotFoundCallback = std::function<bool(const std::string &name)>;
ModuleRegistry(
std::vector<std::unique_ptr<NativeModule>> modules,
ModuleNotFoundCallback callback = nullptr);
void registerModules(std::vector<std::unique_ptr<NativeModule>> modules);
std::vector<std::string> moduleNames();
folly::Optional<ModuleConfig> getConfig(const std::string &name);
void callNativeMethod(
unsigned int moduleId,
unsigned int methodId,
folly::dynamic &&params,
int callId);
MethodCallResult callSerializableNativeHook(
unsigned int moduleId,
unsigned int methodId,
folly::dynamic &&args);
private:
// This is always populated
std::vector<std::unique_ptr<NativeModule>> modules_;
// This is used to extend the population of modulesByName_ if registerModules
// is called after moduleNames
void updateModuleNamesFromIndex(size_t size);
// This is only populated if moduleNames() is called. Values are indices into
// modules_.
std::unordered_map<std::string, size_t> modulesByName_;
// This is populated with modules that are requested via getConfig but are
// unknown. An error will be thrown if they are subsequently added to the
// registry.
std::unordered_set<std::string> unknownModules_;
// Function will be called if a module was requested but was not found.
// If the function returns true, ModuleRegistry will try to find the module
// again (assuming it's registered) If the functon returns false,
// ModuleRegistry will not try to find the module and return nullptr instead.
ModuleNotFoundCallback moduleNotFoundCallback_;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <string>
#include <vector>
#include <folly/Optional.h>
#include <folly/dynamic.h>
namespace facebook {
namespace react {
struct MethodDescriptor {
std::string name;
// type is one of js MessageQueue.MethodTypes
std::string type;
MethodDescriptor(std::string n, std::string t)
: name(std::move(n)), type(std::move(t)) {}
};
using MethodCallResult = folly::Optional<folly::dynamic>;
class NativeModule {
public:
virtual ~NativeModule() {}
virtual std::string getName() = 0;
virtual std::vector<MethodDescriptor> getMethods() = 0;
virtual folly::dynamic getConstants() = 0;
virtual void
invoke(unsigned int reactMethodId, folly::dynamic &&params, int callId) = 0;
virtual MethodCallResult callSerializableNativeHook(
unsigned int reactMethodId,
folly::dynamic &&args) = 0;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,335 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "NativeToJsBridge.h"
#include <ReactCommon/CallInvoker.h>
#include <folly/MoveWrapper.h>
#include <folly/json.h>
#include <glog/logging.h>
#include "Instance.h"
#include "JSBigString.h"
#include "MessageQueueThread.h"
#include "MethodCall.h"
#include "ModuleRegistry.h"
#include "RAMBundleRegistry.h"
#include "SystraceSection.h"
#include <memory>
#ifdef WITH_FBSYSTRACE
#include <fbsystrace.h>
using fbsystrace::FbSystraceAsyncFlow;
#endif
namespace facebook {
namespace react {
// This class manages calls from JS to native code.
class JsToNativeBridge : public react::ExecutorDelegate {
public:
JsToNativeBridge(
std::shared_ptr<ModuleRegistry> registry,
std::shared_ptr<InstanceCallback> callback)
: m_registry(registry), m_callback(callback) {}
std::shared_ptr<ModuleRegistry> getModuleRegistry() override {
return m_registry;
}
bool isBatchActive() {
return m_batchHadNativeModuleOrTurboModuleCalls;
}
void callNativeModules(
__unused JSExecutor &executor,
folly::dynamic &&calls,
bool isEndOfBatch) override {
CHECK(m_registry || calls.empty())
<< "native module calls cannot be completed with no native modules";
m_batchHadNativeModuleOrTurboModuleCalls =
m_batchHadNativeModuleOrTurboModuleCalls || !calls.empty();
// An exception anywhere in here stops processing of the batch. This
// was the behavior of the Android bridge, and since exception handling
// terminates the whole bridge, there's not much point in continuing.
for (auto &call : parseMethodCalls(std::move(calls))) {
m_registry->callNativeMethod(
call.moduleId, call.methodId, std::move(call.arguments), call.callId);
}
if (isEndOfBatch) {
// onBatchComplete will be called on the native (module) queue, but
// decrementPendingJSCalls will be called sync. Be aware that the bridge
// may still be processing native calls when the bridge idle signaler
// fires.
if (m_batchHadNativeModuleOrTurboModuleCalls) {
m_callback->onBatchComplete();
m_batchHadNativeModuleOrTurboModuleCalls = false;
}
m_callback->decrementPendingJSCalls();
}
}
MethodCallResult callSerializableNativeHook(
__unused JSExecutor &executor,
unsigned int moduleId,
unsigned int methodId,
folly::dynamic &&args) override {
return m_registry->callSerializableNativeHook(
moduleId, methodId, std::move(args));
}
void recordTurboModuleAsyncMethodCall() {
m_batchHadNativeModuleOrTurboModuleCalls = true;
}
private:
// These methods are always invoked from an Executor. The NativeToJsBridge
// keeps a reference to the executor, and when destroy() is called, the
// executor is destroyed synchronously on its queue.
std::shared_ptr<ModuleRegistry> m_registry;
std::shared_ptr<InstanceCallback> m_callback;
bool m_batchHadNativeModuleOrTurboModuleCalls = false;
};
NativeToJsBridge::NativeToJsBridge(
JSExecutorFactory *jsExecutorFactory,
std::shared_ptr<ModuleRegistry> registry,
std::shared_ptr<MessageQueueThread> jsQueue,
std::shared_ptr<InstanceCallback> callback)
: m_destroyed(std::make_shared<bool>(false)),
m_delegate(std::make_shared<JsToNativeBridge>(registry, callback)),
m_executor(jsExecutorFactory->createJSExecutor(m_delegate, jsQueue)),
m_executorMessageQueueThread(std::move(jsQueue)),
m_inspectable(m_executor->isInspectable()) {}
// This must be called on the same thread on which the constructor was called.
NativeToJsBridge::~NativeToJsBridge() {
CHECK(*m_destroyed)
<< "NativeToJsBridge::destroy() must be called before deallocating the NativeToJsBridge!";
}
void NativeToJsBridge::initializeRuntime() {
runOnExecutorQueue(
[](JSExecutor *executor) mutable { executor->initializeRuntime(); });
}
void NativeToJsBridge::loadBundle(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL) {
runOnExecutorQueue(
[this,
bundleRegistryWrap = folly::makeMoveWrapper(std::move(bundleRegistry)),
startupScript = folly::makeMoveWrapper(std::move(startupScript)),
startupScriptSourceURL =
std::move(startupScriptSourceURL)](JSExecutor *executor) mutable {
auto bundleRegistry = bundleRegistryWrap.move();
if (bundleRegistry) {
executor->setBundleRegistry(std::move(bundleRegistry));
}
try {
executor->loadBundle(
std::move(*startupScript), std::move(startupScriptSourceURL));
} catch (...) {
m_applicationScriptHasFailure = true;
throw;
}
});
}
void NativeToJsBridge::loadBundleSync(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL) {
if (bundleRegistry) {
m_executor->setBundleRegistry(std::move(bundleRegistry));
}
try {
m_executor->loadBundle(
std::move(startupScript), std::move(startupScriptSourceURL));
} catch (...) {
m_applicationScriptHasFailure = true;
throw;
}
}
void NativeToJsBridge::callFunction(
std::string &&module,
std::string &&method,
folly::dynamic &&arguments) {
int systraceCookie = -1;
#ifdef WITH_FBSYSTRACE
systraceCookie = m_systraceCookie++;
FbSystraceAsyncFlow::begin(
TRACE_TAG_REACT_CXX_BRIDGE, "JSCall", systraceCookie);
#endif
runOnExecutorQueue([this,
module = std::move(module),
method = std::move(method),
arguments = std::move(arguments),
systraceCookie](JSExecutor *executor) {
if (m_applicationScriptHasFailure) {
LOG(ERROR)
<< "Attempting to call JS function on a bad application bundle: "
<< module.c_str() << "." << method.c_str() << "()";
throw std::runtime_error(
"Attempting to call JS function on a bad application bundle: " +
module + "." + method + "()");
}
#ifdef WITH_FBSYSTRACE
FbSystraceAsyncFlow::end(
TRACE_TAG_REACT_CXX_BRIDGE, "JSCall", systraceCookie);
SystraceSection s(
"NativeToJsBridge::callFunction", "module", module, "method", method);
#else
(void)(systraceCookie);
#endif
// This is safe because we are running on the executor's thread: it won't
// destruct until after it's been unregistered (which we check above) and
// that will happen on this thread
executor->callFunction(module, method, arguments);
});
}
void NativeToJsBridge::invokeCallback(
double callbackId,
folly::dynamic &&arguments) {
int systraceCookie = -1;
#ifdef WITH_FBSYSTRACE
systraceCookie = m_systraceCookie++;
FbSystraceAsyncFlow::begin(
TRACE_TAG_REACT_CXX_BRIDGE, "<callback>", systraceCookie);
#endif
runOnExecutorQueue(
[this, callbackId, arguments = std::move(arguments), systraceCookie](
JSExecutor *executor) {
if (m_applicationScriptHasFailure) {
LOG(ERROR)
<< "Attempting to call JS callback on a bad application bundle: "
<< callbackId;
throw std::runtime_error(
"Attempting to invoke JS callback on a bad application bundle.");
}
#ifdef WITH_FBSYSTRACE
FbSystraceAsyncFlow::end(
TRACE_TAG_REACT_CXX_BRIDGE, "<callback>", systraceCookie);
SystraceSection s("NativeToJsBridge::invokeCallback");
#else
(void)(systraceCookie);
#endif
executor->invokeCallback(callbackId, arguments);
});
}
void NativeToJsBridge::registerBundle(
uint32_t bundleId,
const std::string &bundlePath) {
runOnExecutorQueue([bundleId, bundlePath](JSExecutor *executor) {
executor->registerBundle(bundleId, bundlePath);
});
}
void NativeToJsBridge::setGlobalVariable(
std::string propName,
std::unique_ptr<const JSBigString> jsonValue) {
runOnExecutorQueue([propName = std::move(propName),
jsonValue = folly::makeMoveWrapper(std::move(jsonValue))](
JSExecutor *executor) mutable {
executor->setGlobalVariable(propName, jsonValue.move());
});
}
void *NativeToJsBridge::getJavaScriptContext() {
// TODO(cjhopman): this seems unsafe unless we require that it is only called
// on the main js queue.
return m_executor->getJavaScriptContext();
}
bool NativeToJsBridge::isInspectable() {
return m_inspectable;
}
bool NativeToJsBridge::isBatchActive() {
return m_delegate->isBatchActive();
}
void NativeToJsBridge::handleMemoryPressure(int pressureLevel) {
runOnExecutorQueue([=](JSExecutor *executor) {
executor->handleMemoryPressure(pressureLevel);
});
}
void NativeToJsBridge::destroy() {
// All calls made through runOnExecutorQueue have an early exit if
// m_destroyed is true. Setting this before the runOnQueueSync will cause
// pending work to be cancelled and we won't have to wait for it.
*m_destroyed = true;
m_executorMessageQueueThread->runOnQueueSync([this] {
m_executor->destroy();
m_executorMessageQueueThread->quitSynchronous();
m_executor = nullptr;
});
}
void NativeToJsBridge::runOnExecutorQueue(
std::function<void(JSExecutor *)> task) {
if (*m_destroyed) {
return;
}
std::shared_ptr<bool> isDestroyed = m_destroyed;
m_executorMessageQueueThread->runOnQueue(
[this, isDestroyed, task = std::move(task)] {
if (*isDestroyed) {
return;
}
// The executor is guaranteed to be valid for the duration of the task
// because:
// 1. the executor is only destroyed after it is unregistered
// 2. the executor is unregistered on this queue
// 3. we just confirmed that the executor hasn't been unregistered above
task(m_executor.get());
});
}
std::shared_ptr<CallInvoker> NativeToJsBridge::getDecoratedNativeCallInvoker(
std::shared_ptr<CallInvoker> nativeInvoker) {
class NativeCallInvoker : public CallInvoker {
private:
std::weak_ptr<JsToNativeBridge> m_jsToNativeBridge;
std::shared_ptr<CallInvoker> m_nativeInvoker;
public:
NativeCallInvoker(
std::weak_ptr<JsToNativeBridge> jsToNativeBridge,
std::shared_ptr<CallInvoker> nativeInvoker)
: m_jsToNativeBridge(jsToNativeBridge),
m_nativeInvoker(nativeInvoker) {}
void invokeAsync(std::function<void()> &&func) override {
if (auto strongJsToNativeBridge = m_jsToNativeBridge.lock()) {
strongJsToNativeBridge->recordTurboModuleAsyncMethodCall();
}
m_nativeInvoker->invokeAsync(std::move(func));
}
void invokeSync(std::function<void()> &&func) override {
m_nativeInvoker->invokeSync(std::move(func));
}
};
return std::make_shared<NativeCallInvoker>(m_delegate, nativeInvoker);
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,135 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <atomic>
#include <functional>
#include <map>
#include <vector>
#include <ReactCommon/CallInvoker.h>
#include <cxxreact/JSExecutor.h>
namespace folly {
struct dynamic;
}
namespace facebook {
namespace react {
struct InstanceCallback;
class JsToNativeBridge;
class MessageQueueThread;
class ModuleRegistry;
class RAMBundleRegistry;
// This class manages calls from native code to JS. It also manages
// executors and their threads. All functions here can be called from
// any thread.
//
// Except for loadBundleSync(), all void methods will queue
// work to run on the jsQueue passed to the ctor, and return
// immediately.
class NativeToJsBridge {
public:
friend class JsToNativeBridge;
/**
* This must be called on the main JS thread.
*/
NativeToJsBridge(
JSExecutorFactory *jsExecutorFactory,
std::shared_ptr<ModuleRegistry> registry,
std::shared_ptr<MessageQueueThread> jsQueue,
std::shared_ptr<InstanceCallback> callback);
virtual ~NativeToJsBridge();
/**
* Executes a function with the module ID and method ID and any additional
* arguments in JS.
*/
void callFunction(
std::string &&module,
std::string &&method,
folly::dynamic &&args);
/**
* Invokes a callback with the cbID, and optional additional arguments in JS.
*/
void invokeCallback(double callbackId, folly::dynamic &&args);
/**
* Sets global variables in the JS Context.
*/
void initializeRuntime();
/**
* Starts the JS application. If bundleRegistry is non-null, then it is
* used to fetch JavaScript modules as individual scripts.
* Otherwise, the script is assumed to include all the modules.
*/
void loadBundle(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupCode,
std::string sourceURL);
void loadBundleSync(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupCode,
std::string sourceURL);
void registerBundle(uint32_t bundleId, const std::string &bundlePath);
void setGlobalVariable(
std::string propName,
std::unique_ptr<const JSBigString> jsonValue);
void *getJavaScriptContext();
bool isInspectable();
bool isBatchActive();
void handleMemoryPressure(int pressureLevel);
/**
* Synchronously tears down the bridge and the main executor.
*/
void destroy();
void runOnExecutorQueue(std::function<void(JSExecutor *)> task);
/**
* Native CallInvoker is used by TurboModules to schedule work on the
* NativeModule thread(s).
*/
std::shared_ptr<CallInvoker> getDecoratedNativeCallInvoker(
std::shared_ptr<CallInvoker> nativeInvoker);
private:
// This is used to avoid a race condition where a proxyCallback gets queued
// after ~NativeToJsBridge(), on the same thread. In that case, the callback
// will try to run the task on m_callback which will have been destroyed
// within ~NativeToJsBridge(), thus causing a SIGSEGV.
std::shared_ptr<bool> m_destroyed;
std::shared_ptr<JsToNativeBridge> m_delegate;
std::unique_ptr<JSExecutor> m_executor;
std::shared_ptr<MessageQueueThread> m_executorMessageQueueThread;
// Memoize this on the JS thread, so that it can be inspected from
// any thread later. This assumes inspectability doesn't change for
// a JSExecutor instance, which is true for all existing implementations.
bool m_inspectable;
// Keep track of whether the JS bundle containing the application logic causes
// exception when evaluated initially. If so, more calls to JS will very
// likely fail as well, so this flag can help prevent them.
bool m_applicationScriptHasFailure = false;
#ifdef WITH_FBSYSTRACE
std::atomic_uint_least32_t m_systraceCookie = ATOMIC_VAR_INIT(0);
#endif
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,78 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "RAMBundleRegistry.h"
#include <folly/String.h>
#include <memory>
namespace facebook {
namespace react {
constexpr uint32_t RAMBundleRegistry::MAIN_BUNDLE_ID;
std::unique_ptr<RAMBundleRegistry> RAMBundleRegistry::singleBundleRegistry(
std::unique_ptr<JSModulesUnbundle> mainBundle) {
return std::make_unique<RAMBundleRegistry>(std::move(mainBundle));
}
std::unique_ptr<RAMBundleRegistry> RAMBundleRegistry::multipleBundlesRegistry(
std::unique_ptr<JSModulesUnbundle> mainBundle,
std::function<std::unique_ptr<JSModulesUnbundle>(std::string)> factory) {
return std::make_unique<RAMBundleRegistry>(
std::move(mainBundle), std::move(factory));
}
RAMBundleRegistry::RAMBundleRegistry(
std::unique_ptr<JSModulesUnbundle> mainBundle,
std::function<std::unique_ptr<JSModulesUnbundle>(std::string)> factory)
: m_factory(std::move(factory)) {
m_bundles.emplace(MAIN_BUNDLE_ID, std::move(mainBundle));
}
void RAMBundleRegistry::registerBundle(
uint32_t bundleId,
std::string bundlePath) {
m_bundlePaths.emplace(bundleId, std::move(bundlePath));
}
JSModulesUnbundle::Module RAMBundleRegistry::getModule(
uint32_t bundleId,
uint32_t moduleId) {
if (m_bundles.find(bundleId) == m_bundles.end()) {
if (!m_factory) {
throw std::runtime_error(
"You need to register factory function in order to "
"support multiple RAM bundles.");
}
auto bundlePath = m_bundlePaths.find(bundleId);
if (bundlePath == m_bundlePaths.end()) {
throw std::runtime_error(
"In order to fetch RAM bundle from the registry, its file "
"path needs to be registered first.");
}
m_bundles.emplace(bundleId, m_factory(bundlePath->second));
}
auto module = getBundle(bundleId)->getModule(moduleId);
if (bundleId == MAIN_BUNDLE_ID) {
return module;
}
return {
folly::to<std::string>("seg-", bundleId, '_', std::move(module.name)),
std::move(module.code),
};
}
JSModulesUnbundle *RAMBundleRegistry::getBundle(uint32_t bundleId) const {
return m_bundles.at(bundleId).get();
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <cstdint>
#include <functional>
#include <memory>
#include <unordered_map>
#include <utility>
#include <cxxreact/JSModulesUnbundle.h>
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
namespace facebook {
namespace react {
class RN_EXPORT RAMBundleRegistry {
public:
constexpr static uint32_t MAIN_BUNDLE_ID = 0;
static std::unique_ptr<RAMBundleRegistry> singleBundleRegistry(
std::unique_ptr<JSModulesUnbundle> mainBundle);
static std::unique_ptr<RAMBundleRegistry> multipleBundlesRegistry(
std::unique_ptr<JSModulesUnbundle> mainBundle,
std::function<std::unique_ptr<JSModulesUnbundle>(std::string)> factory);
explicit RAMBundleRegistry(
std::unique_ptr<JSModulesUnbundle> mainBundle,
std::function<std::unique_ptr<JSModulesUnbundle>(std::string)> factory =
nullptr);
RAMBundleRegistry(RAMBundleRegistry &&) = default;
RAMBundleRegistry &operator=(RAMBundleRegistry &&) = default;
void registerBundle(uint32_t bundleId, std::string bundlePath);
JSModulesUnbundle::Module getModule(uint32_t bundleId, uint32_t moduleId);
virtual ~RAMBundleRegistry(){};
private:
JSModulesUnbundle *getBundle(uint32_t bundleId) const;
std::function<std::unique_ptr<JSModulesUnbundle>(std::string)> m_factory;
std::unordered_map<uint32_t, std::string> m_bundlePaths;
std::unordered_map<uint32_t, std::unique_ptr<JSModulesUnbundle>> m_bundles;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,44 @@
# Copyright (c) Facebook, Inc. and its 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
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 = '2020.01.13.00'
boost_compiler_flags = '-Wno-documentation'
Pod::Spec.new do |s|
s.name = "React-cxxreact"
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 => "10.0", :tvos => "10.0" }
s.source = source
s.source_files = "*.{cpp,h}"
s.exclude_files = "SampleCxxModule.*"
s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags
s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/Folly\" \"$(PODS_ROOT)/DoubleConversion\"" }
s.header_dir = "cxxreact"
s.dependency "boost-for-react-native", "1.63.0"
s.dependency "DoubleConversion"
s.dependency "Folly", folly_version
s.dependency "glog"
s.dependency "React-jsinspector", version
s.dependency "React-callinvoker", version
end

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "ReactMarker.h"
namespace facebook {
namespace react {
namespace ReactMarker {
#if __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
LogTaggedMarker logTaggedMarker = nullptr;
#if __clang__
#pragma clang diagnostic pop
#endif
void logMarker(const ReactMarkerId markerId) {
logTaggedMarker(markerId, nullptr);
}
} // namespace ReactMarker
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) Facebook, Inc. and its 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
#ifdef __APPLE__
#include <functional>
#endif
namespace facebook {
namespace react {
namespace ReactMarker {
enum ReactMarkerId {
NATIVE_REQUIRE_START,
NATIVE_REQUIRE_STOP,
RUN_JS_BUNDLE_START,
RUN_JS_BUNDLE_STOP,
CREATE_REACT_CONTEXT_STOP,
JS_BUNDLE_STRING_CONVERT_START,
JS_BUNDLE_STRING_CONVERT_STOP,
NATIVE_MODULE_SETUP_START,
NATIVE_MODULE_SETUP_STOP,
REGISTER_JS_SEGMENT_START,
REGISTER_JS_SEGMENT_STOP
};
#ifdef __APPLE__
using LogTaggedMarker =
std::function<void(const ReactMarkerId, const char *tag)>;
#else
typedef void (*LogTaggedMarker)(const ReactMarkerId, const char *tag);
#endif
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
extern RN_EXPORT LogTaggedMarker logTaggedMarker;
extern RN_EXPORT void logMarker(const ReactMarkerId markerId);
} // namespace ReactMarker
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,24 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated by scripts/bump-oss-version.js
*/
#pragma once
#include <cstdint>
#include <string_view>
namespace facebook::react {
constexpr struct {
int32_t Major = 0;
int32_t Minor = 63;
int32_t Patch = 4;
std::string_view Prerelease = "";
} ReactNativeVersion;
} // namespace facebook::react

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <exception>
#include <functional>
#include <string>
namespace facebook {
namespace react {
/**
* RecoverableError
*
* An exception that it is expected we should be able to recover from.
*/
struct RecoverableError : public std::exception {
explicit RecoverableError(const std::string &what_)
: m_what{"facebook::react::Recoverable: " + what_} {}
virtual const char *what() const noexcept override {
return m_what.c_str();
}
/**
* runRethrowingAsRecoverable
*
* Helper function that converts any exception of type `E`, thrown within the
* `act` routine into a recoverable error with the same message.
*/
template <typename E>
inline static void runRethrowingAsRecoverable(std::function<void()> act) {
try {
act();
} catch (const E &err) {
throw RecoverableError(err.what());
}
}
private:
std::string m_what;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,182 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "SampleCxxModule.h"
#include <cxxreact/JsArgumentHelpers.h>
#include <glog/logging.h>
#include <memory>
#include <thread>
using namespace folly;
namespace facebook {
namespace xplat {
namespace samples {
std::string Sample::hello() {
LOG(WARNING) << "glog: hello, world";
return "hello";
}
double Sample::add(double a, double b) {
return a + b;
}
std::string Sample::concat(const std::string &a, const std::string &b) {
return a + b;
}
std::string Sample::repeat(int count, const std::string &str) {
std::string ret;
for (int i = 0; i < count; i++) {
ret += str;
}
return ret;
}
void Sample::save(std::map<std::string, std::string> dict) {
state_ = std::move(dict);
}
std::map<std::string, std::string> Sample::load() {
return state_;
}
void Sample::except() {
// TODO mhorowitz #7128529: There's no way to automatically test this
// right now.
// throw std::runtime_error("oops");
}
void Sample::call_later(int msec, std::function<void()> f) {
std::thread t([=] {
std::this_thread::sleep_for(std::chrono::milliseconds(msec));
f();
});
t.detach();
}
double Sample::twice(double n) {
return n * 2;
}
SampleCxxModule::SampleCxxModule(std::unique_ptr<Sample> sample)
: sample_(std::move(sample)) {}
std::string SampleCxxModule::getName() {
return "Sample";
}
auto SampleCxxModule::getConstants() -> std::map<std::string, folly::dynamic> {
return {
{"one", 1},
{"two", 2},
{"animal", "fox"},
};
}
auto SampleCxxModule::getMethods() -> std::vector<Method> {
return {
Method("hello", [this] { sample_->hello(); }),
Method(
"add",
[this](dynamic args, Callback cb) {
LOG(WARNING) << "Sample: add => "
<< sample_->add(
jsArgAsDouble(args, 0), jsArgAsDouble(args, 1));
cb({sample_->add(jsArgAsDouble(args, 0), jsArgAsDouble(args, 1))});
}),
Method(
"concat",
[this](dynamic args, Callback cb) {
cb({sample_->concat(
jsArgAsString(args, 0), jsArgAsString(args, 1))});
}),
Method(
"repeat",
[this](dynamic args, Callback cb) {
cb({sample_->repeat(
(int)jsArgAsInt(args, 0), jsArgAsString(args, 1))});
}),
Method("save", this, &SampleCxxModule::save),
Method("load", this, &SampleCxxModule::load),
Method(
"call_later",
[this](dynamic args, Callback cb) {
sample_->call_later((int)jsArgAsInt(args, 0), [cb] { cb({}); });
}),
Method("except", [this] { sample_->except(); }),
Method(
"twice",
[this](dynamic args) -> dynamic {
return sample_->twice(jsArgAsDouble(args, 0));
},
SyncTag),
Method(
"syncHello",
[this]() -> dynamic {
sample_->hello();
return nullptr;
},
SyncTag),
Method(
"addIfPositiveAsPromise",
[](dynamic args, Callback cb, Callback cbError) {
auto a = jsArgAsDouble(args, 0);
auto b = jsArgAsDouble(args, 1);
if (a < 0 || b < 0) {
cbError({"Negative number!"});
} else {
cb({a + b});
}
}),
Method(
"addIfPositiveAsAsync",
[](dynamic args, Callback cb, Callback cbError) {
auto a = jsArgAsDouble(args, 0);
auto b = jsArgAsDouble(args, 1);
if (a < 0 || b < 0) {
cbError({"Negative number!"});
} else {
cb({a + b});
}
},
AsyncTag),
};
}
void SampleCxxModule::save(folly::dynamic args) {
std::map<std::string, std::string> m;
for (const auto &p : jsArgN(args, 0, &dynamic::items)) {
m.emplace(
jsArg(p.first, &dynamic::asString, "map key"),
jsArg(p.second, &dynamic::asString, "map value"));
}
sample_->save(std::move(m));
}
void SampleCxxModule::load(__unused folly::dynamic args, Callback cb) {
dynamic d = dynamic::object;
for (const auto &p : sample_->load()) {
d.insert(p.first, p.second);
}
cb({d});
}
} // namespace samples
} // namespace xplat
} // namespace facebook
// By convention, the function name should be the same as the class name.
facebook::xplat::module::CxxModule *SampleCxxModule() {
return new facebook::xplat::samples::SampleCxxModule(
std::make_unique<facebook::xplat::samples::Sample>());
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <vector>
#include <cxxreact/CxxModule.h>
namespace facebook {
namespace xplat {
namespace samples {
// In a less contrived example, Sample would be part of a traditional
// C++ library.
class Sample {
public:
std::string hello();
double add(double a, double b);
std::string concat(const std::string &a, const std::string &b);
std::string repeat(int count, const std::string &str);
void save(std::map<std::string, std::string> dict);
std::map<std::string, std::string> load();
void call_later(int msec, std::function<void()> f);
void except();
double twice(double n);
private:
std::map<std::string, std::string> state_;
};
class SampleCxxModule : public module::CxxModule {
public:
SampleCxxModule(std::unique_ptr<Sample> sample);
std::string getName();
virtual auto getConstants() -> std::map<std::string, folly::dynamic>;
virtual auto getMethods() -> std::vector<Method>;
private:
void save(folly::dynamic args);
void load(folly::dynamic args, Callback cb);
std::unique_ptr<Sample> sample_;
};
} // namespace samples
} // namespace xplat
} // namespace facebook
extern "C" facebook::xplat::module::CxxModule *SampleCxxModule();

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <memory>
#include <cxxreact/CxxModule.h>
namespace facebook {
namespace xplat {
namespace module {
// Allows a Cxx-module to be shared or reused across multiple React instances
// Caveat: the setInstance call is not forwarded, so usages of getInstance
// inside your module (e.g. dispatching events) will always be nullptr.
class SharedProxyCxxModule : public CxxModule {
public:
explicit SharedProxyCxxModule(std::shared_ptr<CxxModule> shared)
: shared_(shared) {}
std::string getName() override {
return shared_->getName();
}
auto getConstants() -> std::map<std::string, folly::dynamic> override {
return shared_->getConstants();
}
auto getMethods() -> std::vector<Method> override {
return shared_->getMethods();
}
private:
std::shared_ptr<CxxModule> shared_;
};
} // namespace module
} // namespace xplat
} // namespace facebook

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) Facebook, Inc. and its 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
#ifdef WITH_FBSYSTRACE
#include <fbsystrace.h>
#endif
namespace facebook {
namespace react {
/**
* This is a convenience class to avoid lots of verbose profiling
* #ifdefs. If WITH_FBSYSTRACE is not defined, the optimizer will
* remove this completely. If it is defined, it will behave as
* FbSystraceSection, with the right tag provided. Use two separate classes to
* to ensure that the ODR rule isn't violated, that is, if WITH_FBSYSTRACE has
* different values in different files, there is no inconsistency in the sizes
* of defined symbols.
*/
#ifdef WITH_FBSYSTRACE
struct ConcreteSystraceSection {
public:
template <typename... ConvertsToStringPiece>
explicit ConcreteSystraceSection(
const char *name,
ConvertsToStringPiece &&... args)
: m_section(TRACE_TAG_REACT_CXX_BRIDGE, name, args...) {}
private:
fbsystrace::FbSystraceSection m_section;
};
using SystraceSection = ConcreteSystraceSection;
#else
struct DummySystraceSection {
public:
template <typename... ConvertsToStringPiece>
explicit DummySystraceSection(
__unused const char *name,
__unused ConvertsToStringPiece &&... args) {}
};
using SystraceSection = DummySystraceSection;
#endif
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,6 @@
{
"bridgeAndroid#android-x86,merge_structure_97d5739e": {
"workerSize": "MEDIUM",
"platformType": "LINUX"
}
}

View File

@ -0,0 +1,71 @@
load(
"//tools/build_defs/oss:rn_defs.bzl",
"APPLE",
"fb_xplat_cxx_test",
"jni_instrumentation_test_lib",
"react_native_xplat_target",
)
TEST_SRCS = [
"RecoverableErrorTest.cpp",
"JSDeltaBundleClientTest.cpp",
"jsarg_helpers.cpp",
"jsbigstring.cpp",
"methodcall.cpp",
]
jni_instrumentation_test_lib(
name = "tests",
srcs = TEST_SRCS,
class_under_test = "com/facebook/react/XplatBridgeTest",
compiler_flags = [
"-fexceptions",
"-frtti",
"-std=c++14",
],
soname = "libxplat-bridge.so",
visibility = [
"//fbandroid/instrumentation_tests/...",
],
deps = [
"//xplat/folly:dynamic",
"//xplat/third-party/gmock:gtest",
"//xplat/third-party/linker_lib:android",
"//xplat/third-party/linker_lib:atomic",
react_native_xplat_target("cxxreact:bridge"),
react_native_xplat_target("cxxreact:jsbigstring"),
],
)
fb_xplat_cxx_test(
name = "tests",
srcs = TEST_SRCS,
compiler_flags = [
"-fexceptions",
"-frtti",
],
platforms = APPLE,
visibility = [
react_native_xplat_target("cxxreact/..."),
],
deps = [
"//xplat/folly:molly",
"//xplat/third-party/gmock:gtest",
react_native_xplat_target("cxxreact:bridge"),
react_native_xplat_target("cxxreact:jsbigstring"),
],
)
fb_xplat_cxx_test(
name = "jsbigstring_test",
srcs = ["jsbigstring.cpp"],
compiler_flags = [
"-fexceptions",
"-frtti",
],
deps = [
"//xplat/folly:molly",
"//xplat/third-party/gmock:gtest",
react_native_xplat_target("cxxreact:jsbigstring"),
],
)

View File

@ -0,0 +1,133 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <gtest/gtest.h>
#include <exception>
#include <iostream>
#include <stdexcept>
#include <cxxreact/JSDeltaBundleClient.h>
#include <folly/dynamic.h>
#include <folly/json.h>
using namespace facebook::react;
TEST(JSDeltaBundleClient, PatchStartupCode) {
JSDeltaBundleClient client;
folly::dynamic delta1 = folly::parseJson(R"({
"base": true,
"revisionId": "rev0",
"pre": "pre",
"post": "post",
"modules": [
[0, "0"],
[1, "1"]
]
})");
client.patch(delta1);
EXPECT_STREQ(client.getStartupCode()->c_str(), "pre\npost\n");
folly::dynamic delta2 = folly::parseJson(R"({
"base": true,
"revisionId": "rev1",
"pre": "pre2",
"post": "post2",
"modules": []
})");
client.patch(delta2);
EXPECT_STREQ(client.getStartupCode()->c_str(), "pre2\npost2\n");
}
TEST(JSDeltaBundleClient, PatchModule) {
JSDeltaBundleClient client;
folly::dynamic delta1 = folly::parseJson(R"({
"base": true,
"revisionId": "rev0",
"pre": "pre",
"post": "post",
"modules": [
[0, "0"],
[1, "1"]
]
})");
client.patch(delta1);
EXPECT_EQ(client.getModule(0).code, "0");
EXPECT_EQ(client.getModule(1).code, "1");
ASSERT_THROW(client.getModule(2), JSModulesUnbundle::ModuleNotFound);
folly::dynamic delta2 = folly::parseJson(R"({
"base": false,
"revisionId": "rev1",
"added": [
[2, "2"]
],
"modified": [
[0, "0.1"]
],
"deleted": [1]
})");
client.patch(delta2);
EXPECT_EQ(client.getModule(0).code, "0.1");
EXPECT_EQ(client.getModule(2).code, "2");
ASSERT_THROW(client.getModule(1), JSModulesUnbundle::ModuleNotFound);
folly::dynamic delta3 = folly::parseJson(R"({
"base": true,
"revisionId": "rev2",
"pre": "pre",
"post": "post",
"modules": [
[3, "3"],
[4, "4"]
]
})");
client.patch(delta3);
ASSERT_THROW(client.getModule(0), JSModulesUnbundle::ModuleNotFound);
ASSERT_THROW(client.getModule(1), JSModulesUnbundle::ModuleNotFound);
ASSERT_THROW(client.getModule(2), JSModulesUnbundle::ModuleNotFound);
EXPECT_EQ(client.getModule(3).code, "3");
EXPECT_EQ(client.getModule(4).code, "4");
}
TEST(JSDeltaBundleClient, Clear) {
JSDeltaBundleClient client;
folly::dynamic delta1 = folly::parseJson(R"({
"base": true,
"revisionId": "rev0",
"pre": "pre",
"post": "post",
"modules": [
[0, "0"],
[1, "1"]
]
})");
client.patch(delta1);
client.clear();
ASSERT_THROW(client.getModule(0), JSModulesUnbundle::ModuleNotFound);
ASSERT_THROW(client.getModule(1), JSModulesUnbundle::ModuleNotFound);
EXPECT_STREQ(client.getStartupCode()->c_str(), "");
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <gtest/gtest.h>
#include <exception>
#include <stdexcept>
#include <cxxreact/RecoverableError.h>
using namespace facebook::react;
TEST(RecoverableError, RunRethrowingAsRecoverableRecoverTest) {
try {
RecoverableError::runRethrowingAsRecoverable<std::runtime_error>(
[]() { throw std::runtime_error("catch me"); });
FAIL() << "Unthrown exception";
} catch (const RecoverableError &err) {
ASSERT_STREQ(err.what(), "facebook::react::Recoverable: catch me");
} catch (...) {
FAIL() << "Uncaught exception";
}
}
TEST(RecoverableError, RunRethrowingAsRecoverableFallthroughTest) {
try {
RecoverableError::runRethrowingAsRecoverable<std::runtime_error>(
[]() { throw std::logic_error("catch me"); });
FAIL() << "Unthrown exception";
} catch (const RecoverableError &err) {
FAIL() << "Recovered exception that should have fallen through";
} catch (const std::exception &err) {
ASSERT_STREQ(err.what(), "catch me");
}
}

View File

@ -0,0 +1,114 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <cxxreact/JsArgumentHelpers.h>
#include <folly/dynamic.h>
#include <gtest/gtest.h>
#include <algorithm>
using namespace std;
using namespace folly;
using namespace facebook::xplat;
#define EXPECT_JSAE(statement, exstr) \
do { \
try { \
statement; \
FAIL() << "Expected JsArgumentException(" << (exstr) << ") not thrown"; \
} catch (const JsArgumentException &ex) { \
EXPECT_EQ(ex.what(), std::string(exstr)); \
} \
} while (0) // let any other exception escape, gtest will deal.
TEST(JsArgumentHelpersTest, args) {
const bool aBool = true;
const int64_t anInt = 17;
const double aDouble = 3.14;
const string aString = "word";
const dynamic anArray = dynamic::array("a", "b", "c");
const dynamic anObject = dynamic::object("k1", "v1")("k2", "v2");
const string aNumericString = to<string>(anInt);
folly::dynamic args = dynamic::array(
aBool, anInt, aDouble, aString, anArray, anObject, aNumericString);
EXPECT_EQ(jsArgAsBool(args, 0), aBool);
EXPECT_EQ(jsArgAsInt(args, 1), anInt);
EXPECT_EQ(jsArgAsDouble(args, 2), aDouble);
EXPECT_EQ(jsArgAsString(args, 3), aString);
EXPECT_EQ(jsArgAsArray(args, 4), anArray);
EXPECT_EQ(jsArgAsObject(args, 5), anObject);
// const args
const folly::dynamic &cargs = args;
const folly::dynamic &a4 = jsArgAsArray(cargs, 4);
EXPECT_EQ(a4, anArray);
EXPECT_EQ(jsArgAsObject(cargs, 5), anObject);
// helpers returning dynamic should return same object without copying
EXPECT_EQ(&jsArgAsArray(args, 4), &(args[4]));
EXPECT_EQ(&jsArgAsArray(cargs, 4), &(args[4]));
// dynamics returned for mutable args should be mutable. The test is that
// this compiles.
jsArgAsArray(args, 4)[2] = "d";
jsArgAsArray(args, 4)[2] = "c";
// These fail to compile due to constness.
// jsArgAsArray(cargs, 4)[2] = "d";
// jsArgAsArray(cargs, 4)[2] = "c";
// ref-qualified member function tests
EXPECT_EQ(jsArgN(args, 3, &folly::dynamic::getString), aString);
EXPECT_EQ(jsArg(args[3], &folly::dynamic::getString), aString);
// conversions
EXPECT_EQ(jsArgAsDouble(args, 1), anInt * 1.0);
EXPECT_EQ(jsArgAsString(args, 1), aNumericString);
EXPECT_EQ(jsArgAsInt(args, 6), anInt);
// Test exception messages.
// out_of_range
EXPECT_JSAE(
jsArgAsBool(args, 7),
"JavaScript provided 7 arguments for C++ method which references at least "
"8 arguments: out of range in dynamic array");
// Conv range_error (invalid value conversion)
const std::string exhead = "Could not convert argument 3 to required type: ";
const std::string extail = ": Invalid leading character: \"word\"";
try {
jsArgAsInt(args, 3);
FAIL() << "Expected JsArgumentException(" << exhead << "..." << extail
<< ") not thrown";
} catch (const JsArgumentException &ex) {
const std::string exwhat = ex.what();
EXPECT_GT(exwhat.size(), exhead.size());
EXPECT_GT(exwhat.size(), extail.size());
EXPECT_TRUE(std::equal(exhead.cbegin(), exhead.cend(), exwhat.cbegin()))
<< "JsArgumentException('" << exwhat << "') does not begin with '"
<< exhead << "'";
EXPECT_TRUE(std::equal(extail.crbegin(), extail.crend(), exwhat.crbegin()))
<< "JsArgumentException('" << exwhat << "') does not end with '"
<< extail << "'";
}
// inconvertible types
EXPECT_JSAE(
jsArgAsArray(args, 2),
"Argument 3 of type double is not required type Array");
EXPECT_JSAE(
jsArgAsInt(args, 4),
"Error converting javascript arg 4 to C++: "
"TypeError: expected dynamic type `int/double/bool/string', but had type `array'");
// type predicate failure
EXPECT_JSAE(
jsArgAsObject(args, 4),
"Argument 5 of type array is not required type Object");
}

View File

@ -0,0 +1,106 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <fcntl.h>
#include <sys/mman.h>
#include <cxxreact/JSBigString.h>
#include <folly/File.h>
#include <gtest/gtest.h>
using namespace facebook;
using namespace facebook::react;
namespace {
int tempFileFromString(std::string contents) {
const char *tmpDir = getenv("TMPDIR");
if (tmpDir == nullptr)
tmpDir = "/tmp";
std::string tmp{tmpDir};
tmp += "/temp.XXXXXX";
std::vector<char> tmpBuf{tmp.begin(), tmp.end()};
tmpBuf.push_back('\0');
const int fd = mkstemp(tmpBuf.data());
write(fd, contents.c_str(), contents.size() + 1);
return fd;
}
}; // namespace
TEST(JSBigFileString, MapWholeFileTest) {
std::string data{"Hello, world"};
const auto size = data.length() + 1;
// Initialise Big String
int fd = tempFileFromString("Hello, world");
JSBigFileString bigStr{fd, size};
// Test
ASSERT_STREQ(data.c_str(), bigStr.c_str());
}
TEST(JSBigFileString, MapPartTest) {
std::string data{"Hello, world"};
// Sub-string to actually map
std::string needle{"or"};
off_t offset = data.find(needle);
// Initialise Big String
int fd = tempFileFromString(data);
JSBigFileString bigStr{fd, needle.size(), offset};
// Test
EXPECT_EQ(needle.length(), bigStr.size());
for (unsigned int i = 0; i < needle.length(); ++i) {
EXPECT_EQ(needle[i], bigStr.c_str()[i]);
}
}
TEST(JSBigFileString, RemapTest) {
static const uint8_t kRemapMagic[] = {
0xc6, 0x1f, 0xbc, 0x03, 0xc1, 0x03, 0x19, 0x1f, 0xa1, 0xd0, 0xeb, 0x73};
std::string data(std::begin(kRemapMagic), std::end(kRemapMagic));
auto app = [&data](uint16_t v) {
data.append(reinterpret_cast<char *>(&v), sizeof(v));
};
size_t pageSizeLog2 = 16;
app(pageSizeLog2);
size_t pageSize = 1 << pageSizeLog2;
app(1); // header pages
app(2); // num mappings
// file page 0 -> memory page 1
app(1); // memory page
app(1); // num pages
// file page 1 -> memory page 0
app(0); // memory page
app(1); // num pages
while (data.size() < pageSize) {
app(0);
}
while (data.size() < pageSize * 2) {
app(0x1111);
}
while (data.size() < pageSize * 3) {
app(0x2222);
}
int fd = tempFileFromString(data);
JSBigFileString bigStr{fd, data.size()};
EXPECT_EQ(pageSize * 2, bigStr.size());
auto remapped = bigStr.c_str();
size_t i = 0;
for (; i < pageSize; ++i) {
EXPECT_EQ(0x22, remapped[i]);
}
for (; i < pageSize * 2; ++i) {
EXPECT_EQ(0x11, remapped[i]);
}
}

View File

@ -0,0 +1,156 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <cxxreact/MethodCall.h>
#include <folly/json.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-compare"
#include <gtest/gtest.h>
#pragma GCC diagnostic pop
using namespace facebook;
using namespace facebook::react;
using namespace folly;
TEST(parseMethodCalls, SingleReturnCallNoArgs) {
auto jsText = "[[7],[3],[[]]]";
auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
EXPECT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
EXPECT_EQ(0, returnedCall.arguments.size());
EXPECT_EQ(7, returnedCall.moduleId);
EXPECT_EQ(3, returnedCall.methodId);
}
TEST(parseMethodCalls, InvalidReturnFormat) {
try {
auto input = dynamic::object("foo", 1);
parseMethodCalls(std::move(input));
ADD_FAILURE();
} catch (const std::invalid_argument &) {
// ignored
}
try {
auto input = dynamic::array(dynamic::object("foo", 1));
parseMethodCalls(std::move(input));
ADD_FAILURE();
} catch (const std::invalid_argument &) {
// ignored
}
try {
auto input = dynamic::array(1, 4, dynamic::object("foo", 2));
parseMethodCalls(std::move(input));
ADD_FAILURE();
} catch (const std::invalid_argument &) {
// ignored
}
try {
auto input = dynamic::array(
dynamic::array(1), dynamic::array(4), dynamic::object("foo", 2));
parseMethodCalls(std::move(input));
ADD_FAILURE();
} catch (const std::invalid_argument &) {
// ignored
}
try {
auto input =
dynamic::array(dynamic::array(1), dynamic::array(4), dynamic::array());
parseMethodCalls(std::move(input));
ADD_FAILURE();
} catch (const std::invalid_argument &) {
// ignored
}
}
TEST(parseMethodCalls, NumberReturn) {
auto jsText = "[[0],[0],[[\"foobar\"]]]";
auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
EXPECT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
EXPECT_EQ(1, returnedCall.arguments.size());
EXPECT_EQ(folly::dynamic::STRING, returnedCall.arguments[0].type());
EXPECT_EQ("foobar", returnedCall.arguments[0].asString());
}
TEST(parseMethodCalls, StringReturn) {
auto jsText = "[[0],[0],[[42.16]]]";
auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
EXPECT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
EXPECT_EQ(1, returnedCall.arguments.size());
EXPECT_EQ(folly::dynamic::DOUBLE, returnedCall.arguments[0].type());
EXPECT_EQ(42.16, returnedCall.arguments[0].asDouble());
}
TEST(parseMethodCalls, BooleanReturn) {
auto jsText = "[[0],[0],[[false]]]";
auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
EXPECT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
EXPECT_EQ(1, returnedCall.arguments.size());
EXPECT_EQ(folly::dynamic::BOOL, returnedCall.arguments[0].type());
ASSERT_FALSE(returnedCall.arguments[0].asBool());
}
TEST(parseMethodCalls, NullReturn) {
auto jsText = "[[0],[0],[[null]]]";
auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
EXPECT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
EXPECT_EQ(1, returnedCall.arguments.size());
EXPECT_EQ(folly::dynamic::NULLT, returnedCall.arguments[0].type());
}
TEST(parseMethodCalls, MapReturn) {
auto jsText =
"[[0],[0],[[{\"foo\": \"hello\", \"bar\": 4.0, \"baz\": true}]]]";
auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
EXPECT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
EXPECT_EQ(1, returnedCall.arguments.size());
EXPECT_EQ(folly::dynamic::OBJECT, returnedCall.arguments[0].type());
auto &returnedMap = returnedCall.arguments[0];
auto foo = returnedMap.at("foo");
EXPECT_EQ(folly::dynamic("hello"), foo);
auto bar = returnedMap.at("bar");
EXPECT_EQ(folly::dynamic(4.0), bar);
auto baz = returnedMap.at("baz");
EXPECT_EQ(folly::dynamic(true), baz);
}
TEST(parseMethodCalls, ArrayReturn) {
auto jsText = "[[0],[0],[[[\"foo\", 42.0, false]]]]";
auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
EXPECT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
EXPECT_EQ(1, returnedCall.arguments.size());
EXPECT_EQ(folly::dynamic::ARRAY, returnedCall.arguments[0].type());
auto &returnedArray = returnedCall.arguments[0];
EXPECT_EQ(3, returnedArray.size());
EXPECT_EQ(folly::dynamic("foo"), returnedArray[0]);
EXPECT_EQ(folly::dynamic(42.0), returnedArray[1]);
EXPECT_EQ(folly::dynamic(false), returnedArray[2]);
}
TEST(parseMethodCalls, ReturnMultipleParams) {
auto jsText = "[[0],[0],[[\"foo\", 14, null, false]]]";
auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
EXPECT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
EXPECT_EQ(4, returnedCall.arguments.size());
EXPECT_EQ(folly::dynamic::STRING, returnedCall.arguments[0].type());
EXPECT_EQ(folly::dynamic::INT64, returnedCall.arguments[1].type());
EXPECT_EQ(folly::dynamic::NULLT, returnedCall.arguments[2].type());
EXPECT_EQ(folly::dynamic::BOOL, returnedCall.arguments[3].type());
}
TEST(parseMethodCalls, ParseTwoCalls) {
auto jsText = "[[0,0],[1,1],[[],[]]]";
auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
EXPECT_EQ(2, returnedCalls.size());
}

View File

@ -0,0 +1,5 @@
---
Checks: '>
clang-diagnostic-*,
'
...

View File

@ -0,0 +1,151 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "AttributedString.h"
#include <react/debug/DebugStringConvertibleItem.h>
namespace facebook {
namespace react {
using Fragment = AttributedString::Fragment;
using Fragments = AttributedString::Fragments;
#pragma mark - Fragment
std::string Fragment::AttachmentCharacter() {
return "\uFFFC"; // Unicode `OBJECT REPLACEMENT CHARACTER`
}
bool Fragment::isAttachment() const {
return string == AttachmentCharacter();
}
bool Fragment::operator==(const Fragment &rhs) const {
return std::tie(
string,
textAttributes,
parentShadowView.tag,
parentShadowView.layoutMetrics) ==
std::tie(
rhs.string,
rhs.textAttributes,
rhs.parentShadowView.tag,
rhs.parentShadowView.layoutMetrics);
}
bool Fragment::operator!=(const Fragment &rhs) const {
return !(*this == rhs);
}
#pragma mark - AttributedString
void AttributedString::appendFragment(const Fragment &fragment) {
ensureUnsealed();
if (fragment.string.empty()) {
return;
}
fragments_.push_back(fragment);
}
void AttributedString::prependFragment(const Fragment &fragment) {
ensureUnsealed();
if (fragment.string.empty()) {
return;
}
fragments_.insert(fragments_.begin(), fragment);
}
void AttributedString::appendAttributedString(
const AttributedString &attributedString) {
ensureUnsealed();
fragments_.insert(
fragments_.end(),
attributedString.fragments_.begin(),
attributedString.fragments_.end());
}
void AttributedString::prependAttributedString(
const AttributedString &attributedString) {
ensureUnsealed();
fragments_.insert(
fragments_.begin(),
attributedString.fragments_.begin(),
attributedString.fragments_.end());
}
Fragments const &AttributedString::getFragments() const {
return fragments_;
}
Fragments &AttributedString::getFragments() {
return fragments_;
}
std::string AttributedString::getString() const {
auto string = std::string{};
for (const auto &fragment : fragments_) {
string += fragment.string;
}
return string;
}
bool AttributedString::isEmpty() const {
return fragments_.empty();
}
bool AttributedString::compareTextAttributesWithoutFrame(
const AttributedString &rhs) const {
if (fragments_.size() != rhs.fragments_.size()) {
return false;
}
for (unsigned i = 0; i < fragments_.size(); i++) {
if (fragments_[i].textAttributes != rhs.fragments_[i].textAttributes ||
fragments_[i].string != rhs.fragments_[i].string) {
return false;
}
}
return true;
}
bool AttributedString::operator==(const AttributedString &rhs) const {
return fragments_ == rhs.fragments_;
}
bool AttributedString::operator!=(const AttributedString &rhs) const {
return !(*this == rhs);
}
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList AttributedString::getDebugChildren() const {
auto list = SharedDebugStringConvertibleList{};
for (auto &&fragment : fragments_) {
auto propsList =
fragment.textAttributes.DebugStringConvertible::getDebugProps();
list.push_back(std::make_shared<DebugStringConvertibleItem>(
"Fragment",
fragment.string,
SharedDebugStringConvertibleList(),
propsList));
}
return list;
}
#endif
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,139 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <functional>
#include <memory>
#include <folly/Hash.h>
#include <folly/Optional.h>
#include <react/attributedstring/TextAttributes.h>
#include <react/core/Sealable.h>
#include <react/core/ShadowNode.h>
#include <react/debug/DebugStringConvertible.h>
#include <react/mounting/ShadowView.h>
namespace facebook {
namespace react {
class AttributedString;
using SharedAttributedString = std::shared_ptr<const AttributedString>;
/*
* Simple, cross-platfrom, React-specific implementation of attributed string
* (aka spanned string).
* `AttributedString` is basically a list of `Fragments` which have `string` and
* `textAttributes` + `shadowNode` associated with the `string`.
*/
class AttributedString : public Sealable, public DebugStringConvertible {
public:
class Fragment {
public:
static std::string AttachmentCharacter();
std::string string;
TextAttributes textAttributes;
ShadowView parentShadowView;
/*
* Returns true is the Fragment represents an attachment.
* Equivalent to `string == AttachmentCharacter()`.
*/
bool isAttachment() const;
bool operator==(const Fragment &rhs) const;
bool operator!=(const Fragment &rhs) const;
};
class Range {
public:
int location{0};
int length{0};
};
using Fragments = better::small_vector<Fragment, 1>;
/*
* Appends and prepends a `fragment` to the string.
*/
void appendFragment(const Fragment &fragment);
void prependFragment(const Fragment &fragment);
/*
* Appends and prepends an `attributedString` (all its fragments) to
* the string.
*/
void appendAttributedString(const AttributedString &attributedString);
void prependAttributedString(const AttributedString &attributedString);
/*
* Returns a read-only reference to a list of fragments.
*/
Fragments const &getFragments() const;
/*
* Returns a reference to a list of fragments.
*/
Fragments &getFragments();
/*
* Returns a string constructed from all strings in all fragments.
*/
std::string getString() const;
/*
* Returns `true` if the string is empty (has no any fragments).
*/
bool isEmpty() const;
/**
* Compares equality of TextAttributes of all Fragments on both sides.
*/
bool compareTextAttributesWithoutFrame(const AttributedString &rhs) const;
bool operator==(const AttributedString &rhs) const;
bool operator!=(const AttributedString &rhs) const;
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList getDebugChildren() const override;
#endif
private:
Fragments fragments_;
};
} // namespace react
} // namespace facebook
namespace std {
template <>
struct hash<facebook::react::AttributedString::Fragment> {
size_t operator()(
const facebook::react::AttributedString::Fragment &fragment) const {
return folly::hash::hash_combine(
0, fragment.string, fragment.textAttributes, fragment.parentShadowView);
}
};
template <>
struct hash<facebook::react::AttributedString> {
size_t operator()(
const facebook::react::AttributedString &attributedString) const {
auto seed = size_t{0};
for (const auto &fragment : attributedString.getFragments()) {
seed = folly::hash::hash_combine(seed, fragment);
}
return seed;
}
};
} // namespace std

View File

@ -0,0 +1,65 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "AttributedStringBox.h"
namespace facebook {
namespace react {
AttributedStringBox::AttributedStringBox()
: mode_(Mode::Value),
value_(std::make_shared<AttributedString const>(AttributedString{})),
opaquePointer_({}){};
AttributedStringBox::AttributedStringBox(AttributedString const &value)
: mode_(Mode::Value),
value_(std::make_shared<AttributedString const>(value)),
opaquePointer_({}){};
AttributedStringBox::AttributedStringBox(
std::shared_ptr<void> const &opaquePointer)
: mode_(Mode::OpaquePointer), value_({}), opaquePointer_(opaquePointer) {}
AttributedStringBox::Mode AttributedStringBox::getMode() const {
return mode_;
}
AttributedString const &AttributedStringBox::getValue() const {
assert(mode_ == AttributedStringBox::Mode::Value);
assert(value_);
return *value_;
}
std::shared_ptr<void> AttributedStringBox::getOpaquePointer() const {
assert(mode_ == AttributedStringBox::Mode::OpaquePointer);
assert(opaquePointer_);
return opaquePointer_;
}
bool operator==(
AttributedStringBox const &lhs,
AttributedStringBox const &rhs) {
if (lhs.getMode() != rhs.getMode()) {
return false;
}
switch (lhs.getMode()) {
case facebook::react::AttributedStringBox::Mode::Value:
return lhs.getValue() == rhs.getValue();
case facebook::react::AttributedStringBox::Mode::OpaquePointer:
return lhs.getOpaquePointer() == rhs.getOpaquePointer();
}
}
bool operator!=(
AttributedStringBox const &lhs,
AttributedStringBox const &rhs) {
return !(lhs == rhs);
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <react/attributedstring/AttributedString.h>
namespace facebook {
namespace react {
/*
* Represents an object storing a shared `AttributedString` or a shared pointer
* to some opaque platform-specific object that can be used as an attributed
* string. The class serves two main purposes:
* - Represent type-erased attributed string entity (which can be
* platform-specific or platform-independent);
* - Represent a container that can be copied with constant complexity.
*/
class AttributedStringBox final {
public:
enum class Mode { Value, OpaquePointer };
/*
* Default constructor constructs an empty string.
*/
AttributedStringBox();
/*
* Custom explicit constructors.
*/
explicit AttributedStringBox(AttributedString const &value);
explicit AttributedStringBox(std::shared_ptr<void> const &opaquePointer);
/*
* Movable, Copyable, Assignable.
*/
AttributedStringBox(AttributedStringBox const &other) = default;
AttributedStringBox(AttributedStringBox &&other) noexcept = default;
AttributedStringBox &operator=(AttributedStringBox const &other) = default;
AttributedStringBox &operator=(AttributedStringBox &&other) = default;
/*
* Getters.
*/
Mode getMode() const;
AttributedString const &getValue() const;
std::shared_ptr<void> getOpaquePointer() const;
private:
Mode mode_;
std::shared_ptr<AttributedString const> value_;
std::shared_ptr<void> opaquePointer_;
};
bool operator==(AttributedStringBox const &lhs, AttributedStringBox const &rhs);
bool operator!=(AttributedStringBox const &lhs, AttributedStringBox const &rhs);
} // namespace react
} // namespace facebook
template <>
struct std::hash<facebook::react::AttributedStringBox> {
size_t operator()(
facebook::react::AttributedStringBox const &attributedStringBox) const {
switch (attributedStringBox.getMode()) {
case facebook::react::AttributedStringBox::Mode::Value:
return std::hash<facebook::react::AttributedString>()(
attributedStringBox.getValue());
case facebook::react::AttributedStringBox::Mode::OpaquePointer:
return std::hash<std::shared_ptr<void>>()(
attributedStringBox.getOpaquePointer());
}
}
};

View File

@ -0,0 +1,83 @@
load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode")
load(
"//tools/build_defs/oss:rn_defs.bzl",
"ANDROID",
"APPLE",
"CXX",
"fb_xplat_cxx_test",
"get_apple_compiler_flags",
"get_apple_inspector_flags",
"react_native_xplat_target",
"rn_xplat_cxx_library",
"subdir_glob",
)
APPLE_COMPILER_FLAGS = get_apple_compiler_flags()
rn_xplat_cxx_library(
name = "attributedstring",
srcs = glob(
["**/*.cpp"],
exclude = glob(["tests/**/*.cpp"]),
),
headers = glob(
["**/*.h"],
exclude = glob(["tests/**/*.h"]),
),
header_namespace = "",
exported_headers = subdir_glob(
[
("", "*.h"),
],
prefix = "react/attributedstring",
),
compiler_flags = [
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wall",
],
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"],
fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(),
force_static = True,
macosx_tests_override = [],
platforms = (ANDROID, APPLE, CXX),
preprocessor_flags = [
"-DLOG_TAG=\"ReactNative\"",
"-DWITH_FBSYSTRACE=1",
],
tests = [":tests"],
visibility = ["PUBLIC"],
deps = [
"//xplat/fbsystrace:fbsystrace",
"//xplat/folly:headers_only",
"//xplat/folly:memory",
"//xplat/folly:molly",
"//xplat/third-party/glog:glog",
react_native_xplat_target("utils:utils"),
react_native_xplat_target("fabric/debug:debug"),
react_native_xplat_target("fabric/core:core"),
react_native_xplat_target("fabric/graphics:graphics"),
react_native_xplat_target("fabric/mounting:mounting"),
],
)
fb_xplat_cxx_test(
name = "tests",
srcs = glob(["tests/**/*.cpp"]),
headers = glob(["tests/**/*.h"]),
compiler_flags = [
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wall",
],
contacts = ["oncall+react_native@xmail.facebook.com"],
platforms = (ANDROID, APPLE, CXX),
deps = [
":attributedstring",
"//xplat/folly:molly",
"//xplat/third-party/gmock:gtest",
],
)

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "ParagraphAttributes.h"
#include <react/attributedstring/conversions.h>
#include <react/debug/debugStringConvertibleUtils.h>
#include <react/graphics/conversions.h>
#include <react/utils/FloatComparison.h>
namespace facebook {
namespace react {
bool ParagraphAttributes::operator==(const ParagraphAttributes &rhs) const {
return std::tie(
maximumNumberOfLines,
ellipsizeMode,
textBreakStrategy,
adjustsFontSizeToFit) ==
std::tie(
rhs.maximumNumberOfLines,
rhs.ellipsizeMode,
rhs.textBreakStrategy,
rhs.adjustsFontSizeToFit) &&
floatEquality(minimumFontSize, rhs.minimumFontSize) &&
floatEquality(maximumFontSize, rhs.maximumFontSize);
}
bool ParagraphAttributes::operator!=(const ParagraphAttributes &rhs) const {
return !(*this == rhs);
}
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList ParagraphAttributes::getDebugProps() const {
return {
debugStringConvertibleItem("maximumNumberOfLines", maximumNumberOfLines),
debugStringConvertibleItem("ellipsizeMode", ellipsizeMode),
debugStringConvertibleItem("textBreakStrategy", textBreakStrategy),
debugStringConvertibleItem("adjustsFontSizeToFit", adjustsFontSizeToFit),
debugStringConvertibleItem("minimumFontSize", minimumFontSize),
debugStringConvertibleItem("maximumFontSize", maximumFontSize)};
}
#endif
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,88 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <limits>
#include <folly/Hash.h>
#include <react/attributedstring/primitives.h>
#include <react/debug/DebugStringConvertible.h>
#include <react/graphics/Geometry.h>
namespace facebook {
namespace react {
class ParagraphAttributes;
using SharedParagraphAttributes = std::shared_ptr<const ParagraphAttributes>;
/*
* Represents all visual attributes of a paragraph of text.
* Two data structures, ParagraphAttributes and AttributedText, should be
* enough to define visual representation of a piece of text on the screen.
*/
class ParagraphAttributes : public DebugStringConvertible {
public:
#pragma mark - Fields
/*
* Maximum number of lines which paragraph can take.
* Zero value represents "no limit".
*/
int maximumNumberOfLines{};
/*
* In case if a text cannot fit given boundaries, defines a place where
* an ellipsize should be placed.
*/
EllipsizeMode ellipsizeMode{};
TextBreakStrategy textBreakStrategy{};
/*
* Enables font size adjustment to fit constrained boundaries.
*/
bool adjustsFontSizeToFit{};
/*
* In case of font size adjustment enabled, defines minimum and maximum
* font sizes.
*/
Float minimumFontSize{std::numeric_limits<Float>::quiet_NaN()};
Float maximumFontSize{std::numeric_limits<Float>::quiet_NaN()};
bool operator==(const ParagraphAttributes &) const;
bool operator!=(const ParagraphAttributes &) const;
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList getDebugProps() const override;
#endif
};
} // namespace react
} // namespace facebook
namespace std {
template <>
struct hash<facebook::react::ParagraphAttributes> {
size_t operator()(
const facebook::react::ParagraphAttributes &attributes) const {
return folly::hash::hash_combine(
0,
attributes.maximumNumberOfLines,
attributes.ellipsizeMode,
attributes.textBreakStrategy,
attributes.adjustsFontSizeToFit,
attributes.minimumFontSize,
attributes.maximumFontSize);
}
};
} // namespace std

View File

@ -0,0 +1,210 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "TextAttributes.h"
#include <react/attributedstring/conversions.h>
#include <react/core/conversions.h>
#include <react/graphics/conversions.h>
#include <react/utils/FloatComparison.h>
#include <cmath>
#include <react/debug/debugStringConvertibleUtils.h>
namespace facebook {
namespace react {
void TextAttributes::apply(TextAttributes textAttributes) {
// Color
foregroundColor = textAttributes.foregroundColor
? textAttributes.foregroundColor
: foregroundColor;
backgroundColor = textAttributes.backgroundColor
? textAttributes.backgroundColor
: backgroundColor;
opacity =
!std::isnan(textAttributes.opacity) ? textAttributes.opacity : opacity;
// Font
fontFamily = !textAttributes.fontFamily.empty() ? textAttributes.fontFamily
: fontFamily;
fontSize =
!std::isnan(textAttributes.fontSize) ? textAttributes.fontSize : fontSize;
fontSizeMultiplier = !std::isnan(textAttributes.fontSizeMultiplier)
? textAttributes.fontSizeMultiplier
: fontSizeMultiplier;
fontWeight = textAttributes.fontWeight.hasValue() ? textAttributes.fontWeight
: fontWeight;
fontStyle = textAttributes.fontStyle.hasValue() ? textAttributes.fontStyle
: fontStyle;
fontVariant = textAttributes.fontVariant.hasValue()
? textAttributes.fontVariant
: fontVariant;
allowFontScaling = textAttributes.allowFontScaling.hasValue()
? textAttributes.allowFontScaling
: allowFontScaling;
letterSpacing = !std::isnan(textAttributes.letterSpacing)
? textAttributes.letterSpacing
: letterSpacing;
// Paragraph Styles
lineHeight = !std::isnan(textAttributes.lineHeight)
? textAttributes.lineHeight
: lineHeight;
alignment = textAttributes.alignment.hasValue() ? textAttributes.alignment
: alignment;
baseWritingDirection = textAttributes.baseWritingDirection.hasValue()
? textAttributes.baseWritingDirection
: baseWritingDirection;
// Decoration
textDecorationColor = textAttributes.textDecorationColor
? textAttributes.textDecorationColor
: textDecorationColor;
textDecorationLineType = textAttributes.textDecorationLineType.hasValue()
? textAttributes.textDecorationLineType
: textDecorationLineType;
textDecorationLineStyle = textAttributes.textDecorationLineStyle.hasValue()
? textAttributes.textDecorationLineStyle
: textDecorationLineStyle;
textDecorationLinePattern =
textAttributes.textDecorationLinePattern.hasValue()
? textAttributes.textDecorationLinePattern
: textDecorationLinePattern;
// Shadow
textShadowOffset = textAttributes.textShadowOffset.hasValue()
? textAttributes.textShadowOffset.value()
: textShadowOffset;
textShadowRadius = !std::isnan(textAttributes.textShadowRadius)
? textAttributes.textShadowRadius
: textShadowRadius;
textShadowColor = textAttributes.textShadowColor
? textAttributes.textShadowColor
: textShadowColor;
// Special
isHighlighted = textAttributes.isHighlighted.hasValue()
? textAttributes.isHighlighted
: isHighlighted;
layoutDirection = textAttributes.layoutDirection.hasValue()
? textAttributes.layoutDirection
: layoutDirection;
}
#pragma mark - Operators
bool TextAttributes::operator==(const TextAttributes &rhs) const {
return std::tie(
foregroundColor,
backgroundColor,
fontFamily,
fontWeight,
fontStyle,
fontVariant,
allowFontScaling,
alignment,
baseWritingDirection,
textDecorationColor,
textDecorationLineType,
textDecorationLineStyle,
textDecorationLinePattern,
textShadowOffset,
textShadowColor,
isHighlighted,
layoutDirection) ==
std::tie(
rhs.foregroundColor,
rhs.backgroundColor,
rhs.fontFamily,
rhs.fontWeight,
rhs.fontStyle,
rhs.fontVariant,
rhs.allowFontScaling,
rhs.alignment,
rhs.baseWritingDirection,
rhs.textDecorationColor,
rhs.textDecorationLineType,
rhs.textDecorationLineStyle,
rhs.textDecorationLinePattern,
rhs.textShadowOffset,
rhs.textShadowColor,
rhs.isHighlighted,
rhs.layoutDirection) &&
floatEquality(opacity, rhs.opacity) &&
floatEquality(fontSize, rhs.fontSize) &&
floatEquality(fontSizeMultiplier, rhs.fontSizeMultiplier) &&
floatEquality(letterSpacing, rhs.letterSpacing) &&
floatEquality(lineHeight, rhs.lineHeight) &&
floatEquality(textShadowRadius, rhs.textShadowRadius);
}
bool TextAttributes::operator!=(const TextAttributes &rhs) const {
return !(*this == rhs);
}
TextAttributes TextAttributes::defaultTextAttributes() {
static auto textAttributes = [] {
auto textAttributes = TextAttributes{};
// Non-obvious (can be different among platforms) default text attributes.
textAttributes.foregroundColor = blackColor();
textAttributes.backgroundColor = clearColor();
textAttributes.fontSize = 14.0;
textAttributes.fontSizeMultiplier = 1.0;
return textAttributes;
}();
return textAttributes;
}
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList TextAttributes::getDebugProps() const {
return {
// Color
debugStringConvertibleItem("backgroundColor", backgroundColor),
debugStringConvertibleItem("foregroundColor", foregroundColor),
debugStringConvertibleItem("opacity", opacity),
// Font
debugStringConvertibleItem("fontFamily", fontFamily),
debugStringConvertibleItem("fontSize", fontSize),
debugStringConvertibleItem("fontSizeMultiplier", fontSizeMultiplier),
debugStringConvertibleItem("fontWeight", fontWeight),
debugStringConvertibleItem("fontStyle", fontStyle),
debugStringConvertibleItem("fontVariant", fontVariant),
debugStringConvertibleItem("allowFontScaling", allowFontScaling),
debugStringConvertibleItem("letterSpacing", letterSpacing),
// Paragraph Styles
debugStringConvertibleItem("lineHeight", lineHeight),
debugStringConvertibleItem("alignment", alignment),
debugStringConvertibleItem("baseWritingDirection", baseWritingDirection),
// Decoration
debugStringConvertibleItem("textDecorationColor", textDecorationColor),
debugStringConvertibleItem(
"textDecorationLineType", textDecorationLineType),
debugStringConvertibleItem(
"textDecorationLineStyle", textDecorationLineStyle),
debugStringConvertibleItem(
"textDecorationLinePattern", textDecorationLinePattern),
// Shadow
debugStringConvertibleItem("textShadowOffset", textShadowOffset),
debugStringConvertibleItem("textShadowRadius", textShadowRadius),
debugStringConvertibleItem("textShadowColor", textShadowColor),
// Special
debugStringConvertibleItem("isHighlighted", isHighlighted),
debugStringConvertibleItem("layoutDirection", layoutDirection),
};
}
#endif
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,133 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <functional>
#include <limits>
#include <folly/Hash.h>
#include <folly/Optional.h>
#include <react/attributedstring/primitives.h>
#include <react/core/LayoutPrimitives.h>
#include <react/core/ReactPrimitives.h>
#include <react/debug/DebugStringConvertible.h>
#include <react/graphics/Color.h>
#include <react/graphics/Geometry.h>
namespace facebook {
namespace react {
class TextAttributes;
using SharedTextAttributes = std::shared_ptr<const TextAttributes>;
class TextAttributes : public DebugStringConvertible {
public:
/*
* Returns TextAttribute object which has actual default attribute values
* (e.g. `foregroundColor = black`), in oppose to TextAttribute's default
* constructor which creates an object with nulled attributes.
*/
static TextAttributes defaultTextAttributes();
#pragma mark - Fields
// Color
SharedColor foregroundColor{};
SharedColor backgroundColor{};
Float opacity{std::numeric_limits<Float>::quiet_NaN()};
// Font
std::string fontFamily{""};
Float fontSize{std::numeric_limits<Float>::quiet_NaN()};
Float fontSizeMultiplier{std::numeric_limits<Float>::quiet_NaN()};
folly::Optional<FontWeight> fontWeight{};
folly::Optional<FontStyle> fontStyle{};
folly::Optional<FontVariant> fontVariant{};
folly::Optional<bool> allowFontScaling{};
Float letterSpacing{std::numeric_limits<Float>::quiet_NaN()};
// Paragraph Styles
Float lineHeight{std::numeric_limits<Float>::quiet_NaN()};
folly::Optional<TextAlignment> alignment{};
folly::Optional<WritingDirection> baseWritingDirection{};
// Decoration
SharedColor textDecorationColor{};
folly::Optional<TextDecorationLineType> textDecorationLineType{};
folly::Optional<TextDecorationLineStyle> textDecorationLineStyle{};
folly::Optional<TextDecorationLinePattern> textDecorationLinePattern{};
// Shadow
// TODO: Use `Point` type instead of `Size` for `textShadowOffset` attribute.
folly::Optional<Size> textShadowOffset{};
Float textShadowRadius{std::numeric_limits<Float>::quiet_NaN()};
SharedColor textShadowColor{};
// Special
folly::Optional<bool> isHighlighted{};
// TODO T59221129: document where this value comes from and how it is set.
// It's not clear if this is being used properly, or if it's being set at all.
// Currently, it is intentionally *not* being set as part of BaseTextProps
// construction.
folly::Optional<LayoutDirection> layoutDirection{};
#pragma mark - Operations
void apply(TextAttributes textAttributes);
#pragma mark - Operators
bool operator==(const TextAttributes &rhs) const;
bool operator!=(const TextAttributes &rhs) const;
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList getDebugProps() const override;
#endif
};
} // namespace react
} // namespace facebook
namespace std {
template <>
struct hash<facebook::react::TextAttributes> {
size_t operator()(
const facebook::react::TextAttributes &textAttributes) const {
return folly::hash::hash_combine(
0,
textAttributes.foregroundColor,
textAttributes.backgroundColor,
textAttributes.opacity,
textAttributes.fontFamily,
textAttributes.fontSize,
textAttributes.fontSizeMultiplier,
textAttributes.fontWeight,
textAttributes.fontStyle,
textAttributes.fontVariant,
textAttributes.allowFontScaling,
textAttributes.letterSpacing,
textAttributes.lineHeight,
textAttributes.alignment,
textAttributes.baseWritingDirection,
textAttributes.textDecorationColor,
textAttributes.textDecorationLineType,
textAttributes.textDecorationLineStyle,
textAttributes.textDecorationLinePattern,
textAttributes.textShadowOffset,
textAttributes.textShadowRadius,
textAttributes.textShadowColor,
textAttributes.isHighlighted,
textAttributes.layoutDirection);
}
};
} // namespace std

View File

@ -0,0 +1,631 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <folly/Conv.h>
#include <folly/dynamic.h>
#include <react/attributedstring/AttributedString.h>
#include <react/attributedstring/ParagraphAttributes.h>
#include <react/attributedstring/TextAttributes.h>
#include <react/attributedstring/conversions.h>
#include <react/attributedstring/primitives.h>
#include <react/core/LayoutableShadowNode.h>
#include <react/core/ShadowNode.h>
#include <react/core/conversions.h>
#include <react/core/propsConversions.h>
#include <react/graphics/Geometry.h>
#include <react/graphics/conversions.h>
#include <cmath>
#include <glog/logging.h>
namespace facebook {
namespace react {
inline std::string toString(const EllipsizeMode &ellipsisMode) {
switch (ellipsisMode) {
case EllipsizeMode::Clip:
return "clip";
case EllipsizeMode::Head:
return "head";
case EllipsizeMode::Tail:
return "tail";
case EllipsizeMode::Middle:
return "middle";
}
}
inline void fromRawValue(const RawValue &value, EllipsizeMode &result) {
auto string = (std::string)value;
if (string == "clip") {
result = EllipsizeMode::Clip;
return;
}
if (string == "head") {
result = EllipsizeMode::Head;
return;
}
if (string == "tail") {
result = EllipsizeMode::Tail;
return;
}
if (string == "middle") {
result = EllipsizeMode::Middle;
return;
}
abort();
}
inline std::string toString(const TextBreakStrategy &textBreakStrategy) {
switch (textBreakStrategy) {
case TextBreakStrategy::Simple:
return "simple";
case TextBreakStrategy::HighQuality:
return "highQuality";
case TextBreakStrategy::Balanced:
return "balanced";
}
}
inline void fromRawValue(const RawValue &value, TextBreakStrategy &result) {
auto string = (std::string)value;
if (string == "simple") {
result = TextBreakStrategy::Simple;
return;
}
if (string == "highQuality") {
result = TextBreakStrategy::HighQuality;
return;
}
if (string == "balanced") {
result = TextBreakStrategy::Balanced;
return;
}
abort();
}
inline void fromRawValue(const RawValue &value, FontWeight &result) {
auto string = (std::string)value;
if (string == "normal") {
result = FontWeight::Regular;
return;
}
if (string == "regular") {
result = FontWeight::Regular;
return;
}
if (string == "bold") {
result = FontWeight::Bold;
return;
}
if (string == "100") {
result = FontWeight::Weight100;
return;
}
if (string == "200") {
result = FontWeight::Weight200;
return;
}
if (string == "300") {
result = FontWeight::Weight300;
return;
}
if (string == "400") {
result = FontWeight::Weight400;
return;
}
if (string == "500") {
result = FontWeight::Weight500;
return;
}
if (string == "600") {
result = FontWeight::Weight600;
return;
}
if (string == "700") {
result = FontWeight::Weight700;
return;
}
if (string == "800") {
result = FontWeight::Weight800;
return;
}
if (string == "900") {
result = FontWeight::Weight900;
return;
}
abort();
}
inline std::string toString(const FontWeight &fontWeight) {
return folly::to<std::string>((int)fontWeight);
}
inline void fromRawValue(const RawValue &value, FontStyle &result) {
auto string = (std::string)value;
if (string == "normal") {
result = FontStyle::Normal;
return;
}
if (string == "italic") {
result = FontStyle::Italic;
return;
}
if (string == "oblique") {
result = FontStyle::Oblique;
return;
}
abort();
}
inline std::string toString(const FontStyle &fontStyle) {
switch (fontStyle) {
case FontStyle::Normal:
return "normal";
case FontStyle::Italic:
return "italic";
case FontStyle::Oblique:
return "oblique";
}
}
inline void fromRawValue(const RawValue &value, FontVariant &result) {
assert(value.hasType<std::vector<std::string>>());
result = FontVariant::Default;
auto items = std::vector<std::string>{value};
for (const auto &item : items) {
if (item == "small-caps") {
result = (FontVariant)((int)result | (int)FontVariant::SmallCaps);
continue;
}
if (item == "oldstyle-nums") {
result = (FontVariant)((int)result | (int)FontVariant::OldstyleNums);
continue;
}
if (item == "lining-nums") {
result = (FontVariant)((int)result | (int)FontVariant::LiningNums);
continue;
}
if (item == "tabular-nums") {
result = (FontVariant)((int)result | (int)FontVariant::TabularNums);
continue;
}
if (item == "proportional-nums") {
result = (FontVariant)((int)result | (int)FontVariant::ProportionalNums);
continue;
}
}
}
inline std::string toString(const FontVariant &fontVariant) {
auto result = std::string{};
auto separator = std::string{", "};
if ((int)fontVariant & (int)FontVariant::SmallCaps) {
result += "small-caps" + separator;
}
if ((int)fontVariant & (int)FontVariant::OldstyleNums) {
result += "oldstyle-nums" + separator;
}
if ((int)fontVariant & (int)FontVariant::LiningNums) {
result += "lining-nums" + separator;
}
if ((int)fontVariant & (int)FontVariant::TabularNums) {
result += "tabular-nums" + separator;
}
if ((int)fontVariant & (int)FontVariant::ProportionalNums) {
result += "proportional-nums" + separator;
}
if (!result.empty()) {
result.erase(result.length() - separator.length());
}
return result;
}
inline void fromRawValue(const RawValue &value, TextAlignment &result) {
auto string = (std::string)value;
if (string == "auto") {
result = TextAlignment::Natural;
return;
}
if (string == "left") {
result = TextAlignment::Left;
return;
}
if (string == "center") {
result = TextAlignment::Center;
return;
}
if (string == "right") {
result = TextAlignment::Right;
return;
}
if (string == "justify") {
result = TextAlignment::Justified;
return;
}
abort();
}
inline std::string toString(const TextAlignment &textAlignment) {
switch (textAlignment) {
case TextAlignment::Natural:
return "natural";
case TextAlignment::Left:
return "left";
case TextAlignment::Center:
return "center";
case TextAlignment::Right:
return "right";
case TextAlignment::Justified:
return "justified";
}
}
inline void fromRawValue(const RawValue &value, WritingDirection &result) {
auto string = (std::string)value;
if (string == "natural") {
result = WritingDirection::Natural;
return;
}
if (string == "ltr") {
result = WritingDirection::LeftToRight;
return;
}
if (string == "rtl") {
result = WritingDirection::RightToLeft;
return;
}
abort();
}
inline std::string toString(const WritingDirection &writingDirection) {
switch (writingDirection) {
case WritingDirection::Natural:
return "natural";
case WritingDirection::LeftToRight:
return "ltr";
case WritingDirection::RightToLeft:
return "rtl";
}
}
inline void fromRawValue(
const RawValue &value,
TextDecorationLineType &result) {
auto string = (std::string)value;
if (string == "none") {
result = TextDecorationLineType::None;
return;
}
if (string == "underline") {
result = TextDecorationLineType::Underline;
return;
}
// TODO: remove "line-through" after deprecation
if (string == "strikethrough" || string == "line-through") {
result = TextDecorationLineType::Strikethrough;
return;
}
// TODO: remove "underline line-through" after "line-through" deprecation
if (string == "underline-strikethrough" ||
string == "underline line-through") {
result = TextDecorationLineType::UnderlineStrikethrough;
return;
}
abort();
}
inline std::string toString(
const TextDecorationLineType &textDecorationLineType) {
switch (textDecorationLineType) {
case TextDecorationLineType::None:
return "none";
case TextDecorationLineType::Underline:
return "underline";
case TextDecorationLineType::Strikethrough:
return "strikethrough";
case TextDecorationLineType::UnderlineStrikethrough:
return "underline-strikethrough";
}
}
inline void fromRawValue(
const RawValue &value,
TextDecorationLineStyle &result) {
auto string = (std::string)value;
if (string == "single") {
result = TextDecorationLineStyle::Single;
return;
}
if (string == "thick") {
result = TextDecorationLineStyle::Thick;
return;
}
if (string == "double") {
result = TextDecorationLineStyle::Double;
return;
}
abort();
}
inline std::string toString(
const TextDecorationLineStyle &textDecorationLineStyle) {
switch (textDecorationLineStyle) {
case TextDecorationLineStyle::Single:
return "single";
case TextDecorationLineStyle::Thick:
return "thick";
case TextDecorationLineStyle::Double:
return "double";
}
}
inline void fromRawValue(
const RawValue &value,
TextDecorationLinePattern &result) {
auto string = (std::string)value;
if (string == "solid") {
result = TextDecorationLinePattern::Solid;
return;
}
if (string == "dot") {
result = TextDecorationLinePattern::Dot;
return;
}
if (string == "dash") {
result = TextDecorationLinePattern::Dash;
return;
}
if (string == "dash-dot") {
result = TextDecorationLinePattern::DashDot;
return;
}
if (string == "dash-dot-dot") {
result = TextDecorationLinePattern::DashDotDot;
return;
}
abort();
}
inline std::string toString(
const TextDecorationLinePattern &textDecorationLinePattern) {
switch (textDecorationLinePattern) {
case TextDecorationLinePattern::Solid:
return "solid";
case TextDecorationLinePattern::Dot:
return "dot";
case TextDecorationLinePattern::Dash:
return "dash";
case TextDecorationLinePattern::DashDot:
return "dash-dot";
case TextDecorationLinePattern::DashDotDot:
return "dash-dot-dot";
}
}
inline ParagraphAttributes convertRawProp(
RawProps const &rawProps,
ParagraphAttributes const &sourceParagraphAttributes,
ParagraphAttributes const &defaultParagraphAttributes) {
auto paragraphAttributes = ParagraphAttributes{};
paragraphAttributes.maximumNumberOfLines = convertRawProp(
rawProps,
"numberOfLines",
sourceParagraphAttributes.maximumNumberOfLines,
defaultParagraphAttributes.maximumNumberOfLines);
paragraphAttributes.ellipsizeMode = convertRawProp(
rawProps,
"ellipsizeMode",
sourceParagraphAttributes.ellipsizeMode,
defaultParagraphAttributes.ellipsizeMode);
paragraphAttributes.textBreakStrategy = convertRawProp(
rawProps,
"textBreakStrategy",
sourceParagraphAttributes.textBreakStrategy,
defaultParagraphAttributes.textBreakStrategy);
paragraphAttributes.adjustsFontSizeToFit = convertRawProp(
rawProps,
"adjustsFontSizeToFit",
sourceParagraphAttributes.adjustsFontSizeToFit,
defaultParagraphAttributes.adjustsFontSizeToFit);
paragraphAttributes.minimumFontSize = convertRawProp(
rawProps,
"minimumFontSize",
sourceParagraphAttributes.minimumFontSize,
defaultParagraphAttributes.minimumFontSize);
paragraphAttributes.maximumFontSize = convertRawProp(
rawProps,
"maximumFontSize",
sourceParagraphAttributes.maximumFontSize,
defaultParagraphAttributes.maximumFontSize);
return paragraphAttributes;
}
inline void fromRawValue(
RawValue const &value,
AttributedString::Range &result) {
auto map = (better::map<std::string, int>)value;
auto start = map.find("start");
if (start != map.end()) {
result.location = start->second;
}
auto end = map.find("end");
if (end != map.end()) {
result.length = start->second - result.location;
}
}
inline std::string toString(AttributedString::Range const &range) {
return "{location: " + folly::to<std::string>(range.location) +
", length: " + folly::to<std::string>(range.length) + "}";
}
#ifdef ANDROID
inline folly::dynamic toDynamic(
const ParagraphAttributes &paragraphAttributes) {
auto values = folly::dynamic::object();
values("maximumNumberOfLines", paragraphAttributes.maximumNumberOfLines);
values("ellipsizeMode", toString(paragraphAttributes.ellipsizeMode));
values("textBreakStrategy", toString(paragraphAttributes.textBreakStrategy));
values("adjustsFontSizeToFit", paragraphAttributes.adjustsFontSizeToFit);
return values;
}
inline folly::dynamic toDynamic(const FontVariant &fontVariant) {
auto result = folly::dynamic::array();
if ((int)fontVariant & (int)FontVariant::SmallCaps) {
result.push_back("small-caps");
}
if ((int)fontVariant & (int)FontVariant::OldstyleNums) {
result.push_back("oldstyle-nums");
}
if ((int)fontVariant & (int)FontVariant::LiningNums) {
result.push_back("lining-nums");
}
if ((int)fontVariant & (int)FontVariant::TabularNums) {
result.push_back("tabular-nums");
}
if ((int)fontVariant & (int)FontVariant::ProportionalNums) {
result.push_back("proportional-nums");
}
return result;
}
inline folly::dynamic toDynamic(const TextAttributes &textAttributes) {
auto _textAttributes = folly::dynamic::object();
if (textAttributes.foregroundColor) {
_textAttributes(
"foregroundColor", toDynamic(textAttributes.foregroundColor));
}
if (textAttributes.backgroundColor) {
_textAttributes(
"backgroundColor", toDynamic(textAttributes.backgroundColor));
}
if (!std::isnan(textAttributes.opacity)) {
_textAttributes("opacity", textAttributes.opacity);
}
if (!textAttributes.fontFamily.empty()) {
_textAttributes("fontFamily", textAttributes.fontFamily);
}
if (!std::isnan(textAttributes.fontSize)) {
_textAttributes("fontSize", textAttributes.fontSize);
}
if (!std::isnan(textAttributes.fontSizeMultiplier)) {
_textAttributes("fontSizeMultiplier", textAttributes.fontSizeMultiplier);
}
if (textAttributes.fontWeight.has_value()) {
_textAttributes("fontWeight", toString(*textAttributes.fontWeight));
}
if (textAttributes.fontStyle.has_value()) {
_textAttributes("fontStyle", toString(*textAttributes.fontStyle));
}
if (textAttributes.fontVariant.has_value()) {
_textAttributes("fontVariant", toDynamic(*textAttributes.fontVariant));
}
if (textAttributes.allowFontScaling.has_value()) {
_textAttributes("allowFontScaling", *textAttributes.allowFontScaling);
}
if (!std::isnan(textAttributes.letterSpacing)) {
_textAttributes("letterSpacing", textAttributes.letterSpacing);
}
if (!std::isnan(textAttributes.lineHeight)) {
_textAttributes("lineHeight", textAttributes.lineHeight);
}
if (textAttributes.alignment.has_value()) {
_textAttributes("alignment", toString(*textAttributes.alignment));
}
if (textAttributes.baseWritingDirection.has_value()) {
_textAttributes(
"baseWritingDirection", toString(*textAttributes.baseWritingDirection));
}
// Decoration
if (textAttributes.textDecorationColor) {
_textAttributes(
"textDecorationColor", toDynamic(textAttributes.textDecorationColor));
}
if (textAttributes.textDecorationLineType.has_value()) {
_textAttributes(
"textDecorationLine", toString(*textAttributes.textDecorationLineType));
}
if (textAttributes.textDecorationLineStyle.has_value()) {
_textAttributes(
"textDecorationLineStyle",
toString(*textAttributes.textDecorationLineStyle));
}
if (textAttributes.textDecorationLinePattern.has_value()) {
_textAttributes(
"textDecorationLinePattern",
toString(*textAttributes.textDecorationLinePattern));
}
// Shadow
// textShadowOffset = textAttributes.textShadowOffset.has_value() ?
// textAttributes.textShadowOffset.value() : textShadowOffset;
if (!std::isnan(textAttributes.textShadowRadius)) {
_textAttributes("textShadowRadius", textAttributes.textShadowRadius);
}
if (textAttributes.textShadowColor) {
_textAttributes(
"textShadowColor", toDynamic(textAttributes.textShadowColor));
}
// Special
if (textAttributes.isHighlighted.has_value()) {
_textAttributes("isHighlighted", *textAttributes.isHighlighted);
}
if (textAttributes.layoutDirection.has_value()) {
_textAttributes(
"layoutDirection", toString(*textAttributes.layoutDirection));
}
return _textAttributes;
}
inline folly::dynamic toDynamic(const AttributedString &attributedString) {
auto value = folly::dynamic::object();
auto fragments = folly::dynamic::array();
for (auto fragment : attributedString.getFragments()) {
folly::dynamic dynamicFragment = folly::dynamic::object();
dynamicFragment["string"] = fragment.string;
if (fragment.parentShadowView.componentHandle) {
dynamicFragment["reactTag"] = fragment.parentShadowView.tag;
}
if (fragment.isAttachment()) {
dynamicFragment["isAttachment"] = true;
dynamicFragment["width"] =
(int)fragment.parentShadowView.layoutMetrics.frame.size.width;
dynamicFragment["height"] =
(int)fragment.parentShadowView.layoutMetrics.frame.size.height;
}
dynamicFragment["textAttributes"] = toDynamic(fragment.textAttributes);
fragments.push_back(dynamicFragment);
}
value("fragments", fragments);
value(
"hash", std::hash<facebook::react::AttributedString>{}(attributedString));
value("string", attributedString.getString());
return value;
}
inline folly::dynamic toDynamic(AttributedString::Range const &range) {
folly::dynamic dynamicValue = folly::dynamic::object();
dynamicValue["location"] = range.location;
dynamicValue["length"] = range.length;
return dynamicValue;
}
#endif
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,163 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <functional>
#include <limits>
namespace facebook {
namespace react {
enum class FontStyle { Normal, Italic, Oblique };
enum class FontWeight : int {
Weight100 = 100,
UltraLight = 100,
Weight200 = 200,
Thin = 200,
Weight300 = 300,
Light = 300,
Weight400 = 400,
Regular = 400,
Weight500 = 500,
Medium = 500,
Weight600 = 600,
Semibold = 600,
Demibold = 600,
Weight700 = 700,
Bold = 700,
Weight800 = 800,
Heavy = 800,
Weight900 = 900,
Black = 900
};
enum class FontVariant : int {
Default = 0,
SmallCaps = 1 << 1,
OldstyleNums = 1 << 2,
LiningNums = 1 << 3,
TabularNums = 1 << 4,
ProportionalNums = 1 << 5
};
enum class EllipsizeMode {
Clip, // Do not add ellipsize, simply clip.
Head, // Truncate at head of line: "...wxyz".
Tail, // Truncate at tail of line: "abcd...".
Middle // Truncate middle of line: "ab...yz".
};
enum class TextBreakStrategy { Simple, Balanced, HighQuality };
enum class TextAlignment {
Natural, // Indicates the default alignment for script.
Left, // Visually left aligned.
Center, // Visually centered.
Right, // Visually right aligned.
Justified // Fully-justified. The last line in a paragraph is natural-aligned.
};
enum class WritingDirection {
Natural, // Determines direction using the Unicode Bidi Algorithm rules P2 and
// P3.
LeftToRight, // Left to right writing direction.
RightToLeft // Right to left writing direction.
};
enum class TextDecorationLineType {
None,
Underline,
Strikethrough,
UnderlineStrikethrough
};
enum class TextDecorationLineStyle { Single, Thick, Double };
enum class TextDecorationLinePattern {
Solid,
Dot,
Dash,
DashDot,
DashDotDot,
};
} // namespace react
} // namespace facebook
namespace std {
template <>
struct hash<facebook::react::FontVariant> {
size_t operator()(const facebook::react::FontVariant &v) const {
return hash<int>()(static_cast<int>(v));
}
};
template <>
struct hash<facebook::react::TextAlignment> {
size_t operator()(const facebook::react::TextAlignment &v) const {
return hash<int>()(static_cast<int>(v));
}
};
template <>
struct hash<facebook::react::FontStyle> {
size_t operator()(const facebook::react::FontStyle &v) const {
return hash<int>()(static_cast<int>(v));
}
};
template <>
struct hash<facebook::react::TextDecorationLineType> {
size_t operator()(const facebook::react::TextDecorationLineType &v) const {
return hash<int>()(static_cast<int>(v));
}
};
template <>
struct hash<facebook::react::WritingDirection> {
size_t operator()(const facebook::react::WritingDirection &v) const {
return hash<int>()(static_cast<int>(v));
}
};
template <>
struct hash<facebook::react::TextDecorationLinePattern> {
size_t operator()(const facebook::react::TextDecorationLinePattern &v) const {
return hash<int>()(static_cast<int>(v));
}
};
template <>
struct hash<facebook::react::TextDecorationLineStyle> {
size_t operator()(const facebook::react::TextDecorationLineStyle &v) const {
return hash<int>()(static_cast<int>(v));
}
};
template <>
struct hash<facebook::react::FontWeight> {
size_t operator()(const facebook::react::FontWeight &v) const {
return hash<int>()(static_cast<int>(v));
}
};
template <>
struct hash<facebook::react::EllipsizeMode> {
size_t operator()(const facebook::react::EllipsizeMode &v) const {
return hash<int>()(static_cast<int>(v));
}
};
template <>
struct hash<facebook::react::TextBreakStrategy> {
size_t operator()(const facebook::react::TextBreakStrategy &v) const {
return hash<int>()(static_cast<int>(v));
}
};
} // namespace std

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <memory>
#include <assert.h>
#include <gtest/gtest.h>
#include <react/attributedstring/TextAttributes.h>
#include <react/attributedstring/conversions.h>
#include <react/attributedstring/primitives.h>
#include <react/graphics/conversions.h>
namespace facebook {
namespace react {
#ifdef ANDROID
TEST(AttributedStringTest, testToDynamic) {
auto attString = new AttributedString();
auto fragment = new AttributedString::Fragment();
fragment->string = "test";
auto text = new TextAttributes();
text->foregroundColor = {
colorFromComponents({100 / 255.0, 153 / 255.0, 200 / 255.0, 1.0})};
text->opacity = 0.5;
text->fontStyle = FontStyle::Italic;
text->fontWeight = FontWeight::Thin;
text->fontVariant = FontVariant::TabularNums;
fragment->textAttributes = *text;
attString->prependFragment(*fragment);
auto result = toDynamic(*attString);
assert(result["string"] == fragment->string);
auto textAttribute = result["fragments"][0]["textAttributes"];
assert(textAttribute["foregroundColor"] == toDynamic(text->foregroundColor));
assert(textAttribute["opacity"] == text->opacity);
assert(textAttribute["fontStyle"] == toString(*text->fontStyle));
assert(textAttribute["fontWeight"] == toString(*text->fontWeight));
}
#endif
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <memory>
#include <assert.h>
#include <gtest/gtest.h>
#include <react/attributedstring/ParagraphAttributes.h>
#include <react/attributedstring/conversions.h>
#include <react/attributedstring/primitives.h>
namespace facebook {
namespace react {
#ifdef ANDROID
TEST(ParagraphAttributesTest, testToDynamic) {
auto paragraphAttributes = ParagraphAttributes();
paragraphAttributes.maximumNumberOfLines = 2;
paragraphAttributes.adjustsFontSizeToFit = false;
paragraphAttributes.ellipsizeMode = EllipsizeMode::Middle;
auto result = toDynamic(paragraphAttributes);
assert(
result["maximumNumberOfLines"] ==
paragraphAttributes.maximumNumberOfLines);
assert(
result["adjustsFontSizeToFit"] ==
paragraphAttributes.adjustsFontSizeToFit);
assert(
result["ellipsizeMode"] == toString(paragraphAttributes.ellipsizeMode));
}
#endif
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <memory>
#include <assert.h>
#include <gtest/gtest.h>
#include <react/attributedstring/TextAttributes.h>
#include <react/attributedstring/conversions.h>
#include <react/attributedstring/primitives.h>
#include <react/graphics/conversions.h>
namespace facebook {
namespace react {
#ifdef ANDROID
TEST(TextAttributesTest, testToDynamic) {
auto text = TextAttributes();
text.foregroundColor = {
colorFromComponents({200 / 255.0, 153 / 255.0, 100 / 255.0, 1.0})};
text.opacity = 0.5;
text.fontStyle = FontStyle::Italic;
text.fontWeight = FontWeight::Thin;
text.fontVariant = FontVariant::TabularNums;
auto result = toDynamic(text);
assert(result["foregroundColor"] == toDynamic(text.foregroundColor));
assert(result["opacity"] == text.opacity);
assert(result["fontStyle"] == toString(*text.fontStyle));
assert(result["fontWeight"] == toString(*text.fontWeight));
}
#endif
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,79 @@
load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode")
load(
"//tools/build_defs/oss:rn_defs.bzl",
"ANDROID",
"APPLE",
"CXX",
"YOGA_CXX_TARGET",
"fb_xplat_cxx_test",
"get_apple_compiler_flags",
"get_apple_inspector_flags",
"react_native_xplat_target",
"rn_xplat_cxx_library",
"subdir_glob",
)
APPLE_COMPILER_FLAGS = get_apple_compiler_flags()
rn_xplat_cxx_library(
name = "activityindicator",
srcs = glob(
["**/*.cpp"],
exclude = glob(["tests/**/*.cpp"]),
),
headers = [],
header_namespace = "",
exported_headers = subdir_glob(
[
("", "*.h"),
],
prefix = "react/components/activityindicator",
),
compiler_flags = [
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wall",
],
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"],
fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(),
macosx_tests_override = [],
platforms = (ANDROID, APPLE, CXX),
preprocessor_flags = [
"-DLOG_TAG=\"ReactNative\"",
"-DWITH_FBSYSTRACE=1",
],
tests = [":tests"],
visibility = ["PUBLIC"],
deps = [
"//xplat/fbsystrace:fbsystrace",
"//xplat/folly:headers_only",
"//xplat/folly:memory",
"//xplat/folly:molly",
"//xplat/third-party/glog:glog",
YOGA_CXX_TARGET,
react_native_xplat_target("fabric/debug:debug"),
react_native_xplat_target("fabric/core:core"),
react_native_xplat_target("fabric/graphics:graphics"),
react_native_xplat_target("fabric/components/view:view"),
],
)
fb_xplat_cxx_test(
name = "tests",
srcs = glob(["tests/**/*.cpp"]),
headers = glob(["tests/**/*.h"]),
compiler_flags = [
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wall",
],
platforms = (ANDROID, APPLE, CXX),
deps = [
":activityindicator",
"//xplat/folly:molly",
"//xplat/third-party/gmock:gtest",
],
)

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <folly/dynamic.h>
#include <react/components/activityindicator/primitives.h>
namespace facebook {
namespace react {
inline void fromRawValue(
const RawValue &value,
ActivityIndicatorViewSize &result) {
auto string = (std::string)value;
if (string == "large") {
result = ActivityIndicatorViewSize::Large;
return;
}
if (string == "small") {
result = ActivityIndicatorViewSize::Small;
return;
}
abort();
}
inline std::string toString(const ActivityIndicatorViewSize &value) {
switch (value) {
case ActivityIndicatorViewSize::Large:
return "large";
case ActivityIndicatorViewSize::Small:
return "small";
}
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,81 @@
load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode")
load(
"//tools/build_defs/oss:rn_defs.bzl",
"ANDROID",
"APPLE",
"CXX",
"YOGA_CXX_TARGET",
"fb_xplat_cxx_test",
"get_apple_compiler_flags",
"get_apple_inspector_flags",
"react_native_xplat_target",
"rn_xplat_cxx_library",
"subdir_glob",
)
APPLE_COMPILER_FLAGS = get_apple_compiler_flags()
rn_xplat_cxx_library(
name = "image",
srcs = glob(
["**/*.cpp"],
exclude = glob(["tests/**/*.cpp"]),
),
headers = [],
header_namespace = "",
exported_headers = subdir_glob(
[
("", "*.h"),
],
prefix = "react/components/image",
),
compiler_flags = [
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wall",
],
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"],
fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(),
macosx_tests_override = [],
platforms = (ANDROID, APPLE, CXX),
preprocessor_flags = [
"-DLOG_TAG=\"ReactNative\"",
"-DWITH_FBSYSTRACE=1",
],
tests = [":tests"],
visibility = ["PUBLIC"],
deps = [
"//xplat/fbsystrace:fbsystrace",
"//xplat/folly:headers_only",
"//xplat/folly:memory",
"//xplat/folly:molly",
"//xplat/third-party/glog:glog",
YOGA_CXX_TARGET,
react_native_xplat_target("fabric/debug:debug"),
react_native_xplat_target("fabric/core:core"),
react_native_xplat_target("fabric/graphics:graphics"),
react_native_xplat_target("fabric/imagemanager:imagemanager"),
react_native_xplat_target("fabric/components/view:view"),
],
)
fb_xplat_cxx_test(
name = "tests",
srcs = glob(["tests/**/*.cpp"]),
headers = glob(["tests/**/*.h"]),
compiler_flags = [
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wall",
],
contacts = ["oncall+react_native@xmail.facebook.com"],
platforms = (ANDROID, APPLE, CXX),
deps = [
":image",
"//xplat/folly:molly",
"//xplat/third-party/gmock:gtest",
],
)

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <react/components/image/ImageShadowNode.h>
#include <react/core/ConcreteComponentDescriptor.h>
#include <react/imagemanager/ImageManager.h>
#include <react/utils/ContextContainer.h>
namespace facebook {
namespace react {
/*
* Descriptor for <Image> component.
*/
class ImageComponentDescriptor final
: public ConcreteComponentDescriptor<ImageShadowNode> {
public:
ImageComponentDescriptor(ComponentDescriptorParameters const &parameters)
: ConcreteComponentDescriptor(parameters),
imageManager_(std::make_shared<ImageManager>(contextContainer_)){};
void adopt(UnsharedShadowNode shadowNode) const override {
ConcreteComponentDescriptor::adopt(shadowNode);
assert(std::dynamic_pointer_cast<ImageShadowNode>(shadowNode));
auto imageShadowNode =
std::static_pointer_cast<ImageShadowNode>(shadowNode);
// `ImageShadowNode` uses `ImageManager` to initiate image loading and
// communicate the loading state and results to mounting layer.
imageShadowNode->setImageManager(imageManager_);
}
private:
const SharedImageManager imageManager_;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "ImageEventEmitter.h"
namespace facebook {
namespace react {
void ImageEventEmitter::onLoadStart() const {
dispatchEvent("loadStart");
}
void ImageEventEmitter::onLoad() const {
dispatchEvent("load");
}
void ImageEventEmitter::onLoadEnd() const {
dispatchEvent("loadEnd");
}
void ImageEventEmitter::onProgress(double progress) const {
dispatchEvent("progress", [=](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "progress", progress);
return payload;
});
}
void ImageEventEmitter::onError() const {
dispatchEvent("error");
}
void ImageEventEmitter::onPartialLoad() const {
dispatchEvent("partialLoad");
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <react/components/view/ViewEventEmitter.h>
namespace facebook {
namespace react {
class ImageEventEmitter : public ViewEventEmitter {
public:
using ViewEventEmitter::ViewEventEmitter;
void onLoadStart() const;
void onLoad() const;
void onLoadEnd() const;
void onProgress(double) const;
void onError() const;
void onPartialLoad() const;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <react/components/image/ImageProps.h>
#include <react/components/image/conversions.h>
#include <react/core/propsConversions.h>
namespace facebook {
namespace react {
ImageProps::ImageProps(const ImageProps &sourceProps, const RawProps &rawProps)
: ViewProps(sourceProps, rawProps),
sources(convertRawProp(rawProps, "source", sourceProps.sources, {})),
defaultSources(convertRawProp(
rawProps,
"defaultSource",
sourceProps.defaultSources,
{})),
resizeMode(convertRawProp(
rawProps,
"resizeMode",
sourceProps.resizeMode,
ImageResizeMode::Stretch)),
blurRadius(
convertRawProp(rawProps, "blurRadius", sourceProps.blurRadius, {})),
capInsets(
convertRawProp(rawProps, "capInsets", sourceProps.capInsets, {})),
tintColor(
convertRawProp(rawProps, "tintColor", sourceProps.tintColor, {})) {}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <react/components/view/ViewProps.h>
#include <react/graphics/Color.h>
#include <react/imagemanager/primitives.h>
namespace facebook {
namespace react {
// TODO (T28334063): Consider for codegen.
class ImageProps final : public ViewProps {
public:
ImageProps() = default;
ImageProps(const ImageProps &sourceProps, const RawProps &rawProps);
#pragma mark - Props
const ImageSources sources{};
const ImageSources defaultSources{};
const ImageResizeMode resizeMode{ImageResizeMode::Stretch};
const Float blurRadius{};
const EdgeInsets capInsets{};
const SharedColor tintColor{};
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,86 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <cstdlib>
#include <limits>
#include <react/components/image/ImageShadowNode.h>
#include <react/core/LayoutContext.h>
#include "ImageState.h"
namespace facebook {
namespace react {
const char ImageComponentName[] = "Image";
void ImageShadowNode::setImageManager(const SharedImageManager &imageManager) {
ensureUnsealed();
imageManager_ = imageManager;
}
void ImageShadowNode::updateStateIfNeeded() {
ensureUnsealed();
auto const &imageSource = getImageSource();
auto const &currentState = getStateData();
if (currentState.getImageSource() == imageSource) {
return;
}
auto state = ImageState{
imageSource, imageManager_->requestImage(imageSource, getSurfaceId())};
setStateData(std::move(state));
}
ImageSource ImageShadowNode::getImageSource() const {
auto sources = getConcreteProps().sources;
if (sources.size() == 0) {
return {
/* .type = */ ImageSource::Type::Invalid,
};
}
if (sources.size() == 1) {
return sources[0];
}
auto layoutMetrics = getLayoutMetrics();
auto size = layoutMetrics.getContentFrame().size;
auto scale = layoutMetrics.pointScaleFactor;
auto targetImageArea = size.width * size.height * scale * scale;
auto bestFit = std::numeric_limits<Float>::infinity();
auto bestSource = ImageSource{};
for (const auto &source : sources) {
auto sourceSize = source.size;
auto sourceScale = source.scale == 0 ? scale : source.scale;
auto sourceArea =
sourceSize.width * sourceSize.height * sourceScale * sourceScale;
auto fit = std::abs(1 - (sourceArea / targetImageArea));
if (fit < bestFit) {
bestFit = fit;
bestSource = source;
}
}
return bestSource;
}
#pragma mark - LayoutableShadowNode
void ImageShadowNode::layout(LayoutContext layoutContext) {
updateStateIfNeeded();
ConcreteViewShadowNode::layout(layoutContext);
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <react/components/image/ImageEventEmitter.h>
#include <react/components/image/ImageProps.h>
#include <react/components/image/ImageState.h>
#include <react/components/view/ConcreteViewShadowNode.h>
#include <react/imagemanager/ImageManager.h>
#include <react/imagemanager/primitives.h>
namespace facebook {
namespace react {
extern const char ImageComponentName[];
/*
* `ShadowNode` for <Image> component.
*/
class ImageShadowNode final : public ConcreteViewShadowNode<
ImageComponentName,
ImageProps,
ImageEventEmitter,
ImageState> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
/*
* Associates a shared `ImageManager` with the node.
*/
void setImageManager(const SharedImageManager &imageManager);
static ImageState initialStateData(
ShadowNodeFragment const &fragment,
SurfaceId const surfaceId,
ComponentDescriptor const &componentDescriptor) {
auto imageSource = ImageSource{ImageSource::Type::Invalid};
return {imageSource, {imageSource, nullptr}};
}
#pragma mark - LayoutableShadowNode
void layout(LayoutContext layoutContext) override;
private:
ImageSource getImageSource() const;
SharedImageManager imageManager_;
void updateStateIfNeeded();
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,22 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "ImageState.h"
namespace facebook {
namespace react {
ImageSource ImageState::getImageSource() const {
return imageSource_;
}
ImageRequest const &ImageState::getImageRequest() const {
return *imageRequest_;
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <folly/dynamic.h>
#include <react/imagemanager/ImageRequest.h>
#include <react/imagemanager/primitives.h>
namespace facebook {
namespace react {
/*
* State for <Image> component.
*/
class ImageState final {
public:
ImageState(ImageSource const &imageSource, ImageRequest imageRequest)
: imageSource_(imageSource),
imageRequest_(
std::make_shared<ImageRequest>(std::move(imageRequest))){};
/*
* Returns stored ImageSource object.
*/
ImageSource getImageSource() const;
/*
* Exposes for reading stored `ImageRequest` object.
* `ImageRequest` object cannot be copied or moved from `ImageLocalData`.
*/
ImageRequest const &getImageRequest() const;
#ifdef ANDROID
ImageState(ImageState const &previousState, folly::dynamic data){};
/*
* Empty implementation for Android because it doesn't use this class.
*/
folly::dynamic getDynamic() const {
return {};
};
#endif
private:
ImageSource imageSource_;
std::shared_ptr<ImageRequest> imageRequest_;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,132 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <better/map.h>
#include <folly/dynamic.h>
#include <react/graphics/conversions.h>
#include <react/imagemanager/primitives.h>
namespace facebook {
namespace react {
inline void fromRawValue(const RawValue &value, ImageSource &result) {
if (value.hasType<std::string>()) {
result = {
/* .type = */ ImageSource::Type::Remote,
/* .uri = */ (std::string)value,
};
return;
}
if (value.hasType<better::map<std::string, RawValue>>()) {
auto items = (better::map<std::string, RawValue>)value;
result = {};
result.type = ImageSource::Type::Remote;
if (items.find("__packager_asset") != items.end()) {
result.type = ImageSource::Type::Local;
}
if (items.find("width") != items.end() &&
items.find("height") != items.end() &&
// The following checks have to be removed after codegen is shipped.
// See T45151459.
items.at("width").hasType<Float>() &&
items.at("height").hasType<Float>()) {
result.size = {(Float)items.at("width"), (Float)items.at("height")};
}
if (items.find("scale") != items.end() &&
// The following checks have to be removed after codegen is shipped.
// See T45151459.
items.at("scale").hasType<Float>()) {
result.scale = (Float)items.at("scale");
} else {
result.scale = items.find("deprecated") != items.end() ? 0.0 : 1.0;
}
if (items.find("url") != items.end() &&
// The following should be removed after codegen is shipped.
// See T45151459.
items.at("url").hasType<std::string>()) {
result.uri = (std::string)items.at("url");
}
if (items.find("uri") != items.end() &&
// The following should be removed after codegen is shipped.
// See T45151459.
items.at("uri").hasType<std::string>()) {
result.uri = (std::string)items.at("uri");
}
if (items.find("bundle") != items.end() &&
// The following should be removed after codegen is shipped.
// See T45151459.
items.at("bundle").hasType<std::string>()) {
result.bundle = (std::string)items.at("bundle");
result.type = ImageSource::Type::Local;
}
return;
}
// The following should be removed after codegen is shipped.
// See T45151459.
result = {};
result.type = ImageSource::Type::Invalid;
}
inline std::string toString(const ImageSource &value) {
return "{uri: " + value.uri + "}";
}
inline void fromRawValue(const RawValue &value, ImageResizeMode &result) {
assert(value.hasType<std::string>());
auto stringValue = (std::string)value;
if (stringValue == "cover") {
result = ImageResizeMode::Cover;
return;
}
if (stringValue == "contain") {
result = ImageResizeMode::Contain;
return;
}
if (stringValue == "stretch") {
result = ImageResizeMode::Stretch;
return;
}
if (stringValue == "center") {
result = ImageResizeMode::Center;
return;
}
if (stringValue == "repeat") {
result = ImageResizeMode::Repeat;
return;
}
abort();
}
inline std::string toString(const ImageResizeMode &value) {
switch (value) {
case ImageResizeMode::Cover:
return "cover";
case ImageResizeMode::Contain:
return "contain";
case ImageResizeMode::Stretch:
return "stretch";
case ImageResizeMode::Center:
return "center";
case ImageResizeMode::Repeat:
return "repeat";
}
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,14 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <memory>
#include <gtest/gtest.h>
TEST(ImageTest, testSomething) {
// TODO
}

View File

@ -0,0 +1,53 @@
load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode")
load(
"//tools/build_defs/oss:rn_defs.bzl",
"APPLE",
"get_apple_compiler_flags",
"get_apple_inspector_flags",
"react_native_xplat_target",
"rn_xplat_cxx_library",
"subdir_glob",
)
APPLE_COMPILER_FLAGS = get_apple_compiler_flags()
rn_xplat_cxx_library(
name = "legacyviewmanagerinterop",
srcs = glob(
[
"**/*.cpp",
"**/*.mm",
],
),
headers = [],
header_namespace = "",
exported_headers = subdir_glob(
[
("", "*.h"),
],
prefix = "react/components/legacyviewmanagerinterop",
),
compiler_flags = [
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wall",
],
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"],
fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(),
force_static = True,
platforms = (APPLE),
preprocessor_flags = [
"-DLOG_TAG=\"ReactNative\"",
"-DWITH_FBSYSTRACE=1",
],
visibility = ["PUBLIC"],
deps = [
"//xplat/folly:headers_only",
"//xplat/folly:memory",
react_native_xplat_target("fabric/core:core"),
react_native_xplat_target("fabric/components/view:view"),
"//xplat/js/react-native-github:generated_components-rncore",
],
)

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <react/components/legacyviewmanagerinterop/LegacyViewManagerInteropShadowNode.h>
#include <react/core/ConcreteComponentDescriptor.h>
namespace facebook {
namespace react {
class LegacyViewManagerInteropComponentDescriptor final
: public ConcreteComponentDescriptor<LegacyViewManagerInteropShadowNode> {
public:
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
LegacyViewManagerInteropComponentDescriptor(
ComponentDescriptorParameters const &parameters);
/*
* Returns `name` and `handle` based on a `flavor`, not on static data from
* `LegacyViewManagerInteropShadowNode`.
*/
ComponentHandle getComponentHandle() const override;
ComponentName getComponentName() const override;
protected:
void adopt(ShadowNode::Unshared shadowNode) const override;
private:
std::shared_ptr<void> const _coordinator;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,83 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "LegacyViewManagerInteropComponentDescriptor.h"
#include <React/RCTBridge.h>
#include <React/RCTComponentData.h>
#include <React/RCTModuleData.h>
#include <React/RCTUIManager.h>
#include <react/utils/ContextContainer.h>
#include <react/utils/ManagedObjectWrapper.h>
#include "LegacyViewManagerInteropState.h"
#include "RCTLegacyViewManagerInteropCoordinator.h"
namespace facebook {
namespace react {
static std::string moduleNameFromComponentName(const std::string &componentName)
{
// TODO: remove FB specific code (T56174424)
if (componentName == "StickerInputView") {
return "FBStickerInputViewManager";
}
std::string fbPrefix("FB");
if (std::mismatch(fbPrefix.begin(), fbPrefix.end(), componentName.begin()).first == fbPrefix.end()) {
// If `moduleName` has "FB" prefix.
return componentName + "Manager";
}
return "RCT" + componentName + "Manager";
}
inline NSString *RCTNSStringFromString(const std::string &string)
{
return [NSString stringWithCString:string.c_str() encoding:NSUTF8StringEncoding];
}
static std::shared_ptr<void> const constructCoordinator(
ContextContainer::Shared const &contextContainer,
ComponentDescriptor::Flavor const &flavor)
{
auto componentName = *std::static_pointer_cast<std::string const>(flavor);
auto moduleName = moduleNameFromComponentName(componentName);
Class module = NSClassFromString(RCTNSStringFromString(moduleName));
assert(module);
RCTBridge *bridge = (RCTBridge *)unwrapManagedObjectWeakly(contextContainer->at<std::shared_ptr<void>>("Bridge"));
RCTComponentData *componentData = [[RCTComponentData alloc] initWithManagerClass:module bridge:bridge];
return wrapManagedObject([[RCTLegacyViewManagerInteropCoordinator alloc] initWithComponentData:componentData
bridge:bridge]);
}
LegacyViewManagerInteropComponentDescriptor::LegacyViewManagerInteropComponentDescriptor(
ComponentDescriptorParameters const &parameters)
: ConcreteComponentDescriptor(parameters), _coordinator(constructCoordinator(contextContainer_, flavor_))
{
}
ComponentHandle LegacyViewManagerInteropComponentDescriptor::getComponentHandle() const
{
return reinterpret_cast<ComponentHandle>(getComponentName());
}
ComponentName LegacyViewManagerInteropComponentDescriptor::getComponentName() const
{
return std::static_pointer_cast<std::string const>(this->flavor_)->c_str();
}
void LegacyViewManagerInteropComponentDescriptor::adopt(ShadowNode::Unshared shadowNode) const
{
ConcreteComponentDescriptor::adopt(shadowNode);
assert(std::dynamic_pointer_cast<LegacyViewManagerInteropShadowNode>(shadowNode));
auto legacyViewManagerInteropShadowNode = std::static_pointer_cast<LegacyViewManagerInteropShadowNode>(shadowNode);
auto state = LegacyViewManagerInteropState{};
state.coordinator = _coordinator;
legacyViewManagerInteropShadowNode->setStateData(std::move(state));
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,15 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
namespace facebook {
namespace react {
extern const char LegacyViewManagerInteropComponentName[] =
"LegacyViewManagerInterop";
} // namespace react
} // namespace facebook

Some files were not shown because too many files have changed in this diff Show More