From 86013afc5a375ae16229a1fd7ada0fec827c07f6 Mon Sep 17 00:00:00 2001 From: aclement Date: Wed, 5 Jan 2005 16:42:21 +0000 Subject: [PATCH] BWD: Main part of the implementation, checks all the rules before allowing the DECP to proceed. --- .../aspectj/weaver/bcel/BcelTypeMunger.java | 303 +++++++++++++++++- 1 file 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("")) { + 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("")) { + 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("")) { + // System.err.println("Transforming super call '"+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")) { + if (rm.getSignature().equals(signature)) return rm; + } + } + return null; + } + + private boolean mungePrivilegedAccess( BcelClassWeaver weaver, PrivilegedAccessMunger munger) -- 2.39.5