/* *******************************************************************
 * 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.BranchHandle;
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 ShadowRange.associateWithShadow(BcelShadow) method.
/** * 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).
Returns:true if managed to remove them
/** * 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.
/** * 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
/** 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.
Params:
  • constructor – The constructor starting this initialization.
/** * 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.
/** * 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
/** * 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
Params:
  • isEnclosingJp – true to have the enclosingJpStaticPart
Returns:
/** * 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
Returns:
/** * 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.
/** * 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.
/** * 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.
/** * 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); }
Returns:a list of all the return instructions in the range of this shadow
/** * @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
Params:
  • returns – list of all the return instructions in the shadow
  • returnInstructions – instruction list into which the return instructions should be generated
Returns:the variable holding the return value, if needed
/** * 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; }
Returns:true, iff this shadow returns a value
/** * @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
Params:
  • munger –
  • firstInstructionInReturnSequence –
Returns:
/** * 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.
Params:
  • advice –
Returns:
/** * 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)
/** * 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()
/** * 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.
/** * 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.
/** * 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"))); BranchHandle jumpOverHandler = advice.append(new InstructionBranch(Constants.GOTO, null)); // Call unlink in finally block // Do not POP the exception off, we need to rethrow it InstructionHandle handlerStart = advice.append(aroundClosureInstance.createLoad(fact)); advice.append(Utility.createInvoke(getFactory(), getWorld(), new MemberImpl(Member.METHOD, UnresolvedType .forName("org.aspectj.runtime.internal.AroundClosure"), Modifier.PUBLIC, "unlink", "()V"))); // After that exception is on the top of the stack again advice.append(InstructionConstants.ATHROW); InstructionHandle jumpTarget = advice.append(InstructionConstants.NOP); jumpOverHandler.setTarget(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
/** 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; }
Params:
  • callbackMethod – the method we will call back to when our run method gets called.
  • proceedMap – A map from state position to proceed argument position. May be non covering on state position.
/** * * @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.
Params:
  • extractedMethodName – name for the new method
  • extractedMethodVisibilityModifier – visibility modifiers for the new method
  • adviceSourceLocation – source location of the advice affecting the shadow
  • beingPlacedInInterface – is this new method going into an interface
/** * 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
/** * 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
/** * 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; } }