/* ******************************************************************* * Copyright (c) 2005,2017 Contributors. * All rights reserved. * This program and the accompanying materials are made available * under the terms of the Eclipse Public License v1.0 * which accompanies this distribution and is available at * http://eclipse.org/legal/epl-v10.html * ******************************************************************/ package org.aspectj.weaver.reflect; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.net.URL; import java.net.URLClassLoader; import java.util.List; import java.util.Map; import org.aspectj.bridge.IMessageHandler; import org.aspectj.weaver.ReferenceType; import org.aspectj.weaver.ResolvedMember; import org.aspectj.weaver.ResolvedType; import org.aspectj.weaver.UnresolvedType; import org.aspectj.weaver.WeakClassLoaderReference; import org.aspectj.weaver.World; import org.aspectj.weaver.bcel.BcelWorld; import junit.framework.TestCase; /** * @author Andy Clement * @author Adrian Colyer */ public class ReflectionWorldTest extends TestCase { public void testDelegateCreation() { World world = new ReflectionWorld(getClass().getClassLoader()); ResolvedType rt = world.resolve("java.lang.Object"); assertNotNull(rt); assertEquals("Ljava/lang/Object;", rt.getSignature()); } // Removed for now. In Spring the reflection worlds are customized by introducing new // PCD handlers. It means more thought needs to be put into reusing worlds. public void xtestReflectionWorldFactory() throws Exception { ClassLoader parent = getClass().getClassLoader(); ClassLoader cl1 = new URLClassLoader(new URL[] {}, parent); ClassLoader cl2 = new URLClassLoader(new URL[] {}, parent); WeakClassLoaderReference wcl1 = new WeakClassLoaderReference(cl1); WeakClassLoaderReference wcl2 = new WeakClassLoaderReference(cl2); ReflectionWorld a = ReflectionWorld.getReflectionWorldFor(wcl1); ResolvedType stringClass1 = a.resolve(String.class); assertNotNull(stringClass1); ReflectionWorld b = ReflectionWorld.getReflectionWorldFor(wcl1); // They should be the same because the classloader has not gone away assertTrue(a==b); cl1 = null; for (int i=0;i<100;i++) { System.gc(); // How robust is it that this should be causing the reference to be collected? } b = ReflectionWorld.getReflectionWorldFor(wcl1); assertFalse(a==b); cl1 = new URLClassLoader(new URL[] {}, parent); wcl1 = new WeakClassLoaderReference(cl1); a = ReflectionWorld.getReflectionWorldFor(wcl1); b = ReflectionWorld.getReflectionWorldFor(wcl2); assertFalse(a==b); Field declaredField = ReflectionWorld.class.getDeclaredField("rworlds"); declaredField.setAccessible(true); Map worlds = (Map)declaredField.get(null); assertEquals(2, worlds.size()); cl2 = null; for (int i=0;i<100;i++) { System.gc(); // How robust is it that this should be causing the reference to be collected? } ReflectionWorld.getReflectionWorldFor(wcl1); // need to call this to trigger tidyup assertEquals(1, worlds.size()); cl1 = null; for (int i=0;i<100;i++) { System.gc(); // How robust is it that this should be causing the reference to be collected? } ReflectionWorld.getReflectionWorldFor(wcl1); // need to call this to trigger tidyup assertEquals(0, worlds.size()); cl1 = new URLClassLoader(new URL[] {}, parent); wcl1 = new WeakClassLoaderReference(cl1); ReflectionWorld reflectionWorldFor = ReflectionWorld.getReflectionWorldFor(wcl1); assertEquals(1, worlds.size()); ReflectionWorld.cleanUpWorlds(); assertEquals(0, worlds.size()); } public void testArrayTypes() { IReflectionWorld world = new ReflectionWorld(getClass().getClassLoader()); String[] strArray = new String[1]; ResolvedType rt = world.resolve(strArray.getClass()); assertTrue(rt.isArray()); } public void testPrimitiveTypes() { IReflectionWorld world = new ReflectionWorld(getClass().getClassLoader()); assertEquals("int", UnresolvedType.INT, world.resolve(int.class)); assertEquals("void", UnresolvedType.VOID, world.resolve(void.class)); } static class AbstractSuperClass {} static interface InterfaceOne {} static interface InterfaceTwo {} static class ID {} static abstract class AbstractTestClass extends AbstractSuperClass implements InterfaceOne, InterfaceTwo { } static class TestType {} // static class ConcreteClass extends AbstractTestClass { static class ConcreteClass extends AbstractTestClass> { } static class Bar extends ConcreteClass {} public void testGenerics() { ReflectionWorld world = new ReflectionWorld(getClass().getClassLoader()); // world.lookupOrCreateName(UnresolvedType.forName(AbstractTestClass.class.getName())); // ResolvedType resolvedType = world.resolve(AbstractTestClass.class); JavaLangTypeToResolvedTypeConverter converter = new JavaLangTypeToResolvedTypeConverter(world); ResolvedType resolvedType2 = converter.fromType(ConcreteClass.class); } public void testTypeConversions_509327() throws Exception { ReflectionWorld rWorld = new ReflectionWorld(getClass().getClassLoader()); JavaLangTypeToResolvedTypeConverter converter = new JavaLangTypeToResolvedTypeConverter(rWorld); // Check basic conversion of String to String Method method = TestClass.class.getDeclaredMethod("m"); Type stringType = method.getGenericReturnType(); assertEquals("java.lang.String",stringType.getTypeName()); ResolvedType stringResolvedType = converter.fromType(stringType); assertEquals("java.lang.String",stringResolvedType.getName()); // public String m() { return ""; } method = TestClass2.class.getDeclaredMethod("m"); stringType = method.getGenericReturnType(); assertEquals("java.lang.String",stringType.getTypeName()); stringResolvedType = converter.fromType(stringType); assertEquals("java.lang.String",stringResolvedType.getName()); // Verify that the conversion process creates the same thing as the bcel unpacking // Here the return type is a non-static inner of a generic class // public Inner m2() { return null; } method = TestClass2.class.getDeclaredMethod("m2"); Type innerType = method.getGenericReturnType(); assertEquals("org.aspectj.weaver.reflect.ReflectionWorldTest.org.aspectj.weaver.reflect.ReflectionWorldTest$TestClass2.Inner",innerType.getTypeName()); ResolvedType rType_Inner = converter.fromType(innerType); assertEquals("Lorg/aspectj/weaver/reflect/ReflectionWorldTest$TestClass2$Inner;",rType_Inner.getSignature()); assertEquals(UnresolvedType.TypeKind.SIMPLE,rType_Inner.getTypekind()); ResolvedType rType_Outer = rType_Inner.getOuterClass(); assertEquals("Lorg/aspectj/weaver/reflect/ReflectionWorldTest$TestClass2;",rType_Outer.getSignature()); BcelWorld bWorld = new BcelWorld(getClass().getClassLoader(), IMessageHandler.THROW, null); bWorld.setBehaveInJava5Way(true); UnresolvedType javaUtilHashMap = UnresolvedType.forName("java.util.HashMap"); ReferenceType rawType = (ReferenceType) bWorld.resolve(javaUtilHashMap); assertNotNull(rawType); // Now use bcel to resolve the same m2 method, and compare the signatures of the return types ResolvedType bResolved_TestClass2 = bWorld.resolve(UnresolvedType.forName(TestClass2.class.getName())); assertNotNull(bResolved_TestClass2); ResolvedMember bMethod_m2 = findMethod(bResolved_TestClass2,"m2"); ResolvedType bType_Inner = (ResolvedType) bMethod_m2.getReturnType(); assertEquals("Lorg/aspectj/weaver/reflect/ReflectionWorldTest$TestClass2$Inner;",bType_Inner.getSignature()); assertEquals(UnresolvedType.TypeKind.SIMPLE,bType_Inner.getTypekind()); ResolvedType bType_Outer = bType_Inner.getOuterClass(); assertEquals("Lorg/aspectj/weaver/reflect/ReflectionWorldTest$TestClass2;",bType_Outer.getSignature()); assertEquals(bType_Inner.getSignature(),rType_Inner.getSignature()); assertEquals(bType_Outer.getSignature(),rType_Outer.getSignature()); } public void testTypeConversions_509327_2() throws Exception { ReflectionWorld world = new ReflectionWorld(getClass().getClassLoader()); JavaLangTypeToResolvedTypeConverter converter = new JavaLangTypeToResolvedTypeConverter(world); BcelWorld bWorld = new BcelWorld(getClass().getClassLoader(), IMessageHandler.THROW, null); bWorld.setBehaveInJava5Way(true); // Slightly more advanced, now the method is returning a parameterized form of the outer // generic class // public TestClass2.Inner m3() { return new TestClass2.Inner("Foo"); } Method method = TestClass2.class.getDeclaredMethod("m3"); Type type_ParameterizedInner = method.getGenericReturnType(); assertEquals("org.aspectj.weaver.reflect.ReflectionWorldTest.org.aspectj.weaver.reflect.ReflectionWorldTest$TestClass2.Inner",type_ParameterizedInner.getTypeName()); ResolvedType rType_ParameterizedInner = converter.fromType(type_ParameterizedInner); // NOTE: DECLARED PARAMETERIZATION OF OUTER IS LOST assertEquals("Lorg/aspectj/weaver/reflect/ReflectionWorldTest$TestClass2$Inner;",rType_ParameterizedInner.getSignature()); ResolvedType bResolved_TestClass2 = bWorld.resolve(UnresolvedType.forName(TestClass2.class.getName())); assertNotNull(bResolved_TestClass2); ResolvedMember bMethod_m3 = findMethod(bResolved_TestClass2,"m3"); ResolvedType bType_Inner = (ResolvedType) bMethod_m3.getReturnType(); // NOTE: DECLARED PARAMETERIZATION OF OUTER IS LOST assertEquals("Lorg/aspectj/weaver/reflect/ReflectionWorldTest$TestClass2$Inner;",bType_Inner.getSignature()); assertEquals(UnresolvedType.TypeKind.SIMPLE,bType_Inner.getTypekind()); ResolvedType bType_Outer = bType_Inner.getOuterClass(); // Fields seem to lose it too, although the backinggenericmember has the info // ResolvedMember bField_f = findField(bResolved_TestClass2,"f"); // ResolvedMember backingGenericMember = bField_f.getBackingGenericMember(); // System.out.println(backingGenericMember); // System.out.println(backingGenericMember.getGenericReturnType()); // System.out.println(bField_f); // System.out.println(bField_f.getSignature()); // System.out.println(bField_f.getGenericReturnType()); } // public void testbar() throws Exception { // ReflectionWorld world = new ReflectionWorld(getClass().getClassLoader()); // JavaLangTypeToResolvedTypeConverter converter = new JavaLangTypeToResolvedTypeConverter(world); // // // public TestClass2.Inner m3() { return new TestClass2.Inner("Foo"); } // Method method = TestClass2.class.getDeclaredMethod("m3"); // Type type_ParameterizedInner = method.getGenericReturnType(); // assertEquals("org.aspectj.weaver.reflect.ReflectionWorldTest.org.aspectj.weaver.reflect.ReflectionWorldTest$TestClass2.Inner",type_ParameterizedInner.getTypeName()); // ResolvedType rType_ParameterizedInner = converter.fromType(type_ParameterizedInner); // System.out.println(rType_ParameterizedInner); // System.out.println(type_ParameterizedInner.getTypeName()); // } // // public void testfoo() { // ReflectionWorld world = new ReflectionWorld(getClass().getClassLoader()); // JavaLangTypeToResolvedTypeConverter converter = new JavaLangTypeToResolvedTypeConverter(world); // BcelWorld bWorld = new BcelWorld(getClass().getClassLoader(), IMessageHandler.THROW, null); // bWorld.setBehaveInJava5Way(true); // // // ResolvedType bResolved_TestClass2 = bWorld.resolve(UnresolvedType.forName(TestClass2.class.getName())); // ResolvedMember bField_f = findField(bResolved_TestClass2,"f"); // System.out.println(bField_f); // System.out.println(bField_f.getGenericReturnType()); // System.out.println(bField_f.getReturnType()); // System.out.println(bField_f.getBackingGenericMember().getGenericReturnType()); // } static class TestClass { public String m() { return ""; } } static class TestClass2 { class Inner { T t; Inner(T t) { this.t = t; } } public String m() { return ""; } public Inner m2() { return null; } public TestClass2 f; public TestClass2.Inner m3() { return new TestClass2.Inner("Foo"); } } private ResolvedMember findMethod(ResolvedType resolvedType, String methodName) { for (ResolvedMember method: resolvedType.getDeclaredMethods()) { if (method.getName().equals(methodName)) { return method; } } return null; } private ResolvedMember findField(ResolvedType resolvedType, String fieldName) { for (ResolvedMember field: resolvedType.getDeclaredFields()) { if (field.getName().equals(fieldName)) { return field; } } return null; } }