Logo Search packages:      
Sourcecode: aspectj version File versions

BcelShadow.java

/* *******************************************************************
 * 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.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;

      // SECRETAPI - for testing, this will tell us if the optimization succeeded *on the last shadow processed*
      public static boolean appliedLazyTjpOptimization;

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

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

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

      public BcelShadow copyInto(LazyMethodGen recipient, BcelShadow enclosing) {
            BcelShadow s = new BcelShadow(world, getKind(), getSignature(), recipient, enclosing);
            if (mungers.size() > 0) {
                  List src = mungers;
                  if (s.mungers == Collections.EMPTY_LIST)
                        s.mungers = new ArrayList();
                  List dest = s.mungers;

                  for (Iterator i = src.iterator(); i.hasNext();) {
                        dest.add(i.next());
                  }
            }
            return s;
      }

      // ---- overridden behaviour

      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) {
            InstructionTargeter[] sources = old.getTargetersArray();
            if (sources != null) {
                  for (int i = sources.length - 1; i >= 0; i--) {
                        if (sources[i] instanceof ExceptionRange) {
                              ExceptionRange it = (ExceptionRange) sources[i];
                              it.updateTarget(old, fresh, it.getBody());
                        } else {
                              sources[i].updateTarget(old, fresh);
                        }
                  }
            }
      }

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

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

      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
                  InstructionTargeter[] targeters = start.getTargetersArray();
                  for (int i = 0; i < targeters.length; i++) {
                        InstructionTargeter t = targeters[i];
                        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 (Iterator iter = mungers.iterator(); iter.hasNext();) {
                  ShadowMunger munger = (ShadowMunger) iter.next();
                  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 iter = badAdvice.iterator(); iter.hasNext();) {
                        BcelAdvice element = (BcelAdvice) 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 iter = badAdvice.iterator(); iter.hasNext();) {
                              BcelAdvice element = (BcelAdvice) 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;
      }

      // overrides
      public UnresolvedType 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) {
            InstructionTargeter[] sources = from.getTargetersArray();
            if (sources != null) {
                  for (int i = sources.length - 1; i >= 0; i--) {
                        InstructionTargeter source = sources[i];
                        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 !getSignature().isStatic();
            } 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> */kindedAnnotationVars = null;
      private Map/* <UnresolvedType,BcelVar> */thisAnnotationVars = null;
      private Map/* <UnresolvedType,BcelVar> */targetAnnotationVars = null;
      private Map/* <UnresolvedType,BcelVar> */[] argAnnotationVars = null;
      private Map/* <UnresolvedType,BcelVar> */withinAnnotationVars = null;
      private Map/* <UnresolvedType,BcelVar> */withincodeAnnotationVars = null;
      private boolean allArgVarsInitialized = false;

      public Var getThisVar() {
            if (!hasThis()) {
                  throw new IllegalStateException("no this");
            }
            initializeThisVar();
            return thisVar;
      }

      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 = (Var) thisAnnotationVars.get(forAnnotationType);
            if (v == null)
                  v = new TypeAnnotationAccessVar(forAnnotationType.resolve(world), (BcelVar) getThisVar());
            return v;
      }

      public Var getTargetVar() {
            if (!hasTarget()) {
                  throw new IllegalStateException("no target");
            }
            initializeTargetVar();
            return targetVar;
      }

      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 = (Var) 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;
      }

      public Var getArgVar(int i) {
            ensureInitializedArgVar(i);
            return argVars[i];
      }

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

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

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

      public Var getWithinCodeAnnotationVar(UnresolvedType forAnnotationType) {
            initializeWithinCodeAnnotationVars();
            return (Var) 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;

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

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

      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
      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();
            // populate..
      }

      public void initializeTargetAnnotationVars() {
            if (targetAnnotationVars != null)
                  return;
            if (getKind().isTargetSameAsThis()) {
                  if (hasThis())
                        initializeThisAnnotationVars();
                  targetAnnotationVars = thisAnnotationVars;
            } else {
                  targetAnnotationVars = new HashMap();
                  ResolvedType[] rtx = this.getTargetType().resolve(world).getAnnotationTypes(); // what about annotations we havent
                  // gotten yet but we will get in
                  // subclasses?
                  for (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 mungers = relevantType.resolve(world).getInterTypeMungers();
            for (Iterator iter = mungers.iterator(); iter.hasNext();) {
                  BcelTypeMunger typeMunger = (BcelTypeMunger) iter.next();
                  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);
                              }
                              // in the above.. what about if it's on an Interface? Can that happen?
                              // then the last arg of the above should be true
                              return foundMember;
                        }
                  }
            }
            return foundMember;
      }

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

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

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

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

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

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

                  if (annotationHolder == null) {
                        // check the ITD'd dooberries
                        List mungers = relevantType.resolve(world).getInterTypeMungers();
                        for (Iterator iter = mungers.iterator(); iter.hasNext();) {
                              BcelTypeMunger typeMunger = (BcelTypeMunger) iter.next();
                              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 rm[] = relevantType.getDeclaredMethods();
                  ResolvedMember foundMember = findMethod2(relevantType.getDeclaredMethods(), getSignature());

                  annotations = getAnnotations(foundMember, shadowSignature, relevantType);
                  annotationHolder = foundMember;
                  annotationHolder = getRelevantMember(foundMember, annotationHolder, relevantType);

            } 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 (int i = 0; i < annotations.length; i++) {
                  ResolvedType annotationType = annotations[i];
                  AnnotationAccessVar accessVar = new AnnotationAccessVar(getKind(), annotationType.resolve(world), relevantType,
                              annotationHolder);
                  kindedAnnotationVars.put(annotationType, accessVar);
            }
      }

      // FIXME asc whats the real diff between this one and the version in findMethod()?
      ResolvedMember findMethod2(ResolvedMember rm[], Member sig) {
            ResolvedMember found = null;
            // String searchString = getSignature().getName()+getSignature().getParameterSignature();
            for (int i = 0; i < rm.length && found == null; i++) {
                  ResolvedMember member = rm[i];
                  if (member.getName().equals(sig.getName()) && member.getParameterSignature().equals(sig.getParameterSignature()))
                        found = member;
            }
            return found;
      }

      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[] 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(k, ann, getEnclosingType(), null));
            }
      }

      public void initializeWithinCodeAnnotationVars() {
            if (withincodeAnnotationVars != null)
                  return;
            withincodeAnnotationVars = new HashMap();

            // For some shadow we are interested in annotations on the method containing that shadow.
            ResolvedType[] annotations = getEnclosingMethod().getMemberView().getAnnotationTypes();
            for (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(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 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 i = returns.iterator(); i.hasNext();) {
                        InstructionHandle ih = (InstructionHandle) 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 findReturnInstructions() {
            List returns = new ArrayList();
            for (InstructionHandle ih = range.getStart(); ih != range.getEnd(); ih = ih.getNext()) {
                  if (ih.getInstruction().isReturnInstruction()) {
                        returns.add(ih);
                  }
            }
            return returns;
      }

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

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

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

            enclosingMethod.setCanInline(false);

            LazyClassGen shadowClass = getEnclosingClass();

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

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

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

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

            if (targetVar != null && targetVar != thisVar) {
                  argsToCallLocalAdviceMethodWith.add(targetVar);
                  proceedVarList.add(new BcelVar(targetVar.getType(), extraParamOffset));
                  extraParamOffset += targetVar.getType().getSize();
            }
            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 i = argsToCallLocalAdviceMethodWith.iterator(); i.hasNext();) {
                        BcelVar var = (BcelVar) i.next();
                        var.appendLoad(advice, fact);
                  }
                  // ??? we don't actually need to push NULL for the closure if we take care
                  advice.append(munger.getAdviceArgSetup(this, null,
                              (munger.getConcreteAspect().isAnnotationStyleAspect() && munger.getDeclaringAspect() != null && munger
                                          .getDeclaringAspect().resolve(world).isAnnotationStyleAspect()) ? this.loadThisJoinPoint()
                                          : new InstructionList(InstructionConstants.ACONST_NULL)));
                  // 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 = (String) 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();
            }
      }

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

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

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

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

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

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

            for (int i = 0, len = stateTypes.length; i < len; i++) {
                  Type stateType = stateTypes[i];
                  ResolvedType stateTypeX = BcelWorld.fromBcel(stateType).resolve(world);
                  if (proceedMap.hasKey(i)) {
                        // throw new RuntimeException("unimplemented");
                        proceedVars[proceedMap.get(i)].appendLoadAndConvert(ret, fact, stateTypeX);
                  } else {
                        ((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 static boolean bindsThisOrTarget(Pointcut pointcut) {
      // ThisTargetFinder visitor = new ThisTargetFinder();
      // pointcut.accept(visitor, null);
      // return visitor.bindsThisOrTarget;
      // }

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

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

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

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

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

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

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

                  int nextArgumentToProvideForCallback = 0;

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

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

            public Object visit(ThisOrTargetPointcut node, Object data) {
                  if (node.isThis() && node.isBinding())
                        usesThis = true;
                  return node;
            }

            public Object visit(AndPointcut node, Object data) {
                  if (!usesThis)
                        node.getLeft().accept(this, data);
                  if (!usesThis)
                        node.getRight().accept(this, data);
                  return node;
            }

            public Object visit(NotPointcut node, Object data) {
                  if (!usesThis)
                        node.getNegatedPointcut().accept(this, data);
                  return node;
            }

            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;

            public Object visit(ThisOrTargetPointcut node, Object data) {
                  if (!node.isThis() && node.isBinding())
                        usesTarget = true;
                  return node;
            }

            public Object visit(AndPointcut node, Object data) {
                  if (!usesTarget)
                        node.getLeft().accept(this, data);
                  if (!usesTarget)
                        node.getRight().accept(this, data);
                  return node;
            }

            public Object visit(NotPointcut node, Object data) {
                  if (!usesTarget)
                        node.getNegatedPointcut().accept(this, data);
                  return node;
            }

            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());

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

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

Generated by  Doxygen 1.6.0   Back to index