iOS13 PostFix #1: Generic class arguments and @Block parameters
19 Oct 2019 | fix compiler postfixiOS13 bindings are complete but CocoaTouch related issues keep arriving, part of them about compiler not able to compile one or other class. Most of these issues can be detected just by compiling entire CocoaTouch library as simple smock test to find out outstanding issues. This can be done by force linking its class path with template like this:
<forceLinkClasses>
<pattern>org.robovm.apple.**</pattern>
</forceLinkClasses>
This post start series of fixes discovered during compilation of CocoaTouch library.
PostFix #1: Generic class arguments and @Block parameters
Other postfixes:
- PostFix #2: Adding support for non-static @Bridge method in enums classes
- PostFix #3: Support for @Block member in structs
- PostFix #4: Compilation failed on @Bridge annotate covariant return synthetic method
- PostFix #5: Support for Struct.offsetOf in structs
- PostFix #6: Fixes to Network framework bindings
- PostFix #7: bindings for ios13.2
- PostFix #8: workaround for missing objc classes(ObjCClassNotFoundException)
- PostFix #9: experimental and formal bitcode support
- PostFix #10: glkit – missing functions (static inline now)
What is wrong
Issue was detected while compility NSOrderedCollectionDifference.getDifferenceByTransformingChanges. It failed with following exception:
org.robovm.compiler.CompilerException: Unresolved type variable T in parameter 1 of @Block method
Setups
All tests were perfromed by modifying ObjCBlockPluginTest.java as it provides quick run experience. For test following block interface will be used:
public interface Block<R, A, B> {
R run(A a, B b);
}
Case 1, reproducing CocoaTouch issue
Attempt to use following generic class with block argument…
public static class BlockTest<T> {
public native void foo(Block<T, Integer, Float> b);
}
Cause exception:
org.robovm.compiler.CompilerException: Unresolved type variable T in return type of @Block method
(Strange) By the way, such cace is expected in UT testResolveTargetMethodSignatureGenericWithUnresolvedIndirectTypeVariable
.
But…
Case 2
Changing a generic class argument name (ONLY!) fixes the exception from case 1.
public static class BlockTest<R> {
public native void foo(Block<R, Integer, Float> b);
}
Case 3 – more bugs
Swaping type arguments…
public static class BlockTest<R, A, B> {
public native void foo(Block<R, B, A> b);
}
causes recursion loop and stack overflow exception in result:
java.lang.StackOverflowError at org.robovm.compiler.plugin.objc.ObjCBlockPlugin.resolveMethodType(ObjCBlockPlugin.java:903) at org.robovm.compiler.plugin.objc.ObjCBlockPlugin.resolveMethodType(ObjCBlockPlugin.java:920)
Case 4 – more bugs2
Naming class arguments same to Block arguments casuses in wrong type resolution:
public static class BlockTest<R, B extends Integer> {
public native void foo(Block<R, B, Float> b);
}
results in following wrong block:
public interface Block<R, A, B> {
java.lang.Object run(java.lang.Float a, java.lang.Float b);
}
Root case
Root case is ObjCBlockPlugin.resolveMethodType
method. It take folowing arguments for class BlockTest
(Case 1) and Block
(Setups):
Type t: R
Type[] resolvedArgs: [T extends Object (TypeVariable), Integer, FLoat]
TypeVariable<?>[] typeParameters: [R, A, B]
Problem here is that it tries to resolve all TypeVariable types using following scheme:
- find parameter index in
typeParameters
by name – 0 in this case; - get its value from type parameters:
t = typeParameters[index]
- T in this case; - resolve this value by recursively calling
resolveMethodType(t)
- fill fail as there is no T intypeParameters:
[R, A, B]
And problems here:
- it can’t resolve any argument parameter, other than in typeParameters (but its wrong by itself);
- even if name matches – it will pick argument by typeParameters.indexOf(t.name) which will cause resolution of not related type, and wrong result (case 4);
- several arguments name matche but their position doesn’t match ones in typeParameter this will cause recursion and StackOverflow exception;
The fix
Idea of fix – is not have any TypeVariable in resovedArgs. For this all resolved arguments are initialy being resolved to their java bounds, as result parameter types resolveMethodType
will not recursively resolve TypeVariable. But insted if TypeVariable is passed it expects it to be one of typeParameters and return already resoved value from resolvedArgs by it index. Otherwise CompilerException will be thrown.
Comments