Don't prevent passing NULL to non-Objective-C pointer parameters.
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).
PiperOrigin-RevId: 388276778
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 @@
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 @@
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 @@
- (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 @@
// 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 @@
XCTAssertNoThrow([dummyOnBackground voidWithBlock:nil]);
XCTAssertNoThrow([dummyOnBackground voidWithBlock:^{
}]);
+ XCTAssertNoThrow([dummyOnBackground voidWithNullCPointer:NULL]);
}
- (void)testOutParameterCanResolveToLocalWhenDereferencing {