Bug 119353 - Inconsistent Implementations of ReferenceType.getDeclaredMethods()
Summary: Inconsistent Implementations of ReferenceType.getDeclaredMethods()
Status: RESOLVED FIXED
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: DEVELOPMENT   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: 1.5.0RC1   Edit
Assignee: Andrew Clement CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-12-05 23:11 EST by Ron Bodkin CLA
Modified: 2005-12-09 09:02 EST (History)
0 users

See Also:


Attachments
Patch to the delegates (1.5 and < 1.5) (1.74 KB, application/octet-stream)
2005-12-08 10:53 EST, Andrew Clement CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Ron Bodkin CLA 2005-12-05 23:11:15 EST
I am running into a problem in load-time weaving when I use reflection delegates because the weaver is generating an incorrect bridge method when I perform an inter-type declaration on Struts.ActionServlet for the init method. On investigation, the BCEL world is recognizing this as an overriding method, because its version of getDeclaredMethods is returning the declared methods for all ancestor superclasses. However, the reflection world is returning only the declared methods for this one class. It therefore appears that the weaver expects getDeclaredMethods to return all of them (making the name quite misleading). I think previously the method was being implemented inconsistently between 1.5 reflection and <1.5 reflection delegates. But it looks like it needs to be handled consistently to include all superclass methods.

However, I don’t know what other places (e.g., the MAP) are really expecting getDeclaredMethods and its siblings to behave like Java reflection's version…

I started work on adding a getAllDeclaredMethods method to ReferenceType and delegates, as an alternative to provide the weaver the ability to check method overriding as in this case, but it's a little bit involved and I wanted to flag the issue first.

Here's a test that fails and illustrates the issue:
Index: ReflectionBasedReferenceTypeDelegateTest.java
===================================================================
RCS file: /home/technology/org.aspectj/modules/weaver/testsrc/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegateTest.java,v
retrieving revision 1.5
diff -u -r1.5 ReflectionBasedReferenceTypeDelegateTest.java
--- ReflectionBasedReferenceTypeDelegateTest.java	28 Nov 2005 17:44:40 -0000	1.5
+++ ReflectionBasedReferenceTypeDelegateTest.java	6 Dec 2005 04:11:41 -0000
@@ -238,6 +238,18 @@
         assertTrue("Superclass for Map generic type should be Object but was "+rt2,rt2.equals(UnresolvedType.OBJECT));         
     }
     
+    public void testCompareSubclassDelegates() {
+        world.setBehaveInJava5Way(true);
+        
+        BcelWorld bcelWorld = new BcelWorld();
+        bcelWorld.setBehaveInJava5Way(true);
+        UnresolvedType javaUtilHashMap = UnresolvedType.forName("java.util.HashMap");
+        ReferenceType rawType = (ReferenceType)bcelWorld.resolve(javaUtilHashMap );
+        
+        ReferenceType rawReflectType = (ReferenceType)world.resolve(javaUtilHashMap );
+        assertEquals(rawType.getDelegate().getDeclaredMethods().length, rawReflectType.getDelegate().getDeclaredMethods().length);
+    }
+    
 	// todo: array of int	
 
 	protected void setUp() throws Exception {


This results in:
junit.framework.AssertionFailedError: expected:<41> but was:<29>
	at junit.framework.Assert.fail(Assert.java:47)
	at junit.framework.Assert.failNotEquals(Assert.java:282)
	at junit.framework.Assert.assertEquals(Assert.java:64)
	at junit.framework.Assert.assertEquals(Assert.java:201)
	at junit.framework.Assert.assertEquals(Assert.java:207)
	at org.aspectj.weaver.reflect.ReflectionBasedReferenceTypeDelegateTest.testCompareSubclassDelegates(ReflectionBasedReferenceTypeDelegateTest.java:250)
	at java.lang.reflect.Method.invoke(Native Method)
	at junit.framework.TestCase.runTest(TestCase.java:154)
	at junit.framework.TestCase.runBare(TestCase.java:127)
	at junit.framework.TestResult$1.protect(TestResult.java:106)
	at junit.framework.TestResult.runProtected(TestResult.java:124)
	at junit.framework.TestResult.run(TestResult.java:109)
	at junit.framework.TestCase.run(TestCase.java:118)
	at junit.framework.TestSuite.runTest(TestSuite.java:208)
	at junit.framework.TestSuite.run(TestSuite.java:203)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:478)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:344)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Comment 1 Ron Bodkin CLA 2005-12-05 23:12:13 EST
Two small clarifications: the incorrect bridge method results in a VerifyError. If I use only BCEL delegates then the system behaves properly.
Comment 2 Andrew Clement CLA 2005-12-08 10:45:12 EST
I modified the test program to output what the differences are between the two sets.  Interesting results:

Couldn't find java.util.HashMap java.util.HashMap.init(int) in the bcel set
Couldn't find java.util.HashMap java.util.HashMap.init() in the bcel set
Couldn't find java.util.HashMap java.util.HashMap.init(java.util.Map) in the bcel set
Couldn't find java.util.HashMap java.util.HashMap.init(int, float) in the bcel set
Couldn't find void java.util.HashMap.<init>(int, float) in the reflection set
Couldn't find void java.util.HashMap.<init>(int) in the reflection set
Couldn't find void java.util.HashMap.<init>() in the reflection set
Couldn't find void java.util.HashMap.<init>(java.util.Map) in the reflection set
Couldn't find void java.util.HashMap.<clinit>() in the reflection set

I don't like the disagreement between the 'init' and '<init>' strings.  I suspect that is the root of this problem.  I'm not sure about the <clinit> - but at least we know that can't have an impact on bridge methods ;)

I'm not sure how this quite ties up with Rons description that getDeclaredMethods() is differing in each case as the initial fix here just seems to be transforming the reflection based entries for ctors to indicate <init>...
Comment 3 Andrew Clement CLA 2005-12-08 10:53:18 EST
Created attachment 31379 [details]
Patch to the delegates (1.5 and < 1.5)

Ron ... this zip contains two patches, one for the weaver project, one for weaver5 - can you try it out and see if it helps your situation with the rogue bridge method?  Basically I've made the reflection world return the same thing as the bcel world.  The test I modified still fails because of the <clinit> method but that shouldnt impact bridge method creation.
Comment 4 Andrew Clement CLA 2005-12-08 10:57:49 EST
Hmmm .. I just re-read the original append by Ron.

In my case I wasn't getting 41 and 29, I was getting 41 and 40.

perhaps I'm not running it in quite the same way - i'll keep hacking away - but if you could try the patch it might help with the bridge problem.
Comment 5 Ron Bodkin CLA 2005-12-08 23:18:53 EST
I will try the patches and report back...

On closer examination, the 29 vs 41 methods I was seeing was on a Java 1.3.1 VM but running in Eclipse where the Java 1.5 JRE was on the classpath and BCEL was using that to resolve types (!). When corrected to use a 1.3 JRE in Eclipse, I see 29 vs 30 methods on a Java 1.3 VM and I always saw 40 vs 41 on a Java 1.5 VM. Hopefully this test will show 29 vs 30 when run via ant (the docs on how to run ant tests for a module from the command line aren't working for me...)

Reflect:
put, clone, get, access$000, access$100, values, size, access$200, clear, remove, access$300, loadFactor, keySet, entrySet, isEmpty, containsValue, containsKey, rehash, putAll, writeObject, readObject, capacity, getHashIterator, access$308, access$110, init, init, init, init
BCEL:
<init>, <init>, <init>, <init>, size, isEmpty, containsValue, containsKey, get, rehash, put, remove, putAll, clear, clone, keySet, values, entrySet, getHashIterator, writeObject, readObject, capacity, loadFactor, access$000, access$100, access$200, access$308, access$110, access$300, <clinit>
Comment 6 Ron Bodkin CLA 2005-12-08 23:51:02 EST
It looks like those patches fix the problem (although I had to patch the patches to use an IReflectionWorld interface that my LTWWorld implementation also implements :-)). I am not seeing the same error messages and things appear to be working properly on a quick run. Thanks!
Comment 7 Andrew Clement CLA 2005-12-09 03:39:40 EST
fixes checked in - thanks for trying them out Ron. waiting on build.
Comment 8 Andrew Clement CLA 2005-12-09 03:41:26 EST
oops - closed prematurely.  i'm really waiting on the build...
Comment 9 Andrew Clement CLA 2005-12-09 09:02:37 EST
fix available.