move C++ bridge into oss directory

Reviewed By: javache

Differential Revision: D4434052

fbshipit-source-id: ff2743eb7fe5d9cabb860752ca3d92d631a2f02b
This commit is contained in:
Marc Horowitz 2017-02-01 14:10:41 -08:00 committed by Facebook Github Bot
parent 5bc7e3934b
commit b774820dc2
18 changed files with 2421 additions and 0 deletions

View File

@ -0,0 +1,17 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <React/RCTBridge.h>
@interface RCTCxxBridge : RCTBridge
+ (void)enable;
+ (void)disable;
@end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,31 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include <string>
#include <cxxreact/MessageQueueThread.h>
#include <dispatch/dispatch.h>
namespace facebook {
namespace react {
class RCTMessageQueue : public MessageQueueThread {
public:
explicit RCTMessageQueue(const std::string &name);
void runOnQueue(std::function<void()>&&) override;
void runOnQueueSync(std::function<void()>&&) override;
void quitSynchronous() override;
private:
dispatch_queue_t m_queue;
std::atomic_bool m_shutdown;
};
}
}

View File

@ -0,0 +1,47 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include "RCTMessageQueue.h"
namespace facebook {
namespace react {
RCTMessageQueue::RCTMessageQueue(const std::string &name) {
m_queue = dispatch_queue_create(name.c_str(), NULL);
}
void RCTMessageQueue::runOnQueue(std::function<void()>&& func) {
if (m_shutdown) {
return;
}
dispatch_async(m_queue, ^{
if (!m_shutdown) {
func();
}
});
}
void RCTMessageQueue::runOnQueueSync(std::function<void()>&& func) {
if (m_shutdown) {
return;
}
dispatch_sync(m_queue, ^{
if (!m_shutdown) {
func();
}
});
}
void RCTMessageQueue::quitSynchronous() {
m_shutdown = true;
dispatch_sync(m_queue, ^{});
}
}
}

View File

@ -0,0 +1,39 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <string>
#import <Foundation/Foundation.h>
#import <React/RCTJavaScriptExecutor.h>
#import <cxxreact/MessageQueueThread.h>
namespace facebook {
namespace react {
class RCTMessageThread : public MessageQueueThread {
public:
RCTMessageThread(NSRunLoop *runLoop, RCTJavaScriptCompleteBlock errorBlock);
~RCTMessageThread() override;
void runOnQueue(std::function<void()>&&) override;
void runOnQueueSync(std::function<void()>&&) override;
void quitSynchronous() override;
private:
void tryFunc(const std::function<void()>& func);
void runAsync(std::function<void()> func);
void runSync(std::function<void()> func);
CFRunLoopRef m_cfRunLoop;
RCTJavaScriptCompleteBlock m_errorBlock;
std::atomic_bool m_shutdown;
};
}
}

View File

@ -0,0 +1,122 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include "RCTMessageThread.h"
#include <condition_variable>
#include <mutex>
#include <React/RCTUtils.h>
#include <jschelpers/JSCHelpers.h>
// A note about the implementation: This class is not used
// generically. It's a thin wrapper around a run loop which
// implements a C++ interface, for use by the C++ xplat bridge code.
// This means it can make certain non-generic assumptions. In
// particular, the sync functions are only used for bridge setup and
// teardown, and quitSynchronous is guaranteed to be called.
namespace facebook {
namespace react {
RCTMessageThread::RCTMessageThread(NSRunLoop *runLoop, RCTJavaScriptCompleteBlock errorBlock)
: m_cfRunLoop([runLoop getCFRunLoop])
, m_errorBlock(errorBlock)
, m_shutdown(false) {
CFRetain(m_cfRunLoop);
}
RCTMessageThread::~RCTMessageThread() {
CFRelease(m_cfRunLoop);
}
// This is analogous to dispatch_async
void RCTMessageThread::runAsync(std::function<void()> func) {
CFRunLoopPerformBlock(m_cfRunLoop, kCFRunLoopCommonModes, ^{ func(); });
CFRunLoopWakeUp(m_cfRunLoop);
}
// This is analogous to dispatch_sync
void RCTMessageThread::runSync(std::function<void()> func) {
if (m_cfRunLoop == CFRunLoopGetCurrent()) {
func();
return;
}
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
runAsync([func=std::make_shared<std::function<void()>>(std::move(func)), &sema] {
(*func)();
dispatch_semaphore_signal(sema);
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
void RCTMessageThread::tryFunc(const std::function<void()>& func) {
try {
@try {
func();
}
@catch (NSException *exception) {
NSString *message =
[NSString stringWithFormat:@"Exception '%@' was thrown from JS thread", exception];
m_errorBlock(RCTErrorWithMessage(message));
}
@catch (id exception) {
// This will catch any other ObjC exception, but no C++ exceptions
m_errorBlock(RCTErrorWithMessage(@"non-std ObjC Exception"));
}
} catch (const facebook::react::JSException &ex) {
// This is a special case. We want to extract the stack
// information and pass it to the redbox. This will lose the C++
// stack, but it's of limited value.
NSDictionary *errorInfo = @{
RCTJSRawStackTraceKey: @(ex.getStack().c_str()),
NSLocalizedDescriptionKey: [@"Unhandled JS Exception: " stringByAppendingString:@(ex.what())]
};
m_errorBlock([NSError errorWithDomain:RCTErrorDomain code:1 userInfo:errorInfo]);
} catch (const std::exception &ex) {
m_errorBlock(RCTErrorWithMessage(@(ex.what())));
} catch (...) {
// On a 64-bit platform, this would catch ObjC exceptions, too, but not on
// 32-bit platforms, so we catch those with id exceptions above.
m_errorBlock(RCTErrorWithMessage(@"non-std C++ exception"));
}
}
void RCTMessageThread::runOnQueue(std::function<void()>&& func) {
if (m_shutdown) {
return;
}
runAsync([this, func=std::make_shared<std::function<void()>>(std::move(func))] {
if (!m_shutdown) {
tryFunc(*func);
}
});
}
void RCTMessageThread::runOnQueueSync(std::function<void()>&& func) {
if (m_shutdown) {
return;
}
runSync([this, func=std::move(func)] {
if (!m_shutdown) {
tryFunc(func);
}
});
}
void RCTMessageThread::quitSynchronous() {
m_shutdown = true;
runSync([]{});
CFRunLoopStop(m_cfRunLoop);
}
}
}

View File

@ -0,0 +1,34 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <React/RCTModuleData.h>
#import <cxxreact/NativeModule.h>
namespace facebook {
namespace react {
class RCTNativeModule : public NativeModule {
public:
RCTNativeModule(RCTBridge *bridge, RCTModuleData *moduleData);
std::string getName() override;
std::vector<MethodDescriptor> getMethods() override;
folly::dynamic getConstants() override;
bool supportsWebWorkers() override;
void invoke(ExecutorToken token, unsigned int methodId, folly::dynamic &&params) override;
MethodCallResult callSerializableNativeHook(ExecutorToken token, unsigned int reactMethodId,
folly::dynamic &&params) override;
private:
__weak RCTBridge *m_bridge;
RCTModuleData *m_moduleData;
};
}
}

View File

@ -0,0 +1,127 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTNativeModule.h"
#import <React/RCTBridge.h>
#import <React/RCTBridgeMethod.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTCxxUtils.h>
#import <React/RCTProfile.h>
#import <React/RCTUtils.h>
namespace facebook {
namespace react {
RCTNativeModule::RCTNativeModule(RCTBridge *bridge, RCTModuleData *moduleData)
: m_bridge(bridge)
, m_moduleData(moduleData) {}
std::string RCTNativeModule::getName() {
return [m_moduleData.name UTF8String];
}
std::vector<MethodDescriptor> RCTNativeModule::getMethods() {
std::vector<MethodDescriptor> descs;
for (id<RCTBridgeMethod> method in m_moduleData.methods) {
descs.emplace_back(
method.JSMethodName.UTF8String,
method.functionType == RCTFunctionTypePromise ? "promise" : "async"
);
}
return descs;
}
folly::dynamic RCTNativeModule::getConstants() {
// TODO mhorowitz #10487027: This does unnecessary work since it
// only needs constants. Think about refactoring RCTModuleData or
// NativeModule to make this more natural.
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways,
@"[RCTNativeModule getConstants] moduleData.config", nil);
NSArray *config = m_moduleData.config;
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
if (!config || config == (id)kCFNull) {
return nullptr;
}
id constants = config[1];
if (![constants isKindOfClass:[NSDictionary class]]) {
return nullptr;
}
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways,
@"[RCTNativeModule getConstants] convert", nil);
folly::dynamic ret = [RCTConvert folly_dynamic:constants];
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
return ret;
}
bool RCTNativeModule::supportsWebWorkers() {
return false;
}
void RCTNativeModule::invoke(ExecutorToken token, unsigned int methodId, folly::dynamic &&params) {
// The BatchedBridge version of this buckets all the callbacks by thread, and
// queues one block on each. This is much simpler; we'll see how it goes and
// iterate.
// There is no flow event handling here until I can understand it.
auto sparams = std::make_shared<folly::dynamic>(std::move(params));
__weak RCTBridge *bridge = m_bridge;
dispatch_block_t block = ^{
if (!bridge || !bridge.valid) {
return;
}
id<RCTBridgeMethod> method = m_moduleData.methods[methodId];
if (RCT_DEBUG && !method) {
RCTLogError(@"Unknown methodID: %ud for module: %@",
methodId, m_moduleData.name);
}
NSArray *objcParams = RCTConvertFollyDynamic(*sparams);
@try {
[method invokeWithBridge:bridge module:m_moduleData.instance arguments:objcParams];
}
@catch (NSException *exception) {
// Pass on JS exceptions
if ([exception.name hasPrefix:RCTFatalExceptionName]) {
@throw exception;
}
NSString *message = [NSString stringWithFormat:
@"Exception '%@' was thrown while invoking %@ on target %@ with params %@",
exception, method.JSMethodName, m_moduleData.name, objcParams];
RCTFatal(RCTErrorWithMessage(message));
}
};
dispatch_queue_t queue = m_moduleData.methodQueue;
if (queue == RCTJSThread) {
block();
} else if (queue) {
dispatch_async(queue, block);
}
}
MethodCallResult RCTNativeModule::callSerializableNativeHook(
ExecutorToken token, unsigned int reactMethodId, folly::dynamic &&params) {
RCTFatal(RCTErrorWithMessage(@"callSerializableNativeHook is not yet supported on iOS"));
return {nullptr, true};
}
}
}

View File

@ -0,0 +1,34 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include <functional>
#include <memory>
#import <React/RCTDefines.h>
#import <React/RCTJavaScriptExecutor.h>
#include <cxxreact/Executor.h>
namespace facebook {
namespace react {
class RCTObjcExecutorFactory : public JSExecutorFactory {
public:
RCTObjcExecutorFactory(id<RCTJavaScriptExecutor> jse, RCTJavaScriptCompleteBlock errorBlock);
std::unique_ptr<JSExecutor> createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> jsQueue) override;
private:
NSURL *m_url;
id<RCTJavaScriptExecutor> m_jse;
RCTJavaScriptCompleteBlock m_errorBlock;
};
}
}

View File

@ -0,0 +1,141 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTObjcExecutor.h"
#import <React/RCTCxxUtils.h>
#import <React/RCTJavaScriptExecutor.h>
#import <React/RCTLog.h>
#import <React/RCTProfile.h>
#import <React/RCTUtils.h>
#import <cxxreact/Executor.h>
#import <cxxreact/ModuleRegistry.h>
#import <folly/json.h>
namespace facebook {
namespace react {
namespace {
class JSEException : public std::runtime_error {
public:
JSEException(NSError *error)
: runtime_error([[error description] UTF8String]) {}
};
class RCTObjcExecutor : public JSExecutor {
public:
RCTObjcExecutor(id<RCTJavaScriptExecutor> jse, RCTJavaScriptCompleteBlock errorBlock,
std::shared_ptr<facebook::react::ExecutorDelegate> delegate)
: m_jse(jse)
, m_errorBlock(errorBlock)
, m_delegate(delegate)
{
m_jsCallback = ^(id json, NSError *error) {
if (error) {
m_errorBlock(error);
return;
}
m_delegate->callNativeModules(*this, [RCTConvert folly_dynamic:json], true);
};
// Synchronously initialize the executor
[jse setUp];
folly::dynamic nativeModuleConfig = folly::dynamic::array;
auto moduleRegistry = delegate->getModuleRegistry();
for (const auto &name : moduleRegistry->moduleNames()) {
auto config = moduleRegistry->getConfig(name);
nativeModuleConfig.push_back(config ? config->config : nullptr);
}
folly::dynamic config =
folly::dynamic::object("remoteModuleConfig", std::move(nativeModuleConfig));
setGlobalVariable(
"__fbBatchedBridgeConfig",
std::make_unique<JSBigStdString>(folly::toJson(config)));
}
void loadApplicationScript(
std::unique_ptr<const JSBigString> script,
std::string sourceURL) override {
RCTProfileBeginFlowEvent();
[m_jse executeApplicationScript:[NSData dataWithBytes:script->c_str() length:script->size()]
sourceURL:[[NSURL alloc]
initWithString:@(sourceURL.c_str())]
onComplete:^(NSError *error) {
RCTProfileEndFlowEvent();
if (error) {
m_errorBlock(error);
return;
}
[m_jse flushedQueue:m_jsCallback];
}];
}
void setJSModulesUnbundle(std::unique_ptr<JSModulesUnbundle>) override {
RCTLogWarn(@"Unbundle is not supported in RCTObjcExecutor");
}
void callFunction(const std::string &module, const std::string &method,
const folly::dynamic &arguments) override {
[m_jse callFunctionOnModule:@(module.c_str())
method:@(method.c_str())
arguments:RCTConvertFollyDynamic(arguments)
callback:m_jsCallback];
}
void invokeCallback(double callbackId, const folly::dynamic &arguments) override {
[m_jse invokeCallbackID:@(callbackId)
arguments:RCTConvertFollyDynamic(arguments)
callback:m_jsCallback];
}
virtual void setGlobalVariable(
std::string propName,
std::unique_ptr<const JSBigString> jsonValue) override {
[m_jse injectJSONText:@(jsonValue->c_str())
asGlobalObjectNamed:@(propName.c_str())
callback:m_errorBlock];
}
virtual bool supportsProfiling() override {
return false;
};
virtual void startProfiler(const std::string &titleString) override {};
virtual void stopProfiler(const std::string &titleString,
const std::string &filename) override {};
private:
id<RCTJavaScriptExecutor> m_jse;
RCTJavaScriptCompleteBlock m_errorBlock;
std::shared_ptr<facebook::react::ExecutorDelegate> m_delegate;
RCTJavaScriptCallback m_jsCallback;
};
}
RCTObjcExecutorFactory::RCTObjcExecutorFactory(
id<RCTJavaScriptExecutor> jse, RCTJavaScriptCompleteBlock errorBlock)
: m_jse(jse)
, m_errorBlock(errorBlock) {}
std::unique_ptr<JSExecutor> RCTObjcExecutorFactory::createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> jsQueue) {
return std::unique_ptr<JSExecutor>(
new RCTObjcExecutor(m_jse, m_errorBlock, delegate));
}
}
}

View File

@ -0,0 +1,19 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <Foundation/Foundation.h>
#import <React/RCTBridgeMethod.h>
#import <cxxreact/CxxModule.h>
@interface RCTCxxMethod : NSObject <RCTBridgeMethod>
- (instancetype)initWithCxxMethod:(const facebook::xplat::module::CxxModule::Method &)cxxMethod;
@end

View File

@ -0,0 +1,123 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTCxxMethod.h"
#import <React/RCTBridge+Private.h>
#import <React/RCTBridge.h>
#import <React/RCTConvert.h>
#import <cxxreact/JsArgumentHelpers.h>
#import <folly/Memory.h>
#import "RCTCxxUtils.h"
using facebook::xplat::module::CxxModule;
@implementation RCTCxxMethod
{
std::unique_ptr<CxxModule::Method> _method;
}
@synthesize JSMethodName = _JSMethodName;
- (instancetype)initWithCxxMethod:(const CxxModule::Method &)method
{
if ((self = [super init])) {
_JSMethodName = @(method.name.c_str());
_method = folly::make_unique<CxxModule::Method>(method);
}
return self;
}
- (id)invokeWithBridge:(RCTBridge *)bridge
module:(id)module
arguments:(NSArray *)arguments
{
// module is unused except for printing errors. The C++ object it represents
// is also baked into _method.
// the last N arguments are callbacks, according to the Method data. The
// preceding arguments are values whic have already been parsed from JS: they
// may be NSNumber (bool, int, double), NSString, NSArray, or NSObject.
CxxModule::Callback first;
CxxModule::Callback second;
if (arguments.count < _method->callbacks) {
RCTLogError(@"Method %@.%s expects at least %lu arguments, but got %tu",
RCTBridgeModuleNameForClass([module class]), _method->name.c_str(),
_method->callbacks, arguments.count);
return nil;
}
if (_method->callbacks >= 1) {
if (![arguments[arguments.count - 1] isKindOfClass:[NSNumber class]]) {
RCTLogError(@"Argument %tu (%@) of %@.%s should be a function",
arguments.count - 1, arguments[arguments.count - 1],
RCTBridgeModuleNameForClass([module class]), _method->name.c_str());
return nil;
}
NSNumber *id1;
if (_method->callbacks == 2) {
if (![arguments[arguments.count - 2] isKindOfClass:[NSNumber class]]) {
RCTLogError(@"Argument %tu (%@) of %@.%s should be a function",
arguments.count - 2, arguments[arguments.count - 2],
RCTBridgeModuleNameForClass([module class]), _method->name.c_str());
return nil;
}
id1 = arguments[arguments.count - 2];
NSNumber *id2 = arguments[arguments.count - 1];
second = ^(std::vector<folly::dynamic> args) {
[bridge enqueueCallback:id2 args:RCTConvertFollyDynamic(folly::dynamic(args.begin(), args.end()))];
};
} else {
id1 = arguments[arguments.count - 1];
}
first = ^(std::vector<folly::dynamic> args) {
[bridge enqueueCallback:id1 args:RCTConvertFollyDynamic(folly::dynamic(args.begin(), args.end()))];
};
}
folly::dynamic args = [RCTConvert folly_dynamic:arguments];
args.resize(args.size() - _method->callbacks);
try {
if (_method->func) {
_method->func(std::move(args), first, second);
return nil;
} else {
auto result = _method->syncFunc(std::move(args));
// TODO: we should convert this to JSValue directly
return RCTConvertFollyDynamic(result);
}
} catch (const facebook::xplat::JsArgumentException &ex) {
RCTLogError(@"Method %@.%s argument error: %s",
RCTBridgeModuleNameForClass([module class]), _method->name.c_str(),
ex.what());
return nil;
}
}
- (RCTFunctionType)functionType
{
// TODO: support promise-style APIs
return _method->syncFunc ? RCTFunctionTypeSync : RCTFunctionTypeNormal;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p; name = %@>",
[self class], self, self.JSMethodName];
}
@end

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <memory>
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import <cxxreact/CxxModule.h>
@interface RCTCxxModule : NSObject <RCTBridgeModule>
- (instancetype)initWithCxxModule:(std::unique_ptr<facebook::xplat::module::CxxModule>)module;
- (NSArray *)methodsToExport;
- (NSDictionary *)constantsToExport;
// Extracts the module from its objc wrapper
- (std::unique_ptr<facebook::xplat::module::CxxModule>)move;
@end

View File

@ -0,0 +1,73 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTCxxModule.h"
#import <React/RCTBridge.h>
#import <cxxreact/CxxModule.h>
#import "RCTCxxMethod.h"
#import "RCTCxxUtils.h"
@implementation RCTCxxModule
{
std::unique_ptr<facebook::xplat::module::CxxModule> _module;
}
- (instancetype)init
{
return nil;
}
- (instancetype)initWithCxxModule:(std::unique_ptr<facebook::xplat::module::CxxModule>)module
{
RCTAssert([RCTBridgeModuleNameForClass([self class]) isEqualToString:@(module->getName().c_str())],
@"CxxModule class name %@ does not match runtime name %s",
RCTBridgeModuleNameForClass([self class]), module->getName().c_str());
if ((self = [super init])) {
_module = std::move(module);
}
return self;
}
- (std::unique_ptr<facebook::xplat::module::CxxModule>)move
{
return std::move(_module);
}
+ (NSString *)moduleName
{
return @"";
}
- (NSArray *)methodsToExport
{
CHECK(_module) << "Can't call methodsToExport on moved module";
NSMutableArray *moduleMethods = [NSMutableArray new];
for (const auto &method : _module->getMethods()) {
[moduleMethods addObject:[[RCTCxxMethod alloc] initWithCxxMethod:method]];
}
return moduleMethods;
}
- (NSDictionary *)constantsToExport
{
CHECK(_module) << "Can't call constantsToExport on moved module";
NSMutableDictionary *moduleConstants = [NSMutableDictionary new];
for (const auto &c : _module->getConstants()) {
moduleConstants[@(c.first.c_str())] = RCTConvertFollyDynamic(c.second);
}
return moduleConstants;
}
@end

View File

@ -0,0 +1,19 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <React/RCTConvert.h>
#include <folly/dynamic.h>
id RCTConvertFollyDynamic(const folly::dynamic &dyn);
@interface RCTConvert (folly)
+ (folly::dynamic)folly_dynamic:(id)json;
@end

View File

@ -0,0 +1,35 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTCxxUtils.h"
#import <React/RCTFollyConvert.h>
using namespace react::CxxUtils;
id RCTConvertFollyDynamic(const folly::dynamic &dyn) {
return convertFollyDynamicToId(dyn);
}
@implementation RCTConvert (folly)
+ (folly::dynamic)folly_dynamic:(id)json;
{
if (json == nil || json == (id)kCFNull) {
return nullptr;
} else {
folly::dynamic dyn = convertIdToFollyDynamic(json);
if (dyn == nil) {
RCTAssert(false, @"RCTConvert input json is of an impossible type");
}
return dyn;
}
}
@end

View File

@ -0,0 +1,19 @@
// Copyright 2004-present Facebook. All Rights Reserved.
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include <folly/dynamic.h>
namespace react { namespace CxxUtils {
folly::dynamic convertIdToFollyDynamic(id json);
id convertFollyDynamicToId(const folly::dynamic &dyn);
}}

View File

@ -0,0 +1,116 @@
// Copyright 2004-present Facebook. All Rights Reserved.
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTFollyConvert.h"
#import <objc/runtime.h>
#import <Foundation/Foundation.h>
namespace react { namespace CxxUtils {
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] initWithData:[NSData dataWithBytes:dyn.data() length:dyn.size()]
encoding:NSUTF8StringEncoding];
case folly::dynamic::ARRAY: {
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:dyn.size()];
for (auto &elem : dyn) {
[array addObject:convertFollyDynamicToId(elem)];
}
return array;
}
case folly::dynamic::OBJECT: {
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:dyn.size()];
for (auto &elem : dyn.items()) {
dict[convertFollyDynamicToId(elem.first)] = convertFollyDynamicToId(elem.second);
}
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 [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 [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;
}
}}