Javassist gets confused by certain synthetic bridges, produces
invalid bytecode
-------------------------------------------------------------------------------
Key: JASSIST-250
URL:
https://issues.jboss.org/browse/JASSIST-250
Project: Javassist
Issue Type: Bug
Affects Versions: 3.20.0-GA
Environment: Ubuntu 15.04, JDK 1.8.0_51.
Reporter: Jari Juslin
Assignee: Shigeru Chiba
Fix For: 3.21.0-GA
Attachments: Bar.java, BaseFoo.java, Foo.java, IBarProvider.java,
IExpirationProvider.java, JavassistSyntheticBridgeTest.java
Javassist gets confused by synthetic bridge methods javac has implicitly created and then
creates method calls to the bytecode that don't pass the JVM verify stage.
The situation here is that we have a class Foo, that has superclass BaseFoo implementing
an interface IBarProvider. BaseFoo has method Bar getBar(), which is also in the
IBarProvider. The IBarProvider defines the method as IExpirationProvider getBar(), where
IExpirationProvider is an interface implemented by Bar.
Now, for some reason I am not completely sure about, javac creates a synthetic bridge
method to Foo class with erasure of IExpirationProvider getBar which then just passes the
call to the Bar BaseFoo.getBar. When Javassist load the class, it misses the fact that the
superclass contains method with same name and wider return scope and instead calls the Foo
level method also in cases where Bar is needed, not just IExpirationProvider. If it just
ignored the synthetic bridge methods altogether, this would work as expected and as Oracle
javac handles it.
The resulting error:
Exception in thread "main" java.lang.VerifyError: Bad return type
Exception Details:
Location:
InstrumentedClass.getBar()LBar; @4: areturn
Reason:
Type 'IExpirationProvider' (current frame, stack[0]) is not assignable to
'Bar' (from method signature)
Current Frame:
bci: @4
flags: { }
locals: { 'InstrumentedClass' }
stack: { 'IExpirationProvider' }
Bytecode:
0x0000000: 2ab7 000b b0
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
at java.lang.Class.getConstructor0(Class.java:3075)
at java.lang.Class.getConstructor(Class.java:1825)
at JavassistSyntheticBridgeTest.main(JavassistSyntheticBridgeTest.java:19)