content: /*
   *  This file is part of AndroidIDE.
   *
   *  AndroidIDE is free software: you can redistribute it and/or modify
   *  it under the terms of the GNU General Public License as published by
   *  the Free Software Foundation, either version 3 of the License, or
   *  (at your option) any later version.
   *
   *  AndroidIDE is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *  GNU General Public License for more details.
   *
   *  You should have received a copy of the GNU General Public License
   *   along with AndroidIDE.  If not, see <https://www.gnu.org/licenses/>.
   */
  
  /*
   * reserved comment block
   * DO NOT REMOVE OR ALTER!
   */
  /*
   * Copyright 2001-2004 The Apache Software Foundation.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  /*
   * $Id: MethodGenerator.java,v 1.2.4.1 2005/09/05 11:16:47 pvedula Exp $
   */
  
  package jaxp.sun.org.apache.xalan.internal.xsltc.compiler.util;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.HashMap;
   import java.util.Hashtable;
  import java.util.Iterator;
  import java.util.Map;
  import java.util.Stack;
  
  
  import jaxp.sun.org.apache.bcel.internal.classfile.Field;
  import jaxp.sun.org.apache.bcel.internal.classfile.Method;
  import jaxp.sun.org.apache.bcel.internal.generic.ALOAD;
  import jaxp.sun.org.apache.bcel.internal.generic.ASTORE;
  import jaxp.sun.org.apache.bcel.internal.generic.BranchHandle;
  import jaxp.sun.org.apache.bcel.internal.generic.BranchInstruction;
  import jaxp.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  import jaxp.sun.org.apache.bcel.internal.generic.DLOAD;
  import jaxp.sun.org.apache.bcel.internal.generic.DSTORE;
  import jaxp.sun.org.apache.bcel.internal.generic.FLOAD;
  import jaxp.sun.org.apache.bcel.internal.generic.FSTORE;
  import jaxp.sun.org.apache.bcel.internal.generic.GETFIELD;
  import jaxp.sun.org.apache.bcel.internal.generic.GOTO;
  import jaxp.sun.org.apache.bcel.internal.generic.ICONST;
  import jaxp.sun.org.apache.bcel.internal.generic.IfInstruction;
  import jaxp.sun.org.apache.bcel.internal.generic.ILOAD;
  import jaxp.sun.org.apache.bcel.internal.generic.IndexedInstruction;
  import jaxp.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
  import jaxp.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
  import jaxp.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
  import jaxp.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
  import jaxp.sun.org.apache.bcel.internal.generic.ISTORE;
  import jaxp.sun.org.apache.bcel.internal.generic.Instruction;
  import jaxp.sun.org.apache.bcel.internal.generic.InstructionConstants;
  import jaxp.sun.org.apache.bcel.internal.generic.InstructionHandle;
  import jaxp.sun.org.apache.bcel.internal.generic.InstructionList;
  import jaxp.sun.org.apache.bcel.internal.generic.InstructionTargeter;
  import jaxp.sun.org.apache.bcel.internal.generic.LocalVariableGen;
  import jaxp.sun.org.apache.bcel.internal.generic.LocalVariableInstruction;
  import jaxp.sun.org.apache.bcel.internal.generic.LLOAD;
  import jaxp.sun.org.apache.bcel.internal.generic.LSTORE;
  import jaxp.sun.org.apache.bcel.internal.generic.MethodGen;
  import jaxp.sun.org.apache.bcel.internal.generic.NEW;
  import jaxp.sun.org.apache.bcel.internal.generic.PUTFIELD;
  import jaxp.sun.org.apache.bcel.internal.generic.RET;
  import jaxp.sun.org.apache.bcel.internal.generic.Select;
  import jaxp.sun.org.apache.bcel.internal.generic.TargetLostException;
  import jaxp.sun.org.apache.bcel.internal.generic.Type;
  
  import jaxp.sun.org.apache.xalan.internal.xsltc.compiler.Pattern;
  import jaxp.sun.org.apache.xalan.internal.xsltc.compiler.XSLTC;
  
  /**
   * @author Jacek Ambroziak
   * @author Santiago Pericas-Geertsen
   */
  public class MethodGenerator extends MethodGen
      implements jaxp.sun.org.apache.xalan.internal.xsltc.compiler.Constants {
      protected static final int INVALID_INDEX   = -1;
  
      private static final String START_ELEMENT_SIG
          = "(" + STRING_SIG + ")V";
      private static final String END_ELEMENT_SIG
          = START_ELEMENT_SIG;
  
      private InstructionList _mapTypeSub;
  
      private static final int DOM_INDEX       = 1;
      private static final int ITERATOR_INDEX  = 2;
      private static final int HANDLER_INDEX   = 3;
  
      private static final int MAX_METHOD_SIZE = 65535;
      private static final int MAX_BRANCH_TARGET_OFFSET = 32767;
      private static final int MIN_BRANCH_TARGET_OFFSET = -32768;
  
      private static final int TARGET_METHOD_SIZE = 60000;
      private static final int MINIMUM_OUTLINEABLE_CHUNK_SIZE = 1000;
  
      private Instruction _iloadCurrent;
      private Instruction       _istoreCurrent;
      private final Instruction _astoreHandler;
      private final Instruction _aloadHandler;
      private final Instruction _astoreIterator;
      private final Instruction _aloadIterator;
      private final Instruction _aloadDom;
      private final Instruction _astoreDom;
  
      private final Instruction _startElement;
      private final Instruction _endElement;
      private final Instruction _startDocument;
      private final Instruction _endDocument;
      private final Instruction _attribute;
      private final Instruction _uniqueAttribute;
      private final Instruction _namespace;
  
      private final Instruction _setStartNode;
      private final Instruction _reset;
      private final Instruction _nextNode;
  
      private SlotAllocator _slotAllocator;
      private boolean _allocatorInit = false;
      private LocalVariableRegistry _localVariableRegistry;
          /**
                   * A mapping between patterns and instruction lists used by
                   * test sequences to avoid compiling the same pattern multiple
                   * times. Note that patterns whose kernels are "*", "node()"
                   * and "@*" can between shared by test sequences.
                   */
          private Hashtable _preCompiled = new Hashtable();
  
  
      public MethodGenerator(int access_flags, jaxp.sun.org.apache.bcel.internal.generic.Type return_type,
                             jaxp.sun.org.apache.bcel.internal.generic.Type[] arg_types, String[] arg_names,
                             String method_name, String class_name,
                             InstructionList il, ConstantPoolGen cpg) {
          super(access_flags, return_type, arg_types, arg_names, method_name,
                class_name, il, cpg);
  
          _astoreHandler  = new ASTORE(HANDLER_INDEX);
          _aloadHandler   = new ALOAD(HANDLER_INDEX);
          _astoreIterator = new ASTORE(ITERATOR_INDEX);
          _aloadIterator  = new ALOAD(ITERATOR_INDEX);
          _aloadDom       = new ALOAD(DOM_INDEX);
          _astoreDom      = new ASTORE(DOM_INDEX);
  
          final int startElement =
              cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE,
                                        "startElement",
                                        START_ELEMENT_SIG);
          _startElement = new INVOKEINTERFACE(startElement, 2);
  
          final int endElement =
              cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE,
                                        "endElement",
                                        END_ELEMENT_SIG);
          _endElement = new INVOKEINTERFACE(endElement, 2);
  
          final int attribute =
              cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE,
                                        "addAttribute",
                                        "("
                                        + STRING_SIG
                                        + STRING_SIG
                                        + ")V");
          _attribute = new INVOKEINTERFACE(attribute, 3);
  
          final int uniqueAttribute =
              cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE,
                                        "addUniqueAttribute",
                                        "("
                                        + STRING_SIG
                                        + STRING_SIG
                                        + "I)V");
          _uniqueAttribute = new INVOKEINTERFACE(uniqueAttribute, 4);
  
          final int namespace =
              cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE,
                                        "namespaceAfterStartElement",
                                        "("
                                        + STRING_SIG
                                        + STRING_SIG
                                        + ")V");
          _namespace = new INVOKEINTERFACE(namespace, 3);
  
          int index = cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE,
                                                "startDocument",
                                                "()V");
          _startDocument = new INVOKEINTERFACE(index, 1);
  
          index = cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE,
                                            "endDocument",
                                            "()V");
          _endDocument = new INVOKEINTERFACE(index, 1);
  
  
          index = cpg.addInterfaceMethodref(NODE_ITERATOR,
                                            SET_START_NODE,
                                            SET_START_NODE_SIG);
          _setStartNode = new INVOKEINTERFACE(index, 2);
  
          index = cpg.addInterfaceMethodref(NODE_ITERATOR,
                                            "reset", "()"+NODE_ITERATOR_SIG);
          _reset = new INVOKEINTERFACE(index, 1);
  
          index = cpg.addInterfaceMethodref(NODE_ITERATOR, NEXT, NEXT_SIG);
          _nextNode = new INVOKEINTERFACE(index, 1);
  
          _slotAllocator = new SlotAllocator();
          _slotAllocator.initialize(getLocalVariableRegistry().getLocals(false));
          _allocatorInit = true;
      }
  
      /**
       * Allocates a local variable. If the slot allocator has already been
       * initialized, then call addLocalVariable2() so that the new variable
       * is known to the allocator. Failing to do this may cause the allocator
       * to return a slot that is already in use.
       */
      public LocalVariableGen addLocalVariable(String name, jaxp.sun.org.apache.bcel.internal.generic.Type type,
                                               InstructionHandle start,
                                               InstructionHandle end)
      {
          LocalVariableGen lvg;
  
          if (_allocatorInit) {
              lvg = addLocalVariable2(name, type, start);
          } else {
              lvg = super.addLocalVariable(name, type, start, end);
              getLocalVariableRegistry().registerLocalVariable(lvg);
          }
          return lvg;
      }
  
      public LocalVariableGen addLocalVariable2(String name, jaxp.sun.org.apache.bcel.internal.generic.Type type,
                                                InstructionHandle start)
      {
          LocalVariableGen lvg = super.addLocalVariable(name, type,
                                                _slotAllocator.allocateSlot(type),
                                                start, null);
          getLocalVariableRegistry().registerLocalVariable(lvg);
          return lvg;
      }
      private LocalVariableRegistry getLocalVariableRegistry() {
          if (_localVariableRegistry == null) {
              _localVariableRegistry = new LocalVariableRegistry();
          }
  
          return _localVariableRegistry;
      }
  
      /**
       * Keeps track of all local variables used in the method.
       * <p>The
       * {@link MethodGen#addLocalVariable(String, jaxp.sun.org.apache.bcel.internal.generic.Type,InstructionHandle,InstructionHandle)}</code>
       * and
       * {@link MethodGen#addLocalVariable(String, jaxp.sun.org.apache.bcel.internal.generic.Type,int,InstructionHandle,InstructionHandle)}</code>
       * methods of {@link MethodGen} will only keep track of
       * {@link LocalVariableGen} object until it'ss removed by a call to
       * {@link MethodGen#removeLocalVariable(LocalVariableGen)}.</p>
       * <p>In order to support efficient copying of local variables to outlined
       * methods by
       * {@link #outline(InstructionHandle,InstructionHandle,String,ClassGenerator)},
       * this class keeps track of all local variables defined by the method.</p>
       */
      protected class LocalVariableRegistry {
          /**
           * <p>A <code>java.lang.ArrayList</code> of all
           * {@link LocalVariableGen}s created for this method, indexed by the
           * slot number of the local variable.  The JVM stack frame of local
           * variables is divided into "slots".  A single slot can be used to
           * store more than one variable in a method, without regard to type, so
           * long as the byte code keeps the ranges of the two disjoint.</p>
           * <p>If only one registration of use of a particular slot occurs, the
           * corresponding entry of <code>_variables</code> contains the
           * <code>LocalVariableGen</code>; if more than one occurs, the
           * corresponding entry contains all such <code>LocalVariableGen</code>s
           * registered for the same slot; and if none occurs, the entry will be
           * <code>null</code>.
           */
          protected ArrayList _variables = new ArrayList();
  
          /**
           * Maps a name to a {@link LocalVariableGen}
           */
          protected HashMap _nameToLVGMap = new HashMap();
  
          /**
           * Registers a {@link org.apache.bcel.generic.LocalVariableGen}
           * for this method.
           * <p><b>Preconditions:</b>
           * <ul>
           * <li>The range of instructions for <code>lvg</code> does not
           * overlap with the range of instructions for any
           * <code>LocalVariableGen</code> with the same slot index previously
           * registered for this method.  <b><em>(Unchecked.)</em></b></li>
           * </ul></p>
           * @param lvg The variable to be registered
           */
          protected void registerLocalVariable(LocalVariableGen lvg) {
              int slot = lvg.getIndex();
  
              int registrySize = _variables.size();
  
              // If the LocalVariableGen uses a slot index beyond any previously
              // encountered, expand the _variables, padding with intervening null
              // entries as required.
              if (slot >= registrySize) {
                  for (int i = registrySize; i < slot; i++) {
                      _variables.add(null);
                  }
                  _variables.add(lvg);
              } else {
                  // If the LocalVariableGen reuses a slot, make sure the entry
                  // in _variables contains an ArrayList and add the newly
                  // registered LocalVariableGen to the list.  If the entry in
                  // _variables just contains null padding, store the
                  // LocalVariableGen directly.
                  Object localsInSlot = _variables.get(slot);
                  if (localsInSlot != null) {
                      if (localsInSlot instanceof LocalVariableGen) {
                          ArrayList listOfLocalsInSlot = new ArrayList();
                          listOfLocalsInSlot.add(localsInSlot);
                          listOfLocalsInSlot.add(lvg);
                          _variables.set(slot, listOfLocalsInSlot);
                      } else {
                          ((ArrayList) localsInSlot).add(lvg);
                      }
                  } else {
                      _variables.set(slot, lvg);
                  }
              }
  
              registerByName(lvg);
          }
  
          /**
           * <p>Find which {@link LocalVariableGen}, if any, is registered for a
           * particular JVM local stack frame slot at a particular position in the
           * byte code for the method.</p>
           * <p><b>Preconditions:</b>
           * <ul>
           * <li>The {@link InstructionList#setPositions()} has been called for
           * the {@link InstructionList} associated with this
           * {@link MethodGenerator}.</li>
           * </ul></p>
           * @param slot the JVM local stack frame slot number
           * @param offset the position in the byte code
           * @return the <code>LocalVariableGen</code> for the local variable
           * stored in the relevant slot at the relevant offset; <code>null</code>
           * if there is none.
           */
          protected LocalVariableGen lookupRegisteredLocalVariable(int slot,
                                                                   int offset) {
              Object localsInSlot = (_variables != null) ? _variables.get(slot)
                                                         : null;
  
              // If this slot index was never used, _variables.get will return
              // null; if it was used once, it will return the LocalVariableGen;
              // more than once it will return an ArrayList of all the
              // LocalVariableGens for variables stored in that slot.  For each
              // LocalVariableGen, check whether its range includes the
              // specified offset, and return the first such encountered.
              if (localsInSlot != null) {
                  if (localsInSlot instanceof LocalVariableGen) {
                      LocalVariableGen lvg = (LocalVariableGen)localsInSlot;
                      if (offsetInLocalVariableGenRange(lvg, offset)) {
                          return lvg;
                      }
                  } else {
                      ArrayList listOfLocalsInSlot = (ArrayList) localsInSlot;
                      int size = listOfLocalsInSlot.size();
  
                      for (int i = 0; i < size; i++) {
                          LocalVariableGen lvg =
                              (LocalVariableGen)listOfLocalsInSlot.get(i);
                          if (offsetInLocalVariableGenRange(lvg, offset)) {
                              return lvg;
                          }
                      }
                  }
              }
  
              // No local variable stored in the specified slot at the specified
              return null;
          }
  
          /**
           * <p>Set up a mapping of the name of the specified
           * {@link LocalVariableGen} object to the <code>LocalVariableGen</code>
           * itself.</p>
           * <p>This is a bit of a hack.  XSLTC is relying on the fact that the
           * name that is being looked up won't be duplicated, which isn't
           * guaranteed.  It replaces code which used to call
           * {@link MethodGen#getLocalVariables()} and looped through the
           * <code>LocalVariableGen</code> objects it contained to find the one
           * with the specified name.  However, <code>getLocalVariables()</code>
           * has the side effect of setting the start and end for any
           * <code>LocalVariableGen</code> which did not already have them
           * set, which causes problems for outlining..</p>
           * <p>See also {@link #lookUpByName(String)} and
           * {@link #removeByNameTracking(LocalVariableGen)}</P
           * @param lvg a <code>LocalVariableGen</code>
           */
          protected void registerByName(LocalVariableGen lvg) {
              Object duplicateNameEntry = _nameToLVGMap.get(lvg.getName());
  
              if (duplicateNameEntry == null) {
                  _nameToLVGMap.put(lvg.getName(), lvg);
              } else {
                  ArrayList sameNameList;
  
                  if (duplicateNameEntry instanceof ArrayList) {
                      sameNameList = (ArrayList) duplicateNameEntry;
                      sameNameList.add(lvg);
                  } else {
                      sameNameList = new ArrayList();
                      sameNameList.add(duplicateNameEntry);
                      sameNameList.add(lvg);
                  }
  
                  _nameToLVGMap.put(lvg.getName(), sameNameList);
              }
          }
  
          /**
           * Remove the mapping from the name of the specified
           * {@link LocalVariableGen} to itself.
           * See also {@link #registerByName(LocalVariableGen)} and
           * {@link #lookUpByName(String)}
           * @param lvg a <code>LocalVariableGen</code>
           */
          protected void removeByNameTracking(LocalVariableGen lvg) {
              Object duplicateNameEntry = _nameToLVGMap.get(lvg.getName());
  
              if (duplicateNameEntry instanceof ArrayList) {
                  ArrayList sameNameList = (ArrayList) duplicateNameEntry;
                  for (int i = 0; i < sameNameList.size(); i++) {
                      if (sameNameList.get(i) == lvg) {
                          sameNameList.remove(i);
                          break;
                      }
                  }
              } else {
                  _nameToLVGMap.remove(lvg);
              }
          }
  
          /**
           * <p>Given the name of a variable, finds a {@link LocalVariableGen}
           * corresponding to it.</p>
           * <p>See also {@link #registerByName(LocalVariableGen)} and
           * {@link #removeByNameTracking(LocalVariableGen)}</p>
           * @param name
           * @return
           */
          protected LocalVariableGen lookUpByName(String name) {
              LocalVariableGen lvg = null;
              Object duplicateNameEntry = _nameToLVGMap.get(name);
  
              if (duplicateNameEntry instanceof ArrayList) {
                  ArrayList sameNameList = (ArrayList) duplicateNameEntry;
  
                  for (int i = 0; i < sameNameList.size(); i++) {
                      lvg = (LocalVariableGen)sameNameList.get(i);
                      if (lvg.getName() == name) {
                          break;
                      }
                  }
              } else {
                  lvg = (LocalVariableGen) duplicateNameEntry;
              }
  
              return lvg;
          }
  
          /**
           * <p>Gets all {@link LocalVariableGen} objects for this method.</p>
           * <p>When the <code>includeRemoved</code> argument has the value
           * <code>false</code>, this method replaces uses of
           * {@link MethodGen#getLocalVariables()} which has
           * a side-effect of setting the start and end range for any
           * <code>LocalVariableGen</code> if either was <code>null</code>.  That
           * side-effect causes problems for outlining of code in XSLTC.
           * @param includeRemoved Specifies whether all local variables ever
           * declared should be returned (<code>true</code>) or only those not
           * removed (<code>false</code>)
           * @return an array of <code>LocalVariableGen</code> containing all the
           * local variables
           */
          protected LocalVariableGen[] getLocals(boolean includeRemoved) {
              LocalVariableGen[] locals = null;
              ArrayList allVarsEverDeclared = new ArrayList();
  
              if (includeRemoved) {
                  int slotCount = allVarsEverDeclared.size();
  
                  for (int i = 0; i < slotCount; i++) {
                      Object slotEntries = _variables.get(i);
                      if (slotEntries != null) {
                          if (slotEntries instanceof ArrayList) {
                              ArrayList slotList = (ArrayList) slotEntries;
  
                              for (int j = 0; j < slotList.size(); j++) {
                                  allVarsEverDeclared.add(slotList.get(i));
                              }
                          } else {
                              allVarsEverDeclared.add(slotEntries);
                          }
                      }
                  }
              } else {
                  Iterator nameVarsPairsIter = _nameToLVGMap.entrySet().iterator();
  
                  while (nameVarsPairsIter.hasNext()) {
                      Map.Entry nameVarsPair =
                                    (Map.Entry) nameVarsPairsIter.next();
                      Object vars = nameVarsPair.getValue();
                      if (vars != null) {
                          if (vars instanceof ArrayList) {
                              ArrayList varsList = (ArrayList) vars;
                              for (int i = 0; i < varsList.size(); i++) {
                                  allVarsEverDeclared.add(varsList.get(i));
                              }
                          } else {
                              allVarsEverDeclared.add(vars);
                          }
                      }
                  }
              }
  
              locals = new LocalVariableGen[allVarsEverDeclared.size()];
              allVarsEverDeclared.toArray(locals);
  
              return locals;
          }
      }
  
      /**
       * Determines whether a particular variable is in use at a particular offset
       * in the byte code for this method.
       * <p><b>Preconditions:</b>
       * <ul>
       * <li>The {@link InstructionList#setPositions()} has been called for the
       * {@link InstructionList} associated with this {@link MethodGenerator}.
       * </li></ul></p>
       * @param lvg the {@link LocalVariableGen} for the variable
       * @param offset the position in the byte code
       * @return <code>true</code> if and only if the specified variable is in
       * use at the particular byte code offset.
       */
      boolean offsetInLocalVariableGenRange(LocalVariableGen lvg, int offset) {
          InstructionHandle lvgStart = lvg.getStart();
          InstructionHandle lvgEnd = lvg.getEnd();
  
          // If no start handle is recorded for the LocalVariableGen, it is
          // assumed to be in use from the beginning of the method.
          if (lvgStart == null) {
              lvgStart = getInstructionList().getStart();
          }
  
          // If no end handle is recorded for the LocalVariableGen, it is assumed
          // to be in use to the end of the method.
          if (lvgEnd == null) {
              lvgEnd = getInstructionList().getEnd();
          }
  
          // Does the range of the instruction include the specified offset?
          // Note that the InstructionHandle.getPosition method returns the
          // offset of the beginning of an instruction.  A LocalVariableGen's
          // range includes the end instruction itself, so that instruction's
          // length must be taken into consideration in computing whether the
          // varible is in range at a particular offset.
          return ((lvgStart.getPosition() <= offset)
                      && (lvgEnd.getPosition()
                              + lvgEnd.getInstruction().getLength() >= offset));
      }
  
      public void removeLocalVariable(LocalVariableGen lvg) {
          _slotAllocator.releaseSlot(lvg);
          getLocalVariableRegistry().removeByNameTracking(lvg);
          super.removeLocalVariable(lvg);
      }
  
      public Instruction loadDOM() {
          return _aloadDom;
      }
  
      public Instruction storeDOM() {
          return _astoreDom;
      }
  
      public Instruction storeHandler() {
          return _astoreHandler;
      }
  
      public Instruction loadHandler() {
          return _aloadHandler;
      }
  
      public Instruction storeIterator() {
          return _astoreIterator;
      }
  
      public Instruction loadIterator() {
          return _aloadIterator;
      }
  
      public final Instruction setStartNode() {
          return _setStartNode;
      }
  
      public final Instruction reset() {
          return _reset;
      }
  
      public final Instruction nextNode() {
          return _nextNode;
      }
  
      public final Instruction startElement() {
          return _startElement;
      }
  
      public final Instruction endElement() {
          return _endElement;
      }
  
      public final Instruction startDocument() {
          return _startDocument;
      }
  
      public final Instruction endDocument() {
          return _endDocument;
      }
  
      public final Instruction attribute() {
          return _attribute;
      }
  
      public final Instruction uniqueAttribute() {
          return _uniqueAttribute;
      }
  
      public final Instruction namespace() {
          return _namespace;
      }
  
      public Instruction loadCurrentNode() {
          if (_iloadCurrent == null) {
              int idx = getLocalIndex("current");
              if (idx > 0)
                  _iloadCurrent = new ILOAD(idx);
              else
                  _iloadCurrent = new ICONST(0);
          }
          return _iloadCurrent;
      }
  
      public Instruction storeCurrentNode() {
          return _istoreCurrent != null
              ? _istoreCurrent
              : (_istoreCurrent = new ISTORE(getLocalIndex("current")));
      }
  
      /** by default context node is the same as current node. MK437 */
      public Instruction loadContextNode() {
          return loadCurrentNode();
      }
  
      public Instruction storeContextNode() {
          return storeCurrentNode();
      }
  
      public int getLocalIndex(String name) {
          return getLocalVariable(name).getIndex();
      }
  
      public LocalVariableGen getLocalVariable(String name) {
          return getLocalVariableRegistry().lookUpByName(name);
      }
  
      public void setMaxLocals() {
  
          // Get the current number of local variable slots
          int maxLocals = super.getMaxLocals();
          int prevLocals = maxLocals;
  
          // Get numer of actual variables
          final LocalVariableGen[] localVars = super.getLocalVariables();
          if (localVars != null) {
              if (localVars.length > maxLocals)
                  maxLocals = localVars.length;
          }
  
          // We want at least 5 local variable slots (for parameters)
          if (maxLocals < 5) maxLocals = 5;
  
          super.setMaxLocals(maxLocals);
      }
  
      /**
       * Add a pre-compiled pattern to this mode.
       */
      public void addInstructionList(Pattern pattern, InstructionList ilist) {
          _preCompiled.put(pattern, ilist);
      }
  
      /**
       * Get the instruction list for a pre-compiled pattern. Used by
       * test sequences to avoid compiling patterns more than once.
       */
      public InstructionList getInstructionList(Pattern pattern) {
          return (InstructionList) _preCompiled.get(pattern);
      }
  
      /**
       * Used to keep track of an outlineable chunk of instructions in the
       * current method.  See {@link OutlineableChunkStart} and
       * {@link OutlineableChunkEnd} for more information.
       */
      private class Chunk implements Comparable {
          /**
           * {@link InstructionHandle} of the first instruction in the outlineable
           * chunk.
           */
          private InstructionHandle m_start;
  
          /**
           * {@link org.apache.bcel.generic.InstructionHandle} of the first
           * instruction in the outlineable chunk.
           */
          private InstructionHandle m_end;
  
          /**
           * Number of bytes in the instructions contained in this outlineable
           * chunk.
           */
          private int m_size;
  
          /**
           * <p>Constructor for an outlineable {@link MethodGenerator.Chunk}.</p>
           * <p><b>Preconditions:</b>
           * <ul>
           * <li>The {@link InstructionList#setPositions()} has been called for
           * the {@link InstructionList} associated with this
           * {@link MethodGenerator}.</li>
           * </ul></p>
           * @param start The {@link InstructionHandle} of the first
           *              instruction in the outlineable chunk.
           * @param end The {@link InstructionHandle} of the last
           *            instruction in the outlineable chunk.
           */
          Chunk(InstructionHandle start, InstructionHandle end) {
              m_start = start;
              m_end = end;
              m_size = end.getPosition() - start.getPosition();
          }
  
          /**
           * Determines whether this outlineable {@link MethodGenerator.Chunk} is
           * followed immediately by the argument
           * <code>MethodGenerator.Chunk</code>, with no other intervening
           * instructions, including {@link OutlineableChunkStart} or
           * {@link OutlineableChunkEnd} instructions.
           * @param neighbour an outlineable {@link MethodGenerator.Chunk}
           * @return <code>true</code> if and only if the argument chunk
           * immediately follows <code>this</code> chunk
           */
          boolean isAdjacentTo(Chunk neighbour) {
              return getChunkEnd().getNext() == neighbour.getChunkStart();
          }
  
          /**
           * Getter method for the start of this {@linke MethodGenerator.Chunk}
           * @return the {@link org.apache.bcel.generic.InstructionHandle} of the
           * start of this chunk
           */
          InstructionHandle getChunkStart() {
              return m_start;
          }
  
          /**
           * Getter method for the end of this {@link MethodGenerator.Chunk}
           * @return the {@link InstructionHandle} of the start of this chunk
           */
          InstructionHandle getChunkEnd() {
              return m_end;
          }
  
          /**
           * The size of this {@link MethodGenerator.Chunk}
           * @return the number of bytes in the byte code represented by this
           *         chunk.
           */
          int getChunkSize() {
              return m_size;
          }
  
          /**
           * Implements the <code>java.util.Comparable.compareTo(Object)</code>
           * method.
           * @return
           * <ul>
           * <li>A positive <code>int</code> if the length of <code>this</code>
           * chunk in bytes is greater than that of <code>comparand</code></li>
           * <li>A negative <code>int</code> if the length of <code>this</code>
           * chunk in bytes is less than that of <code>comparand</code></li>
           * <li>Zero, otherwise.</li>
           * </ul>
           */
          public int compareTo(Object comparand) {
              return getChunkSize() - ((Chunk)comparand).getChunkSize();
          }
      }
  
      /**
       * Find the outlineable chunks in this method that would be the best choices
       * to outline, based on size and position in the method.
       * @param classGen The {@link ClassGen} with which the generated methods
       *                 will be associated
       * @param totalMethodSize the size of the bytecode in the original method
       * @return a <code>java.util.ArrayList</code> containing the
       *  {@link MethodGenerator.Chunk}s that may be outlined from this method
       */
      private ArrayList getCandidateChunks(ClassGenerator classGen,
                                           int totalMethodSize) {
          Iterator instructions = getInstructionList().iterator();
          ArrayList candidateChunks = new ArrayList();
          ArrayList currLevelChunks = new ArrayList();
          Stack subChunkStack = new Stack();
          boolean openChunkAtCurrLevel = false;
          boolean firstInstruction = true;
  
          InstructionHandle currentHandle;
  
          if (m_openChunks != 0) {
              String msg =
                  (new ErrorMsg(ErrorMsg.OUTLINE_ERR_UNBALANCED_MARKERS))
                      .toString();
              throw new InternalError(msg);
          }
  
          // Scan instructions in the method, keeping track of the nesting level
          // of outlineable chunks.
          //
          // currLevelChunks
          //     keeps track of the child chunks of a chunk.  For each chunk,
          //     there will be a pair of entries:  the InstructionHandles for the
          //     start and for the end of the chunk
          // subChunkStack
          //     a stack containing the partially accumulated currLevelChunks for
          //     each chunk that's still open at the current position in the
          //     InstructionList.
          // candidateChunks
          //     the list of chunks which have been accepted as candidates chunks
          //     for outlining
          do {
              // Get the next instruction.  The loop will perform one extra
              // iteration after it reaches the end of the InstructionList, with
              // currentHandle set to null.
              currentHandle = instructions.hasNext()
                                      ? (InstructionHandle) instructions.next()
                                      : null;
              Instruction inst =
                      (currentHandle != null) ? currentHandle.getInstruction()
                                              : null;
  
              // At the first iteration, create a chunk representing all the
              // code in the method.  This is done just to simplify the logic -
              // this chunk can never be outlined because it will be too big.
              if (firstInstruction) {
                  openChunkAtCurrLevel = true;
                  currLevelChunks.add(currentHandle);
                  firstInstruction = false;
              }
  
              // Found a new chunk
              if (inst instanceof OutlineableChunkStart) {
                  // If last MarkerInstruction encountered was an
                  // OutlineableChunkStart, this represents the first chunk
                  // nested within that previous chunk - push the list of chunks
                  // from the outer level onto the stack
                  if (openChunkAtCurrLevel) {
                      subChunkStack.push(currLevelChunks);
                      currLevelChunks = new ArrayList();
                  }
  
                  openChunkAtCurrLevel = true;
                  currLevelChunks.add(currentHandle);
              // Close off an open chunk
              } else if (currentHandle == null
                             || inst instanceof OutlineableChunkEnd) {
                  ArrayList nestedSubChunks = null;
  
                  // If the last MarkerInstruction encountered was an
                  // OutlineableChunkEnd, it means that the current instruction
                  // marks the end of a chunk that contained child chunks.
                  // Those children might need to be examined below in case they
                  // are better candidates for outlining than the current chunk.
                  if (!openChunkAtCurrLevel) {
                      nestedSubChunks = currLevelChunks;
                      currLevelChunks = (ArrayList)subChunkStack.pop();
                  }
  
                  // Get the handle for the start of this chunk (the last entry
                  // in currLevelChunks)
                  InstructionHandle chunkStart =
                          (InstructionHandle) currLevelChunks.get(
                                                        currLevelChunks.size()-1);
  
                  int chunkEndPosition =
                          (currentHandle != null) ? currentHandle.getPosition()
                                                  : totalMethodSize;
                  int chunkSize = chunkEndPosition - chunkStart.getPosition();
  
                  // Two ranges of chunk size to consider:
                  //
                  // 1. [0,TARGET_METHOD_SIZE]
                  //      Keep this chunk in consideration as a candidate,
                  //      and ignore its subchunks, if any - there's nothing to be
                  //      gained by outlining both the current chunk and its
                  //      children!
                  //
                  // 2. (TARGET_METHOD_SIZE,+infinity)
                  //      Ignore this chunk - it's too big.  Add its subchunks
                  //      as candidates, after merging adjacent chunks to produce
                  //      chunks that are as large as possible
                  if (chunkSize <= TARGET_METHOD_SIZE) {
                      currLevelChunks.add(currentHandle);
                  } else {
                      if (!openChunkAtCurrLevel) {
                          int childChunkCount = nestedSubChunks.size() / 2;
                          if (childChunkCount > 0) {
                              Chunk[] childChunks = new Chunk[childChunkCount];
  
                              // Gather all the child chunks of the current chunk
                              for (int i = 0; i < childChunkCount; i++) {
                                  InstructionHandle start =
                                      (InstructionHandle) nestedSubChunks
                                                              .get(i*2);
                                  InstructionHandle end =
                                      (InstructionHandle) nestedSubChunks
                                                              .get(i*2+1);
  
                                  childChunks[i] = new Chunk(start, end);
                              }
  
                              // Merge adjacent siblings
                              ArrayList mergedChildChunks =
                                          mergeAdjacentChunks(childChunks);
  
                              // Add chunks that mean minimum size requirements
                              // to the list of candidate chunks for outlining
                              for (int i = 0; i < mergedChildChunks.size(); i++) {
                                  Chunk mergedChunk =
                                      (Chunk)mergedChildChunks.get(i);
                                  int mergedSize = mergedChunk.getChunkSize();
  
                                  if (mergedSize >= MINIMUM_OUTLINEABLE_CHUNK_SIZE
                                          && mergedSize <= TARGET_METHOD_SIZE) {
                                      candidateChunks.add(mergedChunk);
                                  }
                              }
                          }
                      }
  
                      // Drop the chunk which was too big
                      currLevelChunks.remove(currLevelChunks.size() - 1);
                  }
  
                  // currLevelChunks contains pairs of InstructionHandles.  If
                  // its size is an odd number, the loop has encountered the
                  // start of a chunk at this level, but not its end.
                  openChunkAtCurrLevel = ((currLevelChunks.size() & 0x1) == 1);
              }
  
          } while (currentHandle != null);
  
          return candidateChunks;
      }
  
      /**
       * Merge adjacent sibling chunks to produce larger candidate chunks for
       * outlining
       * @param chunks array of sibling {@link MethodGenerator.Chunk}s that are
       *               under consideration for outlining.  Chunks must be in
       *               the order encountered in the {@link InstructionList}
       * @return a <code>java.util.ArrayList</code> of
       *         <code>MethodGenerator.Chunk</code>s maximally merged
       */
      private ArrayList mergeAdjacentChunks(Chunk[] chunks) {
          int[] adjacencyRunStart = new int[chunks.length];
          int[] adjacencyRunLength = new int[chunks.length];
          boolean[] chunkWasMerged = new boolean[chunks.length];
  
          int maximumRunOfChunks = 0;
          int startOfCurrentRun;
          int numAdjacentRuns = 0;
  
          ArrayList mergedChunks = new ArrayList();
  
          startOfCurrentRun = 0;
  
          // Loop through chunks, and record in adjacencyRunStart where each
          // run of adjacent chunks begins and how many are in that run.  For
          // example, given chunks A B C D E F, if A is adjacent to B, but not
          // to C, and C, D, E and F are all adjacent,
          //   adjacencyRunStart[0] == 0; adjacencyRunLength[0] == 2
          //   adjacencyRunStart[1] == 2; adjacencyRunLength[1] == 4
          for (int i = 1; i < chunks.length; i++) {
              if (!chunks[i-1].isAdjacentTo(chunks[i])) {
                  int lengthOfRun = i - startOfCurrentRun;
  
                  // Track the longest run of chunks found
                  if (maximumRunOfChunks < lengthOfRun) {
                      maximumRunOfChunks = lengthOfRun;
                  }
  
                  if (lengthOfRun > 1 ) {
                      adjacencyRunLength[numAdjacentRuns] = lengthOfRun;
                      adjacencyRunStart[numAdjacentRuns] = startOfCurrentRun;
                      numAdjacentRuns++;
                  }
  
                  startOfCurrentRun = i;
              }
          }
  
          if (chunks.length - startOfCurrentRun > 1) {
              int lengthOfRun = chunks.length - startOfCurrentRun;
  
              // Track the longest run of chunks found
              if (maximumRunOfChunks < lengthOfRun) {
                  maximumRunOfChunks = lengthOfRun;
              }
  
              adjacencyRunLength[numAdjacentRuns] =
                          chunks.length - startOfCurrentRun;
              adjacencyRunStart[numAdjacentRuns] = startOfCurrentRun;
              numAdjacentRuns++;
          }
  
          // Try merging adjacent chunks to come up with better sized chunks for
          // outlining.  This algorithm is not optimal, but it should be
          // reasonably fast.  Consider an example like this, where four chunks
          // of the sizes specified in brackets are adjacent.  The best way of
          // combining these chunks would be to merge the first pair and merge
          // the last three to form two chunks, but the algorithm will merge the
          // three in the middle instead, leaving three chunks in all.
          //    [25000] [25000] [20000] [1000] [20000]
  
          // Start by trying to merge the maximum number of adjacent chunks, and
          // work down from there.
          for (int numToMerge = maximumRunOfChunks; numToMerge>1; numToMerge--) {
              // Look at each run of adjacent chunks
              for (int run = 0; run < numAdjacentRuns; run++) {
                  int runStart = adjacencyRunStart[run];
                  int runEnd = runStart + adjacencyRunLength[run] - 1;
  
                  boolean foundChunksToMerge = false;
  
                  // Within the current run of adjacent chunks, look at all
                  // "subruns" of length numToMerge, until we run out or find
                  // a subrun that can be merged.
                  for (int mergeStart = runStart;
                       mergeStart+numToMerge-1 <= runEnd && !foundChunksToMerge;
                       mergeStart++) {
                      int mergeEnd = mergeStart + numToMerge - 1;
                      int mergeSize = 0;
  
                      // Find out how big the subrun is
                      for (int j = mergeStart; j <= mergeEnd; j++) {
                          mergeSize = mergeSize + chunks[j].getChunkSize();
                      }
  
                      // If the current subrun is small enough to outline,
                      // merge it, and split the remaining chunks in the run
                      if (mergeSize <= TARGET_METHOD_SIZE) {
                          foundChunksToMerge = true;
  
                          for (int j = mergeStart; j <= mergeEnd; j++) {
                              chunkWasMerged[j] = true;
                          }
  
                          mergedChunks.add(
                                  new Chunk(chunks[mergeStart].getChunkStart(),
                                            chunks[mergeEnd].getChunkEnd()));
  
                          // Adjust the length of the current run of adjacent
                          // chunks to end at the newly merged chunk...
                          adjacencyRunLength[run] =
                                  adjacencyRunStart[run] - mergeStart;
  
                          int trailingRunLength = runEnd - mergeEnd;
  
                          // and any chunks that follow the newly merged chunk
                          // in the current run of adjacent chunks form another
                          // new run of adjacent chunks
                          if (trailingRunLength >= 2) {
                              adjacencyRunStart[numAdjacentRuns] = mergeEnd + 1;
                              adjacencyRunLength[numAdjacentRuns] =
                                                              trailingRunLength;
                              numAdjacentRuns++;
                          }
                      }
                  }
              }
          }
  
          // Make a final pass for any chunk that wasn't merged with a sibling
          // and include it in the list of chunks after merging.
          for (int i = 0; i < chunks.length; i++) {
              if (!chunkWasMerged[i]) {
                  mergedChunks.add(chunks[i]);
              }
          }
  
          return mergedChunks;
      }
  
      /**
       * Breaks up the IL for this {@link MethodGenerator} into separate
       * outlined methods so that no method exceeds the 64KB limit on the length
       * of the byte code associated with a method.
       * @param classGen The {@link ClassGen} with which the generated methods
       *                 will be associated
       * @param originalMethodSize The number of bytes of bytecode represented by
       *                 the {@link InstructionList} of this method
       * @return an array of the outlined <code>Method</code>s and the original
       *         method itself
       */
      public Method[] outlineChunks(ClassGenerator classGen,
                                    int originalMethodSize) {
          ArrayList methodsOutlined = new ArrayList();
          int currentMethodSize = originalMethodSize;
  
          int outlinedCount = 0;
          boolean moreMethodsOutlined;
          String originalMethodName = getName();
  
          // Special handling for initialization methods.  No other methods can
          // include the less than and greater than characters in their names,
          // so we munge the names here.
          if (originalMethodName.equals("<init>")) {
              originalMethodName = "$lt$init$gt$";
          } else if (originalMethodName.equals("<clinit>")) {
              originalMethodName = "$lt$clinit$gt$";
          }
  
          // Loop until the original method comes in under the JVM limit or
          // the loop was unable to outline any more methods
          do {
              // Get all the best candidates for outlining, and sort them in
              // ascending order of size
              ArrayList candidateChunks = getCandidateChunks(classGen,
                                                             currentMethodSize);
              Collections.sort(candidateChunks);
  
              moreMethodsOutlined = false;
  
              // Loop over the candidates for outlining, from the largest to the
              // smallest and outline them one at a time, until the loop has
              // outlined all or the original method comes in under the JVM
              // limit on the size of a method.
              for (int i = candidateChunks.size()-1;
                   i >= 0 && currentMethodSize > TARGET_METHOD_SIZE;
                   i--) {
                  Chunk chunkToOutline = (Chunk)candidateChunks.get(i);
  
                  methodsOutlined.add(outline(chunkToOutline.getChunkStart(),
                                              chunkToOutline.getChunkEnd(),
                                              originalMethodName + "$outline$"
                                                                 + outlinedCount,
                                              classGen));
                  outlinedCount++;
                  moreMethodsOutlined = true;
  
                  InstructionList il = getInstructionList();
                  InstructionHandle lastInst = il.getEnd();
                  il.setPositions();
  
                  // Check the size of the method now
                  currentMethodSize =
                          lastInst.getPosition()
                                  + lastInst.getInstruction().getLength();
              }
          } while (moreMethodsOutlined && currentMethodSize > TARGET_METHOD_SIZE);
  
          // Outlining failed to reduce the size of the current method
          // sufficiently.  Throw an internal error.
          if (currentMethodSize > MAX_METHOD_SIZE) {
              String msg = (new ErrorMsg(ErrorMsg.OUTLINE_ERR_METHOD_TOO_BIG))
                                    .toString();
              throw new InternalError(msg);
          }
  
          Method[] methodsArr = new Method[methodsOutlined.size() + 1];
          methodsOutlined.toArray(methodsArr);
  
          methodsArr[methodsOutlined.size()] = getThisMethod();
  
          return methodsArr;
      }
  
      /**
       * Given an outlineable chunk of code in the current {@link MethodGenerator}
       * move ("outline") the chunk to a new method, and replace the chunk in the
       * old method with a reference to that new method.  No
       * {@link OutlineableChunkStart} or {@link OutlineableChunkEnd} instructions
       * are copied.
       * @param first The {@link InstructionHandle} of the first instruction in
       *              the chunk to outline
       * @param last The <code>InstructionHandle</code> of the last instruction in
       *             the chunk to outline
       * @param outlinedMethodName The name of the new method
       * @param classGen The {@link ClassGenerator} of which the original
       *              and new methods will be members
       * @return The new {@link Method} containing the outlined code.
       */
      private Method outline(InstructionHandle first, InstructionHandle last,
                             String outlinedMethodName, ClassGenerator classGen) {
          // We're not equipped to deal with exception handlers yet.  Bail out!
          if (getExceptionHandlers().length != 0) {
              String msg = (new ErrorMsg(ErrorMsg.OUTLINE_ERR_TRY_CATCH))
                                    .toString();
              throw new InternalError(msg);
          }
  
          int outlineChunkStartOffset = first.getPosition();
          int outlineChunkEndOffset = last.getPosition()
                                          + last.getInstruction().getLength();
  
          ConstantPoolGen cpg = getConstantPool();
  
          // Create new outlined method with signature:
          //
          //   private final outlinedMethodName(CopyLocals copyLocals);
          //
          // CopyLocals is an object that is used to copy-in/copy-out local
          // variables that are used by the outlined method.   Only locals whose
          // value is potentially set or referenced outside the range of the
          // chunk that is being outlined will be represented in CopyLocals.  The
          // type of the variable for copying local variables is actually
          // generated to be unique - it is not named CopyLocals.
          //
          // The outlined method never needs to be referenced outside of this
          // class, and will never be overridden, so we mark it private final.
          final InstructionList newIL = new InstructionList();
  
          final XSLTC xsltc = classGen.getParser().getXSLTC();
          final String argTypeName = xsltc.getHelperClassName();
          final jaxp.sun.org.apache.bcel.internal.generic.Type[] argTypes =
              new jaxp.sun.org.apache.bcel.internal.generic.Type[] {(new ObjectType(argTypeName)).toJCType()};
          final String argName = "copyLocals";
          final String[] argNames = new String[] {argName};
  
          int methodAttributes = ACC_PRIVATE | ACC_FINAL;
          final boolean isStaticMethod = (getAccessFlags() & ACC_STATIC) != 0;
  
          if (isStaticMethod) {
              methodAttributes = methodAttributes | ACC_STATIC;
          }
  
          final MethodGenerator outlinedMethodGen =
              new MethodGenerator(methodAttributes,
                                  jaxp.sun.org.apache.bcel.internal.generic.Type.VOID,
                                  argTypes, argNames, outlinedMethodName,
                                  getClassName(), newIL, cpg);
  
          // Create class for copying local variables to the outlined method.
          // The fields the class will need to contain will be determined as the
          // code in the outlineable chunk is examined.
          ClassGenerator copyAreaCG
              = new ClassGenerator(argTypeName, OBJECT_CLASS, argTypeName+".java",
                                   ACC_FINAL | ACC_PUBLIC | ACC_SUPER, null,
                                   classGen.getStylesheet()) {
                        public boolean isExternal() {
                            return true;
                        }
                    };
          ConstantPoolGen copyAreaCPG = copyAreaCG.getConstantPool();
          copyAreaCG.addEmptyConstructor(ACC_PUBLIC);
  
          // Number of fields in the copy class
          int copyAreaFieldCount = 0;
  
          // The handle for the instruction after the last one to be outlined.
          // Note that this should never end up being null.  An outlineable chunk
          // won't contain a RETURN instruction or other branch out of the chunk,
          // and the JVM specification prohibits code in a method from just
          // "falling off the end" so this should always point to a valid handle.
          InstructionHandle limit = last.getNext();
  
          // InstructionLists for copying values into and out of an instance of
          // CopyLocals:
          //      oldMethCoypInIL  - from locals in old method into an instance
          //                         of the CopyLocals class (oldMethCopyInIL)
          //      oldMethCopyOutIL - from CopyLocals back into locals in the old
          //                         method
          //      newMethCopyInIL  - from CopyLocals into locals in the new
          //                         method
          //      newMethCopyOutIL - from locals in new method into the instance
          //                         of the CopyLocals class
          InstructionList oldMethCopyInIL  = new InstructionList();
          InstructionList oldMethCopyOutIL = new InstructionList();
          InstructionList newMethCopyInIL  = new InstructionList();
          InstructionList newMethCopyOutIL = new InstructionList();
  
          // Allocate instance of class in which we'll copy in or copy out locals
          // and make two copies:  last copy is used to invoke constructor;
          // other two are used for references to fields in the CopyLocals object
          InstructionHandle outlinedMethodCallSetup =
              oldMethCopyInIL.append(new NEW(cpg.addClass(argTypeName)));
          oldMethCopyInIL.append(InstructionConstants.DUP);
          oldMethCopyInIL.append(InstructionConstants.DUP);
          oldMethCopyInIL.append(
              new INVOKESPECIAL(cpg.addMethodref(argTypeName, "<init>", "()V")));
  
          // Generate code to invoke the new outlined method, and place the code
          // on oldMethCopyOutIL
          InstructionHandle outlinedMethodRef;
  
          if (isStaticMethod) {
              outlinedMethodRef =
                  oldMethCopyOutIL.append(
                      new INVOKESTATIC(cpg.addMethodref(
                                            classGen.getClassName(),
                                            outlinedMethodName,
                                            outlinedMethodGen.getSignature())));
          } else {
              oldMethCopyOutIL.append(InstructionConstants.THIS);
              oldMethCopyOutIL.append(InstructionConstants.SWAP);
              outlinedMethodRef =
                  oldMethCopyOutIL.append(
                      new INVOKEVIRTUAL(cpg.addMethodref(
                                            classGen.getClassName(),
                                            outlinedMethodName,
                                            outlinedMethodGen.getSignature())));
          }
  
          // Used to keep track of the first in a sequence of
          // OutlineableChunkStart instructions
          boolean chunkStartTargetMappingsPending = false;
          InstructionHandle pendingTargetMappingHandle = null;
  
          // Used to keep track of the last instruction that was copied
          InstructionHandle lastCopyHandle = null;
  
          // Keeps track of the mapping from instruction handles in the old
          // method to instruction handles in the outlined method.  Only need
          // to track instructions that are targeted by something else in the
          // generated BCEL
          HashMap targetMap   = new HashMap();
  
          // Keeps track of the mapping from local variables in the old method
          // to local variables in the outlined method.
          HashMap localVarMap = new HashMap();
  
          HashMap revisedLocalVarStart = new HashMap();
          HashMap revisedLocalVarEnd = new HashMap();
  
          // Pass 1: Make copies of all instructions, append them to the new list
          // and associate old instruction references with the new ones, i.e.,
          // a 1:1 mapping.  The special marker instructions are not copied.
          // Also, identify local variables whose values need to be copied into or
          // out of the new outlined method, and builds up targetMap and
          // localVarMap as described above.  The code identifies those local
          // variables first so that they can have fixed slots in the stack
          // frame for the outlined method assigned them ahead of all those
          // variables that don't need to exist for the entirety of the outlined
          // method invocation.
          for (InstructionHandle ih = first; ih != limit; ih = ih.getNext()) {
              Instruction inst = ih.getInstruction();
  
              // MarkerInstructions are not copied, so if something else targets
              // one, the targetMap will point to the nearest copied sibling
              // InstructionHandle:  for an OutlineableChunkEnd, the nearest
              // preceding sibling; for an OutlineableChunkStart, the nearest
              // following sibling.
              if (inst instanceof MarkerInstruction) {
                  if (ih.hasTargeters()) {
                      if (inst instanceof OutlineableChunkEnd) {
                          targetMap.put(ih, lastCopyHandle);
                      } else {
                          if (!chunkStartTargetMappingsPending)  {
                              chunkStartTargetMappingsPending = true;
                              pendingTargetMappingHandle = ih;
                          }
                      }
                  }
              } else {
                  // Copy the instruction and append it to the outlined method's
                  // InstructionList.
                  Instruction c = inst.copy(); // Use clone for shallow copy
  
                  if (c instanceof BranchInstruction) {
                      lastCopyHandle = newIL.append((BranchInstruction)c);
                  } else {
                      lastCopyHandle = newIL.append(c);
                  }
  
                  if (c instanceof LocalVariableInstruction
                          || c instanceof RET) {
                      // For any instruction that touches a local variable,
                      // check whether the local variable's value needs to be
                      // copied into or out of the outlined method.  If so,
                      // generate the code to perform the necessary copying, and
                      // use localVarMap to map the variable in the original
                      // method to the variable in the new method.
                      IndexedInstruction lvi = (IndexedInstruction)c;
                      int oldLocalVarIndex = lvi.getIndex();
                      LocalVariableGen oldLVG =
                              getLocalVariableRegistry()
                                  .lookupRegisteredLocalVariable(oldLocalVarIndex,
                                                                ih.getPosition());
                      LocalVariableGen newLVG =
                              (LocalVariableGen)localVarMap.get(oldLVG);
  
                      // Has the code already mapped this local variable to a
                      // local in the new method?
                      if (localVarMap.get(oldLVG) == null) {
                          // Determine whether the local variable needs to be
                          // copied into or out of the outlined by checking
                          // whether the range of instructions in which the
                          // variable is accessible is outside the range of
                          // instructions in the outlineable chunk.
                          // Special case a chunk start offset of zero:  a local
                          // variable live at that position must be a method
                          // parameter, so the code doesn't need to check whether
                          // the variable is live before that point; being live
                          // at offset zero is sufficient to know that the value
                          // must be copied in to the outlined method.
                          boolean copyInLocalValue =
                              offsetInLocalVariableGenRange(oldLVG,
                                                  (outlineChunkStartOffset != 0)
                                                      ? outlineChunkStartOffset-1
                                                      : 0);
                          boolean copyOutLocalValue =
                              offsetInLocalVariableGenRange(oldLVG,
                                                  outlineChunkEndOffset+1);
  
                          // For any variable that needs to be copied into or out
                          // of the outlined method, create a field in the
                          // CopyLocals class, and generate the necessary code for
                          // copying the value.
                          if (copyInLocalValue || copyOutLocalValue) {
                              String varName = oldLVG.getName();
                              jaxp.sun.org.apache.bcel.internal.generic.Type varType = oldLVG.getType();
                              newLVG = outlinedMethodGen.addLocalVariable(varName,
                                                                          varType,
                                                                          null,
                                                                          null);
                              int newLocalVarIndex = newLVG.getIndex();
                              String varSignature = varType.getSignature();
  
                              // Record the mapping from the old local to the new
                              localVarMap.put(oldLVG, newLVG);
  
                              copyAreaFieldCount++;
                              String copyAreaFieldName =
                                             "field" + copyAreaFieldCount;
                              copyAreaCG.addField(
                                  new Field(ACC_PUBLIC,
                                          copyAreaCPG.addUtf8(copyAreaFieldName),
                                          copyAreaCPG.addUtf8(varSignature),
                                          null, copyAreaCPG.getConstantPool()));
  
                              int fieldRef = cpg.addFieldref(argTypeName,
                                                             copyAreaFieldName,
                                                             varSignature);
  
                              if (copyInLocalValue) {
                                  // Generate code for the old method to store the
                                  // value of the local into the correct field in
                                  // CopyLocals prior to invocation of the
                                  // outlined method.
                                  oldMethCopyInIL.append(
                                          InstructionConstants.DUP);
                                  InstructionHandle copyInLoad =
                                      oldMethCopyInIL.append(
                                          loadLocal(oldLocalVarIndex, varType));
                                  oldMethCopyInIL.append(new PUTFIELD(fieldRef));
  
                                  // If the end of the live range of the old
                                  // variable was in the middle of the outlined
                                  // chunk.  Make the load of its value the new
                                  // end of its range.
                                  if (!copyOutLocalValue) {
                                      revisedLocalVarEnd.put(oldLVG, copyInLoad);
                                  }
  
                                  // Generate code for start of the outlined
                                  // method to copy the value from a field in
                                  // CopyLocals to the new local in the outlined
                                  // method
                                  newMethCopyInIL.append(
                                          InstructionConstants.ALOAD_1);
                                  newMethCopyInIL.append(new GETFIELD(fieldRef));
                                  newMethCopyInIL.append(
                                          storeLocal(newLocalVarIndex, varType));
                              }
  
                              if (copyOutLocalValue) {
                                  // Generate code for the end of the outlined
                                  // method to copy the value from the new local
                                  // variable into a field in CopyLocals
                                  // method
                                  newMethCopyOutIL.append(
                                          InstructionConstants.ALOAD_1);
                                  newMethCopyOutIL.append(
                                          loadLocal(newLocalVarIndex, varType));
                                  newMethCopyOutIL.append(new PUTFIELD(fieldRef));
  
                                  // Generate code to copy the value from a field
                                  // in CopyLocals into a local in the original
                                  // method following invocation of the outlined
                                  // method.
                                  oldMethCopyOutIL.append(
                                          InstructionConstants.DUP);
                                  oldMethCopyOutIL.append(new GETFIELD(fieldRef));
                                  InstructionHandle copyOutStore =
                                      oldMethCopyOutIL.append(
                                          storeLocal(oldLocalVarIndex, varType));
  
                                  // If the start of the live range of the old
                                  // variable was in the middle of the outlined
                                  // chunk.  Make this store into it the new start
                                  // of its range.
                                  if (!copyInLocalValue) {
                                      revisedLocalVarStart.put(oldLVG,
                                                               copyOutStore);
                                  }
                              }
                          }
                      }
                  }
  
                  if (ih.hasTargeters()) {
                      targetMap.put(ih, lastCopyHandle);
                  }
  
                  // If this is the first instruction copied following a sequence
                  // of OutlineableChunkStart instructions, indicate that the
                  // sequence of old instruction all map to this newly created
                  // instruction
                  if (chunkStartTargetMappingsPending) {
                      do {
                           targetMap.put(pendingTargetMappingHandle,
                                         lastCopyHandle);
                           pendingTargetMappingHandle =
                                   pendingTargetMappingHandle.getNext();
                      } while(pendingTargetMappingHandle != ih);
  
                      chunkStartTargetMappingsPending = false;
                  }
              }
          }
  
          // Pass 2: Walk old and new instruction lists, updating branch targets
          // and local variable references in the new list
          InstructionHandle ih = first;
          InstructionHandle ch = newIL.getStart();
  
          while (ch != null) {
              // i == old instruction; c == copied instruction
              Instruction i = ih.getInstruction();
              Instruction c = ch.getInstruction();
  
              if (i instanceof BranchInstruction) {
                  BranchInstruction bc      = (BranchInstruction)c;
                  BranchInstruction bi      = (BranchInstruction)i;
                  InstructionHandle itarget = bi.getTarget(); // old target
  
                  // New target must be in targetMap
                  InstructionHandle newTarget =
                      (InstructionHandle)targetMap.get(itarget);
  
                  bc.setTarget(newTarget);
  
                  // Handle LOOKUPSWITCH or TABLESWITCH which may have many
                  // target instructions
                  if (bi instanceof Select) {
                      InstructionHandle[] itargets = ((Select)bi).getTargets();
                      InstructionHandle[] ctargets = ((Select)bc).getTargets();
  
                      // Update all targets
                      for (int j=0; j < itargets.length; j++) {
                          ctargets[j] =
                              (InstructionHandle)targetMap.get(itargets[j]);
                      }
                  }
              }  else if (i instanceof LocalVariableInstruction
                              || i instanceof RET) {
                  // For any instruction that touches a local variable,
                  // map the location of the variable in the original
                  // method to its location in the new method.
                  IndexedInstruction lvi = (IndexedInstruction)c;
                  int oldLocalVarIndex = lvi.getIndex();
                  LocalVariableGen oldLVG =
                          getLocalVariableRegistry()
                                  .lookupRegisteredLocalVariable(oldLocalVarIndex,
                                                                ih.getPosition());
                  LocalVariableGen newLVG =
                          (LocalVariableGen)localVarMap.get(oldLVG);
                  int newLocalVarIndex;
  
                  if (newLVG == null) {
                      // Create new variable based on old variable - use same
                      // name and type, but we will let the variable be active
                      // for the entire outlined method.
                      // LocalVariableGen oldLocal = oldLocals[oldLocalVarIndex];
                      String varName = oldLVG.getName();
                      jaxp.sun.org.apache.bcel.internal.generic.Type varType = oldLVG.getType();
                      newLVG = outlinedMethodGen.addLocalVariable(varName,
                                                                  varType,
                                                                  null,
                                                                  null);
                      newLocalVarIndex = newLVG.getIndex();
                      localVarMap.put(oldLVG, newLVG);
  
                      // The old variable's live range was wholly contained in
                      // the outlined chunk.  There should no longer be stores
                      // of values into it or loads of its value, so we can just
                      // mark its live range as the reference to the outlined
                      // method.
                      revisedLocalVarStart.put(oldLVG, outlinedMethodRef);
                      revisedLocalVarEnd.put(oldLVG, outlinedMethodRef);
                  } else {
                      newLocalVarIndex = newLVG.getIndex();
                  }
                  lvi.setIndex(newLocalVarIndex);
              }
  
              // If the old instruction marks the end of the range of a local
              // variable, make sure that any slots on the stack reserved for
              // local variables are made available for reuse by calling
              // MethodGenerator.removeLocalVariable
              if (ih.hasTargeters()) {
                  InstructionTargeter[] targeters = ih.getTargeters();
  
                  for (int idx = 0; idx < targeters.length; idx++) {
                      InstructionTargeter targeter = targeters[idx];
  
                      if (targeter instanceof LocalVariableGen
                              && ((LocalVariableGen)targeter).getEnd()==ih) {
                          Object newLVG = localVarMap.get(targeter);
                          if (newLVG != null) {
                              outlinedMethodGen.removeLocalVariable(
                                                    (LocalVariableGen)newLVG);
                          }
                      }
                  }
              }
  
              // If the current instruction in the original list was a marker,
              // it wasn't copied, so don't advance through the list of copied
              // instructions yet.
              if (!(i instanceof MarkerInstruction)) {
                  ch = ch.getNext();
              }
              ih = ih.getNext();
  
          }
  
          // POP the reference to the CopyLocals object from the stack
          oldMethCopyOutIL.append(InstructionConstants.POP);
  
          // Now that the generation of the outlined code is complete, update
          // the old local variables with new start and end ranges, as required.
          Iterator revisedLocalVarStartPairIter = revisedLocalVarStart.entrySet()
                                                                      .iterator();
          while (revisedLocalVarStartPairIter.hasNext()) {
              Map.Entry lvgRangeStartPair =
                      (Map.Entry)revisedLocalVarStartPairIter.next();
              LocalVariableGen lvg = (LocalVariableGen)lvgRangeStartPair.getKey();
              InstructionHandle startInst =
                      (InstructionHandle)lvgRangeStartPair.getValue();
  
              lvg.setStart(startInst);
  
          }
  
          Iterator revisedLocalVarEndPairIter = revisedLocalVarEnd.entrySet()
                                                                  .iterator();
          while (revisedLocalVarEndPairIter.hasNext()) {
              Map.Entry lvgRangeEndPair =
                      (Map.Entry)revisedLocalVarEndPairIter.next();
              LocalVariableGen lvg = (LocalVariableGen)lvgRangeEndPair.getKey();
              InstructionHandle endInst =
                      (InstructionHandle)lvgRangeEndPair.getValue();
  
              lvg.setEnd(endInst);
          }
  
          xsltc.dumpClass(copyAreaCG.getJavaClass());
  
          // Assemble the instruction lists so that the old method invokes the
          // new outlined method
          InstructionList oldMethodIL = getInstructionList();
  
          oldMethodIL.insert(first, oldMethCopyInIL);
          oldMethodIL.insert(first, oldMethCopyOutIL);
  
          // Insert the copying code into the outlined method
          newIL.insert(newMethCopyInIL);
          newIL.append(newMethCopyOutIL);
          newIL.append(InstructionConstants.RETURN);
  
          // Discard instructions in outlineable chunk from old method
          try {
              oldMethodIL.delete(first, last);
          } catch (TargetLostException e) {
              InstructionHandle[] targets = e.getTargets();
              // If there were still references to old instructions lingering,
              // clean those up.  The only instructions targetting the deleted
              // instructions should have been part of the chunk that was just
              // deleted, except that instructions might branch to the start of
              // the outlined chunk; similarly, all the live ranges of local
              // variables should have been adjusted, except for unreferenced
              // variables.
              for (int i = 0; i < targets.length; i++) {
                  InstructionHandle lostTarget = targets[i];
                  InstructionTargeter[] targeters = lostTarget.getTargeters();
                  for (int j = 0; j < targeters.length; j++) {
                      if (targeters[j] instanceof LocalVariableGen) {
                          LocalVariableGen lvgTargeter =
                                               (LocalVariableGen) targeters[j];
                          // In the case of any lingering variable references,
                          // just make the live range point to the outlined
                          // function reference.  Such variables should be unused
                          // anyway.
                          if (lvgTargeter.getStart() == lostTarget) {
                              lvgTargeter.setStart(outlinedMethodRef);
                          }
                          if (lvgTargeter.getEnd() == lostTarget) {
                              lvgTargeter.setEnd(outlinedMethodRef);
                          }
                      } else {
                          targeters[j].updateTarget(lostTarget,
                                                    outlinedMethodCallSetup);
                      }
                  }
              }
          }
  
          // Make a copy for the new method of all exceptions that might be thrown
          String[] exceptions = getExceptions();
          for (int i = 0; i < exceptions.length; i++) {
              outlinedMethodGen.addException(exceptions[i]);
          }
  
          return outlinedMethodGen.getThisMethod();
      }
  
      /**
       * Helper method to generate an instance of a subclass of
       * {@link LoadInstruction} based on the specified {@link jaxp.sun.org.apache.bcel.internal.generic.Type} that will
       * load the specified local variable
       * @param index the JVM stack frame index of the variable that is to be
       * loaded
       * @param type the {@link jaxp.sun.org.apache.bcel.internal.generic.Type} of the variable
       * @return the generated {@link LoadInstruction}
       */
      private static Instruction loadLocal(int index, jaxp.sun.org.apache.bcel.internal.generic.Type type) {
          if (type == jaxp.sun.org.apache.bcel.internal.generic.Type.BOOLEAN) {
             return new ILOAD(index);
          } else if (type == jaxp.sun.org.apache.bcel.internal.generic.Type.INT) {
             return new ILOAD(index);
          } else if (type == jaxp.sun.org.apache.bcel.internal.generic.Type.SHORT) {
             return new ILOAD(index);
          } else if (type == jaxp.sun.org.apache.bcel.internal.generic.Type.LONG) {
             return new LLOAD(index);
          } else if (type == jaxp.sun.org.apache.bcel.internal.generic.Type.BYTE) {
             return new ILOAD(index);
          } else if (type == jaxp.sun.org.apache.bcel.internal.generic.Type.CHAR) {
             return new ILOAD(index);
          } else if (type == jaxp.sun.org.apache.bcel.internal.generic.Type.FLOAT) {
             return new FLOAD(index);
          } else if (type == jaxp.sun.org.apache.bcel.internal.generic.Type.DOUBLE) {
             return new DLOAD(index);
          } else {
             return new ALOAD(index);
          }
      }
  
      /**
       * Helper method to generate an instance of a subclass of
       * {@link StoreInstruction} based on the specified {@link jaxp.sun.org.apache.bcel.internal.generic.Type} that will
       * store a value in the specified local variable
       * @param index the JVM stack frame index of the variable that is to be
       * stored
       * @param type the {@link jaxp.sun.org.apache.bcel.internal.generic.Type} of the variable
       * @return the generated {@link StoredInstruction}
       */
      private static Instruction storeLocal(int index, jaxp.sun.org.apache.bcel.internal.generic.Type type) {
          if (type == jaxp.sun.org.apache.bcel.internal.generic.Type.BOOLEAN) {
             return new ISTORE(index);
          } else if (type == jaxp.sun.org.apache.bcel.internal.generic.Type.INT) {
             return new ISTORE(index);
          } else if (type == jaxp.sun.org.apache.bcel.internal.generic.Type.SHORT) {
             return new ISTORE(index);
          } else if (type == jaxp.sun.org.apache.bcel.internal.generic.Type.LONG) {
             return new LSTORE(index);
          } else if (type == jaxp.sun.org.apache.bcel.internal.generic.Type.BYTE) {
             return new ISTORE(index);
          } else if (type == jaxp.sun.org.apache.bcel.internal.generic.Type.CHAR) {
             return new ISTORE(index);
          } else if (type == jaxp.sun.org.apache.bcel.internal.generic.Type.FLOAT) {
             return new FSTORE(index);
          } else if (type == Type.DOUBLE) {
             return new DSTORE(index);
          } else {
             return new ASTORE(index);
          }
      }
  
      /**
       * Track the number of outlineable chunks seen.
       */
      private int m_totalChunks = 0;
  
      /**
       * Track the number of outlineable chunks started but not yet ended.  Used
       * to detect imbalances in byte code generation.
       */
      private int m_openChunks = 0;
  
      /**
       * Mark the end of the method's
       * {@link InstructionList} as the start of an outlineable chunk of code.
       * The outlineable chunk begins after the {@link InstructionHandle} that is
       * at the end of the method's {@link InstructionList}, or at the start of
       * the method if the <code>InstructionList</code> is empty.
       * See {@link OutlineableChunkStart} for more information.
       */
      public void markChunkStart() {
          // m_chunkTree.markChunkStart();
          getInstructionList()
                  .append(OutlineableChunkStart.OUTLINEABLECHUNKSTART);
          m_totalChunks++;
          m_openChunks++;
      }
  
      /**
       * Mark the end of an outlineable chunk of code.  See
       * {@link OutlineableChunkStart} for more information.
       */
      public void markChunkEnd() {
          // m_chunkTree.markChunkEnd();
          getInstructionList()
                  .append(OutlineableChunkEnd.OUTLINEABLECHUNKEND);
          m_openChunks--;
          if (m_openChunks < 0) {
              String msg = (new ErrorMsg(ErrorMsg.OUTLINE_ERR_UNBALANCED_MARKERS))
                                   .toString();
              throw new InternalError(msg);
          }
      }
  
      /**
       * <p>Get all {@link Method}s generated by this {@link MethodGenerator}.
       * The {@link MethodGen#getMethod()} only returns a single
       * <code>Method</code> object.  This method takes into account the Java
       * Virtual Machine Specification limit of 64KB on the size of a method, and
       * may return more than one <code>Method</code>.</p>
       * <p>If the code associated with the <code>MethodGenerator</code> would
       * exceed the 64KB limit, this method will attempt to split the code in
       * the {@link InstructionList} associated with this
       * <code>MethodGenerator</code> into several methods.</p>
       * @param classGen the {@link ClassGenerator} of which these methods are
       *                 members
       * @return an array of all the <code>Method</code>s generated
       */
      Method[] getGeneratedMethods(ClassGenerator classGen) {
          Method[] generatedMethods;
          InstructionList il = getInstructionList();
          InstructionHandle last = il.getEnd();
  
          il.setPositions();
  
          int instructionListSize =
                      last.getPosition() + last.getInstruction().getLength();
  
          // Need to look for any branch target offsets that exceed the range
          // [-32768,32767]
          if (instructionListSize > MAX_BRANCH_TARGET_OFFSET) {
              boolean ilChanged = widenConditionalBranchTargetOffsets();
  
              // If any branch instructions needed widening, recompute the size
              // of the byte code for the method
              if (ilChanged) {
                  il.setPositions();
                  last = il.getEnd();
                  instructionListSize =
                          last.getPosition() + last.getInstruction().getLength();
              }
          }
  
          if (instructionListSize > MAX_METHOD_SIZE) {
              generatedMethods = outlineChunks(classGen, instructionListSize);
          } else {
              generatedMethods = new Method[] {getThisMethod()};
          }
          return generatedMethods;
      }
  
      protected Method getThisMethod() {
          stripAttributes(true);
          setMaxLocals();
          setMaxStack();
          removeNOPs();
  
          return getMethod();
      }
      /**
       * <p>Rewrites branches to avoid the JVM limits of relative branch
       * offsets.  There is no need to invoke this method if the bytecode for the
       * {@link MethodGenerator} does not exceed 32KB.</p>
       * <p>The Java Virtual Machine Specification permits the code portion of a
       * method to be up to 64KB in length.  However, some control transfer
       * instructions specify relative offsets as a signed 16-bit quantity,
       * limiting the range to a subset of the instructions that might be in a
       * method.</p>
       * <p>The <code>TABLESWITCH</code> and <code>LOOKUPSWITCH</code>
       * instructions always use 32-bit signed relative offsets, so they are
       * immune to this problem.</p>
       * <p>The <code>GOTO</code> and <code>JSR</code>
       * instructions come in two forms, one of which uses 16-bit relative
       * offsets, and the other of which uses 32-bit relative offsets.  The BCEL
       * library decides whether to use the wide form of <code>GOTO</code> or
       * <code>JSR</code>instructions based on the relative offset of the target
       * of the instruction without any intervention by the user of the
       * library.</p>
       * <p>This leaves the various conditional branch instructions,
       * <code>IFEQ</code>, <code>IFNULL</code>, <code>IF_ICMPEQ</code>,
       * <em>et al.</em>, all of which use 16-bit signed relative offsets, with no
       * 32-bit wide form available.</p>
       * <p>This method scans the {@link InstructionList} associated with this
       * {@link MethodGenerator} and finds all conditional branch instructions
       * that might exceed the 16-bit limitation for relative branch offsets.
       * The logic of each such instruction is inverted, and made to target the
       * instruction which follows it.  An unconditional branch to the original
       * target of the instruction is then inserted between the conditional
       * branch and the instruction which previously followed it.  The
       * unconditional branch is permitted to have a 16-bit or a 32-bit relative
       * offset, as described above.  For example,
       * <code>
       * 1234:   NOP
       *          ...
       * 55278:  IFEQ -54044
       * 55280:  NOP
       * </code>
       * is rewritten as
       * <code>
       * 1234:   NOP
       *          ...
       * 55278:  IFNE 7
       * 55280:  GOTO_W -54046
       * 55285:  NOP
       * </code></p>
       * <p><b>Preconditions:</b>
       * <ul><li>The {@link InstructionList#setPositions()} has been called for
       * the <code>InstructionList</code> associated with this
       * <code>MethodGenerator</code>.
       * </li></ul></p>
       * <p><b>Postconditions:</b>
       * <ul><li>Any further changes to the <code>InstructionList</code> for this
       * <code>MethodGenerator</code> will invalidate the changes made by this
       * method.</li></ul>
       * </p>
       * @return <code>true</code> if the <code>InstructionList</code> was
       * modified; <code>false</code> otherwise
       * @see The Java Virtual Machine Specification, Second Edition
       */
      boolean widenConditionalBranchTargetOffsets() {
          boolean ilChanged = false;
          int maxOffsetChange = 0;
          InstructionList il = getInstructionList();
  
          // Loop through all the instructions, finding those that would be
          // affected by inserting new instructions in the InstructionList, and
          // calculating the maximum amount by which the relative offset between
          // two instructions could possibly change.
          // In part this loop duplicates code in
          // org.apache.bcel.generic.InstructionList.setPosition(), which does
          // this to determine whether to use 16-bit or 32-bit offsets for GOTO
          // and JSR instructions.  Ideally, that method would do the same for
          // conditional branch instructions, but it doesn't, so we duplicate the
          // processing here.
          for (InstructionHandle ih = il.getStart();
               ih != null;
               ih = ih.getNext()) {
              Instruction inst = ih.getInstruction();
  
              switch (inst.getOpcode()) {
                  // Instructions that may have 16-bit or 32-bit branch targets.
                  // The size of the branch offset might increase by two bytes.
                  case jaxp.sun.org.apache.bcel.internal.Constants.GOTO:
                  case jaxp.sun.org.apache.bcel.internal.Constants.JSR:
                      maxOffsetChange = maxOffsetChange + 2;
                      break;
                  // Instructions that contain padding for alignment purposes
                  // Up to three bytes of padding might be needed.  For greater
                  // accuracy, we should be able to discount any padding already
                  // added to these instructions by InstructionList.setPosition(),
                  // their APIs do not expose that information.
                  case jaxp.sun.org.apache.bcel.internal.Constants.TABLESWITCH:
                  case jaxp.sun.org.apache.bcel.internal.Constants.LOOKUPSWITCH:
                      maxOffsetChange = maxOffsetChange + 3;
                      break;
                  // Instructions that might be rewritten by this method as a
                  // conditional branch followed by an unconditional branch.
                  // The unconditional branch would require five bytes.
                  case jaxp.sun.org.apache.bcel.internal.Constants.IF_ACMPEQ:
                  case jaxp.sun.org.apache.bcel.internal.Constants.IF_ACMPNE:
                  case jaxp.sun.org.apache.bcel.internal.Constants.IF_ICMPEQ:
                  case jaxp.sun.org.apache.bcel.internal.Constants.IF_ICMPGE:
                  case jaxp.sun.org.apache.bcel.internal.Constants.IF_ICMPGT:
                  case jaxp.sun.org.apache.bcel.internal.Constants.IF_ICMPLE:
                  case jaxp.sun.org.apache.bcel.internal.Constants.IF_ICMPLT:
                  case jaxp.sun.org.apache.bcel.internal.Constants.IF_ICMPNE:
                  case jaxp.sun.org.apache.bcel.internal.Constants.IFEQ:
                  case jaxp.sun.org.apache.bcel.internal.Constants.IFGE:
                  case jaxp.sun.org.apache.bcel.internal.Constants.IFGT:
                  case jaxp.sun.org.apache.bcel.internal.Constants.IFLE:
                  case jaxp.sun.org.apache.bcel.internal.Constants.IFLT:
                  case jaxp.sun.org.apache.bcel.internal.Constants.IFNE:
                  case jaxp.sun.org.apache.bcel.internal.Constants.IFNONNULL:
                  case jaxp.sun.org.apache.bcel.internal.Constants.IFNULL:
                      maxOffsetChange = maxOffsetChange + 5;
                      break;
              }
          }
  
          // Now that the maximum number of bytes by which the method might grow
          // has been determined, look for conditional branches to see which
          // might possibly exceed the 16-bit relative offset.
          for (InstructionHandle ih = il.getStart();
               ih != null;
               ih = ih.getNext()) {
              Instruction inst = ih.getInstruction();
  
              if (inst instanceof IfInstruction) {
                  IfInstruction oldIfInst = (IfInstruction)inst;
                  BranchHandle oldIfHandle = (BranchHandle)ih;
                  InstructionHandle target = oldIfInst.getTarget();
                  int relativeTargetOffset = target.getPosition()
                                                 - oldIfHandle.getPosition();
  
                  // Consider the worst case scenario in which the conditional
                  // branch and its target are separated by all the instructions
                  // in the method that might increase in size.  If that results
                  // in a relative offset that cannot be represented as a 32-bit
                  // signed quantity, rewrite the instruction as described above.
                  if ((relativeTargetOffset - maxOffsetChange
                               < MIN_BRANCH_TARGET_OFFSET)
                          || (relativeTargetOffset + maxOffsetChange
                                      > MAX_BRANCH_TARGET_OFFSET)) {
                      // Invert the logic of the IF instruction, and append
                      // that to the InstructionList following the original IF
                      // instruction
                      InstructionHandle nextHandle = oldIfHandle.getNext();
                      IfInstruction invertedIfInst = oldIfInst.negate();
                      BranchHandle invertedIfHandle = il.append(oldIfHandle,
                                                                invertedIfInst);
  
                      // Append an unconditional branch to the target of the
                      // original IF instruction after the new IF instruction
                      BranchHandle gotoHandle = il.append(invertedIfHandle,
                                                          new GOTO(target));
  
                      // If the original IF was the last instruction in
                      // InstructionList, add a new no-op to act as the target
                      // of the new IF
                      if (nextHandle == null) {
                          nextHandle = il.append(gotoHandle, InstructionConstants.NOP);
                      }
  
                      // Make the new IF instruction branch around the GOTO
                      invertedIfHandle.updateTarget(target, nextHandle);
  
                      // If anything still "points" to the old IF instruction,
                      // make adjustments to refer to either the new IF or GOTO
                      // instruction
                      if (oldIfHandle.hasTargeters()) {
                          InstructionTargeter[] targeters =
                                                    oldIfHandle.getTargeters();
  
                          for (int i = 0; i < targeters.length; i++) {
                              InstructionTargeter targeter = targeters[i];
                              // Ideally, one should simply be able to use
                              // InstructionTargeter.updateTarget to change
                              // references to the old IF instruction to the new
                              // IF instruction.  However, if a LocalVariableGen
                              // indicated the old IF marked the end of the range
                              // in which the IF variable is in use, the live
                              // range of the variable must extend to include the
                              // newly created GOTO instruction.  The need for
                              // this sort of specific knowledge of an
                              // implementor of the InstructionTargeter interface
                              // makes the code more fragile.  Future implementors
                              // of the interface might have similar requirements
                              // which wouldn't be accommodated seemlessly.
                              if (targeter instanceof LocalVariableGen) {
                                  LocalVariableGen lvg =
                                          (LocalVariableGen) targeter;
                                  if (lvg.getStart() == oldIfHandle) {
                                      lvg.setStart(invertedIfHandle);
                                  } else if (lvg.getEnd() == oldIfHandle) {
                                      lvg.setEnd(gotoHandle);
                                  }
                              } else {
                                  targeter.updateTarget(oldIfHandle,
                                                        invertedIfHandle);
                              }
                          }
                      }
  
                      try {
                          il.delete(oldIfHandle);
                      } catch (TargetLostException tle) {
                          // This can never happen - we updated the list of
                          // instructions that target the deleted instruction
                          // prior to deleting it.
                          String msg =
                              new ErrorMsg(ErrorMsg.OUTLINE_ERR_DELETED_TARGET,
                                           tle.getMessage()).toString();
                          throw new InternalError(msg);
                      }
  
                      // Adjust the pointer in the InstructionList to point after
                      // the newly inserted IF instruction
                      ih = gotoHandle;
  
                      // Indicate that this method rewrote at least one IF
                      ilChanged = true;
                  }
              }
          }
  
          // Did this method rewrite any IF instructions?
          return ilChanged;
      }
  }
  