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 {