1 /*
   2  * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Licensed to the Apache Software Foundation (ASF) under one or more
   6  * contributor license agreements.  See the NOTICE file distributed with
   7  * this work for additional information regarding copyright ownership.
   8  * The ASF licenses this file to You under the Apache License, Version 2.0
   9  * (the "License"); you may not use this file except in compliance with
  10  * the License.  You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 /*
  21  * $Id: MethodGenerator.java,v 1.2.4.1 2005/09/05 11:16:47 pvedula Exp $
  22  */
  23 
  24 package com.sun.org.apache.xalan.internal.xsltc.compiler.util;
  25 
  26 import com.sun.org.apache.bcel.internal.Const;
  27 import com.sun.org.apache.bcel.internal.classfile.Field;
  28 import com.sun.org.apache.bcel.internal.classfile.Method;
  29 import com.sun.org.apache.bcel.internal.generic.ALOAD;
  30 import com.sun.org.apache.bcel.internal.generic.ASTORE;
  31 import com.sun.org.apache.bcel.internal.generic.BranchHandle;
  32 import com.sun.org.apache.bcel.internal.generic.BranchInstruction;
  33 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  34 import com.sun.org.apache.bcel.internal.generic.DLOAD;
  35 import com.sun.org.apache.bcel.internal.generic.DSTORE;
  36 import com.sun.org.apache.bcel.internal.generic.FLOAD;
  37 import com.sun.org.apache.bcel.internal.generic.FSTORE;
  38 import com.sun.org.apache.bcel.internal.generic.GETFIELD;
  39 import com.sun.org.apache.bcel.internal.generic.GOTO;
  40 import com.sun.org.apache.bcel.internal.generic.ICONST;
  41 import com.sun.org.apache.bcel.internal.generic.ILOAD;
  42 import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
  43 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
  44 import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
  45 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
  46 import com.sun.org.apache.bcel.internal.generic.ISTORE;
  47 import com.sun.org.apache.bcel.internal.generic.IfInstruction;
  48 import com.sun.org.apache.bcel.internal.generic.IndexedInstruction;
  49 import com.sun.org.apache.bcel.internal.generic.Instruction;
  50 import com.sun.org.apache.bcel.internal.generic.InstructionConst;
  51 import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
  52 import com.sun.org.apache.bcel.internal.generic.InstructionList;
  53 import com.sun.org.apache.bcel.internal.generic.InstructionTargeter;
  54 import com.sun.org.apache.bcel.internal.generic.LLOAD;
  55 import com.sun.org.apache.bcel.internal.generic.LSTORE;
  56 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
  57 import com.sun.org.apache.bcel.internal.generic.LocalVariableInstruction;
  58 import com.sun.org.apache.bcel.internal.generic.MethodGen;
  59 import com.sun.org.apache.bcel.internal.generic.NEW;
  60 import com.sun.org.apache.bcel.internal.generic.PUTFIELD;
  61 import com.sun.org.apache.bcel.internal.generic.RET;
  62 import com.sun.org.apache.bcel.internal.generic.Select;
  63 import com.sun.org.apache.bcel.internal.generic.TargetLostException;
  64 import com.sun.org.apache.bcel.internal.generic.Type;
  65 import com.sun.org.apache.xalan.internal.xsltc.compiler.Pattern;
  66 import com.sun.org.apache.xalan.internal.xsltc.compiler.XSLTC;
  67 import java.util.ArrayList;
  68 import java.util.Collections;
  69 import java.util.HashMap;
  70 import java.util.Iterator;
  71 import java.util.List;
  72 import java.util.Map;
  73 import java.util.Stack;
  74 
  75 /**
  76  * @author Jacek Ambroziak
  77  * @author Santiago Pericas-Geertsen
  78  * @LastModified: Nov 2017
  79  */
  80 public class MethodGenerator extends MethodGen
  81     implements com.sun.org.apache.xalan.internal.xsltc.compiler.Constants {
  82     protected static final int INVALID_INDEX   = -1;
  83 
  84     private static final String START_ELEMENT_SIG
  85         = "(" + STRING_SIG + ")V";
  86     private static final String END_ELEMENT_SIG
  87         = START_ELEMENT_SIG;
  88 
  89     private static final int DOM_INDEX       = 1;
  90     private static final int ITERATOR_INDEX  = 2;
  91     private static final int HANDLER_INDEX   = 3;
  92 
  93     private static final int MAX_METHOD_SIZE = 65535;
  94     private static final int MAX_BRANCH_TARGET_OFFSET = 32767;
  95     private static final int MIN_BRANCH_TARGET_OFFSET = -32768;
  96 
  97     private static final int TARGET_METHOD_SIZE = 60000;
  98     private static final int MINIMUM_OUTLINEABLE_CHUNK_SIZE = 1000;
  99 
 100     private Instruction       _iloadCurrent;
 101     private Instruction       _istoreCurrent;
 102     private final Instruction _astoreHandler;
 103     private final Instruction _aloadHandler;
 104     private final Instruction _astoreIterator;
 105     private final Instruction _aloadIterator;
 106     private final Instruction _aloadDom;
 107     private final Instruction _astoreDom;
 108 
 109     private final Instruction _startElement;
 110     private final Instruction _endElement;
 111     private final Instruction _startDocument;
 112     private final Instruction _endDocument;
 113     private final Instruction _attribute;
 114     private final Instruction _uniqueAttribute;
 115     private final Instruction _namespace;
 116 
 117     private final Instruction _setStartNode;
 118     private final Instruction _reset;
 119     private final Instruction _nextNode;
 120 
 121     private SlotAllocator _slotAllocator;
 122     private boolean _allocatorInit = false;
 123     private LocalVariableRegistry _localVariableRegistry;
 124         /**
 125                  * A mapping between patterns and instruction lists used by
 126                  * test sequences to avoid compiling the same pattern multiple
 127                  * times. Note that patterns whose kernels are "*", "node()"
 128                  * and "@*" can between shared by test sequences.
 129                  */
 130         private Map<Pattern, InstructionList> _preCompiled = new HashMap<>();
 131 
 132 
 133     public MethodGenerator(int access_flags, Type return_type,
 134                            Type[] arg_types, String[] arg_names,
 135                            String method_name, String class_name,
 136                            InstructionList il, ConstantPoolGen cpg) {
 137         super(access_flags, return_type, arg_types, arg_names, method_name,
 138               class_name, il, cpg);
 139 
 140         _astoreHandler  = new ASTORE(HANDLER_INDEX);
 141         _aloadHandler   = new ALOAD(HANDLER_INDEX);
 142         _astoreIterator = new ASTORE(ITERATOR_INDEX);
 143         _aloadIterator  = new ALOAD(ITERATOR_INDEX);
 144         _aloadDom       = new ALOAD(DOM_INDEX);
 145         _astoreDom      = new ASTORE(DOM_INDEX);
 146 
 147         final int startElement =
 148             cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE,
 149                                       "startElement",
 150                                       START_ELEMENT_SIG);
 151         _startElement = new INVOKEINTERFACE(startElement, 2);
 152 
 153         final int endElement =
 154             cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE,
 155                                       "endElement",
 156                                       END_ELEMENT_SIG);
 157         _endElement = new INVOKEINTERFACE(endElement, 2);
 158 
 159         final int attribute =
 160             cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE,
 161                                       "addAttribute",
 162                                       "("
 163                                       + STRING_SIG
 164                                       + STRING_SIG
 165                                       + ")V");
 166         _attribute = new INVOKEINTERFACE(attribute, 3);
 167 
 168         final int uniqueAttribute =
 169             cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE,
 170                                       "addUniqueAttribute",
 171                                       "("
 172                                       + STRING_SIG
 173                                       + STRING_SIG
 174                                       + "I)V");
 175         _uniqueAttribute = new INVOKEINTERFACE(uniqueAttribute, 4);
 176 
 177         final int namespace =
 178             cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE,
 179                                       "namespaceAfterStartElement",
 180                                       "("
 181                                       + STRING_SIG
 182                                       + STRING_SIG
 183                                       + ")V");
 184         _namespace = new INVOKEINTERFACE(namespace, 3);
 185 
 186         int index = cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE,
 187                                               "startDocument",
 188                                               "()V");
 189         _startDocument = new INVOKEINTERFACE(index, 1);
 190 
 191         index = cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE,
 192                                           "endDocument",
 193                                           "()V");
 194         _endDocument = new INVOKEINTERFACE(index, 1);
 195 
 196 
 197         index = cpg.addInterfaceMethodref(NODE_ITERATOR,
 198                                           SET_START_NODE,
 199                                           SET_START_NODE_SIG);
 200         _setStartNode = new INVOKEINTERFACE(index, 2);
 201 
 202         index = cpg.addInterfaceMethodref(NODE_ITERATOR,
 203                                           "reset", "()"+NODE_ITERATOR_SIG);
 204         _reset = new INVOKEINTERFACE(index, 1);
 205 
 206         index = cpg.addInterfaceMethodref(NODE_ITERATOR, NEXT, NEXT_SIG);
 207         _nextNode = new INVOKEINTERFACE(index, 1);
 208 
 209         _slotAllocator = new SlotAllocator();
 210         _slotAllocator.initialize(getLocalVariableRegistry().getLocals(false));
 211         _allocatorInit = true;
 212     }
 213 
 214     /**
 215      * Allocates a local variable. If the slot allocator has already been
 216      * initialized, then call addLocalVariable2() so that the new variable
 217      * is known to the allocator. Failing to do this may cause the allocator
 218      * to return a slot that is already in use.
 219      */
 220     public LocalVariableGen addLocalVariable(String name, Type type,
 221                                              InstructionHandle start,
 222                                              InstructionHandle end)
 223     {
 224         LocalVariableGen lvg;
 225 
 226         if (_allocatorInit) {
 227             lvg = addLocalVariable2(name, type, start);
 228         } else {
 229             lvg = super.addLocalVariable(name, type, start, end);
 230             getLocalVariableRegistry().registerLocalVariable(lvg);
 231         }
 232         return lvg;
 233     }
 234 
 235     public LocalVariableGen addLocalVariable2(String name, Type type,
 236                                               InstructionHandle start)
 237     {
 238         LocalVariableGen lvg = super.addLocalVariable(name, type,
 239                                               _slotAllocator.allocateSlot(type),
 240                                               start, null);
 241         getLocalVariableRegistry().registerLocalVariable(lvg);
 242         return lvg;
 243     }
 244     private LocalVariableRegistry getLocalVariableRegistry() {
 245         if (_localVariableRegistry == null) {
 246             _localVariableRegistry = new LocalVariableRegistry();
 247         }
 248 
 249         return _localVariableRegistry;
 250     }
 251 
 252     /**
 253      * Keeps track of all local variables used in the method.
 254      * <p>The
 255      * {@link MethodGen#addLocalVariable(String,Type,InstructionHandle,InstructionHandle)}</code>
 256      * and
 257      * {@link MethodGen#addLocalVariable(String,Type,int,InstructionHandle,InstructionHandle)}</code>
 258      * methods of {@link MethodGen} will only keep track of
 259      * {@link LocalVariableGen} object until it'ss removed by a call to
 260      * {@link MethodGen#removeLocalVariable(LocalVariableGen)}.</p>
 261      * <p>In order to support efficient copying of local variables to outlined
 262      * methods by
 263      * {@link #outline(InstructionHandle,InstructionHandle,String,ClassGenerator)},
 264      * this class keeps track of all local variables defined by the method.</p>
 265      */
 266     protected class LocalVariableRegistry {
 267         /**
 268          * <p>A <code>java.lang.List</code> of all
 269          * {@link LocalVariableGen}s created for this method, indexed by the
 270          * slot number of the local variable.  The JVM stack frame of local
 271          * variables is divided into "slots".  A single slot can be used to
 272          * store more than one variable in a method, without regard to type, so
 273          * long as the byte code keeps the ranges of the two disjoint.</p>
 274          * <p>If only one registration of use of a particular slot occurs, the
 275          * corresponding entry of <code>_variables</code> contains the
 276          * <code>LocalVariableGen</code>; if more than one occurs, the
 277          * corresponding entry contains all such <code>LocalVariableGen</code>s
 278          * registered for the same slot; and if none occurs, the entry will be
 279          * <code>null</code>.
 280          */
 281         protected List<Object> _variables = new ArrayList<>();
 282 
 283         /**
 284          * Maps a name to a {@link LocalVariableGen}
 285          */
 286         protected Map<String, Object> _nameToLVGMap = new HashMap<>();
 287 
 288         /**
 289          * Registers a {@link org.apache.bcel.generic.LocalVariableGen}
 290          * for this method.
 291          * <p><b>Preconditions:</b>
 292          * <ul>
 293          * <li>The range of instructions for <code>lvg</code> does not
 294          * overlap with the range of instructions for any
 295          * <code>LocalVariableGen</code> with the same slot index previously
 296          * registered for this method.  <b><em>(Unchecked.)</em></b></li>
 297          * </ul></p>
 298          * @param lvg The variable to be registered
 299          */
 300         @SuppressWarnings("unchecked")
 301         protected void registerLocalVariable(LocalVariableGen lvg) {
 302             int slot = lvg.getIndex();
 303 
 304             int registrySize = _variables.size();
 305 
 306             // If the LocalVariableGen uses a slot index beyond any previously
 307             // encountered, expand the _variables, padding with intervening null
 308             // entries as required.
 309             if (slot >= registrySize) {
 310                 for (int i = registrySize; i < slot; i++) {
 311                     _variables.add(null);
 312                 }
 313                 _variables.add(lvg);
 314             } else {
 315                 // If the LocalVariableGen reuses a slot, make sure the entry
 316                 // in _variables contains an ArrayList and add the newly
 317                 // registered LocalVariableGen to the list.  If the entry in
 318                 // _variables just contains null padding, store the
 319                 // LocalVariableGen directly.
 320                 Object localsInSlot = _variables.get(slot);
 321                 if (localsInSlot != null) {
 322                     if (localsInSlot instanceof LocalVariableGen) {
 323                         List<LocalVariableGen> listOfLocalsInSlot = new ArrayList<>();
 324                         listOfLocalsInSlot.add((LocalVariableGen)localsInSlot);
 325                         listOfLocalsInSlot.add(lvg);
 326                         _variables.set(slot, listOfLocalsInSlot);
 327                     } else {
 328                         ((List<LocalVariableGen>) localsInSlot).add(lvg);
 329                     }
 330                 } else {
 331                     _variables.set(slot, lvg);
 332                 }
 333             }
 334 
 335             registerByName(lvg);
 336         }
 337 
 338         /**
 339          * <p>Find which {@link LocalVariableGen}, if any, is registered for a
 340          * particular JVM local stack frame slot at a particular position in the
 341          * byte code for the method.</p>
 342          * <p><b>Preconditions:</b>
 343          * <ul>
 344          * <li>The {@link InstructionList#setPositions()} has been called for
 345          * the {@link InstructionList} associated with this
 346          * {@link MethodGenerator}.</li>
 347          * </ul></p>
 348          * @param slot the JVM local stack frame slot number
 349          * @param offset the position in the byte code
 350          * @return the <code>LocalVariableGen</code> for the local variable
 351          * stored in the relevant slot at the relevant offset; <code>null</code>
 352          * if there is none.
 353          */
 354         protected LocalVariableGen lookupRegisteredLocalVariable(int slot,
 355                                                                  int offset) {
 356             Object localsInSlot = (_variables != null) ? _variables.get(slot)
 357                                                        : null;
 358 
 359             // If this slot index was never used, _variables.get will return
 360             // null; if it was used once, it will return the LocalVariableGen;
 361             // more than once it will return an ArrayList of all the
 362             // LocalVariableGens for variables stored in that slot.  For each
 363             // LocalVariableGen, check whether its range includes the
 364             // specified offset, and return the first such encountered.
 365             if (localsInSlot != null) {
 366                 if (localsInSlot instanceof LocalVariableGen) {
 367                     LocalVariableGen lvg = (LocalVariableGen)localsInSlot;
 368                     if (offsetInLocalVariableGenRange(lvg, offset)) {
 369                         return lvg;
 370                     }
 371                 } else {
 372                     @SuppressWarnings("unchecked")
 373                     List<LocalVariableGen> listOfLocalsInSlot =
 374                             (List<LocalVariableGen>) localsInSlot;
 375 
 376                     for (LocalVariableGen lvg : listOfLocalsInSlot) {
 377                         if (offsetInLocalVariableGenRange(lvg, offset)) {
 378                             return lvg;
 379                         }
 380                     }
 381                 }
 382             }
 383 
 384             // No local variable stored in the specified slot at the specified
 385             return null;
 386         }
 387 
 388         /**
 389          * <p>Set up a mapping of the name of the specified
 390          * {@link LocalVariableGen} object to the <code>LocalVariableGen</code>
 391          * itself.</p>
 392          * <p>This is a bit of a hack.  XSLTC is relying on the fact that the
 393          * name that is being looked up won't be duplicated, which isn't
 394          * guaranteed.  It replaces code which used to call
 395          * {@link MethodGen#getLocalVariables()} and looped through the
 396          * <code>LocalVariableGen</code> objects it contained to find the one
 397          * with the specified name.  However, <code>getLocalVariables()</code>
 398          * has the side effect of setting the start and end for any
 399          * <code>LocalVariableGen</code> which did not already have them
 400          * set, which causes problems for outlining..</p>
 401          * <p>See also {@link #lookUpByName(String)} and
 402          * {@link #removeByNameTracking(LocalVariableGen)}</P
 403          * @param lvg a <code>LocalVariableGen</code>
 404          */
 405         @SuppressWarnings("unchecked")
 406         protected void registerByName(LocalVariableGen lvg) {
 407             Object duplicateNameEntry = _nameToLVGMap.get(lvg.getName());
 408 
 409             if (duplicateNameEntry == null) {
 410                 _nameToLVGMap.put(lvg.getName(), lvg);
 411             } else {
 412                 List<LocalVariableGen> sameNameList;
 413 
 414                 if (duplicateNameEntry instanceof ArrayList) {
 415                     sameNameList = (List<LocalVariableGen>)duplicateNameEntry;
 416                     sameNameList.add(lvg);
 417                 } else {
 418                     sameNameList = new ArrayList<>();
 419                     sameNameList.add((LocalVariableGen)duplicateNameEntry);
 420                     sameNameList.add(lvg);
 421                 }
 422 
 423                 _nameToLVGMap.put(lvg.getName(), sameNameList);
 424             }
 425         }
 426 
 427         /**
 428          * Remove the mapping from the name of the specified
 429          * {@link LocalVariableGen} to itself.
 430          * See also {@link #registerByName(LocalVariableGen)} and
 431          * {@link #lookUpByName(String)}
 432          * @param lvg a <code>LocalVariableGen</code>
 433          */
 434         @SuppressWarnings("unchecked")
 435         protected void removeByNameTracking(LocalVariableGen lvg) {
 436             Object duplicateNameEntry = _nameToLVGMap.get(lvg.getName());
 437 
 438             if (duplicateNameEntry instanceof ArrayList) {
 439                 List<LocalVariableGen> sameNameList =
 440                         (List<LocalVariableGen>)duplicateNameEntry;
 441                 for (int i = 0; i < sameNameList.size(); i++) {
 442                     if (sameNameList.get(i) == lvg) {
 443                         sameNameList.remove(i);
 444                         break;
 445                     }
 446                 }
 447             } else {
 448                 _nameToLVGMap.remove(lvg);
 449             }
 450         }
 451 
 452         /**
 453          * <p>Given the name of a variable, finds a {@link LocalVariableGen}
 454          * corresponding to it.</p>
 455          * <p>See also {@link #registerByName(LocalVariableGen)} and
 456          * {@link #removeByNameTracking(LocalVariableGen)}</p>
 457          * @param name
 458          * @return
 459          */
 460         @SuppressWarnings("unchecked")
 461         protected LocalVariableGen lookUpByName(String name) {
 462             LocalVariableGen lvg = null;
 463             Object duplicateNameEntry = _nameToLVGMap.get(name);
 464 
 465             if (duplicateNameEntry instanceof ArrayList) {
 466                 List<LocalVariableGen> sameNameList =
 467                         (List<LocalVariableGen>)duplicateNameEntry;
 468 
 469                 for (int i = 0; i < sameNameList.size(); i++) {
 470                     lvg = sameNameList.get(i);
 471                     if (lvg.getName() == null ? name == null : lvg.getName().equals(name)) {
 472                         break;
 473                     }
 474                 }
 475             } else {
 476                 lvg = (LocalVariableGen) duplicateNameEntry;
 477             }
 478 
 479             return lvg;
 480         }
 481 
 482         /**
 483          * <p>Gets all {@link LocalVariableGen} objects for this method.</p>
 484          * <p>When the <code>includeRemoved</code> argument has the value
 485          * <code>false</code>, this method replaces uses of
 486          * {@link MethodGen#getLocalVariables()} which has
 487          * a side-effect of setting the start and end range for any
 488          * <code>LocalVariableGen</code> if either was <code>null</code>.  That
 489          * side-effect causes problems for outlining of code in XSLTC.
 490          * @param includeRemoved Specifies whether all local variables ever
 491          * declared should be returned (<code>true</code>) or only those not
 492          * removed (<code>false</code>)
 493          * @return an array of <code>LocalVariableGen</code> containing all the
 494          * local variables
 495          */
 496         @SuppressWarnings("unchecked")
 497         protected LocalVariableGen[] getLocals(boolean includeRemoved) {
 498             LocalVariableGen[] locals = null;
 499             List<LocalVariableGen> allVarsEverDeclared = new ArrayList<>();
 500 
 501             if (includeRemoved) {
 502                 int slotCount = allVarsEverDeclared.size();
 503 
 504                 for (int i = 0; i < slotCount; i++) {
 505                     Object slotEntries = _variables.get(i);
 506                     if (slotEntries != null) {
 507                         if (slotEntries instanceof ArrayList) {
 508                             List<LocalVariableGen> slotList =
 509                                     (List<LocalVariableGen>)slotEntries;
 510 
 511                             for (int j = 0; j < slotList.size(); j++) {
 512                                 allVarsEverDeclared.add(slotList.get(i));
 513                             }
 514                         } else {
 515                             allVarsEverDeclared.add((LocalVariableGen)slotEntries);
 516                         }
 517                     }
 518                 }
 519             } else {
 520                 for (Map.Entry<String, Object> nameVarsPair : _nameToLVGMap.entrySet()) {
 521                     Object vars = nameVarsPair.getValue();
 522                     if (vars != null) {
 523                         if (vars instanceof ArrayList) {
 524                             List<LocalVariableGen> varsList =
 525                                     (List<LocalVariableGen>) vars;
 526                             for (int i = 0; i < varsList.size(); i++) {
 527                                 allVarsEverDeclared.add(varsList.get(i));
 528                             }
 529                         } else {
 530                             allVarsEverDeclared.add((LocalVariableGen)vars);
 531                         }
 532                     }
 533                 }
 534             }
 535 
 536             locals = new LocalVariableGen[allVarsEverDeclared.size()];
 537             allVarsEverDeclared.toArray(locals);
 538 
 539             return locals;
 540         }
 541     }
 542 
 543     /**
 544      * Determines whether a particular variable is in use at a particular offset
 545      * in the byte code for this method.
 546      * <p><b>Preconditions:</b>
 547      * <ul>
 548      * <li>The {@link InstructionList#setPositions()} has been called for the
 549      * {@link InstructionList} associated with this {@link MethodGenerator}.
 550      * </li></ul></p>
 551      * @param lvg the {@link LocalVariableGen} for the variable
 552      * @param offset the position in the byte code
 553      * @return <code>true</code> if and only if the specified variable is in
 554      * use at the particular byte code offset.
 555      */
 556     boolean offsetInLocalVariableGenRange(LocalVariableGen lvg, int offset) {
 557         InstructionHandle lvgStart = lvg.getStart();
 558         InstructionHandle lvgEnd = lvg.getEnd();
 559 
 560         // If no start handle is recorded for the LocalVariableGen, it is
 561         // assumed to be in use from the beginning of the method.
 562         if (lvgStart == null) {
 563             lvgStart = getInstructionList().getStart();
 564         }
 565 
 566         // If no end handle is recorded for the LocalVariableGen, it is assumed
 567         // to be in use to the end of the method.
 568         if (lvgEnd == null) {
 569             lvgEnd = getInstructionList().getEnd();
 570         }
 571 
 572         // Does the range of the instruction include the specified offset?
 573         // Note that the InstructionHandle.getPosition method returns the
 574         // offset of the beginning of an instruction.  A LocalVariableGen's
 575         // range includes the end instruction itself, so that instruction's
 576         // length must be taken into consideration in computing whether the
 577         // varible is in range at a particular offset.
 578         return ((lvgStart.getPosition() <= offset)
 579                     && (lvgEnd.getPosition()
 580                             + lvgEnd.getInstruction().getLength() >= offset));
 581     }
 582 
 583     public void removeLocalVariable(LocalVariableGen lvg) {
 584         _slotAllocator.releaseSlot(lvg);
 585         getLocalVariableRegistry().removeByNameTracking(lvg);
 586         super.removeLocalVariable(lvg);
 587     }
 588 
 589     public Instruction loadDOM() {
 590         return _aloadDom;
 591     }
 592 
 593     public Instruction storeDOM() {
 594         return _astoreDom;
 595     }
 596 
 597     public Instruction storeHandler() {
 598         return _astoreHandler;
 599     }
 600 
 601     public Instruction loadHandler() {
 602         return _aloadHandler;
 603     }
 604 
 605     public Instruction storeIterator() {
 606         return _astoreIterator;
 607     }
 608 
 609     public Instruction loadIterator() {
 610         return _aloadIterator;
 611     }
 612 
 613     public final Instruction setStartNode() {
 614         return _setStartNode;
 615     }
 616 
 617     public final Instruction reset() {
 618         return _reset;
 619     }
 620 
 621     public final Instruction nextNode() {
 622         return _nextNode;
 623     }
 624 
 625     public final Instruction startElement() {
 626         return _startElement;
 627     }
 628 
 629     public final Instruction endElement() {
 630         return _endElement;
 631     }
 632 
 633     public final Instruction startDocument() {
 634         return _startDocument;
 635     }
 636 
 637     public final Instruction endDocument() {
 638         return _endDocument;
 639     }
 640 
 641     public final Instruction attribute() {
 642         return _attribute;
 643     }
 644 
 645     public final Instruction uniqueAttribute() {
 646         return _uniqueAttribute;
 647     }
 648 
 649     public final Instruction namespace() {
 650         return _namespace;
 651     }
 652 
 653     public Instruction loadCurrentNode() {
 654         if (_iloadCurrent == null) {
 655             int idx = getLocalIndex("current");
 656             if (idx > 0)
 657                 _iloadCurrent = new ILOAD(idx);
 658             else
 659                 _iloadCurrent = new ICONST(0);
 660         }
 661         return _iloadCurrent;
 662     }
 663 
 664     public Instruction storeCurrentNode() {
 665         return _istoreCurrent != null
 666             ? _istoreCurrent
 667             : (_istoreCurrent = new ISTORE(getLocalIndex("current")));
 668     }
 669 
 670     /** by default context node is the same as current node. MK437 */
 671     public Instruction loadContextNode() {
 672         return loadCurrentNode();
 673     }
 674 
 675     public Instruction storeContextNode() {
 676         return storeCurrentNode();
 677     }
 678 
 679     public int getLocalIndex(String name) {
 680         return getLocalVariable(name).getIndex();
 681     }
 682 
 683     public LocalVariableGen getLocalVariable(String name) {
 684         return getLocalVariableRegistry().lookUpByName(name);
 685     }
 686 
 687     public void setMaxLocals() {
 688 
 689         // Get the current number of local variable slots
 690         int maxLocals = super.getMaxLocals();
 691         int prevLocals = maxLocals;
 692 
 693         // Get numer of actual variables
 694         final LocalVariableGen[] localVars = super.getLocalVariables();
 695         if (localVars != null) {
 696             if (localVars.length > maxLocals)
 697                 maxLocals = localVars.length;
 698         }
 699 
 700         // We want at least 5 local variable slots (for parameters)
 701         if (maxLocals < 5) maxLocals = 5;
 702 
 703         super.setMaxLocals(maxLocals);
 704     }
 705 
 706     /**
 707      * Add a pre-compiled pattern to this mode.
 708      */
 709     public void addInstructionList(Pattern pattern, InstructionList ilist) {
 710         _preCompiled.put(pattern, ilist);
 711     }
 712 
 713     /**
 714      * Get the instruction list for a pre-compiled pattern. Used by
 715      * test sequences to avoid compiling patterns more than once.
 716      */
 717     public InstructionList getInstructionList(Pattern pattern) {
 718         return _preCompiled.get(pattern);
 719     }
 720 
 721     /**
 722      * Used to keep track of an outlineable chunk of instructions in the
 723      * current method.  See {@link OutlineableChunkStart} and
 724      * {@link OutlineableChunkEnd} for more information.
 725      */
 726     private class Chunk implements Comparable<Object> {
 727         /**
 728          * {@link InstructionHandle} of the first instruction in the outlineable
 729          * chunk.
 730          */
 731         private InstructionHandle m_start;
 732 
 733         /**
 734          * {@link org.apache.bcel.generic.InstructionHandle} of the first
 735          * instruction in the outlineable chunk.
 736          */
 737         private InstructionHandle m_end;
 738 
 739         /**
 740          * Number of bytes in the instructions contained in this outlineable
 741          * chunk.
 742          */
 743         private int m_size;
 744 
 745         /**
 746          * <p>Constructor for an outlineable {@link MethodGenerator.Chunk}.</p>
 747          * <p><b>Preconditions:</b>
 748          * <ul>
 749          * <li>The {@link InstructionList#setPositions()} has been called for
 750          * the {@link InstructionList} associated with this
 751          * {@link MethodGenerator}.</li>
 752          * </ul></p>
 753          * @param start The {@link InstructionHandle} of the first
 754          *              instruction in the outlineable chunk.
 755          * @param end The {@link InstructionHandle} of the last
 756          *            instruction in the outlineable chunk.
 757          */
 758         Chunk(InstructionHandle start, InstructionHandle end) {
 759             m_start = start;
 760             m_end = end;
 761             m_size = end.getPosition() - start.getPosition();
 762         }
 763 
 764         /**
 765          * Determines whether this outlineable {@link MethodGenerator.Chunk} is
 766          * followed immediately by the argument
 767          * <code>MethodGenerator.Chunk</code>, with no other intervening
 768          * instructions, including {@link OutlineableChunkStart} or
 769          * {@link OutlineableChunkEnd} instructions.
 770          * @param neighbour an outlineable {@link MethodGenerator.Chunk}
 771          * @return <code>true</code> if and only if the argument chunk
 772          * immediately follows <code>this</code> chunk
 773          */
 774         boolean isAdjacentTo(Chunk neighbour) {
 775             return getChunkEnd().getNext() == neighbour.getChunkStart();
 776         }
 777 
 778         /**
 779          * Getter method for the start of this {@linke MethodGenerator.Chunk}
 780          * @return the {@link org.apache.bcel.generic.InstructionHandle} of the
 781          * start of this chunk
 782          */
 783         InstructionHandle getChunkStart() {
 784             return m_start;
 785         }
 786 
 787         /**
 788          * Getter method for the end of this {@link MethodGenerator.Chunk}
 789          * @return the {@link InstructionHandle} of the start of this chunk
 790          */
 791         InstructionHandle getChunkEnd() {
 792             return m_end;
 793         }
 794 
 795         /**
 796          * The size of this {@link MethodGenerator.Chunk}
 797          * @return the number of bytes in the byte code represented by this
 798          *         chunk.
 799          */
 800         int getChunkSize() {
 801             return m_size;
 802         }
 803 
 804         /**
 805          * Implements the <code>java.util.Comparable.compareTo(Object)</code>
 806          * method.
 807          * @return
 808          * <ul>
 809          * <li>A positive <code>int</code> if the length of <code>this</code>
 810          * chunk in bytes is greater than that of <code>comparand</code></li>
 811          * <li>A negative <code>int</code> if the length of <code>this</code>
 812          * chunk in bytes is less than that of <code>comparand</code></li>
 813          * <li>Zero, otherwise.</li>
 814          * </ul>
 815          */
 816         public int compareTo(Object comparand) {
 817             return getChunkSize() - ((Chunk)comparand).getChunkSize();
 818         }
 819     }
 820 
 821     /**
 822      * Find the outlineable chunks in this method that would be the best choices
 823      * to outline, based on size and position in the method.
 824      * @param classGen The {@link ClassGen} with which the generated methods
 825      *                 will be associated
 826      * @param totalMethodSize the size of the bytecode in the original method
 827      * @return a <code>java.util.List</code> containing the
 828      *  {@link MethodGenerator.Chunk}s that may be outlined from this method
 829      */
 830     private List<Chunk> getCandidateChunks(ClassGenerator classGen,
 831                                          int totalMethodSize) {
 832         Iterator<InstructionHandle> instructions = getInstructionList().iterator();
 833         List<Chunk> candidateChunks = new ArrayList<>();
 834         List<InstructionHandle> currLevelChunks = new ArrayList<>();
 835         Stack<List<InstructionHandle>> subChunkStack = new Stack<>();
 836         boolean openChunkAtCurrLevel = false;
 837         boolean firstInstruction = true;
 838 
 839         InstructionHandle currentHandle;
 840 
 841         if (m_openChunks != 0) {
 842             String msg =
 843                 (new ErrorMsg(ErrorMsg.OUTLINE_ERR_UNBALANCED_MARKERS))
 844                     .toString();
 845             throw new InternalError(msg);
 846         }
 847 
 848         // Scan instructions in the method, keeping track of the nesting level
 849         // of outlineable chunks.
 850         //
 851         // currLevelChunks
 852         //     keeps track of the child chunks of a chunk.  For each chunk,
 853         //     there will be a pair of entries:  the InstructionHandles for the
 854         //     start and for the end of the chunk
 855         // subChunkStack
 856         //     a stack containing the partially accumulated currLevelChunks for
 857         //     each chunk that's still open at the current position in the
 858         //     InstructionList.
 859         // candidateChunks
 860         //     the list of chunks which have been accepted as candidates chunks
 861         //     for outlining
 862         do {
 863             // Get the next instruction.  The loop will perform one extra
 864             // iteration after it reaches the end of the InstructionList, with
 865             // currentHandle set to null.
 866             currentHandle = instructions.hasNext()
 867                                     ? instructions.next()
 868                                     : null;
 869             Instruction inst =
 870                     (currentHandle != null) ? currentHandle.getInstruction()
 871                                             : null;
 872 
 873             // At the first iteration, create a chunk representing all the
 874             // code in the method.  This is done just to simplify the logic -
 875             // this chunk can never be outlined because it will be too big.
 876             if (firstInstruction) {
 877                 openChunkAtCurrLevel = true;
 878                 currLevelChunks.add(currentHandle);
 879                 firstInstruction = false;
 880             }
 881 
 882             // Found a new chunk
 883             if (inst instanceof OutlineableChunkStart) {
 884                 // If last MarkerInstruction encountered was an
 885                 // OutlineableChunkStart, this represents the first chunk
 886                 // nested within that previous chunk - push the list of chunks
 887                 // from the outer level onto the stack
 888                 if (openChunkAtCurrLevel) {
 889                     subChunkStack.push(currLevelChunks);
 890                     currLevelChunks = new ArrayList<>();
 891                 }
 892 
 893                 openChunkAtCurrLevel = true;
 894                 currLevelChunks.add(currentHandle);
 895             // Close off an open chunk
 896             } else if (currentHandle == null
 897                            || inst instanceof OutlineableChunkEnd) {
 898                 List<InstructionHandle> nestedSubChunks = null;
 899 
 900                 // If the last MarkerInstruction encountered was an
 901                 // OutlineableChunkEnd, it means that the current instruction
 902                 // marks the end of a chunk that contained child chunks.
 903                 // Those children might need to be examined below in case they
 904                 // are better candidates for outlining than the current chunk.
 905                 if (!openChunkAtCurrLevel) {
 906                     nestedSubChunks = currLevelChunks;
 907                     currLevelChunks = subChunkStack.pop();
 908                 }
 909 
 910                 // Get the handle for the start of this chunk (the last entry
 911                 // in currLevelChunks)
 912                 InstructionHandle chunkStart =
 913                         currLevelChunks.get(currLevelChunks.size()-1);
 914 
 915                 int chunkEndPosition =
 916                         (currentHandle != null) ? currentHandle.getPosition()
 917                                                 : totalMethodSize;
 918                 int chunkSize = chunkEndPosition - chunkStart.getPosition();
 919 
 920                 // Two ranges of chunk size to consider:
 921                 //
 922                 // 1. [0,TARGET_METHOD_SIZE]
 923                 //      Keep this chunk in consideration as a candidate,
 924                 //      and ignore its subchunks, if any - there's nothing to be
 925                 //      gained by outlining both the current chunk and its
 926                 //      children!
 927                 //
 928                 // 2. (TARGET_METHOD_SIZE,+infinity)
 929                 //      Ignore this chunk - it's too big.  Add its subchunks
 930                 //      as candidates, after merging adjacent chunks to produce
 931                 //      chunks that are as large as possible
 932                 if (chunkSize <= TARGET_METHOD_SIZE) {
 933                     currLevelChunks.add(currentHandle);
 934                 } else {
 935                     if (!openChunkAtCurrLevel) {
 936                         int childChunkCount = nestedSubChunks.size() / 2;
 937                         if (childChunkCount > 0) {
 938                             Chunk[] childChunks = new Chunk[childChunkCount];
 939 
 940                             // Gather all the child chunks of the current chunk
 941                             for (int i = 0; i < childChunkCount; i++) {
 942                                 InstructionHandle start = nestedSubChunks.get(i*2);
 943                                 InstructionHandle end = nestedSubChunks.get(i*2+1);
 944 
 945                                 childChunks[i] = new Chunk(start, end);
 946                             }
 947 
 948                             // Merge adjacent siblings
 949                             List<Chunk> mergedChildChunks =
 950                                         mergeAdjacentChunks(childChunks);
 951 
 952                             // Add chunks that mean minimum size requirements
 953                             // to the list of candidate chunks for outlining
 954                             for (Chunk mergedChunk : mergedChildChunks) {
 955                                 int mergedSize = mergedChunk.getChunkSize();
 956 
 957                                 if (mergedSize >= MINIMUM_OUTLINEABLE_CHUNK_SIZE
 958                                         && mergedSize <= TARGET_METHOD_SIZE) {
 959                                     candidateChunks.add(mergedChunk);
 960                                 }
 961                             }
 962                         }
 963                     }
 964 
 965                     // Drop the chunk which was too big
 966                     currLevelChunks.remove(currLevelChunks.size() - 1);
 967                 }
 968 
 969                 // currLevelChunks contains pairs of InstructionHandles.  If
 970                 // its size is an odd number, the loop has encountered the
 971                 // start of a chunk at this level, but not its end.
 972                 openChunkAtCurrLevel = ((currLevelChunks.size() & 0x1) == 1);
 973             }
 974 
 975         } while (currentHandle != null);
 976 
 977         return candidateChunks;
 978     }
 979 
 980     /**
 981      * Merge adjacent sibling chunks to produce larger candidate chunks for
 982      * outlining
 983      * @param chunks array of sibling {@link MethodGenerator.Chunk}s that are
 984      *               under consideration for outlining.  Chunks must be in
 985      *               the order encountered in the {@link InstructionList}
 986      * @return a <code>java.util.List</code> of
 987      *         <code>MethodGenerator.Chunk</code>s maximally merged
 988      */
 989     private List<Chunk> mergeAdjacentChunks(Chunk[] chunks) {
 990         int[] adjacencyRunStart = new int[chunks.length];
 991         int[] adjacencyRunLength = new int[chunks.length];
 992         boolean[] chunkWasMerged = new boolean[chunks.length];
 993 
 994         int maximumRunOfChunks = 0;
 995         int startOfCurrentRun;
 996         int numAdjacentRuns = 0;
 997 
 998         List<Chunk> mergedChunks = new ArrayList<>();
 999 
1000         startOfCurrentRun = 0;
1001 
1002         // Loop through chunks, and record in adjacencyRunStart where each
1003         // run of adjacent chunks begins and how many are in that run.  For
1004         // example, given chunks A B C D E F, if A is adjacent to B, but not
1005         // to C, and C, D, E and F are all adjacent,
1006         //   adjacencyRunStart[0] == 0; adjacencyRunLength[0] == 2
1007         //   adjacencyRunStart[1] == 2; adjacencyRunLength[1] == 4
1008         for (int i = 1; i < chunks.length; i++) {
1009             if (!chunks[i-1].isAdjacentTo(chunks[i])) {
1010                 int lengthOfRun = i - startOfCurrentRun;
1011 
1012                 // Track the longest run of chunks found
1013                 if (maximumRunOfChunks < lengthOfRun) {
1014                     maximumRunOfChunks = lengthOfRun;
1015                 }
1016 
1017                 if (lengthOfRun > 1 ) {
1018                     adjacencyRunLength[numAdjacentRuns] = lengthOfRun;
1019                     adjacencyRunStart[numAdjacentRuns] = startOfCurrentRun;
1020                     numAdjacentRuns++;
1021                 }
1022 
1023                 startOfCurrentRun = i;
1024             }
1025         }
1026 
1027         if (chunks.length - startOfCurrentRun > 1) {
1028             int lengthOfRun = chunks.length - startOfCurrentRun;
1029 
1030             // Track the longest run of chunks found
1031             if (maximumRunOfChunks < lengthOfRun) {
1032                 maximumRunOfChunks = lengthOfRun;
1033             }
1034 
1035             adjacencyRunLength[numAdjacentRuns] =
1036                         chunks.length - startOfCurrentRun;
1037             adjacencyRunStart[numAdjacentRuns] = startOfCurrentRun;
1038             numAdjacentRuns++;
1039         }
1040 
1041         // Try merging adjacent chunks to come up with better sized chunks for
1042         // outlining.  This algorithm is not optimal, but it should be
1043         // reasonably fast.  Consider an example like this, where four chunks
1044         // of the sizes specified in brackets are adjacent.  The best way of
1045         // combining these chunks would be to merge the first pair and merge
1046         // the last three to form two chunks, but the algorithm will merge the
1047         // three in the middle instead, leaving three chunks in all.
1048         //    [25000] [25000] [20000] [1000] [20000]
1049 
1050         // Start by trying to merge the maximum number of adjacent chunks, and
1051         // work down from there.
1052         for (int numToMerge = maximumRunOfChunks; numToMerge>1; numToMerge--) {
1053             // Look at each run of adjacent chunks
1054             for (int run = 0; run < numAdjacentRuns; run++) {
1055                 int runStart = adjacencyRunStart[run];
1056                 int runEnd = runStart + adjacencyRunLength[run] - 1;
1057 
1058                 boolean foundChunksToMerge = false;
1059 
1060                 // Within the current run of adjacent chunks, look at all
1061                 // "subruns" of length numToMerge, until we run out or find
1062                 // a subrun that can be merged.
1063                 for (int mergeStart = runStart;
1064                      mergeStart+numToMerge-1 <= runEnd && !foundChunksToMerge;
1065                      mergeStart++) {
1066                     int mergeEnd = mergeStart + numToMerge - 1;
1067                     int mergeSize = 0;
1068 
1069                     // Find out how big the subrun is
1070                     for (int j = mergeStart; j <= mergeEnd; j++) {
1071                         mergeSize = mergeSize + chunks[j].getChunkSize();
1072                     }
1073 
1074                     // If the current subrun is small enough to outline,
1075                     // merge it, and split the remaining chunks in the run
1076                     if (mergeSize <= TARGET_METHOD_SIZE) {
1077                         foundChunksToMerge = true;
1078 
1079                         for (int j = mergeStart; j <= mergeEnd; j++) {
1080                             chunkWasMerged[j] = true;
1081                         }
1082 
1083                         mergedChunks.add(
1084                                 new Chunk(chunks[mergeStart].getChunkStart(),
1085                                           chunks[mergeEnd].getChunkEnd()));
1086 
1087                         // Adjust the length of the current run of adjacent
1088                         // chunks to end at the newly merged chunk...
1089                         adjacencyRunLength[run] =
1090                                 adjacencyRunStart[run] - mergeStart;
1091 
1092                         int trailingRunLength = runEnd - mergeEnd;
1093 
1094                         // and any chunks that follow the newly merged chunk
1095                         // in the current run of adjacent chunks form another
1096                         // new run of adjacent chunks
1097                         if (trailingRunLength >= 2) {
1098                             adjacencyRunStart[numAdjacentRuns] = mergeEnd + 1;
1099                             adjacencyRunLength[numAdjacentRuns] =
1100                                                             trailingRunLength;
1101                             numAdjacentRuns++;
1102                         }
1103                     }
1104                 }
1105             }
1106         }
1107 
1108         // Make a final pass for any chunk that wasn't merged with a sibling
1109         // and include it in the list of chunks after merging.
1110         for (int i = 0; i < chunks.length; i++) {
1111             if (!chunkWasMerged[i]) {
1112                 mergedChunks.add(chunks[i]);
1113             }
1114         }
1115 
1116         return mergedChunks;
1117     }
1118 
1119     /**
1120      * Breaks up the IL for this {@link MethodGenerator} into separate
1121      * outlined methods so that no method exceeds the 64KB limit on the length
1122      * of the byte code associated with a method.
1123      * @param classGen The {@link ClassGen} with which the generated methods
1124      *                 will be associated
1125      * @param originalMethodSize The number of bytes of bytecode represented by
1126      *                 the {@link InstructionList} of this method
1127      * @return an array of the outlined <code>Method</code>s and the original
1128      *         method itself
1129      */
1130     public Method[] outlineChunks(ClassGenerator classGen,
1131                                   int originalMethodSize) {
1132         List<Method> methodsOutlined = new ArrayList<>();
1133         int currentMethodSize = originalMethodSize;
1134 
1135         int outlinedCount = 0;
1136         boolean moreMethodsOutlined;
1137         String originalMethodName = getName();
1138 
1139         // Special handling for initialization methods.  No other methods can
1140         // include the less than and greater than characters in their names,
1141         // so we munge the names here.
1142         if (originalMethodName.equals("<init>")) {
1143             originalMethodName = "$lt$init$gt$";
1144         } else if (originalMethodName.equals("<clinit>")) {
1145             originalMethodName = "$lt$clinit$gt$";
1146         }
1147 
1148         // Loop until the original method comes in under the JVM limit or
1149         // the loop was unable to outline any more methods
1150         do {
1151             // Get all the best candidates for outlining, and sort them in
1152             // ascending order of size
1153             List<Chunk> candidateChunks = getCandidateChunks(classGen,
1154                                                            currentMethodSize);
1155             Collections.sort(candidateChunks);
1156 
1157             moreMethodsOutlined = false;
1158 
1159             // Loop over the candidates for outlining, from the largest to the
1160             // smallest and outline them one at a time, until the loop has
1161             // outlined all or the original method comes in under the JVM
1162             // limit on the size of a method.
1163             for (int i = candidateChunks.size()-1;
1164                  i >= 0 && currentMethodSize > TARGET_METHOD_SIZE;
1165                  i--) {
1166                 Chunk chunkToOutline = candidateChunks.get(i);
1167 
1168                 methodsOutlined.add(outline(chunkToOutline.getChunkStart(),
1169                                             chunkToOutline.getChunkEnd(),
1170                                             originalMethodName + "$outline$"
1171                                                                + outlinedCount,
1172                                             classGen));
1173                 outlinedCount++;
1174                 moreMethodsOutlined = true;
1175 
1176                 InstructionList il = getInstructionList();
1177                 InstructionHandle lastInst = il.getEnd();
1178                 il.setPositions();
1179 
1180                 // Check the size of the method now
1181                 currentMethodSize =
1182                         lastInst.getPosition()
1183                                 + lastInst.getInstruction().getLength();
1184             }
1185         } while (moreMethodsOutlined && currentMethodSize > TARGET_METHOD_SIZE);
1186 
1187         // Outlining failed to reduce the size of the current method
1188         // sufficiently.  Throw an internal error.
1189         if (currentMethodSize > MAX_METHOD_SIZE) {
1190             String msg = (new ErrorMsg(ErrorMsg.OUTLINE_ERR_METHOD_TOO_BIG))
1191                                   .toString();
1192             throw new InternalError(msg);
1193         }
1194 
1195         Method[] methodsArr = new Method[methodsOutlined.size() + 1];
1196         methodsOutlined.toArray(methodsArr);
1197 
1198         methodsArr[methodsOutlined.size()] = getThisMethod();
1199 
1200         return methodsArr;
1201     }
1202 
1203     /**
1204      * Given an outlineable chunk of code in the current {@link MethodGenerator}
1205      * move ("outline") the chunk to a new method, and replace the chunk in the
1206      * old method with a reference to that new method.  No
1207      * {@link OutlineableChunkStart} or {@link OutlineableChunkEnd} instructions
1208      * are copied.
1209      * @param first The {@link InstructionHandle} of the first instruction in
1210      *              the chunk to outline
1211      * @param last The <code>InstructionHandle</code> of the last instruction in
1212      *             the chunk to outline
1213      * @param outlinedMethodName The name of the new method
1214      * @param classGen The {@link ClassGenerator} of which the original
1215      *              and new methods will be members
1216      * @return The new {@link Method} containing the outlined code.
1217      */
1218     private Method outline(InstructionHandle first, InstructionHandle last,
1219                            String outlinedMethodName, ClassGenerator classGen) {
1220         // We're not equipped to deal with exception handlers yet.  Bail out!
1221         if (getExceptionHandlers().length != 0) {
1222             String msg = (new ErrorMsg(ErrorMsg.OUTLINE_ERR_TRY_CATCH))
1223                                   .toString();
1224             throw new InternalError(msg);
1225         }
1226 
1227         int outlineChunkStartOffset = first.getPosition();
1228         int outlineChunkEndOffset = last.getPosition()
1229                                         + last.getInstruction().getLength();
1230 
1231         ConstantPoolGen cpg = getConstantPool();
1232 
1233         // Create new outlined method with signature:
1234         //
1235         //   private final outlinedMethodName(CopyLocals copyLocals);
1236         //
1237         // CopyLocals is an object that is used to copy-in/copy-out local
1238         // variables that are used by the outlined method.   Only locals whose
1239         // value is potentially set or referenced outside the range of the
1240         // chunk that is being outlined will be represented in CopyLocals.  The
1241         // type of the variable for copying local variables is actually
1242         // generated to be unique - it is not named CopyLocals.
1243         //
1244         // The outlined method never needs to be referenced outside of this
1245         // class, and will never be overridden, so we mark it private final.
1246         final InstructionList newIL = new InstructionList();
1247 
1248         final XSLTC  xsltc = classGen.getParser().getXSLTC();
1249         final String argTypeName = xsltc.getHelperClassName();
1250         final Type[] argTypes =
1251             new Type[] {(new ObjectType(argTypeName)).toJCType()};
1252         final String argName = "copyLocals";
1253         final String[] argNames = new String[] {argName};
1254 
1255         int methodAttributes = ACC_PRIVATE | ACC_FINAL;
1256         final boolean isStaticMethod = (getAccessFlags() & ACC_STATIC) != 0;
1257 
1258         if (isStaticMethod) {
1259             methodAttributes = methodAttributes | ACC_STATIC;
1260         }
1261 
1262         final MethodGenerator outlinedMethodGen =
1263             new MethodGenerator(methodAttributes,
1264                                 com.sun.org.apache.bcel.internal.generic.Type.VOID,
1265                                 argTypes, argNames, outlinedMethodName,
1266                                 getClassName(), newIL, cpg);
1267 
1268         // Create class for copying local variables to the outlined method.
1269         // The fields the class will need to contain will be determined as the
1270         // code in the outlineable chunk is examined.
1271         ClassGenerator copyAreaCG
1272             = new ClassGenerator(argTypeName, OBJECT_CLASS, argTypeName+".java",
1273                                  ACC_FINAL | ACC_PUBLIC | ACC_SUPER, null,
1274                                  classGen.getStylesheet()) {
1275                       public boolean isExternal() {
1276                           return true;
1277                       }
1278                   };
1279         ConstantPoolGen copyAreaCPG = copyAreaCG.getConstantPool();
1280         copyAreaCG.addEmptyConstructor(ACC_PUBLIC);
1281 
1282         // Number of fields in the copy class
1283         int copyAreaFieldCount = 0;
1284 
1285         // The handle for the instruction after the last one to be outlined.
1286         // Note that this should never end up being null.  An outlineable chunk
1287         // won't contain a RETURN instruction or other branch out of the chunk,
1288         // and the JVM specification prohibits code in a method from just
1289         // "falling off the end" so this should always point to a valid handle.
1290         InstructionHandle limit = last.getNext();
1291 
1292         // InstructionLists for copying values into and out of an instance of
1293         // CopyLocals:
1294         //      oldMethCoypInIL  - from locals in old method into an instance
1295         //                         of the CopyLocals class (oldMethCopyInIL)
1296         //      oldMethCopyOutIL - from CopyLocals back into locals in the old
1297         //                         method
1298         //      newMethCopyInIL  - from CopyLocals into locals in the new
1299         //                         method
1300         //      newMethCopyOutIL - from locals in new method into the instance
1301         //                         of the CopyLocals class
1302         InstructionList oldMethCopyInIL  = new InstructionList();
1303         InstructionList oldMethCopyOutIL = new InstructionList();
1304         InstructionList newMethCopyInIL  = new InstructionList();
1305         InstructionList newMethCopyOutIL = new InstructionList();
1306 
1307         // Allocate instance of class in which we'll copy in or copy out locals
1308         // and make two copies:  last copy is used to invoke constructor;
1309         // other two are used for references to fields in the CopyLocals object
1310         InstructionHandle outlinedMethodCallSetup =
1311             oldMethCopyInIL.append(new NEW(cpg.addClass(argTypeName)));
1312         oldMethCopyInIL.append(InstructionConst.DUP);
1313         oldMethCopyInIL.append(InstructionConst.DUP);
1314         oldMethCopyInIL.append(
1315             new INVOKESPECIAL(cpg.addMethodref(argTypeName, "<init>", "()V")));
1316 
1317         // Generate code to invoke the new outlined method, and place the code
1318         // on oldMethCopyOutIL
1319         InstructionHandle outlinedMethodRef;
1320 
1321         if (isStaticMethod) {
1322             outlinedMethodRef =
1323                 oldMethCopyOutIL.append(
1324                     new INVOKESTATIC(cpg.addMethodref(
1325                                           classGen.getClassName(),
1326                                           outlinedMethodName,
1327                                           outlinedMethodGen.getSignature())));
1328         } else {
1329             oldMethCopyOutIL.append(InstructionConst.THIS);
1330             oldMethCopyOutIL.append(InstructionConst.SWAP);
1331             outlinedMethodRef =
1332                 oldMethCopyOutIL.append(
1333                     new INVOKEVIRTUAL(cpg.addMethodref(
1334                                           classGen.getClassName(),
1335                                           outlinedMethodName,
1336                                           outlinedMethodGen.getSignature())));
1337         }
1338 
1339         // Used to keep track of the first in a sequence of
1340         // OutlineableChunkStart instructions
1341         boolean chunkStartTargetMappingsPending = false;
1342         InstructionHandle pendingTargetMappingHandle = null;
1343 
1344         // Used to keep track of the last instruction that was copied
1345         InstructionHandle lastCopyHandle = null;
1346 
1347         // Keeps track of the mapping from instruction handles in the old
1348         // method to instruction handles in the outlined method.  Only need
1349         // to track instructions that are targeted by something else in the
1350         // generated BCEL
1351         HashMap<InstructionHandle, InstructionHandle> targetMap = new HashMap<>();
1352 
1353         // Keeps track of the mapping from local variables in the old method
1354         // to local variables in the outlined method.
1355         HashMap<LocalVariableGen, LocalVariableGen> localVarMap = new HashMap<>();
1356 
1357         HashMap<LocalVariableGen, InstructionHandle> revisedLocalVarStart = new HashMap<>();
1358         HashMap<LocalVariableGen, InstructionHandle> revisedLocalVarEnd = new HashMap<>();
1359 
1360         // Pass 1: Make copies of all instructions, append them to the new list
1361         // and associate old instruction references with the new ones, i.e.,
1362         // a 1:1 mapping.  The special marker instructions are not copied.
1363         // Also, identify local variables whose values need to be copied into or
1364         // out of the new outlined method, and builds up targetMap and
1365         // localVarMap as described above.  The code identifies those local
1366         // variables first so that they can have fixed slots in the stack
1367         // frame for the outlined method assigned them ahead of all those
1368         // variables that don't need to exist for the entirety of the outlined
1369         // method invocation.
1370         for (InstructionHandle ih = first; ih != limit; ih = ih.getNext()) {
1371             Instruction inst = ih.getInstruction();
1372 
1373             // MarkerInstructions are not copied, so if something else targets
1374             // one, the targetMap will point to the nearest copied sibling
1375             // InstructionHandle:  for an OutlineableChunkEnd, the nearest
1376             // preceding sibling; for an OutlineableChunkStart, the nearest
1377             // following sibling.
1378             if (inst instanceof MarkerInstruction) {
1379                 if (ih.hasTargeters()) {
1380                     if (inst instanceof OutlineableChunkEnd) {
1381                         targetMap.put(ih, lastCopyHandle);
1382                     } else {
1383                         if (!chunkStartTargetMappingsPending)  {
1384                             chunkStartTargetMappingsPending = true;
1385                             pendingTargetMappingHandle = ih;
1386                         }
1387                     }
1388                 }
1389             } else {
1390                 // Copy the instruction and append it to the outlined method's
1391                 // InstructionList.
1392                 Instruction c = inst.copy(); // Use clone for shallow copy
1393 
1394                 if (c instanceof BranchInstruction) {
1395                     lastCopyHandle = newIL.append((BranchInstruction)c);
1396                 } else {
1397                     lastCopyHandle = newIL.append(c);
1398                 }
1399 
1400                 if (c instanceof LocalVariableInstruction
1401                         || c instanceof RET) {
1402                     // For any instruction that touches a local variable,
1403                     // check whether the local variable's value needs to be
1404                     // copied into or out of the outlined method.  If so,
1405                     // generate the code to perform the necessary copying, and
1406                     // use localVarMap to map the variable in the original
1407                     // method to the variable in the new method.
1408                     IndexedInstruction lvi = (IndexedInstruction)c;
1409                     int oldLocalVarIndex = lvi.getIndex();
1410                     LocalVariableGen oldLVG =
1411                             getLocalVariableRegistry()
1412                                 .lookupRegisteredLocalVariable(oldLocalVarIndex,
1413                                                               ih.getPosition());
1414                     LocalVariableGen newLVG = localVarMap.get(oldLVG);
1415 
1416                     // Has the code already mapped this local variable to a
1417                     // local in the new method?
1418                     if (localVarMap.get(oldLVG) == null) {
1419                         // Determine whether the local variable needs to be
1420                         // copied into or out of the outlined by checking
1421                         // whether the range of instructions in which the
1422                         // variable is accessible is outside the range of
1423                         // instructions in the outlineable chunk.
1424                         // Special case a chunk start offset of zero:  a local
1425                         // variable live at that position must be a method
1426                         // parameter, so the code doesn't need to check whether
1427                         // the variable is live before that point; being live
1428                         // at offset zero is sufficient to know that the value
1429                         // must be copied in to the outlined method.
1430                         boolean copyInLocalValue =
1431                             offsetInLocalVariableGenRange(oldLVG,
1432                                                 (outlineChunkStartOffset != 0)
1433                                                     ? outlineChunkStartOffset-1
1434                                                     : 0);
1435                         boolean copyOutLocalValue =
1436                             offsetInLocalVariableGenRange(oldLVG,
1437                                                 outlineChunkEndOffset+1);
1438 
1439                         // For any variable that needs to be copied into or out
1440                         // of the outlined method, create a field in the
1441                         // CopyLocals class, and generate the necessary code for
1442                         // copying the value.
1443                         if (copyInLocalValue || copyOutLocalValue) {
1444                             String varName = oldLVG.getName();
1445                             Type varType = oldLVG.getType();
1446                             newLVG = outlinedMethodGen.addLocalVariable(varName,
1447                                                                         varType,
1448                                                                         null,
1449                                                                         null);
1450                             int newLocalVarIndex = newLVG.getIndex();
1451                             String varSignature = varType.getSignature();
1452 
1453                             // Record the mapping from the old local to the new
1454                             localVarMap.put(oldLVG, newLVG);
1455 
1456                             copyAreaFieldCount++;
1457                             String copyAreaFieldName =
1458                                            "field" + copyAreaFieldCount;
1459                             copyAreaCG.addField(
1460                                 new Field(ACC_PUBLIC,
1461                                         copyAreaCPG.addUtf8(copyAreaFieldName),
1462                                         copyAreaCPG.addUtf8(varSignature),
1463                                         null, copyAreaCPG.getConstantPool()));
1464 
1465                             int fieldRef = cpg.addFieldref(argTypeName,
1466                                                            copyAreaFieldName,
1467                                                            varSignature);
1468 
1469                             if (copyInLocalValue) {
1470                                 // Generate code for the old method to store the
1471                                 // value of the local into the correct field in
1472                                 // CopyLocals prior to invocation of the
1473                                 // outlined method.
1474                                 oldMethCopyInIL.append(
1475                                         InstructionConst.DUP);
1476                                 InstructionHandle copyInLoad =
1477                                     oldMethCopyInIL.append(
1478                                         loadLocal(oldLocalVarIndex, varType));
1479                                 oldMethCopyInIL.append(new PUTFIELD(fieldRef));
1480 
1481                                 // If the end of the live range of the old
1482                                 // variable was in the middle of the outlined
1483                                 // chunk.  Make the load of its value the new
1484                                 // end of its range.
1485                                 if (!copyOutLocalValue) {
1486                                     revisedLocalVarEnd.put(oldLVG, copyInLoad);
1487                                 }
1488 
1489                                 // Generate code for start of the outlined
1490                                 // method to copy the value from a field in
1491                                 // CopyLocals to the new local in the outlined
1492                                 // method
1493                                 newMethCopyInIL.append(
1494                                         InstructionConst.ALOAD_1);
1495                                 newMethCopyInIL.append(new GETFIELD(fieldRef));
1496                                 newMethCopyInIL.append(
1497                                         storeLocal(newLocalVarIndex, varType));
1498                             }
1499 
1500                             if (copyOutLocalValue) {
1501                                 // Generate code for the end of the outlined
1502                                 // method to copy the value from the new local
1503                                 // variable into a field in CopyLocals
1504                                 // method
1505                                 newMethCopyOutIL.append(
1506                                         InstructionConst.ALOAD_1);
1507                                 newMethCopyOutIL.append(
1508                                         loadLocal(newLocalVarIndex, varType));
1509                                 newMethCopyOutIL.append(new PUTFIELD(fieldRef));
1510 
1511                                 // Generate code to copy the value from a field
1512                                 // in CopyLocals into a local in the original
1513                                 // method following invocation of the outlined
1514                                 // method.
1515                                 oldMethCopyOutIL.append(
1516                                         InstructionConst.DUP);
1517                                 oldMethCopyOutIL.append(new GETFIELD(fieldRef));
1518                                 InstructionHandle copyOutStore =
1519                                     oldMethCopyOutIL.append(
1520                                         storeLocal(oldLocalVarIndex, varType));
1521 
1522                                 // If the start of the live range of the old
1523                                 // variable was in the middle of the outlined
1524                                 // chunk.  Make this store into it the new start
1525                                 // of its range.
1526                                 if (!copyInLocalValue) {
1527                                     revisedLocalVarStart.put(oldLVG,
1528                                                              copyOutStore);
1529                                 }
1530                             }
1531                         }
1532                     }
1533                 }
1534 
1535                 if (ih.hasTargeters()) {
1536                     targetMap.put(ih, lastCopyHandle);
1537                 }
1538 
1539                 // If this is the first instruction copied following a sequence
1540                 // of OutlineableChunkStart instructions, indicate that the
1541                 // sequence of old instruction all map to this newly created
1542                 // instruction
1543                 if (chunkStartTargetMappingsPending) {
1544                     do {
1545                          targetMap.put(pendingTargetMappingHandle,
1546                                        lastCopyHandle);
1547                          pendingTargetMappingHandle =
1548                                  pendingTargetMappingHandle.getNext();
1549                     } while(pendingTargetMappingHandle != ih);
1550 
1551                     chunkStartTargetMappingsPending = false;
1552                 }
1553             }
1554         }
1555 
1556         // Pass 2: Walk old and new instruction lists, updating branch targets
1557         // and local variable references in the new list
1558         InstructionHandle ih = first;
1559         InstructionHandle ch = newIL.getStart();
1560 
1561         while (ch != null) {
1562             // i == old instruction; c == copied instruction
1563             Instruction i = ih.getInstruction();
1564             Instruction c = ch.getInstruction();
1565 
1566             if (i instanceof BranchInstruction) {
1567                 BranchInstruction bc      = (BranchInstruction)c;
1568                 BranchInstruction bi      = (BranchInstruction)i;
1569                 InstructionHandle itarget = bi.getTarget(); // old target
1570 
1571                 // New target must be in targetMap
1572                 InstructionHandle newTarget = targetMap.get(itarget);
1573 
1574                 bc.setTarget(newTarget);
1575 
1576                 // Handle LOOKUPSWITCH or TABLESWITCH which may have many
1577                 // target instructions
1578                 if (bi instanceof Select) {
1579                     InstructionHandle[] itargets = ((Select)bi).getTargets();
1580                     InstructionHandle[] ctargets = ((Select)bc).getTargets();
1581 
1582                     // Update all targets
1583                     for (int j=0; j < itargets.length; j++) {
1584                         ctargets[j] = targetMap.get(itargets[j]);
1585                     }
1586                 }
1587             }  else if (i instanceof LocalVariableInstruction
1588                             || i instanceof RET) {
1589                 // For any instruction that touches a local variable,
1590                 // map the location of the variable in the original
1591                 // method to its location in the new method.
1592                 IndexedInstruction lvi = (IndexedInstruction)c;
1593                 int oldLocalVarIndex = lvi.getIndex();
1594                 LocalVariableGen oldLVG =
1595                         getLocalVariableRegistry()
1596                                 .lookupRegisteredLocalVariable(oldLocalVarIndex,
1597                                                               ih.getPosition());
1598                 LocalVariableGen newLVG = localVarMap.get(oldLVG);
1599                 int newLocalVarIndex;
1600 
1601                 if (newLVG == null) {
1602                     // Create new variable based on old variable - use same
1603                     // name and type, but we will let the variable be active
1604                     // for the entire outlined method.
1605                     // LocalVariableGen oldLocal = oldLocals[oldLocalVarIndex];
1606                     String varName = oldLVG.getName();
1607                     Type varType = oldLVG.getType();
1608                     newLVG = outlinedMethodGen.addLocalVariable(varName,
1609                                                                 varType,
1610                                                                 null,
1611                                                                 null);
1612                     newLocalVarIndex = newLVG.getIndex();
1613                     localVarMap.put(oldLVG, newLVG);
1614 
1615                     // The old variable's live range was wholly contained in
1616                     // the outlined chunk.  There should no longer be stores
1617                     // of values into it or loads of its value, so we can just
1618                     // mark its live range as the reference to the outlined
1619                     // method.
1620                     revisedLocalVarStart.put(oldLVG, outlinedMethodRef);
1621                     revisedLocalVarEnd.put(oldLVG, outlinedMethodRef);
1622                 } else {
1623                     newLocalVarIndex = newLVG.getIndex();
1624                 }
1625                 lvi.setIndex(newLocalVarIndex);
1626             }
1627 
1628             // If the old instruction marks the end of the range of a local
1629             // variable, make sure that any slots on the stack reserved for
1630             // local variables are made available for reuse by calling
1631             // MethodGenerator.removeLocalVariable
1632             if (ih.hasTargeters()) {
1633                 InstructionTargeter[] targeters = ih.getTargeters();
1634 
1635                 for (int idx = 0; idx < targeters.length; idx++) {
1636                     InstructionTargeter targeter = targeters[idx];
1637 
1638                     if (targeter instanceof LocalVariableGen
1639                             && ((LocalVariableGen)targeter).getEnd()==ih) {
1640                         LocalVariableGen newLVG = localVarMap.get(targeter);
1641                         if (newLVG != null) {
1642                             outlinedMethodGen.removeLocalVariable(newLVG);
1643                         }
1644                     }
1645                 }
1646             }
1647 
1648             // If the current instruction in the original list was a marker,
1649             // it wasn't copied, so don't advance through the list of copied
1650             // instructions yet.
1651             if (!(i instanceof MarkerInstruction)) {
1652                 ch = ch.getNext();
1653             }
1654             ih = ih.getNext();
1655 
1656         }
1657 
1658         // POP the reference to the CopyLocals object from the stack
1659         oldMethCopyOutIL.append(InstructionConst.POP);
1660 
1661         for (Map.Entry<LocalVariableGen, InstructionHandle> lvgRangeStartPair :
1662                 revisedLocalVarStart.entrySet()) {
1663             LocalVariableGen lvg = lvgRangeStartPair.getKey();
1664             InstructionHandle startInst = lvgRangeStartPair.getValue();
1665 
1666             lvg.setStart(startInst);
1667         }
1668 
1669         for (Map.Entry<LocalVariableGen, InstructionHandle> lvgRangeEndPair :
1670                 revisedLocalVarEnd.entrySet()) {
1671             LocalVariableGen lvg = lvgRangeEndPair.getKey();
1672             InstructionHandle endInst = lvgRangeEndPair.getValue();
1673 
1674             lvg.setEnd(endInst);
1675         }
1676 
1677         xsltc.dumpClass(copyAreaCG.getJavaClass());
1678 
1679         // Assemble the instruction lists so that the old method invokes the
1680         // new outlined method
1681         InstructionList oldMethodIL = getInstructionList();
1682 
1683         oldMethodIL.insert(first, oldMethCopyInIL);
1684         oldMethodIL.insert(first, oldMethCopyOutIL);
1685 
1686         // Insert the copying code into the outlined method
1687         newIL.insert(newMethCopyInIL);
1688         newIL.append(newMethCopyOutIL);
1689         newIL.append(InstructionConst.RETURN);
1690 
1691         // Discard instructions in outlineable chunk from old method
1692         try {
1693             oldMethodIL.delete(first, last);
1694         } catch (TargetLostException e) {
1695             InstructionHandle[] targets = e.getTargets();
1696             // If there were still references to old instructions lingering,
1697             // clean those up.  The only instructions targetting the deleted
1698             // instructions should have been part of the chunk that was just
1699             // deleted, except that instructions might branch to the start of
1700             // the outlined chunk; similarly, all the live ranges of local
1701             // variables should have been adjusted, except for unreferenced
1702             // variables.
1703             for (int i = 0; i < targets.length; i++) {
1704                 InstructionHandle lostTarget = targets[i];
1705                 InstructionTargeter[] targeters = lostTarget.getTargeters();
1706                 for (int j = 0; j < targeters.length; j++) {
1707                     if (targeters[j] instanceof LocalVariableGen) {
1708                         LocalVariableGen lvgTargeter =
1709                                              (LocalVariableGen) targeters[j];
1710                         // In the case of any lingering variable references,
1711                         // just make the live range point to the outlined
1712                         // function reference.  Such variables should be unused
1713                         // anyway.
1714                         if (lvgTargeter.getStart() == lostTarget) {
1715                             lvgTargeter.setStart(outlinedMethodRef);
1716                         }
1717                         if (lvgTargeter.getEnd() == lostTarget) {
1718                             lvgTargeter.setEnd(outlinedMethodRef);
1719                         }
1720                     } else {
1721                         targeters[j].updateTarget(lostTarget,
1722                                                   outlinedMethodCallSetup);
1723                     }
1724                 }
1725             }
1726         }
1727 
1728         // Make a copy for the new method of all exceptions that might be thrown
1729         String[] exceptions = getExceptions();
1730         for (int i = 0; i < exceptions.length; i++) {
1731             outlinedMethodGen.addException(exceptions[i]);
1732         }
1733 
1734         return outlinedMethodGen.getThisMethod();
1735     }
1736 
1737     /**
1738      * Helper method to generate an instance of a subclass of
1739      * {@link LoadInstruction} based on the specified {@link Type} that will
1740      * load the specified local variable
1741      * @param index the JVM stack frame index of the variable that is to be
1742      * loaded
1743      * @param type the {@link Type} of the variable
1744      * @return the generated {@link LoadInstruction}
1745      */
1746     private static Instruction loadLocal(int index, Type type) {
1747         if (type == Type.BOOLEAN) {
1748            return new ILOAD(index);
1749         } else if (type == Type.INT) {
1750            return new ILOAD(index);
1751         } else if (type == Type.SHORT) {
1752            return new ILOAD(index);
1753         } else if (type == Type.LONG) {
1754            return new LLOAD(index);
1755         } else if (type == Type.BYTE) {
1756            return new ILOAD(index);
1757         } else if (type == Type.CHAR) {
1758            return new ILOAD(index);
1759         } else if (type == Type.FLOAT) {
1760            return new FLOAD(index);
1761         } else if (type == Type.DOUBLE) {
1762            return new DLOAD(index);
1763         } else {
1764            return new ALOAD(index);
1765         }
1766     }
1767 
1768     /**
1769      * Helper method to generate an instance of a subclass of
1770      * {@link StoreInstruction} based on the specified {@link Type} that will
1771      * store a value in the specified local variable
1772      * @param index the JVM stack frame index of the variable that is to be
1773      * stored
1774      * @param type the {@link Type} of the variable
1775      * @return the generated {@link StoredInstruction}
1776      */
1777     private static Instruction storeLocal(int index, Type type) {
1778         if (type == Type.BOOLEAN) {
1779            return new ISTORE(index);
1780         } else if (type == Type.INT) {
1781            return new ISTORE(index);
1782         } else if (type == Type.SHORT) {
1783            return new ISTORE(index);
1784         } else if (type == Type.LONG) {
1785            return new LSTORE(index);
1786         } else if (type == Type.BYTE) {
1787            return new ISTORE(index);
1788         } else if (type == Type.CHAR) {
1789            return new ISTORE(index);
1790         } else if (type == Type.FLOAT) {
1791            return new FSTORE(index);
1792         } else if (type == Type.DOUBLE) {
1793            return new DSTORE(index);
1794         } else {
1795            return new ASTORE(index);
1796         }
1797     }
1798 
1799     /**
1800      * Track the number of outlineable chunks seen.
1801      */
1802     private int m_totalChunks = 0;
1803 
1804     /**
1805      * Track the number of outlineable chunks started but not yet ended.  Used
1806      * to detect imbalances in byte code generation.
1807      */
1808     private int m_openChunks = 0;
1809 
1810     /**
1811      * Mark the end of the method's
1812      * {@link InstructionList} as the start of an outlineable chunk of code.
1813      * The outlineable chunk begins after the {@link InstructionHandle} that is
1814      * at the end of the method's {@link InstructionList}, or at the start of
1815      * the method if the <code>InstructionList</code> is empty.
1816      * See {@link OutlineableChunkStart} for more information.
1817      */
1818     public void markChunkStart() {
1819         // m_chunkTree.markChunkStart();
1820         getInstructionList()
1821                 .append(OutlineableChunkStart.OUTLINEABLECHUNKSTART);
1822         m_totalChunks++;
1823         m_openChunks++;
1824     }
1825 
1826     /**
1827      * Mark the end of an outlineable chunk of code.  See
1828      * {@link OutlineableChunkStart} for more information.
1829      */
1830     public void markChunkEnd() {
1831         // m_chunkTree.markChunkEnd();
1832         getInstructionList()
1833                 .append(OutlineableChunkEnd.OUTLINEABLECHUNKEND);
1834         m_openChunks--;
1835         if (m_openChunks < 0) {
1836             String msg = (new ErrorMsg(ErrorMsg.OUTLINE_ERR_UNBALANCED_MARKERS))
1837                                  .toString();
1838             throw new InternalError(msg);
1839         }
1840     }
1841 
1842     /**
1843      * <p>Get all {@link Method}s generated by this {@link MethodGenerator}.
1844      * The {@link MethodGen#getMethod()} only returns a single
1845      * <code>Method</code> object.  This method takes into account the Java
1846      * Virtual Machine Specification limit of 64KB on the size of a method, and
1847      * may return more than one <code>Method</code>.</p>
1848      * <p>If the code associated with the <code>MethodGenerator</code> would
1849      * exceed the 64KB limit, this method will attempt to split the code in
1850      * the {@link InstructionList} associated with this
1851      * <code>MethodGenerator</code> into several methods.</p>
1852      * @param classGen the {@link ClassGenerator} of which these methods are
1853      *                 members
1854      * @return an array of all the <code>Method</code>s generated
1855      */
1856     Method[] getGeneratedMethods(ClassGenerator classGen) {
1857         Method[] generatedMethods;
1858         InstructionList il = getInstructionList();
1859         InstructionHandle last = il.getEnd();
1860 
1861         il.setPositions();
1862 
1863         int instructionListSize =
1864                     last.getPosition() + last.getInstruction().getLength();
1865 
1866         // Need to look for any branch target offsets that exceed the range
1867         // [-32768,32767]
1868         if (instructionListSize > MAX_BRANCH_TARGET_OFFSET) {
1869             boolean ilChanged = widenConditionalBranchTargetOffsets();
1870 
1871             // If any branch instructions needed widening, recompute the size
1872             // of the byte code for the method
1873             if (ilChanged) {
1874                 il.setPositions();
1875                 last = il.getEnd();
1876                 instructionListSize =
1877                         last.getPosition() + last.getInstruction().getLength();
1878             }
1879         }
1880 
1881         if (instructionListSize > MAX_METHOD_SIZE) {
1882             generatedMethods = outlineChunks(classGen, instructionListSize);
1883         } else {
1884             generatedMethods = new Method[] {getThisMethod()};
1885         }
1886         return generatedMethods;
1887     }
1888 
1889     protected Method getThisMethod() {
1890         stripAttributes(true);
1891         setMaxLocals();
1892         setMaxStack();
1893         removeNOPs();
1894 
1895         return getMethod();
1896     }
1897     /**
1898      * <p>Rewrites branches to avoid the JVM limits of relative branch
1899      * offsets.  There is no need to invoke this method if the bytecode for the
1900      * {@link MethodGenerator} does not exceed 32KB.</p>
1901      * <p>The Java Virtual Machine Specification permits the code portion of a
1902      * method to be up to 64KB in length.  However, some control transfer
1903      * instructions specify relative offsets as a signed 16-bit quantity,
1904      * limiting the range to a subset of the instructions that might be in a
1905      * method.</p>
1906      * <p>The <code>TABLESWITCH</code> and <code>LOOKUPSWITCH</code>
1907      * instructions always use 32-bit signed relative offsets, so they are
1908      * immune to this problem.</p>
1909      * <p>The <code>GOTO</code> and <code>JSR</code>
1910      * instructions come in two forms, one of which uses 16-bit relative
1911      * offsets, and the other of which uses 32-bit relative offsets.  The BCEL
1912      * library decides whether to use the wide form of <code>GOTO</code> or
1913      * <code>JSR</code>instructions based on the relative offset of the target
1914      * of the instruction without any intervention by the user of the
1915      * library.</p>
1916      * <p>This leaves the various conditional branch instructions,
1917      * <code>IFEQ</code>, <code>IFNULL</code>, <code>IF_ICMPEQ</code>,
1918      * <em>et al.</em>, all of which use 16-bit signed relative offsets, with no
1919      * 32-bit wide form available.</p>
1920      * <p>This method scans the {@link InstructionList} associated with this
1921      * {@link MethodGenerator} and finds all conditional branch instructions
1922      * that might exceed the 16-bit limitation for relative branch offsets.
1923      * The logic of each such instruction is inverted, and made to target the
1924      * instruction which follows it.  An unconditional branch to the original
1925      * target of the instruction is then inserted between the conditional
1926      * branch and the instruction which previously followed it.  The
1927      * unconditional branch is permitted to have a 16-bit or a 32-bit relative
1928      * offset, as described above.  For example,
1929      * <code>
1930      * 1234:   NOP
1931      *          ...
1932      * 55278:  IFEQ -54044
1933      * 55280:  NOP
1934      * </code>
1935      * is rewritten as
1936      * <code>
1937      * 1234:   NOP
1938      *          ...
1939      * 55278:  IFNE 7
1940      * 55280:  GOTO_W -54046
1941      * 55285:  NOP
1942      * </code></p>
1943      * <p><b>Preconditions:</b>
1944      * <ul><li>The {@link InstructionList#setPositions()} has been called for
1945      * the <code>InstructionList</code> associated with this
1946      * <code>MethodGenerator</code>.
1947      * </li></ul></p>
1948      * <p><b>Postconditions:</b>
1949      * <ul><li>Any further changes to the <code>InstructionList</code> for this
1950      * <code>MethodGenerator</code> will invalidate the changes made by this
1951      * method.</li></ul>
1952      * </p>
1953      * @return <code>true</code> if the <code>InstructionList</code> was
1954      * modified; <code>false</code> otherwise
1955      * @see The Java Virtual Machine Specification, Second Edition
1956      */
1957     boolean widenConditionalBranchTargetOffsets() {
1958         boolean ilChanged = false;
1959         int maxOffsetChange = 0;
1960         InstructionList il = getInstructionList();
1961 
1962         // Loop through all the instructions, finding those that would be
1963         // affected by inserting new instructions in the InstructionList, and
1964         // calculating the maximum amount by which the relative offset between
1965         // two instructions could possibly change.
1966         // In part this loop duplicates code in
1967         // org.apache.bcel.generic.InstructionList.setPosition(), which does
1968         // this to determine whether to use 16-bit or 32-bit offsets for GOTO
1969         // and JSR instructions.  Ideally, that method would do the same for
1970         // conditional branch instructions, but it doesn't, so we duplicate the
1971         // processing here.
1972         for (InstructionHandle ih = il.getStart();
1973              ih != null;
1974              ih = ih.getNext()) {
1975             Instruction inst = ih.getInstruction();
1976 
1977             switch (inst.getOpcode()) {
1978                 // Instructions that may have 16-bit or 32-bit branch targets.
1979                 // The size of the branch offset might increase by two bytes.
1980                 case Const.GOTO:
1981                 case Const.JSR:
1982                     maxOffsetChange = maxOffsetChange + 2;
1983                     break;
1984                 // Instructions that contain padding for alignment purposes
1985                 // Up to three bytes of padding might be needed.  For greater
1986                 // accuracy, we should be able to discount any padding already
1987                 // added to these instructions by InstructionList.setPosition(),
1988                 // their APIs do not expose that information.
1989                 case Const.TABLESWITCH:
1990                 case Const.LOOKUPSWITCH:
1991                     maxOffsetChange = maxOffsetChange + 3;
1992                     break;
1993                 // Instructions that might be rewritten by this method as a
1994                 // conditional branch followed by an unconditional branch.
1995                 // The unconditional branch would require five bytes.
1996                 case Const.IF_ACMPEQ:
1997                 case Const.IF_ACMPNE:
1998                 case Const.IF_ICMPEQ:
1999                 case Const.IF_ICMPGE:
2000                 case Const.IF_ICMPGT:
2001                 case Const.IF_ICMPLE:
2002                 case Const.IF_ICMPLT:
2003                 case Const.IF_ICMPNE:
2004                 case Const.IFEQ:
2005                 case Const.IFGE:
2006                 case Const.IFGT:
2007                 case Const.IFLE:
2008                 case Const.IFLT:
2009                 case Const.IFNE:
2010                 case Const.IFNONNULL:
2011                 case Const.IFNULL:
2012                     maxOffsetChange = maxOffsetChange + 5;
2013                     break;
2014             }
2015         }
2016 
2017         // Now that the maximum number of bytes by which the method might grow
2018         // has been determined, look for conditional branches to see which
2019         // might possibly exceed the 16-bit relative offset.
2020         for (InstructionHandle ih = il.getStart();
2021              ih != null;
2022              ih = ih.getNext()) {
2023             Instruction inst = ih.getInstruction();
2024 
2025             if (inst instanceof IfInstruction) {
2026                 IfInstruction oldIfInst = (IfInstruction)inst;
2027                 BranchHandle oldIfHandle = (BranchHandle)ih;
2028                 InstructionHandle target = oldIfInst.getTarget();
2029                 int relativeTargetOffset = target.getPosition()
2030                                                - oldIfHandle.getPosition();
2031 
2032                 // Consider the worst case scenario in which the conditional
2033                 // branch and its target are separated by all the instructions
2034                 // in the method that might increase in size.  If that results
2035                 // in a relative offset that cannot be represented as a 32-bit
2036                 // signed quantity, rewrite the instruction as described above.
2037                 if ((relativeTargetOffset - maxOffsetChange
2038                              < MIN_BRANCH_TARGET_OFFSET)
2039                         || (relativeTargetOffset + maxOffsetChange
2040                                     > MAX_BRANCH_TARGET_OFFSET)) {
2041                     // Invert the logic of the IF instruction, and append
2042                     // that to the InstructionList following the original IF
2043                     // instruction
2044                     InstructionHandle nextHandle = oldIfHandle.getNext();
2045                     IfInstruction invertedIfInst = oldIfInst.negate();
2046                     BranchHandle invertedIfHandle = il.append(oldIfHandle,
2047                                                               invertedIfInst);
2048 
2049                     // Append an unconditional branch to the target of the
2050                     // original IF instruction after the new IF instruction
2051                     BranchHandle gotoHandle = il.append(invertedIfHandle,
2052                                                         new GOTO(target));
2053 
2054                     // If the original IF was the last instruction in
2055                     // InstructionList, add a new no-op to act as the target
2056                     // of the new IF
2057                     if (nextHandle == null) {
2058                         nextHandle = il.append(gotoHandle, InstructionConst.NOP);
2059                     }
2060 
2061                     // Make the new IF instruction branch around the GOTO
2062                     invertedIfHandle.updateTarget(target, nextHandle);
2063 
2064                     // If anything still "points" to the old IF instruction,
2065                     // make adjustments to refer to either the new IF or GOTO
2066                     // instruction
2067                     if (oldIfHandle.hasTargeters()) {
2068                         InstructionTargeter[] targeters =
2069                                                   oldIfHandle.getTargeters();
2070 
2071                         for (int i = 0; i < targeters.length; i++) {
2072                             InstructionTargeter targeter = targeters[i];
2073                             // Ideally, one should simply be able to use
2074                             // InstructionTargeter.updateTarget to change
2075                             // references to the old IF instruction to the new
2076                             // IF instruction.  However, if a LocalVariableGen
2077                             // indicated the old IF marked the end of the range
2078                             // in which the IF variable is in use, the live
2079                             // range of the variable must extend to include the
2080                             // newly created GOTO instruction.  The need for
2081                             // this sort of specific knowledge of an
2082                             // implementor of the InstructionTargeter interface
2083                             // makes the code more fragile.  Future implementors
2084                             // of the interface might have similar requirements
2085                             // which wouldn't be accommodated seemlessly.
2086                             if (targeter instanceof LocalVariableGen) {
2087                                 LocalVariableGen lvg =
2088                                         (LocalVariableGen) targeter;
2089                                 if (lvg.getStart() == oldIfHandle) {
2090                                     lvg.setStart(invertedIfHandle);
2091                                 } else if (lvg.getEnd() == oldIfHandle) {
2092                                     lvg.setEnd(gotoHandle);
2093                                 }
2094                             } else {
2095                                 targeter.updateTarget(oldIfHandle,
2096                                                       invertedIfHandle);
2097                             }
2098                         }
2099                     }
2100 
2101                     try {
2102                         il.delete(oldIfHandle);
2103                     } catch (TargetLostException tle) {
2104                         // This can never happen - we updated the list of
2105                         // instructions that target the deleted instruction
2106                         // prior to deleting it.
2107                         String msg =
2108                             new ErrorMsg(ErrorMsg.OUTLINE_ERR_DELETED_TARGET,
2109                                          tle.getMessage()).toString();
2110                         throw new InternalError(msg);
2111                     }
2112 
2113                     // Adjust the pointer in the InstructionList to point after
2114                     // the newly inserted IF instruction
2115                     ih = gotoHandle;
2116 
2117                     // Indicate that this method rewrote at least one IF
2118                     ilChanged = true;
2119                 }
2120             }
2121         }
2122 
2123         // Did this method rewrite any IF instructions?
2124         return ilChanged;
2125     }
2126 }