Permalink
Browse files

Add support for Cxx objects as arguments to native modules

Reviewed By: fkgozali

Differential Revision: D5589269

fbshipit-source-id: 1bd7004adc397241cabfb1dc59ba1aebad943bf8
  • Loading branch information...
fromcelticpark authored and facebook-github-bot committed Aug 14, 2017
1 parent 2a6965d commit 6783694158057662fd7b11fc123c339b2b21bfe6
@@ -87,6 +87,16 @@ - (void)testUntypedUnnamedArgs
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[2]).type, @"id");
}
- (void)testNamespacedCxxStruct
{
NSArray *arguments;
const char *methodSignature = "foo:(foo::type &)foo";
SEL selector = RCTParseMethodSignature(methodSignature, &arguments);
XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:");
XCTAssertEqual(arguments.count, (NSUInteger)1);
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"foo::type");
}
- (void)testAttributes
{
NSArray *arguments;
View
@@ -0,0 +1,20 @@
/**
* 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>
/**
* This class provides a collection of conversion functions for mapping
* JSON objects to cxx types. Extensible via categories.
* Convert methods are expected to return cxx objects wraped in RCTManagedPointer.
*/
@interface RCTCxxConvert : NSObject
@end
View
@@ -0,0 +1,14 @@
/**
* 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 "RCTCxxConvert.h"
@implementation RCTCxxConvert
@end
@@ -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 <memory>
#import <Foundation/Foundation.h>
/**
* Type erased wrapper over any cxx value that can be passed as an argument
* to native method.
*/
@interface RCTManagedPointer: NSObject
@property (nonatomic, readonly) void *voidPointer;
- (instancetype)initWithPointer:(std::shared_ptr<void>)pointer;
@end
namespace RCT {
template <typename T, typename P>
RCTManagedPointer *managedPointer(P initializer)
{
auto ptr = std::shared_ptr<void>(new T((NSDictionary *)initializer));
return [[RCTManagedPointer alloc] initWithPointer:std::move(ptr)];
}
}
@@ -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 "RCTManagedPointer.h"
@implementation RCTManagedPointer {
std::shared_ptr<void> _pointer;
}
- (instancetype)initWithPointer:(std::shared_ptr<void>)pointer {
if (self = [super init]) {
_pointer = std::move(pointer);
}
return self;
}
- (void *)voidPointer {
return _pointer.get();
}
@end
@@ -15,7 +15,9 @@
#import "RCTBridge+Private.h"
#import "RCTBridge.h"
#import "RCTConvert.h"
#import "RCTCxxConvert.h"
#import "RCTLog.h"
#import "RCTManagedPointer.h"
#import "RCTParserUtils.h"
#import "RCTProfile.h"
#import "RCTUtils.h"
@@ -60,12 +62,14 @@ static void RCTLogArgumentError(RCTModuleMethod *method, NSUInteger index,
RCT_NOT_IMPLEMENTED(- (instancetype)init)
RCT_EXTERN_C_BEGIN
// returns YES if the selector ends in a colon (indicating that there is at
// least one argument, and maybe more selector parts) or NO if it doesn't.
static BOOL RCTParseSelectorPart(const char **input, NSMutableString *selector)
{
NSString *selectorPart;
if (RCTParseIdentifier(input, &selectorPart)) {
if (RCTParseSelectorIdentifier(input, &selectorPart)) {
[selector appendString:selectorPart];
}
RCTSkipWhitespace(input);
@@ -157,14 +161,16 @@ SEL RCTParseMethodSignature(const char *input, NSArray<RCTMethodArgument *> **ar
}
// Argument name
RCTParseIdentifier(&input, NULL);
RCTParseArgumentIdentifier(&input, NULL);
RCTSkipWhitespace(&input);
}
*arguments = [args copy];
return NSSelectorFromString(selector);
}
RCT_EXTERN_C_END
- (instancetype)initWithExportedMethod:(const RCTMethodInfo *)exportedMethod
moduleClass:(Class)moduleClass
{
@@ -211,7 +217,7 @@ - (void)processMethodSignature
#define __PRIMITIVE_CASE(_type, _nullable) { \
isNullableType = _nullable; \
_type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \
_type (*convert)(id, SEL, id) = (__typeof__(convert))objc_msgSend; \
[argumentBlocks addObject:^(__unused RCTBridge *bridge, NSUInteger index, id json) { \
_type value = convert([RCTConvert class], selector, json); \
[invocation setArgument:&value atIndex:(index) + 2]; \
@@ -274,7 +280,7 @@ - (void)processMethodSignature
case _C_ID: {
isNullableType = YES;
id (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend;
id (*convert)(id, SEL, id) = (__typeof__(convert))objc_msgSend;
RCT_RETAINED_ARG_BLOCK(
id value = convert([RCTConvert class], selector, json);
);
@@ -300,7 +306,7 @@ - (void)processMethodSignature
}
default: {
static const char *blockType = @encode(typeof(^{}));
static const char *blockType = @encode(__typeof__(^{}));
if (!strcmp(objcType, blockType)) {
BLOCK_CASE((NSArray *args), {
[bridge enqueueCallback:json args:args];
@@ -334,6 +340,22 @@ - (void)processMethodSignature
NSDictionary *errorJSON = RCTJSErrorFromCodeMessageAndNSError(code, message, error);
[bridge enqueueCallback:json args:@[errorJSON]];
});
} else if ([typeName hasPrefix:@"JS::"]) {
NSString *selectorNameForCxxType =
[[typeName stringByReplacingOccurrencesOfString:@"::" withString:@"_"]
stringByAppendingString:@":"];
selector = NSSelectorFromString(selectorNameForCxxType);
[argumentBlocks addObject:^(__unused RCTBridge *bridge, NSUInteger index, id json) {
RCTManagedPointer *(*convert)(id, SEL, id) = (__typeof__(convert))objc_msgSend;
RCTManagedPointer *box = convert([RCTCxxConvert class], selector, json);
void *pointer = box.voidPointer;
[invocation setArgument:&pointer atIndex:index + 2];
[retainedObjects addObject:box];
return YES;
}];
} else {
// Unknown argument type
RCTLogError(@"Unknown argument type '%@' in method %@. Extend RCTConvert to support this type.",
@@ -19,7 +19,8 @@
RCT_EXTERN BOOL RCTReadChar(const char **input, char c);
RCT_EXTERN BOOL RCTReadString(const char **input, const char *string);
RCT_EXTERN void RCTSkipWhitespace(const char **input);
RCT_EXTERN BOOL RCTParseIdentifier(const char **input, NSString **string);
RCT_EXTERN BOOL RCTParseSelectorIdentifier(const char **input, NSString **string);
RCT_EXTERN BOOL RCTParseArgumentIdentifier(const char **input, NSString **string);
/**
* Parse an Objective-C type into a form that can be used by RCTConvert.
@@ -51,7 +51,32 @@ static BOOL RCTIsIdentifierTail(const char c)
return isalnum(c) || c == '_';
}
BOOL RCTParseIdentifier(const char **input, NSString **string)
BOOL RCTParseArgumentIdentifier(const char **input, NSString **string)
{
const char *start = *input;
do {
if (!RCTIsIdentifierHead(**input)) {
return NO;
}
(*input)++;
while (RCTIsIdentifierTail(**input)) {
(*input)++;
}
// allow namespace resolution operator
} while (RCTReadString(input, "::"));
if (string) {
*string = [[NSString alloc] initWithBytes:start
length:(NSInteger)(*input - start)
encoding:NSASCIIStringEncoding];
}
return YES;
}
BOOL RCTParseSelectorIdentifier(const char **input, NSString **string)
{
const char *start = *input;
if (!RCTIsIdentifierHead(**input)) {
@@ -83,7 +108,7 @@ static BOOL RCTIsCollectionType(NSString *type)
NSString *RCTParseType(const char **input)
{
NSString *type;
RCTParseIdentifier(input, &type);
RCTParseArgumentIdentifier(input, &type);
RCTSkipWhitespace(input);
if (RCTReadChar(input, '<')) {
RCTSkipWhitespace(input);
Oops, something went wrong.

0 comments on commit 6783694

Please sign in to comment.