--- /dev/null
+public class Sub4 {
+ public Integer m() {return new Integer(42);}
+}
--- /dev/null
+public class Super4 {
+ public Object m() { return null;}
+}
public static void main(String []argv) {
Super1 s = new Sub1();
Integer i = (Integer)s.m();
-
- Util.dumpMethods("Sub1");
}
}
public static void main(String []argv) {
Super2 s = new Sub2();
Integer i = (Integer)s.m();
-
- Util.dumpMethods("Sub2");
}
}
public static void main(String []argv) {
Super3 s = new Sub3();
Integer i = (Integer)s.m();
-
- Util.dumpMethods("Sub3");
+ if (i!=42) throw new RuntimeException("Should be 42 but is "+i);
}
}
--- /dev/null
+public aspect X4 {
+
+ declare parents: Sub4 extends Super4;
+
+ public static void main(String []argv) {
+ Super4 s = new Sub4();
+ Integer i = (Integer)s.m();
+ if (i!=42) throw new RuntimeException("Should be 42 but is "+i);
+ }
+}
package org.aspectj.systemtest.ajc150;
import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
import junit.framework.Test;
public void testITDBridgeMethods2Itd() {runTest("basic bridging with type vars - 2 - itd");}
public void testITDBridgeMethodsPr91381() {runTest("Abstract intertype method and covariant returns");}
- public void testGenericITDsBridgeMethods1() {runTest("bridge methods - 1");}
- public void testGenericITDsBridgeMethods1binary() {runTest("bridge methods - 1 - binary");}
- public void testGenericITDsBridgeMethods2() {runTest("bridge methods - 2");}
-// public void testGenericITDsBridgeMethods2binary() {runTest("bridge methods - 2 - binary");} // pr108101
- public void testGenericITDsBridgeMethods3() {runTest("bridge methods - 3");}
-// public void testGenericITDsBridgeMethods3binary() {runTest("bridge methods - 3 - binary");} // pr108101
+
+ // Just normal source compile of two types with a method override between them
+ public void testGenericITDsBridgeMethods1() {
+ runTest("bridge methods - 1");
+ checkMethodsExist("Sub1",new String[]{
+ "java.lang.Integer Sub1.m()",
+ "java.lang.Object Sub1.m() [BridgeMethod]"});
+ }
+ // Now the same thing but the aspect (which doesn't do much!) is binary woven in.
+ public void testGenericITDsBridgeMethods1binary() {
+ runTest("bridge methods - 1 - binary");
+ checkMethodsExist("Sub1",new String[]{
+ "java.lang.Integer Sub1.m()",
+ "java.lang.Object Sub1.m() [BridgeMethod]"});
+ }
+ // Now the method is put into the superclass via ITD - there should be a bridge method in the subclass
+ public void testGenericITDsBridgeMethods2() {
+ runTest("bridge methods - 2");
+ checkMethodsExist("Sub2",new String[]{
+ "java.lang.Integer Sub2.m()",
+ "java.lang.Object Sub2.m() [BridgeMethod]"});
+ }
+ // Now the superclass ITD is done with binary weaving so the weaver (rather than compiler) has to create the bridge method
+ public void testGenericITDsBridgeMethods2binary() {
+ runTest("bridge methods - 2 - binary");
+ checkMethodsExist("Sub2",new String[]{
+ "java.lang.Integer Sub2.m()",
+ "java.lang.Object Sub2.m() [BridgeMethod]"});
+ }
+ // Now the method is put into the subclass via ITD - there should be a bridge method alongside it in the subclass
+ public void testGenericITDsBridgeMethods3() {
+ runTest("bridge methods - 3");
+ checkMethodsExist("Sub3",new String[]{
+ "java.lang.Integer Sub3.m()",
+ "java.lang.Object Sub3.m() [BridgeMethod]"});
+ }
+ // Now the subclass ITD is done with binary weaving - the weaver should create the necessary bridge method
+// public void testGenericITDsBridgeMethods3binary() {
+// runTest("bridge methods - 3 - binary");
+// checkMethodsExist("Sub3",new String[]{
+// "java.lang.Integer Sub3.m()",
+// "java.lang.Object Sub3.m() [BridgeMethod]"});
+// }
+ // Now the two types are disconnected until the aspect supplies a declare parents relationship -
+ // the bridge method should still be created in the subtype
+ public void testGenericITDSBridgeMethods4() {
+ runTest("bridge methods - 4");
+ checkMethodsExist("Sub4",new String[]{
+ "java.lang.Integer Sub4.m()",
+ "java.lang.Object Sub4.m() [BridgeMethod]"});
+ }
+ // now the aspect doing the decp between the types is applied via binary weaving - weaver should create the bridge method
+// public void testGenericITDSBridgeMethods4binary() {
+// runTest("bridge methods - 4 - binary");
+// checkMethodsExist("Sub4",new String[]{
+// "java.lang.Integer Sub4.m()",
+// "java.lang.Object Sub4.m() [BridgeMethod]"});
+// }
+
+ // FIXME asc need a testcase for when a decp wires two classes together and causes the subtype to need bridge methods
public void testGenericITDsBridgeMethodsPR91381() {runTest("abstract intertype methods and covariant returns");}
public void testGenericITDsBridgeMethodsPR91381_2() {runTest("abstract intertype methods and covariant returns - error");}
public void testMultiLevelGenericAspects() {
runTest("multi-level generic abstract aspects");
}
-
// --- helpers
+
+ /**
+ * When a class has been written to the sandbox directory, you can ask this method to
+ * verify it contains a particular set of methods. Typically this is used to verify that
+ * bridge methods have been created.
+ */
+ public void checkMethodsExist(String classname,String[] methods) {
+ Set methodsFound = new HashSet();
+ StringBuffer debugString = new StringBuffer();
+ try {
+ ClassLoader cl = new URLClassLoader(new URL[]{ajc.getSandboxDirectory().toURL()});
+ Class clz = Class.forName(classname,false,cl);
+ java.lang.reflect.Method[] ms = clz.getDeclaredMethods();
+ if (ms!=null) {
+ for (int i =0;i<ms.length;i++) {
+ String methodString = ms[i].getReturnType().getName()+" "+ms[i].getDeclaringClass().getName()+"."+
+ ms[i].getName()+"("+stringify(ms[i].getParameterTypes())+")"+
+ (ms[i].isBridge()?" [BridgeMethod]":"");
+ methodsFound.add(methodString);
+ debugString.append("[").append(methodString).append("]");
+ }
+ }
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
- public static Signature getClassSignature(Ajc ajc,String classname) {
+ // check the methods specified do exist
+ for (int i = 0; i < methods.length; i++) {
+ String string = methods[i];
+ if (!methodsFound.remove(string)) {
+ fail("Couldn't find ["+string+"] in the set of methods in "+classname+" => "+debugString);
+ }
+ }
+ StringBuffer unexpectedMethods = new StringBuffer();
+ if (!methodsFound.isEmpty()) {
+ for (Iterator iter = methodsFound.iterator(); iter.hasNext();) {
+ String element = (String) iter.next();
+ unexpectedMethods.append("[").append(element).append("]");
+ }
+ fail("These methods weren't expected: "+unexpectedMethods);
+ }
+
+ }
+
+ public static JavaClass getClass(Ajc ajc, String classname) {
try {
ClassPath cp =
new ClassPath(ajc.getSandboxDirectory() + File.pathSeparator + System.getProperty("java.class.path"));
SyntheticRepository sRepos = SyntheticRepository.getInstance(cp);
JavaClass clazz = sRepos.loadClass(classname);
- Signature sigAttr = null;
- Attribute[] attrs = clazz.getAttributes();
- for (int i = 0; i < attrs.length; i++) {
- Attribute attribute = attrs[i];
- if (attribute.getName().equals("Signature")) sigAttr = (Signature)attribute;
- }
- return sigAttr;
+ return clazz;
} catch (ClassNotFoundException e) {
fail("Couldn't find class "+classname+" in the sandbox directory.");
}
return null;
}
+
+ public static Signature getClassSignature(Ajc ajc,String classname) {
+ JavaClass clazz = getClass(ajc,classname);
+ Signature sigAttr = null;
+ Attribute[] attrs = clazz.getAttributes();
+ for (int i = 0; i < attrs.length; i++) {
+ Attribute attribute = attrs[i];
+ if (attribute.getName().equals("Signature")) sigAttr = (Signature)attribute;
+ }
+ return sigAttr;
+ }
+
// Check the signature attribute on a class is correct
public static void verifyClassSignature(Ajc ajc,String classname,String sig) {
Signature sigAttr = getClassSignature(ajc,classname);
sigAttr.getSignature().equals(sig));
}
+ private static String stringify(Class[] clazzes) {
+ if (clazzes==null) return "";
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < clazzes.length; i++) {
+ Class class1 = clazzes[i];
+ if (i>0) sb.append(",");
+ sb.append(clazzes[i].getName());
+ }
+ return sb.toString();
+ }
}
<!-- generics/itds and bridge methods -->
<ajc-test dir="java5/generics/itds/bridgeMethods" vm="1.5" title="bridge methods - 1">
- <compile files="Sub1.java,Super1.java,X1.aj,Util.java" options="-1.5"/>
- <run class="X1">
- <stderr>
- <line text="Number of methods defined for Sub1 is 2"/>
- <line text="java.lang.Integer Sub1.m()"/>
- <line text="java.lang.Object Sub1.m() [BridgeMethod]"/>
- </stderr>
- </run>
+ <compile files="Sub1.java,Super1.java,X1.aj" options="-1.5"/>
+ <run class="X1"/>
</ajc-test>
-
<ajc-test dir="java5/generics/itds/bridgeMethods" vm="1.5" title="bridge methods - 1 - binary">
<compile files="Sub1.java,Super1.java" outjar="code.jar" options="-1.5"/>
- <compile files="X1.aj,Util.java" inpath="code.jar" options ="-1.5"/>
- <run class="X1">
- <stderr>
- <line text="Number of methods defined for Sub1 is 2"/>
- <line text="java.lang.Integer Sub1.m()"/>
- <line text="java.lang.Object Sub1.m() [BridgeMethod]"/>
- </stderr>
- </run>
+ <compile files="X1.aj" inpath="code.jar" options ="-1.5"/>
+ <run class="X1"/>
</ajc-test>
-
<ajc-test dir="java5/generics/itds/bridgeMethods" vm="1.5" title="bridge methods - 2">
- <compile files="Sub2.java,Super2.java,X2.aj,Util.java" options="-1.5"/>
- <run class="X2">
- <stderr>
- <line text="Number of methods defined for Sub2 is 2"/>
- <line text="java.lang.Integer Sub2.m()"/>
- <line text="java.lang.Object Sub2.m() [BridgeMethod]"/>
- </stderr>
- </run>
+ <compile files="Sub2.java,Super2.java,X2.aj" options="-1.5"/>
+ <run class="X2"/>
</ajc-test>
<ajc-test dir="java5/generics/itds/bridgeMethods" vm="1.5" title="bridge methods - 2 - binary">
<compile files="Sub2.java,Super2.java" outjar="code.jar" options="-1.5"/>
<compile files="X2.aj,Util.java" inpath="code.jar" options ="-1.5"/>
- <run class="X2">
- <stderr>
- <line text="Number of methods defined for Sub2 is 2"/>
- <line text="java.lang.Integer Sub2.m()"/>
- <line text="java.lang.Object Sub2.m() [BridgeMethod]"/>
- </stderr>
- </run>
+ <run class="X2"/>
</ajc-test>
<ajc-test dir="java5/generics/itds/bridgeMethods" vm="1.5" title="bridge methods - 3">
- <compile files="Sub3.java,Super3.java,X3.aj,Util.java" options="-1.5"/>
- <run class="X3">
- <stderr>
- <line text="Number of methods defined for Sub3 is 2"/>
- <line text="java.lang.Integer Sub3.m()"/>
- <line text="java.lang.Object Sub3.m() [BridgeMethod]"/>
- </stderr>
- </run>
+ <compile files="Sub3.java,Super3.java,X3.aj" options="-1.5"/>
+ <run class="X3"/>
</ajc-test>
-
<ajc-test dir="java5/generics/itds/bridgeMethods" vm="1.5" title="bridge methods - 3 - binary">
<compile files="Sub3.java,Super3.java" outjar="code.jar" options="-1.5"/>
- <compile files="X3.aj,Util.java" inpath="code.jar" options ="-1.5"/>
- <run class="X3">
- <stderr>
- <line text="Number of methods defined for Sub3 is 2"/>
- <line text="java.lang.Integer Sub3.m()"/>
- <line text="java.lang.Object Sub3.m() [BridgeMethod]"/>
- </stderr>
- </run>
+ <compile files="X3.aj" inpath="code.jar" options ="-1.5"/>
+ <run class="X3"/>
+ </ajc-test>
+
+ <ajc-test dir="java5/generics/itds/bridgeMethods" vm="1.5" title="bridge methods - 4">
+ <compile files="Sub4.java,Super4.java,X4.aj" options="-1.5"/>
+ <run class="X4"/>
</ajc-test>
+ <ajc-test dir="java5/generics/itds/bridgeMethods" vm="1.5" title="bridge methods - 4 - binary">
+ <compile files="Sub4.java,Super4.java" outjar="code.jar" options="-1.5"/>
+ <compile files="X4.aj" inpath="code.jar" options ="-1.5"/>
+ <run class="X4"/>
+ </ajc-test>
+
+
<ajc-test dir="java5/generics/itds/bridgeMethods" vm="1.5" title="abstract intertype methods and covariant returns">
<compile files="pr91381.aj" options="-1.5"/>
ResolvedMember memberHoldingAnyAnnotations = interMethodDispatcher;
ResolvedType onType = weaver.getWorld().resolve(unMangledInterMethod.getDeclaringType(),munger.getSourceLocation());
- LazyClassGen gen = weaver.getLazyClassGen();
- boolean mungingInterface = gen.isInterface();
+ LazyClassGen gen = weaver.getLazyClassGen();
+ boolean mungingInterface = gen.isInterface();
if (onType.isRawType()) onType = onType.getGenericType();
boolean onInterface = onType.isInterface();
+
+ // Simple checks, can't ITD on annotations or enums
if (onType.isAnnotation()) {
signalError(WeaverMessages.ITDM_ON_ANNOTATION_NOT_ALLOWED,weaver,onType);
return false;
return false;
}
+
+
if (onInterface && gen.getLazyMethodGen(unMangledInterMethod.getName(), unMangledInterMethod.getSignature(),true) != null) {
// this is ok, we could be providing the default implementation of a method
// that the target has already declared
return false;
}
+ // If we are processing the intended ITD target type (might be an interface)
if (onType.equals(gen.getType())) {
+
+
ResolvedMember mangledInterMethod =
AjcMemberMaker.interMethod(unMangledInterMethod, aspectType, onInterface);
-
- LazyMethodGen mg = makeMethodGen(gen, mangledInterMethod);
+ LazyMethodGen newMethod = makeMethodGen(gen, mangledInterMethod);
if (mungingInterface) {
// we want the modifiers of the ITD to be used for all *implementors* of the
// interface, but the method itself we add to the interface must be public abstract
- mg.setAccessFlags(Modifier.PUBLIC | Modifier.ABSTRACT);
+ newMethod.setAccessFlags(Modifier.PUBLIC | Modifier.ABSTRACT);
}
// pr98901
AnnotationX annotationX = annotationsOnRealMember[i];
Annotation a = annotationX.getBcelAnnotation();
AnnotationGen ag = new AnnotationGen(a,weaver.getLazyClassGen().getConstantPoolGen(),true);
- mg.addAnnotation(new AnnotationX(ag.getAnnotation(),weaver.getWorld()));
+ newMethod.addAnnotation(new AnnotationX(ag.getAnnotation(),weaver.getWorld()));
}
}
// the below loop fixes the very special (and very stupid)
for (Iterator i = allDecams.iterator(); i.hasNext();){
DeclareAnnotation decaMC = (DeclareAnnotation) i.next();
if (decaMC.matches(unMangledInterMethod,weaver.getWorld())
- && mg.getEnclosingClass().getType() == aspectType) {
- mg.addAnnotation(decaMC.getAnnotationX());
+ && newMethod.getEnclosingClass().getType() == aspectType) {
+ newMethod.addAnnotation(decaMC.getAnnotationX());
}
}
}
+ // If it doesn't target an interface and there is a body (i.e. it isnt abstract)
if (!onInterface && !Modifier.isAbstract(mangledInterMethod.getModifiers())) {
- InstructionList body = mg.getBody();
+ InstructionList body = newMethod.getBody();
InstructionFactory fact = gen.getFactory();
int pos = 0;
body.append(
InstructionFactory.createReturn(
BcelWorld.makeBcelType(mangledInterMethod.getReturnType())));
+
+ if (weaver.getWorld().isInJava5Mode()) { // Don't need bridge methods if not in 1.5 mode.
+ createAnyBridgeMethodsForCovariance(weaver, munger, unMangledInterMethod, onType, gen, paramTypes);
+ }
+
} else {
//??? this is okay
//if (!(mg.getBody() == null)) throw new RuntimeException("bas");
// XXX make sure to check that we set exceptions properly on this guy.
- weaver.addLazyMethodGen(mg);
- weaver.getLazyClassGen().warnOnAddedMethod(mg.getMethod(),getSignature().getSourceLocation());
+ weaver.addLazyMethodGen(newMethod);
+ weaver.getLazyClassGen().warnOnAddedMethod(newMethod.getMethod(),getSignature().getSourceLocation());
addNeededSuperCallMethods(weaver, onType, munger.getSuperMethodsCalled());
return false;
}
}
+
+ /**
+ * Create any bridge method required because of covariant returns being used. This method is used in the case
+ * where an ITD is applied to some type and it may be in an override relationship with a method from the supertype - but
+ * due to covariance there is a mismatch in return values.
+ * Example of when required:
+ Super defines: Object m(String s)
+ Sub defines: String m(String s)
+ then we need a bridge method in Sub called 'Object m(String s)' that forwards to 'String m(String s)'
+ */
+ private void createAnyBridgeMethodsForCovariance(BcelClassWeaver weaver, NewMethodTypeMunger munger, ResolvedMember unMangledInterMethod, ResolvedType onType, LazyClassGen gen, Type[] paramTypes) {
+ // PERFORMANCE BOTTLENECK? Might need investigating, method analysis between types in a hierarchy just seems expensive...
+ // COVARIANCE BRIDGING
+ // Algorithm: Step1. Check in this type - has someone already created the bridge method?
+ // Step2. Look above us - do we 'override' a method and yet differ in return type (i.e. covariance)
+ // Step3. Create a forwarding bridge method
+ ResolvedType superclass = onType.getSuperclass();
+ boolean quitRightNow = false;
+
+ String localMethodName = unMangledInterMethod.getName();
+ String localParameterSig = unMangledInterMethod.getParameterSignature();
+ String localReturnTypeESig = unMangledInterMethod.getReturnType().getErasureSignature();
+
+ // Step1
+ boolean alreadyDone = false; // Compiler might have done it
+ ResolvedMember[] localMethods = onType.getDeclaredMethods();
+ for (int i = 0; i < localMethods.length; i++) {
+ ResolvedMember member = localMethods[i];
+ if (member.getName().equals(localMethodName)) {
+ // Check the params
+ if (member.getParameterSignature().equals(localParameterSig)) alreadyDone = true;
+ }
+ }
+
+ // Step2
+ if (!alreadyDone) {
+ // Use the iterator form of 'getMethods()' so we do as little work as necessary
+ for (Iterator iter = onType.getSuperclass().getMethods();iter.hasNext() && !quitRightNow;) {
+ ResolvedMember aMethod = (ResolvedMember) iter.next();
+ if (aMethod.getName().equals(localMethodName) && aMethod.getParameterSignature().equals(localParameterSig)) {
+ // check the return types, if they are different we need a bridging method.
+ if (!aMethod.getReturnType().getErasureSignature().equals(localReturnTypeESig)) {
+ // Step3
+ createBridgeMethod(weaver.getWorld(), munger, unMangledInterMethod, gen, paramTypes, aMethod);
+ quitRightNow = true;
+ }
+ }
+ }
+ }
+ }
+
+ private void createBridgeMethod(BcelWorld world, NewMethodTypeMunger munger,
+ ResolvedMember unMangledInterMethod, LazyClassGen gen, Type[] paramTypes, ResolvedMember aMethod) {
+ InstructionList body;
+ InstructionFactory fact;
+ int pos = 0;
+
+ LazyMethodGen bridgeMethod = makeMethodGen(gen,aMethod); // The bridge method in this type will have the same signature as the one in the supertype
+ bridgeMethod.setAccessFlags(bridgeMethod.getAccessFlags() | 0x00000040 /*BRIDGE = 0x00000040*/ );
+ UnresolvedType[] newParams = munger.getSignature().getParameterTypes();
+// paramTypes = BcelWorld.makeBcelTypes(bridgingSetter.getParameterTypes());
+// Type[] bridgingToParms = BcelWorld.makeBcelTypes(unMangledInterMethod.getParameterTypes());
+ Type returnType = BcelWorld.makeBcelType(aMethod.getReturnType());
+ body = bridgeMethod.getBody();
+ fact = gen.getFactory();
+
+ if (!unMangledInterMethod.isStatic()) {
+ body.append(InstructionFactory.createThis());
+ pos++;
+ }
+ for (int i = 0, len = paramTypes.length; i < len; i++) {
+ Type paramType = paramTypes[i];
+ body.append(InstructionFactory.createLoad(paramType, pos));
+// if (!bridgingSetter.getParameterTypes()[i].getErasureSignature().equals(unMangledInterMethod.getParameterTypes()[i].getErasureSignature())) {
+// System.err.println("Putting in cast from "+paramType+" to "+bridgingToParms[i]);
+// body.append(fact.createCast(paramType,bridgingToParms[i]));
+// }
+ pos+=paramType.getSize();
+ }
+
+ body.append(Utility.createInvoke(fact, world,unMangledInterMethod));
+ body.append(InstructionFactory.createReturn(returnType));
+ gen.addMethodGen(bridgeMethod);
+ }
private boolean mungeMethodDelegate(BcelClassWeaver weaver, MethodDelegateTypeMunger munger) {
ResolvedMember introduced = munger.getSignature();