move C++ bridge into oss directory
Reviewed By: javache Differential Revision: D4434052 fbshipit-source-id: ff2743eb7fe5d9cabb860752ca3d92d631a2f02b
This commit is contained in:
Родитель
5bc7e3934b
Коммит
b774820dc2
|
@ -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
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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, ^{});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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 &¶ms) override;
|
||||
MethodCallResult callSerializableNativeHook(ExecutorToken token, unsigned int reactMethodId,
|
||||
folly::dynamic &¶ms) override;
|
||||
|
||||
private:
|
||||
__weak RCTBridge *m_bridge;
|
||||
RCTModuleData *m_moduleData;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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 &¶ms) {
|
||||
// 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 &¶ms) {
|
||||
RCTFatal(RCTErrorWithMessage(@"callSerializableNativeHook is not yet supported on iOS"));
|
||||
return {nullptr, true};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
|
||||
}}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}}
|
Загрузка…
Ссылка в новой задаче