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,139 @@
/*
* 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 <random>
namespace facebook::react {
/*
* The source of pseudo-random numbers and some problem-oriented tools built on
* top of that. We need this class to maintain a reproducible stream of random
* numbers and abstract away complex math of and C++ STL API behind that.
*/
class Entropy final {
public:
using Generator = std::mt19937;
/*
* Creates an instance seeded with a real, not pseudo-random, number.
*/
Entropy()
{
std::random_device device;
seed_ = device();
generator_ = std::mt19937(seed_);
}
/*
* Creates an instance seeded with a given number.
*/
Entropy(uint_fast32_t seed)
{
seed_ = seed;
generator_ = std::mt19937(seed_);
}
uint_fast32_t getSeed() const
{
return seed_;
}
/*
* Family of methods that return uniformly distributed instances of a type
* within a specified range.
*/
template <typename T>
bool random() const
{
T result;
generateRandomValue(generator_, result);
return result;
}
template <typename T, typename Arg1>
T random(Arg1 arg1) const
{
T result;
generateRandomValue(generator_, result, arg1);
return result;
}
template <typename T, typename Arg1, typename Arg2>
T random(Arg1 arg1, Arg2 arg2) const
{
T result;
generateRandomValue(generator_, result, arg1, arg2);
return result;
}
void generateRandomValue(Generator &generator, bool &result, double ratio = 0.5) const
{
result = generator() % 10000 < 10000 * ratio;
}
void generateRandomValue(Generator &generator, int &result) const
{
result = generator();
}
void generateRandomValue(Generator &generator, int &result, int min, int max) const
{
std::uniform_int_distribution<int> distribution(min, max);
result = distribution(generator);
}
/*
* Shuffles `std::vector` in place.
*/
template <typename T>
void shuffle(T array) const
{
std::shuffle(array.begin(), array.end(), generator_);
}
/*
* Distribute items from a given array into buckets using a normal
* distribution and given `deviation`.
*/
template <typename T>
std::vector<std::vector<T>> distribute(std::vector<T> items, double deviation) const
{
std::normal_distribution<> distribution{0, deviation};
auto deviationLimit = int(deviation * 10);
auto spreadResult = std::vector<std::vector<T>>(deviationLimit * 2);
std::fill(spreadResult.begin(), spreadResult.end(), std::vector<T>{});
for (const auto &item : items) {
auto position = int(distribution(generator_) + deviationLimit);
position = std::max(0, std::min(position, deviationLimit * 2));
if (position < spreadResult.size()) {
spreadResult[position].push_back(item);
}
}
auto result = std::vector<std::vector<T>>{};
for (const auto &chunk : spreadResult) {
if (chunk.size() == 0) {
continue;
}
result.push_back(chunk);
}
return result;
}
private:
mutable std::mt19937 generator_;
uint_fast32_t seed_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,29 @@
/*
* 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>
class MockClock {
public:
using time_point = std::chrono::time_point<std::chrono::steady_clock, std::chrono::nanoseconds>;
static time_point now() noexcept
{
return time_;
}
template <typename TDuration>
static void advance_by(const TDuration duration)
{
time_ += duration;
}
private:
static time_point time_;
};

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.
*/
#pragma once
#include <gmock/gmock.h>
#include <react/renderer/scheduler/SurfaceHandler.h>
namespace facebook::react {
class MockSurfaceHandler : public SurfaceHandler {
public:
MockSurfaceHandler() : SurfaceHandler("moduleName", 0) {};
MOCK_METHOD(void, setDisplayMode, (DisplayMode), (const, noexcept));
MOCK_METHOD(SurfaceId, getSurfaceId, (), (const, noexcept));
};
} // namespace facebook::react

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.
*/
#import <Foundation/Foundation.h>
#import <React/RCTDefines.h>
RCT_EXTERN_C_BEGIN
int RCTGetRetainCount(id _Nullable object);
void RCTAutoReleasePoolPush(void);
void RCTAutoReleasePoolPop(void);
RCT_EXTERN_C_END

View File

@@ -0,0 +1,45 @@
/*
* 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 "RCTMemoryUtils.h"
int RCTGetRetainCount(id _Nullable object)
{
return object != nil ? CFGetRetainCount((__bridge CFTypeRef)object) - 1 : 0;
}
OBJC_EXPORT
void *objc_autoreleasePoolPush(void) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
OBJC_EXPORT
void objc_autoreleasePoolPop(void *context) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
static NSString *const kAutoreleasePoolContextStackKey = @"autorelease_pool_context_stack";
void RCTAutoReleasePoolPush(void)
{
assert([NSThread isMainThread]);
NSMutableDictionary *dictionary = [[NSThread currentThread] threadDictionary];
void *context = objc_autoreleasePoolPush();
NSMutableArray<NSValue *> *contextStack = dictionary[kAutoreleasePoolContextStackKey];
if (!contextStack) {
contextStack = [NSMutableArray array];
dictionary[kAutoreleasePoolContextStackKey] = contextStack;
}
[contextStack addObject:[NSValue valueWithPointer:context]];
}
void RCTAutoReleasePoolPop(void)
{
assert([NSThread isMainThread]);
NSMutableDictionary *dictionary = [[NSThread currentThread] threadDictionary];
NSMutableArray<NSValue *> *contextStack = dictionary[kAutoreleasePoolContextStackKey];
assert(contextStack.count > 0);
NSValue *lastContext = contextStack.lastObject;
[contextStack removeLastObject];
objc_autoreleasePoolPop(lastContext.pointerValue);
}

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.
*/
#import <Foundation/Foundation.h>
#import <React/RCTDefines.h>
RCT_EXTERN_C_BEGIN
void RCTSwizzleInstanceSelector(Class targetClass, Class swizzleClass, SEL selector);
RCT_EXTERN_C_END

View File

@@ -0,0 +1,17 @@
/*
* 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 "RCTSwizzleHelpers.h"
#import <objc/runtime.h>
void RCTSwizzleInstanceSelector(Class targetClass, Class swizzleClass, SEL selector)
{
Method originalMethod = class_getInstanceMethod(targetClass, selector);
Method swizzleMethod = class_getInstanceMethod(swizzleClass, selector);
method_exchangeImplementations(originalMethod, swizzleMethod);
}

View File

@@ -0,0 +1,21 @@
/*
* 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>
@interface ShimRCTInstance : NSObject
@property int initCount;
@property int invalidateCount;
@property NSDictionary *launchOptions;
@property NSString *jsModuleName;
@property NSString *method;
@property NSArray *args;
- (void)reset;
@end

View File

@@ -0,0 +1,78 @@
/*
* 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 "ShimRCTInstance.h"
#import <ReactCommon/RCTInstance.h>
#import "RCTSwizzleHelpers.h"
static __weak ShimRCTInstance *weakShim = nil;
@implementation ShimRCTInstance
- (instancetype)init
{
if (self = [super init]) {
_initCount = 0;
RCTSwizzleInstanceSelector(
[RCTInstance class],
[ShimRCTInstance class],
@selector
(initWithDelegate:
jsRuntimeFactory:bundleManager:turboModuleManagerDelegate:moduleRegistry:parentInspectorTarget:launchOptions
:devMenuConfiguration:));
RCTSwizzleInstanceSelector([RCTInstance class], [ShimRCTInstance class], @selector(invalidate));
RCTSwizzleInstanceSelector(
[RCTInstance class], [ShimRCTInstance class], @selector(callFunctionOnJSModule:method:args:));
weakShim = self;
}
return self;
}
- (void)reset
{
RCTSwizzleInstanceSelector(
[RCTInstance class],
[ShimRCTInstance class],
@selector
(initWithDelegate:
jsRuntimeFactory:bundleManager:turboModuleManagerDelegate:moduleRegistry:parentInspectorTarget:launchOptions
:devMenuConfiguration:));
RCTSwizzleInstanceSelector([RCTInstance class], [ShimRCTInstance class], @selector(invalidate));
RCTSwizzleInstanceSelector(
[RCTInstance class], [ShimRCTInstance class], @selector(callFunctionOnJSModule:method:args:));
_initCount = 0;
_invalidateCount = 0;
}
- (instancetype)initWithDelegate:(id<RCTInstanceDelegate>)delegate
jsRuntimeFactory:(std::shared_ptr<facebook::react::JSRuntimeFactory>)jsRuntimeFactory
bundleManager:(RCTBundleManager *)bundleManager
turboModuleManagerDelegate:(id<RCTTurboModuleManagerDelegate>)tmmDelegate
moduleRegistry:(RCTModuleRegistry *)moduleRegistry
parentInspectorTarget:(facebook::react::jsinspector_modern::HostTarget *)parentInspectorTarget
launchOptions:(NSDictionary *)launchOptions
devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration
{
weakShim.initCount++;
return self;
}
- (void)invalidate
{
weakShim.invalidateCount++;
}
- (void)callFunctionOnJSModule:(NSString *)moduleName method:(NSString *)method args:(NSArray *)args
{
weakShim.jsModuleName = moduleName;
weakShim.method = method;
weakShim.args = [args copy];
}
@end

View File

@@ -0,0 +1,296 @@
/*
* 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <algorithm>
#include <memory>
#include <react/renderer/core/PropsParserContext.h>
#include <react/renderer/mounting/Differentiator.h>
#include <react/renderer/mounting/stubs/stubs.h>
#include "Entropy.h"
namespace facebook::react {
static Tag generateReactTag()
{
static Tag tag = 1000;
return tag++;
}
class ShadowTreeEdge final {
public:
std::shared_ptr<const ShadowNode> shadowNode{nullptr};
std::shared_ptr<const ShadowNode> parentShadowNode{nullptr};
int index{0};
};
static bool traverseShadowTree(
const std::shared_ptr<const ShadowNode> &parentShadowNode,
const std::function<void(const ShadowTreeEdge &edge, bool &stop)> &callback)
{
auto index = int{0};
for (const auto &childNode : parentShadowNode->getChildren()) {
auto stop = bool{false};
callback(ShadowTreeEdge{childNode, parentShadowNode, index}, stop);
if (stop) {
return true;
}
if (traverseShadowTree(childNode, callback)) {
return true;
}
index++;
}
return false;
}
static int countShadowNodes(const std::shared_ptr<const ShadowNode> &rootShadowNode)
{
auto counter = int{0};
traverseShadowTree(rootShadowNode, [&](const ShadowTreeEdge &edge, bool &stop) { counter++; });
return counter;
}
static ShadowTreeEdge findShadowNodeWithIndex(const std::shared_ptr<const ShadowNode> &rootNode, int index)
{
auto counter = int{0};
auto result = ShadowTreeEdge{};
traverseShadowTree(rootNode, [&](const ShadowTreeEdge &edge, bool &stop) {
if (index == counter) {
result = edge;
}
counter++;
});
return result;
}
static ShadowTreeEdge findRandomShadowNode(
const Entropy &entropy,
const std::shared_ptr<const ShadowNode> &rootShadowNode)
{
auto count = countShadowNodes(rootShadowNode);
return findShadowNodeWithIndex(rootShadowNode, entropy.random<int>(1 /* Excluding a root node */, count - 1));
}
static ShadowNode::ListOfShared cloneSharedShadowNodeList(const ShadowNode::ListOfShared &list)
{
auto result = ShadowNode::ListOfShared{};
result.reserve(list.size());
for (const auto &shadowNode : list) {
result.push_back(shadowNode->clone({}));
}
return result;
}
static inline std::shared_ptr<ShadowNode> messWithChildren(const Entropy &entropy, const ShadowNode &shadowNode)
{
auto children = shadowNode.getChildren();
children = cloneSharedShadowNodeList(children);
entropy.shuffle(children);
return shadowNode.clone(
{ShadowNodeFragment::propsPlaceholder(), std::make_shared<const ShadowNode::ListOfShared>(children)});
}
static inline std::shared_ptr<ShadowNode> messWithLayoutableOnlyFlag(
const Entropy &entropy,
const ShadowNode &shadowNode)
{
auto oldProps = shadowNode.getProps();
ContextContainer contextContainer{};
PropsParserContext parserContext{-1, contextContainer};
auto newProps =
shadowNode.getComponentDescriptor().cloneProps(parserContext, oldProps, RawProps(folly::dynamic::object()));
auto &viewProps = const_cast<ViewProps &>(static_cast<const ViewProps &>(*newProps));
if (entropy.random<bool>(0.1)) {
viewProps.nativeId = entropy.random<bool>() ? "42" : "";
}
if (entropy.random<bool>(0.1)) {
viewProps.backgroundColor = entropy.random<bool>() ? SharedColor() : whiteColor();
}
if (entropy.random<bool>(0.1)) {
viewProps.shadowColor = entropy.random<bool>() ? SharedColor() : blackColor();
}
if (entropy.random<bool>(0.1)) {
viewProps.accessible = entropy.random<bool>();
}
if (entropy.random<bool>(0.1)) {
viewProps.zIndex = entropy.random<int>();
}
if (entropy.random<bool>(0.1)) {
viewProps.pointerEvents = entropy.random<bool>() ? PointerEventsMode::Auto : PointerEventsMode::None;
}
if (entropy.random<bool>(0.1)) {
viewProps.transform = entropy.random<bool>() ? Transform::Identity() : Transform::Perspective(42);
}
#ifdef ANDROID
if (entropy.random<bool>(0.1)) {
viewProps.elevation = entropy.random<bool>() ? 1 : 0;
}
#endif
return shadowNode.clone({newProps});
}
// Similar to `messWithLayoutableOnlyFlag` but has a 50/50 chance of flattening
// (or unflattening) a node's children.
static inline std::shared_ptr<ShadowNode> messWithNodeFlattenednessFlags(
const Entropy &entropy,
const ShadowNode &shadowNode)
{
ContextContainer contextContainer{};
PropsParserContext parserContext{-1, contextContainer};
auto oldProps = shadowNode.getProps();
auto newProps =
shadowNode.getComponentDescriptor().cloneProps(parserContext, oldProps, RawProps(folly::dynamic::object()));
auto &viewProps = const_cast<ViewProps &>(static_cast<const ViewProps &>(*newProps));
if (entropy.random<bool>(0.5)) {
viewProps.nativeId = "";
viewProps.collapsable = true;
viewProps.backgroundColor = SharedColor();
viewProps.shadowColor = SharedColor();
viewProps.accessible = false;
viewProps.zIndex = {};
viewProps.pointerEvents = PointerEventsMode::Auto;
viewProps.transform = Transform::Identity();
#ifdef ANDROID
viewProps.elevation = 0;
#endif
} else {
viewProps.nativeId = "42";
viewProps.backgroundColor = whiteColor();
viewProps.shadowColor = blackColor();
viewProps.accessible = true;
viewProps.zIndex = {entropy.random<int>()};
viewProps.pointerEvents = PointerEventsMode::None;
viewProps.transform = Transform::Perspective(entropy.random<int>());
#ifdef ANDROID
viewProps.elevation = entropy.random<int>();
#endif
}
return shadowNode.clone({newProps});
}
static inline std::shared_ptr<ShadowNode> messWithYogaStyles(const Entropy &entropy, const ShadowNode &shadowNode)
{
folly::dynamic dynamic = folly::dynamic::object();
if (entropy.random<bool>()) {
dynamic["flexDirection"] = entropy.random<bool>() ? "row" : "column";
}
std::vector<std::string> properties = {
"flex", "flexGrow", "flexShrink", "flexBasis", "left", "top", "marginLeft",
"marginTop", "marginRight", "marginBottom", "paddingLeft", "paddingTop", "paddingRight", "paddingBottom",
"width", "height", "maxWidth", "maxHeight", "minWidth", "minHeight",
};
// It is not safe to add new Yoga properties to this list. Unit tests
// validate specific seeds, and what they test may change and cause unrelated
// failures if the size of properties also changes.
EXPECT_EQ(properties.size(), 20);
for (const auto &property : properties) {
if (entropy.random<bool>(0.1)) {
dynamic[property] = entropy.random<int>(0, 1024);
}
}
ContextContainer contextContainer;
PropsParserContext parserContext{-1, contextContainer};
auto oldProps = shadowNode.getProps();
auto newProps = shadowNode.getComponentDescriptor().cloneProps(parserContext, oldProps, RawProps(dynamic));
return shadowNode.clone({newProps});
}
using ShadowNodeAlteration =
std::function<std::shared_ptr<ShadowNode>(const Entropy &entropy, const ShadowNode &shadowNode)>;
static inline void
alterShadowTree(const Entropy &entropy, RootShadowNode::Shared &rootShadowNode, ShadowNodeAlteration alteration)
{
auto edge = findRandomShadowNode(entropy, rootShadowNode);
rootShadowNode = std::static_pointer_cast<RootShadowNode>(rootShadowNode->cloneTree(
edge.shadowNode->getFamily(),
[&](const ShadowNode &oldShadowNode) { return alteration(entropy, oldShadowNode); }));
}
static inline void alterShadowTree(
const Entropy &entropy,
RootShadowNode::Shared &rootShadowNode,
std::vector<ShadowNodeAlteration> alterations)
{
auto i = entropy.random<int>(0, alterations.size() - 1);
alterShadowTree(entropy, rootShadowNode, alterations[i]);
}
static SharedViewProps generateDefaultProps(const ComponentDescriptor &componentDescriptor)
{
ContextContainer contextContainer{};
PropsParserContext parserContext{-1, contextContainer};
return std::static_pointer_cast<const ViewProps>(componentDescriptor.cloneProps(parserContext, nullptr, RawProps{}));
}
static inline std::shared_ptr<const ShadowNode> generateShadowNodeTree(
const Entropy &entropy,
const ComponentDescriptor &componentDescriptor,
int size,
int deviation = 3)
{
if (size <= 1) {
auto family = componentDescriptor.createFamily({generateReactTag(), SurfaceId(1), nullptr});
return componentDescriptor.createShadowNode(ShadowNodeFragment{generateDefaultProps(componentDescriptor)}, family);
}
auto items = std::vector<int>(size);
std::fill(items.begin(), items.end(), 1);
auto chunks = entropy.distribute(items, deviation);
auto children = ShadowNode::ListOfShared{};
for (const auto &chunk : chunks) {
children.push_back(generateShadowNodeTree(entropy, componentDescriptor, chunk.size()));
}
auto family = componentDescriptor.createFamily({generateReactTag(), SurfaceId(1), nullptr});
return componentDescriptor.createShadowNode(
ShadowNodeFragment{
generateDefaultProps(componentDescriptor), std::make_shared<const ShadowNode::ListOfShared>(children)},
family);
}
} // namespace facebook::react