org.aspectj.weaver.bcel.BcelShadow Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aspectjweaver Show documentation
Show all versions of aspectjweaver Show documentation
The AspectJ weaver applies aspects to Java classes. It can be used as a Java agent in order to apply load-time
weaving (LTW) during class-loading and also contains the AspectJ runtime classes.
/* *******************************************************************
* 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 v 2.0
* which accompanies this distribution and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
*
* 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.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 {@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 src = mungers;
if (s.mungers == Collections.EMPTY_LIST) {
s.mungers = new ArrayList<>();
}
List dest = s.mungers;
for (ShadowMunger shadowMunger : src) {
dest.add(shadowMunger);
}
}
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
// // 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."":(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:
// // 65: return
//
// // if we attempt to match on the ctor call to StringBuffer. then we get into trouble.
// // if we simply delete the new/dup pair without fixing up the dup_x2 then the dup_x2 will fail due to there
// // not being 3 elements on the stack for it to work with. The fix *in this situation* is to change it to
// // a simple 'dup'
//
// // this fix is *not* very clean - but a general purpose decent solution will take much longer and this
// // bytecode sequence has only been seen once in the wild.
// ih.setInstruction(InstructionConstants.DUP);
/**
* The new/dup (or new/dup_x1/swap) are removed and will be readded later (after the advice call) by the caller of this method.
* The groovy compiler produces unusual code where the new/dup isn't visible (when making a this() call from an existing ctor),
* an aload_0 is used to load the uninitialized object (as an example see the ctors in grails.util.BuildSettings).
*
* @return true if managed to remove them
*/
private boolean deleteNewAndDup() {
final ConstantPool cpool = getEnclosingClass().getConstantPool();
int depth = 1;
InstructionHandle ih = range.getStart();
// Go back from where we are looking for 'NEW' that takes us to a stack depth of 0. INVOKESPECIAL
while (ih != null) {
Instruction inst = ih.getInstruction();
if (inst.opcode == Constants.INVOKESPECIAL && ((InvokeInstruction) inst).getName(cpool).equals("")) {
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 badAdvice = null;
public void addAdvicePreventingLazyTjp(BcelAdvice advice) {
if (badAdvice == null) {
badAdvice = new ArrayList<>();
}
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 (BcelAdvice element : badAdvice) {
ISourceLocation sLoc = element.getSourceLocation();
if (sLoc != null && sLoc.getLine() > 0) {
valid++;
}
}
if (valid != 0) {
ISourceLocation[] badLocs = new ISourceLocation[valid];
int i = 0;
for (BcelAdvice element : badAdvice) {
ISourceLocation sLoc = element.getSourceLocation();
if (sLoc != null && sLoc.getLine() > 0) {
badLocs[i++] = sLoc;
}
}
world.getLint().multipleAdviceStoppingLazyTjp
.signal(new String[] { this.toString() }, getSourceLocation(), badLocs);
}
}
badAdvice = null;
// If we are an expression kind, we require our target/arguments on the stack
// before we do our actual thing. However, they may have been removed
// from the stack as the shadowMungers have requested state.
// if any of our shadowMungers requested either the arguments or target,
// the munger will have added code
// to pop the target/arguments into temporary variables, represented by
// targetVar and argVars. In such a case, we must make sure to re-push the
// values.
// If we are nonExpressionKind, we don't expect arguments on the stack
// so this is moot. If our argVars happen to be null, then we know that
// no ShadowMunger has squirrelled away our arguments, so they're still
// on the stack.
InstructionFactory fact = getFactory();
if (getKind().argsOnStack() && argVars != null) {
// Special case first (pr46298). If we are an exception handler and the instruction
// just after the shadow is a POP then we should remove the pop. The code
// above which generated the store instruction has already cleared the stack.
// We also don't generate any code for the arguments in this case as it would be
// an incorrect aload.
if (getKind() == ExceptionHandler && range.getEnd().getNext().getInstruction().equals(InstructionConstants.POP)) {
// easier than deleting it ...
range.getEnd().getNext().setInstruction(InstructionConstants.NOP);
} else {
range.insert(BcelRenderer.renderExprs(fact, world, argVars), Range.InsideBefore);
if (targetVar != null) {
range.insert(BcelRenderer.renderExpr(fact, world, targetVar), Range.InsideBefore);
}
if (getKind() == ConstructorCall) {
if (!world.isJoinpointArrayConstructionEnabled() || !this.getSignature().getDeclaringType().isArray()) {
if (deletedNewAndDup) { // if didnt delete them, dont insert any!
range.insert(InstructionFactory.createDup(1), Range.InsideBefore);
range.insert(fact.createNew((ObjectType) BcelWorld.makeBcelType(getSignature().getDeclaringType())),
Range.InsideBefore);
}
}
}
}
}
}
// ---- getters
public ShadowRange getRange() {
return range;
}
public void setRange(ShadowRange range) {
this.range = range;
}
private int sourceline = -1;
public int getSourceLine() {
// if the kind of join point for which we are a shadow represents
// a method or constructor execution, then the best source line is
// the one from the enclosingMethod declarationLineNumber if available.
if (sourceline != -1) {
return sourceline;
}
Kind kind = getKind();
if ((kind == MethodExecution) || (kind == ConstructorExecution) || (kind == AdviceExecution)
|| (kind == StaticInitialization) || (kind == PreInitialization) || (kind == Initialization)) {
if (getEnclosingMethod().hasDeclaredLineNumberInfo()) {
sourceline = getEnclosingMethod().getDeclarationLineNumber();
return sourceline;
}
}
if (range == null) {
if (getEnclosingMethod().hasBody()) {
sourceline = Utility.getSourceLine(getEnclosingMethod().getBody().getStart());
return sourceline;
} else {
sourceline = 0;
return sourceline;
}
}
sourceline = Utility.getSourceLine(range.getStart());
if (sourceline < 0) {
sourceline = 0;
}
return sourceline;
}
@Override
public ResolvedType getEnclosingType() {
return getEnclosingClass().getType();
}
public LazyClassGen getEnclosingClass() {
return enclosingMethod.getEnclosingClass();
}
public BcelWorld getWorld() {
return world;
}
// ---- factory methods
public static BcelShadow makeConstructorExecution(BcelWorld world, LazyMethodGen enclosingMethod,
InstructionHandle justBeforeStart) {
final InstructionList body = enclosingMethod.getBody();
BcelShadow s = new BcelShadow(world, ConstructorExecution, world.makeJoinPointSignatureFromMethod(enclosingMethod,
Member.CONSTRUCTOR), enclosingMethod, null);
ShadowRange r = new ShadowRange(body);
r.associateWithShadow(s);
r.associateWithTargets(Range.genStart(body, justBeforeStart.getNext()), Range.genEnd(body));
return s;
}
public static BcelShadow makeStaticInitialization(BcelWorld world, LazyMethodGen enclosingMethod) {
InstructionList body = enclosingMethod.getBody();
// move the start past ajc$preClinit
InstructionHandle clinitStart = body.getStart();
if (clinitStart.getInstruction() instanceof InvokeInstruction) {
InvokeInstruction ii = (InvokeInstruction) clinitStart.getInstruction();
if (ii.getName(enclosingMethod.getEnclosingClass().getConstantPool()).equals(NameMangler.AJC_PRE_CLINIT_NAME)) {
clinitStart = clinitStart.getNext();
}
}
InstructionHandle clinitEnd = body.getEnd();
// XXX should move the end before the postClinit, but the return is then tricky...
// if (clinitEnd.getInstruction() instanceof InvokeInstruction) {
// InvokeInstruction ii = (InvokeInstruction)clinitEnd.getInstruction();
// if (ii.getName(enclosingMethod.getEnclosingClass().getConstantPool()).equals(NameMangler.AJC_POST_CLINIT_NAME)) {
// clinitEnd = clinitEnd.getPrev();
// }
// }
BcelShadow s = new BcelShadow(world, StaticInitialization, world.makeJoinPointSignatureFromMethod(enclosingMethod,
Member.STATIC_INITIALIZATION), enclosingMethod, null);
ShadowRange r = new ShadowRange(body);
r.associateWithShadow(s);
r.associateWithTargets(Range.genStart(body, clinitStart), Range.genEnd(body, clinitEnd));
return s;
}
/**
* Make the shadow for an exception handler. Currently makes an empty shadow that only allows before advice to be woven into it.
*/
public static BcelShadow makeExceptionHandler(BcelWorld world, ExceptionRange exceptionRange, LazyMethodGen enclosingMethod,
InstructionHandle startOfHandler, BcelShadow enclosingShadow) {
InstructionList body = enclosingMethod.getBody();
UnresolvedType catchType = exceptionRange.getCatchType();
UnresolvedType inType = enclosingMethod.getEnclosingClass().getType();
ResolvedMemberImpl sig = MemberImpl.makeExceptionHandlerSignature(inType, catchType);
sig.setParameterNames(new String[] { findHandlerParamName(startOfHandler) });
BcelShadow s = new BcelShadow(world, ExceptionHandler, sig, enclosingMethod, enclosingShadow);
ShadowRange r = new ShadowRange(body);
r.associateWithShadow(s);
InstructionHandle start = Range.genStart(body, startOfHandler);
InstructionHandle end = Range.genEnd(body, start);
r.associateWithTargets(start, end);
exceptionRange.updateTarget(startOfHandler, start, body);
return s;
}
private static String findHandlerParamName(InstructionHandle startOfHandler) {
if (startOfHandler.getInstruction().isStoreInstruction() && startOfHandler.getNext() != null) {
int slot = startOfHandler.getInstruction().getIndex();
// System.out.println("got store: " + startOfHandler.getInstruction() + ", " + index);
for (InstructionTargeter targeter : startOfHandler.getNext().getTargeters()) {
if (targeter instanceof LocalVariableTag) {
LocalVariableTag t = (LocalVariableTag) targeter;
if (t.getSlot() == slot) {
return t.getName();
}
}
}
}
return "";
}
/** 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, its 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 initially just around the
// call to the constructor. If ANY advice gets put on it, we move
// the NEW instruction inside the join point, which involves putting
// all the arguments in temps.
public static BcelShadow makeConstructorCall(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle callHandle,
BcelShadow enclosingShadow) {
final InstructionList body = enclosingMethod.getBody();
Member sig = world.makeJoinPointSignatureForMethodInvocation(enclosingMethod.getEnclosingClass(),
(InvokeInstruction) callHandle.getInstruction());
BcelShadow s = new BcelShadow(world, ConstructorCall, sig, enclosingMethod, enclosingShadow);
ShadowRange r = new ShadowRange(body);
r.associateWithShadow(s);
r.associateWithTargets(Range.genStart(body, callHandle), Range.genEnd(body, callHandle));
retargetAllBranches(callHandle, r.getStart());
return s;
}
public static BcelShadow makeArrayConstructorCall(BcelWorld world, LazyMethodGen enclosingMethod,
InstructionHandle arrayInstruction, BcelShadow enclosingShadow) {
final InstructionList body = enclosingMethod.getBody();
Member sig = world.makeJoinPointSignatureForArrayConstruction(enclosingMethod.getEnclosingClass(), arrayInstruction);
BcelShadow s = new BcelShadow(world, ConstructorCall, sig, enclosingMethod, enclosingShadow);
ShadowRange r = new ShadowRange(body);
r.associateWithShadow(s);
r.associateWithTargets(Range.genStart(body, arrayInstruction), Range.genEnd(body, arrayInstruction));
retargetAllBranches(arrayInstruction, r.getStart());
return s;
}
public static BcelShadow makeMonitorEnter(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle monitorInstruction,
BcelShadow enclosingShadow) {
final InstructionList body = enclosingMethod.getBody();
Member sig = world.makeJoinPointSignatureForMonitorEnter(enclosingMethod.getEnclosingClass(), monitorInstruction);
BcelShadow s = new BcelShadow(world, SynchronizationLock, sig, enclosingMethod, enclosingShadow);
ShadowRange r = new ShadowRange(body);
r.associateWithShadow(s);
r.associateWithTargets(Range.genStart(body, monitorInstruction), Range.genEnd(body, monitorInstruction));
retargetAllBranches(monitorInstruction, r.getStart());
return s;
}
public static BcelShadow makeMonitorExit(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle monitorInstruction,
BcelShadow enclosingShadow) {
final InstructionList body = enclosingMethod.getBody();
Member sig = world.makeJoinPointSignatureForMonitorExit(enclosingMethod.getEnclosingClass(), monitorInstruction);
BcelShadow s = new BcelShadow(world, SynchronizationUnlock, sig, enclosingMethod, enclosingShadow);
ShadowRange r = new ShadowRange(body);
r.associateWithShadow(s);
r.associateWithTargets(Range.genStart(body, monitorInstruction), Range.genEnd(body, monitorInstruction));
retargetAllBranches(monitorInstruction, r.getStart());
return s;
}
// see pr77166
// public static BcelShadow makeArrayLoadCall(
// BcelWorld world,
// LazyMethodGen enclosingMethod,
// InstructionHandle arrayInstruction,
// BcelShadow enclosingShadow)
// {
// final InstructionList body = enclosingMethod.getBody();
// Member sig = world.makeJoinPointSignatureForArrayLoad(enclosingMethod.getEnclosingClass(),arrayInstruction);
// BcelShadow s =
// new BcelShadow(
// world,
// MethodCall,
// sig,
// enclosingMethod,
// enclosingShadow);
// ShadowRange r = new ShadowRange(body);
// r.associateWithShadow(s);
// r.associateWithTargets(
// Range.genStart(body, arrayInstruction),
// Range.genEnd(body, arrayInstruction));
// retargetAllBranches(arrayInstruction, r.getStart());
// return s;
// }
public static BcelShadow makeMethodCall(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle callHandle,
BcelShadow enclosingShadow) {
final InstructionList body = enclosingMethod.getBody();
BcelShadow s = new BcelShadow(world, MethodCall, world.makeJoinPointSignatureForMethodInvocation(
enclosingMethod.getEnclosingClass(), (InvokeInstruction) callHandle.getInstruction()), enclosingMethod,
enclosingShadow);
ShadowRange r = new ShadowRange(body);
r.associateWithShadow(s);
r.associateWithTargets(Range.genStart(body, callHandle), Range.genEnd(body, callHandle));
retargetAllBranches(callHandle, r.getStart());
return s;
}
public static BcelShadow makeShadowForMethodCall(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle callHandle,
BcelShadow enclosingShadow, Kind kind, ResolvedMember sig) {
final InstructionList body = enclosingMethod.getBody();
BcelShadow s = new BcelShadow(world, kind, sig, enclosingMethod, enclosingShadow);
ShadowRange r = new ShadowRange(body);
r.associateWithShadow(s);
r.associateWithTargets(Range.genStart(body, callHandle), Range.genEnd(body, callHandle));
retargetAllBranches(callHandle, r.getStart());
return s;
}
public static BcelShadow makeFieldGet(BcelWorld world, ResolvedMember field, LazyMethodGen enclosingMethod,
InstructionHandle getHandle, BcelShadow enclosingShadow) {
final InstructionList body = enclosingMethod.getBody();
BcelShadow s = new BcelShadow(world, FieldGet, field,
// BcelWorld.makeFieldSignature(
// enclosingMethod.getEnclosingClass(),
// (FieldInstruction) getHandle.getInstruction()),
enclosingMethod, enclosingShadow);
ShadowRange r = new ShadowRange(body);
r.associateWithShadow(s);
r.associateWithTargets(Range.genStart(body, getHandle), Range.genEnd(body, getHandle));
retargetAllBranches(getHandle, r.getStart());
return s;
}
public static BcelShadow makeFieldSet(BcelWorld world, ResolvedMember field, LazyMethodGen enclosingMethod,
InstructionHandle setHandle, BcelShadow enclosingShadow) {
final InstructionList body = enclosingMethod.getBody();
BcelShadow s = new BcelShadow(world, FieldSet, field,
// BcelWorld.makeFieldJoinPointSignature(
// enclosingMethod.getEnclosingClass(),
// (FieldInstruction) setHandle.getInstruction()),
enclosingMethod, enclosingShadow);
ShadowRange r = new ShadowRange(body);
r.associateWithShadow(s);
r.associateWithTargets(Range.genStart(body, setHandle), Range.genEnd(body, setHandle));
retargetAllBranches(setHandle, r.getStart());
return s;
}
public static void retargetAllBranches(InstructionHandle from, InstructionHandle to) {
for (InstructionTargeter source : from.getTargetersCopy()) {
if (source instanceof InstructionBranch) {
source.updateTarget(from, to);
}
}
}
// // ---- type access methods
// private ObjectType getTargetBcelType() {
// return (ObjectType) BcelWorld.makeBcelType(getTargetType());
// }
// private Type getArgBcelType(int arg) {
// return BcelWorld.makeBcelType(getArgType(arg));
// }
// ---- kinding
/**
* If the end of my range has no real instructions following then my context needs a return at the end.
*/
public boolean terminatesWithReturn() {
return getRange().getRealNext() == null;
}
/**
* Is arg0 occupied with the value of this
*/
public boolean arg0HoldsThis() {
if (getKind().isEnclosingKind()) {
return !Modifier.isStatic(getSignature().getModifiers());
} else if (enclosingShadow == null) {
// XXX this is mostly right
// this doesn't do the right thing for calls in the pre part of introduced constructors.
return !enclosingMethod.isStatic();
} else {
return ((BcelShadow) enclosingShadow).arg0HoldsThis();
}
}
// ---- argument getting methods
private BcelVar thisVar = null;
private BcelVar targetVar = null;
private BcelVar[] argVars = null;
private Map kindedAnnotationVars = null;
private Map thisAnnotationVars = null;
private Map targetAnnotationVars = null;
// private Map/* */[] argAnnotationVars = null;
private Map withinAnnotationVars = null;
private Map 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 (ShadowMunger munger : mungers) {
if (munger instanceof Advice) {
if (((Advice) munger).getKind() == AdviceKind.Around) {
if (munger.getSourceLocation() != null) { // do we know enough to bother reporting?
if (world.getLint().canNotImplementLazyTjp.isEnabled()) {
world.getLint().canNotImplementLazyTjp.signal(new String[]{toString()}, getSourceLocation(),
new ISourceLocation[]{munger.getSourceLocation()});
}
}
return false;
}
}
}
return true;
}
InstructionList loadThisJoinPoint() {
InstructionFactory fact = getFactory();
InstructionList il = new InstructionList();
if (isThisJoinPointLazy) {
// If we're lazy, build the join point right here.
il.append(createThisJoinPoint());
// Does someone else need it? If so, store it for later retrieval
if (lazyTjpConsumers > 1) {
il.append(thisJoinPointVar.createStore(fact));
InstructionHandle end = il.append(thisJoinPointVar.createLoad(fact));
il.insert(InstructionFactory.createBranchInstruction(Constants.IFNONNULL, end));
il.insert(thisJoinPointVar.createLoad(fact));
}
} else {
// If not lazy, its already been built and stored, just retrieve it
thisJoinPointVar.appendLoad(il, fact);
}
return il;
}
InstructionList createThisJoinPoint() {
InstructionFactory fact = getFactory();
InstructionList il = new InstructionList();
BcelVar staticPart = getThisJoinPointStaticPartBcelVar();
staticPart.appendLoad(il, fact);
if (hasThis()) {
((BcelVar) getThisVar()).appendLoad(il, fact);
} else {
il.append(InstructionConstants.ACONST_NULL);
}
if (hasTarget()) {
((BcelVar) getTargetVar()).appendLoad(il, fact);
} else {
il.append(InstructionConstants.ACONST_NULL);
}
switch (getArgCount()) {
case 0:
il.append(fact.createInvoke("org.aspectj.runtime.reflect.Factory", "makeJP", LazyClassGen.tjpType, new Type[] {
LazyClassGen.staticTjpType, Type.OBJECT, Type.OBJECT }, Constants.INVOKESTATIC));
break;
case 1:
((BcelVar) getArgVar(0)).appendLoadAndConvert(il, fact, world.getCoreType(ResolvedType.OBJECT));
il.append(fact.createInvoke("org.aspectj.runtime.reflect.Factory", "makeJP", LazyClassGen.tjpType, new Type[] {
LazyClassGen.staticTjpType, Type.OBJECT, Type.OBJECT, Type.OBJECT }, Constants.INVOKESTATIC));
break;
case 2:
((BcelVar) getArgVar(0)).appendLoadAndConvert(il, fact, world.getCoreType(ResolvedType.OBJECT));
((BcelVar) getArgVar(1)).appendLoadAndConvert(il, fact, world.getCoreType(ResolvedType.OBJECT));
il.append(fact.createInvoke("org.aspectj.runtime.reflect.Factory", "makeJP", LazyClassGen.tjpType, new Type[] {
LazyClassGen.staticTjpType, Type.OBJECT, Type.OBJECT, Type.OBJECT, Type.OBJECT }, Constants.INVOKESTATIC));
break;
default:
il.append(makeArgsObjectArray());
il.append(fact.createInvoke("org.aspectj.runtime.reflect.Factory", "makeJP", LazyClassGen.tjpType, new Type[] {
LazyClassGen.staticTjpType, Type.OBJECT, Type.OBJECT, new ArrayType(Type.OBJECT, 1) }, Constants.INVOKESTATIC));
break;
}
return il;
}
public BcelVar getThisJoinPointStaticPartBcelVar() {
return getThisJoinPointStaticPartBcelVar(false);
}
@Override
public BcelVar getThisAspectInstanceVar(ResolvedType aspectType) {
return new AspectInstanceVar(aspectType);
}
/**
* Get the Var for the xxxxJpStaticPart, xxx = this or enclosing
*
* @param isEnclosingJp true to have the enclosingJpStaticPart
* @return
*/
public BcelVar getThisJoinPointStaticPartBcelVar(final boolean isEnclosingJp) {
if (thisJoinPointStaticPartVar == null) {
Field field = getEnclosingClass().getTjpField(this, isEnclosingJp);
ResolvedType sjpType = null;
if (world.isTargettingAspectJRuntime12()) { // TAG:SUPPORTING12: We didn't have different jpsp types in 1.2
sjpType = world.getCoreType(UnresolvedType.JOINPOINT_STATICPART);
} else {
sjpType = isEnclosingJp ? world.getCoreType(UnresolvedType.JOINPOINT_ENCLOSINGSTATICPART) : world
.getCoreType(UnresolvedType.JOINPOINT_STATICPART);
}
thisJoinPointStaticPartVar = new BcelFieldRef(sjpType, getEnclosingClass().getClassName(), field.getName());
// getEnclosingClass().warnOnAddedStaticInitializer(this,munger.getSourceLocation());
}
return thisJoinPointStaticPartVar;
}
/**
* Get the Var for the enclosingJpStaticPart
*
* @return
*/
public BcelVar getThisEnclosingJoinPointStaticPartBcelVar() {
if (enclosingShadow == null) {
// the enclosing of an execution is itself
return getThisJoinPointStaticPartBcelVar(true);
} else {
return ((BcelShadow) enclosingShadow).getThisJoinPointStaticPartBcelVar(true);
}
}
// ??? need to better understand all the enclosing variants
@Override
public Member getEnclosingCodeSignature() {
if (getKind().isEnclosingKind()) {
return getSignature();
} else if (getKind() == Shadow.PreInitialization) {
// PreInit doesn't enclose code but its signature
// is correctly the signature of the ctor.
return getSignature();
} else if (enclosingShadow == null) {
return getEnclosingMethod().getMemberView();
} else {
return enclosingShadow.getSignature();
}
}
public Member getRealEnclosingCodeSignature() {
return enclosingMethod.getMemberView();
}
// public Member getEnclosingCodeSignatureForModel() {
// if (getKind().isEnclosingKind()) {
// return getSignature();
// } else if (getKind() == Shadow.PreInitialization) {
// // PreInit doesn't enclose code but its signature
// // is correctly the signature of the ctor.
// return getSignature();
// } else if (enclosingShadow == null) {
// return getEnclosingMethod().getMemberView();
// } else {
// if (enclosingShadow.getKind() == Shadow.MethodExecution && enclosingMethod.getEffectiveSignature() != null) {
//
// } else {
// return enclosingShadow.getSignature();
// }
// }
// }
private InstructionList makeArgsObjectArray() {
InstructionFactory fact = getFactory();
BcelVar arrayVar = genTempVar(UnresolvedType.OBJECTARRAY);
final InstructionList il = new InstructionList();
int alen = getArgCount();
il.append(Utility.createConstant(fact, alen));
il.append(fact.createNewArray(Type.OBJECT, (short) 1));
arrayVar.appendStore(il, fact);
int stateIndex = 0;
for (int i = 0, len = getArgCount(); i < len; i++) {
arrayVar.appendConvertableArrayStore(il, fact, stateIndex, (BcelVar) getArgVar(i));
stateIndex++;
}
arrayVar.appendLoad(il, fact);
return il;
}
// ---- initializing var tables
/*
* initializing this is doesn't do anything, because this is protected from side-effects, so we don't need to copy its location
*/
private void initializeThisVar() {
if (thisVar != null) {
return;
}
thisVar = new BcelVar(getThisType().resolve(world), 0);
thisVar.setPositionInAroundState(0);
}
public void initializeTargetVar() {
InstructionFactory fact = getFactory();
if (targetVar != null) {
return;
}
if (getKind().isTargetSameAsThis()) {
if (hasThis()) {
initializeThisVar();
}
targetVar = thisVar;
} else {
initializeArgVars(); // gotta pop off the args before we find the target
UnresolvedType type = getTargetType();
type = ensureTargetTypeIsCorrect(type);
targetVar = genTempVar(type, "ajc$target");
range.insert(targetVar.createStore(fact), Range.OutsideBefore);
targetVar.setPositionInAroundState(hasThis() ? 1 : 0);
}
}
/*
* PR 72528 This method double checks the target type under certain conditions. The Java 1.4 compilers seem to take calls to
* clone methods on array types and create bytecode that looks like clone is being called on Object. If we advise a clone call
* with around advice we extract the call into a helper method which we can then refer to. Because the type in the bytecode for
* the call to clone is Object we create a helper method with an Object parameter - this is not correct as we have lost the fact
* that the actual type is an array type. If we don't do the check below we will create code that fails java verification. This
* method checks for the peculiar set of conditions and if they are true, it has a sneak peek at the code before the call to see
* what is on the stack.
*/
public UnresolvedType ensureTargetTypeIsCorrect(UnresolvedType tx) {
Member msig = getSignature();
if (msig.getArity() == 0 && getKind() == MethodCall && msig.getName().charAt(0) == 'c' && tx.equals(ResolvedType.OBJECT)
&& msig.getReturnType().equals(ResolvedType.OBJECT) && msig.getName().equals("clone")) {
// Lets go back through the code from the start of the shadow
InstructionHandle searchPtr = range.getStart().getPrev();
while (Range.isRangeHandle(searchPtr) || searchPtr.getInstruction().isStoreInstruction()) { // ignore this instruction -
// it doesnt give us the
// info we want
searchPtr = searchPtr.getPrev();
}
// A load instruction may tell us the real type of what the clone() call is on
if (searchPtr.getInstruction().isLoadInstruction()) {
LocalVariableTag lvt = LazyMethodGen.getLocalVariableTag(searchPtr, searchPtr.getInstruction().getIndex());
if (lvt != null) {
return UnresolvedType.forSignature(lvt.getType());
}
}
// A field access instruction may tell us the real type of what the clone() call is on
if (searchPtr.getInstruction() instanceof FieldInstruction) {
FieldInstruction si = (FieldInstruction) searchPtr.getInstruction();
Type t = si.getFieldType(getEnclosingClass().getConstantPool());
return BcelWorld.fromBcel(t);
}
// A new array instruction obviously tells us it is an array type !
if (searchPtr.getInstruction().opcode == Constants.ANEWARRAY) {
// ANEWARRAY ana = (ANEWARRAY)searchPoint.getInstruction();
// Type t = ana.getType(getEnclosingClass().getConstantPool());
// Just use a standard java.lang.object array - that will work fine
return BcelWorld.fromBcel(new ArrayType(Type.OBJECT, 1));
}
// A multi new array instruction obviously tells us it is an array type !
if (searchPtr.getInstruction() instanceof MULTIANEWARRAY) {
MULTIANEWARRAY ana = (MULTIANEWARRAY) searchPtr.getInstruction();
// Type t = ana.getType(getEnclosingClass().getConstantPool());
// t = new ArrayType(t,ana.getDimensions());
// Just use a standard java.lang.object array - that will work fine
return BcelWorld.fromBcel(new ArrayType(Type.OBJECT, ana.getDimensions()));
}
throw new BCException("Can't determine real target of clone() when processing instruction "
+ searchPtr.getInstruction() + ". Perhaps avoid selecting clone with your pointcut?");
}
return tx;
}
public void ensureInitializedArgVar(int argNumber) {
if (allArgVarsInitialized || (argVars != null && argVars[argNumber] != null)) {
return;
}
InstructionFactory fact = getFactory();
int len = getArgCount();
if (argVars == null) {
argVars = new BcelVar[len];
}
// Need to initialize argument i
int positionOffset = (hasTarget() ? 1 : 0) + ((hasThis() && !getKind().isTargetSameAsThis()) ? 1 : 0);
if (getKind().argsOnStack()) {
// Let's just do them all now since they are on the stack
// we move backwards because we're popping off the stack
for (int i = len - 1; i >= 0; i--) {
UnresolvedType type = getArgType(i);
BcelVar tmp = genTempVar(type, "ajc$arg" + i);
range.insert(tmp.createStore(getFactory()), Range.OutsideBefore);
int position = i;
position += positionOffset;
tmp.setPositionInAroundState(position);
argVars[i] = tmp;
}
allArgVarsInitialized = true;
} else {
int index = 0;
if (arg0HoldsThis()) {
index++;
}
boolean allInited = true;
for (int i = 0; i < len; i++) {
UnresolvedType type = getArgType(i);
if (i == argNumber) {
argVars[argNumber] = genTempVar(type, "ajc$arg" + argNumber);
range.insert(argVars[argNumber].createCopyFrom(fact, index), Range.OutsideBefore);
argVars[argNumber].setPositionInAroundState(argNumber + positionOffset);
}
allInited = allInited && argVars[i] != null;
index += type.getSize();
}
if (allInited && (argNumber + 1) == len) {
allArgVarsInitialized = true;
}
}
}
/**
* Initialize all the available arguments at the shadow. This means creating a copy of them that we can then use for advice
* calls (the copy ensures we are not affected by other advice changing the values). This method initializes all arguments
* whereas the method ensureInitializedArgVar will only ensure a single argument is setup.
*/
public void initializeArgVars() {
if (allArgVarsInitialized) {
return;
}
InstructionFactory fact = getFactory();
int len = getArgCount();
if (argVars == null) {
argVars = new BcelVar[len];
}
int positionOffset = (hasTarget() ? 1 : 0) + ((hasThis() && !getKind().isTargetSameAsThis()) ? 1 : 0);
if (getKind().argsOnStack()) {
// we move backwards because we're popping off the stack
for (int i = len - 1; i >= 0; i--) {
UnresolvedType type = getArgType(i);
BcelVar tmp = genTempVar(type, "ajc$arg" + i);
range.insert(tmp.createStore(getFactory()), Range.OutsideBefore);
int position = i;
position += positionOffset;
tmp.setPositionInAroundState(position);
argVars[i] = tmp;
}
} else {
int index = 0;
if (arg0HoldsThis()) {
index++;
}
for (int i = 0; i < len; i++) {
UnresolvedType type = getArgType(i);
if (argVars[i] == null) {
BcelVar tmp = genTempVar(type, "ajc$arg" + i);
range.insert(tmp.createCopyFrom(fact, index), Range.OutsideBefore);
argVars[i] = tmp;
tmp.setPositionInAroundState(i + positionOffset);
}
index += type.resolve(world).getSize();
}
}
allArgVarsInitialized = true;
}
public void initializeForAroundClosure() {
initializeArgVars();
if (hasTarget()) {
initializeTargetVar();
}
if (hasThis()) {
initializeThisVar();
// System.out.println("initialized: " + this + " thisVar = " + thisVar);
}
}
public void initializeThisAnnotationVars() {
if (thisAnnotationVars != null) {
return;
}
thisAnnotationVars = new HashMap<>();
// populate..
}
public void initializeTargetAnnotationVars() {
if (targetAnnotationVars != null) {
return;
}
if (getKind().isTargetSameAsThis()) {
if (hasThis()) {
initializeThisAnnotationVars();
}
targetAnnotationVars = thisAnnotationVars;
} else {
targetAnnotationVars = new HashMap<>();
ResolvedType[] rtx = this.getTargetType().resolve(world).getAnnotationTypes(); // what about annotations we havent
// gotten yet but we will get in
// subclasses?
for (ResolvedType typeX : rtx) {
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 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 mungers = relevantType.resolve(world).getInterTypeMungers();
for (ConcreteTypeMunger typeMunger : mungers) {
if (typeMunger.getMunger() instanceof NewMethodTypeMunger
|| typeMunger.getMunger() instanceof NewConstructorTypeMunger) {
ResolvedMember fakerm = typeMunger.getSignature();
// if (fakerm.hasAnnotations())
ResolvedMember ajcMethod = (getSignature().getKind() == ResolvedMember.CONSTRUCTOR ? AjcMemberMaker
.postIntroducedConstructor(typeMunger.getAspectType(), fakerm.getDeclaringType(),
fakerm.getParameterTypes()) : AjcMemberMaker.interMethodDispatcher(fakerm,
typeMunger.getAspectType()));
// AjcMemberMaker.interMethodBody(fakerm,typeMunger.getAspectType()));
ResolvedMember rmm = findMethod(typeMunger.getAspectType(), ajcMethod);
if (fakerm.getName().equals(getSignature().getName())
&& fakerm.getParameterSignature().equals(getSignature().getParameterSignature())) {
relevantType = typeMunger.getAspectType();
foundMember = rmm;
return foundMember.getAnnotationTypes();
}
}
}
// didn't find in ITDs, look in supers
foundMember = relevantType.lookupMemberWithSupersAndITDs(relevantMember);
if (foundMember == null) {
throw new IllegalStateException("Couldn't find member " + relevantMember + " for type " + relevantType);
}
}
return foundMember.getAnnotationTypes();
}
/**
* By determining what "kind" of shadow we are, we can find out the annotations on the appropriate element (method, field,
* constructor, type). Then create one BcelVar entry in the map for each annotation, keyed by annotation type.
*/
public void initializeKindedAnnotationVars() {
if (kindedAnnotationVars != null) {
return;
}
kindedAnnotationVars = new HashMap<>();
ResolvedType[] 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 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 (ResolvedMember member : decMethods) {
if (member.equals(ajcMethod)) {
return member;
}
}
return null;
}
private ResolvedMember findField(ResolvedMember[] members, Member lookingFor) {
for (ResolvedMember member : members) {
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[] annotations = getEnclosingType().resolve(world).getAnnotationTypes();
for (ResolvedType ann : annotations) {
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<>();
// For some shadow we are interested in annotations on the method containing that shadow.
ResolvedType[] annotations = getEnclosingMethod().getMemberView().getAnnotationTypes();
for (ResolvedType ann : annotations) {
Kind k = (getEnclosingMethod().getMemberView().getKind() == Member.CONSTRUCTOR ? Shadow.ConstructorExecution
: Shadow.MethodExecution);
withincodeAnnotationVars.put(ann, new AnnotationAccessVar(this, k, ann, getEnclosingType(),
getEnclosingCodeSignature(), true));
}
}
// ---- weave methods
void weaveBefore(BcelAdvice munger) {
range.insert(munger.getAdviceInstructions(this, null, range.getRealStart()), Range.InsideBefore);
}
public void weaveAfter(BcelAdvice munger) {
weaveAfterThrowing(munger, UnresolvedType.THROWABLE);
weaveAfterReturning(munger);
}
/**
* The basic strategy here is to add a set of instructions at the end of the shadow range that dispatch the advice, and then
* return whatever the shadow was going to return anyway.
*
* To achieve this, we note all the return statements in the advice, and replace them with code that: 1) stores the return value
* on top of the stack in a temp var 2) jumps to the start of our advice block 3) restores the return value at the end of the
* advice block before ultimately returning
*
* We also need to bind the return value into a returning parameter, if the advice specified one.
*/
public void weaveAfterReturning(BcelAdvice munger) {
List 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 (InstructionHandle ih : returns) {
retargetReturnInstruction(munger.hasExtraParameter(), returnValueVar, gotoTarget, ih);
}
}
range.append(advice);
range.append(retList);
}
/**
* @return a list of all the return instructions in the range of this shadow
*/
private List findReturnInstructions() {
List returns = new ArrayList<>();
for (InstructionHandle ih = range.getStart(); ih != range.getEnd(); ih = ih.getNext()) {
if (ih.getInstruction().isReturnInstruction()) {
returns.add(ih);
}
}
return returns;
}
/**
* Given a list containing all the return instruction handles for this shadow, finds the last return instruction and copies it,
* making this the ultimate return. If the shadow has a non-void return type, we also create a temporary variable to hold the
* return value, and load the value from this var before returning (see pr148007 for why we do this - it works around a JRockit
* bug, and is also closer to what javac generates)
*
* Sometimes the 'last return' isnt the right one - some rogue code can include the real return from the body of a subroutine
* that exists at the end of the method. In this case the last return is RETURN but that may not be correct for a method with a
* non-void return type... pr151673
*
* @param returns list of all the return instructions in the shadow
* @param returnInstructions instruction list into which the return instructions should be generated
* @return the variable holding the return value, if needed
*/
private BcelVar generateReturnInstructions(List returns, InstructionList returnInstructions) {
BcelVar returnValueVar = null;
if (this.hasANonVoidReturnType()) {
// Find the last *correct* return - this is a method with a non-void return type
// so ignore RETURN
Instruction newReturnInstruction = null;
int i = returns.size() - 1;
while (newReturnInstruction == null && i >= 0) {
InstructionHandle ih = returns.get(i);
if (ih.getInstruction().opcode != Constants.RETURN) {
newReturnInstruction = Utility.copyInstruction(ih.getInstruction());
}
i--;
}
returnValueVar = genTempVar(this.getReturnType());
returnValueVar.appendLoad(returnInstructions, getFactory());
returnInstructions.append(newReturnInstruction);
} else {
InstructionHandle lastReturnHandle = returns.get(returns.size() - 1);
Instruction newReturnInstruction = Utility.copyInstruction(lastReturnHandle.getInstruction());
returnInstructions.append(newReturnInstruction);
}
return returnValueVar;
}
/**
* @return true, iff this shadow returns a value
*/
private boolean hasANonVoidReturnType() {
return !this.getReturnType().equals(UnresolvedType.VOID);
}
/**
* Get the list of instructions used to dispatch to the after advice
*
* @param munger
* @param firstInstructionInReturnSequence
* @return
*/
private InstructionList getAfterReturningAdviceDispatchInstructions(BcelAdvice munger,
InstructionHandle firstInstructionInReturnSequence) {
InstructionList advice = new InstructionList();
BcelVar tempVar = null;
if (munger.hasExtraParameter()) {
tempVar = insertAdviceInstructionsForBindingReturningParameter(advice);
}
advice.append(munger.getAdviceInstructions(this, tempVar, firstInstructionInReturnSequence));
return advice;
}
/**
* If the after() returning(Foo f) form is used, bind the return value to the parameter. If the shadow returns void, bind null.
*
* @param advice
* @return
*/
private BcelVar insertAdviceInstructionsForBindingReturningParameter(InstructionList advice) {
BcelVar tempVar;
UnresolvedType tempVarType = getReturnType();
if (tempVarType.equals(UnresolvedType.VOID)) {
tempVar = genTempVar(UnresolvedType.OBJECT);
advice.append(InstructionConstants.ACONST_NULL);
tempVar.appendStore(advice, getFactory());
} else {
tempVar = genTempVar(tempVarType);
advice.append(InstructionFactory.createDup(tempVarType.getSize()));
tempVar.appendStore(advice, getFactory());
}
return tempVar;
}
/**
* Helper method for weaveAfterReturning
*
* Each return instruction in the method body is retargeted by calling this method. The return instruction is replaced by up to
* three instructions: 1) if the shadow returns a value, and that value is bound to an after returning parameter, then we DUP
* the return value on the top of the stack 2) if the shadow returns a value, we store it in the returnValueVar (it will be
* retrieved from here when we ultimately return after the advice dispatch) 3) if the return was the last instruction, we add a
* NOP (it will fall through to the advice dispatch), otherwise we add a GOTO that branches to the supplied gotoTarget (start of
* the advice dispatch)
*/
private void retargetReturnInstruction(boolean hasReturningParameter, BcelVar returnValueVar, InstructionHandle gotoTarget,
InstructionHandle returnHandle) {
// pr148007, work around JRockit bug
// replace ret with store into returnValueVar, followed by goto if not
// at the end of the instruction list...
InstructionList newInstructions = new InstructionList();
if (returnValueVar != null) {
if (hasReturningParameter) {
// we have to dup the return val before consuming it...
newInstructions.append(InstructionFactory.createDup(this.getReturnType().getSize()));
}
// store the return value into this var
returnValueVar.appendStore(newInstructions, getFactory());
}
if (!isLastInstructionInRange(returnHandle, range)) {
newInstructions.append(InstructionFactory.createBranchInstruction(Constants.GOTO, gotoTarget));
}
if (newInstructions.isEmpty()) {
newInstructions.append(InstructionConstants.NOP);
}
Utility.replaceInstruction(returnHandle, newInstructions, enclosingMethod);
}
private boolean isLastInstructionInRange(InstructionHandle ih, ShadowRange aRange) {
return ih.getNext() == aRange.getEnd();
}
public void weaveAfterThrowing(BcelAdvice munger, UnresolvedType catchType) {
// a good optimization would be not to generate anything here
// if the shadow is GUARANTEED empty (i.e., there's NOTHING, not even
// a shadow, inside me).
if (getRange().getStart().getNext() == getRange().getEnd()) {
return;
}
InstructionFactory fact = getFactory();
InstructionList handler = new InstructionList();
BcelVar exceptionVar = genTempVar(catchType);
exceptionVar.appendStore(handler, fact);
// pr62642
// I will now jump through some firey BCEL hoops to generate a trivial bit of code:
// if (exc instanceof ExceptionInInitializerError)
// throw (ExceptionInInitializerError)exc;
if (this.getEnclosingMethod().getName().equals("")) {
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, "", Type.VOID, new Type[] { Type.THROWABLE },
Constants.INVOKESPECIAL)); // ??? special
handler.append(InstructionConstants.ATHROW);
// ENH 42737
exceptionVar.appendStore(rtExHandler, fact);
// aload_1
rtExHandler.append(exceptionVar.createLoad(fact));
// instanceof class java/lang/RuntimeException
rtExHandler.append(fact.createInstanceOf(new ObjectType("java.lang.RuntimeException")));
// ifeq go to new SOFT_EXCEPTION_TYPE instruction
rtExHandler.append(InstructionFactory.createBranchInstruction(Constants.IFEQ, handler.getStart()));
// aload_1
rtExHandler.append(exceptionVar.createLoad(fact));
// athrow
rtExHandler.append(InstructionFactory.ATHROW);
InstructionHandle handlerStart = rtExHandler.getStart();
if (isFallsThrough()) {
InstructionHandle jumpTarget = range.getEnd();// handler.append(fact.NOP);
rtExHandler.insert(InstructionFactory.createBranchInstruction(Constants.GOTO, jumpTarget));
}
rtExHandler.append(handler);
InstructionHandle protectedEnd = rtExHandler.getStart();
range.insert(rtExHandler, Range.InsideAfter);
enclosingMethod.addExceptionHandler(range.getStart().getNext(), protectedEnd.getPrev(), handlerStart,
(ObjectType) BcelWorld.makeBcelType(catchType),
// high priority if our args are on the stack
getKind().hasHighPriorityExceptions());
}
public void weavePerObjectEntry(final BcelAdvice munger, final BcelVar onVar) {
final InstructionFactory fact = getFactory();
InstructionList entryInstructions = new InstructionList();
InstructionList entrySuccessInstructions = new InstructionList();
onVar.appendLoad(entrySuccessInstructions, fact);
entrySuccessInstructions
.append(Utility.createInvoke(fact, world, AjcMemberMaker.perObjectBind(munger.getConcreteAspect())));
InstructionList testInstructions = munger.getTestInstructions(this, entrySuccessInstructions.getStart(),
range.getRealStart(), entrySuccessInstructions.getStart());
entryInstructions.append(testInstructions);
entryInstructions.append(entrySuccessInstructions);
range.insert(entryInstructions, Range.InsideBefore);
}
// PTWIMPL Create static initializer to call the aspect factory
/**
* Causes the aspect instance to be *set* for later retrievable through localAspectof()/aspectOf()
*/
public void weavePerTypeWithinAspectInitialization(final BcelAdvice munger, UnresolvedType t) {
ResolvedType tResolved = t.resolve(world);
if (tResolved.isInterface()) {
return; // Don't initialize statics in interfaces
}
ResolvedType aspectRT = munger.getConcreteAspect();
BcelWorld.getBcelObjectType(aspectRT);
// Although matched, if the visibility rules prevent the aspect from seeing this type, don't
// insert any code (easier to do it here than try to affect the matching logic, unfortunately)
if (!(tResolved.canBeSeenBy(aspectRT) || aspectRT.isPrivilegedAspect())) {
return;
}
final InstructionFactory fact = getFactory();
InstructionList entryInstructions = new InstructionList();
InstructionList entrySuccessInstructions = new InstructionList();
String aspectname = munger.getConcreteAspect().getName();
String ptwField = NameMangler.perTypeWithinFieldForTarget(munger.getConcreteAspect());
entrySuccessInstructions.append(InstructionFactory.PUSH(fact.getConstantPool(), t.getName()));
entrySuccessInstructions.append(fact.createInvoke(aspectname, "ajc$createAspectInstance", new ObjectType(aspectname),
new Type[] { new ObjectType("java.lang.String") }, Constants.INVOKESTATIC));
entrySuccessInstructions.append(fact.createPutStatic(t.getName(), ptwField, new ObjectType(aspectname)));
entryInstructions.append(entrySuccessInstructions);
range.insert(entryInstructions, Range.InsideBefore);
}
public void weaveCflowEntry(final BcelAdvice munger, final Member cflowField) {
final boolean isPer = munger.getKind() == AdviceKind.PerCflowBelowEntry || munger.getKind() == AdviceKind.PerCflowEntry;
if (!isPer && getKind() == PreInitialization) {
return;
}
final Type objectArrayType = new ArrayType(Type.OBJECT, 1);
final InstructionFactory fact = getFactory();
final BcelVar testResult = genTempVar(UnresolvedType.BOOLEAN);
InstructionList entryInstructions = new InstructionList();
{
InstructionList entrySuccessInstructions = new InstructionList();
if (munger.hasDynamicTests()) {
entryInstructions.append(Utility.createConstant(fact, 0));
testResult.appendStore(entryInstructions, fact);
entrySuccessInstructions.append(Utility.createConstant(fact, 1));
testResult.appendStore(entrySuccessInstructions, fact);
}
if (isPer) {
entrySuccessInstructions.append(fact.createInvoke(munger.getConcreteAspect().getName(),
NameMangler.PERCFLOW_PUSH_METHOD, Type.VOID, Type.NO_ARGS, 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,
Type.NO_ARGS, 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, Type.NO_ARGS,
Constants.INVOKEVIRTUAL));
} else {
exitInstructions.append(fact.createInvoke(NameMangler.CFLOW_STACK_TYPE, "pop", Type.VOID, Type.NO_ARGS,
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 parameterNames = new ArrayList<>();
boolean shadowClassIsInterface = shadowClass.isInterface();
LazyMethodGen extractedShadowMethod = extractShadowInstructionsIntoNewMethod(extractedShadowMethodName,
shadowClassIsInterface?Modifier.PUBLIC:Modifier.PRIVATE,
munger.getSourceLocation(), parameterNames,shadowClassIsInterface);
List argsToCallLocalAdviceMethodWith = new ArrayList<>();
List proceedVarList = new ArrayList<>();
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 (BcelVar var : argsToCallLocalAdviceMethodWith) {
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 StringBuilder("unknown").append(argNumber).toString();
} else {
argumentName = parameterNames.get(argNumber);
}
String argumentSignature = args[argNumber].getSignature();
LocalVariableTag lvt = new LocalVariableTag(argumentSignature, argumentName, slot, 0);
start.addTargeter(lvt);
end.addTargeter(lvt);
slot += args[argNumber].getSize();
}
}
/**
* Check if the advice method passes a pjp parameter out via an invoke instruction - if so we can't risk inlining.
*/
private boolean isAnnotationStylePassingProceedingJoinPointOutOfAdvice(BcelAdvice munger, boolean hasDynamicTest,
LazyMethodGen adviceMethod) {
if (munger.getConcreteAspect().isAnnotationStyleAspect()) {
// if we can't find one proceed() we suspect that the call
// is happening in an inner class so we don't inline it.
// Note: for code style, this is done at Aspect compilation time.
boolean canSeeProceedPassedToOther = false;
InstructionHandle curr = adviceMethod.getBody().getStart();
InstructionHandle end = adviceMethod.getBody().getEnd();
ConstantPool cpg = adviceMethod.getEnclosingClass().getConstantPool();
while (curr != end) {
InstructionHandle next = curr.getNext();
Instruction inst = curr.getInstruction();
if ((inst instanceof InvokeInstruction)
&& ((InvokeInstruction) inst).getSignature(cpg).indexOf("Lorg/aspectj/lang/ProceedingJoinPoint;") > 0) {
// we may want to refine to exclude stuff returning jp ?
// does code style skip inline if i write dump(thisJoinPoint) ?
canSeeProceedPassedToOther = true;// we see one pjp passed around - dangerous
break;
}
curr = next;
}
if (canSeeProceedPassedToOther) {
// remember this decision to avoid re-analysis
adviceMethod.setCanInline(false);
weaveAroundClosure(munger, hasDynamicTest);
return true;
}
}
return false;
}
private InstructionList getRedoneProceedCall(InstructionFactory fact, LazyMethodGen callbackMethod, BcelAdvice munger,
LazyMethodGen localAdviceMethod, List argVarList) {
InstructionList ret = new InstructionList();
// we have on stack all the arguments for the ADVICE call.
// we have in frame somewhere all the arguments for the non-advice call.
BcelVar[] adviceVars = munger.getExposedStateAsBcelVars(true);
IntMap proceedMap = makeProceedArgumentMap(adviceVars);
// System.out.println(proceedMap + " for " + this);
// System.out.println(argVarList);
ResolvedType[] proceedParamTypes = world.resolve(munger.getSignature().getParameterTypes());
// remove this*JoinPoint* as arguments to proceed
if (munger.getBaseParameterCount() + 1 < proceedParamTypes.length) {
int len = munger.getBaseParameterCount() + 1;
ResolvedType[] newTypes = new ResolvedType[len];
System.arraycopy(proceedParamTypes, 0, newTypes, 0, len);
proceedParamTypes = newTypes;
}
// System.out.println("stateTypes: " + Arrays.asList(stateTypes));
BcelVar[] proceedVars = Utility.pushAndReturnArrayOfVars(proceedParamTypes, ret, fact, localAdviceMethod);
Type[] stateTypes = callbackMethod.getArgumentTypes();
// System.out.println("stateTypes: " + Arrays.asList(stateTypes));
for (int i = 0, len = stateTypes.length; i < len; i++) {
Type stateType = stateTypes[i];
ResolvedType stateTypeX = BcelWorld.fromBcel(stateType).resolve(world);
if (proceedMap.hasKey(i)) {
// throw new RuntimeException("unimplemented");
proceedVars[proceedMap.get(i)].appendLoadAndConvert(ret, fact, stateTypeX);
} else {
argVarList.get(i).appendLoad(ret, fact);
}
}
ret.append(Utility.createInvoke(fact, callbackMethod));
ret.append(Utility.createConversion(fact, callbackMethod.getReturnType(),
BcelWorld.makeBcelType(munger.getSignature().getReturnType()), world.isInJava5Mode()));
return ret;
}
// private static boolean bindsThisOrTarget(Pointcut pointcut) {
// ThisTargetFinder visitor = new ThisTargetFinder();
// pointcut.accept(visitor, null);
// return visitor.bindsThisOrTarget;
// }
// private static class ThisTargetFinder extends IdentityPointcutVisitor {
// boolean bindsThisOrTarget = false;
//
// public Object visit(ThisOrTargetPointcut node, Object data) {
// if (node.isBinding()) {
// bindsThisOrTarget = true;
// }
// return node;
// }
//
// public Object visit(AndPointcut node, Object data) {
// if (!bindsThisOrTarget) node.getLeft().accept(this, data);
// if (!bindsThisOrTarget) node.getRight().accept(this, data);
// return node;
// }
//
// public Object visit(NotPointcut node, Object data) {
// if (!bindsThisOrTarget) node.getNegatedPointcut().accept(this, data);
// return node;
// }
//
// public Object visit(OrPointcut node, Object data) {
// if (!bindsThisOrTarget) node.getLeft().accept(this, data);
// if (!bindsThisOrTarget) node.getRight().accept(this, data);
// return node;
// }
// }
/**
* Annotation style handling for inlining.
*
* Note: The proceedingjoinpoint is already on the stack (since the user was calling pjp.proceed(...)
*
* The proceed map is ignored (in terms of argument repositioning) since we have a fixed expected format for annotation style.
* The aim here is to change the proceed() call into a call to the xxx_aroundBody0 method.
*
*
*/
private InstructionList getRedoneProceedCallForAnnotationStyle(InstructionFactory fact, LazyMethodGen callbackMethod,
BcelAdvice munger, LazyMethodGen localAdviceMethod, List 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<>(),shadowClassIsInterface);
BcelVar[] adviceVars = munger.getExposedStateAsBcelVars(true);
String closureClassName = NameMangler.makeClosureClassName(getEnclosingClass().getType(), getEnclosingClass()
.getNewGeneratedNameTag());
Member constructorSig = new MemberImpl(Member.CONSTRUCTOR, UnresolvedType.forName(closureClassName), 0, "",
"([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(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 */
private InstructionList makeClosureInstantiation(Member constructor, BcelVar holder) {
// LazyMethodGen constructor) {
InstructionFactory fact = getFactory();
BcelVar arrayVar = genTempVar(UnresolvedType.OBJECTARRAY);
// final Type objectArrayType = new ArrayType(Type.OBJECT, 1);
final InstructionList il = new InstructionList();
int alen = getArgCount() + (thisVar == null ? 0 : 1) + ((targetVar != null && targetVar != thisVar) ? 1 : 0)
+ (thisJoinPointVar == null ? 0 : 1);
il.append(Utility.createConstant(fact, alen));
il.append(fact.createNewArray(Type.OBJECT, (short) 1));
arrayVar.appendStore(il, fact);
int stateIndex = 0;
if (thisVar != null) {
arrayVar.appendConvertableArrayStore(il, fact, stateIndex, thisVar);
thisVar.setPositionInAroundState(stateIndex);
stateIndex++;
}
if (targetVar != null && targetVar != thisVar) {
arrayVar.appendConvertableArrayStore(il, fact, stateIndex, targetVar);
targetVar.setPositionInAroundState(stateIndex);
stateIndex++;
}
for (int i = 0, len = getArgCount(); i < len; i++) {
arrayVar.appendConvertableArrayStore(il, fact, stateIndex, argVars[i]);
argVars[i].setPositionInAroundState(stateIndex);
stateIndex++;
}
if (thisJoinPointVar != null) {
arrayVar.appendConvertableArrayStore(il, fact, stateIndex, thisJoinPointVar);
thisJoinPointVar.setPositionInAroundState(stateIndex);
stateIndex++;
}
il.append(fact.createNew(new ObjectType(constructor.getDeclaringType().getName())));
il.append(InstructionConstants.DUP);
arrayVar.appendLoad(il, fact);
il.append(Utility.createInvoke(fact, world, constructor));
if (getKind() == PreInitialization) {
il.append(InstructionConstants.DUP);
holder.appendStore(il, fact);
}
return il;
}
private IntMap makeProceedArgumentMap(BcelVar[] adviceArgs) {
// System.err.println("coming in with " + Arrays.asList(adviceArgs));
IntMap ret = new IntMap();
for (int i = 0, len = adviceArgs.length; i < len; i++) {
BcelVar v = adviceArgs[i];
if (v == null) {
continue; // XXX we don't know why this is required
}
int pos = v.getPositionInAroundState();
if (pos >= 0) { // need this test to avoid args bound via cflow
ret.put(pos, i);
}
}
// System.err.println("returning " + ret);
return ret;
}
/**
*
* @param callbackMethod the method we will call back to when our run method gets called.
* @param proceedMap A map from state position to proceed argument position. May be non covering on state position.
*/
private LazyMethodGen makeClosureClassAndReturnConstructor(String closureClassName, LazyMethodGen callbackMethod,
IntMap proceedMap) {
String superClassName = "org.aspectj.runtime.internal.AroundClosure";
Type objectArrayType = new ArrayType(Type.OBJECT, 1);
LazyClassGen closureClass = new LazyClassGen(closureClassName, superClassName, getEnclosingClass().getFileName(),
Modifier.PUBLIC, new String[] {}, getWorld());
closureClass.setMajorMinor(getEnclosingClass().getMajor(), getEnclosingClass().getMinor());
InstructionFactory fact = new InstructionFactory(closureClass.getConstantPool());
// constructor
LazyMethodGen constructor = new LazyMethodGen(Modifier.PUBLIC, Type.VOID, "", 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, "", Type.VOID, new Type[] { objectArrayType }, Constants.INVOKESPECIAL));
cbody.append(InstructionFactory.createReturn(Type.VOID));
closureClass.addMethodGen(constructor);
// Create the 'Object run(Object[])' method
LazyMethodGen runMethod = new LazyMethodGen(Modifier.PUBLIC, Type.OBJECT, "run", new Type[] { objectArrayType },
new String[] {}, closureClass);
InstructionList mbody = runMethod.getBody();
BcelVar proceedVar = new BcelVar(UnresolvedType.OBJECTARRAY.resolve(world), 1);
// int proceedVarIndex = 1;
BcelVar stateVar = new BcelVar(UnresolvedType.OBJECTARRAY.resolve(world), runMethod.allocateLocal(1));
// int stateVarIndex = runMethod.allocateLocal(1);
mbody.append(InstructionFactory.createThis());
mbody.append(fact.createGetField(superClassName, "state", objectArrayType));
mbody.append(stateVar.createStore(fact));
// mbody.append(fact.createStore(objectArrayType, stateVarIndex));
Type[] stateTypes = callbackMethod.getArgumentTypes();
for (int i = 0, len = stateTypes.length; i < len; i++) {
ResolvedType resolvedStateType = BcelWorld.fromBcel(stateTypes[i]).resolve(world);
if (proceedMap.hasKey(i)) {
mbody.append(proceedVar.createConvertableArrayLoad(fact, proceedMap.get(i), resolvedStateType));
} else {
mbody.append(stateVar.createConvertableArrayLoad(fact, i, resolvedStateType));
}
}
mbody.append(Utility.createInvoke(fact, callbackMethod));
if (getKind() == PreInitialization) {
mbody.append(Utility.createSet(fact, AjcMemberMaker.aroundClosurePreInitializationField()));
mbody.append(InstructionConstants.ACONST_NULL);
} else {
mbody.append(Utility.createConversion(fact, callbackMethod.getReturnType(), Type.OBJECT));
}
mbody.append(InstructionFactory.createReturn(Type.OBJECT));
closureClass.addMethodGen(runMethod);
// class
getEnclosingClass().addGeneratedInner(closureClass);
return constructor;
}
// ---- extraction methods
/**
* Extract the instructions in the shadow to a new method.
*
* @param extractedMethodName name for the new method
* @param extractedMethodVisibilityModifier visibility modifiers for the new method
* @param adviceSourceLocation source location of the advice affecting the shadow
* @param beingPlacedInInterface is this new method going into an interface
*/
LazyMethodGen extractShadowInstructionsIntoNewMethod(String extractedMethodName, int extractedMethodVisibilityModifier,
ISourceLocation adviceSourceLocation, List parameterNames, boolean beingPlacedInInterface) {
// LazyMethodGen.assertGoodBody(range.getBody(), extractedMethodName);
if (!getKind().allowsExtraction()) {
throw new BCException("Attempt to extract method from a shadow kind (" + getKind()
+ ") that does not support this operation");
}
LazyMethodGen newMethod = createShadowMethodGen(extractedMethodName, extractedMethodVisibilityModifier, parameterNames, beingPlacedInInterface);
IntMap remapper = makeRemap();
range.extractInstructionsInto(newMethod, remapper, (getKind() != PreInitialization) && isFallsThrough());
if (getKind() == PreInitialization) {
addPreInitializationReturnCode(newMethod, getSuperConstructorParameterTypes());
}
getEnclosingClass().addMethodGen(newMethod, adviceSourceLocation);
return newMethod;
}
private void addPreInitializationReturnCode(LazyMethodGen extractedMethod, Type[] superConstructorTypes) {
InstructionList body = extractedMethod.getBody();
final InstructionFactory fact = getFactory();
BcelVar arrayVar = new BcelVar(world.getCoreType(UnresolvedType.OBJECTARRAY), extractedMethod.allocateLocal(1));
int len = superConstructorTypes.length;
body.append(Utility.createConstant(fact, len));
body.append(fact.createNewArray(Type.OBJECT, (short) 1));
arrayVar.appendStore(body, fact);
for (int i = len - 1; i >= 0; i++) {
// convert thing on top of stack to object
body.append(Utility.createConversion(fact, superConstructorTypes[i], Type.OBJECT));
// push object array
arrayVar.appendLoad(body, fact);
// swap
body.append(InstructionConstants.SWAP);
// do object array store.
body.append(Utility.createConstant(fact, i));
body.append(InstructionConstants.SWAP);
body.append(InstructionFactory.createArrayStore(Type.OBJECT));
}
arrayVar.appendLoad(body, fact);
body.append(InstructionConstants.ARETURN);
}
private Type[] getSuperConstructorParameterTypes() {
// assert getKind() == PreInitialization
InstructionHandle superCallHandle = getRange().getEnd().getNext();
InvokeInstruction superCallInstruction = (InvokeInstruction) superCallHandle.getInstruction();
return superCallInstruction.getArgumentTypes(getEnclosingClass().getConstantPool());
}
/**
* make a map from old frame location to new frame location. Any unkeyed frame location picks out a copied local
*/
private IntMap makeRemap() {
IntMap ret = new IntMap(5);
int reti = 0;
if (thisVar != null) {
ret.put(0, reti++); // thisVar guaranteed to be 0
}
if (targetVar != null && targetVar != thisVar) {
ret.put(targetVar.getSlot(), reti++);
}
for (BcelVar argVar : argVars) {
ret.put(argVar.getSlot(), reti);
reti += argVar.getType().getSize();
}
if (thisJoinPointVar != null) {
ret.put(thisJoinPointVar.getSlot(), reti++);
}
// we not only need to put the arguments, we also need to remap their
// aliases, which we so helpfully put into temps at the beginning of this join
// point.
if (!getKind().argsOnStack()) {
int oldi = 0;
int newi = 0;
// if we're passing in a this and we're not argsOnStack we're always
// passing in a target too
if (arg0HoldsThis()) {
ret.put(0, 0);
oldi++;
newi += 1;
}
// assert targetVar == thisVar
for (int i = 0; i < getArgCount(); i++) {
UnresolvedType type = getArgType(i);
ret.put(oldi, newi);
oldi += type.getSize();
newi += type.getSize();
}
}
// System.err.println("making remap for : " + this);
// if (targetVar != null) System.err.println("target slot : " + targetVar.getSlot());
// if (thisVar != null) System.err.println(" this slot : " + thisVar.getSlot());
// System.err.println(ret);
return ret;
}
/**
* The new method always static. It may take some extra arguments: this, target. If it's argsOnStack, then it must take both
* this/target If it's argsOnFrame, it shares this and target. ??? rewrite this to do less array munging, please
*/
private LazyMethodGen createShadowMethodGen(String newMethodName, int visibilityModifier, List 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;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy