mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-03-26 07:04:05 +08:00
NSNumber arguments must now be nonnull
Summary: The bridge implementation on React Android does not currently support boxed numeric/boolean types (the equivalent of NSNumber arguments on iOS), nor does Java support Objective-C's nil messaging system that transparently casts nil to zero, false, etc for primitive types. To avoid platform incompatibilities, we now treat all primitive arguments as non-nullable rather than silently converting NSNull -> nil -> 0/false. We also now enforce that NSNumber * objects must be explicitly marked as `nonnull` (this restriction may be lifted in future if/when Android supports boxed numbers). Other object types are still assumed to be nullable unless specifically annotated with `nonnull`.
This commit is contained in:
@@ -128,4 +128,17 @@ extern void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **argTypes
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"BOOL");
|
||||
}
|
||||
|
||||
- (void)testUnused
|
||||
{
|
||||
NSArray *arguments;
|
||||
NSString *methodName = @"foo:(__unused NSString *)foo bar:(NSNumber *)bar";
|
||||
RCTParseObjCMethodName(&methodName, &arguments);
|
||||
XCTAssertEqualObjects(methodName, @"foo:bar:");
|
||||
XCTAssertEqual(arguments.count, (NSUInteger)2);
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSString");
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"NSNumber");
|
||||
XCTAssertTrue(((RCTMethodArgument *)arguments[0]).unused);
|
||||
XCTAssertFalse(((RCTMethodArgument *)arguments[1]).unused);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -18,6 +18,18 @@
|
||||
#import "RCTModuleMethod.h"
|
||||
#import "RCTLog.h"
|
||||
|
||||
static BOOL RCTLogsError(void (^block)(void))
|
||||
{
|
||||
__block BOOL loggedError = NO;
|
||||
RCTPerformBlockWithLogFunction(block, ^(RCTLogLevel level,
|
||||
__unused NSString *fileName,
|
||||
__unused NSNumber *lineNumber,
|
||||
__unused NSString *message) {
|
||||
loggedError = (level == RCTLogLevelError);
|
||||
});
|
||||
return loggedError;
|
||||
}
|
||||
|
||||
@interface RCTModuleMethodTests : XCTestCase
|
||||
|
||||
@end
|
||||
@@ -32,31 +44,59 @@
|
||||
RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName
|
||||
JSMethodName:nil
|
||||
moduleClass:[self class]];
|
||||
XCTAssertFalse(RCTLogsError(^{
|
||||
[method invokeWithBridge:nil module:self arguments:@[@"Hello World"]];
|
||||
}));
|
||||
|
||||
XCTAssertTrue(RCTLogsError(^{
|
||||
[method invokeWithBridge:nil module:self arguments:@[[NSNull null]]];
|
||||
}));
|
||||
}
|
||||
|
||||
- (void)doFooWithNumber:(__unused NSNumber *)n { }
|
||||
- (void)doFooWithDouble:(__unused double)n { }
|
||||
- (void)doFooWithInteger:(__unused NSInteger)n { }
|
||||
|
||||
- (void)testNumbersNonnull
|
||||
{
|
||||
{
|
||||
__block BOOL loggedError = NO;
|
||||
RCTPerformBlockWithLogFunction(^{
|
||||
[method invokeWithBridge:nil module:self arguments:@[@"Hello World"]];
|
||||
}, ^(RCTLogLevel level,
|
||||
__unused NSString *fileName,
|
||||
__unused NSNumber *lineNumber,
|
||||
__unused NSString *message) {
|
||||
loggedError = (level == RCTLogLevelError);
|
||||
});
|
||||
XCTAssertFalse(loggedError);
|
||||
// Specifying an NSNumber param without nonnull isn't allowed
|
||||
XCTAssertTrue(RCTLogsError(^{
|
||||
NSString *methodName = @"doFooWithNumber:(NSNumber *)n";
|
||||
(void)[[RCTModuleMethod alloc] initWithObjCMethodName:methodName
|
||||
JSMethodName:nil
|
||||
moduleClass:[self class]];
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
__block BOOL loggedError = NO;
|
||||
RCTPerformBlockWithLogFunction(^{
|
||||
NSString *methodName = @"doFooWithNumber:(nonnull NSNumber *)n";
|
||||
RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName
|
||||
JSMethodName:nil
|
||||
moduleClass:[self class]];
|
||||
XCTAssertTrue(RCTLogsError(^{
|
||||
[method invokeWithBridge:nil module:self arguments:@[[NSNull null]]];
|
||||
}, ^(RCTLogLevel level,
|
||||
__unused NSString *fileName,
|
||||
__unused NSNumber *lineNumber,
|
||||
__unused NSString *message) {
|
||||
loggedError = (level == RCTLogLevelError);
|
||||
});
|
||||
XCTAssertTrue(loggedError);
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
NSString *methodName = @"doFooWithDouble:(double)n";
|
||||
RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName
|
||||
JSMethodName:nil
|
||||
moduleClass:[self class]];
|
||||
XCTAssertTrue(RCTLogsError(^{
|
||||
[method invokeWithBridge:nil module:self arguments:@[[NSNull null]]];
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
NSString *methodName = @"doFooWithInteger:(NSInteger)n";
|
||||
RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName
|
||||
JSMethodName:nil
|
||||
moduleClass:[self class]];
|
||||
XCTAssertTrue(RCTLogsError(^{
|
||||
[method invokeWithBridge:nil module:self arguments:@[[NSNull null]]];
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user