| /* ******************************************************************* |
| * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). |
| * 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://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * PARC initial implementation |
| * Alexandre Vasseur support for @AJ aspects |
| * ******************************************************************/ |
| |
| package org.aspectj.weaver.bcel; |
| |
| import java.lang.reflect.Modifier; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.aspectj.apache.bcel.Constants; |
| import org.aspectj.apache.bcel.classfile.ConstantPool; |
| import org.aspectj.apache.bcel.classfile.Field; |
| import org.aspectj.apache.bcel.generic.ArrayType; |
| import org.aspectj.apache.bcel.generic.FieldInstruction; |
| import org.aspectj.apache.bcel.generic.INVOKEINTERFACE; |
| import org.aspectj.apache.bcel.generic.Instruction; |
| import org.aspectj.apache.bcel.generic.InstructionBranch; |
| 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.InstructionLV; |
| import org.aspectj.apache.bcel.generic.InstructionList; |
| import org.aspectj.apache.bcel.generic.InstructionTargeter; |
| import org.aspectj.apache.bcel.generic.InvokeInstruction; |
| import org.aspectj.apache.bcel.generic.LineNumberTag; |
| import org.aspectj.apache.bcel.generic.LocalVariableTag; |
| import org.aspectj.apache.bcel.generic.MULTIANEWARRAY; |
| import org.aspectj.apache.bcel.generic.ObjectType; |
| import org.aspectj.apache.bcel.generic.TargetLostException; |
| import org.aspectj.apache.bcel.generic.Type; |
| import org.aspectj.bridge.ISourceLocation; |
| import org.aspectj.weaver.Advice; |
| import org.aspectj.weaver.AdviceKind; |
| import org.aspectj.weaver.AjcMemberMaker; |
| import org.aspectj.weaver.BCException; |
| import org.aspectj.weaver.ConcreteTypeMunger; |
| import org.aspectj.weaver.IntMap; |
| import org.aspectj.weaver.Member; |
| import org.aspectj.weaver.MemberImpl; |
| import org.aspectj.weaver.NameMangler; |
| import org.aspectj.weaver.NewConstructorTypeMunger; |
| import org.aspectj.weaver.NewFieldTypeMunger; |
| import org.aspectj.weaver.NewMethodTypeMunger; |
| import org.aspectj.weaver.ResolvedMember; |
| import org.aspectj.weaver.ResolvedMemberImpl; |
| import org.aspectj.weaver.ResolvedType; |
| import org.aspectj.weaver.Shadow; |
| import org.aspectj.weaver.ShadowMunger; |
| import org.aspectj.weaver.UnresolvedType; |
| import org.aspectj.weaver.WeaverMessages; |
| import org.aspectj.weaver.World; |
| import org.aspectj.weaver.ast.Var; |
| import org.aspectj.weaver.patterns.AbstractPatternNodeVisitor; |
| import org.aspectj.weaver.patterns.AndPointcut; |
| import org.aspectj.weaver.patterns.NotPointcut; |
| import org.aspectj.weaver.patterns.OrPointcut; |
| import org.aspectj.weaver.patterns.ThisOrTargetPointcut; |
| |
| |
| /* |
| * Some fun implementation stuff: |
| * |
| * * expressionKind advice is non-execution advice |
| * * may have a target. |
| * * if the body is extracted, it will be extracted into |
| * a static method. The first argument to the static |
| * method is the target |
| * * advice may expose a this object, but that's the advice's |
| * consideration, not ours. This object will NOT be cached in another |
| * local, but will always come from frame zero. |
| * |
| * * non-expressionKind advice is execution advice |
| * * may have a this. |
| * * target is same as this, and is exposed that way to advice |
| * (i.e., target will not be cached, will always come from frame zero) |
| * * if the body is extracted, it will be extracted into a method |
| * with same static/dynamic modifier as enclosing method. If non-static, |
| * target of callback call will be this. |
| * |
| * * because of these two facts, the setup of the actual arguments (including |
| * possible target) callback method is the same for both kinds of advice: |
| * push the targetVar, if it exists (it will not exist for advice on static |
| * things), then push all the argVars. |
| * |
| * Protected things: |
| * |
| * * the above is sufficient for non-expressionKind advice for protected things, |
| * since the target will always be this. |
| * |
| * * For expressionKind things, we have to modify the signature of the callback |
| * method slightly. For non-static expressionKind things, we modify |
| * the first argument of the callback method NOT to be the type specified |
| * by the method/field signature (the owner), but rather we type it to |
| * the currentlyEnclosing type. We are guaranteed this will be fine, |
| * since the verifier verifies that the target is a subtype of the currently |
| * enclosingType. |
| * |
| * Worries: |
| * |
| * * ConstructorCalls will be weirder than all of these, since they |
| * supposedly don't have a target (according to AspectJ), but they clearly |
| * do have a target of sorts, just one that needs to be pushed on the stack, |
| * dupped, and not touched otherwise until the constructor runs. |
| * |
| * @author Jim Hugunin |
| * @author Erik Hilsdale |
| * |
| */ |
| |
| public class BcelShadow extends Shadow { |
| |
| private static final String[] NoDeclaredExceptions = new String[0]; |
| |
| private ShadowRange range; |
| private final BcelWorld world; |
| private final LazyMethodGen enclosingMethod; |
| |
| // TESTING this will tell us if the optimisation succeeded *on the last shadow processed* |
| public static boolean appliedLazyTjpOptimization; |
| |
| // Some instructions have a target type that will vary |
| // from the signature (pr109728) (1.4 declaring type issue) |
| private String actualInstructionTargetType; |
| |
| /** |
| * This generates an unassociated shadow, rooted in a particular method but not rooted to any particular point in the code. It |
| * should be given to a rooted ShadowRange in the {@link ShadowRange#associateWithShadow(BcelShadow)} method. |
| */ |
| public BcelShadow(BcelWorld world, Kind kind, Member signature, LazyMethodGen enclosingMethod, BcelShadow enclosingShadow) { |
| super(kind, signature, enclosingShadow); |
| this.world = world; |
| this.enclosingMethod = enclosingMethod; |
| } |
| |
| // ---- copies all state, including Shadow's mungers... |
| |
| public BcelShadow copyInto(LazyMethodGen recipient, BcelShadow enclosing) { |
| BcelShadow s = new BcelShadow(world, getKind(), getSignature(), recipient, enclosing); |
| if (mungers.size() > 0) { |
| List<ShadowMunger> src = mungers; |
| if (s.mungers == Collections.EMPTY_LIST) { |
| s.mungers = new ArrayList<ShadowMunger>(); |
| } |
| List<ShadowMunger> dest = s.mungers; |
| for (Iterator<ShadowMunger> i = src.iterator(); i.hasNext();) { |
| dest.add(i.next()); |
| } |
| } |
| return s; |
| } |
| |
| // ---- overridden behaviour |
| |
| @Override |
| public World getIWorld() { |
| return world; |
| } |
| |
| // see comment in deleteNewAndDup |
| // } else if (inst.opcode == Constants.DUP_X2) { |
| // // This code seen in the wild (by Brad): |
| // // 40: new #12; //class java/lang/StringBuffer |
| // // STACK: STRINGBUFFER |
| // // 43: dup |
| // // STACK: STRINGBUFFER/STRINGBUFFER |
| // // 44: aload_0 |
| // // STACK: STRINGBUFFER/STRINGBUFFER/THIS |
| // // 45: dup_x2 |
| // // STACK: THIS/STRINGBUFFER/STRINGBUFFER/THIS |
| // // 46: getfield #36; //Field value:Ljava/lang/String; |
| // // STACK: THIS/STRINGBUFFER/STRINGBUFFER/STRING<value> |
| // // 49: invokestatic #37; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String; |
| // // STACK: THIS/STRINGBUFFER/STRINGBUFFER/STRING |
| // // 52: invokespecial #19; //Method java/lang/StringBuffer."<init>":(Ljava/lang/String;)V |
| // // STACK: THIS/STRINGBUFFER |
| // // 55: aload_1 |
| // // STACK: THIS/STRINGBUFFER/LOCAL1 |
| // // 56: invokevirtual #22; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer; |
| // // STACK: THIS/STRINGBUFFER |
| // // 59: invokevirtual #34; //Method java/lang/StringBuffer.toString:()Ljava/lang/String; |
| // // STACK: THIS/STRING |
| // // 62: putfield #36; //Field value:Ljava/lang/String; |
| // // STACK: <empty> |
| // // 65: return |
| // |
| // // if we attempt to match on the ctor call to StringBuffer.<init> then we get into trouble. |
| // // if we simply delete the new/dup pair without fixing up the dup_x2 then the dup_x2 will fail due to there |
| // // not being 3 elements on the stack for it to work with. The fix *in this situation* is to change it to |
| // // a simple 'dup' |
| // |
| // // this fix is *not* very clean - but a general purpose decent solution will take much longer and this |
| // // bytecode sequence has only been seen once in the wild. |
| // ih.setInstruction(InstructionConstants.DUP); |
| |
| /** |
| * The new/dup (or new/dup_x1/swap) are removed and will be readded later (after the advice call) by the caller of this method. |
| * The groovy compiler produces unusual code where the new/dup isn't visible (when making a this() call from an existing ctor), |
| * an aload_0 is used to load the uninitialized object (as an example see the ctors in grails.util.BuildSettings). |
| * |
| * @return true if managed to remove them |
| */ |
| private boolean deleteNewAndDup() { |
| final ConstantPool cpool = getEnclosingClass().getConstantPool(); |
| int depth = 1; |
| InstructionHandle ih = range.getStart(); |
| |
| // Go back from where we are looking for 'NEW' that takes us to a stack depth of 0. INVOKESPECIAL <init> |
| while (ih != null) { |
| Instruction inst = ih.getInstruction(); |
| if (inst.opcode == Constants.INVOKESPECIAL && ((InvokeInstruction) inst).getName(cpool).equals("<init>")) { |
| depth++; |
| } else if (inst.opcode == Constants.NEW) { |
| depth--; |
| if (depth == 0) { |
| break; |
| } |
| // need a testcase to show this can really happen in a modern compiler - removed due to 315398 - moved this out to |
| // comment proceeding this method: |
| |
| } |
| ih = ih.getPrev(); |
| } |
| if (ih == null) { |
| return false; |
| } |
| // now IH points to the NEW. We're followed by the DUP, and that is followed |
| // by the actual instruction we care about. |
| InstructionHandle newHandle = ih; |
| InstructionHandle endHandle = newHandle.getNext(); |
| InstructionHandle nextHandle; |
| if (endHandle.getInstruction().opcode == Constants.DUP) { |
| nextHandle = endHandle.getNext(); |
| retargetFrom(newHandle, nextHandle); |
| retargetFrom(endHandle, nextHandle); |
| } else if (endHandle.getInstruction().opcode == Constants.DUP_X1) { |
| InstructionHandle dupHandle = endHandle; |
| endHandle = endHandle.getNext(); |
| nextHandle = endHandle.getNext(); |
| boolean skipEndRepositioning = false; |
| if (endHandle.getInstruction().opcode == Constants.SWAP) { |
| } else if (endHandle.getInstruction().opcode == Constants.IMPDEP1) { |
| skipEndRepositioning = true; // pr186884 |
| } else { |
| // XXX see next XXX comment |
| throw new RuntimeException("Unhandled kind of new " + endHandle); |
| } |
| // Now make any jumps to the 'new', the 'dup' or the 'end' now target the nextHandle |
| retargetFrom(newHandle, nextHandle); |
| retargetFrom(dupHandle, nextHandle); |
| if (!skipEndRepositioning) { |
| retargetFrom(endHandle, nextHandle); |
| } |
| } else { |
| endHandle = newHandle; |
| nextHandle = endHandle.getNext(); |
| retargetFrom(newHandle, nextHandle); |
| // add a POP here... we found a NEW w/o a dup or anything else, so |
| // we must be in statement context. |
| getRange().insert(InstructionConstants.POP, Range.OutsideAfter); |
| } |
| // assert (dupHandle.getInstruction() instanceof DUP); |
| |
| try { |
| range.getBody().delete(newHandle, endHandle); |
| } catch (TargetLostException e) { |
| throw new BCException("shouldn't happen"); |
| } |
| return true; |
| } |
| |
| private void retargetFrom(InstructionHandle old, InstructionHandle fresh) { |
| for (InstructionTargeter targeter : old.getTargetersCopy()) { |
| if (targeter instanceof ExceptionRange) { |
| ExceptionRange it = (ExceptionRange) targeter; |
| it.updateTarget(old, fresh, it.getBody()); |
| } else { |
| targeter.updateTarget(old, fresh); |
| } |
| } |
| } |
| |
| // records advice that is stopping us doing the lazyTjp optimization |
| private List<BcelAdvice> badAdvice = null; |
| |
| public void addAdvicePreventingLazyTjp(BcelAdvice advice) { |
| if (badAdvice == null) { |
| badAdvice = new ArrayList<BcelAdvice>(); |
| } |
| badAdvice.add(advice); |
| } |
| |
| @Override |
| protected void prepareForMungers() { |
| // if we're a constructor call, we need to remove the new:dup or the new:dup_x1:swap, |
| // and store all our arguments on the frame. |
| |
| // ??? This is a bit of a hack (for the Java langauge). We do this because |
| // we sometime add code "outsideBefore" when dealing with weaving join points. We only |
| // do this for exposing state that is on the stack. It turns out to just work for |
| // everything except for constructor calls and exception handlers. If we were to clean |
| // this up, every ShadowRange would have three instructionHandle points, the start of |
| // the arg-setup code, the start of the running code, and the end of the running code. |
| boolean deletedNewAndDup = true; |
| if (getKind() == ConstructorCall) { |
| if (!world.isJoinpointArrayConstructionEnabled() || !this.getSignature().getDeclaringType().isArray()) { |
| deletedNewAndDup = deleteNewAndDup(); // no new/dup for new array construction |
| } |
| initializeArgVars(); |
| } else if (getKind() == PreInitialization) { // pr74952 |
| ShadowRange range = getRange(); |
| range.insert(InstructionConstants.NOP, Range.InsideAfter); |
| } else if (getKind() == ExceptionHandler) { |
| |
| ShadowRange range = getRange(); |
| InstructionList body = range.getBody(); |
| InstructionHandle start = range.getStart(); |
| |
| // Create a store instruction to put the value from the top of the |
| // stack into a local variable slot. This is a trimmed version of |
| // what is in initializeArgVars() (since there is only one argument |
| // at a handler jp and only before advice is supported) (pr46298) |
| argVars = new BcelVar[1]; |
| // int positionOffset = (hasTarget() ? 1 : 0) + ((hasThis() && !getKind().isTargetSameAsThis()) ? 1 : 0); |
| UnresolvedType tx = getArgType(0); |
| argVars[0] = genTempVar(tx, "ajc$arg0"); |
| InstructionHandle insertedInstruction = range.insert(argVars[0].createStore(getFactory()), Range.OutsideBefore); |
| |
| // Now the exception range starts just after our new instruction. |
| // The next bit of code changes the exception range to point at |
| // the store instruction |
| for (InstructionTargeter t : start.getTargetersCopy()) { |
| if (t instanceof ExceptionRange) { |
| ExceptionRange er = (ExceptionRange) t; |
| er.updateTarget(start, insertedInstruction, body); |
| } |
| } |
| } |
| |
| // now we ask each munger to request our state |
| isThisJoinPointLazy = true;// world.isXlazyTjp(); // lazy is default now |
| |
| badAdvice = null; |
| for (ShadowMunger munger : mungers) { |
| munger.specializeOn(this); |
| } |
| |
| initializeThisJoinPoint(); |
| |
| if (thisJoinPointVar != null && !isThisJoinPointLazy && badAdvice != null && badAdvice.size() > 1) { |
| // something stopped us making it a lazy tjp |
| // can't build tjp lazily, no suitable test... |
| int valid = 0; |
| for (Iterator<BcelAdvice> iter = badAdvice.iterator(); iter.hasNext();) { |
| BcelAdvice element = iter.next(); |
| ISourceLocation sLoc = element.getSourceLocation(); |
| if (sLoc != null && sLoc.getLine() > 0) { |
| valid++; |
| } |
| } |
| if (valid != 0) { |
| ISourceLocation[] badLocs = new ISourceLocation[valid]; |
| int i = 0; |
| for (Iterator<BcelAdvice> iter = badAdvice.iterator(); iter.hasNext();) { |
| BcelAdvice element = iter.next(); |
| ISourceLocation sLoc = element.getSourceLocation(); |
| if (sLoc != null) { |
| badLocs[i++] = sLoc; |
| } |
| } |
| world.getLint().multipleAdviceStoppingLazyTjp |
| .signal(new String[] { this.toString() }, getSourceLocation(), badLocs); |
| } |
| } |
| badAdvice = null; |
| |
| // If we are an expression kind, we require our target/arguments on the stack |
| // before we do our actual thing. However, they may have been removed |
| // from the stack as the shadowMungers have requested state. |
| // if any of our shadowMungers requested either the arguments or target, |
| // the munger will have added code |
| // to pop the target/arguments into temporary variables, represented by |
| // targetVar and argVars. In such a case, we must make sure to re-push the |
| // values. |
| |
| // If we are nonExpressionKind, we don't expect arguments on the stack |
| // so this is moot. If our argVars happen to be null, then we know that |
| // no ShadowMunger has squirrelled away our arguments, so they're still |
| // on the stack. |
| InstructionFactory fact = getFactory(); |
| if (getKind().argsOnStack() && argVars != null) { |
| |
| // Special case first (pr46298). If we are an exception handler and the instruction |
| // just after the shadow is a POP then we should remove the pop. The code |
| // above which generated the store instruction has already cleared the stack. |
| // We also don't generate any code for the arguments in this case as it would be |
| // an incorrect aload. |
| if (getKind() == ExceptionHandler && range.getEnd().getNext().getInstruction().equals(InstructionConstants.POP)) { |
| // easier than deleting it ... |
| range.getEnd().getNext().setInstruction(InstructionConstants.NOP); |
| } else { |
| range.insert(BcelRenderer.renderExprs(fact, world, argVars), Range.InsideBefore); |
| if (targetVar != null) { |
| range.insert(BcelRenderer.renderExpr(fact, world, targetVar), Range.InsideBefore); |
| } |
| if (getKind() == ConstructorCall) { |
| if (!world.isJoinpointArrayConstructionEnabled() || !this.getSignature().getDeclaringType().isArray()) { |
| if (deletedNewAndDup) { // if didnt delete them, dont insert any! |
| range.insert(InstructionFactory.createDup(1), Range.InsideBefore); |
| range.insert(fact.createNew((ObjectType) BcelWorld.makeBcelType(getSignature().getDeclaringType())), |
| Range.InsideBefore); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| // ---- getters |
| |
| public ShadowRange getRange() { |
| return range; |
| } |
| |
| public void setRange(ShadowRange range) { |
| this.range = range; |
| } |
| |
| private int sourceline = -1; |
| |
| public int getSourceLine() { |
| // if the kind of join point for which we are a shadow represents |
| // a method or constructor execution, then the best source line is |
| // the one from the enclosingMethod declarationLineNumber if available. |
| if (sourceline != -1) { |
| return sourceline; |
| } |
| Kind kind = getKind(); |
| if ((kind == MethodExecution) || (kind == ConstructorExecution) || (kind == AdviceExecution) |
| || (kind == StaticInitialization) || (kind == PreInitialization) || (kind == Initialization)) { |
| if (getEnclosingMethod().hasDeclaredLineNumberInfo()) { |
| sourceline = getEnclosingMethod().getDeclarationLineNumber(); |
| return sourceline; |
| } |
| } |
| |
| if (range == null) { |
| if (getEnclosingMethod().hasBody()) { |
| sourceline = Utility.getSourceLine(getEnclosingMethod().getBody().getStart()); |
| return sourceline; |
| } else { |
| sourceline = 0; |
| return sourceline; |
| } |
| } |
| sourceline = Utility.getSourceLine(range.getStart()); |
| if (sourceline < 0) { |
| sourceline = 0; |
| } |
| return sourceline; |
| } |
| |
| @Override |
| public ResolvedType getEnclosingType() { |
| return getEnclosingClass().getType(); |
| } |
| |
| public LazyClassGen getEnclosingClass() { |
| return enclosingMethod.getEnclosingClass(); |
| } |
| |
| public BcelWorld getWorld() { |
| return world; |
| } |
| |
| // ---- factory methods |
| |
| public static BcelShadow makeConstructorExecution(BcelWorld world, LazyMethodGen enclosingMethod, |
| InstructionHandle justBeforeStart) { |
| final InstructionList body = enclosingMethod.getBody(); |
| BcelShadow s = new BcelShadow(world, ConstructorExecution, world.makeJoinPointSignatureFromMethod(enclosingMethod, |
| Member.CONSTRUCTOR), enclosingMethod, null); |
| ShadowRange r = new ShadowRange(body); |
| r.associateWithShadow(s); |
| r.associateWithTargets(Range.genStart(body, justBeforeStart.getNext()), Range.genEnd(body)); |
| return s; |
| } |
| |
| public static BcelShadow makeStaticInitialization(BcelWorld world, LazyMethodGen enclosingMethod) { |
| InstructionList body = enclosingMethod.getBody(); |
| // move the start past ajc$preClinit |
| InstructionHandle clinitStart = body.getStart(); |
| if (clinitStart.getInstruction() instanceof InvokeInstruction) { |
| InvokeInstruction ii = (InvokeInstruction) clinitStart.getInstruction(); |
| if (ii.getName(enclosingMethod.getEnclosingClass().getConstantPool()).equals(NameMangler.AJC_PRE_CLINIT_NAME)) { |
| clinitStart = clinitStart.getNext(); |
| } |
| } |
| |
| InstructionHandle clinitEnd = body.getEnd(); |
| |
| // XXX should move the end before the postClinit, but the return is then tricky... |
| // if (clinitEnd.getInstruction() instanceof InvokeInstruction) { |
| // InvokeInstruction ii = (InvokeInstruction)clinitEnd.getInstruction(); |
| // if (ii.getName(enclosingMethod.getEnclosingClass().getConstantPool()).equals(NameMangler.AJC_POST_CLINIT_NAME)) { |
| // clinitEnd = clinitEnd.getPrev(); |
| // } |
| // } |
| |
| BcelShadow s = new BcelShadow(world, StaticInitialization, world.makeJoinPointSignatureFromMethod(enclosingMethod, |
| Member.STATIC_INITIALIZATION), enclosingMethod, null); |
| ShadowRange r = new ShadowRange(body); |
| r.associateWithShadow(s); |
| r.associateWithTargets(Range.genStart(body, clinitStart), Range.genEnd(body, clinitEnd)); |
| return s; |
| } |
| |
| /** |
| * Make the shadow for an exception handler. Currently makes an empty shadow that only allows before advice to be woven into it. |
| */ |
| |
| public static BcelShadow makeExceptionHandler(BcelWorld world, ExceptionRange exceptionRange, LazyMethodGen enclosingMethod, |
| InstructionHandle startOfHandler, BcelShadow enclosingShadow) { |
| InstructionList body = enclosingMethod.getBody(); |
| UnresolvedType catchType = exceptionRange.getCatchType(); |
| UnresolvedType inType = enclosingMethod.getEnclosingClass().getType(); |
| |
| ResolvedMemberImpl sig = MemberImpl.makeExceptionHandlerSignature(inType, catchType); |
| sig.setParameterNames(new String[] { findHandlerParamName(startOfHandler) }); |
| |
| BcelShadow s = new BcelShadow(world, ExceptionHandler, sig, enclosingMethod, enclosingShadow); |
| ShadowRange r = new ShadowRange(body); |
| r.associateWithShadow(s); |
| InstructionHandle start = Range.genStart(body, startOfHandler); |
| InstructionHandle end = Range.genEnd(body, start); |
| |
| r.associateWithTargets(start, end); |
| exceptionRange.updateTarget(startOfHandler, start, body); |
| return s; |
| } |
| |
| private static String findHandlerParamName(InstructionHandle startOfHandler) { |
| if (startOfHandler.getInstruction().isStoreInstruction() && startOfHandler.getNext() != null) { |
| int slot = startOfHandler.getInstruction().getIndex(); |
| // System.out.println("got store: " + startOfHandler.getInstruction() + ", " + index); |
| Iterator<InstructionTargeter> tIter = startOfHandler.getNext().getTargeters().iterator(); |
| while (tIter.hasNext()) { |
| InstructionTargeter targeter = tIter.next(); |
| if (targeter instanceof LocalVariableTag) { |
| LocalVariableTag t = (LocalVariableTag) targeter; |
| if (t.getSlot() == slot) { |
| return t.getName(); |
| } |
| } |
| } |
| } |
| |
| return "<missing>"; |
| } |
| |
| /** create an init join point associated w/ an interface in the body of a constructor */ |
| |
| public static BcelShadow makeIfaceInitialization(BcelWorld world, LazyMethodGen constructor, |
| Member interfaceConstructorSignature) { |
| // this call marks the instruction list as changed |
| constructor.getBody(); |
| // UnresolvedType inType = constructor.getEnclosingClass().getType(); |
| BcelShadow s = new BcelShadow(world, Initialization, interfaceConstructorSignature, constructor, null); |
| // s.fallsThrough = true; |
| // ShadowRange r = new ShadowRange(body); |
| // r.associateWithShadow(s); |
| // InstructionHandle start = Range.genStart(body, handle); |
| // InstructionHandle end = Range.genEnd(body, handle); |
| // |
| // r.associateWithTargets(start, end); |
| return s; |
| } |
| |
| public void initIfaceInitializer(InstructionHandle end) { |
| final InstructionList body = enclosingMethod.getBody(); |
| ShadowRange r = new ShadowRange(body); |
| r.associateWithShadow(this); |
| InstructionHandle nop = body.insert(end, InstructionConstants.NOP); |
| |
| r.associateWithTargets(Range.genStart(body, nop), Range.genEnd(body, nop)); |
| } |
| |
| // public static BcelShadow makeIfaceConstructorExecution( |
| // BcelWorld world, |
| // LazyMethodGen constructor, |
| // InstructionHandle next, |
| // Member interfaceConstructorSignature) |
| // { |
| // // final InstructionFactory fact = constructor.getEnclosingClass().getFactory(); |
| // InstructionList body = constructor.getBody(); |
| // // UnresolvedType inType = constructor.getEnclosingClass().getType(); |
| // BcelShadow s = |
| // new BcelShadow( |
| // world, |
| // ConstructorExecution, |
| // interfaceConstructorSignature, |
| // constructor, |
| // null); |
| // s.fallsThrough = true; |
| // ShadowRange r = new ShadowRange(body); |
| // r.associateWithShadow(s); |
| // // ??? this may or may not work |
| // InstructionHandle start = Range.genStart(body, next); |
| // //InstructionHandle end = Range.genEnd(body, body.append(start, fact.NOP)); |
| // InstructionHandle end = Range.genStart(body, next); |
| // //body.append(start, fact.NOP); |
| // |
| // r.associateWithTargets(start, end); |
| // return s; |
| // } |
| |
| /** |
| * Create an initialization join point associated with a constructor, but not with any body of code yet. If this is actually |
| * matched, it's range will be set when we inline self constructors. |
| * |
| * @param constructor The constructor starting this initialization. |
| */ |
| public static BcelShadow makeUnfinishedInitialization(BcelWorld world, LazyMethodGen constructor) { |
| BcelShadow ret = new BcelShadow(world, Initialization, world.makeJoinPointSignatureFromMethod(constructor, |
| Member.CONSTRUCTOR), constructor, null); |
| if (constructor.getEffectiveSignature() != null) { |
| ret.setMatchingSignature(constructor.getEffectiveSignature().getEffectiveSignature()); |
| } |
| return ret; |
| } |
| |
| public static BcelShadow makeUnfinishedPreinitialization(BcelWorld world, LazyMethodGen constructor) { |
| BcelShadow ret = new BcelShadow(world, PreInitialization, world.makeJoinPointSignatureFromMethod(constructor, |
| Member.CONSTRUCTOR), constructor, null); |
| if (constructor.getEffectiveSignature() != null) { |
| ret.setMatchingSignature(constructor.getEffectiveSignature().getEffectiveSignature()); |
| } |
| return ret; |
| } |
| |
| public static BcelShadow makeMethodExecution(BcelWorld world, LazyMethodGen enclosingMethod, boolean lazyInit) { |
| if (!lazyInit) { |
| return makeMethodExecution(world, enclosingMethod); |
| } |
| |
| BcelShadow s = new BcelShadow(world, MethodExecution, enclosingMethod.getMemberView(), enclosingMethod, null); |
| |
| return s; |
| } |
| |
| public void init() { |
| if (range != null) { |
| return; |
| } |
| |
| final InstructionList body = enclosingMethod.getBody(); |
| ShadowRange r = new ShadowRange(body); |
| r.associateWithShadow(this); |
| r.associateWithTargets(Range.genStart(body), Range.genEnd(body)); |
| } |
| |
| public static BcelShadow makeMethodExecution(BcelWorld world, LazyMethodGen enclosingMethod) { |
| return makeShadowForMethod(world, enclosingMethod, MethodExecution, enclosingMethod.getMemberView()); |
| } |
| |
| public static BcelShadow makeShadowForMethod(BcelWorld world, LazyMethodGen enclosingMethod, Shadow.Kind kind, Member sig) { |
| final InstructionList body = enclosingMethod.getBody(); |
| BcelShadow s = new BcelShadow(world, kind, sig, enclosingMethod, null); |
| ShadowRange r = new ShadowRange(body); |
| r.associateWithShadow(s); |
| r.associateWithTargets(// OPTIMIZE this occurs lots of times for all jp kinds... |
| Range.genStart(body), Range.genEnd(body)); |
| return s; |
| } |
| |
| public static BcelShadow makeAdviceExecution(BcelWorld world, LazyMethodGen enclosingMethod) { |
| final InstructionList body = enclosingMethod.getBody(); |
| BcelShadow s = new BcelShadow(world, AdviceExecution, |
| world.makeJoinPointSignatureFromMethod(enclosingMethod, Member.ADVICE), enclosingMethod, null); |
| ShadowRange r = new ShadowRange(body); |
| r.associateWithShadow(s); |
| r.associateWithTargets(Range.genStart(body), Range.genEnd(body)); |
| return s; |
| } |
| |
| // constructor call shadows are <em>initially</em> just around the |
| // call to the constructor. If ANY advice gets put on it, we move |
| // the NEW instruction inside the join point, which involves putting |
| // all the arguments in temps. |
| public static BcelShadow makeConstructorCall(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle callHandle, |
| BcelShadow enclosingShadow) { |
| final InstructionList body = enclosingMethod.getBody(); |
| |
| Member sig = world.makeJoinPointSignatureForMethodInvocation(enclosingMethod.getEnclosingClass(), |
| (InvokeInstruction) callHandle.getInstruction()); |
| |
| BcelShadow s = new BcelShadow(world, ConstructorCall, sig, enclosingMethod, enclosingShadow); |
| ShadowRange r = new ShadowRange(body); |
| r.associateWithShadow(s); |
| r.associateWithTargets(Range.genStart(body, callHandle), Range.genEnd(body, callHandle)); |
| retargetAllBranches(callHandle, r.getStart()); |
| return s; |
| } |
| |
| public static BcelShadow makeArrayConstructorCall(BcelWorld world, LazyMethodGen enclosingMethod, |
| InstructionHandle arrayInstruction, BcelShadow enclosingShadow) { |
| final InstructionList body = enclosingMethod.getBody(); |
| Member sig = world.makeJoinPointSignatureForArrayConstruction(enclosingMethod.getEnclosingClass(), arrayInstruction); |
| BcelShadow s = new BcelShadow(world, ConstructorCall, sig, enclosingMethod, enclosingShadow); |
| ShadowRange r = new ShadowRange(body); |
| r.associateWithShadow(s); |
| r.associateWithTargets(Range.genStart(body, arrayInstruction), Range.genEnd(body, arrayInstruction)); |
| retargetAllBranches(arrayInstruction, r.getStart()); |
| return s; |
| } |
| |
| public static BcelShadow makeMonitorEnter(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle monitorInstruction, |
| BcelShadow enclosingShadow) { |
| final InstructionList body = enclosingMethod.getBody(); |
| Member sig = world.makeJoinPointSignatureForMonitorEnter(enclosingMethod.getEnclosingClass(), monitorInstruction); |
| BcelShadow s = new BcelShadow(world, SynchronizationLock, sig, enclosingMethod, enclosingShadow); |
| ShadowRange r = new ShadowRange(body); |
| r.associateWithShadow(s); |
| r.associateWithTargets(Range.genStart(body, monitorInstruction), Range.genEnd(body, monitorInstruction)); |
| retargetAllBranches(monitorInstruction, r.getStart()); |
| return s; |
| } |
| |
| public static BcelShadow makeMonitorExit(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle monitorInstruction, |
| BcelShadow enclosingShadow) { |
| final InstructionList body = enclosingMethod.getBody(); |
| Member sig = world.makeJoinPointSignatureForMonitorExit(enclosingMethod.getEnclosingClass(), monitorInstruction); |
| BcelShadow s = new BcelShadow(world, SynchronizationUnlock, sig, enclosingMethod, enclosingShadow); |
| ShadowRange r = new ShadowRange(body); |
| r.associateWithShadow(s); |
| r.associateWithTargets(Range.genStart(body, monitorInstruction), Range.genEnd(body, monitorInstruction)); |
| retargetAllBranches(monitorInstruction, r.getStart()); |
| return s; |
| } |
| |
| // see pr77166 |
| // public static BcelShadow makeArrayLoadCall( |
| // BcelWorld world, |
| // LazyMethodGen enclosingMethod, |
| // InstructionHandle arrayInstruction, |
| // BcelShadow enclosingShadow) |
| // { |
| // final InstructionList body = enclosingMethod.getBody(); |
| // Member sig = world.makeJoinPointSignatureForArrayLoad(enclosingMethod.getEnclosingClass(),arrayInstruction); |
| // BcelShadow s = |
| // new BcelShadow( |
| // world, |
| // MethodCall, |
| // sig, |
| // enclosingMethod, |
| // enclosingShadow); |
| // ShadowRange r = new ShadowRange(body); |
| // r.associateWithShadow(s); |
| // r.associateWithTargets( |
| // Range.genStart(body, arrayInstruction), |
| // Range.genEnd(body, arrayInstruction)); |
| // retargetAllBranches(arrayInstruction, r.getStart()); |
| // return s; |
| // } |
| |
| public static BcelShadow makeMethodCall(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle callHandle, |
| BcelShadow enclosingShadow) { |
| final InstructionList body = enclosingMethod.getBody(); |
| BcelShadow s = new BcelShadow(world, MethodCall, world.makeJoinPointSignatureForMethodInvocation( |
| enclosingMethod.getEnclosingClass(), (InvokeInstruction) callHandle.getInstruction()), enclosingMethod, |
| enclosingShadow); |
| ShadowRange r = new ShadowRange(body); |
| r.associateWithShadow(s); |
| r.associateWithTargets(Range.genStart(body, callHandle), Range.genEnd(body, callHandle)); |
| retargetAllBranches(callHandle, r.getStart()); |
| return s; |
| } |
| |
| public static BcelShadow makeShadowForMethodCall(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle callHandle, |
| BcelShadow enclosingShadow, Kind kind, ResolvedMember sig) { |
| final InstructionList body = enclosingMethod.getBody(); |
| BcelShadow s = new BcelShadow(world, kind, sig, enclosingMethod, enclosingShadow); |
| ShadowRange r = new ShadowRange(body); |
| r.associateWithShadow(s); |
| r.associateWithTargets(Range.genStart(body, callHandle), Range.genEnd(body, callHandle)); |
| retargetAllBranches(callHandle, r.getStart()); |
| return s; |
| } |
| |
| public static BcelShadow makeFieldGet(BcelWorld world, ResolvedMember field, LazyMethodGen enclosingMethod, |
| InstructionHandle getHandle, BcelShadow enclosingShadow) { |
| final InstructionList body = enclosingMethod.getBody(); |
| BcelShadow s = new BcelShadow(world, FieldGet, field, |
| // BcelWorld.makeFieldSignature( |
| // enclosingMethod.getEnclosingClass(), |
| // (FieldInstruction) getHandle.getInstruction()), |
| enclosingMethod, enclosingShadow); |
| ShadowRange r = new ShadowRange(body); |
| r.associateWithShadow(s); |
| r.associateWithTargets(Range.genStart(body, getHandle), Range.genEnd(body, getHandle)); |
| retargetAllBranches(getHandle, r.getStart()); |
| return s; |
| } |
| |
| public static BcelShadow makeFieldSet(BcelWorld world, ResolvedMember field, LazyMethodGen enclosingMethod, |
| InstructionHandle setHandle, BcelShadow enclosingShadow) { |
| final InstructionList body = enclosingMethod.getBody(); |
| BcelShadow s = new BcelShadow(world, FieldSet, field, |
| // BcelWorld.makeFieldJoinPointSignature( |
| // enclosingMethod.getEnclosingClass(), |
| // (FieldInstruction) setHandle.getInstruction()), |
| enclosingMethod, enclosingShadow); |
| ShadowRange r = new ShadowRange(body); |
| r.associateWithShadow(s); |
| r.associateWithTargets(Range.genStart(body, setHandle), Range.genEnd(body, setHandle)); |
| retargetAllBranches(setHandle, r.getStart()); |
| return s; |
| } |
| |
| public static void retargetAllBranches(InstructionHandle from, InstructionHandle to) { |
| for (InstructionTargeter source : from.getTargetersCopy()) { |
| if (source instanceof InstructionBranch) { |
| source.updateTarget(from, to); |
| } |
| } |
| } |
| |
| // // ---- type access methods |
| // private ObjectType getTargetBcelType() { |
| // return (ObjectType) BcelWorld.makeBcelType(getTargetType()); |
| // } |
| // private Type getArgBcelType(int arg) { |
| // return BcelWorld.makeBcelType(getArgType(arg)); |
| // } |
| |
| // ---- kinding |
| |
| /** |
| * If the end of my range has no real instructions following then my context needs a return at the end. |
| */ |
| public boolean terminatesWithReturn() { |
| return getRange().getRealNext() == null; |
| } |
| |
| /** |
| * Is arg0 occupied with the value of this |
| */ |
| public boolean arg0HoldsThis() { |
| if (getKind().isEnclosingKind()) { |
| return !Modifier.isStatic(getSignature().getModifiers()); |
| } else if (enclosingShadow == null) { |
| // XXX this is mostly right |
| // this doesn't do the right thing for calls in the pre part of introduced constructors. |
| return !enclosingMethod.isStatic(); |
| } else { |
| return ((BcelShadow) enclosingShadow).arg0HoldsThis(); |
| } |
| } |
| |
| // ---- argument getting methods |
| |
| private BcelVar thisVar = null; |
| private BcelVar targetVar = null; |
| private BcelVar[] argVars = null; |
| private Map<ResolvedType, AnnotationAccessVar> kindedAnnotationVars = null; |
| private Map<ResolvedType, TypeAnnotationAccessVar> thisAnnotationVars = null; |
| private Map<ResolvedType, TypeAnnotationAccessVar> targetAnnotationVars = null; |
| // private Map/* <UnresolvedType,BcelVar> */[] argAnnotationVars = null; |
| private Map<ResolvedType, AnnotationAccessVar> withinAnnotationVars = null; |
| private Map<ResolvedType, AnnotationAccessVar> withincodeAnnotationVars = null; |
| private boolean allArgVarsInitialized = false; |
| |
| // If in annotation style and the relevant advice is using PJP then this will |
| // be set to true when the closure variable is initialized - if it gets set |
| // (which means link() has been called) then we will need to call unlink() |
| // after the code has been run. |
| boolean closureVarInitialized = false; |
| |
| @Override |
| public Var getThisVar() { |
| if (!hasThis()) { |
| throw new IllegalStateException("no this"); |
| } |
| initializeThisVar(); |
| return thisVar; |
| } |
| |
| @Override |
| public Var getThisAnnotationVar(UnresolvedType forAnnotationType) { |
| if (!hasThis()) { |
| throw new IllegalStateException("no this"); |
| } |
| initializeThisAnnotationVars(); // FIXME asc Why bother with this if we always return one? |
| // Even if we can't find one, we have to return one as we might have this annotation at runtime |
| Var v = thisAnnotationVars.get(forAnnotationType); |
| if (v == null) { |
| v = new TypeAnnotationAccessVar(forAnnotationType.resolve(world), (BcelVar) getThisVar()); |
| } |
| return v; |
| } |
| |
| @Override |
| public Var getTargetVar() { |
| if (!hasTarget()) { |
| throw new IllegalStateException("no target"); |
| } |
| initializeTargetVar(); |
| return targetVar; |
| } |
| |
| @Override |
| public Var getTargetAnnotationVar(UnresolvedType forAnnotationType) { |
| if (!hasTarget()) { |
| throw new IllegalStateException("no target"); |
| } |
| initializeTargetAnnotationVars(); // FIXME asc why bother with this if we always return one? |
| Var v = targetAnnotationVars.get(forAnnotationType); |
| // Even if we can't find one, we have to return one as we might have this annotation at runtime |
| if (v == null) { |
| v = new TypeAnnotationAccessVar(forAnnotationType.resolve(world), (BcelVar) getTargetVar()); |
| } |
| return v; |
| } |
| |
| @Override |
| public Var getArgVar(int i) { |
| ensureInitializedArgVar(i); |
| return argVars[i]; |
| } |
| |
| @Override |
| public Var getArgAnnotationVar(int i, UnresolvedType forAnnotationType) { |
| return new TypeAnnotationAccessVar(forAnnotationType.resolve(world), (BcelVar) getArgVar(i)); |
| // initializeArgAnnotationVars(); |
| // |
| // Var v = (Var) argAnnotationVars[i].get(forAnnotationType); |
| // if (v == null) { |
| // v = new TypeAnnotationAccessVar(forAnnotationType.resolve(world), (BcelVar) getArgVar(i)); |
| // } |
| // return v; |
| } |
| |
| @Override |
| public Var getKindedAnnotationVar(UnresolvedType forAnnotationType) { |
| initializeKindedAnnotationVars(); |
| return kindedAnnotationVars.get(forAnnotationType); |
| } |
| |
| @Override |
| public Var getWithinAnnotationVar(UnresolvedType forAnnotationType) { |
| initializeWithinAnnotationVars(); |
| return withinAnnotationVars.get(forAnnotationType); |
| } |
| |
| @Override |
| public Var getWithinCodeAnnotationVar(UnresolvedType forAnnotationType) { |
| initializeWithinCodeAnnotationVars(); |
| return withincodeAnnotationVars.get(forAnnotationType); |
| } |
| |
| // reflective thisJoinPoint support |
| private BcelVar thisJoinPointVar = null; |
| private boolean isThisJoinPointLazy; |
| private int lazyTjpConsumers = 0; |
| private BcelVar thisJoinPointStaticPartVar = null; |
| |
| // private BcelVar thisEnclosingJoinPointStaticPartVar = null; |
| |
| @Override |
| public final Var getThisJoinPointStaticPartVar() { |
| return getThisJoinPointStaticPartBcelVar(); |
| } |
| |
| @Override |
| public final Var getThisEnclosingJoinPointStaticPartVar() { |
| return getThisEnclosingJoinPointStaticPartBcelVar(); |
| } |
| |
| public void requireThisJoinPoint(boolean hasGuardTest, boolean isAround) { |
| if (!isAround) { |
| if (!hasGuardTest) { |
| isThisJoinPointLazy = false; |
| } else { |
| lazyTjpConsumers++; |
| } |
| } |
| // if (!hasGuardTest) { |
| // isThisJoinPointLazy = false; |
| // } else { |
| // lazyTjpConsumers++; |
| // } |
| if (thisJoinPointVar == null) { |
| thisJoinPointVar = genTempVar(UnresolvedType.forName("org.aspectj.lang.JoinPoint")); |
| } |
| } |
| |
| @Override |
| public Var getThisJoinPointVar() { |
| requireThisJoinPoint(false, false); |
| return thisJoinPointVar; |
| } |
| |
| void initializeThisJoinPoint() { |
| if (thisJoinPointVar == null) { |
| return; |
| } |
| |
| if (isThisJoinPointLazy) { |
| isThisJoinPointLazy = checkLazyTjp(); |
| } |
| |
| if (isThisJoinPointLazy) { |
| appliedLazyTjpOptimization = true; |
| createThisJoinPoint(); // make sure any state needed is initialized, but throw the instructions out |
| |
| if (lazyTjpConsumers == 1) { |
| return; // special case only one lazyTjpUser |
| } |
| |
| InstructionFactory fact = getFactory(); |
| InstructionList il = new InstructionList(); |
| il.append(InstructionConstants.ACONST_NULL); |
| il.append(thisJoinPointVar.createStore(fact)); |
| range.insert(il, Range.OutsideBefore); |
| } else { |
| appliedLazyTjpOptimization = false; |
| InstructionFactory fact = getFactory(); |
| InstructionList il = createThisJoinPoint(); |
| il.append(thisJoinPointVar.createStore(fact)); |
| range.insert(il, Range.OutsideBefore); |
| } |
| } |
| |
| private boolean checkLazyTjp() { |
| // check for around advice |
| for (Iterator<ShadowMunger> i = mungers.iterator(); i.hasNext();) { |
| ShadowMunger munger = i.next(); |
| if (munger instanceof Advice) { |
| if (((Advice) munger).getKind() == AdviceKind.Around) { |
| if (munger.getSourceLocation() != null) { // do we know enough to bother reporting? |
| if (world.getLint().canNotImplementLazyTjp.isEnabled()) { |
| world.getLint().canNotImplementLazyTjp.signal(new String[] { toString() }, getSourceLocation(), |
| new ISourceLocation[] { munger.getSourceLocation() }); |
| } |
| } |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| InstructionList loadThisJoinPoint() { |
| InstructionFactory fact = getFactory(); |
| InstructionList il = new InstructionList(); |
| |
| if (isThisJoinPointLazy) { |
| // If we're lazy, build the join point right here. |
| il.append(createThisJoinPoint()); |
| |
| // Does someone else need it? If so, store it for later retrieval |
| if (lazyTjpConsumers > 1) { |
| il.append(thisJoinPointVar.createStore(fact)); |
| |
| InstructionHandle end = il.append(thisJoinPointVar.createLoad(fact)); |
| |
| il.insert(InstructionFactory.createBranchInstruction(Constants.IFNONNULL, end)); |
| il.insert(thisJoinPointVar.createLoad(fact)); |
| } |
| } else { |
| // If not lazy, its already been built and stored, just retrieve it |
| thisJoinPointVar.appendLoad(il, fact); |
| } |
| |
| return il; |
| } |
| |
| InstructionList createThisJoinPoint() { |
| InstructionFactory fact = getFactory(); |
| InstructionList il = new InstructionList(); |
| |
| BcelVar staticPart = getThisJoinPointStaticPartBcelVar(); |
| staticPart.appendLoad(il, fact); |
| if (hasThis()) { |
| ((BcelVar) getThisVar()).appendLoad(il, fact); |
| } else { |
| il.append(InstructionConstants.ACONST_NULL); |
| } |
| if (hasTarget()) { |
| ((BcelVar) getTargetVar()).appendLoad(il, fact); |
| } else { |
| il.append(InstructionConstants.ACONST_NULL); |
| } |
| |
| switch (getArgCount()) { |
| case 0: |
| il.append(fact.createInvoke("org.aspectj.runtime.reflect.Factory", "makeJP", LazyClassGen.tjpType, new Type[] { |
| LazyClassGen.staticTjpType, Type.OBJECT, Type.OBJECT }, Constants.INVOKESTATIC)); |
| break; |
| case 1: |
| ((BcelVar) getArgVar(0)).appendLoadAndConvert(il, fact, world.getCoreType(ResolvedType.OBJECT)); |
| il.append(fact.createInvoke("org.aspectj.runtime.reflect.Factory", "makeJP", LazyClassGen.tjpType, new Type[] { |
| LazyClassGen.staticTjpType, Type.OBJECT, Type.OBJECT, Type.OBJECT }, Constants.INVOKESTATIC)); |
| break; |
| case 2: |
| ((BcelVar) getArgVar(0)).appendLoadAndConvert(il, fact, world.getCoreType(ResolvedType.OBJECT)); |
| ((BcelVar) getArgVar(1)).appendLoadAndConvert(il, fact, world.getCoreType(ResolvedType.OBJECT)); |
| il.append(fact.createInvoke("org.aspectj.runtime.reflect.Factory", "makeJP", LazyClassGen.tjpType, new Type[] { |
| LazyClassGen.staticTjpType, Type.OBJECT, Type.OBJECT, Type.OBJECT, Type.OBJECT }, Constants.INVOKESTATIC)); |
| break; |
| default: |
| il.append(makeArgsObjectArray()); |
| il.append(fact.createInvoke("org.aspectj.runtime.reflect.Factory", "makeJP", LazyClassGen.tjpType, new Type[] { |
| LazyClassGen.staticTjpType, Type.OBJECT, Type.OBJECT, new ArrayType(Type.OBJECT, 1) }, Constants.INVOKESTATIC)); |
| break; |
| } |
| |
| return il; |
| } |
| |
| public BcelVar getThisJoinPointStaticPartBcelVar() { |
| return getThisJoinPointStaticPartBcelVar(false); |
| } |
| |
| @Override |
| public BcelVar getThisAspectInstanceVar(ResolvedType aspectType) { |
| return new AspectInstanceVar(aspectType); |
| } |
| |
| /** |
| * Get the Var for the xxxxJpStaticPart, xxx = this or enclosing |
| * |
| * @param isEnclosingJp true to have the enclosingJpStaticPart |
| * @return |
| */ |
| public BcelVar getThisJoinPointStaticPartBcelVar(final boolean isEnclosingJp) { |
| if (thisJoinPointStaticPartVar == null) { |
| Field field = getEnclosingClass().getTjpField(this, isEnclosingJp); |
| ResolvedType sjpType = null; |
| if (world.isTargettingAspectJRuntime12()) { // TAG:SUPPORTING12: We didn't have different jpsp types in 1.2 |
| sjpType = world.getCoreType(UnresolvedType.JOINPOINT_STATICPART); |
| } else { |
| sjpType = isEnclosingJp ? world.getCoreType(UnresolvedType.JOINPOINT_ENCLOSINGSTATICPART) : world |
| .getCoreType(UnresolvedType.JOINPOINT_STATICPART); |
| } |
| thisJoinPointStaticPartVar = new BcelFieldRef(sjpType, getEnclosingClass().getClassName(), field.getName()); |
| // getEnclosingClass().warnOnAddedStaticInitializer(this,munger.getSourceLocation()); |
| } |
| return thisJoinPointStaticPartVar; |
| } |
| |
| /** |
| * Get the Var for the enclosingJpStaticPart |
| * |
| * @return |
| */ |
| public BcelVar getThisEnclosingJoinPointStaticPartBcelVar() { |
| if (enclosingShadow == null) { |
| // the enclosing of an execution is itself |
| return getThisJoinPointStaticPartBcelVar(true); |
| } else { |
| return ((BcelShadow) enclosingShadow).getThisJoinPointStaticPartBcelVar(true); |
| } |
| } |
| |
| // ??? need to better understand all the enclosing variants |
| @Override |
| public Member getEnclosingCodeSignature() { |
| if (getKind().isEnclosingKind()) { |
| return getSignature(); |
| } else if (getKind() == Shadow.PreInitialization) { |
| // PreInit doesn't enclose code but its signature |
| // is correctly the signature of the ctor. |
| return getSignature(); |
| } else if (enclosingShadow == null) { |
| return getEnclosingMethod().getMemberView(); |
| } else { |
| return enclosingShadow.getSignature(); |
| } |
| } |
| |
| public Member getRealEnclosingCodeSignature() { |
| return enclosingMethod.getMemberView(); |
| } |
| |
| // public Member getEnclosingCodeSignatureForModel() { |
| // if (getKind().isEnclosingKind()) { |
| // return getSignature(); |
| // } else if (getKind() == Shadow.PreInitialization) { |
| // // PreInit doesn't enclose code but its signature |
| // // is correctly the signature of the ctor. |
| // return getSignature(); |
| // } else if (enclosingShadow == null) { |
| // return getEnclosingMethod().getMemberView(); |
| // } else { |
| // if (enclosingShadow.getKind() == Shadow.MethodExecution && enclosingMethod.getEffectiveSignature() != null) { |
| // |
| // } else { |
| // return enclosingShadow.getSignature(); |
| // } |
| // } |
| // } |
| |
| private InstructionList makeArgsObjectArray() { |
| InstructionFactory fact = getFactory(); |
| BcelVar arrayVar = genTempVar(UnresolvedType.OBJECTARRAY); |
| final InstructionList il = new InstructionList(); |
| int alen = getArgCount(); |
| il.append(Utility.createConstant(fact, alen)); |
| il.append(fact.createNewArray(Type.OBJECT, (short) 1)); |
| arrayVar.appendStore(il, fact); |
| |
| int stateIndex = 0; |
| for (int i = 0, len = getArgCount(); i < len; i++) { |
| arrayVar.appendConvertableArrayStore(il, fact, stateIndex, (BcelVar) getArgVar(i)); |
| stateIndex++; |
| } |
| arrayVar.appendLoad(il, fact); |
| return il; |
| } |
| |
| // ---- initializing var tables |
| |
| /* |
| * initializing this is doesn't do anything, because this is protected from side-effects, so we don't need to copy its location |
| */ |
| |
| private void initializeThisVar() { |
| if (thisVar != null) { |
| return; |
| } |
| thisVar = new BcelVar(getThisType().resolve(world), 0); |
| thisVar.setPositionInAroundState(0); |
| } |
| |
| public void initializeTargetVar() { |
| InstructionFactory fact = getFactory(); |
| if (targetVar != null) { |
| return; |
| } |
| if (getKind().isTargetSameAsThis()) { |
| if (hasThis()) { |
| initializeThisVar(); |
| } |
| targetVar = thisVar; |
| } else { |
| initializeArgVars(); // gotta pop off the args before we find the target |
| UnresolvedType type = getTargetType(); |
| type = ensureTargetTypeIsCorrect(type); |
| targetVar = genTempVar(type, "ajc$target"); |
| range.insert(targetVar.createStore(fact), Range.OutsideBefore); |
| targetVar.setPositionInAroundState(hasThis() ? 1 : 0); |
| } |
| } |
| |
| /* |
| * PR 72528 This method double checks the target type under certain conditions. The Java 1.4 compilers seem to take calls to |
| * clone methods on array types and create bytecode that looks like clone is being called on Object. If we advise a clone call |
| * with around advice we extract the call into a helper method which we can then refer to. Because the type in the bytecode for |
| * the call to clone is Object we create a helper method with an Object parameter - this is not correct as we have lost the fact |
| * that the actual type is an array type. If we don't do the check below we will create code that fails java verification. This |
| * method checks for the peculiar set of conditions and if they are true, it has a sneak peek at the code before the call to see |
| * what is on the stack. |
| */ |
| public UnresolvedType ensureTargetTypeIsCorrect(UnresolvedType tx) { |
| |
| Member msig = getSignature(); |
| if (msig.getArity() == 0 && getKind() == MethodCall && msig.getName().charAt(0) == 'c' && tx.equals(ResolvedType.OBJECT) |
| && msig.getReturnType().equals(ResolvedType.OBJECT) && msig.getName().equals("clone")) { |
| |
| // Lets go back through the code from the start of the shadow |
| InstructionHandle searchPtr = range.getStart().getPrev(); |
| while (Range.isRangeHandle(searchPtr) || searchPtr.getInstruction().isStoreInstruction()) { // ignore this instruction - |
| // it doesnt give us the |
| // info we want |
| searchPtr = searchPtr.getPrev(); |
| } |
| |
| // A load instruction may tell us the real type of what the clone() call is on |
| if (searchPtr.getInstruction().isLoadInstruction()) { |
| LocalVariableTag lvt = LazyMethodGen.getLocalVariableTag(searchPtr, searchPtr.getInstruction().getIndex()); |
| if (lvt != null) { |
| return UnresolvedType.forSignature(lvt.getType()); |
| } |
| } |
| // A field access instruction may tell us the real type of what the clone() call is on |
| if (searchPtr.getInstruction() instanceof FieldInstruction) { |
| FieldInstruction si = (FieldInstruction) searchPtr.getInstruction(); |
| Type t = si.getFieldType(getEnclosingClass().getConstantPool()); |
| return BcelWorld.fromBcel(t); |
| } |
| // A new array instruction obviously tells us it is an array type ! |
| if (searchPtr.getInstruction().opcode == Constants.ANEWARRAY) { |
| // ANEWARRAY ana = (ANEWARRAY)searchPoint.getInstruction(); |
| // Type t = ana.getType(getEnclosingClass().getConstantPool()); |
| // Just use a standard java.lang.object array - that will work fine |
| return BcelWorld.fromBcel(new ArrayType(Type.OBJECT, 1)); |
| } |
| // A multi new array instruction obviously tells us it is an array type ! |
| if (searchPtr.getInstruction() instanceof MULTIANEWARRAY) { |
| MULTIANEWARRAY ana = (MULTIANEWARRAY) searchPtr.getInstruction(); |
| // Type t = ana.getType(getEnclosingClass().getConstantPool()); |
| // t = new ArrayType(t,ana.getDimensions()); |
| // Just use a standard java.lang.object array - that will work fine |
| return BcelWorld.fromBcel(new ArrayType(Type.OBJECT, ana.getDimensions())); |
| } |
| throw new BCException("Can't determine real target of clone() when processing instruction " |
| + searchPtr.getInstruction() + ". Perhaps avoid selecting clone with your pointcut?"); |
| } |
| return tx; |
| } |
| |
| public void ensureInitializedArgVar(int argNumber) { |
| if (allArgVarsInitialized || (argVars != null && argVars[argNumber] != null)) { |
| return; |
| } |
| InstructionFactory fact = getFactory(); |
| int len = getArgCount(); |
| if (argVars == null) { |
| argVars = new BcelVar[len]; |
| } |
| |
| // Need to initialize argument i |
| int positionOffset = (hasTarget() ? 1 : 0) + ((hasThis() && !getKind().isTargetSameAsThis()) ? 1 : 0); |
| |
| if (getKind().argsOnStack()) { |
| // Let's just do them all now since they are on the stack |
| // we move backwards because we're popping off the stack |
| for (int i = len - 1; i >= 0; i--) { |
| UnresolvedType type = getArgType(i); |
| BcelVar tmp = genTempVar(type, "ajc$arg" + i); |
| range.insert(tmp.createStore(getFactory()), Range.OutsideBefore); |
| int position = i; |
| position += positionOffset; |
| tmp.setPositionInAroundState(position); |
| argVars[i] = tmp; |
| } |
| allArgVarsInitialized = true; |
| } else { |
| int index = 0; |
| if (arg0HoldsThis()) { |
| index++; |
| } |
| boolean allInited = true; |
| for (int i = 0; i < len; i++) { |
| UnresolvedType type = getArgType(i); |
| if (i == argNumber) { |
| argVars[argNumber] = genTempVar(type, "ajc$arg" + argNumber); |
| range.insert(argVars[argNumber].createCopyFrom(fact, index), Range.OutsideBefore); |
| argVars[argNumber].setPositionInAroundState(argNumber + positionOffset); |
| } |
| allInited = allInited && argVars[i] != null; |
| index += type.getSize(); |
| } |
| if (allInited && (argNumber + 1) == len) { |
| allArgVarsInitialized = true; |
| } |
| } |
| } |
| |
| /** |
| * Initialize all the available arguments at the shadow. This means creating a copy of them that we can then use for advice |
| * calls (the copy ensures we are not affected by other advice changing the values). This method initializes all arguments |
| * whereas the method ensureInitializedArgVar will only ensure a single argument is setup. |
| */ |
| public void initializeArgVars() { |
| if (allArgVarsInitialized) { |
| return; |
| } |
| InstructionFactory fact = getFactory(); |
| int len = getArgCount(); |
| if (argVars == null) { |
| argVars = new BcelVar[len]; |
| } |
| int positionOffset = (hasTarget() ? 1 : 0) + ((hasThis() && !getKind().isTargetSameAsThis()) ? 1 : 0); |
| |
| if (getKind().argsOnStack()) { |
| // we move backwards because we're popping off the stack |
| for (int i = len - 1; i >= 0; i--) { |
| UnresolvedType type = getArgType(i); |
| BcelVar tmp = genTempVar(type, "ajc$arg" + i); |
| range.insert(tmp.createStore(getFactory()), Range.OutsideBefore); |
| int position = i; |
| position += positionOffset; |
| tmp.setPositionInAroundState(position); |
| argVars[i] = tmp; |
| } |
| } else { |
| int index = 0; |
| if (arg0HoldsThis()) { |
| index++; |
| } |
| |
| for (int i = 0; i < len; i++) { |
| UnresolvedType type = getArgType(i); |
| if (argVars[i] == null) { |
| BcelVar tmp = genTempVar(type, "ajc$arg" + i); |
| range.insert(tmp.createCopyFrom(fact, index), Range.OutsideBefore); |
| argVars[i] = tmp; |
| tmp.setPositionInAroundState(i + positionOffset); |
| } |
| index += type.resolve(world).getSize(); |
| } |
| } |
| allArgVarsInitialized = true; |
| |
| } |
| |
| public void initializeForAroundClosure() { |
| initializeArgVars(); |
| if (hasTarget()) { |
| initializeTargetVar(); |
| } |
| if (hasThis()) { |
| initializeThisVar(); |
| // System.out.println("initialized: " + this + " thisVar = " + thisVar); |
| } |
| } |
| |
| public void initializeThisAnnotationVars() { |
| if (thisAnnotationVars != null) { |
| return; |
| } |
| thisAnnotationVars = new HashMap<ResolvedType, TypeAnnotationAccessVar>(); |
| // populate.. |
| } |
| |
| public void initializeTargetAnnotationVars() { |
| if (targetAnnotationVars != null) { |
| return; |
| } |
| if (getKind().isTargetSameAsThis()) { |
| if (hasThis()) { |
| initializeThisAnnotationVars(); |
| } |
| targetAnnotationVars = thisAnnotationVars; |
| } else { |
| targetAnnotationVars = new HashMap<ResolvedType, TypeAnnotationAccessVar>(); |
| ResolvedType[] rtx = this.getTargetType().resolve(world).getAnnotationTypes(); // what about annotations we havent |
| // gotten yet but we will get in |
| // subclasses? |
| for (int i = 0; i < rtx.length; i++) { |
| ResolvedType typeX = rtx[i]; |
| targetAnnotationVars.put(typeX, new TypeAnnotationAccessVar(typeX, (BcelVar) getTargetVar())); |
| } |
| // populate. |
| } |
| } |
| |
| // public void initializeArgAnnotationVars() { |
| // if (argAnnotationVars != null) { |
| // return; |
| // } |
| // int numArgs = getArgCount(); |
| // argAnnotationVars = new Map[numArgs]; |
| // for (int i = 0; i < argAnnotationVars.length; i++) { |
| // argAnnotationVars[i] = new HashMap(); |
| // // FIXME asc just delete this logic - we always build the Var on demand, as we don't know at weave time |
| // // what the full set of annotations could be (due to static/dynamic type differences...) |
| // } |
| // } |
| |
| protected ResolvedMember getRelevantMember(ResolvedMember foundMember, Member relevantMember, ResolvedType relevantType) { |
| if (foundMember != null) { |
| return foundMember; |
| } |
| |
| foundMember = getSignature().resolve(world); |
| if (foundMember == null && relevantMember != null) { |
| foundMember = relevantType.lookupMemberWithSupersAndITDs(relevantMember); |
| } |
| |
| // check the ITD'd dooberries |
| List<ConcreteTypeMunger> mungers = relevantType.resolve(world).getInterTypeMungers(); |
| for (ConcreteTypeMunger typeMunger : mungers) { |
| if (typeMunger.getMunger() instanceof NewMethodTypeMunger || typeMunger.getMunger() instanceof NewConstructorTypeMunger) { |
| ResolvedMember fakerm = typeMunger.getSignature(); |
| if (fakerm.getName().equals(getSignature().getName()) |
| && fakerm.getParameterSignature().equals(getSignature().getParameterSignature())) { |
| if (foundMember.getKind() == ResolvedMember.CONSTRUCTOR) { |
| foundMember = AjcMemberMaker.interConstructor(relevantType, foundMember, typeMunger.getAspectType()); |
| } else { |
| foundMember = AjcMemberMaker.interMethod(foundMember, typeMunger.getAspectType(), false); |
| // ResolvedMember o = AjcMemberMaker.interMethodBody(fakerm, typeMunger.getAspectType()); |
| // // Object os = o.getAnnotations(); |
| // ResolvedMember foundMember2 = findMethod(typeMunger.getAspectType(), o); |
| // Object os2 = foundMember2.getAnnotations(); |
| // int stop = 1; |
| // foundMember = foundMember2; |
| // foundMember = AjcMemberMaker.interMethod(foundMember, typeMunger.getAspectType()); |
| } |
| // in the above.. what about if it's on an Interface? Can that happen? |
| // then the last arg of the above should be true |
| return foundMember; |
| } |
| } |
| } |
| return foundMember; |
| } |
| |
| protected ResolvedType[] getAnnotations(ResolvedMember foundMember, Member relevantMember, ResolvedType relevantType) { |
| if (foundMember == null) { |
| // check the ITD'd dooberries |
| List<ConcreteTypeMunger> mungers = relevantType.resolve(world).getInterTypeMungers(); |
| for (Iterator<ConcreteTypeMunger> iter = mungers.iterator(); iter.hasNext();) { |
| Object munger = iter.next(); |
| ConcreteTypeMunger typeMunger = (ConcreteTypeMunger) munger; |
| if (typeMunger.getMunger() instanceof NewMethodTypeMunger |
| || typeMunger.getMunger() instanceof NewConstructorTypeMunger) { |
| ResolvedMember fakerm = typeMunger.getSignature(); |
| // if (fakerm.hasAnnotations()) |
| |
| ResolvedMember ajcMethod = (getSignature().getKind() == ResolvedMember.CONSTRUCTOR ? AjcMemberMaker |
| .postIntroducedConstructor(typeMunger.getAspectType(), fakerm.getDeclaringType(), |
| fakerm.getParameterTypes()) : AjcMemberMaker.interMethodDispatcher(fakerm, |
| typeMunger.getAspectType())); |
| // AjcMemberMaker.interMethodBody(fakerm,typeMunger.getAspectType())); |
| ResolvedMember rmm = findMethod(typeMunger.getAspectType(), ajcMethod); |
| if (fakerm.getName().equals(getSignature().getName()) |
| && fakerm.getParameterSignature().equals(getSignature().getParameterSignature())) { |
| relevantType = typeMunger.getAspectType(); |
| foundMember = rmm; |
| return foundMember.getAnnotationTypes(); |
| } |
| } |
| } |
| // didn't find in ITDs, look in supers |
| foundMember = relevantType.lookupMemberWithSupersAndITDs(relevantMember); |
| if (foundMember == null) { |
| throw new IllegalStateException("Couldn't find member " + relevantMember + " for type " + relevantType); |
| } |
| } |
| return foundMember.getAnnotationTypes(); |
| } |
| |
| /** |
| * By determining what "kind" of shadow we are, we can find out the annotations on the appropriate element (method, field, |
| * constructor, type). Then create one BcelVar entry in the map for each annotation, keyed by annotation type. |
| */ |
| public void initializeKindedAnnotationVars() { |
| if (kindedAnnotationVars != null) { |
| return; |
| } |
| kindedAnnotationVars = new HashMap<ResolvedType, AnnotationAccessVar>(); |
| |
| ResolvedType[] annotations = null; |
| Member shadowSignature = getSignature(); |
| Member annotationHolder = getSignature(); |
| ResolvedType relevantType = shadowSignature.getDeclaringType().resolve(world); |
| |
| if (relevantType.isRawType() || relevantType.isParameterizedType()) { |
| relevantType = relevantType.getGenericType(); |
| } |
| |
| // Determine the annotations that are of interest |
| if (getKind() == Shadow.StaticInitialization) { |
| annotations = relevantType.resolve(world).getAnnotationTypes(); |
| } else if (getKind() == Shadow.MethodCall || getKind() == Shadow.ConstructorCall) { |
| ResolvedMember foundMember = findMethod2(relevantType.resolve(world).getDeclaredMethods(), getSignature()); |
| annotations = getAnnotations(foundMember, shadowSignature, relevantType); |
| annotationHolder = getRelevantMember(foundMember, shadowSignature, relevantType); |
| relevantType = annotationHolder.getDeclaringType().resolve(world); |
| } else if (getKind() == Shadow.FieldSet || getKind() == Shadow.FieldGet) { |
| annotationHolder = findField(relevantType.getDeclaredFields(), getSignature()); |
| |
| if (annotationHolder == null) { |
| // check the ITD'd dooberries |
| List<ConcreteTypeMunger> mungers = relevantType.resolve(world).getInterTypeMungers(); |
| for (ConcreteTypeMunger typeMunger : mungers) { |
| if (typeMunger.getMunger() instanceof NewFieldTypeMunger) { |
| ResolvedMember fakerm = typeMunger.getSignature(); |
| // if (fakerm.hasAnnotations()) |
| ResolvedMember ajcMethod = AjcMemberMaker.interFieldInitializer(fakerm, typeMunger.getAspectType()); |
| ResolvedMember rmm = findMethod(typeMunger.getAspectType(), ajcMethod); |
| if (fakerm.equals(getSignature())) { |
| relevantType = typeMunger.getAspectType(); |
| annotationHolder = rmm; |
| } |
| } |
| } |
| } |
| annotations = ((ResolvedMember) annotationHolder).getAnnotationTypes(); |
| |
| } else if (getKind() == Shadow.MethodExecution || getKind() == Shadow.ConstructorExecution |
| || getKind() == Shadow.AdviceExecution) { |
| |
| ResolvedMember foundMember = findMethod2(relevantType.getDeclaredMethods(), getSignature()); |
| annotations = getAnnotations(foundMember, shadowSignature, relevantType); |
| annotationHolder = getRelevantMember(foundMember, annotationHolder, relevantType); |
| UnresolvedType ut = annotationHolder.getDeclaringType(); |
| relevantType = ut.resolve(world); |
| |
| } else if (getKind() == Shadow.ExceptionHandler) { |
| relevantType = getSignature().getParameterTypes()[0].resolve(world); |
| annotations = relevantType.getAnnotationTypes(); |
| |
| } else if (getKind() == Shadow.PreInitialization || getKind() == Shadow.Initialization) { |
| ResolvedMember found = findMethod2(relevantType.getDeclaredMethods(), getSignature()); |
| annotations = found.getAnnotationTypes(); |
| } |
| |
| if (annotations == null) { |
| // We can't have recognized the shadow - should blow up now to be on the safe side |
| throw new BCException("Could not discover annotations for shadow: " + getKind()); |
| } |
| |
| for (ResolvedType annotationType : annotations) { |
| AnnotationAccessVar accessVar = new AnnotationAccessVar(this, getKind(), annotationType.resolve(world), relevantType, |
| annotationHolder, false); |
| kindedAnnotationVars.put(annotationType, accessVar); |
| } |
| } |
| |
| private ResolvedMember findMethod2(ResolvedMember members[], Member sig) { |
| String signatureName = sig.getName(); |
| String parameterSignature = sig.getParameterSignature(); |
| for (ResolvedMember member : members) { |
| if (member.getName().equals(signatureName) && member.getParameterSignature().equals(parameterSignature)) { |
| return member; |
| } |
| } |
| return null; |
| } |
| |
| private ResolvedMember findMethod(ResolvedType aspectType, ResolvedMember ajcMethod) { |
| ResolvedMember decMethods[] = aspectType.getDeclaredMethods(); |
| for (int i = 0; i < decMethods.length; i++) { |
| ResolvedMember member = decMethods[i]; |
| if (member.equals(ajcMethod)) { |
| return member; |
| } |
| } |
| return null; |
| } |
| |
| private ResolvedMember findField(ResolvedMember[] members, Member lookingFor) { |
| for (int i = 0; i < members.length; i++) { |
| ResolvedMember member = members[i]; |
| if (member.getName().equals(getSignature().getName()) && member.getType().equals(getSignature().getType())) { |
| return member; |
| } |
| } |
| return null; |
| } |
| |
| public void initializeWithinAnnotationVars() { |
| if (withinAnnotationVars != null) { |
| return; |
| } |
| withinAnnotationVars = new HashMap<ResolvedType, AnnotationAccessVar>(); |
| |
| ResolvedType[] annotations = getEnclosingType().resolve(world).getAnnotationTypes(); |
| for (int i = 0; i < annotations.length; i++) { |
| ResolvedType ann = annotations[i]; |
| Kind k = Shadow.StaticInitialization; |
| withinAnnotationVars.put(ann, new AnnotationAccessVar(this, k, ann, getEnclosingType(), null, true)); |
| } |
| } |
| |
| public void initializeWithinCodeAnnotationVars() { |
| if (withincodeAnnotationVars != null) { |
| return; |
| } |
| withincodeAnnotationVars = new HashMap<ResolvedType, AnnotationAccessVar>(); |
| |
| // For some shadow we are interested in annotations on the method containing that shadow. |
| ResolvedType[] annotations = getEnclosingMethod().getMemberView().getAnnotationTypes(); |
| for (int i = 0; i < annotations.length; i++) { |
| ResolvedType ann = annotations[i]; |
| Kind k = (getEnclosingMethod().getMemberView().getKind() == Member.CONSTRUCTOR ? Shadow.ConstructorExecution |
| : Shadow.MethodExecution); |
| withincodeAnnotationVars.put(ann, new AnnotationAccessVar(this, k, ann, getEnclosingType(), |
| getEnclosingCodeSignature(), true)); |
| } |
| } |
| |
| // ---- weave methods |
| |
| void weaveBefore(BcelAdvice munger) { |
| range.insert(munger.getAdviceInstructions(this, null, range.getRealStart()), Range.InsideBefore); |
| } |
| |
| public void weaveAfter(BcelAdvice munger) { |
| weaveAfterThrowing(munger, UnresolvedType.THROWABLE); |
| weaveAfterReturning(munger); |
| } |
| |
| /** |
| * The basic strategy here is to add a set of instructions at the end of the shadow range that dispatch the advice, and then |
| * return whatever the shadow was going to return anyway. |
| * |
| * To achieve this, we note all the return statements in the advice, and replace them with code that: 1) stores the return value |
| * on top of the stack in a temp var 2) jumps to the start of our advice block 3) restores the return value at the end of the |
| * advice block before ultimately returning |
| * |
| * We also need to bind the return value into a returning parameter, if the advice specified one. |
| */ |
| public void weaveAfterReturning(BcelAdvice munger) { |
| List<InstructionHandle> returns = findReturnInstructions(); |
| boolean hasReturnInstructions = !returns.isEmpty(); |
| |
| // list of instructions that handle the actual return from the join point |
| InstructionList retList = new InstructionList(); |
| |
| // variable that holds the return value |
| BcelVar returnValueVar = null; |
| |
| if (hasReturnInstructions) { |
| returnValueVar = generateReturnInstructions(returns, retList); |
| } else { |
| // we need at least one instruction, as the target for jumps |
| retList.append(InstructionConstants.NOP); |
| } |
| |
| // list of instructions for dispatching to the advice itself |
| InstructionList advice = getAfterReturningAdviceDispatchInstructions(munger, retList.getStart()); |
| |
| if (hasReturnInstructions) { |
| InstructionHandle gotoTarget = advice.getStart(); |
| for (Iterator<InstructionHandle> i = returns.iterator(); i.hasNext();) { |
| InstructionHandle ih = i.next(); |
| retargetReturnInstruction(munger.hasExtraParameter(), returnValueVar, gotoTarget, ih); |
| } |
| } |
| |
| range.append(advice); |
| range.append(retList); |
| } |
| |
| /** |
| * @return a list of all the return instructions in the range of this shadow |
| */ |
| private List<InstructionHandle> findReturnInstructions() { |
| List<InstructionHandle> returns = new ArrayList<InstructionHandle>(); |
| for (InstructionHandle ih = range.getStart(); ih != range.getEnd(); ih = ih.getNext()) { |
| if (ih.getInstruction().isReturnInstruction()) { |
| returns.add(ih); |
| } |
| } |
| return returns; |
| } |
| |
| /** |
| * Given a list containing all the return instruction handles for this shadow, finds the last return instruction and copies it, |
| * making this the ultimate return. If the shadow has a non-void return type, we also create a temporary variable to hold the |
| * return value, and load the value from this var before returning (see pr148007 for why we do this - it works around a JRockit |
| * bug, and is also closer to what javac generates) |
| * |
| * Sometimes the 'last return' isnt the right one - some rogue code can include the real return from the body of a subroutine |
| * that exists at the end of the method. In this case the last return is RETURN but that may not be correct for a method with a |
| * non-void return type... pr151673 |
| * |
| * @param returns list of all the return instructions in the shadow |
| * @param returnInstructions instruction list into which the return instructions should be generated |
| * @return the variable holding the return value, if needed |
| */ |
| private BcelVar generateReturnInstructions(List<InstructionHandle> returns, InstructionList returnInstructions) { |
| BcelVar returnValueVar = null; |
| if (this.hasANonVoidReturnType()) { |
| // Find the last *correct* return - this is a method with a non-void return type |
| // so ignore RETURN |
| Instruction newReturnInstruction = null; |
| int i = returns.size() - 1; |
| while (newReturnInstruction == null && i >= 0) { |
| InstructionHandle ih = returns.get(i); |
| if (ih.getInstruction().opcode != Constants.RETURN) { |
| newReturnInstruction = Utility.copyInstruction(ih.getInstruction()); |
| } |
| i--; |
| } |
| returnValueVar = genTempVar(this.getReturnType()); |
| returnValueVar.appendLoad(returnInstructions, getFactory()); |
| returnInstructions.append(newReturnInstruction); |
| } else { |
| InstructionHandle lastReturnHandle = returns.get(returns.size() - 1); |
| Instruction newReturnInstruction = Utility.copyInstruction(lastReturnHandle.getInstruction()); |
| returnInstructions.append(newReturnInstruction); |
| } |
| return returnValueVar; |
| } |
| |
| /** |
| * @return true, iff this shadow returns a value |
| */ |
| private boolean hasANonVoidReturnType() { |
| return !this.getReturnType().equals(UnresolvedType.VOID); |
| } |
| |
| /** |
| * Get the list of instructions used to dispatch to the after advice |
| * |
| * @param munger |
| * @param firstInstructionInReturnSequence |
| * @return |
| */ |
| private InstructionList getAfterReturningAdviceDispatchInstructions(BcelAdvice munger, |
| InstructionHandle firstInstructionInReturnSequence) { |
| InstructionList advice = new InstructionList(); |
| |
| BcelVar tempVar = null; |
| if (munger.hasExtraParameter()) { |
| tempVar = insertAdviceInstructionsForBindingReturningParameter(advice); |
| } |
| advice.append(munger.getAdviceInstructions(this, tempVar, firstInstructionInReturnSequence)); |
| return advice; |
| } |
| |
| /** |
| * If the after() returning(Foo f) form is used, bind the return value to the parameter. If the shadow returns void, bind null. |
| * |
| * @param advice |
| * @return |
| */ |
| private BcelVar insertAdviceInstructionsForBindingReturningParameter(InstructionList advice) { |
| BcelVar tempVar; |
| UnresolvedType tempVarType = getReturnType(); |
| if (tempVarType.equals(UnresolvedType.VOID)) { |
| tempVar = genTempVar(UnresolvedType.OBJECT); |
| advice.append(InstructionConstants.ACONST_NULL); |
| tempVar.appendStore(advice, getFactory()); |
| } else { |
| tempVar = genTempVar(tempVarType); |
| advice.append(InstructionFactory.createDup(tempVarType.getSize())); |
| tempVar.appendStore(advice, getFactory()); |
| } |
| return tempVar; |
| } |
| |
| /** |
| * Helper method for weaveAfterReturning |
| * |
| * Each return instruction in the method body is retargeted by calling this method. The return instruction is replaced by up to |
| * three instructions: 1) if the shadow returns a value, and that value is bound to an after returning parameter, then we DUP |
| * the return value on the top of the stack 2) if the shadow returns a value, we store it in the returnValueVar (it will be |
| * retrieved from here when we ultimately return after the advice dispatch) 3) if the return was the last instruction, we add a |
| * NOP (it will fall through to the advice dispatch), otherwise we add a GOTO that branches to the supplied gotoTarget (start of |
| * the advice dispatch) |
| */ |
| private void retargetReturnInstruction(boolean hasReturningParameter, BcelVar returnValueVar, InstructionHandle gotoTarget, |
| InstructionHandle returnHandle) { |
| // pr148007, work around JRockit bug |
| // replace ret with store into returnValueVar, followed by goto if not |
| // at the end of the instruction list... |
| InstructionList newInstructions = new InstructionList(); |
| if (returnValueVar != null) { |
| if (hasReturningParameter) { |
| // we have to dup the return val before consuming it... |
| newInstructions.append(InstructionFactory.createDup(this.getReturnType().getSize())); |
| } |
| // store the return value into this var |
| returnValueVar.appendStore(newInstructions, getFactory()); |
| } |
| if (!isLastInstructionInRange(returnHandle, range)) { |
| newInstructions.append(InstructionFactory.createBranchInstruction(Constants.GOTO, gotoTarget)); |
| } |
| if (newInstructions.isEmpty()) { |
| newInstructions.append(InstructionConstants.NOP); |
| } |
| Utility.replaceInstruction(returnHandle, newInstructions, enclosingMethod); |
| } |
| |
| private boolean isLastInstructionInRange(InstructionHandle ih, ShadowRange aRange) { |
| return ih.getNext() == aRange.getEnd(); |
| } |
| |
| public void weaveAfterThrowing(BcelAdvice munger, UnresolvedType catchType) { |
| // a good optimization would be not to generate anything here |
| // if the shadow is GUARANTEED empty (i.e., there's NOTHING, not even |
| // a shadow, inside me). |
| if (getRange().getStart().getNext() == getRange().getEnd()) { |
| return; |
| } |
| InstructionFactory fact = getFactory(); |
| InstructionList handler = new InstructionList(); |
| BcelVar exceptionVar = genTempVar(catchType); |
| exceptionVar.appendStore(handler, fact); |
| |
| // pr62642 |
| // I will now jump through some firey BCEL hoops to generate a trivial bit of code: |
| // if (exc instanceof ExceptionInInitializerError) |
| // throw (ExceptionInInitializerError)exc; |
| if (this.getEnclosingMethod().getName().equals("<clinit>")) { |
| ResolvedType eiieType = world.resolve("java.lang.ExceptionInInitializerError"); |
| ObjectType eiieBcelType = (ObjectType) BcelWorld.makeBcelType(eiieType); |
| InstructionList ih = new InstructionList(InstructionConstants.NOP); |
| handler.append(exceptionVar.createLoad(fact)); |
| handler.append(fact.createInstanceOf(eiieBcelType)); |
| InstructionBranch bi = InstructionFactory.createBranchInstruction(Constants.IFEQ, ih.getStart()); |
| handler.append(bi); |
| handler.append(exceptionVar.createLoad(fact)); |
| handler.append(fact.createCheckCast(eiieBcelType)); |
| handler.append(InstructionConstants.ATHROW); |
| handler.append(ih); |
| } |
| |
| InstructionList endHandler = new InstructionList(exceptionVar.createLoad(fact)); |
| handler.append(munger.getAdviceInstructions(this, exceptionVar, endHandler.getStart())); |
| handler.append(endHandler); |
| handler.append(InstructionConstants.ATHROW); |
| InstructionHandle handlerStart = handler.getStart(); |
| |
| if (isFallsThrough()) { |
| InstructionHandle jumpTarget = handler.append(InstructionConstants.NOP); |
| handler.insert(InstructionFactory.createBranchInstruction(Constants.GOTO, jumpTarget)); |
| } |
| InstructionHandle protectedEnd = handler.getStart(); |
| range.insert(handler, Range.InsideAfter); |
| |
| enclosingMethod.addExceptionHandler(range.getStart().getNext(), protectedEnd.getPrev(), handlerStart, |
| (ObjectType) BcelWorld.makeBcelType(catchType), // ???Type.THROWABLE, |
| // high priority if our args are on the stack |
| getKind().hasHighPriorityExceptions()); |
| } |
| |
| // ??? this shares a lot of code with the above weaveAfterThrowing |
| // ??? would be nice to abstract that to say things only once |
| public void weaveSoftener(BcelAdvice munger, UnresolvedType catchType) { |
| // a good optimization would be not to generate anything here |
| // if the shadow is GUARANTEED empty (i.e., there's NOTHING, not even |
| // a shadow, inside me). |
| if (getRange().getStart().getNext() == getRange().getEnd()) { |
| return; |
| } |
| |
| InstructionFactory fact = getFactory(); |
| InstructionList handler = new InstructionList(); |
| InstructionList rtExHandler = new InstructionList(); |
| BcelVar exceptionVar = genTempVar(catchType); |
| |
| handler.append(fact.createNew(NameMangler.SOFT_EXCEPTION_TYPE)); |
| handler.append(InstructionFactory.createDup(1)); |
| handler.append(exceptionVar.createLoad(fact)); |
| handler.append(fact.createInvoke(NameMangler.SOFT_EXCEPTION_TYPE, "<init>", Type.VOID, new Type[] { Type.THROWABLE }, |
| Constants.INVOKESPECIAL)); // ??? special |
| handler.append(InstructionConstants.ATHROW); |
| |
| // ENH 42737 |
| exceptionVar.appendStore(rtExHandler, fact); |
| // aload_1 |
| rtExHandler.append(exceptionVar.createLoad(fact)); |
| // instanceof class java/lang/RuntimeException |
| rtExHandler.append(fact.createInstanceOf(new ObjectType("java.lang.RuntimeException"))); |
| // ifeq go to new SOFT_EXCEPTION_TYPE instruction |
| rtExHandler.append(InstructionFactory.createBranchInstruction(Constants.IFEQ, handler.getStart())); |
| // aload_1 |
| rtExHandler.append(exceptionVar.createLoad(fact)); |
| // athrow |
| rtExHandler.append(InstructionFactory.ATHROW); |
| |
| InstructionHandle handlerStart = rtExHandler.getStart(); |
| |
| if (isFallsThrough()) { |
| InstructionHandle jumpTarget = range.getEnd();// handler.append(fact.NOP); |
| rtExHandler.insert(InstructionFactory.createBranchInstruction(Constants.GOTO, jumpTarget)); |
| } |
| |
| rtExHandler.append(handler); |
| |
| InstructionHandle protectedEnd = rtExHandler.getStart(); |
| range.insert(rtExHandler, Range.InsideAfter); |
| |
| enclosingMethod.addExceptionHandler(range.getStart().getNext(), protectedEnd.getPrev(), handlerStart, |
| (ObjectType) BcelWorld.makeBcelType(catchType), |
| // high priority if our args are on the stack |
| getKind().hasHighPriorityExceptions()); |
| } |
| |
| public void weavePerObjectEntry(final BcelAdvice munger, final BcelVar onVar) { |
| final InstructionFactory fact = getFactory(); |
| |
| InstructionList entryInstructions = new InstructionList(); |
| InstructionList entrySuccessInstructions = new InstructionList(); |
| onVar.appendLoad(entrySuccessInstructions, fact); |
| |
| entrySuccessInstructions |
| .append(Utility.createInvoke(fact, world, AjcMemberMaker.perObjectBind(munger.getConcreteAspect()))); |
| |
| InstructionList testInstructions = munger.getTestInstructions(this, entrySuccessInstructions.getStart(), |
| range.getRealStart(), entrySuccessInstructions.getStart()); |
| |
| entryInstructions.append(testInstructions); |
| entryInstructions.append(entrySuccessInstructions); |
| |
| range.insert(entryInstructions, Range.InsideBefore); |
| } |
| |
| // PTWIMPL Create static initializer to call the aspect factory |
| /** |
| * Causes the aspect instance to be *set* for later retrievable through localAspectof()/aspectOf() |
| */ |
| public void weavePerTypeWithinAspectInitialization(final BcelAdvice munger, UnresolvedType t) { |
| ResolvedType tResolved = t.resolve(world); |
| if (tResolved.isInterface()) { |
| return; // Don't initialize statics in interfaces |
| } |
| ResolvedType aspectRT = munger.getConcreteAspect(); |
| BcelWorld.getBcelObjectType(aspectRT); |
| |
| // Although matched, if the visibility rules prevent the aspect from seeing this type, don't |
| // insert any code (easier to do it here than try to affect the matching logic, unfortunately) |
| if (!(tResolved.canBeSeenBy(aspectRT) || aspectRT.isPrivilegedAspect())) { |
| return; |
| } |
| |
| final InstructionFactory fact = getFactory(); |
| |
| InstructionList entryInstructions = new InstructionList(); |
| InstructionList entrySuccessInstructions = new InstructionList(); |
| |
| String aspectname = munger.getConcreteAspect().getName(); |
| |
| String ptwField = NameMangler.perTypeWithinFieldForTarget(munger.getConcreteAspect()); |
| entrySuccessInstructions.append(InstructionFactory.PUSH(fact.getConstantPool(), t.getName())); |
| |
| entrySuccessInstructions.append(fact.createInvoke(aspectname, "ajc$createAspectInstance", new ObjectType(aspectname), |
| new Type[] { new ObjectType("java.lang.String") }, Constants.INVOKESTATIC)); |
| entrySuccessInstructions.append(fact.createPutStatic(t.getName(), ptwField, new ObjectType(aspectname))); |
| |
| entryInstructions.append(entrySuccessInstructions); |
| |
| range.insert(entryInstructions, Range.InsideBefore); |
| } |
| |
| public void weaveCflowEntry(final BcelAdvice munger, final Member cflowField) { |
| final boolean isPer = munger.getKind() == AdviceKind.PerCflowBelowEntry || munger.getKind() == AdviceKind.PerCflowEntry; |
| if (!isPer && getKind() == PreInitialization) { |
| return; |
| } |
| final Type objectArrayType = new ArrayType(Type.OBJECT, 1); |
| final InstructionFactory fact = getFactory(); |
| |
| final BcelVar testResult = genTempVar(UnresolvedType.BOOLEAN); |
| |
| InstructionList entryInstructions = new InstructionList(); |
| { |
| InstructionList entrySuccessInstructions = new InstructionList(); |
| |
| if (munger.hasDynamicTests()) { |
| entryInstructions.append(Utility.createConstant(fact, 0)); |
| testResult.appendStore(entryInstructions, fact); |
| |
| entrySuccessInstructions.append(Utility.createConstant(fact, 1)); |
| testResult.appendStore(entrySuccessInstructions, fact); |
| } |
| |
| if (isPer) { |
| entrySuccessInstructions.append(fact.createInvoke(munger.getConcreteAspect().getName(), |
| NameMangler.PERCFLOW_PUSH_METHOD, Type.VOID, new Type[] {}, Constants.INVOKESTATIC)); |
| } else { |
| BcelVar[] cflowStateVars = munger.getExposedStateAsBcelVars(false); |
| |
| if (cflowStateVars.length == 0) { |
| // This should be getting managed by a counter - lets make sure. |
| if (!cflowField.getType().getName().endsWith("CFlowCounter")) { |
| throw new RuntimeException("Incorrectly attempting counter operation on stacked cflow"); |
| } |
| entrySuccessInstructions.append(Utility.createGet(fact, cflowField)); |
| // arrayVar.appendLoad(entrySuccessInstructions, fact); |
| entrySuccessInstructions.append(fact.createInvoke(NameMangler.CFLOW_COUNTER_TYPE, "inc", Type.VOID, |
| new Type[] {}, Constants.INVOKEVIRTUAL)); |
| } else { |
| BcelVar arrayVar = genTempVar(UnresolvedType.OBJECTARRAY); |
| |
| int alen = cflowStateVars.length; |
| entrySuccessInstructions.append(Utility.createConstant(fact, alen)); |
| entrySuccessInstructions.append(fact.createNewArray(Type.OBJECT, (short) 1)); |
| arrayVar.appendStore(entrySuccessInstructions, fact); |
| |
| for (int i = 0; i < alen; i++) { |
| arrayVar.appendConvertableArrayStore(entrySuccessInstructions, fact, i, cflowStateVars[i]); |
| } |
| |
| entrySuccessInstructions.append(Utility.createGet(fact, cflowField)); |
| arrayVar.appendLoad(entrySuccessInstructions, fact); |
| |
| entrySuccessInstructions.append(fact.createInvoke(NameMangler.CFLOW_STACK_TYPE, "push", Type.VOID, |
| new Type[] { objectArrayType }, Constants.INVOKEVIRTUAL)); |
| } |
| } |
| |
| InstructionList testInstructions = munger.getTestInstructions(this, entrySuccessInstructions.getStart(), |
| range.getRealStart(), entrySuccessInstructions.getStart()); |
| entryInstructions.append(testInstructions); |
| entryInstructions.append(entrySuccessInstructions); |
| } |
| |
| BcelAdvice exitAdvice = new BcelAdvice(null, null, null, 0, 0, 0, null, munger.getConcreteAspect()) { |
| @Override |
| public InstructionList getAdviceInstructions(BcelShadow s, BcelVar extraArgVar, InstructionHandle ifNoAdvice) { |
| InstructionList exitInstructions = new InstructionList(); |
| if (munger.hasDynamicTests()) { |
| testResult.appendLoad(exitInstructions, fact); |
| exitInstructions.append(InstructionFactory.createBranchInstruction(Constants.IFEQ, ifNoAdvice)); |
| } |
| exitInstructions.append(Utility.createGet(fact, cflowField)); |
| if (munger.getKind() != AdviceKind.PerCflowEntry && munger.getKind() != AdviceKind.PerCflowBelowEntry |
| && munger.getExposedStateAsBcelVars(false).length == 0) { |
| exitInstructions.append(fact.createInvoke(NameMangler.CFLOW_COUNTER_TYPE, "dec", Type.VOID, new Type[] {}, |
| Constants.INVOKEVIRTUAL)); |
| } else { |
| exitInstructions.append(fact.createInvoke(NameMangler.CFLOW_STACK_TYPE, "pop", Type.VOID, new Type[] {}, |
| Constants.INVOKEVIRTUAL)); |
| } |
| return exitInstructions; |
| } |
| }; |
| // if (getKind() == PreInitialization) { |
| // weaveAfterReturning(exitAdvice); |
| // } |
| // else { |
| weaveAfter(exitAdvice); |
| // } |
| |
| range.insert(entryInstructions, Range.InsideBefore); |
| } |
| |
| /* |
| * Implementation notes: |
| * |
| * AroundInline still extracts the instructions of the original shadow into an extracted method. This allows inlining of even |
| * that advice that doesn't call proceed or calls proceed more than once. |
| * |
| * It extracts the instructions of the original shadow into a method. |
| * |
| * Then it extracts the instructions of the advice into a new method defined on this enclosing class. This new method can then |
| * be specialized as below. |
| * |
| * Then it searches in the instructions of the advice for any call to the proceed method. |
| * |
| * At such a call, there is stuff on the stack representing the arguments to proceed. Pop these into the frame. |
| * |
| * Now build the stack for the call to the extracted method, taking values either from the join point state or from the new |
| * frame locs from proceed. Now call the extracted method. The right return value should be on the stack, so no cast is |
| * necessary. |
| * |
| * If only one call to proceed is made, we can re-inline the original shadow. We are not doing that presently. |
| * |
| * If the body of the advice can be determined to not alter the stack, or if this shadow doesn't care about the stack, i.e. |
| * method-execution, then the new method for the advice can also be re-lined. We are not doing that presently. |
| */ |
| public void weaveAroundInline(BcelAdvice munger, boolean hasDynamicTest) { |
| // !!! THIS BLOCK OF CODE SHOULD BE IN A METHOD CALLED weaveAround(...); |
| Member mungerSig = munger.getSignature(); |
| // Member originalSig = mungerSig; // If mungerSig is on a parameterized type, originalSig is the member on the generic type |
| if (mungerSig instanceof ResolvedMember) { |
| ResolvedMember rm = (ResolvedMember) mungerSig; |
| if (rm.hasBackingGenericMember()) { |
| mungerSig = rm.getBackingGenericMember(); |
| } |
| } |
| ResolvedType declaringAspectType = world.resolve(mungerSig.getDeclaringType(), true); |
| if (declaringAspectType.isMissing()) { |
| world.getLint().cantFindType.signal( |
| new String[] { WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_DURING_AROUND_WEAVE, |
| declaringAspectType.getClassName()) }, getSourceLocation(), |
| new ISourceLocation[] { munger.getSourceLocation() }); |
| } |
| |
| // ??? might want some checks here to give better errors |
| ResolvedType rt = (declaringAspectType.isParameterizedType() ? declaringAspectType.getGenericType() : declaringAspectType); |
| BcelObjectType ot = BcelWorld.getBcelObjectType(rt); |
| LazyMethodGen adviceMethod = ot.getLazyClassGen().getLazyMethodGen(mungerSig); |
| if (!adviceMethod.getCanInline()) { |
| weaveAroundClosure(munger, hasDynamicTest); |
| return; |
| } |
| |
| // specific test for @AJ proceedInInners |
| if (isAnnotationStylePassingProceedingJoinPointOutOfAdvice(munger, hasDynamicTest, adviceMethod)) { |
| return; |
| } |
| |
| // We can't inline around methods if they have around advice on them, this |
| // is because the weaving will extract the body and hence the proceed call. |
| |
| // TODO should consider optimizations to recognize simple cases that don't require body extraction |
| |
| enclosingMethod.setCanInline(false); |
| |
| LazyClassGen shadowClass = getEnclosingClass(); |
| |
| // Extract the shadow into a new method. For example: |
| // "private static final void method_aroundBody0(M, M, String, org.aspectj.lang.JoinPoint)" |
| // Parameters are: this if there is one, target if there is one and its different to this, then original arguments |
| // at the shadow, then tjp |
| String extractedShadowMethodName = NameMangler.aroundShadowMethodName(getSignature(), shadowClass.getNewGeneratedNameTag()); |
| List<String> parameterNames = new ArrayList<String>(); |
| boolean shadowClassIsInterface = shadowClass.isInterface(); |
| LazyMethodGen extractedShadowMethod = extractShadowInstructionsIntoNewMethod(extractedShadowMethodName, |
| shadowClassIsInterface?Modifier.PUBLIC:Modifier.PRIVATE, |
| munger.getSourceLocation(), parameterNames,shadowClassIsInterface); |
| |
| List<BcelVar> argsToCallLocalAdviceMethodWith = new ArrayList<BcelVar>(); |
| List<BcelVar> proceedVarList = new ArrayList<BcelVar>(); |
| int extraParamOffset = 0; |
| |
| // Create the extra parameters that are needed for passing to proceed |
| // This code is very similar to that found in makeCallToCallback and should |
| // be rationalized in the future |
| |
| if (thisVar != null) { |
| argsToCallLocalAdviceMethodWith.add(thisVar); |
| proceedVarList.add(new BcelVar(thisVar.getType(), extraParamOffset)); |
| extraParamOffset += thisVar.getType().getSize(); |
| } |
| |
| if (targetVar != null && targetVar != thisVar) { |
| argsToCallLocalAdviceMethodWith.add(targetVar); |
| proceedVarList.add(new BcelVar(targetVar.getType(), extraParamOffset)); |
| extraParamOffset += targetVar.getType().getSize(); |
| } |
| for (int i = 0, len = getArgCount(); i < len; i++) { |
| argsToCallLocalAdviceMethodWith.add(argVars[i]); |
| proceedVarList.add(new BcelVar(argVars[i].getType(), extraParamOffset)); |
| extraParamOffset += argVars[i].getType().getSize(); |
| } |
| if (thisJoinPointVar != null) { |
| argsToCallLocalAdviceMethodWith.add(thisJoinPointVar); |
| proceedVarList.add(new BcelVar(thisJoinPointVar.getType(), extraParamOffset)); |
| extraParamOffset += thisJoinPointVar.getType().getSize(); |
| } |
| |
| // We use the munger signature here because it allows for any parameterization of the mungers pointcut that |
| // may have occurred ie. if the pointcut is p(T t) in the super aspect and that has become p(Foo t) in the sub aspect |
| // then here the munger signature will have 'Foo' as an argument in it whilst the adviceMethod argument type will be |
| // 'Object' - since it represents the advice method in the superaspect which uses the erasure of the type variable p(Object |
| // t) - see pr174449. |
| |
| Type[] adviceParameterTypes = BcelWorld.makeBcelTypes(munger.getSignature().getParameterTypes()); |
| |
| // forces initialization ... dont like this but seems to be required for some tests to pass, I think that means there |
| // is a LazyMethodGen method that is not correctly setup to call initialize() when it is invoked - but I dont have |
| // time right now to discover which |
| adviceMethod.getArgumentTypes(); |
| |
| Type[] extractedMethodParameterTypes = extractedShadowMethod.getArgumentTypes(); |
| |
| Type[] parameterTypes = new Type[extractedMethodParameterTypes.length + adviceParameterTypes.length + 1]; |
| int parameterIndex = 0; |
| System.arraycopy(extractedMethodParameterTypes, 0, parameterTypes, parameterIndex, extractedMethodParameterTypes.length); |
| parameterIndex += extractedMethodParameterTypes.length; |
| parameterTypes[parameterIndex++] = BcelWorld.makeBcelType(adviceMethod.getEnclosingClass().getType()); |
| System.arraycopy(adviceParameterTypes, 0, parameterTypes, parameterIndex, adviceParameterTypes.length); |
| |
| // Extract the advice into a new method. This will go in the same type as the shadow |
| // name will be something like foo_aroundBody1$advice |
| String localAdviceMethodName = NameMangler.aroundAdviceMethodName(getSignature(), shadowClass.getNewGeneratedNameTag()); |
| int localAdviceMethodModifiers = Modifier.PRIVATE | (world.useFinal() & !shadowClassIsInterface ? Modifier.FINAL : 0) | Modifier.STATIC; |
| LazyMethodGen localAdviceMethod = new LazyMethodGen(localAdviceMethodModifiers, BcelWorld.makeBcelType(mungerSig.getReturnType()), localAdviceMethodName, parameterTypes, |
| NoDeclaredExceptions, shadowClass); |
| |
| // Doesnt work properly, so leave it out: |
| // String aspectFilename = adviceMethod.getEnclosingClass().getInternalFileName(); |
| // String shadowFilename = shadowClass.getInternalFileName(); |
| // if (!aspectFilename.equals(shadowFilename)) { |
| // localAdviceMethod.fromFilename = aspectFilename; |
| // shadowClass.addInlinedSourceFileInfo(aspectFilename, adviceMethod.highestLineNumber); |
| // } |
| |
| shadowClass.addMethodGen(localAdviceMethod); |
| |
| // create a map that will move all slots in advice method forward by extraParamOffset |
| // in order to make room for the new proceed-required arguments that are added at |
| // the beginning of the parameter list |
| int nVars = adviceMethod.getMaxLocals() + extraParamOffset; |
| IntMap varMap = IntMap.idMap(nVars); |
| for (int i = extraParamOffset; i < nVars; i++) { |
| varMap.put(i - extraParamOffset, i); |
| } |
| |
| final InstructionFactory fact = getFactory(); |
| |
| localAdviceMethod.getBody().insert( |
| BcelClassWeaver.genInlineInstructions(adviceMethod, localAdviceMethod, varMap, fact, true)); |
| |
| localAdviceMethod.setMaxLocals(nVars); |
| |
| // the shadow is now empty. First, create a correct call |
| // to the around advice. This includes both the call (which may involve |
| // value conversion of the advice arguments) and the return |
| // (which may involve value conversion of the return value). Right now |
| // we push a null for the unused closure. It's sad, but there it is. |
| |
| InstructionList advice = new InstructionList(); |
| // InstructionHandle adviceMethodInvocation; |
| { |
| for (Iterator<BcelVar> i = argsToCallLocalAdviceMethodWith.iterator(); i.hasNext();) { |
| BcelVar var = i.next(); |
| var.appendLoad(advice, fact); |
| } |
| // ??? we don't actually need to push NULL for the closure if we take care |
| boolean isAnnoStyleConcreteAspect = munger.getConcreteAspect().isAnnotationStyleAspect(); |
| boolean isAnnoStyleDeclaringAspect = munger.getDeclaringAspect() != null ? munger.getDeclaringAspect().resolve(world) |
| .isAnnotationStyleAspect() : false; |
| |
| InstructionList iList = null; |
| if (isAnnoStyleConcreteAspect && isAnnoStyleDeclaringAspect) { |
| iList = this.loadThisJoinPoint(); |
| iList.append(Utility.createConversion(getFactory(), LazyClassGen.tjpType, LazyClassGen.proceedingTjpType)); |
| } else { |
| iList = new InstructionList(InstructionConstants.ACONST_NULL); |
| } |
| advice.append(munger.getAdviceArgSetup(this, null, iList)); |
| // adviceMethodInvocation = |
| advice.append(Utility.createInvoke(fact, localAdviceMethod)); // (fact, getWorld(), munger.getSignature())); |
| advice.append(Utility.createConversion(getFactory(), BcelWorld.makeBcelType(mungerSig.getReturnType()), |
| extractedShadowMethod.getReturnType(), world.isInJava5Mode())); |
| if (!isFallsThrough()) { |
| advice.append(InstructionFactory.createReturn(extractedShadowMethod.getReturnType())); |
| } |
| } |
| |
| // now, situate the call inside the possible dynamic tests, |
| // and actually add the whole mess to the shadow |
| if (!hasDynamicTest) { |
| range.append(advice); |
| } else { |
| InstructionList afterThingie = new InstructionList(InstructionConstants.NOP); |
| InstructionList callback = makeCallToCallback(extractedShadowMethod); |
| if (terminatesWithReturn()) { |
| callback.append(InstructionFactory.createReturn(extractedShadowMethod.getReturnType())); |
| } else { |
| // InstructionHandle endNop = range.insert(fact.NOP, Range.InsideAfter); |
| advice.append(InstructionFactory.createBranchInstruction(Constants.GOTO, afterThingie.getStart())); |
| } |
| range.append(munger.getTestInstructions(this, advice.getStart(), callback.getStart(), advice.getStart())); |
| range.append(advice); |
| range.append(callback); |
| range.append(afterThingie); |
| } |
| |
| // now search through the advice, looking for a call to PROCEED. |
| // Then we replace the call to proceed with some argument setup, and a |
| // call to the extracted method. |
| |
| // inlining support for code style aspects |
| if (!munger.getDeclaringType().isAnnotationStyleAspect()) { |
| String proceedName = NameMangler.proceedMethodName(munger.getSignature().getName()); |
| |
| InstructionHandle curr = localAdviceMethod.getBody().getStart(); |
| InstructionHandle end = localAdviceMethod.getBody().getEnd(); |
| ConstantPool cpg = localAdviceMethod.getEnclosingClass().getConstantPool(); |
| while (curr != end) { |
| InstructionHandle next = curr.getNext(); |
| Instruction inst = curr.getInstruction(); |
| if ((inst.opcode == Constants.INVOKESTATIC) && proceedName.equals(((InvokeInstruction) inst).getMethodName(cpg))) { |
| |
| localAdviceMethod.getBody().append(curr, |
| getRedoneProceedCall(fact, extractedShadowMethod, munger, localAdviceMethod, proceedVarList)); |
| Utility.deleteInstruction(curr, localAdviceMethod); |
| } |
| curr = next; |
| } |
| // and that's it. |
| } else { |
| // ATAJ inlining support for @AJ aspects |
| // [TODO document @AJ code rule: don't manipulate 2 jps proceed at the same time.. in an advice body] |
| InstructionHandle curr = localAdviceMethod.getBody().getStart(); |
| InstructionHandle end = localAdviceMethod.getBody().getEnd(); |
| ConstantPool cpg = localAdviceMethod.getEnclosingClass().getConstantPool(); |
| while (curr != end) { |
| InstructionHandle next = curr.getNext(); |
| Instruction inst = curr.getInstruction(); |
| if ((inst instanceof INVOKEINTERFACE) && "proceed".equals(((INVOKEINTERFACE) inst).getMethodName(cpg))) { |
| final boolean isProceedWithArgs; |
| if (((INVOKEINTERFACE) inst).getArgumentTypes(cpg).length == 1) { |
| // proceed with args as a boxed Object[] |
| isProceedWithArgs = true; |
| } else { |
| isProceedWithArgs = false; |
| } |
| InstructionList insteadProceedIl = getRedoneProceedCallForAnnotationStyle(fact, extractedShadowMethod, munger, |
| localAdviceMethod, proceedVarList, isProceedWithArgs); |
| localAdviceMethod.getBody().append(curr, insteadProceedIl); |
| Utility.deleteInstruction(curr, localAdviceMethod); |
| } |
| curr = next; |
| } |
| } |
| |
| // if (parameterNames.size() == 0) { |
| // On return we have inserted the advice body into the local advice method. We have remapped all the local variables |
| // that were referenced in the advice as we did the copy, and so the local variable table for localAdviceMethod is |
| // now lacking any information about all the initial variables. |
| InstructionHandle start = localAdviceMethod.getBody().getStart(); |
| InstructionHandle end = localAdviceMethod.getBody().getEnd(); |
| |
| // Find the real start and end |
| while (start.getInstruction().opcode == Constants.IMPDEP1) { |
| start = start.getNext(); |
| } |
| while (end.getInstruction().opcode == Constants.IMPDEP1) { |
| end = end.getPrev(); |
| } |
| Type[] args = localAdviceMethod.getArgumentTypes(); |
| int argNumber = 0; |
| for (int slot = 0; slot < extraParamOffset; argNumber++) { // slot will increase by the argument size each time |
| String argumentName = null; |
| if (argNumber >= args.length || parameterNames.size() == 0 || argNumber >= parameterNames.size()) { |
| // this should be unnecessary as I think all known joinpoints and helper methods |
| // propagate the parameter names around correctly - but just in case let us do this |
| // rather than fail. If a bug is raised reporting unknown as a local variable name |
| // then investigate the joinpoint giving rise to the ResolvedMember and why it has |
| // no parameter names specified |
| argumentName = new StringBuffer("unknown").append(argNumber).toString(); |
| } else { |
| argumentName = parameterNames.get(argNumber); |
| } |
| String argumentSignature = args[argNumber].getSignature(); |
| LocalVariableTag lvt = new LocalVariableTag(argumentSignature, argumentName, slot, 0); |
| start.addTargeter(lvt); |
| end.addTargeter(lvt); |
| slot += args[argNumber].getSize(); |
| } |
| } |
| |
| /** |
| * Check if the advice method passes a pjp parameter out via an invoke instruction - if so we can't risk inlining. |
| */ |
| private boolean isAnnotationStylePassingProceedingJoinPointOutOfAdvice(BcelAdvice munger, boolean hasDynamicTest, |
| LazyMethodGen adviceMethod) { |
| if (munger.getConcreteAspect().isAnnotationStyleAspect()) { |
| // if we can't find one proceed() we suspect that the call |
| // is happening in an inner class so we don't inline it. |
| // Note: for code style, this is done at Aspect compilation time. |
| boolean canSeeProceedPassedToOther = false; |
| InstructionHandle curr = adviceMethod.getBody().getStart(); |
| InstructionHandle end = adviceMethod.getBody().getEnd(); |
| ConstantPool cpg = adviceMethod.getEnclosingClass().getConstantPool(); |
| while (curr != end) { |
| InstructionHandle next = curr.getNext(); |
| Instruction inst = curr.getInstruction(); |
| if ((inst instanceof InvokeInstruction) |
| && ((InvokeInstruction) inst).getSignature(cpg).indexOf("Lorg/aspectj/lang/ProceedingJoinPoint;") > 0) { |
| // we may want to refine to exclude stuff returning jp ? |
| // does code style skip inline if i write dump(thisJoinPoint) ? |
| canSeeProceedPassedToOther = true;// we see one pjp passed around - dangerous |
| break; |
| } |
| curr = next; |
| } |
| if (canSeeProceedPassedToOther) { |
| // remember this decision to avoid re-analysis |
| adviceMethod.setCanInline(false); |
| weaveAroundClosure(munger, hasDynamicTest); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private InstructionList getRedoneProceedCall(InstructionFactory fact, LazyMethodGen callbackMethod, BcelAdvice munger, |
| LazyMethodGen localAdviceMethod, List<BcelVar> argVarList) { |
| InstructionList ret = new InstructionList(); |
| // we have on stack all the arguments for the ADVICE call. |
| // we have in frame somewhere all the arguments for the non-advice call. |
| |
| BcelVar[] adviceVars = munger.getExposedStateAsBcelVars(true); |
| IntMap proceedMap = makeProceedArgumentMap(adviceVars); |
| |
| // System.out.println(proceedMap + " for " + this); |
| // System.out.println(argVarList); |
| |
| ResolvedType[] proceedParamTypes = world.resolve(munger.getSignature().getParameterTypes()); |
| // remove this*JoinPoint* as arguments to proceed |
| if (munger.getBaseParameterCount() + 1 < proceedParamTypes.length) { |
| int len = munger.getBaseParameterCount() + 1; |
| ResolvedType[] newTypes = new ResolvedType[len]; |
| System.arraycopy(proceedParamTypes, 0, newTypes, 0, len); |
| proceedParamTypes = newTypes; |
| } |
| |
| // System.out.println("stateTypes: " + Arrays.asList(stateTypes)); |
| BcelVar[] proceedVars = Utility.pushAndReturnArrayOfVars(proceedParamTypes, ret, fact, localAdviceMethod); |
| |
| Type[] stateTypes = callbackMethod.getArgumentTypes(); |
| // System.out.println("stateTypes: " + Arrays.asList(stateTypes)); |
| |
| for (int i = 0, len = stateTypes.length; i < len; i++) { |
| Type stateType = stateTypes[i]; |
| ResolvedType stateTypeX = BcelWorld.fromBcel(stateType).resolve(world); |
| if (proceedMap.hasKey(i)) { |
| // throw new RuntimeException("unimplemented"); |
| proceedVars[proceedMap.get(i)].appendLoadAndConvert(ret, fact, stateTypeX); |
| } else { |
| argVarList.get(i).appendLoad(ret, fact); |
| } |
| } |
| |
| ret.append(Utility.createInvoke(fact, callbackMethod)); |
| ret.append(Utility.createConversion(fact, callbackMethod.getReturnType(), |
| BcelWorld.makeBcelType(munger.getSignature().getReturnType()), world.isInJava5Mode())); |
| return ret; |
| } |
| |
| // private static boolean bindsThisOrTarget(Pointcut pointcut) { |
| // ThisTargetFinder visitor = new ThisTargetFinder(); |
| // pointcut.accept(visitor, null); |
| // return visitor.bindsThisOrTarget; |
| // } |
| |
| // private static class ThisTargetFinder extends IdentityPointcutVisitor { |
| // boolean bindsThisOrTarget = false; |
| // |
| // public Object visit(ThisOrTargetPointcut node, Object data) { |
| // if (node.isBinding()) { |
| // bindsThisOrTarget = true; |
| // } |
| // return node; |
| // } |
| // |
| // public Object visit(AndPointcut node, Object data) { |
| // if (!bindsThisOrTarget) node.getLeft().accept(this, data); |
| // if (!bindsThisOrTarget) node.getRight().accept(this, data); |
| // return node; |
| // } |
| // |
| // public Object visit(NotPointcut node, Object data) { |
| // if (!bindsThisOrTarget) node.getNegatedPointcut().accept(this, data); |
| // return node; |
| // } |
| // |
| // public Object visit(OrPointcut node, Object data) { |
| // if (!bindsThisOrTarget) node.getLeft().accept(this, data); |
| // if (!bindsThisOrTarget) node.getRight().accept(this, data); |
| // return node; |
| // } |
| // } |
| |
| /** |
| * Annotation style handling for inlining. |
| * |
| * Note: The proceedingjoinpoint is already on the stack (since the user was calling pjp.proceed(...) |
| * |
| * The proceed map is ignored (in terms of argument repositioning) since we have a fixed expected format for annotation style. |
| * The aim here is to change the proceed() call into a call to the xxx_aroundBody0 method. |
| * |
| * |
| */ |
| private InstructionList getRedoneProceedCallForAnnotationStyle(InstructionFactory fact, LazyMethodGen callbackMethod, |
| BcelAdvice munger, LazyMethodGen localAdviceMethod, List<BcelVar> argVarList, boolean isProceedWithArgs) { |
| InstructionList ret = new InstructionList(); |
| |
| // store the Object[] array on stack if proceed with args |
| if (isProceedWithArgs) { |
| |
| // STORE the Object[] into a local variable |
| Type objectArrayType = Type.OBJECT_ARRAY; |
| int theObjectArrayLocalNumber = localAdviceMethod.allocateLocal(objectArrayType); |
| ret.append(InstructionFactory.createStore(objectArrayType, theObjectArrayLocalNumber)); |
| |
| // STORE the ProceedingJoinPoint instance into a local variable |
| Type proceedingJpType = Type.getType("Lorg/aspectj/lang/ProceedingJoinPoint;"); |
| int pjpLocalNumber = localAdviceMethod.allocateLocal(proceedingJpType); |
| ret.append(InstructionFactory.createStore(proceedingJpType, pjpLocalNumber)); |
| |
| // Aim here initially is to determine whether the user will have provided a new |
| // this/target in the object array and consume them if they have, leaving us the rest of |
| // the arguments to process as regular arguments to the invocation at the original join point |
| |
| boolean pointcutBindsThis = bindsThis(munger); |
| boolean pointcutBindsTarget = bindsTarget(munger); |
| boolean targetIsSameAsThis = getKind().isTargetSameAsThis(); |
| |
| int nextArgumentToProvideForCallback = 0; |
| |
| if (hasThis()) { |
| if (!(pointcutBindsTarget && targetIsSameAsThis)) { |
| if (pointcutBindsThis) { |
| // they have supplied new this as first entry in object array, consume it |
| ret.append(InstructionFactory.createLoad(objectArrayType, theObjectArrayLocalNumber)); |
| ret.append(Utility.createConstant(fact, 0)); |
| ret.append(InstructionFactory.createArrayLoad(Type.OBJECT)); |
| ret.append(Utility.createConversion(fact, Type.OBJECT, callbackMethod.getArgumentTypes()[0])); |
| } else { |
| // use local variable 0 |
| ret.append(InstructionFactory.createALOAD(0)); |
| } |
| nextArgumentToProvideForCallback++; |
| } |
| } |
| |
| if (hasTarget()) { |
| if (pointcutBindsTarget) { |
| if (getKind().isTargetSameAsThis()) { |
| ret.append(InstructionFactory.createLoad(objectArrayType, theObjectArrayLocalNumber)); |
| ret.append(Utility.createConstant(fact, pointcutBindsThis ? 1 : 0)); |
| ret.append(InstructionFactory.createArrayLoad(Type.OBJECT)); |
| ret.append(Utility.createConversion(fact, Type.OBJECT, callbackMethod.getArgumentTypes()[0])); |
| } else { |
| int position = (hasThis() && pointcutBindsThis)? 1 : 0; |
| ret.append(InstructionFactory.createLoad(objectArrayType, theObjectArrayLocalNumber)); |
| ret.append(Utility.createConstant(fact, position)); |
| ret.append(InstructionFactory.createArrayLoad(Type.OBJECT)); |
| ret.append(Utility.createConversion(fact, Type.OBJECT, callbackMethod.getArgumentTypes()[nextArgumentToProvideForCallback])); |
| } |
| nextArgumentToProvideForCallback++; |
| } else { |
| if (getKind().isTargetSameAsThis()) { |
| // ret.append(new ALOAD(0)); |
| } else { |
| ret.append(InstructionFactory.createLoad(localAdviceMethod.getArgumentTypes()[0], hasThis() ? 1 : 0)); |
| nextArgumentToProvideForCallback++; |
| } |
| } |
| } |
| |
| // Where to start in the object array in order to pick up arguments |
| int indexIntoObjectArrayForArguments = (pointcutBindsThis ? 1 : 0) + (pointcutBindsTarget ? 1 : 0); |
| |
| int len = callbackMethod.getArgumentTypes().length; |
| for (int i = nextArgumentToProvideForCallback; i < len; i++) { |
| Type stateType = callbackMethod.getArgumentTypes()[i]; |
| BcelWorld.fromBcel(stateType).resolve(world); |
| if ("Lorg/aspectj/lang/JoinPoint;".equals(stateType.getSignature())) { |
| ret.append(new InstructionLV(Constants.ALOAD, pjpLocalNumber)); |
| } else { |
| ret.append(InstructionFactory.createLoad(objectArrayType, theObjectArrayLocalNumber)); |
| ret.append(Utility |
| .createConstant(fact, i - nextArgumentToProvideForCallback + indexIntoObjectArrayForArguments)); |
| ret.append(InstructionFactory.createArrayLoad(Type.OBJECT)); |
| ret.append(Utility.createConversion(fact, Type.OBJECT, stateType)); |
| } |
| } |
| |
| } else { |
| Type proceedingJpType = Type.getType("Lorg/aspectj/lang/ProceedingJoinPoint;"); |
| int localJp = localAdviceMethod.allocateLocal(proceedingJpType); |
| ret.append(InstructionFactory.createStore(proceedingJpType, localJp)); |
| |
| int idx = 0; |
| for (int i = 0, len = callbackMethod.getArgumentTypes().length; i < len; i++) { |
| Type stateType = callbackMethod.getArgumentTypes()[i]; |
| /* ResolvedType stateTypeX = */ |
| BcelWorld.fromBcel(stateType).resolve(world); |
| if ("Lorg/aspectj/lang/JoinPoint;".equals(stateType.getSignature())) { |
| ret.append(InstructionFactory.createALOAD(localJp));// from localAdvice signature |
| // } else if ("Lorg/aspectj/lang/ProceedingJoinPoint;".equals(stateType.getSignature())) { |
| // //FIXME ALEX? |
| // ret.append(new ALOAD(localJp));// from localAdvice signature |
| // // ret.append(fact.createCheckCast( |
| // // (ReferenceType) BcelWorld.makeBcelType(stateTypeX) |
| // // )); |
| // // cast ? |
| // |
| idx++; |
| } else { |
| ret.append(InstructionFactory.createLoad(stateType, idx)); |
| idx += stateType.getSize(); |
| } |
| } |
| } |
| |
| // do the callback invoke |
| ret.append(Utility.createInvoke(fact, callbackMethod)); |
| |
| // box it again. Handles cases where around advice does return something else than Object |
| if (!UnresolvedType.OBJECT.equals(munger.getSignature().getReturnType())) { |
| ret.append(Utility.createConversion(fact, callbackMethod.getReturnType(), Type.OBJECT)); |
| } |
| ret.append(Utility.createConversion(fact, callbackMethod.getReturnType(), |
| BcelWorld.makeBcelType(munger.getSignature().getReturnType()), world.isInJava5Mode())); |
| |
| return ret; |
| |
| // |
| // |
| // |
| // if (proceedMap.hasKey(i)) { |
| // ret.append(new ALOAD(i)); |
| // //throw new RuntimeException("unimplemented"); |
| // //proceedVars[proceedMap.get(i)].appendLoadAndConvert(ret, fact, stateTypeX); |
| // } else { |
| // //((BcelVar) argVarList.get(i)).appendLoad(ret, fact); |
| // //ret.append(new ALOAD(i)); |
| // if ("Lorg/aspectj/lang/JoinPoint;".equals(stateType.getSignature())) { |
| // ret.append(new ALOAD(i)); |
| // } else { |
| // ret.append(new ALOAD(i)); |
| // } |
| // } |
| // } |
| // |
| // ret.append(Utility.createInvoke(fact, callbackMethod)); |
| // ret.append(Utility.createConversion(fact, callbackMethod.getReturnType(), |
| // BcelWorld.makeBcelType(munger.getSignature().getReturnType()))); |
| // |
| // //ret.append(new ACONST_NULL());//will be POPed |
| // if (true) return ret; |
| // |
| // |
| // |
| // // we have on stack all the arguments for the ADVICE call. |
| // // we have in frame somewhere all the arguments for the non-advice call. |
| // |
| // BcelVar[] adviceVars = munger.getExposedStateAsBcelVars(); |
| // IntMap proceedMap = makeProceedArgumentMap(adviceVars); |
| // |
| // System.out.println(proceedMap + " for " + this); |
| // System.out.println(argVarList); |
| // |
| // ResolvedType[] proceedParamTypes = |
| // world.resolve(munger.getSignature().getParameterTypes()); |
| // // remove this*JoinPoint* as arguments to proceed |
| // if (munger.getBaseParameterCount()+1 < proceedParamTypes.length) { |
| // int len = munger.getBaseParameterCount()+1; |
| // ResolvedType[] newTypes = new ResolvedType[len]; |
| // System.arraycopy(proceedParamTypes, 0, newTypes, 0, len); |
| // proceedParamTypes = newTypes; |
| // } |
| // |
| // //System.out.println("stateTypes: " + Arrays.asList(stateTypes)); |
| // BcelVar[] proceedVars = |
| // Utility.pushAndReturnArrayOfVars(proceedParamTypes, ret, fact, localAdviceMethod); |
| // |
| // Type[] stateTypes = callbackMethod.getArgumentTypes(); |
| // // System.out.println("stateTypes: " + Arrays.asList(stateTypes)); |
| // |
| // for (int i=0, len=stateTypes.length; i < len; i++) { |
| // Type stateType = stateTypes[i]; |
| // ResolvedType stateTypeX = BcelWorld.fromBcel(stateType).resolve(world); |
| // if (proceedMap.hasKey(i)) { |
| // //throw new RuntimeException("unimplemented"); |
| // proceedVars[proceedMap.get(i)].appendLoadAndConvert(ret, fact, stateTypeX); |
| // } else { |
| // ((BcelVar) argVarList.get(i)).appendLoad(ret, fact); |
| // } |
| // } |
| // |
| // ret.append(Utility.createInvoke(fact, callbackMethod)); |
| // ret.append(Utility.createConversion(fact, callbackMethod.getReturnType(), |
| // BcelWorld.makeBcelType(munger.getSignature().getReturnType()))); |
| // return ret; |
| } |
| |
| private boolean bindsThis(BcelAdvice munger) { |
| UsesThisVisitor utv = new UsesThisVisitor(); |
| munger.getPointcut().accept(utv, null); |
| return utv.usesThis; |
| } |
| |
| private boolean bindsTarget(BcelAdvice munger) { |
| UsesTargetVisitor utv = new UsesTargetVisitor(); |
| munger.getPointcut().accept(utv, null); |
| return utv.usesTarget; |
| } |
| |
| private static class UsesThisVisitor extends AbstractPatternNodeVisitor { |
| boolean usesThis = false; |
| |
| @Override |
| public Object visit(ThisOrTargetPointcut node, Object data) { |
| if (node.isThis() && node.isBinding()) { |
| usesThis = true; |
| } |
| return node; |
| } |
| |
| @Override |
| public Object visit(AndPointcut node, Object data) { |
| if (!usesThis) { |
| node.getLeft().accept(this, data); |
| } |
| if (!usesThis) { |
| node.getRight().accept(this, data); |
| } |
| return node; |
| } |
| |
| @Override |
| public Object visit(NotPointcut node, Object data) { |
| if (!usesThis) { |
| node.getNegatedPointcut().accept(this, data); |
| } |
| return node; |
| } |
| |
| @Override |
| public Object visit(OrPointcut node, Object data) { |
| if (!usesThis) { |
| node.getLeft().accept(this, data); |
| } |
| if (!usesThis) { |
| node.getRight().accept(this, data); |
| } |
| return node; |
| } |
| } |
| |
| private static class UsesTargetVisitor extends AbstractPatternNodeVisitor { |
| boolean usesTarget = false; |
| |
| @Override |
| public Object visit(ThisOrTargetPointcut node, Object data) { |
| if (!node.isThis() && node.isBinding()) { |
| usesTarget = true; |
| } |
| return node; |
| } |
| |
| @Override |
| public Object visit(AndPointcut node, Object data) { |
| if (!usesTarget) { |
| node.getLeft().accept(this, data); |
| } |
| if (!usesTarget) { |
| node.getRight().accept(this, data); |
| } |
| return node; |
| } |
| |
| @Override |
| public Object visit(NotPointcut node, Object data) { |
| if (!usesTarget) { |
| node.getNegatedPointcut().accept(this, data); |
| } |
| return node; |
| } |
| |
| @Override |
| public Object visit(OrPointcut node, Object data) { |
| if (!usesTarget) { |
| node.getLeft().accept(this, data); |
| } |
| if (!usesTarget) { |
| node.getRight().accept(this, data); |
| } |
| return node; |
| } |
| } |
| |
| BcelVar aroundClosureInstance = null; |
| |
| public void weaveAroundClosure(BcelAdvice munger, boolean hasDynamicTest) { |
| InstructionFactory fact = getFactory(); |
| |
| enclosingMethod.setCanInline(false); |
| |
| int linenumber = getSourceLine(); |
| // MOVE OUT ALL THE INSTRUCTIONS IN MY SHADOW INTO ANOTHER METHOD! |
| |
| // callbackMethod will be something like: "static final void m_aroundBody0(I)" |
| boolean shadowClassIsInterface = getEnclosingClass().isInterface(); |
| LazyMethodGen callbackMethod = extractShadowInstructionsIntoNewMethod( |
| NameMangler.aroundShadowMethodName(getSignature(), getEnclosingClass().getNewGeneratedNameTag()), shadowClassIsInterface?Modifier.PUBLIC:0, |
| munger.getSourceLocation(), new ArrayList<String>(),shadowClassIsInterface); |
| |
| BcelVar[] adviceVars = munger.getExposedStateAsBcelVars(true); |
| |
| String closureClassName = NameMangler.makeClosureClassName(getEnclosingClass().getType(), getEnclosingClass() |
| .getNewGeneratedNameTag()); |
| |
| Member constructorSig = new MemberImpl(Member.CONSTRUCTOR, UnresolvedType.forName(closureClassName), 0, "<init>", |
| "([Ljava/lang/Object;)V"); |
| |
| BcelVar closureHolder = null; |
| |
| // This is not being used currently since getKind() == preinitializaiton |
| // cannot happen in around advice |
| if (getKind() == PreInitialization) { |
| closureHolder = genTempVar(AjcMemberMaker.AROUND_CLOSURE_TYPE); |
| } |
| |
| InstructionList closureInstantiation = makeClosureInstantiation(constructorSig, closureHolder); |
| |
| /* LazyMethodGen constructor = */ |
| makeClosureClassAndReturnConstructor(closureClassName, callbackMethod, makeProceedArgumentMap(adviceVars)); |
| |
| InstructionList returnConversionCode; |
| if (getKind() == PreInitialization) { |
| returnConversionCode = new InstructionList(); |
| |
| BcelVar stateTempVar = genTempVar(UnresolvedType.OBJECTARRAY); |
| closureHolder.appendLoad(returnConversionCode, fact); |
| |
| returnConversionCode.append(Utility.createInvoke(fact, world, AjcMemberMaker.aroundClosurePreInitializationGetter())); |
| stateTempVar.appendStore(returnConversionCode, fact); |
| |
| Type[] stateTypes = getSuperConstructorParameterTypes(); |
| |
| returnConversionCode.append(InstructionConstants.ALOAD_0); // put "this" back on the stack |
| for (int i = 0, len = stateTypes.length; i < len; i++) { |
| UnresolvedType bcelTX = BcelWorld.fromBcel(stateTypes[i]); |
| ResolvedType stateRTX = world.resolve(bcelTX, true); |
| if (stateRTX.isMissing()) { |
| world.getLint().cantFindType.signal( |
| new String[] { WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_DURING_AROUND_WEAVE_PREINIT, |
| bcelTX.getClassName()) }, getSourceLocation(), |
| new ISourceLocation[] { munger.getSourceLocation() }); |
| // IMessage msg = new Message( |
| // WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_DURING_AROUND_WEAVE_PREINIT,bcelTX.getClassName()), |
| // "",IMessage.ERROR,getSourceLocation(),null, |
| // new ISourceLocation[]{ munger.getSourceLocation()}); |
| // world.getMessageHandler().handleMessage(msg); |
| } |
| stateTempVar.appendConvertableArrayLoad(returnConversionCode, fact, i, stateRTX); |
| } |
| } else { |
| // pr226201 |
| Member mungerSignature = munger.getSignature(); |
| if (munger.getSignature() instanceof ResolvedMember) { |
| if (((ResolvedMember) mungerSignature).hasBackingGenericMember()) { |
| mungerSignature = ((ResolvedMember) mungerSignature).getBackingGenericMember(); |
| } |
| } |
| UnresolvedType returnType = mungerSignature.getReturnType(); |
| returnConversionCode = Utility.createConversion(getFactory(), BcelWorld.makeBcelType(returnType), |
| callbackMethod.getReturnType(), world.isInJava5Mode()); |
| if (!isFallsThrough()) { |
| returnConversionCode.append(InstructionFactory.createReturn(callbackMethod.getReturnType())); |
| } |
| } |
| |
| // initialize the bit flags for this shadow |
| int bitflags = 0x000000; |
| if (getKind().isTargetSameAsThis()) { |
| bitflags |= 0x010000; |
| } |
| if (hasThis()) { |
| bitflags |= 0x001000; |
| } |
| if (bindsThis(munger)) { |
| bitflags |= 0x000100; |
| } |
| if (hasTarget()) { |
| bitflags |= 0x000010; |
| } |
| if (bindsTarget(munger)) { |
| bitflags |= 0x000001; |
| } |
| |
| closureVarInitialized = false; |
| |
| // ATAJ for @AJ aspect we need to link the closure with the joinpoint instance |
| if (munger.getConcreteAspect() != null && munger.getConcreteAspect().isAnnotationStyleAspect() |
| && munger.getDeclaringAspect() != null && munger.getDeclaringAspect().resolve(world).isAnnotationStyleAspect()) { |
| |
| aroundClosureInstance = genTempVar(AjcMemberMaker.AROUND_CLOSURE_TYPE); |
| closureInstantiation.append(fact.createDup(1)); |
| aroundClosureInstance.appendStore(closureInstantiation, fact); |
| |
| // stick the bitflags on the stack and call the variant of linkClosureAndJoinPoint that takes an int |
| closureInstantiation.append(fact.createConstant(Integer.valueOf(bitflags))); |
| if (needAroundClosureStacking) { |
| closureInstantiation.append(Utility.createInvoke(getFactory(), getWorld(), |
| new MemberImpl(Member.METHOD, UnresolvedType.forName("org.aspectj.runtime.internal.AroundClosure"), |
| Modifier.PUBLIC, "linkStackClosureAndJoinPoint", String.format("%s%s", "(I)", "Lorg/aspectj/lang/ProceedingJoinPoint;")))); |
| |
| } else { |
| closureInstantiation.append(Utility.createInvoke(getFactory(), getWorld(), |
| new MemberImpl(Member.METHOD, UnresolvedType.forName("org.aspectj.runtime.internal.AroundClosure"), |
| Modifier.PUBLIC, "linkClosureAndJoinPoint", String.format("%s%s", "(I)", "Lorg/aspectj/lang/ProceedingJoinPoint;")))); |
| } |
| |
| } |
| |
| InstructionList advice = new InstructionList(); |
| advice.append(munger.getAdviceArgSetup(this, null, closureInstantiation)); |
| |
| // invoke the advice |
| InstructionHandle tryUnlinkPosition = advice.append(munger.getNonTestAdviceInstructions(this)); |
| |
| if (needAroundClosureStacking) { |
| // Call AroundClosure.unlink() in a 'finally' block |
| if (munger.getConcreteAspect() != null && munger.getConcreteAspect().isAnnotationStyleAspect() |
| && munger.getDeclaringAspect() != null |
| && munger.getDeclaringAspect().resolve(world).isAnnotationStyleAspect() |
| && closureVarInitialized) { |
| |
| // Call unlink when 'normal' flow occurring |
| aroundClosureInstance.appendLoad(advice, fact); |
| InstructionHandle unlinkInsn = advice.append(Utility.createInvoke(getFactory(), getWorld(), new MemberImpl(Member.METHOD, UnresolvedType |
| .forName("org.aspectj.runtime.internal.AroundClosure"), Modifier.PUBLIC, "unlink", |
| "()V"))); |
| |
| InstructionHandle jumpOverHandler = advice.append(InstructionConstants.NOP); |
| |
| // Call unlink in finally block |
| InstructionHandle handlerStart = advice.append(InstructionConstants.POP); |
| aroundClosureInstance.appendLoad(advice, fact); |
| advice.append(Utility.createInvoke(getFactory(), getWorld(), new MemberImpl(Member.METHOD, UnresolvedType |
| .forName("org.aspectj.runtime.internal.AroundClosure"), Modifier.PUBLIC, "unlink", |
| "()V"))); |
| advice.append(InstructionConstants.ACONST_NULL); |
| advice.append(InstructionConstants.ATHROW); |
| InstructionHandle jumpTarget = advice.append(InstructionConstants.NOP); |
| jumpOverHandler.setInstruction(InstructionFactory.createBranchInstruction(Constants.GOTO, jumpTarget)); |
| enclosingMethod.addExceptionHandler(tryUnlinkPosition, unlinkInsn, handlerStart, null/* ==finally */, false); |
| } |
| } |
| |
| advice.append(returnConversionCode); |
| if (getKind() == Shadow.MethodExecution && linenumber > 0) { |
| advice.getStart().addTargeter(new LineNumberTag(linenumber)); |
| } |
| |
| if (!hasDynamicTest) { |
| range.append(advice); |
| } else { |
| InstructionList callback = makeCallToCallback(callbackMethod); |
| InstructionList postCallback = new InstructionList(); |
| if (terminatesWithReturn()) { |
| callback.append(InstructionFactory.createReturn(callbackMethod.getReturnType())); |
| } else { |
| advice.append(InstructionFactory.createBranchInstruction(Constants.GOTO, |
| postCallback.append(InstructionConstants.NOP))); |
| } |
| range.append(munger.getTestInstructions(this, advice.getStart(), callback.getStart(), advice.getStart())); |
| range.append(advice); |
| range.append(callback); |
| range.append(postCallback); |
| } |
| } |
| |
| // exposed for testing |
| InstructionList makeCallToCallback(LazyMethodGen callbackMethod) { |
| InstructionFactory fact = getFactory(); |
| InstructionList callback = new InstructionList(); |
| if (thisVar != null) { |
| callback.append(InstructionConstants.ALOAD_0); |
| } |
| if (targetVar != null && targetVar != thisVar) { |
| callback.append(BcelRenderer.renderExpr(fact, world, targetVar)); |
| } |
| callback.append(BcelRenderer.renderExprs(fact, world, argVars)); |
| // remember to render tjps |
| if (thisJoinPointVar != null) { |
| callback.append(BcelRenderer.renderExpr(fact, world, thisJoinPointVar)); |
| } |
| callback.append(Utility.createInvoke(fact, callbackMethod)); |
| return callback; |
| } |
| |
| /** side-effect-free */ |
| private InstructionList makeClosureInstantiation(Member constructor, BcelVar holder) { |
| |
| // LazyMethodGen constructor) { |
| InstructionFactory fact = getFactory(); |
| BcelVar arrayVar = genTempVar(UnresolvedType.OBJECTARRAY); |
| // final Type objectArrayType = new ArrayType(Type.OBJECT, 1); |
| final InstructionList il = new InstructionList(); |
| int alen = getArgCount() + (thisVar == null ? 0 : 1) + ((targetVar != null && targetVar != thisVar) ? 1 : 0) |
| + (thisJoinPointVar == null ? 0 : 1); |
| il.append(Utility.createConstant(fact, alen)); |
| il.append(fact.createNewArray(Type.OBJECT, (short) 1)); |
| arrayVar.appendStore(il, fact); |
| |
| int stateIndex = 0; |
| if (thisVar != null) { |
| arrayVar.appendConvertableArrayStore(il, fact, stateIndex, thisVar); |
| thisVar.setPositionInAroundState(stateIndex); |
| stateIndex++; |
| } |
| if (targetVar != null && targetVar != thisVar) { |
| arrayVar.appendConvertableArrayStore(il, fact, stateIndex, targetVar); |
| targetVar.setPositionInAroundState(stateIndex); |
| stateIndex++; |
| } |
| for (int i = 0, len = getArgCount(); i < len; i++) { |
| arrayVar.appendConvertableArrayStore(il, fact, stateIndex, argVars[i]); |
| argVars[i].setPositionInAroundState(stateIndex); |
| stateIndex++; |
| } |
| if (thisJoinPointVar != null) { |
| arrayVar.appendConvertableArrayStore(il, fact, stateIndex, thisJoinPointVar); |
| thisJoinPointVar.setPositionInAroundState(stateIndex); |
| stateIndex++; |
| } |
| il.append(fact.createNew(new ObjectType(constructor.getDeclaringType().getName()))); |
| il.append(InstructionConstants.DUP); |
| arrayVar.appendLoad(il, fact); |
| il.append(Utility.createInvoke(fact, world, constructor)); |
| if (getKind() == PreInitialization) { |
| il.append(InstructionConstants.DUP); |
| holder.appendStore(il, fact); |
| } |
| return il; |
| } |
| |
| private IntMap makeProceedArgumentMap(BcelVar[] adviceArgs) { |
| // System.err.println("coming in with " + Arrays.asList(adviceArgs)); |
| |
| IntMap ret = new IntMap(); |
| for (int i = 0, len = adviceArgs.length; i < len; i++) { |
| BcelVar v = adviceArgs[i]; |
| if (v == null) { |
| continue; // XXX we don't know why this is required |
| } |
| int pos = v.getPositionInAroundState(); |
| if (pos >= 0) { // need this test to avoid args bound via cflow |
| ret.put(pos, i); |
| } |
| } |
| // System.err.println("returning " + ret); |
| |
| return ret; |
| } |
| |
| /** |
| * |
| * @param callbackMethod the method we will call back to when our run method gets called. |
| * @param proceedMap A map from state position to proceed argument position. May be non covering on state position. |
| */ |
| private LazyMethodGen makeClosureClassAndReturnConstructor(String closureClassName, LazyMethodGen callbackMethod, |
| IntMap proceedMap) { |
| String superClassName = "org.aspectj.runtime.internal.AroundClosure"; |
| Type objectArrayType = new ArrayType(Type.OBJECT, 1); |
| |
| LazyClassGen closureClass = new LazyClassGen(closureClassName, superClassName, getEnclosingClass().getFileName(), |
| Modifier.PUBLIC, new String[] {}, getWorld()); |
| closureClass.setMajorMinor(getEnclosingClass().getMajor(), getEnclosingClass().getMinor()); |
| InstructionFactory fact = new InstructionFactory(closureClass.getConstantPool()); |
| |
| // constructor |
| LazyMethodGen constructor = new LazyMethodGen(Modifier.PUBLIC, Type.VOID, "<init>", new Type[] { objectArrayType }, |
| new String[] {}, closureClass); |
| InstructionList cbody = constructor.getBody(); |
| cbody.append(InstructionFactory.createLoad(Type.OBJECT, 0)); |
| cbody.append(InstructionFactory.createLoad(objectArrayType, 1)); |
| cbody.append(fact |
| .createInvoke(superClassName, "<init>", Type.VOID, new Type[] { objectArrayType }, Constants.INVOKESPECIAL)); |
| cbody.append(InstructionFactory.createReturn(Type.VOID)); |
| |
| closureClass.addMethodGen(constructor); |
| |
| // Create the 'Object run(Object[])' method |
| LazyMethodGen runMethod = new LazyMethodGen(Modifier.PUBLIC, Type.OBJECT, "run", new Type[] { objectArrayType }, |
| new String[] {}, closureClass); |
| InstructionList mbody = runMethod.getBody(); |
| BcelVar proceedVar = new BcelVar(UnresolvedType.OBJECTARRAY.resolve(world), 1); |
| // int proceedVarIndex = 1; |
| BcelVar stateVar = new BcelVar(UnresolvedType.OBJECTARRAY.resolve(world), runMethod.allocateLocal(1)); |
| // int stateVarIndex = runMethod.allocateLocal(1); |
| mbody.append(InstructionFactory.createThis()); |
| mbody.append(fact.createGetField(superClassName, "state", objectArrayType)); |
| mbody.append(stateVar.createStore(fact)); |
| // mbody.append(fact.createStore(objectArrayType, stateVarIndex)); |
| |
| Type[] stateTypes = callbackMethod.getArgumentTypes(); |
| |
| for (int i = 0, len = stateTypes.length; i < len; i++) { |
| ResolvedType resolvedStateType = BcelWorld.fromBcel(stateTypes[i]).resolve(world); |
| if (proceedMap.hasKey(i)) { |
| mbody.append(proceedVar.createConvertableArrayLoad(fact, proceedMap.get(i), resolvedStateType)); |
| } else { |
| mbody.append(stateVar.createConvertableArrayLoad(fact, i, resolvedStateType)); |
| } |
| } |
| |
| mbody.append(Utility.createInvoke(fact, callbackMethod)); |
| |
| if (getKind() == PreInitialization) { |
| mbody.append(Utility.createSet(fact, AjcMemberMaker.aroundClosurePreInitializationField())); |
| mbody.append(InstructionConstants.ACONST_NULL); |
| } else { |
| mbody.append(Utility.createConversion(fact, callbackMethod.getReturnType(), Type.OBJECT)); |
| } |
| mbody.append(InstructionFactory.createReturn(Type.OBJECT)); |
| |
| closureClass.addMethodGen(runMethod); |
| |
| // class |
| getEnclosingClass().addGeneratedInner(closureClass); |
| |
| return constructor; |
| } |
| |
| // ---- extraction methods |
| |
| /** |
| * Extract the instructions in the shadow to a new method. |
| * |
| * @param extractedMethodName name for the new method |
| * @param extractedMethodVisibilityModifier visibility modifiers for the new method |
| * @param adviceSourceLocation source location of the advice affecting the shadow |
| * @param beingPlacedInInterface is this new method going into an interface |
| */ |
| LazyMethodGen extractShadowInstructionsIntoNewMethod(String extractedMethodName, int extractedMethodVisibilityModifier, |
| ISourceLocation adviceSourceLocation, List<String> parameterNames, boolean beingPlacedInInterface) { |
| // LazyMethodGen.assertGoodBody(range.getBody(), extractedMethodName); |
| if (!getKind().allowsExtraction()) { |
| throw new BCException("Attempt to extract method from a shadow kind (" + getKind() |
| + ") that does not support this operation"); |
| } |
| LazyMethodGen newMethod = createShadowMethodGen(extractedMethodName, extractedMethodVisibilityModifier, parameterNames, beingPlacedInInterface); |
| IntMap remapper = makeRemap(); |
| range.extractInstructionsInto(newMethod, remapper, (getKind() != PreInitialization) && isFallsThrough()); |
| if (getKind() == PreInitialization) { |
| addPreInitializationReturnCode(newMethod, getSuperConstructorParameterTypes()); |
| } |
| getEnclosingClass().addMethodGen(newMethod, adviceSourceLocation); |
| return newMethod; |
| } |
| |
| private void addPreInitializationReturnCode(LazyMethodGen extractedMethod, Type[] superConstructorTypes) { |
| InstructionList body = extractedMethod.getBody(); |
| final InstructionFactory fact = getFactory(); |
| |
| BcelVar arrayVar = new BcelVar(world.getCoreType(UnresolvedType.OBJECTARRAY), extractedMethod.allocateLocal(1)); |
| |
| int len = superConstructorTypes.length; |
| |
| body.append(Utility.createConstant(fact, len)); |
| |
| body.append(fact.createNewArray(Type.OBJECT, (short) 1)); |
| arrayVar.appendStore(body, fact); |
| |
| for (int i = len - 1; i >= 0; i++) { |
| // convert thing on top of stack to object |
| body.append(Utility.createConversion(fact, superConstructorTypes[i], Type.OBJECT)); |
| // push object array |
| arrayVar.appendLoad(body, fact); |
| // swap |
| body.append(InstructionConstants.SWAP); |
| // do object array store. |
| body.append(Utility.createConstant(fact, i)); |
| body.append(InstructionConstants.SWAP); |
| body.append(InstructionFactory.createArrayStore(Type.OBJECT)); |
| } |
| arrayVar.appendLoad(body, fact); |
| body.append(InstructionConstants.ARETURN); |
| } |
| |
| private Type[] getSuperConstructorParameterTypes() { |
| // assert getKind() == PreInitialization |
| InstructionHandle superCallHandle = getRange().getEnd().getNext(); |
| InvokeInstruction superCallInstruction = (InvokeInstruction) superCallHandle.getInstruction(); |
| return superCallInstruction.getArgumentTypes(getEnclosingClass().getConstantPool()); |
| } |
| |
| /** |
| * make a map from old frame location to new frame location. Any unkeyed frame location picks out a copied local |
| */ |
| private IntMap makeRemap() { |
| IntMap ret = new IntMap(5); |
| int reti = 0; |
| if (thisVar != null) { |
| ret.put(0, reti++); // thisVar guaranteed to be 0 |
| } |
| if (targetVar != null && targetVar != thisVar) { |
| ret.put(targetVar.getSlot(), reti++); |
| } |
| for (int i = 0, len = argVars.length; i < len; i++) { |
| ret.put(argVars[i].getSlot(), reti); |
| reti += argVars[i].getType().getSize(); |
| } |
| if (thisJoinPointVar != null) { |
| ret.put(thisJoinPointVar.getSlot(), reti++); |
| } |
| // we not only need to put the arguments, we also need to remap their |
| // aliases, which we so helpfully put into temps at the beginning of this join |
| // point. |
| if (!getKind().argsOnStack()) { |
| int oldi = 0; |
| int newi = 0; |
| // if we're passing in a this and we're not argsOnStack we're always |
| // passing in a target too |
| if (arg0HoldsThis()) { |
| ret.put(0, 0); |
| oldi++; |
| newi += 1; |
| } |
| // assert targetVar == thisVar |
| for (int i = 0; i < getArgCount(); i++) { |
| UnresolvedType type = getArgType(i); |
| ret.put(oldi, newi); |
| oldi += type.getSize(); |
| newi += type.getSize(); |
| } |
| } |
| |
| // System.err.println("making remap for : " + this); |
| // if (targetVar != null) System.err.println("target slot : " + targetVar.getSlot()); |
| // if (thisVar != null) System.err.println(" this slot : " + thisVar.getSlot()); |
| // System.err.println(ret); |
| |
| return ret; |
| } |
| |
| /** |
| * The new method always static. It may take some extra arguments: this, target. If it's argsOnStack, then it must take both |
| * this/target If it's argsOnFrame, it shares this and target. ??? rewrite this to do less array munging, please |
| */ |
| private LazyMethodGen createShadowMethodGen(String newMethodName, int visibilityModifier, List<String> parameterNames, boolean beingPlacedInInterface) { |
| Type[] shadowParameterTypes = BcelWorld.makeBcelTypes(getArgTypes()); |
| int modifiers = (world.useFinal() && !beingPlacedInInterface ? Modifier.FINAL : 0) | Modifier.STATIC | visibilityModifier; |
| if (targetVar != null && targetVar != thisVar) { |
| UnresolvedType targetType = getTargetType(); |
| targetType = ensureTargetTypeIsCorrect(targetType); |
| // see pr109728,pr229910 - this fixes the case when the declaring class is sometype 'X' but the (gs)etfield |
| // in the bytecode refers to a subtype of 'X'. This makes sure we use the type originally |
| // mentioned in the fieldget instruction as the method parameter and *not* the type upon which the |
| // field is declared because when the instructions are extracted into the new around body, |
| // they will still refer to the subtype. |
| if ((getKind() == FieldGet || getKind() == FieldSet) && getActualTargetType() != null |
| && !getActualTargetType().equals(targetType.getName())) { |
| targetType = UnresolvedType.forName(getActualTargetType()).resolve(world); |
| } |
| ResolvedMember resolvedMember = getSignature().resolve(world); |
| |
| // pr230075, pr197719 |
| if (resolvedMember != null && Modifier.isProtected(resolvedMember.getModifiers()) |
| && !samePackage(resolvedMember.getDeclaringType().getPackageName(), getEnclosingType().getPackageName()) |
| && !resolvedMember.getName().equals("clone")) { |
| if (!hasThis()) { // pr197719 - static accessor has been created to handle the call |
| if (Modifier.isStatic(enclosingMethod.getAccessFlags()) && enclosingMethod.getName().startsWith("access$")) { |
| targetType = BcelWorld.fromBcel(enclosingMethod.getArgumentTypes()[0]); |
| } |
| } else { |
| if (!targetType.resolve(world).isAssignableFrom(getThisType().resolve(world))) { |
| throw new BCException("bad bytecode"); |
| } |
| targetType = getThisType(); |
| } |
| } |
| parameterNames.add("target"); |
| // There is a 'target' and it is not the same as 'this', so add it to the parameter list |
| shadowParameterTypes = addTypeToFront(BcelWorld.makeBcelType(targetType), shadowParameterTypes); |
| } |
| |
| if (thisVar != null) { |
| UnresolvedType thisType = getThisType(); |
| parameterNames.add(0, "ajc$this"); |
| shadowParameterTypes = addTypeToFront(BcelWorld.makeBcelType(thisType), shadowParameterTypes); |
| } |
| |
| if (this.getKind() == Shadow.FieldSet || this.getKind() == Shadow.FieldGet) { |
| parameterNames.add(getSignature().getName()); |
| } else { |
| String[] pnames = getSignature().getParameterNames(world); |
| if (pnames != null) { |
| for (int i = 0; i < pnames.length; i++) { |
| if (i == 0 && pnames[i].equals("this")) { |
| parameterNames.add("ajc$this"); |
| } else { |
| parameterNames.add(pnames[i]); |
| } |
| } |
| } |
| } |
| |
| // We always want to pass down thisJoinPoint in case we have already woven |
| // some advice in here. If we only have a single piece of around advice on a |
| // join point, it is unnecessary to accept (and pass) tjp. |
| if (thisJoinPointVar != null) { |
| parameterNames.add("thisJoinPoint"); |
| shadowParameterTypes = addTypeToEnd(LazyClassGen.tjpType, shadowParameterTypes); |
| } |
| |
| UnresolvedType returnType; |
| if (getKind() == PreInitialization) { |
| returnType = UnresolvedType.OBJECTARRAY; |
| } else { |
| if (getKind() == ConstructorCall) { |
| returnType = getSignature().getDeclaringType(); |
| } else if (getKind() == FieldSet) { |
| returnType = UnresolvedType.VOID; |
| } else { |
| returnType = getSignature().getReturnType().resolve(world); |
| // returnType = getReturnType(); // for this and above lines, see pr137496 |
| } |
| } |
| return new LazyMethodGen(modifiers, BcelWorld.makeBcelType(returnType), newMethodName, shadowParameterTypes, |
| NoDeclaredExceptions, getEnclosingClass()); |
| } |
| |
| private boolean samePackage(String p1, String p2) { |
| if (p1 == null) { |
| return p2 == null; |
| } |
| if (p2 == null) { |
| return false; |
| } |
| return p1.equals(p2); |
| } |
| |
| private Type[] addTypeToFront(Type type, Type[] types) { |
| int len = types.length; |
| Type[] ret = new Type[len + 1]; |
| ret[0] = type; |
| System.arraycopy(types, 0, ret, 1, len); |
| return ret; |
| } |
| |
| private Type[] addTypeToEnd(Type type, Type[] types) { |
| int len = types.length; |
| Type[] ret = new Type[len + 1]; |
| ret[len] = type; |
| System.arraycopy(types, 0, ret, 0, len); |
| return ret; |
| } |
| |
| public BcelVar genTempVar(UnresolvedType utype) { |
| ResolvedType rtype = utype.resolve(world); |
| return new BcelVar(rtype, genTempVarIndex(rtype.getSize())); |
| } |
| |
| // public static final boolean CREATE_TEMP_NAMES = true; |
| |
| public BcelVar genTempVar(UnresolvedType typeX, String localName) { |
| BcelVar tv = genTempVar(typeX); |
| |
| // if (CREATE_TEMP_NAMES) { |
| // for (InstructionHandle ih = range.getStart(); ih != range.getEnd(); ih = ih.getNext()) { |
| // if (Range.isRangeHandle(ih)) continue; |
| // ih.addTargeter(new LocalVariableTag(typeX, localName, tv.getSlot())); |
| // } |
| // } |
| return tv; |
| } |
| |
| // eh doesn't think we need to garbage collect these (64K is a big number...) |
| private int genTempVarIndex(int size) { |
| return enclosingMethod.allocateLocal(size); |
| } |
| |
| public InstructionFactory getFactory() { |
| return getEnclosingClass().getFactory(); |
| } |
| |
| @Override |
| public ISourceLocation getSourceLocation() { |
| int sourceLine = getSourceLine(); |
| if (sourceLine == 0 || sourceLine == -1) { |
| // Thread.currentThread().dumpStack(); |
| // System.err.println(this + ": " + range); |
| return getEnclosingClass().getType().getSourceLocation(); |
| } else { |
| // For staticinitialization, if we have a nice offset, don't build a new source loc |
| if (getKind() == Shadow.StaticInitialization && getEnclosingClass().getType().getSourceLocation().getOffset() != 0) { |
| return getEnclosingClass().getType().getSourceLocation(); |
| } else { |
| int offset = 0; |
| Kind kind = getKind(); |
| if ((kind == MethodExecution) || (kind == ConstructorExecution) || (kind == AdviceExecution) |
| || (kind == StaticInitialization) || (kind == PreInitialization) || (kind == Initialization)) { |
| if (getEnclosingMethod().hasDeclaredLineNumberInfo()) { |
| offset = getEnclosingMethod().getDeclarationOffset(); |
| } |
| } |
| return getEnclosingClass().getType().getSourceContext().makeSourceLocation(sourceLine, offset); |
| } |
| } |
| } |
| |
| public Shadow getEnclosingShadow() { |
| return enclosingShadow; |
| } |
| |
| public LazyMethodGen getEnclosingMethod() { |
| return enclosingMethod; |
| } |
| |
| public boolean isFallsThrough() { |
| return !terminatesWithReturn(); |
| } |
| |
| public void setActualTargetType(String className) { |
| this.actualInstructionTargetType = className; |
| } |
| |
| public String getActualTargetType() { |
| return actualInstructionTargetType; |
| } |
| } |