summaryrefslogtreecommitdiffstats
path: root/weaver
diff options
context:
space:
mode:
authoraclement <aclement>2005-01-05 16:42:21 +0000
committeraclement <aclement>2005-01-05 16:42:21 +0000
commit86013afc5a375ae16229a1fd7ada0fec827c07f6 (patch)
tree466cbc8cdc8123c0c774a24a71bca2046f9978e0 /weaver
parenta504e4ab1f4778a365848bd7975955edc2202ea5 (diff)
downloadaspectj-86013afc5a375ae16229a1fd7ada0fec827c07f6.tar.gz
aspectj-86013afc5a375ae16229a1fd7ada0fec827c07f6.zip
BWD: Main part of the implementation, checks all the rules before allowing the DECP to proceed.
Diffstat (limited to 'weaver')
-rw-r--r--weaver/src/org/aspectj/weaver/bcel/BcelTypeMunger.java303
1 files changed, 292 insertions, 11 deletions
diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelTypeMunger.java b/weaver/src/org/aspectj/weaver/bcel/BcelTypeMunger.java
index 4d5e68be3..3bf3642b7 100644
--- a/weaver/src/org/aspectj/weaver/bcel/BcelTypeMunger.java
+++ b/weaver/src/org/aspectj/weaver/bcel/BcelTypeMunger.java
@@ -15,16 +15,21 @@ package org.aspectj.weaver.bcel;
import java.lang.reflect.Modifier;
import java.util.Iterator;
+import java.util.List;
import java.util.Set;
import org.aspectj.apache.bcel.Constants;
+import org.aspectj.apache.bcel.generic.ConstantPoolGen;
import org.aspectj.apache.bcel.generic.FieldGen;
+import org.aspectj.apache.bcel.generic.INVOKESPECIAL;
import org.aspectj.apache.bcel.generic.InstructionConstants;
import org.aspectj.apache.bcel.generic.InstructionFactory;
+import org.aspectj.apache.bcel.generic.InstructionHandle;
import org.aspectj.apache.bcel.generic.InstructionList;
import org.aspectj.apache.bcel.generic.Type;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.ISourceLocation;
+import org.aspectj.bridge.Message;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.bridge.WeaveMessage;
import org.aspectj.weaver.AjcMemberMaker;
@@ -104,8 +109,8 @@ public class BcelTypeMunger extends ConcreteTypeMunger {
else tName = getShortname(weaver.getLazyClassGen().getType().getSourceLocation().getSourceFile().getPath());
String fName = getShortname(getAspectType().getSourceLocation().getSourceFile().getPath());
if (munger.getKind().equals(ResolvedTypeMunger.Parent)) {
- // This message will come out of AjLookupEnvironment.addParent if doing a source
- // compilation.
+ // This message could come out of AjLookupEnvironment.addParent if doing parents
+ // munging at compile time only...
NewParentTypeMunger parentTM = (NewParentTypeMunger)munger;
if (parentTM.getNewParent().isInterface()) {
weaver.getWorld().getMessageHandler().handleMessage(WeaveMessage.constructWeavingMessage(WeaveMessage.WEAVEMESSAGE_DECLAREPARENTSIMPLEMENTS,
@@ -113,7 +118,14 @@ public class BcelTypeMunger extends ConcreteTypeMunger {
tName,parentTM.getNewParent().getName(),fName},
weaver.getLazyClassGen().getClassName(), getAspectType().getName()));
} else {
- System.err.println("BANG, you need to fix this. BcelTypeMunger");
+ weaver.getWorld().getMessageHandler().handleMessage(
+ WeaveMessage.constructWeavingMessage(WeaveMessage.WEAVEMESSAGE_DECLAREPARENTSEXTENDS,
+ new String[]{weaver.getLazyClassGen().getType().getName(),
+ tName,parentTM.getNewParent().getName(),fName
+ }));
+// TAG: WeavingMessage DECLARE PARENTS: EXTENDS
+// reportDeclareParentsMessage(WeaveMessage.WEAVEMESSAGE_DECLAREPARENTSEXTENDS,sourceType,parent);
+
}
} else {
weaver.getWorld().getMessageHandler().handleMessage(WeaveMessage.constructWeavingMessage(WeaveMessage.WEAVEMESSAGE_ITD,
@@ -122,7 +134,6 @@ public class BcelTypeMunger extends ConcreteTypeMunger {
getAspectType().getName(),
fName+":'"+munger.getSignature()+"'"},
weaver.getLazyClassGen().getClassName(), getAspectType().getName()));
- // ??? If only type mungers knew their originating line numbers ...
}
}
@@ -137,18 +148,288 @@ public class BcelTypeMunger extends ConcreteTypeMunger {
return path.substring(takefrom+1);
}
-
+ /**
+ * For a long time, AspectJ did not allow binary weaving of declare parents. This restriction is now lifted
+ * but could do with more testing!
+ */
private boolean mungeNewParent(BcelClassWeaver weaver, NewParentTypeMunger munger) {
- LazyClassGen gen = weaver.getLazyClassGen();
- ResolvedTypeX newParent = munger.getNewParent();
- if (newParent.isClass()) {
- //gen.setSuperClass(newParent);
- } else {
- gen.addInterface(newParent,getSourceLocation());
+ LazyClassGen newParentTarget = weaver.getLazyClassGen();
+ ResolvedTypeX newParent = munger.getNewParent();
+
+ boolean cont = true; // Set to false when we error, so we don't actually *do* the munge
+ cont = enforceDecpRule1_abstractMethodsImplemented(weaver, munger.getSourceLocation(),newParentTarget, newParent);
+ cont = enforceDecpRule2_cantExtendFinalClass(weaver,munger.getSourceLocation(),newParentTarget,newParent) && cont;
+
+ List methods = newParent.getMethodsWithoutIterator();
+ for (Iterator iter = methods.iterator(); iter.hasNext();) {
+ BcelMethod superMethod = (BcelMethod) iter.next();
+ if (!superMethod.getName().equals("<init>")) {
+ LazyMethodGen subMethod = findMatchingMethod(newParentTarget, superMethod);
+ if (subMethod!=null) {
+ cont = enforceDecpRule3_visibilityChanges(weaver, newParent, superMethod, subMethod) && cont;
+ cont = enforceDecpRule4_compatibleReturnTypes(weaver, superMethod, subMethod) && cont;
+ cont = enforceDecpRule5_cantChangeFromStaticToNonstatic(weaver,munger.getSourceLocation(),superMethod,subMethod) && cont;
+ }
+ }
+ }
+ if (!cont) return false; // A rule was violated and an error message already reported
+
+ if (newParent.isClass()) { // Changing the supertype
+ if (!attemptToModifySuperCalls(weaver,newParentTarget,newParent)) return false;
+ newParentTarget.setSuperClass(newParent);
+ } else { // Adding a new interface
+ newParentTarget.addInterface(newParent,getSourceLocation());
}
return true;
}
+
+ /**
+ * Rule 1: For the declare parents to be allowed, the target type must override and implement
+ * inherited abstract methods (if the type is not declared abstract)
+ */
+ private boolean enforceDecpRule1_abstractMethodsImplemented(BcelClassWeaver weaver, ISourceLocation mungerLoc,LazyClassGen newParentTarget, ResolvedTypeX newParent) {
+ boolean ruleCheckingSucceeded = true;
+ if (!(newParentTarget.isAbstract() || newParentTarget.isInterface())) { // Ignore abstract classes or interfaces
+ List methods = newParent.getMethodsWithoutIterator();
+ for (Iterator i = methods.iterator(); i.hasNext();) {
+ BcelMethod o = (BcelMethod)i.next();
+ if (o.isAbstract() && !o.getName().startsWith("ajc$interField")) { // Ignore abstract methods of ajc$interField prefixed methods
+ BcelMethod discoveredImpl = null;
+ List newParentTargetMethods = newParentTarget.getType().getMethodsWithoutIterator();
+ for (Iterator ii = newParentTargetMethods.iterator(); ii.hasNext() && discoveredImpl==null;) {
+ BcelMethod gen2 = (BcelMethod) ii.next();
+ if (gen2.getName().equals(o.getName()) &&
+ gen2.getParameterSignature().equals(o.getParameterSignature()) && !gen2.isAbstract()) {
+ discoveredImpl = gen2; // Found a valid implementation !
+ }
+ }
+ if (discoveredImpl == null) {
+ // didnt find a valid implementation, lets check the ITDs on this type to see if they satisfy it
+ boolean satisfiedByITD = false;
+ for (Iterator ii = newParentTarget.getType().getInterTypeMungersIncludingSupers().iterator(); ii.hasNext(); ) {
+ ConcreteTypeMunger m = (ConcreteTypeMunger)ii.next();
+ if (m.getMunger() instanceof NewMethodTypeMunger) {
+ ResolvedMember sig = m.getSignature();
+ if (!Modifier.isAbstract(sig.getModifiers())) {
+ if (ResolvedTypeX
+ .matches(
+ AjcMemberMaker.interMethod(
+ sig,m.getAspectType(),sig.getDeclaringType().isInterface(weaver.getWorld())),o)) {
+ satisfiedByITD = true;
+ }
+ }
+ }
+ }
+ if (!satisfiedByITD) {
+ error(weaver,
+ "Class must implement the inherited abstract method "+o.getDeclaringType()+"."+o.getName()+o.getParameterSignature(),
+ newParentTarget.getType().getSourceLocation(),new ISourceLocation[]{o.getSourceLocation(),mungerLoc});
+ ruleCheckingSucceeded=false;
+ }
+ }
+ }
+ }
+ }
+ return ruleCheckingSucceeded;
+ }
+
+ /**
+ * Rule 2. Can't extend final types
+ */
+ private boolean enforceDecpRule2_cantExtendFinalClass(BcelClassWeaver weaver, ISourceLocation mungerLoc,
+ LazyClassGen newParentTarget, ResolvedTypeX newParent) {
+ if (newParent.isFinal()) {
+ error(weaver,"Cannot make type "+newParentTarget.getName()+" extend final class "+newParent.getName(),
+ newParentTarget.getType().getSourceLocation(),
+ new ISourceLocation[]{mungerLoc});
+ return false;
+ }
+ return true;
+ }
+
+
+ /**
+ * Rule 3. Can't narrow visibility of methods when overriding
+ */
+ private boolean enforceDecpRule3_visibilityChanges(BcelClassWeaver weaver, ResolvedTypeX newParent, BcelMethod superMethod, LazyMethodGen subMethod) {
+ boolean cont = true;
+ if (superMethod.isPublic()) {
+ if (subMethod.isProtected() || subMethod.isDefault() || subMethod.isPrivate()) {
+ weaver.getWorld().getMessageHandler().handleMessage(MessageUtil.error(
+ "Cannot reduce the visibility of the inherited method '"+superMethod+"' from "+newParent.getName(),
+ superMethod.getSourceLocation()));
+ cont=false;
+ }
+ } else if (superMethod.isProtected()) {
+ if (subMethod.isDefault() || subMethod.isPrivate()) {
+ weaver.getWorld().getMessageHandler().handleMessage(MessageUtil.error(
+ "Cannot reduce the visibility of the inherited method '"+superMethod+"' from "+newParent.getName(),
+ superMethod.getSourceLocation()));
+ cont=false;
+ }
+ } else if (superMethod.isDefault()) {
+ if (subMethod.isPrivate()) {
+ weaver.getWorld().getMessageHandler().handleMessage(MessageUtil.error(
+ "Cannot reduce the visibility of the inherited method '"+superMethod+"' from "+newParent.getName(),
+ superMethod.getSourceLocation()));
+ cont=false;
+ }
+ }
+ return cont;
+ }
+
+ /**
+ * Rule 4. Can't have incompatible return types
+ */
+ private boolean enforceDecpRule4_compatibleReturnTypes(BcelClassWeaver weaver, BcelMethod superMethod, LazyMethodGen subMethod) {
+ boolean cont = true;
+ String superReturnTypeSig = superMethod.getReturnType().getSignature();
+ String subReturnTypeSig = subMethod.getReturnType().getSignature();
+ if (!superReturnTypeSig.equals(subReturnTypeSig)) {
+ // Allow for covariance - wish I could test this (need Java5...)
+ ResolvedTypeX subType = weaver.getWorld().resolve(subMethod.getReturnType());
+ ResolvedTypeX superType = weaver.getWorld().resolve(superMethod.getReturnType());
+ if (!subType.isAssignableFrom(superType)) {
+ ISourceLocation sloc = subMethod.getSourceLocation();
+ weaver.getWorld().getMessageHandler().handleMessage(MessageUtil.error(
+ "The return type is incompatible with "+superMethod.getDeclaringType()+"."+superMethod.getName()+superMethod.getParameterSignature(),
+ subMethod.getSourceLocation()));
+ cont=false;
+ }
+ }
+ return cont;
+ }
+
+ /**
+ * Rule5. Method overrides can't change the staticality (word?) - you can't override and make an instance
+ * method static or override and make a static method an instance method.
+ */
+ private boolean enforceDecpRule5_cantChangeFromStaticToNonstatic(BcelClassWeaver weaver,ISourceLocation mungerLoc,BcelMethod superMethod, LazyMethodGen subMethod ) {
+ if (superMethod.isStatic() && !subMethod.isStatic()) {
+ error(weaver,"This instance method "+subMethod.getName()+subMethod.getParameterSignature()+
+ " cannot override the static method from "+superMethod.getDeclaringType().getName(),
+ subMethod.getSourceLocation(),new ISourceLocation[]{mungerLoc});
+ return false;
+ } else if (!superMethod.isStatic() && subMethod.isStatic()) {
+ error(weaver,"The static method "+subMethod.getName()+subMethod.getParameterSignature()+
+ " cannot hide the instance method from "+superMethod.getDeclaringType().getName(),
+ subMethod.getSourceLocation(),new ISourceLocation[]{mungerLoc});
+ return false;
+ }
+ return true;
+ }
+
+ public void error(BcelClassWeaver weaver,String text,ISourceLocation primaryLoc,ISourceLocation[] extraLocs) {
+ IMessage msg = new Message(text, primaryLoc, true, extraLocs);
+ weaver.getWorld().getMessageHandler().handleMessage(msg);
+ }
+
+
+ private LazyMethodGen findMatchingMethod(LazyClassGen newParentTarget, BcelMethod m) {
+ LazyMethodGen found = null;
+ // Search the type for methods overriding super methods (methods that come from the new parent)
+ // Don't use the return value in the comparison as overriding doesnt
+ for (Iterator i = newParentTarget.getMethodGens().iterator(); i.hasNext() && found==null;) {
+ LazyMethodGen gen = (LazyMethodGen) i.next();
+ if (gen.getName().equals(m.getName()) &&
+ gen.getParameterSignature().equals(m.getParameterSignature())) {
+ found = gen;
+ }
+ }
+ return found;
+ }
+
+ /**
+ * The main part of implementing declare parents extends. Modify super ctor calls to target the new type.
+ */
+ public boolean attemptToModifySuperCalls(BcelClassWeaver weaver,LazyClassGen newParentTarget, ResolvedTypeX newParent) {
+ String currentParent = newParentTarget.getSuperClassname();
+ List mgs = newParentTarget.getMethodGens();
+
+ // Look for ctors to modify
+ for (Iterator iter = mgs.iterator(); iter.hasNext();) {
+ LazyMethodGen aMethod = (LazyMethodGen) iter.next();
+
+ if (aMethod.getName().equals("<init>")) {
+ InstructionList insList = aMethod.getBody();
+ InstructionHandle handle = insList.getStart();
+ while (handle!= null) {
+ if (handle.getInstruction() instanceof INVOKESPECIAL) {
+ ConstantPoolGen cpg = newParentTarget.getConstantPoolGen();
+ INVOKESPECIAL invokeSpecial = (INVOKESPECIAL)handle.getInstruction();
+ if (invokeSpecial.getClassName(cpg).equals(currentParent) && invokeSpecial.getMethodName(cpg).equals("<init>")) {
+ // System.err.println("Transforming super call '<init>"+sp.getSignature(cpg)+"'");
+
+ // 1. Check there is a ctor in the new parent with the same signature
+ ResolvedMember newCtor = getConstructorWithSignature(newParent,invokeSpecial.getSignature(cpg));
+
+ if (newCtor == null) {
+
+ // 2. Check ITDCs to see if the necessary ctor is provided that way
+ boolean satisfiedByITDC = false;
+ for (Iterator ii = newParentTarget.getType().getInterTypeMungersIncludingSupers().iterator(); ii.hasNext() && !satisfiedByITDC; ) {
+ ConcreteTypeMunger m = (ConcreteTypeMunger)ii.next();
+ if (m.getMunger() instanceof NewConstructorTypeMunger) {
+ if (m.getSignature().getSignature().equals(invokeSpecial.getSignature(cpg))) {
+ satisfiedByITDC = true;
+ }
+ }
+ }
+
+ if (!satisfiedByITDC) {
+ String csig = createReadableCtorSig(newParent, cpg, invokeSpecial);
+ weaver.getWorld().getMessageHandler().handleMessage(MessageUtil.error(
+ "Unable to modify hierarchy for "+newParentTarget.getClassName()+" - the constructor "+
+ csig+" is missing",this.getSourceLocation()));
+ return false;
+ }
+ }
+
+ int idx = cpg.addMethodref(newParent.getClassName(), invokeSpecial.getMethodName(cpg), invokeSpecial.getSignature(cpg));
+ invokeSpecial.setIndex(idx);
+ }
+ }
+ handle = handle.getNext();
+ }
+ }
+ }
+ return true;
+ }
+
+
+ /**
+ * Creates a nice signature for the ctor, something like "(int,Integer,String)"
+ */
+ private String createReadableCtorSig(ResolvedTypeX newParent, ConstantPoolGen cpg, INVOKESPECIAL invokeSpecial) {
+ StringBuffer sb = new StringBuffer();
+ Type[] ctorArgs = invokeSpecial.getArgumentTypes(cpg);
+ sb.append(newParent.getClassName());
+ sb.append("(");
+ for (int i = 0; i < ctorArgs.length; i++) {
+ String argtype = ctorArgs[i].toString();
+ if (argtype.lastIndexOf(".")!=-1)
+ sb.append(argtype.substring(argtype.lastIndexOf(".")+1));
+ else
+ sb.append(argtype);
+ if (i+1<ctorArgs.length) sb.append(",");
+ }
+ sb.append(")");
+ return sb.toString();
+ }
+
+ private ResolvedMember getConstructorWithSignature(ResolvedTypeX tx,String signature) {
+ ResolvedMember[] mems = tx.getDeclaredJavaMethods();
+ for (int i = 0; i < mems.length; i++) {
+ ResolvedMember rm = mems[i];
+ if (rm.getName().equals("<init>")) {
+ if (rm.getSignature().equals(signature)) return rm;
+ }
+ }
+ return null;
+ }
+
+
private boolean mungePrivilegedAccess(
BcelClassWeaver weaver,
PrivilegedAccessMunger munger)