diff --git a/Service/Sources/EDOInvocationMessage.m b/Service/Sources/EDOInvocationMessage.m index b3cf94d..b30ce14 100644 --- a/Service/Sources/EDOInvocationMessage.m +++ b/Service/Sources/EDOInvocationMessage.m @@ -279,9 +279,20 @@ + (instancetype)requestWithInvocation:(NSInvocation *)invocation value = objRef ? BOX_VALUE(*objRef, target, service, nil) : [EDOBoxedValueType parameterForDoublePointerNullValue]; } else if (EDO_IS_POINTER(ctype)) { - // TODO(haowoo): Add the proper error and/or exception handler. - NSAssert(NO, @"Not supported type (%s) in the argument for selector (%@).", ctype, - selector ? NSStringFromSelector(selector) : @"(block)"); + void *objRef; + [invocation getArgument:&objRef atIndex:i]; + + // Don't assert if the pointer is NULL. The purpose for disallowing non-Objective-C pointer + // parameters is because there's no way to know how big a C pointer's underlying data is. But + // if the pointer is NULL, then there's nothing to pass, so just pass NULL and don't throw an + // exception. This opens up partial support for key-value observing and other APIs that take + // optional context pointers (so long as the caller doesn't provide one). + if (objRef != NULL) { + // TODO(haowoo): Add the proper error and/or exception handler. + NSAssert(NO, @"Not supported type (%s) in argument %@ for selector (%@).", ctype, @(i), + selector ? NSStringFromSelector(selector) : @"(block)"); + } + value = [EDOBoxedValueType parameterForNilValue]; } else { NSUInteger typeSize = 0L; NSGetSizeAndAlignment(ctype, &typeSize, NULL); @@ -345,7 +356,8 @@ + (EDORequestHandler)requestHandler { NSAssert(EDO_IS_OBJPOINTER(ctype) || (EDO_IS_OBJECT(ctype) && EDO_IS_OBJECT(argument.objCType)) || (EDO_IS_CLASS(ctype) && EDO_IS_OBJECT(argument.objCType)) || - strcmp(ctype, argument.objCType) == 0, + strcmp(ctype, argument.objCType) == 0 || + (EDO_IS_POINTER(ctype) && argument.value == NULL), @"The argument type is not matched (%s : %s).", ctype, argument.objCType); if (EDO_IS_OBJPOINTER(ctype)) { diff --git a/Service/Tests/TestsBundle/EDOTestDummy.h b/Service/Tests/TestsBundle/EDOTestDummy.h index 1add02d..d9bd3ff 100644 --- a/Service/Tests/TestsBundle/EDOTestDummy.h +++ b/Service/Tests/TestsBundle/EDOTestDummy.h @@ -66,6 +66,7 @@ typedef EDOTestDummy * (^EDOMultiTypesHandler)(EDOTestDummyStruct, int, id, EDOT - (void)voidWithOutObject:(EDOTestDummy **)dummyOut; - (void)voidWithValue:(int)value outSelf:(EDOTestDummy **)dummyOut; - (void)voidWithProtocol:(Protocol *)protocol; +- (void)voidWithNullCPointer:(void *)cPointer; /// no parameters with returns of different types - (int)returnInt; diff --git a/Service/Tests/TestsBundle/EDOTestDummy.m b/Service/Tests/TestsBundle/EDOTestDummy.m index 810cb46..cbdf1a3 100644 --- a/Service/Tests/TestsBundle/EDOTestDummy.m +++ b/Service/Tests/TestsBundle/EDOTestDummy.m @@ -144,6 +144,12 @@ - (void)voidWithProtocol:(Protocol *)protocol { // Do nothing. } +- (void)voidWithNullCPointer:(void *)cPointer { + // This can be called remotely, but only with NULL pointer values. + NSAssert(cPointer == NULL, + @"It should not be possible to pass a non-NULL value to this parameter."); +} + - (EDOTestDummyStruct)returnStructWithBlockStret:(EDOTestDummyStruct (^)(void))block { return block(); } diff --git a/Service/Tests/UnitTests/EDOServiceTest.m b/Service/Tests/UnitTests/EDOServiceTest.m index 9dbe3dd..b3fcb40 100644 --- a/Service/Tests/UnitTests/EDOServiceTest.m +++ b/Service/Tests/UnitTests/EDOServiceTest.m @@ -519,6 +519,7 @@ - (void)testVoidReturnWithParametersOfDifferentKinds { XCTAssertNoThrow([dummyOnBackground voidWithBlock:nil]); XCTAssertNoThrow([dummyOnBackground voidWithBlock:^{ }]); + XCTAssertNoThrow([dummyOnBackground voidWithNullCPointer:NULL]); } - (void)testOutParameterCanResolveToLocalWhenDereferencing {