Skip to content

Fix bridge type erasure #89

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

Closed
Closed
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
2 changes: 1 addition & 1 deletion cglib-integration-test/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<parent>
<groupId>cglib</groupId>
<artifactId>cglib-parent</artifactId>
<version>3.2.5-SNAPSHOT</version>
<version>3.2.5-GREG-SNAPSHOT</version>
</parent>

<!-- ====================================================================== -->
Expand Down
2 changes: 1 addition & 1 deletion cglib-jmh/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<artifactId>cglib-parent</artifactId>
<groupId>cglib</groupId>
<version>3.2.5-SNAPSHOT</version>
<version>3.2.5-GREG-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

Expand Down
2 changes: 1 addition & 1 deletion cglib-nodep/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<parent>
<groupId>cglib</groupId>
<artifactId>cglib-parent</artifactId>
<version>3.2.5-SNAPSHOT</version>
<version>3.2.5-GREG-SNAPSHOT</version>
</parent>

<!-- ====================================================================== -->
Expand Down
2 changes: 1 addition & 1 deletion cglib-sample/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<parent>
<groupId>cglib</groupId>
<artifactId>cglib-parent</artifactId>
<version>3.2.5-SNAPSHOT</version>
<version>3.2.5-GREG-SNAPSHOT</version>
</parent>

<!-- ====================================================================== -->
Expand Down
2 changes: 1 addition & 1 deletion cglib/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<parent>
<groupId>cglib</groupId>
<artifactId>cglib-parent</artifactId>
<version>3.2.5-SNAPSHOT</version>
<version>3.2.5-GREG-SNAPSHOT</version>
</parent>

<!-- ====================================================================== -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public BridgeMethodResolver(Map declToBridge) {
Set bridges = (Set)entry.getValue();
try {
new ClassReader(owner.getName())
.accept(new BridgedFinder(bridges, resolved),
.accept(new BridgedFinder(bridges, resolved, owner.getName()),
ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG);
} catch(IOException ignored) {}
}
Expand All @@ -71,11 +71,13 @@ private static class BridgedFinder extends ClassVisitor {
private Set/*<Signature>*/ eligableMethods;

private Signature currentMethod = null;
private String name;

BridgedFinder(Set eligableMethods, Map resolved) {
BridgedFinder(Set eligableMethods, Map resolved, String name) {
super(Opcodes.ASM5);
this.resolved = resolved;
this.eligableMethods = eligableMethods;
this.name = name;
}

public void visit(int version, int access, String name,
Expand All @@ -85,11 +87,14 @@ public void visit(int version, int access, String name,
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
Signature sig = new Signature(name, desc);

final String selfName = this.name;
if (eligableMethods.remove(sig)) {
currentMethod = sig;
return new MethodVisitor(Opcodes.ASM5) {
public void visitMethodInsn(int opcode, String owner, String name,
String desc, boolean itf) {

if (opcode == Opcodes.INVOKESPECIAL && currentMethod != null) {
Signature target = new Signature(name, desc);
// If the target signature is the same as the current,
Expand Down
29 changes: 29 additions & 0 deletions cglib/src/main/java/net/sf/cglib/proxy/Enhancer.java
Original file line number Diff line number Diff line change
Expand Up @@ -1162,6 +1162,35 @@ public void emitInvoke(CodeEmitter e, MethodInfo method) {
// any proxies on the target.
Signature bridgeTarget = (Signature)bridgeToTarget.get(method.getSignature());
if (bridgeTarget != null) {
Type[] argumentTypes = method.getSignature().getArgumentTypes();
Type[] bridgeTypes = bridgeTarget.getArgumentTypes();

// This should never happen, but its a simple check
if (argumentTypes.length != bridgeTarget.getArgumentTypes().length) {
throw new IllegalStateException("Mismatched method signatures?");
}

ClassLoader loader = Thread.currentThread().getContextClassLoader();

try {
for (int i = 0; i < argumentTypes.length; i++) {
Type argType = argumentTypes[i];
Type targetType = bridgeTypes[i];
// This should be a fast check
if (!argType.getClassName().equals(targetType.getClassName())) {
Class argClass = loader.loadClass(argType.getClassName());
Class targetClass = loader.loadClass(targetType.getClassName());

if (argClass == Object.class && targetClass != Object.class) {
e.super_invoke(method.getSignature());
return;
}
}
}
} catch (ClassNotFoundException ignored) {
e.super_invoke(method.getSignature());
}

// TODO: this assumes that the target has wider or the same type
// parameters than the current.
// In reality this should always be true because otherwise we wouldn't
Expand Down
47 changes: 46 additions & 1 deletion cglib/src/test/java/net/sf/cglib/proxy/TestEnhancer.java
Original file line number Diff line number Diff line change
Expand Up @@ -1174,7 +1174,38 @@ public int accept(Method method) {
intf.widenReturn(null);
assertEquals(Arrays.asList(RetType.class, ErasedType.class), paramTypes);
}


public void testBridgesTypeErasedFromChildToParent() {
List<Class> retTypes = new ArrayList<Class>();
List<Class> paramTypes = new ArrayList<Class>();
Interceptor interceptor = new Interceptor(retTypes, paramTypes);

Enhancer e = new Enhancer();
e.setSuperclass(OtherImpl.class);
e.setCallbackFilter(new CallbackFilter() {
public int accept(Method method) {
return method.getDeclaringClass() != Object.class ? 0 : 1;
}
});
e.setCallbacks(new Callback[] { interceptor, NoOp.INSTANCE });
// We expect the bridge ('ret') to be called & forward us to the non-bridge 'erased'
ParameterisedInterfaceNotTypeBounded intf =
(ParameterisedInterfaceNotTypeBounded) e.create();
intf.aMethod(new Refined());
// There is no actual implementation of aMethod in the subclass, however it has a bridge going
// Object -> superclass with a cast
assertEquals(Arrays.asList(Object.class), retTypes);

// The bridge internally emits a checkclass, if it does not cglib should emit this.
boolean fail = true;
try {
intf.aMethod("String");
} catch (ClassCastException cce) {
fail = false;
}
assertFalse("Bridge did not emit checkclass instruction", fail);
}

public void testBridgeForcesInvokeVirtualEvenWithoutInterceptingBridge() {
List<Class> retTypes = new ArrayList<Class>();
Interceptor interceptor = new Interceptor(retTypes);
Expand Down Expand Up @@ -1280,11 +1311,25 @@ public interface Interface { // the usage of the interface forces the bridge
// a wider type than in superclass
ErasedType widenReturn(RetType obj);
}
// This has a shape that fits the typing of the class heirachy, but the generic T
// is not bound to ErasedType directly. It creates bridge methods that
// given an abstract parent, will need to be ignored for creation of
// invokevirtual
public interface ParameterisedInterfaceNotTypeBounded<T> {
T aMethod(T obj);
void voidReturn(T obj);
int intReturn(T obj);
// a wider type than in superclass
ErasedType widenReturn(T obj);
}
public static class Impl extends Superclass<RetType> implements Interface {
// An even more narrowed type, just to make sure
// it doesn't confuse us.
public Refined aMethod(Refined obj) { return null; }
}
public static class OtherImpl extends Superclass<Refined> implements ParameterisedInterfaceNotTypeBounded<Refined> {
public String irrelvantMethod() { return "Not important"; }
}

// Another set of classes -- this time with the bridging in reverse,
// to make sure that if we define the concrete type, a bridge
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<!-- ====================================================================== -->
<groupId>cglib</groupId>
<artifactId>cglib-parent</artifactId>
<version>3.2.5-SNAPSHOT</version>
<version>3.2.5-GREG-SNAPSHOT</version>
<packaging>pom</packaging>

<name>Code Generation Library</name>
Expand Down