Package org.aspectj.weaver.bcel

Source Code of org.aspectj.weaver.bcel.BcelShadow

/* *******************************************************************
* Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     PARC     initial implementation
*     Alexandre Vasseur    support for @AJ aspects
* ******************************************************************/

package org.aspectj.weaver.bcel;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.aspectj.apache.bcel.Constants;
import org.aspectj.apache.bcel.classfile.ConstantPool;
import org.aspectj.apache.bcel.classfile.Field;
import org.aspectj.apache.bcel.generic.ArrayType;
import org.aspectj.apache.bcel.generic.FieldInstruction;
import org.aspectj.apache.bcel.generic.INVOKEINTERFACE;
import org.aspectj.apache.bcel.generic.Instruction;
import org.aspectj.apache.bcel.generic.InstructionBranch;
import org.aspectj.apache.bcel.generic.InstructionConstants;
import org.aspectj.apache.bcel.generic.InstructionFactory;
import org.aspectj.apache.bcel.generic.InstructionHandle;
import org.aspectj.apache.bcel.generic.InstructionLV;
import org.aspectj.apache.bcel.generic.InstructionList;
import org.aspectj.apache.bcel.generic.InstructionTargeter;
import org.aspectj.apache.bcel.generic.InvokeInstruction;
import org.aspectj.apache.bcel.generic.LineNumberTag;
import org.aspectj.apache.bcel.generic.LocalVariableTag;
import org.aspectj.apache.bcel.generic.MULTIANEWARRAY;
import org.aspectj.apache.bcel.generic.ObjectType;
import org.aspectj.apache.bcel.generic.TargetLostException;
import org.aspectj.apache.bcel.generic.Type;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.weaver.Advice;
import org.aspectj.weaver.AdviceKind;
import org.aspectj.weaver.AjcMemberMaker;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.ConcreteTypeMunger;
import org.aspectj.weaver.IntMap;
import org.aspectj.weaver.Member;
import org.aspectj.weaver.MemberImpl;
import org.aspectj.weaver.NameMangler;
import org.aspectj.weaver.NewConstructorTypeMunger;
import org.aspectj.weaver.NewFieldTypeMunger;
import org.aspectj.weaver.NewMethodTypeMunger;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedMemberImpl;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.Shadow;
import org.aspectj.weaver.ShadowMunger;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.WeaverMessages;
import org.aspectj.weaver.World;
import org.aspectj.weaver.ast.Var;
import org.aspectj.weaver.patterns.AbstractPatternNodeVisitor;
import org.aspectj.weaver.patterns.AndPointcut;
import org.aspectj.weaver.patterns.NotPointcut;
import org.aspectj.weaver.patterns.OrPointcut;
import org.aspectj.weaver.patterns.ThisOrTargetPointcut;

/*
* Some fun implementation stuff:
*
*   * expressionKind advice is non-execution advice
*     * may have a target.
*     * if the body is extracted, it will be extracted into
*       a static method.  The first argument to the static
*       method is the target
*     * advice may expose a this object, but that's the advice's
*       consideration, not ours.  This object will NOT be cached in another
*       local, but will always come from frame zero.
*
*   * non-expressionKind advice is execution advice
*     * may have a this.
*     * target is same as this, and is exposed that way to advice
*       (i.e., target will not be cached, will always come from frame zero)
*     * if the body is extracted, it will be extracted into a method
*       with same static/dynamic modifier as enclosing method.  If non-static,
*       target of callback call will be this.
*
*   * because of these two facts, the setup of the actual arguments (including
*     possible target) callback method is the same for both kinds of advice:
*     push the targetVar, if it exists (it will not exist for advice on static
*     things), then push all the argVars. 
*
* Protected things:
*
*   * the above is sufficient for non-expressionKind advice for protected things,
*     since the target will always be this.
*
*   * For expressionKind things, we have to modify the signature of the callback
*     method slightly.  For non-static expressionKind things, we modify
*     the first argument of the callback method NOT to be the type specified
*     by the method/field signature (the owner), but rather we type it to
*     the currentlyEnclosing type. We are guaranteed this will be fine,
*     since the verifier verifies that the target is a subtype of the currently
*     enclosingType.
*
* Worries:
*
*    * ConstructorCalls will be weirder than all of these, since they
*      supposedly don't have a target (according to AspectJ), but they clearly
*      do have a target of sorts, just one that needs to be pushed on the stack,
*      dupped, and not touched otherwise until the constructor runs.
* @author Jim Hugunin
* @author Erik Hilsdale
*
*/

public class BcelShadow extends Shadow {

  private static final String[] NoDeclaredExceptions = new String[0];

  private ShadowRange range;
  private final BcelWorld world;
  private final LazyMethodGen enclosingMethod;

  // TESTING this will tell us if the optimisation succeeded *on the last shadow processed*
  public static boolean appliedLazyTjpOptimization;

  // Some instructions have a target type that will vary
  // from the signature (pr109728) (1.4 declaring type issue)
  private String actualInstructionTargetType;

  /**
   * This generates an unassociated shadow, rooted in a particular method but not rooted to any particular point in the code. It
   * should be given to a rooted ShadowRange in the {@link ShadowRange#associateWithShadow(BcelShadow)} method.
   */
  public BcelShadow(BcelWorld world, Kind kind, Member signature, LazyMethodGen enclosingMethod, BcelShadow enclosingShadow) {
    super(kind, signature, enclosingShadow);
    this.world = world;
    this.enclosingMethod = enclosingMethod;
  }

  // ---- copies all state, including Shadow's mungers...

  public BcelShadow copyInto(LazyMethodGen recipient, BcelShadow enclosing) {
    BcelShadow s = new BcelShadow(world, getKind(), getSignature(), recipient, enclosing);
    if (mungers.size() > 0) {
      List<ShadowMunger> src = mungers;
      if (s.mungers == Collections.EMPTY_LIST) {
        s.mungers = new ArrayList<ShadowMunger>();
      }
      List<ShadowMunger> dest = s.mungers;
      for (Iterator<ShadowMunger> i = src.iterator(); i.hasNext();) {
        dest.add(i.next());
      }
    }
    return s;
  }

  // ---- overridden behaviour

  @Override
  public World getIWorld() {
    return world;
  }

  private void deleteNewAndDup() {
    final ConstantPool cpg = getEnclosingClass().getConstantPool();
    int depth = 1;
    InstructionHandle ih = range.getStart();

    // Go back from where we are looking for 'NEW' that takes us to a stack depth of 0. INVOKESPECIAL <init>
    while (true) {
      Instruction inst = ih.getInstruction();
      if (inst.opcode == Constants.INVOKESPECIAL && ((InvokeInstruction) inst).getName(cpg).equals("<init>")) {
        depth++;
      } else if (inst.opcode == Constants.NEW) {
        depth--;
        if (depth == 0) {
          break;
        }
      } else if (inst.opcode == Constants.DUP_X2) {
        // This code seen in the wild (by Brad):
        // 40: new #12; //class java/lang/StringBuffer
        // STACK: STRINGBUFFER
        // 43: dup
        // STACK: STRINGBUFFER/STRINGBUFFER
        // 44: aload_0
        // STACK: STRINGBUFFER/STRINGBUFFER/THIS
        // 45: dup_x2
        // STACK: THIS/STRINGBUFFER/STRINGBUFFER/THIS
        // 46: getfield #36; //Field value:Ljava/lang/String;
        // STACK: THIS/STRINGBUFFER/STRINGBUFFER/STRING<value>
        // 49: invokestatic #37; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
        // STACK: THIS/STRINGBUFFER/STRINGBUFFER/STRING
        // 52: invokespecial #19; //Method java/lang/StringBuffer."<init>":(Ljava/lang/String;)V
        // STACK: THIS/STRINGBUFFER
        // 55: aload_1
        // STACK: THIS/STRINGBUFFER/LOCAL1
        // 56: invokevirtual #22; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
        // STACK: THIS/STRINGBUFFER
        // 59: invokevirtual #34; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;
        // STACK: THIS/STRING
        // 62: putfield #36; //Field value:Ljava/lang/String;
        // STACK: <empty>
        // 65: return

        // if we attempt to match on the ctor call to StringBuffer.<init> then we get into trouble.
        // if we simply delete the new/dup pair without fixing up the dup_x2 then the dup_x2 will fail due to there
        // not being 3 elements on the stack for it to work with. The fix *in this situation* is to change it to
        // a simple 'dup'

        // this fix is *not* very clean - but a general purpose decent solution will take much longer and this
        // bytecode sequence has only been seen once in the wild.
        ih.setInstruction(InstructionConstants.DUP);
      }
      ih = ih.getPrev();
    }
    // 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");
    }
  }

  private void retargetFrom(InstructionHandle old, InstructionHandle fresh) {
    for (InstructionTargeter targeter : old.getTargetersCopy()) {
      if (targeter instanceof ExceptionRange) {
        ExceptionRange it = (ExceptionRange) targeter;
        it.updateTarget(old, fresh, it.getBody());
      } else {
        targeter.updateTarget(old, fresh);
      }
    }
  }

  // records advice that is stopping us doing the lazyTjp optimization
  private List<BcelAdvice> badAdvice = null;

  public void addAdvicePreventingLazyTjp(BcelAdvice advice) {
    if (badAdvice == null) {
      badAdvice = new ArrayList<BcelAdvice>();
    }
    badAdvice.add(advice);
  }

  @Override
  protected void prepareForMungers() {
    // if we're a constructor call, we need to remove the new:dup or the new:dup_x1:swap,
    // and store all our
    // arguments on the frame.

    // ??? This is a bit of a hack (for the Java langauge). We do this because
    // we sometime add code "outsideBefore" when dealing with weaving join points. We only
    // do this for exposing state that is on the stack. It turns out to just work for
    // everything except for constructor calls and exception handlers. If we were to clean
    // this up, every ShadowRange would have three instructionHandle points, the start of
    // the arg-setup code, the start of the running code, and the end of the running code.
    if (getKind() == ConstructorCall) {
      if (!world.isJoinpointArrayConstructionEnabled() || !this.getSignature().getDeclaringType().isArray()) {
        deleteNewAndDup(); // no new/dup for new array construction
      }
      initializeArgVars();
    } else if (getKind() == PreInitialization) { // pr74952
      ShadowRange range = getRange();
      range.insert(InstructionConstants.NOP, Range.InsideAfter);
    } else if (getKind() == ExceptionHandler) {

      ShadowRange range = getRange();
      InstructionList body = range.getBody();
      InstructionHandle start = range.getStart();

      // Create a store instruction to put the value from the top of the
      // stack into a local variable slot. This is a trimmed version of
      // what is in initializeArgVars() (since there is only one argument
      // at a handler jp and only before advice is supported) (pr46298)
      argVars = new BcelVar[1];
      // int positionOffset = (hasTarget() ? 1 : 0) + ((hasThis() && !getKind().isTargetSameAsThis()) ? 1 : 0);
      UnresolvedType tx = getArgType(0);
      argVars[0] = genTempVar(tx, "ajc$arg0");
      InstructionHandle insertedInstruction = range.insert(argVars[0].createStore(getFactory()), Range.OutsideBefore);

      // Now the exception range starts just after our new instruction.
      // The next bit of code changes the exception range to point at
      // the store instruction
      for (InstructionTargeter t : start.getTargetersCopy()) {
        if (t instanceof ExceptionRange) {
          ExceptionRange er = (ExceptionRange) t;
          er.updateTarget(start, insertedInstruction, body);
        }
      }
    }

    // now we ask each munger to request our state
    isThisJoinPointLazy = true;// world.isXlazyTjp(); // lazy is default now

    badAdvice = null;
    for (ShadowMunger munger : mungers) {
      munger.specializeOn(this);
    }

    initializeThisJoinPoint();

    if (thisJoinPointVar != null && !isThisJoinPointLazy && badAdvice != null && badAdvice.size() > 1) {
      // something stopped us making it a lazy tjp
      // can't build tjp lazily, no suitable test...
      int valid = 0;
      for (Iterator<BcelAdvice> iter = badAdvice.iterator(); iter.hasNext();) {
        BcelAdvice element = iter.next();
        ISourceLocation sLoc = element.getSourceLocation();
        if (sLoc != null && sLoc.getLine() > 0) {
          valid++;
        }
      }
      if (valid != 0) {
        ISourceLocation[] badLocs = new ISourceLocation[valid];
        int i = 0;
        for (Iterator<BcelAdvice> iter = badAdvice.iterator(); iter.hasNext();) {
          BcelAdvice element = iter.next();
          ISourceLocation sLoc = element.getSourceLocation();
          if (sLoc != null) {
            badLocs[i++] = sLoc;
          }
        }
        world.getLint().multipleAdviceStoppingLazyTjp
            .signal(new String[] { this.toString() }, getSourceLocation(), badLocs);
      }
    }
    badAdvice = null;

    // If we are an expression kind, we require our target/arguments on the stack
    // before we do our actual thing. However, they may have been removed
    // from the stack as the shadowMungers have requested state.
    // if any of our shadowMungers requested either the arguments or target,
    // the munger will have added code
    // to pop the target/arguments into temporary variables, represented by
    // targetVar and argVars. In such a case, we must make sure to re-push the
    // values.

    // If we are nonExpressionKind, we don't expect arguments on the stack
    // so this is moot. If our argVars happen to be null, then we know that
    // no ShadowMunger has squirrelled away our arguments, so they're still
    // on the stack.
    InstructionFactory fact = getFactory();
    if (getKind().argsOnStack() && argVars != null) {

      // Special case first (pr46298). If we are an exception handler and the instruction
      // just after the shadow is a POP then we should remove the pop. The code
      // above which generated the store instruction has already cleared the stack.
      // We also don't generate any code for the arguments in this case as it would be
      // an incorrect aload.
      if (getKind() == ExceptionHandler && range.getEnd().getNext().getInstruction().equals(InstructionConstants.POP)) {
        // easier than deleting it ...
        range.getEnd().getNext().setInstruction(InstructionConstants.NOP);
      } else {
        range.insert(BcelRenderer.renderExprs(fact, world, argVars), Range.InsideBefore);
        if (targetVar != null) {
          range.insert(BcelRenderer.renderExpr(fact, world, targetVar), Range.InsideBefore);
        }
        if (getKind() == ConstructorCall) {
          if (!world.isJoinpointArrayConstructionEnabled() || !this.getSignature().getDeclaringType().isArray()) {
            range.insert(InstructionFactory.createDup(1), Range.InsideBefore);
            range.insert(fact.createNew((ObjectType) BcelWorld.makeBcelType(getSignature().getDeclaringType())),
                Range.InsideBefore);
          }
        }
      }
    }
  }

  // ---- getters

  public ShadowRange getRange() {
    return range;
  }

  public void setRange(ShadowRange range) {
    this.range = range;
  }

  private int sourceline = -1;

  public int getSourceLine() {
    // if the kind of join point for which we are a shadow represents
    // a method or constructor execution, then the best source line is
    // the one from the enclosingMethod declarationLineNumber if available.
    if (sourceline != -1) {
      return sourceline;
    }
    Kind kind = getKind();
    if ((kind == MethodExecution) || (kind == ConstructorExecution) || (kind == AdviceExecution)
        || (kind == StaticInitialization) || (kind == PreInitialization) || (kind == Initialization)) {
      if (getEnclosingMethod().hasDeclaredLineNumberInfo()) {
        sourceline = getEnclosingMethod().getDeclarationLineNumber();
        return sourceline;
      }
    }

    if (range == null) {
      if (getEnclosingMethod().hasBody()) {
        sourceline = Utility.getSourceLine(getEnclosingMethod().getBody().getStart());
        return sourceline;
      } else {
        sourceline = 0;
        return sourceline;
      }
    }
    sourceline = Utility.getSourceLine(range.getStart());
    if (sourceline < 0) {
      sourceline = 0;
    }
    return sourceline;
  }

  @Override
  public ResolvedType getEnclosingType() {
    return getEnclosingClass().getType();
  }

  public LazyClassGen getEnclosingClass() {
    return enclosingMethod.getEnclosingClass();
  }

  public BcelWorld getWorld() {
    return world;
  }

  // ---- factory methods

  public static BcelShadow makeConstructorExecution(BcelWorld world, LazyMethodGen enclosingMethod,
      InstructionHandle justBeforeStart) {
    final InstructionList body = enclosingMethod.getBody();
    BcelShadow s = new BcelShadow(world, ConstructorExecution, world.makeJoinPointSignatureFromMethod(enclosingMethod,
        Member.CONSTRUCTOR), enclosingMethod, null);
    ShadowRange r = new ShadowRange(body);
    r.associateWithShadow(s);
    r.associateWithTargets(Range.genStart(body, justBeforeStart.getNext()), Range.genEnd(body));
    return s;
  }

  public static BcelShadow makeStaticInitialization(BcelWorld world, LazyMethodGen enclosingMethod) {
    InstructionList body = enclosingMethod.getBody();
    // move the start past ajc$preClinit
    InstructionHandle clinitStart = body.getStart();
    if (clinitStart.getInstruction() instanceof InvokeInstruction) {
      InvokeInstruction ii = (InvokeInstruction) clinitStart.getInstruction();
      if (ii.getName(enclosingMethod.getEnclosingClass().getConstantPool()).equals(NameMangler.AJC_PRE_CLINIT_NAME)) {
        clinitStart = clinitStart.getNext();
      }
    }

    InstructionHandle clinitEnd = body.getEnd();

    // XXX should move the end before the postClinit, but the return is then tricky...
    // if (clinitEnd.getInstruction() instanceof InvokeInstruction) {
    // InvokeInstruction ii = (InvokeInstruction)clinitEnd.getInstruction();
    // if (ii.getName(enclosingMethod.getEnclosingClass().getConstantPool()).equals(NameMangler.AJC_POST_CLINIT_NAME)) {
    // clinitEnd = clinitEnd.getPrev();
    // }
    // }

    BcelShadow s = new BcelShadow(world, StaticInitialization, world.makeJoinPointSignatureFromMethod(enclosingMethod,
        Member.STATIC_INITIALIZATION), enclosingMethod, null);
    ShadowRange r = new ShadowRange(body);
    r.associateWithShadow(s);
    r.associateWithTargets(Range.genStart(body, clinitStart), Range.genEnd(body, clinitEnd));
    return s;
  }

  /**
   * Make the shadow for an exception handler. Currently makes an empty shadow that only allows before advice to be woven into it.
   */

  public static BcelShadow makeExceptionHandler(BcelWorld world, ExceptionRange exceptionRange, LazyMethodGen enclosingMethod,
      InstructionHandle startOfHandler, BcelShadow enclosingShadow) {
    InstructionList body = enclosingMethod.getBody();
    UnresolvedType catchType = exceptionRange.getCatchType();
    UnresolvedType inType = enclosingMethod.getEnclosingClass().getType();

    ResolvedMemberImpl sig = MemberImpl.makeExceptionHandlerSignature(inType, catchType);
    sig.setParameterNames(new String[] { findHandlerParamName(startOfHandler) });

    BcelShadow s = new BcelShadow(world, ExceptionHandler, sig, enclosingMethod, enclosingShadow);
    ShadowRange r = new ShadowRange(body);
    r.associateWithShadow(s);
    InstructionHandle start = Range.genStart(body, startOfHandler);
    InstructionHandle end = Range.genEnd(body, start);

    r.associateWithTargets(start, end);
    exceptionRange.updateTarget(startOfHandler, start, body);
    return s;
  }

  private static String findHandlerParamName(InstructionHandle startOfHandler) {
    if (startOfHandler.getInstruction().isStoreInstruction() && startOfHandler.getNext() != null) {
      int slot = startOfHandler.getInstruction().getIndex();
      // System.out.println("got store: " + startOfHandler.getInstruction() + ", " + index);
      Iterator tIter = startOfHandler.getNext().getTargeters().iterator();
      while (tIter.hasNext()) {
        InstructionTargeter targeter = (InstructionTargeter) tIter.next();
        if (targeter instanceof LocalVariableTag) {
          LocalVariableTag t = (LocalVariableTag) targeter;
          if (t.getSlot() == slot) {
            return t.getName();
          }
        }
      }
    }

    return "<missing>";
  }

  /** create an init join point associated w/ an interface in the body of a constructor */

  public static BcelShadow makeIfaceInitialization(BcelWorld world, LazyMethodGen constructor,
      Member interfaceConstructorSignature) {
    // this call marks the instruction list as changed
    constructor.getBody();
    // UnresolvedType inType = constructor.getEnclosingClass().getType();
    BcelShadow s = new BcelShadow(world, Initialization, interfaceConstructorSignature, constructor, null);
    // s.fallsThrough = true;
    // ShadowRange r = new ShadowRange(body);
    // r.associateWithShadow(s);
    // InstructionHandle start = Range.genStart(body, handle);
    // InstructionHandle end = Range.genEnd(body, handle);
    //       
    // r.associateWithTargets(start, end);
    return s;
  }

  public void initIfaceInitializer(InstructionHandle end) {
    final InstructionList body = enclosingMethod.getBody();
    ShadowRange r = new ShadowRange(body);
    r.associateWithShadow(this);
    InstructionHandle nop = body.insert(end, InstructionConstants.NOP);

    r.associateWithTargets(Range.genStart(body, nop), Range.genEnd(body, nop));
  }

  // public static BcelShadow makeIfaceConstructorExecution(
  // BcelWorld world,
  // LazyMethodGen constructor,
  // InstructionHandle next,
  // Member interfaceConstructorSignature)
  // {
  // // final InstructionFactory fact = constructor.getEnclosingClass().getFactory();
  // InstructionList body = constructor.getBody();
  // // UnresolvedType inType = constructor.getEnclosingClass().getType();
  // BcelShadow s =
  // new BcelShadow(
  // world,
  // ConstructorExecution,
  // interfaceConstructorSignature,
  // constructor,
  // null);
  // s.fallsThrough = true;
  // ShadowRange r = new ShadowRange(body);
  // r.associateWithShadow(s);
  // // ??? this may or may not work
  // InstructionHandle start = Range.genStart(body, next);
  // //InstructionHandle end = Range.genEnd(body, body.append(start, fact.NOP));
  // InstructionHandle end = Range.genStart(body, next);
  // //body.append(start, fact.NOP);
  //       
  // r.associateWithTargets(start, end);
  // return s;
  // }

  /**
   * Create an initialization join point associated with a constructor, but not with any body of code yet. If this is actually
   * matched, it's range will be set when we inline self constructors.
   *
   * @param constructor The constructor starting this initialization.
   */
  public static BcelShadow makeUnfinishedInitialization(BcelWorld world, LazyMethodGen constructor) {
    BcelShadow ret = new BcelShadow(world, Initialization, world.makeJoinPointSignatureFromMethod(constructor,
        Member.CONSTRUCTOR), constructor, null);
    if (constructor.getEffectiveSignature() != null) {
      ret.setMatchingSignature(constructor.getEffectiveSignature().getEffectiveSignature());
    }
    return ret;
  }

  public static BcelShadow makeUnfinishedPreinitialization(BcelWorld world, LazyMethodGen constructor) {
    BcelShadow ret = new BcelShadow(world, PreInitialization, world.makeJoinPointSignatureFromMethod(constructor,
        Member.CONSTRUCTOR), constructor, null);
    if (constructor.getEffectiveSignature() != null) {
      ret.setMatchingSignature(constructor.getEffectiveSignature().getEffectiveSignature());
    }
    return ret;
  }

  public static BcelShadow makeMethodExecution(BcelWorld world, LazyMethodGen enclosingMethod, boolean lazyInit) {
    if (!lazyInit) {
      return makeMethodExecution(world, enclosingMethod);
    }

    BcelShadow s = new BcelShadow(world, MethodExecution, enclosingMethod.getMemberView(), enclosingMethod, null);

    return s;
  }

  public void init() {
    if (range != null) {
      return;
    }

    final InstructionList body = enclosingMethod.getBody();
    ShadowRange r = new ShadowRange(body);
    r.associateWithShadow(this);
    r.associateWithTargets(Range.genStart(body), Range.genEnd(body));
  }

  public static BcelShadow makeMethodExecution(BcelWorld world, LazyMethodGen enclosingMethod) {
    return makeShadowForMethod(world, enclosingMethod, MethodExecution, enclosingMethod.getMemberView());
  }

  public static BcelShadow makeShadowForMethod(BcelWorld world, LazyMethodGen enclosingMethod, Shadow.Kind kind, Member sig) {
    final InstructionList body = enclosingMethod.getBody();
    BcelShadow s = new BcelShadow(world, kind, sig, enclosingMethod, null);
    ShadowRange r = new ShadowRange(body);
    r.associateWithShadow(s);
    r.associateWithTargets(// OPTIMIZE this occurs lots of times for all jp kinds...
        Range.genStart(body), Range.genEnd(body));
    return s;
  }

  public static BcelShadow makeAdviceExecution(BcelWorld world, LazyMethodGen enclosingMethod) {
    final InstructionList body = enclosingMethod.getBody();
    BcelShadow s = new BcelShadow(world, AdviceExecution, world
        .makeJoinPointSignatureFromMethod(enclosingMethod, Member.ADVICE), enclosingMethod, null);
    ShadowRange r = new ShadowRange(body);
    r.associateWithShadow(s);
    r.associateWithTargets(Range.genStart(body), Range.genEnd(body));
    return s;
  }

  // constructor call shadows are <em>initially</em> just around the
  // call to the constructor. If ANY advice gets put on it, we move
  // the NEW instruction inside the join point, which involves putting
  // all the arguments in temps.
  public static BcelShadow makeConstructorCall(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle callHandle,
      BcelShadow enclosingShadow) {
    final InstructionList body = enclosingMethod.getBody();

    Member sig = world.makeJoinPointSignatureForMethodInvocation(enclosingMethod.getEnclosingClass(),
        (InvokeInstruction) callHandle.getInstruction());

    BcelShadow s = new BcelShadow(world, ConstructorCall, sig, enclosingMethod, enclosingShadow);
    ShadowRange r = new ShadowRange(body);
    r.associateWithShadow(s);
    r.associateWithTargets(Range.genStart(body, callHandle), Range.genEnd(body, callHandle));
    retargetAllBranches(callHandle, r.getStart());
    return s;
  }

  public static BcelShadow makeArrayConstructorCall(BcelWorld world, LazyMethodGen enclosingMethod,
      InstructionHandle arrayInstruction, BcelShadow enclosingShadow) {
    final InstructionList body = enclosingMethod.getBody();
    Member sig = world.makeJoinPointSignatureForArrayConstruction(enclosingMethod.getEnclosingClass(), arrayInstruction);
    BcelShadow s = new BcelShadow(world, ConstructorCall, sig, enclosingMethod, enclosingShadow);
    ShadowRange r = new ShadowRange(body);
    r.associateWithShadow(s);
    r.associateWithTargets(Range.genStart(body, arrayInstruction), Range.genEnd(body, arrayInstruction));
    retargetAllBranches(arrayInstruction, r.getStart());
    return s;
  }

  public static BcelShadow makeMonitorEnter(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle monitorInstruction,
      BcelShadow enclosingShadow) {
    final InstructionList body = enclosingMethod.getBody();
    Member sig = world.makeJoinPointSignatureForMonitorEnter(enclosingMethod.getEnclosingClass(), monitorInstruction);
    BcelShadow s = new BcelShadow(world, SynchronizationLock, sig, enclosingMethod, enclosingShadow);
    ShadowRange r = new ShadowRange(body);
    r.associateWithShadow(s);
    r.associateWithTargets(Range.genStart(body, monitorInstruction), Range.genEnd(body, monitorInstruction));
    retargetAllBranches(monitorInstruction, r.getStart());
    return s;
  }

  public static BcelShadow makeMonitorExit(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle monitorInstruction,
      BcelShadow enclosingShadow) {
    final InstructionList body = enclosingMethod.getBody();
    Member sig = world.makeJoinPointSignatureForMonitorExit(enclosingMethod.getEnclosingClass(), monitorInstruction);
    BcelShadow s = new BcelShadow(world, SynchronizationUnlock, sig, enclosingMethod, enclosingShadow);
    ShadowRange r = new ShadowRange(body);
    r.associateWithShadow(s);
    r.associateWithTargets(Range.genStart(body, monitorInstruction), Range.genEnd(body, monitorInstruction));
    retargetAllBranches(monitorInstruction, r.getStart());
    return s;
  }

  // see pr77166
  // public static BcelShadow makeArrayLoadCall(
  // BcelWorld world,
  // LazyMethodGen enclosingMethod,
  // InstructionHandle arrayInstruction,
  // BcelShadow enclosingShadow)
  // {
  // final InstructionList body = enclosingMethod.getBody();
  // Member sig = world.makeJoinPointSignatureForArrayLoad(enclosingMethod.getEnclosingClass(),arrayInstruction);
  // BcelShadow s =
  // new BcelShadow(
  // world,
  // MethodCall,
  // sig,
  // enclosingMethod,
  // enclosingShadow);
  // ShadowRange r = new ShadowRange(body);
  // r.associateWithShadow(s);
  // r.associateWithTargets(
  // Range.genStart(body, arrayInstruction),
  // Range.genEnd(body, arrayInstruction));
  // retargetAllBranches(arrayInstruction, r.getStart());
  // return s;
  // }

  public static BcelShadow makeMethodCall(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle callHandle,
      BcelShadow enclosingShadow) {
    final InstructionList body = enclosingMethod.getBody();
    BcelShadow s = new BcelShadow(world, MethodCall, world.makeJoinPointSignatureForMethodInvocation(enclosingMethod
        .getEnclosingClass(), (InvokeInstruction) callHandle.getInstruction()), enclosingMethod, enclosingShadow);
    ShadowRange r = new ShadowRange(body);
    r.associateWithShadow(s);
    r.associateWithTargets(Range.genStart(body, callHandle), Range.genEnd(body, callHandle));
    retargetAllBranches(callHandle, r.getStart());
    return s;
  }

  public static BcelShadow makeShadowForMethodCall(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle callHandle,
      BcelShadow enclosingShadow, Kind kind, ResolvedMember sig) {
    final InstructionList body = enclosingMethod.getBody();
    BcelShadow s = new BcelShadow(world, kind, sig, enclosingMethod, enclosingShadow);
    ShadowRange r = new ShadowRange(body);
    r.associateWithShadow(s);
    r.associateWithTargets(Range.genStart(body, callHandle), Range.genEnd(body, callHandle));
    retargetAllBranches(callHandle, r.getStart());
    return s;
  }

  public static BcelShadow makeFieldGet(BcelWorld world, ResolvedMember field, LazyMethodGen enclosingMethod,
      InstructionHandle getHandle, BcelShadow enclosingShadow) {
    final InstructionList body = enclosingMethod.getBody();
    BcelShadow s = new BcelShadow(world, FieldGet, field,
    // BcelWorld.makeFieldSignature(
        // enclosingMethod.getEnclosingClass(),
        // (FieldInstruction) getHandle.getInstruction()),
        enclosingMethod, enclosingShadow);
    ShadowRange r = new ShadowRange(body);
    r.associateWithShadow(s);
    r.associateWithTargets(Range.genStart(body, getHandle), Range.genEnd(body, getHandle));
    retargetAllBranches(getHandle, r.getStart());
    return s;
  }

  public static BcelShadow makeFieldSet(BcelWorld world, ResolvedMember field, LazyMethodGen enclosingMethod,
      InstructionHandle setHandle, BcelShadow enclosingShadow) {
    final InstructionList body = enclosingMethod.getBody();
    BcelShadow s = new BcelShadow(world, FieldSet, field,
    // BcelWorld.makeFieldJoinPointSignature(
        // enclosingMethod.getEnclosingClass(),
        // (FieldInstruction) setHandle.getInstruction()),
        enclosingMethod, enclosingShadow);
    ShadowRange r = new ShadowRange(body);
    r.associateWithShadow(s);
    r.associateWithTargets(Range.genStart(body, setHandle), Range.genEnd(body, setHandle));
    retargetAllBranches(setHandle, r.getStart());
    return s;
  }

  public static void retargetAllBranches(InstructionHandle from, InstructionHandle to) {
    for (InstructionTargeter source : from.getTargetersCopy()) {
      if (source instanceof InstructionBranch) {
        source.updateTarget(from, to);
      }
    }
  }

  // // ---- type access methods
  // private ObjectType getTargetBcelType() {
  // return (ObjectType) BcelWorld.makeBcelType(getTargetType());
  // }
  // private Type getArgBcelType(int arg) {
  // return BcelWorld.makeBcelType(getArgType(arg));
  // }

  // ---- kinding

  /**
   * If the end of my range has no real instructions following then my context needs a return at the end.
   */
  public boolean terminatesWithReturn() {
    return getRange().getRealNext() == null;
  }

  /**
   * Is arg0 occupied with the value of this
   */
  public boolean arg0HoldsThis() {
    if (getKind().isEnclosingKind()) {
      return !Modifier.isStatic(getSignature().getModifiers());
    } else if (enclosingShadow == null) {
      // XXX this is mostly right
      // this doesn't do the right thing for calls in the pre part of introduced constructors.
      return !enclosingMethod.isStatic();
    } else {
      return ((BcelShadow) enclosingShadow).arg0HoldsThis();
    }
  }

  // ---- argument getting methods

  private BcelVar thisVar = null;
  private BcelVar targetVar = null;
  private BcelVar[] argVars = null;
  private Map/* <UnresolvedType,BcelVar> */<ResolvedType, AnnotationAccessVar> kindedAnnotationVars = null;
  private Map/* <UnresolvedType,BcelVar> */<ResolvedType, TypeAnnotationAccessVar> thisAnnotationVars = null;
  private Map/* <UnresolvedType,BcelVar> */<ResolvedType, TypeAnnotationAccessVar> targetAnnotationVars = null;
  private Map/* <UnresolvedType,BcelVar> */[] argAnnotationVars = null;
  private Map/* <UnresolvedType,BcelVar> */<ResolvedType, AnnotationAccessVar> withinAnnotationVars = null;
  private Map/* <UnresolvedType,BcelVar> */<ResolvedType, AnnotationAccessVar> withincodeAnnotationVars = null;
  private boolean allArgVarsInitialized = 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) {
    initializeArgAnnotationVars();

    Var v = (Var) argAnnotationVars[i].get(forAnnotationType);
    if (v == null) {
      v = new TypeAnnotationAccessVar(forAnnotationType.resolve(world), (BcelVar) getArgVar(i));
    }
    return v;
  }

  @Override
  public Var getKindedAnnotationVar(UnresolvedType forAnnotationType) {
    initializeKindedAnnotationVars();
    return kindedAnnotationVars.get(forAnnotationType);
  }

  @Override
  public Var getWithinAnnotationVar(UnresolvedType forAnnotationType) {
    initializeWithinAnnotationVars();
    return withinAnnotationVars.get(forAnnotationType);
  }

  @Override
  public Var getWithinCodeAnnotationVar(UnresolvedType forAnnotationType) {
    initializeWithinCodeAnnotationVars();
    return withincodeAnnotationVars.get(forAnnotationType);
  }

  // reflective thisJoinPoint support
  private BcelVar thisJoinPointVar = null;
  private boolean isThisJoinPointLazy;
  private int lazyTjpConsumers = 0;
  private BcelVar thisJoinPointStaticPartVar = null;

  // private BcelVar thisEnclosingJoinPointStaticPartVar = null;

  @Override
  public final Var getThisJoinPointStaticPartVar() {
    return getThisJoinPointStaticPartBcelVar();
  }

  @Override
  public final Var getThisEnclosingJoinPointStaticPartVar() {
    return getThisEnclosingJoinPointStaticPartBcelVar();
  }

  public void requireThisJoinPoint(boolean hasGuardTest, boolean isAround) {
    if (!isAround) {
      if (!hasGuardTest) {
        isThisJoinPointLazy = false;
      } else {
        lazyTjpConsumers++;
      }
    }
    // if (!hasGuardTest) {
    // isThisJoinPointLazy = false;
    // } else {
    // lazyTjpConsumers++;
    // }
    if (thisJoinPointVar == null) {
      thisJoinPointVar = genTempVar(UnresolvedType.forName("org.aspectj.lang.JoinPoint"));
    }
  }

  @Override
  public Var getThisJoinPointVar() {
    requireThisJoinPoint(false, false);
    return thisJoinPointVar;
  }

  void initializeThisJoinPoint() {
    if (thisJoinPointVar == null) {
      return;
    }

    if (isThisJoinPointLazy) {
      isThisJoinPointLazy = checkLazyTjp();
    }

    if (isThisJoinPointLazy) {
      appliedLazyTjpOptimization = true;
      createThisJoinPoint(); // make sure any state needed is initialized, but throw the instructions out

      if (lazyTjpConsumers == 1) {
        return; // special case only one lazyTjpUser
      }

      InstructionFactory fact = getFactory();
      InstructionList il = new InstructionList();
      il.append(InstructionConstants.ACONST_NULL);
      il.append(thisJoinPointVar.createStore(fact));
      range.insert(il, Range.OutsideBefore);
    } else {
      appliedLazyTjpOptimization = false;
      InstructionFactory fact = getFactory();
      InstructionList il = createThisJoinPoint();
      il.append(thisJoinPointVar.createStore(fact));
      range.insert(il, Range.OutsideBefore);
    }
  }

  private boolean checkLazyTjp() {
    // check for around advice
    for (Iterator i = mungers.iterator(); i.hasNext();) {
      ShadowMunger munger = (ShadowMunger) i.next();
      if (munger instanceof Advice) {
        if (((Advice) munger).getKind() == AdviceKind.Around) {
          if (munger.getSourceLocation() != null) { // do we know enough to bother reporting?
            if (world.getLint().canNotImplementLazyTjp.isEnabled()) {
              world.getLint().canNotImplementLazyTjp.signal(new String[] { toString() }, getSourceLocation(),
                  new ISourceLocation[] { munger.getSourceLocation() });
            }
          }
          return false;
        }
      }
    }

    return true;
  }

  InstructionList loadThisJoinPoint() {
    InstructionFactory fact = getFactory();
    InstructionList il = new InstructionList();

    if (isThisJoinPointLazy) {
      // If we're lazy, build the join point right here.
      il.append(createThisJoinPoint());

      // Does someone else need it? If so, store it for later retrieval
      if (lazyTjpConsumers > 1) {
        il.append(thisJoinPointVar.createStore(fact));

        InstructionHandle end = il.append(thisJoinPointVar.createLoad(fact));

        il.insert(InstructionFactory.createBranchInstruction(Constants.IFNONNULL, end));
        il.insert(thisJoinPointVar.createLoad(fact));
      }
    } else {
      // If not lazy, its already been built and stored, just retrieve it
      thisJoinPointVar.appendLoad(il, fact);
    }

    return il;
  }

  InstructionList createThisJoinPoint() {
    InstructionFactory fact = getFactory();
    InstructionList il = new InstructionList();

    BcelVar staticPart = getThisJoinPointStaticPartBcelVar();
    staticPart.appendLoad(il, fact);
    if (hasThis()) {
      ((BcelVar) getThisVar()).appendLoad(il, fact);
    } else {
      il.append(InstructionConstants.ACONST_NULL);
    }
    if (hasTarget()) {
      ((BcelVar) getTargetVar()).appendLoad(il, fact);
    } else {
      il.append(InstructionConstants.ACONST_NULL);
    }

    switch (getArgCount()) {
    case 0:
      il.append(fact.createInvoke("org.aspectj.runtime.reflect.Factory", "makeJP", LazyClassGen.tjpType, new Type[] {
          LazyClassGen.staticTjpType, Type.OBJECT, Type.OBJECT }, Constants.INVOKESTATIC));
      break;
    case 1:
      ((BcelVar) getArgVar(0)).appendLoadAndConvert(il, fact, world.getCoreType(ResolvedType.OBJECT));
      il.append(fact.createInvoke("org.aspectj.runtime.reflect.Factory", "makeJP", LazyClassGen.tjpType, new Type[] {
          LazyClassGen.staticTjpType, Type.OBJECT, Type.OBJECT, Type.OBJECT }, Constants.INVOKESTATIC));
      break;
    case 2:
      ((BcelVar) getArgVar(0)).appendLoadAndConvert(il, fact, world.getCoreType(ResolvedType.OBJECT));
      ((BcelVar) getArgVar(1)).appendLoadAndConvert(il, fact, world.getCoreType(ResolvedType.OBJECT));
      il.append(fact.createInvoke("org.aspectj.runtime.reflect.Factory", "makeJP", LazyClassGen.tjpType, new Type[] {
          LazyClassGen.staticTjpType, Type.OBJECT, Type.OBJECT, Type.OBJECT, Type.OBJECT }, Constants.INVOKESTATIC));
      break;
    default:
      il.append(makeArgsObjectArray());
      il.append(fact.createInvoke("org.aspectj.runtime.reflect.Factory", "makeJP", LazyClassGen.tjpType, new Type[] {
          LazyClassGen.staticTjpType, Type.OBJECT, Type.OBJECT, new ArrayType(Type.OBJECT, 1) }, Constants.INVOKESTATIC));
      break;
    }

    return il;
  }

  /**
   * Get the Var for the jpStaticPart
   *
   * @return
   */
  public BcelVar getThisJoinPointStaticPartBcelVar() {
    return getThisJoinPointStaticPartBcelVar(false);
  }

  /**
   * 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.getSize();
      }
    }
    allArgVarsInitialized = true;

  }

  public void initializeForAroundClosure() {
    initializeArgVars();
    if (hasTarget()) {
      initializeTargetVar();
    }
    if (hasThis()) {
      initializeThisVar();
      // System.out.println("initialized: " + this + " thisVar = " + thisVar);
    }
  }

  public void initializeThisAnnotationVars() {
    if (thisAnnotationVars != null) {
      return;
    }
    thisAnnotationVars = new HashMap<ResolvedType, TypeAnnotationAccessVar>();
    // populate..
  }

  public void initializeTargetAnnotationVars() {
    if (targetAnnotationVars != null) {
      return;
    }
    if (getKind().isTargetSameAsThis()) {
      if (hasThis()) {
        initializeThisAnnotationVars();
      }
      targetAnnotationVars = thisAnnotationVars;
    } else {
      targetAnnotationVars = new HashMap<ResolvedType, TypeAnnotationAccessVar>();
      ResolvedType[] rtx = this.getTargetType().resolve(world).getAnnotationTypes(); // what about annotations we havent
      // gotten yet but we will get in
      // subclasses?
      for (int i = 0; i < rtx.length; i++) {
        ResolvedType typeX = rtx[i];
        targetAnnotationVars.put(typeX, new TypeAnnotationAccessVar(typeX, (BcelVar) getTargetVar()));
      }
      // populate.
    }
  }

  public void initializeArgAnnotationVars() {
    if (argAnnotationVars != null) {
      return;
    }
    int numArgs = getArgCount();
    argAnnotationVars = new Map[numArgs];
    for (int i = 0; i < argAnnotationVars.length; i++) {
      argAnnotationVars[i] = new HashMap();
      // FIXME asc just delete this logic - we always build the Var on demand, as we don't know at weave time
      // what the full set of annotations could be (due to static/dynamic type differences...)
    }
  }

  protected ResolvedMember getRelevantMember(ResolvedMember foundMember, Member relevantMember, ResolvedType relevantType) {
    if (foundMember != null) {
      return foundMember;
    }

    foundMember = getSignature().resolve(world);
    if (foundMember == null && relevantMember != null) {
      foundMember = relevantType.lookupMemberWithSupersAndITDs(relevantMember);
    }

    // check the ITD'd dooberries
    List<ConcreteTypeMunger> mungers = relevantType.resolve(world).getInterTypeMungers();
    for (ConcreteTypeMunger typeMunger : mungers) {
      if (typeMunger.getMunger() instanceof NewMethodTypeMunger || typeMunger.getMunger() instanceof NewConstructorTypeMunger) {
        ResolvedMember fakerm = typeMunger.getSignature();
        if (fakerm.getName().equals(getSignature().getName())
            && fakerm.getParameterSignature().equals(getSignature().getParameterSignature())) {
          if (foundMember.getKind() == ResolvedMember.CONSTRUCTOR) {
            foundMember = AjcMemberMaker.interConstructor(relevantType, foundMember, typeMunger.getAspectType());
          } else {
            foundMember = AjcMemberMaker.interMethod(foundMember, typeMunger.getAspectType(), false);
            // ResolvedMember o = AjcMemberMaker.interMethodBody(fakerm, typeMunger.getAspectType());
            // // Object os = o.getAnnotations();
            // ResolvedMember foundMember2 = findMethod(typeMunger.getAspectType(), o);
            // Object os2 = foundMember2.getAnnotations();
            // int stop = 1;
            // foundMember = foundMember2;
            // foundMember = AjcMemberMaker.interMethod(foundMember, typeMunger.getAspectType());
          }
          // in the above.. what about if it's on an Interface? Can that happen?
          // then the last arg of the above should be true
          return foundMember;
        }
      }
    }
    return foundMember;
  }

  protected ResolvedType[] getAnnotations(ResolvedMember foundMember, Member relevantMember, ResolvedType relevantType) {
    if (foundMember == null) {
      // check the ITD'd dooberries
      List<ConcreteTypeMunger> mungers = relevantType.resolve(world).getInterTypeMungers();
      for (Iterator<ConcreteTypeMunger> iter = mungers.iterator(); iter.hasNext();) {
        Object munger = iter.next();
        ConcreteTypeMunger typeMunger = (ConcreteTypeMunger) munger;
        if (typeMunger.getMunger() instanceof NewMethodTypeMunger
            || typeMunger.getMunger() instanceof NewConstructorTypeMunger) {
          ResolvedMember fakerm = typeMunger.getSignature();
          // if (fakerm.hasAnnotations())

          ResolvedMember ajcMethod = (getSignature().getKind() == ResolvedMember.CONSTRUCTOR ? AjcMemberMaker
              .postIntroducedConstructor(typeMunger.getAspectType(), fakerm.getDeclaringType(), fakerm
                  .getParameterTypes()) : AjcMemberMaker
              .interMethodDispatcher(fakerm, typeMunger.getAspectType()));
          // AjcMemberMaker.interMethodBody(fakerm,typeMunger.getAspectType()));
          ResolvedMember rmm = findMethod(typeMunger.getAspectType(), ajcMethod);
          if (fakerm.getName().equals(getSignature().getName())
              && fakerm.getParameterSignature().equals(getSignature().getParameterSignature())) {
            relevantType = typeMunger.getAspectType();
            foundMember = rmm;
            return foundMember.getAnnotationTypes();
          }
        }
      }
      // didn't find in ITDs, look in supers
      foundMember = relevantType.lookupMemberWithSupersAndITDs(relevantMember);
      if (foundMember == null) {
        throw new IllegalStateException("Couldn't find member " + relevantMember + " for type " + relevantType);
      }
    }
    return foundMember.getAnnotationTypes();
  }

  /**
   * By determining what "kind" of shadow we are, we can find out the annotations on the appropriate element (method, field,
   * constructor, type). Then create one BcelVar entry in the map for each annotation, keyed by annotation type.
   */
  public void initializeKindedAnnotationVars() {
    if (kindedAnnotationVars != null) {
      return;
    }
    kindedAnnotationVars = new HashMap<ResolvedType, AnnotationAccessVar>();

    ResolvedType[] annotations = null;
    Member shadowSignature = getSignature();
    Member annotationHolder = getSignature();
    ResolvedType relevantType = shadowSignature.getDeclaringType().resolve(world);

    if (relevantType.isRawType() || relevantType.isParameterizedType()) {
      relevantType = relevantType.getGenericType();
    }

    // Determine the annotations that are of interest
    if (getKind() == Shadow.StaticInitialization) {
      annotations = relevantType.resolve(world).getAnnotationTypes();
    } else if (getKind() == Shadow.MethodCall || getKind() == Shadow.ConstructorCall) {
      ResolvedMember foundMember = findMethod2(relevantType.resolve(world).getDeclaredMethods(), getSignature());
      annotations = getAnnotations(foundMember, shadowSignature, relevantType);
      annotationHolder = getRelevantMember(foundMember, shadowSignature, relevantType);
      relevantType = annotationHolder.getDeclaringType().resolve(world);
    } else if (getKind() == Shadow.FieldSet || getKind() == Shadow.FieldGet) {
      annotationHolder = findField(relevantType.getDeclaredFields(), getSignature());

      if (annotationHolder == null) {
        // check the ITD'd dooberries
        List<ConcreteTypeMunger> mungers = relevantType.resolve(world).getInterTypeMungers();
        for (ConcreteTypeMunger typeMunger : mungers) {
          if (typeMunger.getMunger() instanceof NewFieldTypeMunger) {
            ResolvedMember fakerm = typeMunger.getSignature();
            // if (fakerm.hasAnnotations())
            ResolvedMember ajcMethod = AjcMemberMaker.interFieldInitializer(fakerm, typeMunger.getAspectType());
            ResolvedMember rmm = findMethod(typeMunger.getAspectType(), ajcMethod);
            if (fakerm.equals(getSignature())) {
              relevantType = typeMunger.getAspectType();
              annotationHolder = rmm;
            }
          }
        }
      }
      annotations = ((ResolvedMember) annotationHolder).getAnnotationTypes();

    } else if (getKind() == Shadow.MethodExecution || getKind() == Shadow.ConstructorExecution
        || getKind() == Shadow.AdviceExecution) {

      ResolvedMember foundMember = findMethod2(relevantType.getDeclaredMethods(), getSignature());
      annotations = getAnnotations(foundMember, shadowSignature, relevantType);
      annotationHolder = getRelevantMember(foundMember, annotationHolder, relevantType);
      UnresolvedType ut = annotationHolder.getDeclaringType();
      relevantType = ut.resolve(world);

    } else if (getKind() == Shadow.ExceptionHandler) {
      relevantType = getSignature().getParameterTypes()[0].resolve(world);
      annotations = relevantType.getAnnotationTypes();

    } else if (getKind() == Shadow.PreInitialization || getKind() == Shadow.Initialization) {
      ResolvedMember found = findMethod2(relevantType.getDeclaredMethods(), getSignature());
      annotations = found.getAnnotationTypes();
    }

    if (annotations == null) {
      // We can't have recognized the shadow - should blow up now to be on the safe side
      throw new BCException("Could not discover annotations for shadow: " + getKind());
    }

    for (ResolvedType annotationType : annotations) {
      AnnotationAccessVar accessVar = new AnnotationAccessVar(this, getKind(), annotationType.resolve(world), relevantType,
          annotationHolder);
      kindedAnnotationVars.put(annotationType, accessVar);
    }
  }

  private ResolvedMember findMethod2(ResolvedMember members[], Member sig) {
    String signatureName = sig.getName();
    String parameterSignature = sig.getParameterSignature();
    for (ResolvedMember member : members) {
      if (member.getName().equals(signatureName) && member.getParameterSignature().equals(parameterSignature)) {
        return member;
      }
    }
    return null;
  }

  private ResolvedMember findMethod(ResolvedType aspectType, ResolvedMember ajcMethod) {
    ResolvedMember decMethods[] = aspectType.getDeclaredMethods();
    for (int i = 0; i < decMethods.length; i++) {
      ResolvedMember member = decMethods[i];
      if (member.equals(ajcMethod)) {
        return member;
      }
    }
    return null;
  }

  private ResolvedMember findField(ResolvedMember[] members, Member lookingFor) {
    for (int i = 0; i < members.length; i++) {
      ResolvedMember member = members[i];
      if (member.getName().equals(getSignature().getName()) && member.getType().equals(getSignature().getType())) {
        return member;
      }
    }
    return null;
  }

  public void initializeWithinAnnotationVars() {
    if (withinAnnotationVars != null) {
      return;
    }
    withinAnnotationVars = new HashMap<ResolvedType, AnnotationAccessVar>();

    ResolvedType[] annotations = getEnclosingType().resolve(world).getAnnotationTypes();
    for (int i = 0; i < annotations.length; i++) {
      ResolvedType ann = annotations[i];
      Kind k = Shadow.StaticInitialization;
      withinAnnotationVars.put(ann, new AnnotationAccessVar(this, k, ann, getEnclosingType(), null));
    }
  }

  public void initializeWithinCodeAnnotationVars() {
    if (withincodeAnnotationVars != null) {
      return;
    }
    withincodeAnnotationVars = new HashMap<ResolvedType, AnnotationAccessVar>();

    // For some shadow we are interested in annotations on the method containing that shadow.
    ResolvedType[] annotations = getEnclosingMethod().getMemberView().getAnnotationTypes();
    for (int i = 0; i < annotations.length; i++) {
      ResolvedType ann = annotations[i];
      Kind k = (getEnclosingMethod().getMemberView().getKind() == Member.CONSTRUCTOR ? Shadow.ConstructorExecution
          : Shadow.MethodExecution);
      withincodeAnnotationVars.put(ann,
          new AnnotationAccessVar(this, k, ann, getEnclosingType(), getEnclosingCodeSignature()));
    }
  }

  // ---- weave methods

  void weaveBefore(BcelAdvice munger) {
    range.insert(munger.getAdviceInstructions(this, null, range.getRealStart()), Range.InsideBefore);
  }

  public void weaveAfter(BcelAdvice munger) {
    weaveAfterThrowing(munger, UnresolvedType.THROWABLE);
    weaveAfterReturning(munger);
  }

  /**
   * The basic strategy here is to add a set of instructions at the end of the shadow range that dispatch the advice, and then
   * return whatever the shadow was going to return anyway.
   *
   * To achieve this, we note all the return statements in the advice, and replace them with code that: 1) stores the return value
   * on top of the stack in a temp var 2) jumps to the start of our advice block 3) restores the return value at the end of the
   * advice block before ultimately returning
   *
   * We also need to bind the return value into a returning parameter, if the advice specified one.
   */
  public void weaveAfterReturning(BcelAdvice munger) {
    List<InstructionHandle> returns = findReturnInstructions();
    boolean hasReturnInstructions = !returns.isEmpty();

    // list of instructions that handle the actual return from the join point
    InstructionList retList = new InstructionList();

    // variable that holds the return value
    BcelVar returnValueVar = null;

    if (hasReturnInstructions) {
      returnValueVar = generateReturnInstructions(returns, retList);
    } else {
      // we need at least one instruction, as the target for jumps
      retList.append(InstructionConstants.NOP);
    }

    // list of instructions for dispatching to the advice itself
    InstructionList advice = getAfterReturningAdviceDispatchInstructions(munger, retList.getStart());

    if (hasReturnInstructions) {
      InstructionHandle gotoTarget = advice.getStart();
      for (Iterator<InstructionHandle> i = returns.iterator(); i.hasNext();) {
        InstructionHandle ih = i.next();
        retargetReturnInstruction(munger.hasExtraParameter(), returnValueVar, gotoTarget, ih);
      }
    }

    range.append(advice);
    range.append(retList);
  }

  /**
   * @return a list of all the return instructions in the range of this shadow
   */
  private List<InstructionHandle> findReturnInstructions() {
    List<InstructionHandle> returns = new ArrayList<InstructionHandle>();
    for (InstructionHandle ih = range.getStart(); ih != range.getEnd(); ih = ih.getNext()) {
      if (ih.getInstruction().isReturnInstruction()) {
        returns.add(ih);
      }
    }
    return returns;
  }

  /**
   * Given a list containing all the return instruction handles for this shadow, finds the last return instruction and copies it,
   * making this the ultimate return. If the shadow has a non-void return type, we also create a temporary variable to hold the
   * return value, and load the value from this var before returning (see pr148007 for why we do this - it works around a JRockit
   * bug, and is also closer to what javac generates)
   *
   * Sometimes the 'last return' isnt the right one - some rogue code can include the real return from the body of a subroutine
   * that exists at the end of the method. In this case the last return is RETURN but that may not be correct for a method with a
   * non-void return type... pr151673
   *
   * @param returns list of all the return instructions in the shadow
   * @param returnInstructions instruction list into which the return instructions should be generated
   * @return the variable holding the return value, if needed
   */
  private BcelVar generateReturnInstructions(List<InstructionHandle> returns, InstructionList returnInstructions) {
    BcelVar returnValueVar = null;
    if (this.hasANonVoidReturnType()) {
      // Find the last *correct* return - this is a method with a non-void return type
      // so ignore RETURN
      Instruction newReturnInstruction = null;
      int i = returns.size() - 1;
      while (newReturnInstruction == null && i >= 0) {
        InstructionHandle ih = returns.get(i);
        if (ih.getInstruction().opcode != Constants.RETURN) {
          newReturnInstruction = Utility.copyInstruction(ih.getInstruction());
        }
        i--;
      }
      returnValueVar = genTempVar(this.getReturnType());
      returnValueVar.appendLoad(returnInstructions, getFactory());
      returnInstructions.append(newReturnInstruction);
    } else {
      InstructionHandle lastReturnHandle = returns.get(returns.size() - 1);
      Instruction newReturnInstruction = Utility.copyInstruction(lastReturnHandle.getInstruction());
      returnInstructions.append(newReturnInstruction);
    }
    return returnValueVar;
  }

  /**
   * @return true, iff this shadow returns a value
   */
  private boolean hasANonVoidReturnType() {
    return this.getReturnType() != ResolvedType.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(ResolvedType.VOID)) {
      tempVar = genTempVar(UnresolvedType.OBJECT);
      advice.append(InstructionConstants.ACONST_NULL);
      tempVar.appendStore(advice, getFactory());
    } else {
      tempVar = genTempVar(tempVarType);
      advice.append(InstructionFactory.createDup(tempVarType.getSize()));
      tempVar.appendStore(advice, getFactory());
    }
    return tempVar;
  }

  /**
   * Helper method for weaveAfterReturning
   *
   * Each return instruction in the method body is retargeted by calling this method. The return instruction is replaced by up to
   * three instructions: 1) if the shadow returns a value, and that value is bound to an after returning parameter, then we DUP
   * the return value on the top of the stack 2) if the shadow returns a value, we store it in the returnValueVar (it will be
   * retrieved from here when we ultimately return after the advice dispatch) 3) if the return was the last instruction, we add a
   * NOP (it will fall through to the advice dispatch), otherwise we add a GOTO that branches to the supplied gotoTarget (start of
   * the advice dispatch)
   */
  private void retargetReturnInstruction(boolean hasReturningParameter, BcelVar returnValueVar, InstructionHandle gotoTarget,
      InstructionHandle returnHandle) {
    // pr148007, work around JRockit bug
    // replace ret with store into returnValueVar, followed by goto if not
    // at the end of the instruction list...
    InstructionList newInstructions = new InstructionList();
    if (returnValueVar != null) {
      if (hasReturningParameter) {
        // we have to dup the return val before consuming it...
        newInstructions.append(InstructionFactory.createDup(this.getReturnType().getSize()));
      }
      // store the return value into this var
      returnValueVar.appendStore(newInstructions, getFactory());
    }
    if (!isLastInstructionInRange(returnHandle, range)) {
      newInstructions.append(InstructionFactory.createBranchInstruction(Constants.GOTO, gotoTarget));
    }
    if (newInstructions.isEmpty()) {
      newInstructions.append(InstructionConstants.NOP);
    }
    Utility.replaceInstruction(returnHandle, newInstructions, enclosingMethod);
  }

  private boolean isLastInstructionInRange(InstructionHandle ih, ShadowRange aRange) {
    return ih.getNext() == aRange.getEnd();
  }

  public void weaveAfterThrowing(BcelAdvice munger, UnresolvedType catchType) {
    // a good optimization would be not to generate anything here
    // if the shadow is GUARANTEED empty (i.e., there's NOTHING, not even
    // a shadow, inside me).
    if (getRange().getStart().getNext() == getRange().getEnd()) {
      return;
    }
    InstructionFactory fact = getFactory();
    InstructionList handler = new InstructionList();
    BcelVar exceptionVar = genTempVar(catchType);
    exceptionVar.appendStore(handler, fact);

    // pr62642
    // I will now jump through some firey BCEL hoops to generate a trivial bit of code:
    // if (exc instanceof ExceptionInInitializerError)
    // throw (ExceptionInInitializerError)exc;
    if (this.getEnclosingMethod().getName().equals("<clinit>")) {
      ResolvedType eiieType = world.resolve("java.lang.ExceptionInInitializerError");
      ObjectType eiieBcelType = (ObjectType) BcelWorld.makeBcelType(eiieType);
      InstructionList ih = new InstructionList(InstructionConstants.NOP);
      handler.append(exceptionVar.createLoad(fact));
      handler.append(fact.createInstanceOf(eiieBcelType));
      InstructionBranch bi = InstructionFactory.createBranchInstruction(Constants.IFEQ, ih.getStart());
      handler.append(bi);
      handler.append(exceptionVar.createLoad(fact));
      handler.append(fact.createCheckCast(eiieBcelType));
      handler.append(InstructionConstants.ATHROW);
      handler.append(ih);
    }

    InstructionList endHandler = new InstructionList(exceptionVar.createLoad(fact));
    handler.append(munger.getAdviceInstructions(this, exceptionVar, endHandler.getStart()));
    handler.append(endHandler);
    handler.append(InstructionConstants.ATHROW);
    InstructionHandle handlerStart = handler.getStart();

    if (isFallsThrough()) {
      InstructionHandle jumpTarget = handler.append(InstructionConstants.NOP);
      handler.insert(InstructionFactory.createBranchInstruction(Constants.GOTO, jumpTarget));
    }
    InstructionHandle protectedEnd = handler.getStart();
    range.insert(handler, Range.InsideAfter);

    enclosingMethod.addExceptionHandler(range.getStart().getNext(), protectedEnd.getPrev(), handlerStart,
        (ObjectType) BcelWorld.makeBcelType(catchType), // ???Type.THROWABLE,
        // high priority if our args are on the stack
        getKind().hasHighPriorityExceptions());
  }

  // ??? this shares a lot of code with the above weaveAfterThrowing
  // ??? would be nice to abstract that to say things only once
  public void weaveSoftener(BcelAdvice munger, UnresolvedType catchType) {
    // a good optimization would be not to generate anything here
    // if the shadow is GUARANTEED empty (i.e., there's NOTHING, not even
    // a shadow, inside me).
    if (getRange().getStart().getNext() == getRange().getEnd()) {
      return;
    }

    InstructionFactory fact = getFactory();
    InstructionList handler = new InstructionList();
    InstructionList rtExHandler = new InstructionList();
    BcelVar exceptionVar = genTempVar(catchType);

    handler.append(fact.createNew(NameMangler.SOFT_EXCEPTION_TYPE));
    handler.append(InstructionFactory.createDup(1));
    handler.append(exceptionVar.createLoad(fact));
    handler.append(fact.createInvoke(NameMangler.SOFT_EXCEPTION_TYPE, "<init>", Type.VOID, new Type[] { Type.THROWABLE },
        Constants.INVOKESPECIAL)); // ??? special
    handler.append(InstructionConstants.ATHROW);

    // ENH 42737
    exceptionVar.appendStore(rtExHandler, fact);
    // aload_1
    rtExHandler.append(exceptionVar.createLoad(fact));
    // instanceof class java/lang/RuntimeException
    rtExHandler.append(fact.createInstanceOf(new ObjectType("java.lang.RuntimeException")));
    // ifeq go to new SOFT_EXCEPTION_TYPE instruction
    rtExHandler.append(InstructionFactory.createBranchInstruction(Constants.IFEQ, handler.getStart()));
    // aload_1
    rtExHandler.append(exceptionVar.createLoad(fact));
    // athrow
    rtExHandler.append(InstructionFactory.ATHROW);

    InstructionHandle handlerStart = rtExHandler.getStart();

    if (isFallsThrough()) {
      InstructionHandle jumpTarget = range.getEnd();// handler.append(fact.NOP);
      rtExHandler.insert(InstructionFactory.createBranchInstruction(Constants.GOTO, jumpTarget));
    }

    rtExHandler.append(handler);

    InstructionHandle protectedEnd = rtExHandler.getStart();
    range.insert(rtExHandler, Range.InsideAfter);

    enclosingMethod.addExceptionHandler(range.getStart().getNext(), protectedEnd.getPrev(), handlerStart,
        (ObjectType) BcelWorld.makeBcelType(catchType),
        // high priority if our args are on the stack
        getKind().hasHighPriorityExceptions());
  }

  public void weavePerObjectEntry(final BcelAdvice munger, final BcelVar onVar) {
    final InstructionFactory fact = getFactory();

    InstructionList entryInstructions = new InstructionList();
    InstructionList entrySuccessInstructions = new InstructionList();
    onVar.appendLoad(entrySuccessInstructions, fact);

    entrySuccessInstructions
        .append(Utility.createInvoke(fact, world, AjcMemberMaker.perObjectBind(munger.getConcreteAspect())));

    InstructionList testInstructions = munger.getTestInstructions(this, entrySuccessInstructions.getStart(), range
        .getRealStart(), entrySuccessInstructions.getStart());

    entryInstructions.append(testInstructions);
    entryInstructions.append(entrySuccessInstructions);

    range.insert(entryInstructions, Range.InsideBefore);
  }

  // PTWIMPL Create static initializer to call the aspect factory
  /**
   * Causes the aspect instance to be *set* for later retrievable through localAspectof()/aspectOf()
   */
  public void weavePerTypeWithinAspectInitialization(final BcelAdvice munger, UnresolvedType t) {

    if (t.resolve(world).isInterface()) {
      return; // Don't initialize statics in
    }
    final InstructionFactory fact = getFactory();

    InstructionList entryInstructions = new InstructionList();
    InstructionList entrySuccessInstructions = new InstructionList();

    BcelWorld.getBcelObjectType(munger.getConcreteAspect());
    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;

    final Type objectArrayType = new ArrayType(Type.OBJECT, 1);
    final InstructionFactory fact = getFactory();

    final BcelVar testResult = genTempVar(ResolvedType.BOOLEAN);

    InstructionList entryInstructions = new InstructionList();
    {
      InstructionList entrySuccessInstructions = new InstructionList();

      if (munger.hasDynamicTests()) {
        entryInstructions.append(Utility.createConstant(fact, 0));
        testResult.appendStore(entryInstructions, fact);

        entrySuccessInstructions.append(Utility.createConstant(fact, 1));
        testResult.appendStore(entrySuccessInstructions, fact);
      }

      if (isPer) {
        entrySuccessInstructions.append(fact.createInvoke(munger.getConcreteAspect().getName(),
            NameMangler.PERCFLOW_PUSH_METHOD, Type.VOID, new Type[] {}, Constants.INVOKESTATIC));
      } else {
        BcelVar[] cflowStateVars = munger.getExposedStateAsBcelVars(false);

        if (cflowStateVars.length == 0) {
          // This should be getting managed by a counter - lets make sure.
          if (!cflowField.getType().getName().endsWith("CFlowCounter")) {
            throw new RuntimeException("Incorrectly attempting counter operation on stacked cflow");
          }
          entrySuccessInstructions.append(Utility.createGet(fact, cflowField));
          // arrayVar.appendLoad(entrySuccessInstructions, fact);
          entrySuccessInstructions.append(fact.createInvoke(NameMangler.CFLOW_COUNTER_TYPE, "inc", Type.VOID,
              new Type[] {}, Constants.INVOKEVIRTUAL));
        } else {
          BcelVar arrayVar = genTempVar(UnresolvedType.OBJECTARRAY);

          int alen = cflowStateVars.length;
          entrySuccessInstructions.append(Utility.createConstant(fact, alen));
          entrySuccessInstructions.append(fact.createNewArray(Type.OBJECT, (short) 1));
          arrayVar.appendStore(entrySuccessInstructions, fact);

          for (int i = 0; i < alen; i++) {
            arrayVar.appendConvertableArrayStore(entrySuccessInstructions, fact, i, cflowStateVars[i]);
          }

          entrySuccessInstructions.append(Utility.createGet(fact, cflowField));
          arrayVar.appendLoad(entrySuccessInstructions, fact);

          entrySuccessInstructions.append(fact.createInvoke(NameMangler.CFLOW_STACK_TYPE, "push", Type.VOID,
              new Type[] { objectArrayType }, Constants.INVOKEVIRTUAL));
        }
      }

      InstructionList testInstructions = munger.getTestInstructions(this, entrySuccessInstructions.getStart(), range
          .getRealStart(), entrySuccessInstructions.getStart());
      entryInstructions.append(testInstructions);
      entryInstructions.append(entrySuccessInstructions);
    }

    // this is the same for both per and non-per
    weaveAfter(new BcelAdvice(null, null, null, 0, 0, 0, null, munger.getConcreteAspect()) {
      @Override
      public InstructionList getAdviceInstructions(BcelShadow s, BcelVar extraArgVar, InstructionHandle ifNoAdvice) {
        InstructionList exitInstructions = new InstructionList();
        if (munger.hasDynamicTests()) {
          testResult.appendLoad(exitInstructions, fact);
          exitInstructions.append(InstructionFactory.createBranchInstruction(Constants.IFEQ, ifNoAdvice));
        }
        exitInstructions.append(Utility.createGet(fact, cflowField));
        if (munger.getKind() != AdviceKind.PerCflowEntry && munger.getKind() != AdviceKind.PerCflowBelowEntry
            && munger.getExposedStateAsBcelVars(false).length == 0) {
          exitInstructions.append(fact.createInvoke(NameMangler.CFLOW_COUNTER_TYPE, "dec", Type.VOID, new Type[] {},
              Constants.INVOKEVIRTUAL));
        } else {
          exitInstructions.append(fact.createInvoke(NameMangler.CFLOW_STACK_TYPE, "pop", Type.VOID, new Type[] {},
              Constants.INVOKEVIRTUAL));
        }
        return exitInstructions;
      }
    });

    range.insert(entryInstructions, Range.InsideBefore);
  }

  /*
   * Implementation notes:
   *
   * AroundInline still extracts the instructions of the original shadow into an extracted method. This allows inlining of even
   * that advice that doesn't call proceed or calls proceed more than once.
   *
   * It extracts the instructions of the original shadow into a method.
   *
   * Then it extracts the instructions of the advice into a new method defined on this enclosing class. This new method can then
   * be specialized as below.
   *
   * Then it searches in the instructions of the advice for any call to the proceed method.
   *
   * At such a call, there is stuff on the stack representing the arguments to proceed. Pop these into the frame.
   *
   * Now build the stack for the call to the extracted method, taking values either from the join point state or from the new
   * frame locs from proceed. Now call the extracted method. The right return value should be on the stack, so no cast is
   * necessary.
   *
   * If only one call to proceed is made, we can re-inline the original shadow. We are not doing that presently.
   *
   * If the body of the advice can be determined to not alter the stack, or if this shadow doesn't care about the stack, i.e.
   * method-execution, then the new method for the advice can also be re-lined. We are not doing that presently.
   */
  public void weaveAroundInline(BcelAdvice munger, boolean hasDynamicTest) {
    // !!! THIS BLOCK OF CODE SHOULD BE IN A METHOD CALLED weaveAround(...);
    Member mungerSig = munger.getSignature();
    // Member originalSig = mungerSig; // If mungerSig is on a parameterized type, originalSig is the member on the generic type
    if (mungerSig instanceof ResolvedMember) {
      ResolvedMember rm = (ResolvedMember) mungerSig;
      if (rm.hasBackingGenericMember()) {
        mungerSig = rm.getBackingGenericMember();
      }
    }
    ResolvedType declaringAspectType = world.resolve(mungerSig.getDeclaringType(), true);
    if (declaringAspectType.isMissing()) {
      world.getLint().cantFindType.signal(new String[] { WeaverMessages.format(
          WeaverMessages.CANT_FIND_TYPE_DURING_AROUND_WEAVE, declaringAspectType.getClassName()) }, getSourceLocation(),
          new ISourceLocation[] { munger.getSourceLocation() });
    }

    // ??? might want some checks here to give better errors
    ResolvedType rt = (declaringAspectType.isParameterizedType() ? declaringAspectType.getGenericType() : declaringAspectType);
    BcelObjectType ot = BcelWorld.getBcelObjectType(rt);
    LazyMethodGen adviceMethod = ot.getLazyClassGen().getLazyMethodGen(mungerSig);
    if (!adviceMethod.getCanInline()) {
      weaveAroundClosure(munger, hasDynamicTest);
      return;
    }

    // specific test for @AJ proceedInInners
    if (isAnnotationStylePassingProceedingJoinPointOutOfAdvice(munger, hasDynamicTest, adviceMethod)) {
      return;
    }

    // We can't inline around methods if they have around advice on them, this
    // is because the weaving will extract the body and hence the proceed call.

    // TODO should consider optimizations to recognize simple cases that don't require body extraction

    enclosingMethod.setCanInline(false);

    LazyClassGen shadowClass = getEnclosingClass();

    // Extract the shadow into a new method. For example:
    // "private static final void method_aroundBody0(M, M, String, org.aspectj.lang.JoinPoint)"
    // Parameters are: this if there is one, target if there is one and its different to this, then original arguments
    // at the shadow, then tjp
    String extractedShadowMethodName = NameMangler.aroundShadowMethodName(getSignature(), shadowClass.getNewGeneratedNameTag());
    List<String> parameterNames = new ArrayList<String>();
    LazyMethodGen extractedShadowMethod = extractShadowInstructionsIntoNewMethod(extractedShadowMethodName, Modifier.PRIVATE,
        munger.getSourceLocation(), parameterNames);

    List<BcelVar> argsToCallLocalAdviceMethodWith = new ArrayList<BcelVar>();
    List<BcelVar> proceedVarList = new ArrayList<BcelVar>();
    int extraParamOffset = 0;

    // Create the extra parameters that are needed for passing to proceed
    // This code is very similar to that found in makeCallToCallback and should
    // be rationalized in the future

    if (thisVar != null) {
      argsToCallLocalAdviceMethodWith.add(thisVar);
      proceedVarList.add(new BcelVar(thisVar.getType(), extraParamOffset));
      extraParamOffset += thisVar.getType().getSize();
    }

    if (targetVar != null && targetVar != thisVar) {
      argsToCallLocalAdviceMethodWith.add(targetVar);
      proceedVarList.add(new BcelVar(targetVar.getType(), extraParamOffset));
      extraParamOffset += targetVar.getType().getSize();
    }
    int idx = 0;
    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());
    LazyMethodGen localAdviceMethod = new LazyMethodGen(Modifier.PRIVATE | Modifier.FINAL | Modifier.STATIC, BcelWorld
        .makeBcelType(mungerSig.getReturnType()), localAdviceMethodName, parameterTypes, NoDeclaredExceptions, shadowClass);

    // Doesnt work properly, so leave it out:
    // String aspectFilename = adviceMethod.getEnclosingClass().getInternalFileName();
    // String shadowFilename = shadowClass.getInternalFileName();
    // if (!aspectFilename.equals(shadowFilename)) {
    // localAdviceMethod.fromFilename = aspectFilename;
    // shadowClass.addInlinedSourceFileInfo(aspectFilename, adviceMethod.highestLineNumber);
    // }

    shadowClass.addMethodGen(localAdviceMethod);

    // create a map that will move all slots in advice method forward by extraParamOffset
    // in order to make room for the new proceed-required arguments that are added at
    // the beginning of the parameter list
    int nVars = adviceMethod.getMaxLocals() + extraParamOffset;
    IntMap varMap = IntMap.idMap(nVars);
    for (int i = extraParamOffset; i < nVars; i++) {
      varMap.put(i - extraParamOffset, i);
    }

    final InstructionFactory fact = getFactory();

    localAdviceMethod.getBody().insert(
        BcelClassWeaver.genInlineInstructions(adviceMethod, localAdviceMethod, varMap, fact, true));

    localAdviceMethod.setMaxLocals(nVars);

    // the shadow is now empty. First, create a correct call
    // to the around advice. This includes both the call (which may involve
    // value conversion of the advice arguments) and the return
    // (which may involve value conversion of the return value). Right now
    // we push a null for the unused closure. It's sad, but there it is.

    InstructionList advice = new InstructionList();
    // InstructionHandle adviceMethodInvocation;
    {
      for (Iterator<BcelVar> i = argsToCallLocalAdviceMethodWith.iterator(); i.hasNext();) {
        BcelVar var = i.next();
        var.appendLoad(advice, fact);
      }
      // ??? we don't actually need to push NULL for the closure if we take care
      boolean isAnnoStyleConcreteAspect = munger.getConcreteAspect().isAnnotationStyleAspect();
      boolean isAnnoStyleDeclaringAspect = munger.getDeclaringAspect() != null ? munger.getDeclaringAspect().resolve(world)
          .isAnnotationStyleAspect() : false;

      InstructionList iList = null;
      if (isAnnoStyleConcreteAspect && isAnnoStyleDeclaringAspect) {
        iList = this.loadThisJoinPoint();
        iList.append(Utility.createConversion(getFactory(), LazyClassGen.tjpType, LazyClassGen.proceedingTjpType));
      } else {
        iList = new InstructionList(InstructionConstants.ACONST_NULL);
      }
      advice.append(munger.getAdviceArgSetup(this, null, iList));
      // adviceMethodInvocation =
      advice.append(Utility.createInvoke(fact, localAdviceMethod)); // (fact, getWorld(), munger.getSignature()));
      advice.append(Utility.createConversion(getFactory(), BcelWorld.makeBcelType(mungerSig.getReturnType()),
          extractedShadowMethod.getReturnType(), world.isInJava5Mode()));
      if (!isFallsThrough()) {
        advice.append(InstructionFactory.createReturn(extractedShadowMethod.getReturnType()));
      }
    }

    // now, situate the call inside the possible dynamic tests,
    // and actually add the whole mess to the shadow
    if (!hasDynamicTest) {
      range.append(advice);
    } else {
      InstructionList afterThingie = new InstructionList(InstructionConstants.NOP);
      InstructionList callback = makeCallToCallback(extractedShadowMethod);
      if (terminatesWithReturn()) {
        callback.append(InstructionFactory.createReturn(extractedShadowMethod.getReturnType()));
      } else {
        // InstructionHandle endNop = range.insert(fact.NOP, Range.InsideAfter);
        advice.append(InstructionFactory.createBranchInstruction(Constants.GOTO, afterThingie.getStart()));
      }
      range.append(munger.getTestInstructions(this, advice.getStart(), callback.getStart(), advice.getStart()));
      range.append(advice);
      range.append(callback);
      range.append(afterThingie);
    }

    // now search through the advice, looking for a call to PROCEED.
    // Then we replace the call to proceed with some argument setup, and a
    // call to the extracted method.

    // inlining support for code style aspects
    if (!munger.getDeclaringType().isAnnotationStyleAspect()) {
      String proceedName = NameMangler.proceedMethodName(munger.getSignature().getName());

      InstructionHandle curr = localAdviceMethod.getBody().getStart();
      InstructionHandle end = localAdviceMethod.getBody().getEnd();
      ConstantPool cpg = localAdviceMethod.getEnclosingClass().getConstantPool();
      while (curr != end) {
        InstructionHandle next = curr.getNext();
        Instruction inst = curr.getInstruction();
        if ((inst.opcode == Constants.INVOKESTATIC) && proceedName.equals(((InvokeInstruction) inst).getMethodName(cpg))) {

          localAdviceMethod.getBody().append(curr,
              getRedoneProceedCall(fact, extractedShadowMethod, munger, localAdviceMethod, proceedVarList));
          Utility.deleteInstruction(curr, localAdviceMethod);
        }
        curr = next;
      }
      // and that's it.
    } else {
      // ATAJ inlining support for @AJ aspects
      // [TODO document @AJ code rule: don't manipulate 2 jps proceed at the same time.. in an advice body]
      InstructionHandle curr = localAdviceMethod.getBody().getStart();
      InstructionHandle end = localAdviceMethod.getBody().getEnd();
      ConstantPool cpg = localAdviceMethod.getEnclosingClass().getConstantPool();
      while (curr != end) {
        InstructionHandle next = curr.getNext();
        Instruction inst = curr.getInstruction();
        if ((inst instanceof INVOKEINTERFACE) && "proceed".equals(((INVOKEINTERFACE) inst).getMethodName(cpg))) {
          final boolean isProceedWithArgs;
          if (((INVOKEINTERFACE) inst).getArgumentTypes(cpg).length == 1) {
            // proceed with args as a boxed Object[]
            isProceedWithArgs = true;
          } else {
            isProceedWithArgs = false;
          }
          InstructionList insteadProceedIl = getRedoneProceedCallForAnnotationStyle(fact, extractedShadowMethod, munger,
              localAdviceMethod, proceedVarList, isProceedWithArgs);
          localAdviceMethod.getBody().append(curr, insteadProceedIl);
          Utility.deleteInstruction(curr, localAdviceMethod);
        }
        curr = next;
      }
    }

    // if (parameterNames.size() == 0) {
    // On return we have inserted the advice body into the local advice method. We have remapped all the local variables
    // that were referenced in the advice as we did the copy, and so the local variable table for localAdviceMethod is
    // now lacking any information about all the initial variables.
    InstructionHandle start = localAdviceMethod.getBody().getStart();
    InstructionHandle end = localAdviceMethod.getBody().getEnd();

    // Find the real start and end
    while (start.getInstruction().opcode == Constants.IMPDEP1) {
      start = start.getNext();
    }
    while (end.getInstruction().opcode == Constants.IMPDEP1) {
      end = end.getPrev();
    }
    Type[] args = localAdviceMethod.getArgumentTypes();
    int argNumber = 0;
    for (int slot = 0; slot < extraParamOffset; argNumber++) { // slot will increase by the argument size each time
      String argumentName = null;
      if (argNumber >= args.length || parameterNames.size() == 0 || argNumber >= parameterNames.size()) {
        // this should be unnecessary as I think all known joinpoints and helper methods
        // propagate the parameter names around correctly - but just in case let us do this
        // rather than fail. If a bug is raised reporting unknown as a local variable name
        // then investigate the joinpoint giving rise to the ResolvedMember and why it has
        // no parameter names specified
        argumentName = new StringBuffer("unknown").append(argNumber).toString();
      } else {
        argumentName = parameterNames.get(argNumber);
      }
      String argumentSignature = args[argNumber].getSignature();
      LocalVariableTag lvt = new LocalVariableTag(argumentSignature, argumentName, slot, 0);
      start.addTargeter(lvt);
      end.addTargeter(lvt);
      slot += args[argNumber].getSize();
    }
  }

  /**
   * Check if the advice method passes a pjp parameter out via an invoke instruction - if so we can't risk inlining.
   */
  private boolean isAnnotationStylePassingProceedingJoinPointOutOfAdvice(BcelAdvice munger, boolean hasDynamicTest,
      LazyMethodGen adviceMethod) {
    if (munger.getConcreteAspect().isAnnotationStyleAspect()) {
      // if we can't find one proceed() we suspect that the call
      // is happening in an inner class so we don't inline it.
      // Note: for code style, this is done at Aspect compilation time.
      boolean canSeeProceedPassedToOther = false;
      InstructionHandle curr = adviceMethod.getBody().getStart();
      InstructionHandle end = adviceMethod.getBody().getEnd();
      ConstantPool cpg = adviceMethod.getEnclosingClass().getConstantPool();
      while (curr != end) {
        InstructionHandle next = curr.getNext();
        Instruction inst = curr.getInstruction();
        if ((inst instanceof InvokeInstruction)
            && ((InvokeInstruction) inst).getSignature(cpg).indexOf("Lorg/aspectj/lang/ProceedingJoinPoint;") > 0) {
          // we may want to refine to exclude stuff returning jp ?
          // does code style skip inline if i write dump(thisJoinPoint) ?
          canSeeProceedPassedToOther = true;// we see one pjp passed around - dangerous
          break;
        }
        curr = next;
      }
      if (canSeeProceedPassedToOther) {
        // remember this decision to avoid re-analysis
        adviceMethod.setCanInline(false);
        weaveAroundClosure(munger, hasDynamicTest);
        return true;
      }
    }
    return false;
  }

  private InstructionList getRedoneProceedCall(InstructionFactory fact, LazyMethodGen callbackMethod, BcelAdvice munger,
      LazyMethodGen localAdviceMethod, List<BcelVar> argVarList) {
    InstructionList ret = new InstructionList();
    // we have on stack all the arguments for the ADVICE call.
    // we have in frame somewhere all the arguments for the non-advice call.

    BcelVar[] adviceVars = munger.getExposedStateAsBcelVars(true);
    IntMap proceedMap = makeProceedArgumentMap(adviceVars);

    // System.out.println(proceedMap + " for " + this);
    // System.out.println(argVarList);

    ResolvedType[] proceedParamTypes = world.resolve(munger.getSignature().getParameterTypes());
    // remove this*JoinPoint* as arguments to proceed
    if (munger.getBaseParameterCount() + 1 < proceedParamTypes.length) {
      int len = munger.getBaseParameterCount() + 1;
      ResolvedType[] newTypes = new ResolvedType[len];
      System.arraycopy(proceedParamTypes, 0, newTypes, 0, len);
      proceedParamTypes = newTypes;
    }

    // System.out.println("stateTypes: " + Arrays.asList(stateTypes));
    BcelVar[] proceedVars = Utility.pushAndReturnArrayOfVars(proceedParamTypes, ret, fact, localAdviceMethod);

    Type[] stateTypes = callbackMethod.getArgumentTypes();
    // System.out.println("stateTypes: " + Arrays.asList(stateTypes));

    for (int i = 0, len = stateTypes.length; i < len; i++) {
      Type stateType = stateTypes[i];
      ResolvedType stateTypeX = BcelWorld.fromBcel(stateType).resolve(world);
      if (proceedMap.hasKey(i)) {
        // throw new RuntimeException("unimplemented");
        proceedVars[proceedMap.get(i)].appendLoadAndConvert(ret, fact, stateTypeX);
      } else {
        argVarList.get(i).appendLoad(ret, fact);
      }
    }

    ret.append(Utility.createInvoke(fact, callbackMethod));
    ret.append(Utility.createConversion(fact, callbackMethod.getReturnType(), BcelWorld.makeBcelType(munger.getSignature()
        .getReturnType())));
    return ret;
  }

  // private static boolean bindsThisOrTarget(Pointcut pointcut) {
  // ThisTargetFinder visitor = new ThisTargetFinder();
  // pointcut.accept(visitor, null);
  // return visitor.bindsThisOrTarget;
  // }

  // private static class ThisTargetFinder extends IdentityPointcutVisitor {
  // boolean bindsThisOrTarget = false;
  //
  // public Object visit(ThisOrTargetPointcut node, Object data) {
  // if (node.isBinding()) {
  // bindsThisOrTarget = true;
  // }
  // return node;
  // }
  //
  // public Object visit(AndPointcut node, Object data) {
  // if (!bindsThisOrTarget) node.getLeft().accept(this, data);
  // if (!bindsThisOrTarget) node.getRight().accept(this, data);
  // return node;
  // }
  //
  // public Object visit(NotPointcut node, Object data) {
  // if (!bindsThisOrTarget) node.getNegatedPointcut().accept(this, data);
  // return node;
  // }
  //
  // public Object visit(OrPointcut node, Object data) {
  // if (!bindsThisOrTarget) node.getLeft().accept(this, data);
  // if (!bindsThisOrTarget) node.getRight().accept(this, data);
  // return node;
  // }
  // }

  /**
   * Annotation style handling for inlining.
   *
   * Note: The proceedingjoinpoint is already on the stack (since the user was calling pjp.proceed(...)
   *
   * The proceed map is ignored (in terms of argument repositioning) since we have a fixed expected format for annotation style.
   * The aim here is to change the proceed() call into a call to the xxx_aroundBody0 method.
   *
   *
   */
  private InstructionList getRedoneProceedCallForAnnotationStyle(InstructionFactory fact, LazyMethodGen callbackMethod,
      BcelAdvice munger, LazyMethodGen localAdviceMethod, List<BcelVar> argVarList, boolean isProceedWithArgs) {
    InstructionList ret = new InstructionList();

    // store the Object[] array on stack if proceed with args
    if (isProceedWithArgs) {

      // STORE the Object[] into a local variable
      Type objectArrayType = Type.OBJECT_ARRAY;
      int theObjectArrayLocalNumber = localAdviceMethod.allocateLocal(objectArrayType);
      ret.append(InstructionFactory.createStore(objectArrayType, theObjectArrayLocalNumber));

      // STORE the ProceedingJoinPoint instance into a local variable
      Type proceedingJpType = Type.getType("Lorg/aspectj/lang/ProceedingJoinPoint;");
      int pjpLocalNumber = localAdviceMethod.allocateLocal(proceedingJpType);
      ret.append(InstructionFactory.createStore(proceedingJpType, pjpLocalNumber));

      // Aim here initially is to determine whether the user will have provided a new
      // this/target in the object array and consume them if they have, leaving us the rest of
      // the arguments to process as regular arguments to the invocation at the original join point

      boolean pointcutBindsThis = bindsThis(munger);
      boolean pointcutBindsTarget = bindsTarget(munger);
      boolean targetIsSameAsThis = getKind().isTargetSameAsThis();

      int nextArgumentToProvideForCallback = 0;

      if (hasThis()) {
        if (!(pointcutBindsTarget && targetIsSameAsThis)) {
          if (pointcutBindsThis) {
            // they have supplied new this as first entry in object array, consume it
            ret.append(InstructionFactory.createLoad(objectArrayType, theObjectArrayLocalNumber));
            ret.append(Utility.createConstant(fact, 0));
            ret.append(InstructionFactory.createArrayLoad(Type.OBJECT));
            ret.append(Utility.createConversion(fact, Type.OBJECT, callbackMethod.getArgumentTypes()[0]));
          } else {
            // use local variable 0
            ret.append(InstructionFactory.createALOAD(0));
          }
          nextArgumentToProvideForCallback++;
        }
      }

      if (hasTarget()) {
        if (pointcutBindsTarget) {
          if (getKind().isTargetSameAsThis()) {
            ret.append(InstructionFactory.createLoad(objectArrayType, theObjectArrayLocalNumber));
            ret.append(Utility.createConstant(fact, pointcutBindsThis ? 1 : 0));
            ret.append(InstructionFactory.createArrayLoad(Type.OBJECT));
            ret.append(Utility.createConversion(fact, Type.OBJECT, callbackMethod.getArgumentTypes()[0]));
          } else {
            int position = (hasThis()/* && pointcutBindsThis */? 1 : 0);
            ret.append(InstructionFactory.createLoad(objectArrayType, theObjectArrayLocalNumber));
            ret.append(Utility.createConstant(fact, position));
            ret.append(InstructionFactory.createArrayLoad(Type.OBJECT));
            ret.append(Utility.createConversion(fact, Type.OBJECT, callbackMethod.getArgumentTypes()[position]));
          }
          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())));

    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;
    }
  }

  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!
    LazyMethodGen callbackMethod = extractShadowInstructionsIntoNewMethod(NameMangler.aroundShadowMethodName(getSignature(),
        getEnclosingClass().getNewGeneratedNameTag()), 0, munger.getSourceLocation(), new ArrayList<String>());

    BcelVar[] adviceVars = munger.getExposedStateAsBcelVars(true);

    String closureClassName = NameMangler.makeClosureClassName(getEnclosingClass().getType(), getEnclosingClass()
        .getNewGeneratedNameTag());

    Member constructorSig = new MemberImpl(Member.CONSTRUCTOR, UnresolvedType.forName(closureClassName), 0, "<init>",
        "([Ljava/lang/Object;)V");

    BcelVar closureHolder = null;

    // This is not being used currently since getKind() == preinitializaiton
    // cannot happen in around advice
    if (getKind() == PreInitialization) {
      closureHolder = genTempVar(AjcMemberMaker.AROUND_CLOSURE_TYPE);
    }

    InstructionList closureInstantiation = makeClosureInstantiation(constructorSig, closureHolder);

    /* LazyMethodGen constructor = */
    makeClosureClassAndReturnConstructor(closureClassName, callbackMethod, makeProceedArgumentMap(adviceVars));

    InstructionList returnConversionCode;
    if (getKind() == PreInitialization) {
      returnConversionCode = new InstructionList();

      BcelVar stateTempVar = genTempVar(UnresolvedType.OBJECTARRAY);
      closureHolder.appendLoad(returnConversionCode, fact);

      returnConversionCode.append(Utility.createInvoke(fact, world, AjcMemberMaker.aroundClosurePreInitializationGetter()));
      stateTempVar.appendStore(returnConversionCode, fact);

      Type[] stateTypes = getSuperConstructorParameterTypes();

      returnConversionCode.append(InstructionConstants.ALOAD_0); // put "this" back on the stack
      for (int i = 0, len = stateTypes.length; i < len; i++) {
        UnresolvedType bcelTX = BcelWorld.fromBcel(stateTypes[i]);
        ResolvedType stateRTX = world.resolve(bcelTX, true);
        if (stateRTX.isMissing()) {
          world.getLint().cantFindType.signal(new String[] { WeaverMessages.format(
              WeaverMessages.CANT_FIND_TYPE_DURING_AROUND_WEAVE_PREINIT, bcelTX.getClassName()) },
              getSourceLocation(), new ISourceLocation[] { munger.getSourceLocation() });
          // IMessage msg = new Message(
          // WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_DURING_AROUND_WEAVE_PREINIT,bcelTX.getClassName()),
          // "",IMessage.ERROR,getSourceLocation(),null,
          // new ISourceLocation[]{ munger.getSourceLocation()});
          // world.getMessageHandler().handleMessage(msg);
        }
        stateTempVar.appendConvertableArrayLoad(returnConversionCode, fact, i, stateRTX);
      }
    } else {
      // pr226201
      Member mungerSignature = munger.getSignature();
      if (munger.getSignature() instanceof ResolvedMember) {
        if (((ResolvedMember) mungerSignature).hasBackingGenericMember()) {
          mungerSignature = ((ResolvedMember) mungerSignature).getBackingGenericMember();
        }
      }
      UnresolvedType returnType = mungerSignature.getReturnType();
      returnConversionCode = Utility.createConversion(getFactory(), BcelWorld.makeBcelType(returnType), callbackMethod
          .getReturnType(), world.isInJava5Mode());
      if (!isFallsThrough()) {
        returnConversionCode.append(InstructionFactory.createReturn(callbackMethod.getReturnType()));
      }
    }

    // initialize the bit flags for this shadow
    int bitflags = 0x000000;
    if (getKind().isTargetSameAsThis()) {
      bitflags |= 0x010000;
    }
    if (hasThis()) {
      bitflags |= 0x001000;
    }
    if (bindsThis(munger)) {
      bitflags |= 0x000100;
    }
    if (hasTarget()) {
      bitflags |= 0x000010;
    }
    if (bindsTarget(munger)) {
      bitflags |= 0x000001;
    }

    // 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()) {
      // stick the bitflags on the stack and call the variant of linkClosureAndJoinPoint that takes an int
      closureInstantiation.append(fact.createConstant(Integer.valueOf(bitflags)));
      closureInstantiation.append(Utility.createInvoke(getFactory(), getWorld(), new MemberImpl(Member.METHOD, UnresolvedType
          .forName("org.aspectj.runtime.internal.AroundClosure"), Modifier.PUBLIC, "linkClosureAndJoinPoint",
          "(I)Lorg/aspectj/lang/ProceedingJoinPoint;")));
    }

    InstructionList advice = new InstructionList();
    advice.append(munger.getAdviceArgSetup(this, null, closureInstantiation));

    // invoke the advice
    advice.append(munger.getNonTestAdviceInstructions(this));
    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());
    InstructionFactory fact = new InstructionFactory(closureClass.getConstantPool());

    // constructor
    LazyMethodGen constructor = new LazyMethodGen(Modifier.PUBLIC, Type.VOID, "<init>", new Type[] { objectArrayType },
        new String[] {}, closureClass);
    InstructionList cbody = constructor.getBody();
    cbody.append(InstructionFactory.createLoad(Type.OBJECT, 0));
    cbody.append(InstructionFactory.createLoad(objectArrayType, 1));
    cbody.append(fact
        .createInvoke(superClassName, "<init>", Type.VOID, new Type[] { objectArrayType }, Constants.INVOKESPECIAL));
    cbody.append(InstructionFactory.createReturn(Type.VOID));

    closureClass.addMethodGen(constructor);

    // 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++) {
      Type stateType = stateTypes[i];
      ResolvedType stateTypeX = BcelWorld.fromBcel(stateType).resolve(world);
      if (proceedMap.hasKey(i)) {
        mbody.append(proceedVar.createConvertableArrayLoad(fact, proceedMap.get(i), stateTypeX));
      } else {
        mbody.append(stateVar.createConvertableArrayLoad(fact, i, stateTypeX));
      }
    }

    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
   */
  LazyMethodGen extractShadowInstructionsIntoNewMethod(String extractedMethodName, int extractedMethodVisibilityModifier,
      ISourceLocation adviceSourceLocation, List<String> parameterNames) {
    // 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);
    IntMap remapper = makeRemap();
    range.extractInstructionsInto(newMethod, remapper, (getKind() != PreInitialization) && isFallsThrough());
    if (getKind() == PreInitialization) {
      addPreInitializationReturnCode(newMethod, getSuperConstructorParameterTypes());
    }
    getEnclosingClass().addMethodGen(newMethod, adviceSourceLocation);
    return newMethod;
  }

  private void addPreInitializationReturnCode(LazyMethodGen extractedMethod, Type[] superConstructorTypes) {
    InstructionList body = extractedMethod.getBody();
    final InstructionFactory fact = getFactory();

    BcelVar arrayVar = new BcelVar(world.getCoreType(UnresolvedType.OBJECTARRAY), extractedMethod.allocateLocal(1));

    int len = superConstructorTypes.length;

    body.append(Utility.createConstant(fact, len));

    body.append(fact.createNewArray(Type.OBJECT, (short) 1));
    arrayVar.appendStore(body, fact);

    for (int i = len - 1; i >= 0; i++) {
      // convert thing on top of stack to object
      body.append(Utility.createConversion(fact, superConstructorTypes[i], Type.OBJECT));
      // push object array
      arrayVar.appendLoad(body, fact);
      // swap
      body.append(InstructionConstants.SWAP);
      // do object array store.
      body.append(Utility.createConstant(fact, i));
      body.append(InstructionConstants.SWAP);
      body.append(InstructionFactory.createArrayStore(Type.OBJECT));
    }
    arrayVar.appendLoad(body, fact);
    body.append(InstructionConstants.ARETURN);
  }

  private Type[] getSuperConstructorParameterTypes() {
    // assert getKind() == PreInitialization
    InstructionHandle superCallHandle = getRange().getEnd().getNext();
    InvokeInstruction superCallInstruction = (InvokeInstruction) superCallHandle.getInstruction();
    return superCallInstruction.getArgumentTypes(getEnclosingClass().getConstantPool());
  }

  /**
   * make a map from old frame location to new frame location. Any unkeyed frame location picks out a copied local
   */
  private IntMap makeRemap() {
    IntMap ret = new IntMap(5);
    int reti = 0;
    if (thisVar != null) {
      ret.put(0, reti++); // thisVar guaranteed to be 0
    }
    if (targetVar != null && targetVar != thisVar) {
      ret.put(targetVar.getSlot(), reti++);
    }
    for (int i = 0, len = argVars.length; i < len; i++) {
      ret.put(argVars[i].getSlot(), reti);
      reti += argVars[i].getType().getSize();
    }
    if (thisJoinPointVar != null) {
      ret.put(thisJoinPointVar.getSlot(), reti++);
    }
    // we not only need to put the arguments, we also need to remap their
    // aliases, which we so helpfully put into temps at the beginning of this join
    // point.
    if (!getKind().argsOnStack()) {
      int oldi = 0;
      int newi = 0;
      // if we're passing in a this and we're not argsOnStack we're always
      // passing in a target too
      if (arg0HoldsThis()) {
        ret.put(0, 0);
        oldi++;
        newi += 1;
      }
      // assert targetVar == thisVar
      for (int i = 0; i < getArgCount(); i++) {
        UnresolvedType type = getArgType(i);
        ret.put(oldi, newi);
        oldi += type.getSize();
        newi += type.getSize();
      }
    }

    // System.err.println("making remap for : " + this);
    // if (targetVar != null) System.err.println("target slot : " + targetVar.getSlot());
    // if (thisVar != null) System.err.println("  this slot : " + thisVar.getSlot());
    // System.err.println(ret);

    return ret;
  }

  /**
   * The new method always static. It may take some extra arguments: this, target. If it's argsOnStack, then it must take both
   * this/target If it's argsOnFrame, it shares this and target. ??? rewrite this to do less array munging, please
   */
  private LazyMethodGen createShadowMethodGen(String newMethodName, int visibilityModifier, List<String> parameterNames) {
    Type[] shadowParameterTypes = BcelWorld.makeBcelTypes(getArgTypes());
    int modifiers = Modifier.FINAL | 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 = ResolvedType.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 typeX) {
    return new BcelVar(typeX.resolve(world), genTempVarIndex(typeX.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;
  }
}
TOP

Related Classes of org.aspectj.weaver.bcel.BcelShadow

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.