Fabric: Unification of registration process of `ComponentView`s and `ComponentDescriptor`s

Summary:
Registries, providers, providers of registries, registres of providers. All that can be really confusing, but that represents best the constraints and desires that we have:
* We need to be able to register components on-the-fly (so we need a mechanism that will propagate changes);
* We don't want to register ComponentDescriptors separately from ComponentView classes;
* C++ not always gives us abstractions that we want (e.g. pointers to constructors).

After the change, we can remove the whole Buck target that has a bunch of handwritten boilerplate code.

There is a still room for polish and removing some concepts, types or classes but this diff is already huge.

Reviewed By: JoshuaGross

Differential Revision: D14983906

fbshipit-source-id: ce536ebea0c905059c7a4d644dc25233e2809761
This commit is contained in:
Valentin Shergin 2019-04-17 22:39:41 -07:00 коммит произвёл Facebook Github Bot
Родитель 00eab3d6fb
Коммит 832164169d
11 изменённых файлов: 266 добавлений и 16 удалений

Просмотреть файл

@ -9,6 +9,8 @@
#import <React/RCTComponentViewProtocol.h>
#import <react/uimanager/ComponentDescriptorRegistry.h>
NS_ASSUME_NONNULL_BEGIN
/**
@ -27,12 +29,24 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)registerComponentViewClass:(Class<RCTComponentViewProtocol>)componentViewClass;
/**
* Unregisters a component view class in the factory.
*/
- (void)unregisterComponentViewClass:(Class<RCTComponentViewProtocol>)componentViewClass;
/**
* Creates a component view with given component handle.
*/
- (UIView<RCTComponentViewProtocol> *)createComponentViewWithComponentHandle:
(facebook::react::ComponentHandle)componentHandle;
/**
* Creates *managed* `ComponentDescriptorRegistry`. After creation, the object continues to store a weak pointer to the
* registry and update it accordingly to the changes in the object.
*/
- (facebook::react::ComponentDescriptorRegistry::Shared)createComponentDescriptorRegistryWithParameters:
(facebook::react::ComponentDescriptorParameters)parameters;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -8,7 +8,11 @@
#import "RCTComponentViewFactory.h"
#import <React/RCTAssert.h>
#import <better/map.h>
#import <better/mutex.h>
#import <react/core/ReactPrimitives.h>
#import <react/uimanager/ComponentDescriptorProviderRegistry.h>
#import "RCTActivityIndicatorViewComponentView.h"
#import "RCTImageComponentView.h"
@ -23,13 +27,13 @@
using namespace facebook::react;
@implementation RCTComponentViewFactory {
std::unordered_map<ComponentHandle, Class<RCTComponentViewProtocol>> _registry;
better::map<ComponentHandle, Class<RCTComponentViewProtocol>> _componentViewClasses;
ComponentDescriptorProviderRegistry _providerRegistry;
better::shared_mutex _mutex;
}
+ (RCTComponentViewFactory *)standardComponentViewFactory
{
RCTAssertMainQueue();
RCTComponentViewFactory *componentViewFactory = [[RCTComponentViewFactory alloc] init];
[componentViewFactory registerComponentViewClass:[RCTViewComponentView class]];
@ -47,20 +51,36 @@ using namespace facebook::react;
- (void)registerComponentViewClass:(Class<RCTComponentViewProtocol>)componentViewClass
{
RCTAssertMainQueue();
std::unique_lock<better::shared_mutex> lock(_mutex);
auto componentHandle = [componentViewClass componentDescriptorProvider].handle;
_registry[componentHandle] = componentViewClass;
auto componentDescriptorProvider = [componentViewClass componentDescriptorProvider];
_componentViewClasses[componentDescriptorProvider.handle] = componentViewClass;
_providerRegistry.add(componentDescriptorProvider);
auto supplementalComponentDescriptorProviders = [componentViewClass supplementalComponentDescriptorProviders];
for (const auto &provider : supplementalComponentDescriptorProviders) {
_providerRegistry.add(provider);
}
}
- (void)unregisterComponentViewClass:(Class<RCTComponentViewProtocol>)componentViewClass
{
std::unique_lock<better::shared_mutex> lock(_mutex);
auto componentDescriptorProvider = [componentViewClass componentDescriptorProvider];
_componentViewClasses.erase(componentDescriptorProvider.handle);
_providerRegistry.remove(componentDescriptorProvider);
}
- (UIView<RCTComponentViewProtocol> *)createComponentViewWithComponentHandle:
(facebook::react::ComponentHandle)componentHandle
{
RCTAssertMainQueue();
std::shared_lock<better::shared_mutex> lock(_mutex);
auto iterator = _registry.find(componentHandle);
auto iterator = _componentViewClasses.find(componentHandle);
RCTAssert(
iterator != _registry.end(),
iterator != _componentViewClasses.end(),
@"ComponentView with componentHandle `%lli` (`%s`) not found.",
componentHandle,
(char *)componentHandle);
@ -68,4 +88,12 @@ using namespace facebook::react;
return [[componentViewClass alloc] init];
}
- (facebook::react::ComponentDescriptorRegistry::Shared)createComponentDescriptorRegistryWithParameters:
(facebook::react::ComponentDescriptorParameters)parameters
{
std::shared_lock<better::shared_mutex> lock(_mutex);
return _providerRegistry.createComponentDescriptorRegistry(parameters);
}
@end

Просмотреть файл

@ -13,6 +13,7 @@
#import <react/core/LayoutConstraints.h>
#import <react/core/LayoutContext.h>
#import <react/mounting/ShadowViewMutation.h>
#import <react/uimanager/ComponentDescriptorFactory.h>
#import <react/utils/ContextContainer.h>
NS_ASSUME_NONNULL_BEGIN
@ -38,7 +39,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (atomic, weak, nullable) id<RCTSchedulerDelegate> delegate;
- (instancetype)initWithContextContainer:(facebook::react::ContextContainer::Shared)contextContatiner;
- (instancetype)initWithContextContainer:(facebook::react::ContextContainer::Shared)contextContatiner
componentRegistryFactory:(facebook::react::ComponentRegistryFactory)componentRegistryFactory;
- (void)startSurfaceWithSurfaceId:(facebook::react::SurfaceId)surfaceId
moduleName:(NSString *)moduleName

Просмотреть файл

@ -48,10 +48,11 @@ class SchedulerDelegateProxy : public SchedulerDelegate {
}
- (instancetype)initWithContextContainer:(ContextContainer::Shared)contextContainer
componentRegistryFactory:(ComponentRegistryFactory)componentRegistryFactory
{
if (self = [super init]) {
_delegateProxy = std::make_shared<SchedulerDelegateProxy>((__bridge void *)self);
_scheduler = std::make_shared<Scheduler>(contextContainer, getDefaultComponentRegistryFactory());
_scheduler = std::make_shared<Scheduler>(contextContainer, componentRegistryFactory);
_scheduler->setDelegate(_delegateProxy.get());
}

Просмотреть файл

@ -14,6 +14,7 @@
#import <React/RCTAssert.h>
#import <React/RCTBridge+Private.h>
#import <React/RCTComponentViewFactory.h>
#import <React/RCTComponentViewRegistry.h>
#import <React/RCTFabricSurface.h>
#import <React/RCTFollyConvert.h>
@ -29,6 +30,7 @@
#import <react/core/LayoutConstraints.h>
#import <react/core/LayoutContext.h>
#import <react/imagemanager/ImageManager.h>
#import <react/uimanager/ComponentDescriptorFactory.h>
#import <react/utils/ContextContainer.h>
#import "MainRunLoopEventBeat.h"
@ -193,7 +195,15 @@ using namespace facebook::react;
return _scheduler;
}
_scheduler = [[RCTScheduler alloc] initWithContextContainer:self.contextContainer];
auto componentRegistryFactory = [factory = RNWrapManagedObject(self.componentViewFactory)](
EventDispatcher::Shared const &eventDispatcher,
ContextContainer::Shared const &contextContainer) {
return [(RCTComponentViewFactory *)RNUnwrapManagedObject(factory)
createComponentDescriptorRegistryWithParameters:{eventDispatcher, contextContainer}];
};
_scheduler = [[RCTScheduler alloc] initWithContextContainer:self.contextContainer
componentRegistryFactory:componentRegistryFactory];
_scheduler.delegate = self;
return _scheduler;

Просмотреть файл

@ -19,7 +19,7 @@ namespace react {
class ComponentDescriptor;
using SharedComponentDescriptor = std::shared_ptr<ComponentDescriptor>;
using SharedComponentDescriptor = std::shared_ptr<ComponentDescriptor const>;
/*
* Abstract class defining an interface of `ComponentDescriptor`.

Просмотреть файл

@ -14,6 +14,16 @@
namespace facebook {
namespace react {
/*
* Represents a collection of arguments that sufficient to construct a
* `ComponentDescriptor`.
*/
class ComponentDescriptorParameters {
public:
EventDispatcher::Shared eventDispatcher;
ContextContainer::Shared contextContainer;
};
/*
* Callable signature that represents the signature of `ComponentDescriptor`
* constructor. The callable returns a unique pointer conveniently represents an
@ -32,6 +42,7 @@ using ComponentDescriptorConstructor = ComponentDescriptor::Unique(
class ComponentDescriptorProvider final {
public:
ComponentHandle handle;
ComponentName name;
ComponentDescriptorConstructor *constructor;
};
@ -62,6 +73,7 @@ ComponentDescriptorProvider concreteComponentDescriptorProvider() {
"ComponentDescriptorT must be a descendant of ComponentDescriptor");
return {ComponentDescriptorT::ConcreteShadowNode::Handle(),
ComponentDescriptorT::ConcreteShadowNode::Name(),
&concreteComponentDescriptorConstructor<ComponentDescriptorT>};
}

Просмотреть файл

@ -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.
*/
#include "ComponentDescriptorProviderRegistry.h"
namespace facebook {
namespace react {
void ComponentDescriptorProviderRegistry::add(
ComponentDescriptorProvider provider) const {
std::unique_lock<better::shared_mutex> lock(mutex_);
componentDescriptorProviders_.insert({provider.handle, provider});
for (auto const &weakRegistry : componentDescriptorRegistries_) {
auto registry = weakRegistry.lock();
if (!registry) {
continue;
}
registry->add(provider);
}
}
void ComponentDescriptorProviderRegistry::remove(
ComponentDescriptorProvider provider) const {
std::unique_lock<better::shared_mutex> lock(mutex_);
componentDescriptorProviders_.erase(provider.handle);
for (auto const &weakRegistry : componentDescriptorRegistries_) {
auto registry = weakRegistry.lock();
if (!registry) {
continue;
}
registry->remove(provider);
}
}
ComponentDescriptorRegistry::Shared
ComponentDescriptorProviderRegistry::createComponentDescriptorRegistry(
ComponentDescriptorParameters const &parameters) const {
std::shared_lock<better::shared_mutex> lock(mutex_);
auto registry =
std::make_shared<ComponentDescriptorRegistry const>(parameters);
for (auto const &pair : componentDescriptorProviders_) {
registry->add(pair.second);
}
return registry;
}
} // namespace react
} // namespace facebook

Просмотреть файл

@ -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 <better/mutex.h>
#include <react/core/ComponentDescriptor.h>
#include <react/uimanager/ComponentDescriptorProvider.h>
#include <react/uimanager/ComponentDescriptorRegistry.h>
namespace facebook {
namespace react {
/*
* Registry of `ComponentDescriptorProvider`s (and managed
* `ComponentDescriptorRegistry`s). The class maintains a list of
* `ComponentDescriptorRegistry`s (retaining pointers weakly) and update them
* accordingly to changes in the provider registry.
*/
class ComponentDescriptorProviderRegistry final {
public:
/*
* Adds (or removes) a `ComponentDescriptorProvider`s and update the managed
* `ComponentDescriptorRegistry`s accordingly.
*/
void add(ComponentDescriptorProvider provider) const;
void remove(ComponentDescriptorProvider provider) const;
/*
* Creates managed `ComponentDescriptorRegistry` based on a stored list of
* `ComponentDescriptorProvider`s and given `ComponentDescriptorParameters`.
*/
ComponentDescriptorRegistry::Shared createComponentDescriptorRegistry(
ComponentDescriptorParameters const &parameters) const;
private:
mutable better::shared_mutex mutex_;
mutable std::vector<std::weak_ptr<ComponentDescriptorRegistry const>>
componentDescriptorRegistries_;
mutable better::map<ComponentHandle, ComponentDescriptorProvider const>
componentDescriptorProviders_;
};
} // namespace react
} // namespace facebook

Просмотреть файл

@ -11,8 +11,47 @@
namespace facebook {
namespace react {
ComponentDescriptorRegistry::ComponentDescriptorRegistry(
ComponentDescriptorParameters const &parameters)
: parameters_(parameters) {}
void ComponentDescriptorRegistry::add(
ComponentDescriptorProvider componentDescriptorProvider) const {
std::unique_lock<better::shared_mutex> lock(mutex_);
auto componentDescriptor = componentDescriptorProvider.constructor(
parameters_.eventDispatcher, parameters_.contextContainer);
assert(
componentDescriptor->getComponentHandle() ==
componentDescriptorProvider.handle);
assert(
componentDescriptor->getComponentName() ==
componentDescriptorProvider.name);
auto sharedComponentDescriptor = std::shared_ptr<ComponentDescriptor const>(
std::move(componentDescriptor));
_registryByHandle[componentDescriptorProvider.handle] =
sharedComponentDescriptor;
_registryByName[componentDescriptorProvider.name] = sharedComponentDescriptor;
}
void ComponentDescriptorRegistry::remove(
ComponentDescriptorProvider componentDescriptorProvider) const {
std::unique_lock<better::shared_mutex> lock(mutex_);
assert(
_registryByHandle.find(componentDescriptorProvider.handle) !=
_registryByHandle.end());
assert(
_registryByName.find(componentDescriptorProvider.name) !=
_registryByName.end());
_registryByHandle.erase(componentDescriptorProvider.handle);
_registryByName.erase(componentDescriptorProvider.name);
}
void ComponentDescriptorRegistry::registerComponentDescriptor(
SharedComponentDescriptor componentDescriptor) {
SharedComponentDescriptor componentDescriptor) const {
ComponentHandle componentHandle = componentDescriptor->getComponentHandle();
_registryByHandle[componentHandle] = componentDescriptor;
@ -72,6 +111,8 @@ static ComponentName componentNameByReactViewName(ComponentName viewName) {
ComponentDescriptor const &ComponentDescriptorRegistry::at(
ComponentName const &componentName) const {
std::shared_lock<better::shared_mutex> lock(mutex_);
auto unifiedComponentName = componentNameByReactViewName(componentName);
auto it = _registryByName.find(unifiedComponentName);
@ -88,6 +129,8 @@ ComponentDescriptor const &ComponentDescriptorRegistry::at(
ComponentDescriptor const &ComponentDescriptorRegistry::at(
ComponentHandle componentHandle) const {
std::shared_lock<better::shared_mutex> lock(mutex_);
return *_registryByHandle.at(componentHandle);
}

Просмотреть файл

@ -8,7 +8,10 @@
#include <memory>
#include <better/map.h>
#include <better/mutex.h>
#include <react/core/ComponentDescriptor.h>
#include <react/uimanager/ComponentDescriptorProvider.h>
namespace facebook {
namespace react {
@ -25,8 +28,22 @@ class ComponentDescriptorRegistry {
public:
using Shared = std::shared_ptr<const ComponentDescriptorRegistry>;
/*
* Deprecated. Use custom constructor instead.
*/
ComponentDescriptorRegistry() = default;
/*
* Creates an object with stored `ComponentDescriptorParameters` which will
* be used later to create `ComponentDescriptor`s.
*/
ComponentDescriptorRegistry(ComponentDescriptorParameters const &parameters);
/*
* Deprecated. Use `add` instead.
*/
void registerComponentDescriptor(
SharedComponentDescriptor componentDescriptor);
SharedComponentDescriptor componentDescriptor) const;
ComponentDescriptor const &at(ComponentName const &componentName) const;
ComponentDescriptor const &at(ComponentHandle componentHandle) const;
@ -42,9 +59,23 @@ class ComponentDescriptorRegistry {
ComponentDescriptor::Shared getFallbackComponentDescriptor() const;
private:
better::map<ComponentHandle, SharedComponentDescriptor> _registryByHandle;
better::map<ComponentName, SharedComponentDescriptor> _registryByName;
friend class ComponentDescriptorProviderRegistry;
/*
* Adds (or removes) a `ComponentDescriptor ` created using given
* `ComponentDescriptorProvider` and stored `ComponentDescriptorParameters`.
* To be used by `ComponentDescriptorProviderRegistry` only.
* Thread safe.
*/
void add(ComponentDescriptorProvider componentDescriptorProvider) const;
void remove(ComponentDescriptorProvider componentDescriptorProvider) const;
mutable better::shared_mutex mutex_;
mutable better::map<ComponentHandle, SharedComponentDescriptor>
_registryByHandle;
mutable better::map<ComponentName, SharedComponentDescriptor> _registryByName;
ComponentDescriptor::Shared _fallbackComponentDescriptor;
ComponentDescriptorParameters parameters_{};
};
} // namespace react