first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,36 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake)
react_native_android_selector(platform_SRC
platform/android/react/utils/*.cpp
cxx/android/react/utils/*.cpp)
file(GLOB react_utils_SRC CONFIGURE_DEPENDS
*.cpp
${platform_SRC})
add_library(react_utils OBJECT ${react_utils_SRC})
react_native_android_selector(platform_DIR
${CMAKE_CURRENT_SOURCE_DIR}/platform/android/
${CMAKE_CURRENT_SOURCE_DIR}/platform/cxx/
)
target_include_directories(react_utils
PUBLIC
${REACT_COMMON_DIR}
${platform_DIR}
)
target_link_libraries(react_utils
glog
glog_init
jsi
react_debug)
target_compile_reactnative_options(react_utils PRIVATE)
target_compile_options(react_utils PRIVATE -Wpedantic)

View File

@@ -0,0 +1,114 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <memory>
#include <mutex>
#include <optional>
#include <shared_mutex>
#include <string>
#include <unordered_map>
#include <react/debug/flags.h>
#include <react/debug/react_native_assert.h>
namespace facebook::react {
/*
* General purpose dependency injection container.
* Instance types must be copyable.
*/
class ContextContainer final {
public:
using Shared [[deprecated("Use std::shared_ptr<const ContextContainer> instead.")]] =
std::shared_ptr<const ContextContainer>;
/*
* Registers an instance of the particular type `T` in the container
* using the provided `key`. Only one instance can be registered per key.
* The method does nothing if given `key` already exists in the container.
*
* Convention is to use the plain base class name for the key, so for
* example if the type `T` is `std::shared_ptr<const ReactNativeConfig>`,
* then one would use `"ReactNativeConfig"` for the `key`, even if the
* instance is actually a `shared_ptr` of derived class
*`ReactNativeConfig`.
*/
template <typename T>
void insert(const std::string &key, const T &instance) const
{
std::unique_lock lock(mutex_);
instances_.insert({key, std::make_shared<T>(instance)});
}
/*
* Removes an instance stored for a given `key`.
* Does nothing if the instance was not found.
*/
void erase(const std::string &key) const
{
std::unique_lock lock(mutex_);
instances_.erase(key);
}
/*
* Updates the container with values from a given container.
* Values with keys that already exist in the container will be replaced with
* values from the given container.
*/
void update(const ContextContainer &contextContainer) const
{
std::unique_lock lock(mutex_);
for (const auto &pair : contextContainer.instances_) {
instances_.erase(pair.first);
instances_.insert(pair);
}
}
/*
* Returns a previously registered instance of the particular type `T`
* for `key`.
* Throws an exception if the instance could not be found.
*/
template <typename T>
T at(const std::string &key) const
{
std::shared_lock lock(mutex_);
react_native_assert(
instances_.find(key) != instances_.end() && "ContextContainer doesn't have an instance for given key.");
return *static_cast<T *>(instances_.at(key).get());
}
/*
* Returns a (wrapped in an optional) previously registered instance of
* the particular type `T` for given `key`.
* Returns an empty optional if the instance could not be found.
*/
template <typename T>
std::optional<T> find(const std::string &key) const
{
std::shared_lock lock(mutex_);
auto iterator = instances_.find(key);
if (iterator == instances_.end()) {
return {};
}
return *static_cast<T *>(iterator->second.get());
}
private:
mutable std::shared_mutex mutex_;
// Protected by mutex_`.
mutable std::unordered_map<std::string, std::shared_ptr<void>> instances_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <cmath>
namespace facebook::react {
constexpr float kDefaultEpsilon = 0.005f;
template <typename T>
inline bool floatEquality(T a, T b, T epsilon = static_cast<T>(kDefaultEpsilon))
{
return (std::isnan(a) && std::isnan(b)) || (!std::isnan(a) && !std::isnan(b) && std::abs(a - b) < epsilon);
}
} // namespace facebook::react

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/debug/react_native_assert.h>
#if defined(__APPLE__)
#include <TargetConditionals.h>
#endif
#if defined(__OBJC__) && defined(__cplusplus)
#if TARGET_OS_MAC
#include <memory>
#import <Foundation/Foundation.h>
@interface RCTInternalGenericWeakWrapper : NSObject
@property (nonatomic, weak) id object;
@end
namespace facebook::react {
namespace detail {
/*
* A custom deleter used for the deallocation of Objective-C managed objects.
* To be used only by `wrapManagedObject`.
*/
void wrappedManagedObjectDeleter(void *cfPointer) noexcept;
} // namespace detail
/*
* `wrapManagedObject` and `unwrapManagedObject` are wrapper functions that
* convert ARC-managed objects into `std::shared_ptr<void>` and vice-versa. It's
* a very useful mechanism when we need to pass Objective-C objects through pure
* C++ code, pass blocks into C++ lambdas, and so on.
*
* The idea behind this mechanism is quite simple but tricky: When we
* instantiate a C++ shared pointer for a managed object, we practically call
* `CFRetain` for it once and then we represent this single retaining operation
* as a counter inside the shared pointer; when the counter became zero, we call
* `CFRelease` on the object. In this model, one bump of ARC-managed counter is
* represented as multiple bumps of C++ counter, so we can have multiple
* counters for the same object that form some kind of counters tree.
*/
inline std::shared_ptr<void> wrapManagedObject(id object) noexcept
{
return std::shared_ptr<void>((__bridge_retained void *)object, detail::wrappedManagedObjectDeleter);
}
inline id unwrapManagedObject(const std::shared_ptr<void> &object) noexcept
{
return (__bridge id)object.get();
}
inline std::shared_ptr<void> wrapManagedObjectWeakly(id object) noexcept
{
RCTInternalGenericWeakWrapper *weakWrapper = [RCTInternalGenericWeakWrapper new];
weakWrapper.object = object;
return wrapManagedObject(weakWrapper);
}
inline id unwrapManagedObjectWeakly(const std::shared_ptr<void> &object) noexcept
{
RCTInternalGenericWeakWrapper *weakWrapper = (RCTInternalGenericWeakWrapper *)unwrapManagedObject(object);
react_native_assert(weakWrapper && "`RCTInternalGenericWeakWrapper` instance must not be `nil`.");
return weakWrapper.object;
}
} // namespace facebook::react
#endif
#endif

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "ManagedObjectWrapper.h"
#if TARGET_OS_MAC
namespace facebook::react::detail {
void wrappedManagedObjectDeleter(void *cfPointer) noexcept
{
// A shared pointer does call custom deleter on `nullptr`s.
// This is somewhat counter-intuitively but makes sense considering the type-erasured nature of shared pointer and an
// aliasing constructor feature. `CFRelease` crashes on null pointer though. Therefore we must check for this case
// explicitly.
if (cfPointer == NULL) {
return;
}
CFRelease(cfPointer);
}
} // namespace facebook::react::detail
@implementation RCTInternalGenericWeakWrapper
@end
#endif

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <utility>
namespace facebook::react {
template <typename Lambda>
class OnScopeExit {
public:
explicit OnScopeExit(const Lambda &&lambda) noexcept : lambda_(std::move(lambda)) {}
// Non-movable
OnScopeExit(const OnScopeExit &) = delete;
OnScopeExit(OnScopeExit &&) = delete;
// Non-copyable
OnScopeExit &operator=(const OnScopeExit &) = delete;
OnScopeExit &operator=(OnScopeExit &&) = delete;
~OnScopeExit() noexcept
{
lambda_();
}
private:
Lambda lambda_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <type_traits>
namespace facebook::react::traits {
template <typename ExpectedT>
static constexpr bool containsType()
{
return false;
}
template <typename ExpectedT, typename FirstT, typename... RestT>
static constexpr bool containsType()
{
if constexpr (sizeof...(RestT) > 0) {
return std::is_same_v<ExpectedT, FirstT> || containsType<ExpectedT, RestT...>();
} else {
return std::is_same_v<ExpectedT, FirstT>;
}
}
} // namespace facebook::react::traits

View File

@@ -0,0 +1,57 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
require "json"
package = JSON.parse(File.read(File.join(__dir__, "..", "..", "..", "package.json")))
version = package['version']
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which were presumably in.
source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1")
else
source[:tag] = "v#{version}"
end
Pod::Spec.new do |s|
source_files = ["*.{m,mm,cpp,h}", "platform/ios/**/*.{m,mm,cpp,h}"]
header_search_paths = [
"\"$(PODS_TARGET_SRCROOT)/../../\"",
]
s.name = "React-utils"
s.version = version
s.summary = "-" # TODO
s.homepage = "https://reactnative.dev/"
s.license = package["license"]
s.author = "Meta Platforms, Inc. and its affiliates"
s.platforms = min_supported_versions
s.source = source
s.source_files = podspec_sources(source_files, ["*.h", "platform/ios/**/*.h"])
s.header_dir = "react/utils"
s.exclude_files = "tests"
if ENV['USE_FRAMEWORKS']
header_search_paths = header_search_paths + ["\"$(PODS_TARGET_SRCROOT)/platform/ios\""]
end
resolve_use_frameworks(s, header_mappings_dir: "../..", module_name: "React_utils")
s.pod_target_xcconfig = { "USE_HEADERMAP" => "NO",
"CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard(),
"HEADER_SEARCH_PATHS" => header_search_paths.join(' '),
"DEFINES_MODULE" => "YES" }
s.dependency "React-jsi", version
if use_hermes()
s.dependency "hermes-engine"
end
add_rn_third_party_dependencies(s)
add_rncore_dependency(s)
add_dependency(s, "React-debug")
end

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "RunLoopObserver.h"
#include <react/debug/react_native_assert.h>
namespace facebook::react {
RunLoopObserver::RunLoopObserver(Activity activities, WeakOwner owner) noexcept
: activities_(activities), owner_(std::move(owner)) {}
void RunLoopObserver::setDelegate(const Delegate* delegate) const noexcept {
// We need these constraints to ensure basic thread-safety.
react_native_assert(delegate && "A delegate must not be `nullptr`.");
react_native_assert(
!delegate_ && "`RunLoopObserver::setDelegate` must be called once.");
delegate_ = delegate;
}
void RunLoopObserver::enable() const noexcept {
if (enabled_) {
return;
}
enabled_ = true;
startObserving();
}
void RunLoopObserver::disable() const noexcept {
if (!enabled_) {
return;
}
enabled_ = false;
stopObserving();
}
void RunLoopObserver::activityDidChange(Activity activity) const noexcept {
if (!enabled_) {
return;
}
react_native_assert(
!owner_.expired() &&
"`owner_` is null. The caller must `lock` the owner and check it for being not null.");
delegate_->activityDidChange(delegate_, activity);
}
RunLoopObserver::WeakOwner RunLoopObserver::getOwner() const noexcept {
return owner_;
}
} // namespace facebook::react

View File

@@ -0,0 +1,122 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <atomic>
#include <functional>
#include <memory>
namespace facebook::react {
/*
* A cross-platform abstraction for observing a run loop life cycle.
*/
class RunLoopObserver {
public:
/*
* The concept of an owner.
* A run loop observer normally observes a run loop running on a different
* thread. That implies that this other thread (the run loop thread) will call
* methods of this class owned by some other thread. To make it safe, we need
* to ensure that at the moment of the calling the observer still exists. To
* do so, we use an owner object (a weak pointer) that must retain (possibly
* indirectly) the observer. The platform-specific code should convert the
* weak pointer (owner) to a strong one right before calling the observer,
* ensuring the safety of calling; right after the call, the strong pointer
* should be safely released.
*
* Note, in the case when the pointer to the actual owner will be available
* later, only after calling the constructor of the class, the caller can
* create a dummy pointer beforehand and then merge it (using
* `shared_ptr<X>(shared_ptr<Y> const &, X *)`) with the actual one (sharing
* the control block).
*/
using Owner = std::shared_ptr<const void>;
using WeakOwner = std::weak_ptr<const void>;
/*
* Run loop activity stages which run loop observers can be observe.
*/
enum Activity : int32_t {
None = 0,
BeforeWaiting = 1 << 0,
AfterWaiting = 1 << 1,
};
/*
* A delegate interface.
*/
class Delegate {
public:
/*
* Called on every run loop tick at moments corresponding to requested
* activities.
*
* A platform-specific implementation guarantees that the owner pointer
* is retained during this call.
* Will be called on the thread associated with the run loop.
*/
virtual void activityDidChange(const Delegate *delegate, Activity activity) const noexcept = 0;
virtual ~Delegate() noexcept = default;
};
using Factory = std::function<std::unique_ptr<RunLoopObserver>(Activity activities, const WeakOwner &owner)>;
/*
* Constructs a run loop observer.
*/
RunLoopObserver(Activity activities, WeakOwner owner) noexcept;
virtual ~RunLoopObserver() noexcept = default;
/*
* Sets the delegate.
* Must be called just once.
*/
void setDelegate(const Delegate *delegate) const noexcept;
/*
* Enables or disables run loop observing.
* It can be used to save CPU cycles during periods of time when observing
* is not required.
* A newly constructed run time observer is initially disabled.
*/
void enable() const noexcept;
void disable() const noexcept;
/*
* Returns true if called on a thread associated with the run loop.
* Must be implemented in subclasses.
*/
virtual bool isOnRunLoopThread() const noexcept = 0;
/*
* Returns an owner associated with the observer.
* It might be useful to ensure the safety of consequent asynchronous calls.
*/
WeakOwner getOwner() const noexcept;
protected:
/*
* Must be implemented in subclasses.
*/
virtual void startObserving() const noexcept = 0;
virtual void stopObserving() const noexcept = 0;
/*
* Called by subclasses to generate a call on a delegate.
*/
void activityDidChange(Activity activity) const noexcept;
const Activity activities_{};
const WeakOwner owner_;
mutable const Delegate *delegate_{nullptr};
mutable std::atomic<bool> enabled_{false};
};
} // namespace facebook::react

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <functional>
#include <memory>
#include <mutex>
#include <shared_mutex>
namespace facebook::react {
/*
* `SharedFunction` implements a pattern of a shared callable object that
* contains the same executable inside. It's similar to `std::function` with
* one important difference: when the object is copied, the stored function (and
* captured values) are shared between instances (not copied). `SharedFunction`
* can be stored inside `std::function` because it's callable. It useful in some
* scenarios, such as:
* - When captured by `std::function` arguments are not copyable;
* - When we need to replace the content of the callable later on the go.
*/
template <typename... ArgumentT>
class SharedFunction {
using T = void(ArgumentT...);
struct Pair {
Pair(std::function<T> &&function) : function(std::move(function)) {}
std::function<T> function;
std::shared_mutex mutex{};
};
public:
SharedFunction(std::function<T> &&function = nullptr) : pair_(std::make_shared<Pair>(std::move(function))) {}
SharedFunction(const SharedFunction &other) = default;
SharedFunction(SharedFunction &&other) noexcept = default;
SharedFunction &operator=(const SharedFunction &other) = default;
SharedFunction &operator=(SharedFunction &&other) noexcept = default;
void assign(std::function<T> function) const
{
std::unique_lock lock(pair_->mutex);
pair_->function = function;
}
void operator()(ArgumentT... args) const
{
std::shared_lock lock(pair_->mutex);
if (pair_->function) {
pair_->function(args...);
}
}
private:
std::shared_ptr<Pair> pair_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,106 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <concepts>
#include <list>
#include <mutex>
#include <unordered_map>
namespace facebook::react {
template <typename GeneratorT, typename ValueT>
concept CacheGeneratorFunction = std::invocable<GeneratorT> && std::same_as<std::invoke_result_t<GeneratorT>, ValueT>;
/*
* Simple thread-safe LRU cache.
*
* TODO T228961279: The maxSize template parameter should be removed, since it
* may be overriden by the constructor.
*/
template <typename KeyT, typename ValueT, int maxSize>
class SimpleThreadSafeCache {
public:
SimpleThreadSafeCache() : maxSize_(maxSize) {}
SimpleThreadSafeCache(unsigned long size) : maxSize_{size} {}
/*
* Returns a value from the map with a given key.
* If the value wasn't found in the cache, constructs the value using given
* generator function, stores it inside a cache and returns it.
* Can be called from any thread.
*/
ValueT get(const KeyT &key, CacheGeneratorFunction<ValueT> auto generator) const
{
return getMapIterator(key, std::move(generator))->second->second;
}
/*
* Returns pointers to both the key and value from the map with a given key.
* If the value wasn't found in the cache, constructs the value using given
* generator function, stores it inside a cache and returns it.
* Can be called from any thread.
*/
std::pair<const KeyT *, const ValueT *> getWithKey(const KeyT &key, CacheGeneratorFunction<ValueT> auto generator)
const
{
auto it = getMapIterator(key, std::move(generator));
return std::make_pair(&it->first, &it->second->second);
}
/*
* Returns a value from the map with a given key.
* If the value wasn't found in the cache, returns empty optional.
* Can be called from any thread.
*/
std::optional<ValueT> get(const KeyT &key) const
{
std::lock_guard<std::mutex> lock(mutex_);
if (auto it = map_.find(key); it != map_.end()) {
// Move accessed item to front of list
list_.splice(list_.begin(), list_, it->second);
return it->second->second;
}
return ValueT{};
}
private:
using EntryT = std::pair<KeyT, ValueT>;
using iterator = typename std::list<EntryT>::iterator;
auto getMapIterator(const KeyT &key, CacheGeneratorFunction<ValueT> auto generator) const
{
std::lock_guard<std::mutex> lock(mutex_);
if (auto it = map_.find(key); it != map_.end()) {
// Move accessed item to front of list
list_.splice(list_.begin(), list_, it->second);
return it;
}
auto value = generator();
// Add new value to front of list and map
list_.emplace_front(key, value);
auto [it, _] = map_.insert_or_assign(key, list_.begin());
if (list_.size() > maxSize_) {
// Evict least recently used item (back of list)
map_.erase(list_.back().first);
list_.pop_back();
}
return it;
}
size_t maxSize_;
mutable std::mutex mutex_;
mutable std::list<EntryT> list_;
mutable std::unordered_map<KeyT, iterator> map_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,105 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <chrono>
#include <type_traits>
namespace facebook::react {
/*
* Represents a monotonic clock suitable for measuring intervals.
*/
using TelemetryClock = std::chrono::steady_clock;
/*
* Represents a point in time satisfied the requirements of TelemetryClock.
*/
using TelemetryTimePoint = TelemetryClock::time_point;
/*
* Represents a time interval satisfied the requirements of TelemetryClock.
*/
using TelemetryDuration = std::chrono::nanoseconds;
/*
* Represents a time point which never happens.
*/
static const TelemetryTimePoint kTelemetryUndefinedTimePoint = TelemetryTimePoint::max();
/*
* Returns a time point representing the current point in time.
*/
static inline TelemetryTimePoint telemetryTimePointNow()
{
return TelemetryClock::now();
}
/*
* Returns a number of milliseconds that passed from some epoch starting time
* point to a given time point. The epoch starting time point is not specified
* but stays the same for an application run.
*/
static inline int64_t telemetryTimePointToMilliseconds(TelemetryTimePoint timePoint)
{
return std::chrono::duration_cast<std::chrono::milliseconds>(timePoint - TelemetryTimePoint{}).count();
}
/*
* Returns a number of seconds that passed from "Steady Clock" epoch starting
* time point to a given time point.
*/
static inline double telemetryTimePointToSteadyClockSeconds(TelemetryTimePoint timePoint)
{
static_assert(
std::is_same<decltype(timePoint), std::chrono::steady_clock::time_point>::value,
"`TelemetryClock` must be `std::chrono::steady_clock` to make the "
"following implementation work correctly.");
auto nanoseconds = std::chrono::duration_cast<std::chrono::nanoseconds>(timePoint.time_since_epoch()).count();
return (double)nanoseconds / 1.0e9;
}
/*
* Converts a time point on one clock to a time point on a different clock.
*/
template <
typename DestinationTimePointT,
typename SourceTimePointT,
typename DestnationClockT = typename DestinationTimePointT::clock,
typename SourceClockT = typename SourceTimePointT::clock>
DestinationTimePointT clockCast(SourceTimePointT timePoint)
{
auto sourseClockNow = SourceClockT::now();
auto destinationClockNow = DestnationClockT::now();
return std::chrono::time_point_cast<typename DestnationClockT::duration>(
timePoint - sourseClockNow + destinationClockNow);
}
/*
* Returns a number of seconds that passed from the UNIX Epoch starting time
* point to a given time point.
* Also known as POSIX time or UNIX Timestamp.
*/
static inline double telemetryTimePointToSecondsSinceEpoch(TelemetryTimePoint timePoint)
{
auto systemClockTimePoint = clockCast<std::chrono::system_clock::time_point, TelemetryTimePoint>(timePoint);
return (double)std::chrono::duration_cast<std::chrono::microseconds>(systemClockTimePoint.time_since_epoch())
.count() /
1000000.0;
}
/*
* Returns a number of milliseconds that represents the given duration object.
*/
static inline int64_t telemetryDurationToMilliseconds(TelemetryDuration duration)
{
return std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
}
} // namespace facebook::react

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <algorithm>
#include <array>
#include <string_view>
namespace facebook::react {
/**
* Helper to allow passing string literals as template parameters.
* https://ctrpeach.io/posts/cpp20-string-literal-template-parameters/
*/
template <size_t N>
struct TemplateStringLiteral {
/* implicit */ constexpr TemplateStringLiteral(const char (&str)[N])
{ // NOLINT(modernize-avoid-c-arrays)
std::copy_n(str, N, value.data());
}
constexpr operator std::string_view() const
{
return {value.begin(), value.end() - 1};
}
// Not private, since structural types required for template parameters cannot
// have non-punlic data members.
std::array<char, N> value{};
};
} // namespace facebook::react

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "Uuid.h"
#include <array>
#include <iomanip>
#include <random>
#include <sstream>
namespace {
std::mt19937& getThreadLocalEngine() {
static thread_local std::random_device device;
static thread_local std::mt19937 engine(device());
return engine;
}
uint64_t randomUInt64(uint64_t max = std::numeric_limits<uint64_t>::max()) {
std::uniform_int_distribution<uint64_t> distribution(0, max);
return distribution(getThreadLocalEngine());
}
} // namespace
namespace facebook::react {
std::string generateRandomUuidString() {
static constexpr uint8_t kUuidLength = 16;
// see: https://tools.ietf.org/html/rfc4122#section-4.1.2 for reference
static constexpr uint8_t kTimeLowSegBufEnd = sizeof(uint32_t);
static constexpr uint8_t kTimeMidSegBufEnd =
kTimeLowSegBufEnd + sizeof(uint16_t);
static constexpr uint8_t kTimeHiAndVersionSegBufEnd =
kTimeMidSegBufEnd + sizeof(uint16_t);
static constexpr uint8_t kClockHiLoSegBufEnd =
kTimeHiAndVersionSegBufEnd + sizeof(uint16_t);
static constexpr uint8_t kNodeSegHiBufEnd =
kClockHiLoSegBufEnd + sizeof(uint16_t);
std::array<uint8_t, kUuidLength> uuidBytes;
*(uint64_t*)uuidBytes.data() = randomUInt64();
*(uint64_t*)(uuidBytes.data() + sizeof(uint64_t)) = randomUInt64();
// Conforming to RFC 4122: https://www.cryptosys.net/pki/uuid-rfc4122.html
uuidBytes[7] &= 0x0f;
uuidBytes[7] |= 0x40;
uuidBytes[9] &= 0x3f;
uuidBytes[9] |= 0x80;
std::stringstream ss;
ss << std::setfill('0') << std::setw(8) << std::right << std::hex
<< *(uint32_t*)uuidBytes.data() << "-" << std::setw(4)
<< *(uint16_t*)(uuidBytes.data() + kTimeLowSegBufEnd) << "-"
<< *(uint16_t*)(uuidBytes.data() + kTimeMidSegBufEnd) << "-"
<< *(uint16_t*)(uuidBytes.data() + kTimeHiAndVersionSegBufEnd) << "-"
<< std::setw(8) << *(uint32_t*)(uuidBytes.data() + kNodeSegHiBufEnd)
<< std::setw(4) << *(uint16_t*)(uuidBytes.data() + kClockHiLoSegBufEnd);
return ss.str();
}
} // namespace facebook::react

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <string>
namespace facebook::react {
/**
* Generates a random UUID version 4 string in the format
* xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx (lowercase).
*/
std::string generateRandomUuidString();
} // namespace facebook::react

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <cstdint>
#include <functional>
#include <string_view>
#include <react/utils/toLower.h>
namespace facebook::react {
/**
* FNV-1a hash function implementation.
* Implemented as described in http://www.isthe.com/chongo/tech/comp/fnv/.
*
* Please use std::hash if possible. `fnv1a` should only be used in cases
* when std::hash does not provide the needed functionality. For example,
* constexpr.
*/
template <typename CharTransformT = std::identity>
constexpr uint32_t fnv1a(std::string_view string) noexcept
{
constexpr uint32_t offset_basis = 2166136261;
uint32_t hash = offset_basis;
for (const auto &c : string) {
hash ^= static_cast<int8_t>(CharTransformT{}(c));
// Using shifts and adds instead of multiplication with a prime number.
// This is faster when compiled with optimizations.
hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
}
return hash;
}
constexpr uint32_t fnv1aLowercase(std::string_view string)
{
struct LowerCaseTransform {
constexpr char operator()(char c) const
{
return toLower(c);
}
};
return fnv1a<LowerCaseTransform>(string);
}
} // namespace facebook::react

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <functional>
#include <type_traits>
namespace facebook::react {
template <typename T>
concept Hashable = !std::is_same_v<T, const char *> && (requires(T a) {
{ std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
});
template <Hashable T, Hashable... Rest>
void hash_combine(std::size_t &seed, const T &v, const Rest &...rest)
{
seed ^= std::hash<T>{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
(hash_combine(seed, rest), ...);
}
template <Hashable T, Hashable... Args>
std::size_t hash_combine(const T &v, const Args &...args)
{
std::size_t seed = 0;
hash_combine<T, Args...>(seed, v, args...);
return seed;
}
} // namespace facebook::react

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/utils/toLower.h>
#include <string_view>
namespace facebook::react {
/**
* constexpr check for case insensitive equality of two strings.
*/
constexpr bool iequals(std::string_view a, std::string_view b)
{
if (a.size() != b.size()) {
return false;
}
for (size_t i = 0; i < a.size(); i++) {
if (toLower(a[i]) != toLower(b[i])) {
return false;
}
}
return true;
}
} // namespace facebook::react

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "jsi-utils.h"
namespace facebook::react {
void defineReadOnlyGlobal(
jsi::Runtime& runtime,
const std::string& propName,
jsi::Value&& value) {
auto global = runtime.global();
if (global.hasProperty(runtime, propName.c_str())) {
throw jsi::JSError(
runtime,
"Tried to redefine read-only global \"" + propName +
"\", but read-only globals can only be defined once.");
}
jsi::Object jsObject =
global.getProperty(runtime, "Object").asObject(runtime);
jsi::Function defineProperty = jsObject.getProperty(runtime, "defineProperty")
.asObject(runtime)
.asFunction(runtime);
jsi::Object descriptor = jsi::Object(runtime);
descriptor.setProperty(runtime, "value", std::move(value));
defineProperty.callWithThis(
runtime,
jsObject,
global,
jsi::String::createFromUtf8(runtime, propName),
descriptor);
}
} // namespace facebook::react

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <jsi/jsi.h>
#include <string>
namespace facebook::react {
/**
* Defines a property on the global object that is neither enumerable, nor
* configurable, nor writable. This ensures that the private globals exposed by
* ReactInstance cannot overwritten by third-party JavaScript code. It also
* ensures that third-party JavaScript code unaware of these globals isn't able
* to accidentally access them. In JavaScript, equivalent to:
*
* Object.defineProperty(global, propName, {
* value: value
* })
*/
void defineReadOnlyGlobal(jsi::Runtime &runtime, const std::string &propName, jsi::Value &&value);
} // namespace facebook::react

View File

@@ -0,0 +1,95 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <pthread.h>
#include <sched.h>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <thread>
#include <utility>
namespace facebook::react::LowPriorityExecutor {
struct LowPriorityExecutorThread {
LowPriorityExecutorThread() : thread_{std::thread([this] { run(); })} {
pthread_t hThread = thread_.native_handle();
struct sched_param param{};
param.sched_priority = 19; // Higher value means lower priority
pthread_setschedparam(hThread, SCHED_OTHER, &param);
}
// Deleted constructors
LowPriorityExecutorThread(const LowPriorityExecutorThread& other) = delete;
LowPriorityExecutorThread(LowPriorityExecutorThread&& other) = delete;
LowPriorityExecutorThread& operator=(const LowPriorityExecutorThread& other) =
delete;
LowPriorityExecutorThread& operator=(LowPriorityExecutorThread&& other) =
delete;
~LowPriorityExecutorThread() {
// Stop the thread
{
std::lock_guard<std::mutex> lock(mutex_);
running_ = false;
}
// Unblock the thread to check the running_ flag and terminate.
cv_.notify_one();
// Wait for thread completion to avoid use-after-free on background thread.
thread_.join();
}
void post(std::function<void()>&& workItem) {
// Move the object to the queue.
{
std::lock_guard<std::mutex> lock(mutex_);
queue_.emplace(std::move(workItem));
}
// Notify the background thread.
cv_.notify_one();
}
private:
void run() {
pthread_setname_np(pthread_self(), "LowPriorityExecutorThread");
while (true) {
std::unique_lock<std::mutex> lock(mutex_);
// Wait until an object is in the queue or the thread is stopped.
cv_.wait(lock, [this] { return !queue_.empty() || !running_; });
// Empty the queue.
while (!queue_.empty()) {
queue_.front()();
queue_.pop();
}
// Check if the thread is stopping.
if (!running_) {
break;
}
}
}
std::string threadName_;
std::thread thread_;
std::queue<std::function<void()>> queue_;
std::mutex mutex_;
std::condition_variable cv_;
bool running_{true};
};
void execute(std::function<void()>&& workItem) {
static LowPriorityExecutorThread thread{};
thread.post(std::move(workItem));
}
} // namespace facebook::react::LowPriorityExecutor

View File

@@ -0,0 +1,14 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
namespace facebook::react::LowPriorityExecutor {
void execute(std::function<void()> &&workItem);
} // namespace facebook::react::LowPriorityExecutor

View File

@@ -0,0 +1,19 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <functional>
namespace facebook::react::LowPriorityExecutor {
inline void execute(std::function<void()> &&workItem)
{
workItem();
}
} // namespace facebook::react::LowPriorityExecutor

View File

@@ -0,0 +1,18 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
#include <folly/dynamic.h>
namespace facebook::react {
folly::dynamic convertIdToFollyDynamic(id json);
id convertFollyDynamicToId(const folly::dynamic &dyn);
NSArray<NSString *> *extractKeysFromFollyDynamic(const folly::dynamic &dyn);
} // namespace facebook::react

View File

@@ -0,0 +1,132 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "FollyConvert.h"
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
namespace facebook::react {
id convertFollyDynamicToId(const folly::dynamic &dyn)
{
// I could imagine an implementation which avoids copies by wrapping the
// dynamic in a derived class of NSDictionary. We can do that if profiling
// implies it will help.
switch (dyn.type()) {
case folly::dynamic::NULLT:
return (id)kCFNull;
case folly::dynamic::BOOL:
return dyn.getBool() ? @YES : @NO;
case folly::dynamic::INT64:
return @(dyn.getInt());
case folly::dynamic::DOUBLE:
return @(dyn.getDouble());
case folly::dynamic::STRING:
return [[NSString alloc] initWithBytes:dyn.c_str() length:dyn.size() encoding:NSUTF8StringEncoding];
case folly::dynamic::ARRAY: {
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:dyn.size()];
for (const auto &elem : dyn) {
id value = convertFollyDynamicToId(elem);
if (value != nullptr) {
[array addObject:value];
}
}
return array;
}
case folly::dynamic::OBJECT: {
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:dyn.size()];
for (const auto &elem : dyn.items()) {
id key = convertFollyDynamicToId(elem.first);
id value = convertFollyDynamicToId(elem.second);
if ((key != nullptr) && (value != nullptr)) {
dict[key] = value;
}
}
return dict;
}
}
}
folly::dynamic convertIdToFollyDynamic(id json)
{
if (json == nil || json == (id)kCFNull) {
return nullptr;
} else if ([json isKindOfClass:[NSNumber class]]) {
const char *objCType = [json objCType];
switch (objCType[0]) {
// This is a c++ bool or C99 _Bool. On some platforms, BOOL is a bool.
case _C_BOOL:
return (bool)[json boolValue];
case _C_CHR:
// On some platforms, objc BOOL is a signed char, but it
// might also be a small number. Use the same hack JSC uses
// to distinguish them:
// https://phabricator.intern.facebook.com/diffusion/FBS/browse/master/fbobjc/xplat/third-party/jsc/safari-600-1-4-17/JavaScriptCore/API/JSValue.mm;b8ee03916489f8b12143cd5c0bca546da5014fc9$901
if ([json isKindOfClass:[@YES class]]) {
return (bool)[json boolValue];
} else {
return [json longLongValue];
}
case _C_UCHR:
case _C_SHT:
case _C_USHT:
case _C_INT:
case _C_UINT:
case _C_LNG:
case _C_ULNG:
case _C_LNG_LNG:
case _C_ULNG_LNG:
return [json longLongValue];
case _C_FLT:
case _C_DBL:
return [json doubleValue];
// default:
// fall through
}
} else if ([json isKindOfClass:[NSString class]]) {
NSData *data = [json dataUsingEncoding:NSUTF8StringEncoding];
return std::string(reinterpret_cast<const char *>(data.bytes), data.length);
} else if ([json isKindOfClass:[NSArray class]]) {
folly::dynamic array = folly::dynamic::array;
for (id element in json) {
array.push_back(convertIdToFollyDynamic(element));
}
return array;
} else if ([json isKindOfClass:[NSDictionary class]]) {
__block folly::dynamic object = folly::dynamic::object();
[json enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, __unused BOOL *stop) {
object.insert(convertIdToFollyDynamic(key), convertIdToFollyDynamic(value));
}];
return object;
}
return nil;
}
NSArray<NSString *> *extractKeysFromFollyDynamic(const folly::dynamic &dyn)
{
if (dyn.type() == folly::dynamic::OBJECT) {
NSMutableArray<NSString *> *result = [NSMutableArray arrayWithCapacity:dyn.size()];
for (const auto &elem : dyn.items()) {
NSString *key = [[NSString alloc] initWithBytes:elem.first.c_str()
length:elem.first.size()
encoding:NSUTF8StringEncoding];
[result addObject:key];
}
return result;
}
return @[];
}
} // namespace facebook::react

View File

@@ -0,0 +1,16 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <functional>
namespace facebook::react::LowPriorityExecutor {
void execute(std::function<void()> &&workItem);
} // namespace facebook::react::LowPriorityExecutor

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "LowPriorityExecutor.h"
#import <Foundation/Foundation.h>
namespace facebook::react::LowPriorityExecutor {
void execute(std::function<void()> &&workItem)
{
std::function<void()> localWorkItem = std::move(workItem);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
localWorkItem();
});
}
} // namespace facebook::react::LowPriorityExecutor

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <gtest/gtest.h>
#include <react/utils/SimpleThreadSafeCache.h>
namespace facebook::react {
TEST(EvictingCacheMapTest, BasicInsertAndGet) {
SimpleThreadSafeCache<int, std::string, 3> cache;
EXPECT_EQ(cache.get(1), ""); // Default constructed value is returned
EXPECT_EQ(cache.get(2), ""); // Default constructed value is returned
EXPECT_EQ(cache.get(3), ""); // Default constructed value is returned
cache.get(1, []() { return std::string("one"); });
cache.get(2, []() { return std::string("two"); });
cache.get(3, []() { return std::string("three"); });
EXPECT_EQ(cache.get(1), "one");
EXPECT_EQ(cache.get(2), "two");
EXPECT_EQ(cache.get(3), "three");
}
TEST(EvictingCacheMapTest, Eviction) {
SimpleThreadSafeCache<int, std::string, 2> cache;
cache.get(1, []() { return std::string("one"); });
cache.get(2, []() { return std::string("two"); });
cache.get(3, []() { return std::string("three"); }); // should evict key 1
EXPECT_EQ(
cache.get(1),
""); // key 1 should be evicted and default constructed value is returned
EXPECT_EQ(cache.get(2), "two");
EXPECT_EQ(cache.get(3), "three");
}
} // namespace facebook::react

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <react/utils/Uuid.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using namespace ::testing;
namespace facebook::react {
TEST(UuidTest, TestGenerateRandomUuidString) {
static constexpr auto kUuidV4Regex =
"[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}";
std::string uuid1 = generateRandomUuidString();
std::string uuid2 = generateRandomUuidString();
EXPECT_THAT(uuid1, MatchesRegex(kUuidV4Regex));
EXPECT_THAT(uuid2, MatchesRegex(kUuidV4Regex));
EXPECT_NE(uuid1, uuid2);
}
} // namespace facebook::react

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <gtest/gtest.h>
#include <react/utils/fnv1a.h>
namespace facebook::react {
TEST(fnv1aTests, testBasicHashing) {
EXPECT_EQ(fnv1a("react"), fnv1a("react"));
EXPECT_NE(fnv1a("react"), fnv1a("tceat"));
auto string1 = "case 1";
auto string2 = "different string";
EXPECT_EQ(fnv1a(string1), fnv1a(string1));
EXPECT_NE(fnv1a(string1), fnv1a(string2));
}
} // namespace facebook::react

View File

@@ -0,0 +1,82 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <gtest/gtest.h>
#include <react/utils/hash_combine.h>
struct Person {
std::string firstName;
std::string lastName;
};
namespace std {
template <>
struct hash<Person> {
size_t operator()(const Person& person) const {
return facebook::react::hash_combine(person.firstName, person.lastName);
}
};
} // namespace std
namespace facebook::react {
TEST(hash_combineTests, testIntegerTemplating) {
std::size_t seed = 0;
hash_combine(seed, 1);
auto hashedValue = hash_combine(1);
EXPECT_EQ(hashedValue, seed);
EXPECT_NE(hash_combine(1), hash_combine(2));
}
TEST(hash_combineTests, testIntegerCombinationsHashing) {
std::size_t seed = 0;
hash_combine(seed, 1, 2);
auto hashedValue = hash_combine(1, 2);
EXPECT_EQ(hashedValue, seed);
EXPECT_NE(hash_combine(1, 2), hash_combine(2, 1));
}
TEST(hash_combineTests, testContiniousIntegerHashing) {
std::size_t seed = 0;
for (int i = 1; i <= 200; ++i) {
auto previousSeed = seed;
hash_combine(seed, i);
EXPECT_NE(seed, previousSeed);
}
}
TEST(hash_combineTests, testStrings) {
std::size_t seed = 0;
hash_combine<std::string>(seed, "react");
auto hashedValue = hash_combine<std::string>("react");
EXPECT_EQ(hashedValue, seed);
EXPECT_NE(
hash_combine<std::string>("react"),
hash_combine<std::string>("react native"));
}
TEST(hash_combineTests, testCustomTypes) {
auto person1 = Person{.firstName = "John", .lastName = "Doe"};
auto person2 = Person{.firstName = "Jane", .lastName = "Doe"};
std::size_t seed = 0;
hash_combine(seed, person1);
auto hashedValue = hash_combine(person1);
EXPECT_EQ(hashedValue, seed);
EXPECT_NE(hash_combine(person1), hash_combine(person2));
}
} // namespace facebook::react

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
namespace facebook::react {
/**
* constexpr version of tolower
*/
constexpr char toLower(char c)
{
if (c >= 'A' && c <= 'Z') {
return static_cast<char>(c + 32);
}
return c;
}
} // namespace facebook::react

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <type_traits>
namespace facebook::react {
/**
* Polyfill of C++ 23 to_underlying()
* https://en.cppreference.com/w/cpp/utility/to_underlying
*/
constexpr auto to_underlying(auto e) noexcept
{
return static_cast<std::underlying_type_t<decltype(e)>>(e);
}
} // namespace facebook::react