Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

* fixed: ObjCClassNotFoundException when marshaling protocols implemented in pure Swift classes #784

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions compiler/objc/src/main/java/org/robovm/objc/ObjCClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,12 @@ public static ObjCClass getFromObject(ObjCObject id) {
}
return getByType(id.getClass());
}

public static ObjCClass getFromObject(long handle) {
return getFromObject(handle, false);
}

public static ObjCClass getFromObject(long handle, boolean optional) {
long classPtr = ObjCRuntime.object_getClass(handle);
// dkimitsa. There is a bug observed in iOS12 that causes not all Objective-C class fields properly initialized
// in Class instance of Swift classes. This causes a crash in APIs like class_copyProtocolList to crash
Expand All @@ -260,7 +264,7 @@ public static ObjCClass getFromObject(long handle) {
if (classPtr != 0 && ObjCRuntime.class_respondsToSelector(classPtr, SELECTOR_NSOBJECT_CLASS.getHandle())) {
classPtr = ObjCRuntime.ptr_objc_msgSend(handle, SELECTOR_NSOBJECT_CLASS.getHandle());
}
return toObjCClass(classPtr);
return toObjCClass(classPtr, optional);
}

public static ObjCClass getByType(Class<? extends ObjCObject> type) {
Expand Down Expand Up @@ -328,6 +332,10 @@ private static List<String> getProtocols(long handle, boolean isProtocol) {
}

public static ObjCClass toObjCClass(final long handle) {
return toObjCClass(handle, false);
}

public static ObjCClass toObjCClass(final long handle, final boolean optional) {
long classPtr = handle;
ObjCClass c = ObjCObject.getPeerObject(classPtr);
if (c == null) {
Expand Down Expand Up @@ -358,7 +366,7 @@ public static ObjCClass toObjCClass(final long handle) {
}
}
}
if (c == null) {
if (c == null && !optional) {
String name = VM.newStringUTF(ObjCRuntime.class_getName(handle));
throw new ObjCClassNotFoundException("Could not find Java class corresponding to Objective-C class: " + name);
}
Expand Down
12 changes: 10 additions & 2 deletions compiler/objc/src/main/java/org/robovm/objc/ObjCObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,16 @@ public static <T extends ObjCObject> T toObjCObject(Class<T> cls, long handle, i
}
}

ObjCClass objCClass = ObjCClass.getFromObject(handle);
if (!expectedType.isAssignableFrom(objCClass.getType())) {
// dkimitsa: when ObjCProxy is a target at java level it expected to return Interface/Protocol implementation
// But not always is possible to recognizable ObjC object implementing the protocol behind the handle.
// For example in case protocol is implemented by pure Swift object.
// In this cause case ObjCClass might not be resolved (cause ObjCClassNotFoundException)
// or not resolve to one that implement the protocol (not isAssignableFrom).
// To workaround -- allow getFromObject to be optional and return Null.
// in this case objCClass will be resolved using getByType() from provided $ObjCProxy
// it's the case when proper ObjC object that implement the protocol can't be identified
ObjCClass objCClass = ObjCClass.getFromObject(handle, expectedType != cls);
if (objCClass == null || !expectedType.isAssignableFrom(objCClass.getType())) {
/*
* If the expected return type is incompatible with the type of
* the native instance we have to make sure we return an
Expand Down
Loading