]> source.dussan.org Git - aspectj.git/commitdiff
BWD: Main part of the implementation, checks all the rules before allowing the DECP...
authoraclement <aclement>
Wed, 5 Jan 2005 16:42:21 +0000 (16:42 +0000)
committeraclement <aclement>
Wed, 5 Jan 2005 16:42:21 +0000 (16:42 +0000)
weaver/src/org/aspectj/weaver/bcel/BcelTypeMunger.java

index 4d5e68be3518db211597a7c35d50714d35e02ab1..3bf3642b7a66b786beeeed876f288c9d08443fc1 100644 (file)
@@ -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)