1 /*
   2  * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * @LastModified: Oct 2017
   4  */
   5 /*
   6  * Licensed to the Apache Software Foundation (ASF) under one or more
   7  * contributor license agreements.  See the NOTICE file distributed with
   8  * this work for additional information regarding copyright ownership.
   9  * The ASF licenses this file to You under the Apache License, Version 2.0
  10  * (the "License"); you may not use this file except in compliance with
  11  * the License.  You may obtain a copy of the License at
  12  *
  13  *      http://www.apache.org/licenses/LICENSE-2.0
  14  *
  15  * Unless required by applicable law or agreed to in writing, software
  16  * distributed under the License is distributed on an "AS IS" BASIS,
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18  * See the License for the specific language governing permissions and
  19  * limitations under the License.
  20  */
  21 /*
  22  * $Id: MethodGenerator.java,v 1.2.4.1 2005/09/05 11:16:47 pvedula Exp $
  23  */
  24 
  25 package com.sun.org.apache.xalan.internal.xsltc.compiler.util;
  26 
  27 import com.sun.org.apache.bcel.internal.Const;
  28 import com.sun.org.apache.bcel.internal.classfile.Field;
  29 import com.sun.org.apache.bcel.internal.classfile.Method;
  30 import com.sun.org.apache.bcel.internal.generic.ALOAD;
  31 import com.sun.org.apache.bcel.internal.generic.ASTORE;
  32 import com.sun.org.apache.bcel.internal.generic.BranchHandle;
  33 import com.sun.org.apache.bcel.internal.generic.BranchInstruction;
  34 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  35 import com.sun.org.apache.bcel.internal.generic.DLOAD;
  36 import com.sun.org.apache.bcel.internal.generic.DSTORE;
  37 import com.sun.org.apache.bcel.internal.generic.FLOAD;
  38 import com.sun.org.apache.bcel.internal.generic.FSTORE;
  39 import com.sun.org.apache.bcel.internal.generic.GETFIELD;
  40 import com.sun.org.apache.bcel.internal.generic.GOTO;
  41 import com.sun.org.apache.bcel.internal.generic.ICONST;
  42 import com.sun.org.apache.bcel.internal.generic.ILOAD;
  43 import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
  44 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
  45 import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
  46 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
  47 import com.sun.org.apache.bcel.internal.generic.ISTORE;
  48 import com.sun.org.apache.bcel.internal.generic.IfInstruction;
  49 import com.sun.org.apache.bcel.internal.generic.IndexedInstruction;
  50 import com.sun.org.apache.bcel.internal.generic.Instruction;
  51 import com.sun.org.apache.bcel.internal.generic.InstructionConst;
  52 import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
  53 import com.sun.org.apache.bcel.internal.generic.InstructionList;
  54 import com.sun.org.apache.bcel.internal.generic.InstructionTargeter;
  55 import com.sun.org.apache.bcel.internal.generic.LLOAD;
  56 import com.sun.org.apache.bcel.internal.generic.LSTORE;
  57 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
  58 import com.sun.org.apache.bcel.internal.generic.LocalVariableInstruction;
  59 import com.sun.org.apache.bcel.internal.generic.MethodGen;
  60 import com.sun.org.apache.bcel.internal.generic.NEW;
  61 import com.sun.org.apache.bcel.internal.generic.PUTFIELD;
  62 import com.sun.org.apache.bcel.internal.generic.RET;
  63 import com.sun.org.apache.bcel.internal.generic.Select;
  64 import com.sun.org.apache.bcel.internal.generic.TargetLostException;
  65 import com.sun.org.apache.bcel.internal.generic.Type;
  66 import com.sun.org.apache.xalan.internal.xsltc.compiler.Pattern;
  67 import com.sun.org.apache.xalan.internal.xsltc.compiler.XSLTC;
  68 import java.util.ArrayList;
  69 import java.util.Collections;
  70 import java.util.HashMap;
  71 import java.util.Iterator;
  72 import java.util.List;
  73 import java.util.Map;
  74 import java.util.Stack;
  75 
  76 /**
  77  * @author Jacek Ambroziak
  78  * @author Santiago Pericas-Geertsen
  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                                     ? (InstructionHandle) 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 = (List<InstructionHandle>)subChunkStack.pop();
 908                 }
 909 
 910                 // Get the handle for the start of this chunk (the last entry
 911                 // in currLevelChunks)
 912                 InstructionHandle chunkStart =
 913                         (InstructionHandle) currLevelChunks.get(
 914                                                       currLevelChunks.size()-1);
 915 
 916                 int chunkEndPosition =
 917                         (currentHandle != null) ? currentHandle.getPosition()
 918                                                 : totalMethodSize;
 919                 int chunkSize = chunkEndPosition - chunkStart.getPosition();
 920 
 921                 // Two ranges of chunk size to consider:
 922                 //
 923                 // 1. [0,TARGET_METHOD_SIZE]
 924                 //      Keep this chunk in consideration as a candidate,
 925                 //      and ignore its subchunks, if any - there's nothing to be
 926                 //      gained by outlining both the current chunk and its
 927                 //      children!
 928                 //
 929                 // 2. (TARGET_METHOD_SIZE,+infinity)
 930                 //      Ignore this chunk - it's too big.  Add its subchunks
 931                 //      as candidates, after merging adjacent chunks to produce
 932                 //      chunks that are as large as possible
 933                 if (chunkSize <= TARGET_METHOD_SIZE) {
 934                     currLevelChunks.add(currentHandle);
 935                 } else {
 936                     if (!openChunkAtCurrLevel) {
 937                         int childChunkCount = nestedSubChunks.size() / 2;
 938                         if (childChunkCount > 0) {
 939                             Chunk[] childChunks = new Chunk[childChunkCount];
 940 
 941                             // Gather all the child chunks of the current chunk
 942                             for (int i = 0; i < childChunkCount; i++) {
 943                                 InstructionHandle start =
 944                                     (InstructionHandle) nestedSubChunks
 945                                                             .get(i*2);
 946                                 InstructionHandle end =
 947                                     (InstructionHandle) nestedSubChunks
 948                                                             .get(i*2+1);
 949 
 950                                 childChunks[i] = new Chunk(start, end);
 951                             }
 952 
 953                             // Merge adjacent siblings
 954                             List<Chunk> mergedChildChunks =
 955                                         mergeAdjacentChunks(childChunks);
 956 
 957                             // Add chunks that mean minimum size requirements
 958                             // to the list of candidate chunks for outlining
 959                             for (Chunk mergedChunk : mergedChildChunks) {
 960                                 int mergedSize = mergedChunk.getChunkSize();
 961 
 962                                 if (mergedSize >= MINIMUM_OUTLINEABLE_CHUNK_SIZE
 963                                         && mergedSize <= TARGET_METHOD_SIZE) {
 964                                     candidateChunks.add(mergedChunk);
 965                                 }
 966                             }
 967                         }
 968                     }
 969 
 970                     // Drop the chunk which was too big
 971                     currLevelChunks.remove(currLevelChunks.size() - 1);
 972                 }
 973 
 974                 // currLevelChunks contains pairs of InstructionHandles.  If
 975                 // its size is an odd number, the loop has encountered the
 976                 // start of a chunk at this level, but not its end.
 977                 openChunkAtCurrLevel = ((currLevelChunks.size() & 0x1) == 1);
 978             }
 979 
 980         } while (currentHandle != null);
 981 
 982         return candidateChunks;
 983     }
 984 
 985     /**
 986      * Merge adjacent sibling chunks to produce larger candidate chunks for
 987      * outlining
 988      * @param chunks array of sibling {@link MethodGenerator.Chunk}s that are
 989      *               under consideration for outlining.  Chunks must be in
 990      *               the order encountered in the {@link InstructionList}
 991      * @return a <code>java.util.List</code> of
 992      *         <code>MethodGenerator.Chunk</code>s maximally merged
 993      */
 994     private List<Chunk> mergeAdjacentChunks(Chunk[] chunks) {
 995         int[] adjacencyRunStart = new int[chunks.length];
 996         int[] adjacencyRunLength = new int[chunks.length];
 997         boolean[] chunkWasMerged = new boolean[chunks.length];
 998 
 999         int maximumRunOfChunks = 0;
1000         int startOfCurrentRun;
1001         int numAdjacentRuns = 0;
1002 
1003         List<Chunk> mergedChunks = new ArrayList<>();
1004 
1005         startOfCurrentRun = 0;
1006 
1007         // Loop through chunks, and record in adjacencyRunStart where each
1008         // run of adjacent chunks begins and how many are in that run.  For
1009         // example, given chunks A B C D E F, if A is adjacent to B, but not
1010         // to C, and C, D, E and F are all adjacent,
1011         //   adjacencyRunStart[0] == 0; adjacencyRunLength[0] == 2
1012         //   adjacencyRunStart[1] == 2; adjacencyRunLength[1] == 4
1013         for (int i = 1; i < chunks.length; i++) {
1014             if (!chunks[i-1].isAdjacentTo(chunks[i])) {
1015                 int lengthOfRun = i - startOfCurrentRun;
1016 
1017                 // Track the longest run of chunks found
1018                 if (maximumRunOfChunks < lengthOfRun) {
1019                     maximumRunOfChunks = lengthOfRun;
1020                 }
1021 
1022                 if (lengthOfRun > 1 ) {
1023                     adjacencyRunLength[numAdjacentRuns] = lengthOfRun;
1024                     adjacencyRunStart[numAdjacentRuns] = startOfCurrentRun;
1025                     numAdjacentRuns++;
1026                 }
1027 
1028                 startOfCurrentRun = i;
1029             }
1030         }
1031 
1032         if (chunks.length - startOfCurrentRun > 1) {
1033             int lengthOfRun = chunks.length - startOfCurrentRun;
1034 
1035             // Track the longest run of chunks found
1036             if (maximumRunOfChunks < lengthOfRun) {
1037                 maximumRunOfChunks = lengthOfRun;
1038             }
1039 
1040             adjacencyRunLength[numAdjacentRuns] =
1041                         chunks.length - startOfCurrentRun;
1042             adjacencyRunStart[numAdjacentRuns] = startOfCurrentRun;
1043             numAdjacentRuns++;
1044         }
1045 
1046         // Try merging adjacent chunks to come up with better sized chunks for
1047         // outlining.  This algorithm is not optimal, but it should be
1048         // reasonably fast.  Consider an example like this, where four chunks
1049         // of the sizes specified in brackets are adjacent.  The best way of
1050         // combining these chunks would be to merge the first pair and merge
1051         // the last three to form two chunks, but the algorithm will merge the
1052         // three in the middle instead, leaving three chunks in all.
1053         //    [25000] [25000] [20000] [1000] [20000]
1054 
1055         // Start by trying to merge the maximum number of adjacent chunks, and
1056         // work down from there.
1057         for (int numToMerge = maximumRunOfChunks; numToMerge>1; numToMerge--) {
1058             // Look at each run of adjacent chunks
1059             for (int run = 0; run < numAdjacentRuns; run++) {
1060                 int runStart = adjacencyRunStart[run];
1061                 int runEnd = runStart + adjacencyRunLength[run] - 1;
1062 
1063                 boolean foundChunksToMerge = false;
1064 
1065                 // Within the current run of adjacent chunks, look at all
1066                 // "subruns" of length numToMerge, until we run out or find
1067                 // a subrun that can be merged.
1068                 for (int mergeStart = runStart;
1069                      mergeStart+numToMerge-1 <= runEnd && !foundChunksToMerge;
1070                      mergeStart++) {
1071                     int mergeEnd = mergeStart + numToMerge - 1;
1072                     int mergeSize = 0;
1073 
1074                     // Find out how big the subrun is
1075                     for (int j = mergeStart; j <= mergeEnd; j++) {
1076                         mergeSize = mergeSize + chunks[j].getChunkSize();
1077                     }
1078 
1079                     // If the current subrun is small enough to outline,
1080                     // merge it, and split the remaining chunks in the run
1081                     if (mergeSize <= TARGET_METHOD_SIZE) {
1082                         foundChunksToMerge = true;
1083 
1084                         for (int j = mergeStart; j <= mergeEnd; j++) {
1085                             chunkWasMerged[j] = true;
1086                         }
1087 
1088                         mergedChunks.add(
1089                                 new Chunk(chunks[mergeStart].getChunkStart(),
1090                                           chunks[mergeEnd].getChunkEnd()));
1091 
1092                         // Adjust the length of the current run of adjacent
1093                         // chunks to end at the newly merged chunk...
1094                         adjacencyRunLength[run] =
1095                                 adjacencyRunStart[run] - mergeStart;
1096 
1097                         int trailingRunLength = runEnd - mergeEnd;
1098 
1099                         // and any chunks that follow the newly merged chunk
1100                         // in the current run of adjacent chunks form another
1101                         // new run of adjacent chunks
1102                         if (trailingRunLength >= 2) {
1103                             adjacencyRunStart[numAdjacentRuns] = mergeEnd + 1;
1104                             adjacencyRunLength[numAdjacentRuns] =
1105                                                             trailingRunLength;
1106                             numAdjacentRuns++;
1107                         }
1108                     }
1109                 }
1110             }
1111         }
1112 
1113         // Make a final pass for any chunk that wasn't merged with a sibling
1114         // and include it in the list of chunks after merging.
1115         for (int i = 0; i < chunks.length; i++) {
1116             if (!chunkWasMerged[i]) {
1117                 mergedChunks.add(chunks[i]);
1118             }
1119         }
1120 
1121         return mergedChunks;
1122     }
1123 
1124     /**
1125      * Breaks up the IL for this {@link MethodGenerator} into separate
1126      * outlined methods so that no method exceeds the 64KB limit on the length
1127      * of the byte code associated with a method.
1128      * @param classGen The {@link ClassGen} with which the generated methods
1129      *                 will be associated
1130      * @param originalMethodSize The number of bytes of bytecode represented by
1131      *                 the {@link InstructionList} of this method
1132      * @return an array of the outlined <code>Method</code>s and the original
1133      *         method itself
1134      */
1135     public Method[] outlineChunks(ClassGenerator classGen,
1136                                   int originalMethodSize) {
1137         List<Method> methodsOutlined = new ArrayList<>();
1138         int currentMethodSize = originalMethodSize;
1139 
1140         int outlinedCount = 0;
1141         boolean moreMethodsOutlined;
1142         String originalMethodName = getName();
1143 
1144         // Special handling for initialization methods.  No other methods can
1145         // include the less than and greater than characters in their names,
1146         // so we munge the names here.
1147         if (originalMethodName.equals("<init>")) {
1148             originalMethodName = "$lt$init$gt$";
1149         } else if (originalMethodName.equals("<clinit>")) {
1150             originalMethodName = "$lt$clinit$gt$";
1151         }
1152 
1153         // Loop until the original method comes in under the JVM limit or
1154         // the loop was unable to outline any more methods
1155         do {
1156             // Get all the best candidates for outlining, and sort them in
1157             // ascending order of size
1158             List<Chunk> candidateChunks = getCandidateChunks(classGen,
1159                                                            currentMethodSize);
1160             Collections.sort(candidateChunks);
1161 
1162             moreMethodsOutlined = false;
1163 
1164             // Loop over the candidates for outlining, from the largest to the
1165             // smallest and outline them one at a time, until the loop has
1166             // outlined all or the original method comes in under the JVM
1167             // limit on the size of a method.
1168             for (int i = candidateChunks.size()-1;
1169                  i >= 0 && currentMethodSize > TARGET_METHOD_SIZE;
1170                  i--) {
1171                 Chunk chunkToOutline = (Chunk)candidateChunks.get(i);
1172 
1173                 methodsOutlined.add(outline(chunkToOutline.getChunkStart(),
1174                                             chunkToOutline.getChunkEnd(),
1175                                             originalMethodName + "$outline$"
1176                                                                + outlinedCount,
1177                                             classGen));
1178                 outlinedCount++;
1179                 moreMethodsOutlined = true;
1180 
1181                 InstructionList il = getInstructionList();
1182                 InstructionHandle lastInst = il.getEnd();
1183                 il.setPositions();
1184 
1185                 // Check the size of the method now
1186                 currentMethodSize =
1187                         lastInst.getPosition()
1188                                 + lastInst.getInstruction().getLength();
1189             }
1190         } while (moreMethodsOutlined && currentMethodSize > TARGET_METHOD_SIZE);
1191 
1192         // Outlining failed to reduce the size of the current method
1193         // sufficiently.  Throw an internal error.
1194         if (currentMethodSize > MAX_METHOD_SIZE) {
1195             String msg = (new ErrorMsg(ErrorMsg.OUTLINE_ERR_METHOD_TOO_BIG))
1196                                   .toString();
1197             throw new InternalError(msg);
1198         }
1199 
1200         Method[] methodsArr = new Method[methodsOutlined.size() + 1];
1201         methodsOutlined.toArray(methodsArr);
1202 
1203         methodsArr[methodsOutlined.size()] = getThisMethod();
1204 
1205         return methodsArr;
1206     }
1207 
1208     /**
1209      * Given an outlineable chunk of code in the current {@link MethodGenerator}
1210      * move ("outline") the chunk to a new method, and replace the chunk in the
1211      * old method with a reference to that new method.  No
1212      * {@link OutlineableChunkStart} or {@link OutlineableChunkEnd} instructions
1213      * are copied.
1214      * @param first The {@link InstructionHandle} of the first instruction in
1215      *              the chunk to outline
1216      * @param last The <code>InstructionHandle</code> of the last instruction in
1217      *             the chunk to outline
1218      * @param outlinedMethodName The name of the new method
1219      * @param classGen The {@link ClassGenerator} of which the original
1220      *              and new methods will be members
1221      * @return The new {@link Method} containing the outlined code.
1222      */
1223     private Method outline(InstructionHandle first, InstructionHandle last,
1224                            String outlinedMethodName, ClassGenerator classGen) {
1225         // We're not equipped to deal with exception handlers yet.  Bail out!
1226         if (getExceptionHandlers().length != 0) {
1227             String msg = (new ErrorMsg(ErrorMsg.OUTLINE_ERR_TRY_CATCH))
1228                                   .toString();
1229             throw new InternalError(msg);
1230         }
1231 
1232         int outlineChunkStartOffset = first.getPosition();
1233         int outlineChunkEndOffset = last.getPosition()
1234                                         + last.getInstruction().getLength();
1235 
1236         ConstantPoolGen cpg = getConstantPool();
1237 
1238         // Create new outlined method with signature:
1239         //
1240         //   private final outlinedMethodName(CopyLocals copyLocals);
1241         //
1242         // CopyLocals is an object that is used to copy-in/copy-out local
1243         // variables that are used by the outlined method.   Only locals whose
1244         // value is potentially set or referenced outside the range of the
1245         // chunk that is being outlined will be represented in CopyLocals.  The
1246         // type of the variable for copying local variables is actually
1247         // generated to be unique - it is not named CopyLocals.
1248         //
1249         // The outlined method never needs to be referenced outside of this
1250         // class, and will never be overridden, so we mark it private final.
1251         final InstructionList newIL = new InstructionList();
1252 
1253         final XSLTC  xsltc = classGen.getParser().getXSLTC();
1254         final String argTypeName = xsltc.getHelperClassName();
1255         final Type[] argTypes =
1256             new Type[] {(new ObjectType(argTypeName)).toJCType()};
1257         final String argName = "copyLocals";
1258         final String[] argNames = new String[] {argName};
1259 
1260         int methodAttributes = ACC_PRIVATE | ACC_FINAL;
1261         final boolean isStaticMethod = (getAccessFlags() & ACC_STATIC) != 0;
1262 
1263         if (isStaticMethod) {
1264             methodAttributes = methodAttributes | ACC_STATIC;
1265         }
1266 
1267         final MethodGenerator outlinedMethodGen =
1268             new MethodGenerator(methodAttributes,
1269                                 com.sun.org.apache.bcel.internal.generic.Type.VOID,
1270                                 argTypes, argNames, outlinedMethodName,
1271                                 getClassName(), newIL, cpg);
1272 
1273         // Create class for copying local variables to the outlined method.
1274         // The fields the class will need to contain will be determined as the
1275         // code in the outlineable chunk is examined.
1276         ClassGenerator copyAreaCG
1277             = new ClassGenerator(argTypeName, OBJECT_CLASS, argTypeName+".java",
1278                                  ACC_FINAL | ACC_PUBLIC | ACC_SUPER, null,
1279                                  classGen.getStylesheet()) {
1280                       public boolean isExternal() {
1281                           return true;
1282                       }
1283                   };
1284         ConstantPoolGen copyAreaCPG = copyAreaCG.getConstantPool();
1285         copyAreaCG.addEmptyConstructor(ACC_PUBLIC);
1286 
1287         // Number of fields in the copy class
1288         int copyAreaFieldCount = 0;
1289 
1290         // The handle for the instruction after the last one to be outlined.
1291         // Note that this should never end up being null.  An outlineable chunk
1292         // won't contain a RETURN instruction or other branch out of the chunk,
1293         // and the JVM specification prohibits code in a method from just
1294         // "falling off the end" so this should always point to a valid handle.
1295         InstructionHandle limit = last.getNext();
1296 
1297         // InstructionLists for copying values into and out of an instance of
1298         // CopyLocals:
1299         //      oldMethCoypInIL  - from locals in old method into an instance
1300         //                         of the CopyLocals class (oldMethCopyInIL)
1301         //      oldMethCopyOutIL - from CopyLocals back into locals in the old
1302         //                         method
1303         //      newMethCopyInIL  - from CopyLocals into locals in the new
1304         //                         method
1305         //      newMethCopyOutIL - from locals in new method into the instance
1306         //                         of the CopyLocals class
1307         InstructionList oldMethCopyInIL  = new InstructionList();
1308         InstructionList oldMethCopyOutIL = new InstructionList();
1309         InstructionList newMethCopyInIL  = new InstructionList();
1310         InstructionList newMethCopyOutIL = new InstructionList();
1311 
1312         // Allocate instance of class in which we'll copy in or copy out locals
1313         // and make two copies:  last copy is used to invoke constructor;
1314         // other two are used for references to fields in the CopyLocals object
1315         InstructionHandle outlinedMethodCallSetup =
1316             oldMethCopyInIL.append(new NEW(cpg.addClass(argTypeName)));
1317         oldMethCopyInIL.append(InstructionConst.DUP);
1318         oldMethCopyInIL.append(InstructionConst.DUP);
1319         oldMethCopyInIL.append(
1320             new INVOKESPECIAL(cpg.addMethodref(argTypeName, "<init>", "()V")));
1321 
1322         // Generate code to invoke the new outlined method, and place the code
1323         // on oldMethCopyOutIL
1324         InstructionHandle outlinedMethodRef;
1325 
1326         if (isStaticMethod) {
1327             outlinedMethodRef =
1328                 oldMethCopyOutIL.append(
1329                     new INVOKESTATIC(cpg.addMethodref(
1330                                           classGen.getClassName(),
1331                                           outlinedMethodName,
1332                                           outlinedMethodGen.getSignature())));
1333         } else {
1334             oldMethCopyOutIL.append(InstructionConst.THIS);
1335             oldMethCopyOutIL.append(InstructionConst.SWAP);
1336             outlinedMethodRef =
1337                 oldMethCopyOutIL.append(
1338                     new INVOKEVIRTUAL(cpg.addMethodref(
1339                                           classGen.getClassName(),
1340                                           outlinedMethodName,
1341                                           outlinedMethodGen.getSignature())));
1342         }
1343 
1344         // Used to keep track of the first in a sequence of
1345         // OutlineableChunkStart instructions
1346         boolean chunkStartTargetMappingsPending = false;
1347         InstructionHandle pendingTargetMappingHandle = null;
1348 
1349         // Used to keep track of the last instruction that was copied
1350         InstructionHandle lastCopyHandle = null;
1351 
1352         // Keeps track of the mapping from instruction handles in the old
1353         // method to instruction handles in the outlined method.  Only need
1354         // to track instructions that are targeted by something else in the
1355         // generated BCEL
1356         HashMap<InstructionHandle, InstructionHandle> targetMap = new HashMap<>();
1357 
1358         // Keeps track of the mapping from local variables in the old method
1359         // to local variables in the outlined method.
1360         HashMap<LocalVariableGen, LocalVariableGen> localVarMap = new HashMap<>();
1361 
1362         HashMap<LocalVariableGen, InstructionHandle> revisedLocalVarStart = new HashMap<>();
1363         HashMap<LocalVariableGen, InstructionHandle> revisedLocalVarEnd = new HashMap<>();
1364 
1365         // Pass 1: Make copies of all instructions, append them to the new list
1366         // and associate old instruction references with the new ones, i.e.,
1367         // a 1:1 mapping.  The special marker instructions are not copied.
1368         // Also, identify local variables whose values need to be copied into or
1369         // out of the new outlined method, and builds up targetMap and
1370         // localVarMap as described above.  The code identifies those local
1371         // variables first so that they can have fixed slots in the stack
1372         // frame for the outlined method assigned them ahead of all those
1373         // variables that don't need to exist for the entirety of the outlined
1374         // method invocation.
1375         for (InstructionHandle ih = first; ih != limit; ih = ih.getNext()) {
1376             Instruction inst = ih.getInstruction();
1377 
1378             // MarkerInstructions are not copied, so if something else targets
1379             // one, the targetMap will point to the nearest copied sibling
1380             // InstructionHandle:  for an OutlineableChunkEnd, the nearest
1381             // preceding sibling; for an OutlineableChunkStart, the nearest
1382             // following sibling.
1383             if (inst instanceof MarkerInstruction) {
1384                 if (ih.hasTargeters()) {
1385                     if (inst instanceof OutlineableChunkEnd) {
1386                         targetMap.put(ih, lastCopyHandle);
1387                     } else {
1388                         if (!chunkStartTargetMappingsPending)  {
1389                             chunkStartTargetMappingsPending = true;
1390                             pendingTargetMappingHandle = ih;
1391                         }
1392                     }
1393                 }
1394             } else {
1395                 // Copy the instruction and append it to the outlined method's
1396                 // InstructionList.
1397                 Instruction c = inst.copy(); // Use clone for shallow copy
1398 
1399                 if (c instanceof BranchInstruction) {
1400                     lastCopyHandle = newIL.append((BranchInstruction)c);
1401                 } else {
1402                     lastCopyHandle = newIL.append(c);
1403                 }
1404 
1405                 if (c instanceof LocalVariableInstruction
1406                         || c instanceof RET) {
1407                     // For any instruction that touches a local variable,
1408                     // check whether the local variable's value needs to be
1409                     // copied into or out of the outlined method.  If so,
1410                     // generate the code to perform the necessary copying, and
1411                     // use localVarMap to map the variable in the original
1412                     // method to the variable in the new method.
1413                     IndexedInstruction lvi = (IndexedInstruction)c;
1414                     int oldLocalVarIndex = lvi.getIndex();
1415                     LocalVariableGen oldLVG =
1416                             getLocalVariableRegistry()
1417                                 .lookupRegisteredLocalVariable(oldLocalVarIndex,
1418                                                               ih.getPosition());
1419                     LocalVariableGen newLVG = localVarMap.get(oldLVG);
1420 
1421                     // Has the code already mapped this local variable to a
1422                     // local in the new method?
1423                     if (localVarMap.get(oldLVG) == null) {
1424                         // Determine whether the local variable needs to be
1425                         // copied into or out of the outlined by checking
1426                         // whether the range of instructions in which the
1427                         // variable is accessible is outside the range of
1428                         // instructions in the outlineable chunk.
1429                         // Special case a chunk start offset of zero:  a local
1430                         // variable live at that position must be a method
1431                         // parameter, so the code doesn't need to check whether
1432                         // the variable is live before that point; being live
1433                         // at offset zero is sufficient to know that the value
1434                         // must be copied in to the outlined method.
1435                         boolean copyInLocalValue =
1436                             offsetInLocalVariableGenRange(oldLVG,
1437                                                 (outlineChunkStartOffset != 0)
1438                                                     ? outlineChunkStartOffset-1
1439                                                     : 0);
1440                         boolean copyOutLocalValue =
1441                             offsetInLocalVariableGenRange(oldLVG,
1442                                                 outlineChunkEndOffset+1);
1443 
1444                         // For any variable that needs to be copied into or out
1445                         // of the outlined method, create a field in the
1446                         // CopyLocals class, and generate the necessary code for
1447                         // copying the value.
1448                         if (copyInLocalValue || copyOutLocalValue) {
1449                             String varName = oldLVG.getName();
1450                             Type varType = oldLVG.getType();
1451                             newLVG = outlinedMethodGen.addLocalVariable(varName,
1452                                                                         varType,
1453                                                                         null,
1454                                                                         null);
1455                             int newLocalVarIndex = newLVG.getIndex();
1456                             String varSignature = varType.getSignature();
1457 
1458                             // Record the mapping from the old local to the new
1459                             localVarMap.put(oldLVG, newLVG);
1460 
1461                             copyAreaFieldCount++;
1462                             String copyAreaFieldName =
1463                                            "field" + copyAreaFieldCount;
1464                             copyAreaCG.addField(
1465                                 new Field(ACC_PUBLIC,
1466                                         copyAreaCPG.addUtf8(copyAreaFieldName),
1467                                         copyAreaCPG.addUtf8(varSignature),
1468                                         null, copyAreaCPG.getConstantPool()));
1469 
1470                             int fieldRef = cpg.addFieldref(argTypeName,
1471                                                            copyAreaFieldName,
1472                                                            varSignature);
1473 
1474                             if (copyInLocalValue) {
1475                                 // Generate code for the old method to store the
1476                                 // value of the local into the correct field in
1477                                 // CopyLocals prior to invocation of the
1478                                 // outlined method.
1479                                 oldMethCopyInIL.append(
1480                                         InstructionConst.DUP);
1481                                 InstructionHandle copyInLoad =
1482                                     oldMethCopyInIL.append(
1483                                         loadLocal(oldLocalVarIndex, varType));
1484                                 oldMethCopyInIL.append(new PUTFIELD(fieldRef));
1485 
1486                                 // If the end of the live range of the old
1487                                 // variable was in the middle of the outlined
1488                                 // chunk.  Make the load of its value the new
1489                                 // end of its range.
1490                                 if (!copyOutLocalValue) {
1491                                     revisedLocalVarEnd.put(oldLVG, copyInLoad);
1492                                 }
1493 
1494                                 // Generate code for start of the outlined
1495                                 // method to copy the value from a field in
1496                                 // CopyLocals to the new local in the outlined
1497                                 // method
1498                                 newMethCopyInIL.append(
1499                                         InstructionConst.ALOAD_1);
1500                                 newMethCopyInIL.append(new GETFIELD(fieldRef));
1501                                 newMethCopyInIL.append(
1502                                         storeLocal(newLocalVarIndex, varType));
1503                             }
1504 
1505                             if (copyOutLocalValue) {
1506                                 // Generate code for the end of the outlined
1507                                 // method to copy the value from the new local
1508                                 // variable into a field in CopyLocals
1509                                 // method
1510                                 newMethCopyOutIL.append(
1511                                         InstructionConst.ALOAD_1);
1512                                 newMethCopyOutIL.append(
1513                                         loadLocal(newLocalVarIndex, varType));
1514                                 newMethCopyOutIL.append(new PUTFIELD(fieldRef));
1515 
1516                                 // Generate code to copy the value from a field
1517                                 // in CopyLocals into a local in the original
1518                                 // method following invocation of the outlined
1519                                 // method.
1520                                 oldMethCopyOutIL.append(
1521                                         InstructionConst.DUP);
1522                                 oldMethCopyOutIL.append(new GETFIELD(fieldRef));
1523                                 InstructionHandle copyOutStore =
1524                                     oldMethCopyOutIL.append(
1525                                         storeLocal(oldLocalVarIndex, varType));
1526 
1527                                 // If the start of the live range of the old
1528                                 // variable was in the middle of the outlined
1529                                 // chunk.  Make this store into it the new start
1530                                 // of its range.
1531                                 if (!copyInLocalValue) {
1532                                     revisedLocalVarStart.put(oldLVG,
1533                                                              copyOutStore);
1534                                 }
1535                             }
1536                         }
1537                     }
1538                 }
1539 
1540                 if (ih.hasTargeters()) {
1541                     targetMap.put(ih, lastCopyHandle);
1542                 }
1543 
1544                 // If this is the first instruction copied following a sequence
1545                 // of OutlineableChunkStart instructions, indicate that the
1546                 // sequence of old instruction all map to this newly created
1547                 // instruction
1548                 if (chunkStartTargetMappingsPending) {
1549                     do {
1550                          targetMap.put(pendingTargetMappingHandle,
1551                                        lastCopyHandle);
1552                          pendingTargetMappingHandle =
1553                                  pendingTargetMappingHandle.getNext();
1554                     } while(pendingTargetMappingHandle != ih);
1555 
1556                     chunkStartTargetMappingsPending = false;
1557                 }
1558             }
1559         }
1560 
1561         // Pass 2: Walk old and new instruction lists, updating branch targets
1562         // and local variable references in the new list
1563         InstructionHandle ih = first;
1564         InstructionHandle ch = newIL.getStart();
1565 
1566         while (ch != null) {
1567             // i == old instruction; c == copied instruction
1568             Instruction i = ih.getInstruction();
1569             Instruction c = ch.getInstruction();
1570 
1571             if (i instanceof BranchInstruction) {
1572                 BranchInstruction bc      = (BranchInstruction)c;
1573                 BranchInstruction bi      = (BranchInstruction)i;
1574                 InstructionHandle itarget = bi.getTarget(); // old target
1575 
1576                 // New target must be in targetMap
1577                 InstructionHandle newTarget = targetMap.get(itarget);
1578 
1579                 bc.setTarget(newTarget);
1580 
1581                 // Handle LOOKUPSWITCH or TABLESWITCH which may have many
1582                 // target instructions
1583                 if (bi instanceof Select) {
1584                     InstructionHandle[] itargets = ((Select)bi).getTargets();
1585                     InstructionHandle[] ctargets = ((Select)bc).getTargets();
1586 
1587                     // Update all targets
1588                     for (int j=0; j < itargets.length; j++) {
1589                         ctargets[j] = targetMap.get(itargets[j]);
1590                     }
1591                 }
1592             }  else if (i instanceof LocalVariableInstruction
1593                             || i instanceof RET) {
1594                 // For any instruction that touches a local variable,
1595                 // map the location of the variable in the original
1596                 // method to its location in the new method.
1597                 IndexedInstruction lvi = (IndexedInstruction)c;
1598                 int oldLocalVarIndex = lvi.getIndex();
1599                 LocalVariableGen oldLVG =
1600                         getLocalVariableRegistry()
1601                                 .lookupRegisteredLocalVariable(oldLocalVarIndex,
1602                                                               ih.getPosition());
1603                 LocalVariableGen newLVG = localVarMap.get(oldLVG);
1604                 int newLocalVarIndex;
1605 
1606                 if (newLVG == null) {
1607                     // Create new variable based on old variable - use same
1608                     // name and type, but we will let the variable be active
1609                     // for the entire outlined method.
1610                     // LocalVariableGen oldLocal = oldLocals[oldLocalVarIndex];
1611                     String varName = oldLVG.getName();
1612                     Type varType = oldLVG.getType();
1613                     newLVG = outlinedMethodGen.addLocalVariable(varName,
1614                                                                 varType,
1615                                                                 null,
1616                                                                 null);
1617                     newLocalVarIndex = newLVG.getIndex();
1618                     localVarMap.put(oldLVG, newLVG);
1619 
1620                     // The old variable's live range was wholly contained in
1621                     // the outlined chunk.  There should no longer be stores
1622                     // of values into it or loads of its value, so we can just
1623                     // mark its live range as the reference to the outlined
1624                     // method.
1625                     revisedLocalVarStart.put(oldLVG, outlinedMethodRef);
1626                     revisedLocalVarEnd.put(oldLVG, outlinedMethodRef);
1627                 } else {
1628                     newLocalVarIndex = newLVG.getIndex();
1629                 }
1630                 lvi.setIndex(newLocalVarIndex);
1631             }
1632 
1633             // If the old instruction marks the end of the range of a local
1634             // variable, make sure that any slots on the stack reserved for
1635             // local variables are made available for reuse by calling
1636             // MethodGenerator.removeLocalVariable
1637             if (ih.hasTargeters()) {
1638                 InstructionTargeter[] targeters = ih.getTargeters();
1639 
1640                 for (int idx = 0; idx < targeters.length; idx++) {
1641                     InstructionTargeter targeter = targeters[idx];
1642 
1643                     if (targeter instanceof LocalVariableGen
1644                             && ((LocalVariableGen)targeter).getEnd()==ih) {
1645                         LocalVariableGen newLVG = localVarMap.get(targeter);
1646                         if (newLVG != null) {
1647                             outlinedMethodGen.removeLocalVariable(newLVG);
1648                         }
1649                     }
1650                 }
1651             }
1652 
1653             // If the current instruction in the original list was a marker,
1654             // it wasn't copied, so don't advance through the list of copied
1655             // instructions yet.
1656             if (!(i instanceof MarkerInstruction)) {
1657                 ch = ch.getNext();
1658             }
1659             ih = ih.getNext();
1660 
1661         }
1662 
1663         // POP the reference to the CopyLocals object from the stack
1664         oldMethCopyOutIL.append(InstructionConst.POP);
1665 
1666         for (Map.Entry<LocalVariableGen, InstructionHandle> lvgRangeStartPair :
1667                 revisedLocalVarStart.entrySet()) {
1668             LocalVariableGen lvg = lvgRangeStartPair.getKey();
1669             InstructionHandle startInst = lvgRangeStartPair.getValue();
1670 
1671             lvg.setStart(startInst);
1672         }
1673 
1674         for (Map.Entry<LocalVariableGen, InstructionHandle> lvgRangeEndPair :
1675                 revisedLocalVarEnd.entrySet()) {
1676             LocalVariableGen lvg = lvgRangeEndPair.getKey();
1677             InstructionHandle endInst = lvgRangeEndPair.getValue();
1678 
1679             lvg.setEnd(endInst);
1680         }
1681 
1682         xsltc.dumpClass(copyAreaCG.getJavaClass());
1683 
1684         // Assemble the instruction lists so that the old method invokes the
1685         // new outlined method
1686         InstructionList oldMethodIL = getInstructionList();
1687 
1688         oldMethodIL.insert(first, oldMethCopyInIL);
1689         oldMethodIL.insert(first, oldMethCopyOutIL);
1690 
1691         // Insert the copying code into the outlined method
1692         newIL.insert(newMethCopyInIL);
1693         newIL.append(newMethCopyOutIL);
1694         newIL.append(InstructionConst.RETURN);
1695 
1696         // Discard instructions in outlineable chunk from old method
1697         try {
1698             oldMethodIL.delete(first, last);
1699         } catch (TargetLostException e) {
1700             InstructionHandle[] targets = e.getTargets();
1701             // If there were still references to old instructions lingering,
1702             // clean those up.  The only instructions targetting the deleted
1703             // instructions should have been part of the chunk that was just
1704             // deleted, except that instructions might branch to the start of
1705             // the outlined chunk; similarly, all the live ranges of local
1706             // variables should have been adjusted, except for unreferenced
1707             // variables.
1708             for (int i = 0; i < targets.length; i++) {
1709                 InstructionHandle lostTarget = targets[i];
1710                 InstructionTargeter[] targeters = lostTarget.getTargeters();
1711                 for (int j = 0; j < targeters.length; j++) {
1712                     if (targeters[j] instanceof LocalVariableGen) {
1713                         LocalVariableGen lvgTargeter =
1714                                              (LocalVariableGen) targeters[j];
1715                         // In the case of any lingering variable references,
1716                         // just make the live range point to the outlined
1717                         // function reference.  Such variables should be unused
1718                         // anyway.
1719                         if (lvgTargeter.getStart() == lostTarget) {
1720                             lvgTargeter.setStart(outlinedMethodRef);
1721                         }
1722                         if (lvgTargeter.getEnd() == lostTarget) {
1723                             lvgTargeter.setEnd(outlinedMethodRef);
1724                         }
1725                     } else {
1726                         targeters[j].updateTarget(lostTarget,
1727                                                   outlinedMethodCallSetup);
1728                     }
1729                 }
1730             }
1731         }
1732 
1733         // Make a copy for the new method of all exceptions that might be thrown
1734         String[] exceptions = getExceptions();
1735         for (int i = 0; i < exceptions.length; i++) {
1736             outlinedMethodGen.addException(exceptions[i]);
1737         }
1738 
1739         return outlinedMethodGen.getThisMethod();
1740     }
1741 
1742     /**
1743      * Helper method to generate an instance of a subclass of
1744      * {@link LoadInstruction} based on the specified {@link Type} that will
1745      * load the specified local variable
1746      * @param index the JVM stack frame index of the variable that is to be
1747      * loaded
1748      * @param type the {@link Type} of the variable
1749      * @return the generated {@link LoadInstruction}
1750      */
1751     private static Instruction loadLocal(int index, Type type) {
1752         if (type == Type.BOOLEAN) {
1753            return new ILOAD(index);
1754         } else if (type == Type.INT) {
1755            return new ILOAD(index);
1756         } else if (type == Type.SHORT) {
1757            return new ILOAD(index);
1758         } else if (type == Type.LONG) {
1759            return new LLOAD(index);
1760         } else if (type == Type.BYTE) {
1761            return new ILOAD(index);
1762         } else if (type == Type.CHAR) {
1763            return new ILOAD(index);
1764         } else if (type == Type.FLOAT) {
1765            return new FLOAD(index);
1766         } else if (type == Type.DOUBLE) {
1767            return new DLOAD(index);
1768         } else {
1769            return new ALOAD(index);
1770         }
1771     }
1772 
1773     /**
1774      * Helper method to generate an instance of a subclass of
1775      * {@link StoreInstruction} based on the specified {@link Type} that will
1776      * store a value in the specified local variable
1777      * @param index the JVM stack frame index of the variable that is to be
1778      * stored
1779      * @param type the {@link Type} of the variable
1780      * @return the generated {@link StoredInstruction}
1781      */
1782     private static Instruction storeLocal(int index, Type type) {
1783         if (type == Type.BOOLEAN) {
1784            return new ISTORE(index);
1785         } else if (type == Type.INT) {
1786            return new ISTORE(index);
1787         } else if (type == Type.SHORT) {
1788            return new ISTORE(index);
1789         } else if (type == Type.LONG) {
1790            return new LSTORE(index);
1791         } else if (type == Type.BYTE) {
1792            return new ISTORE(index);
1793         } else if (type == Type.CHAR) {
1794            return new ISTORE(index);
1795         } else if (type == Type.FLOAT) {
1796            return new FSTORE(index);
1797         } else if (type == Type.DOUBLE) {
1798            return new DSTORE(index);
1799         } else {
1800            return new ASTORE(index);
1801         }
1802     }
1803 
1804     /**
1805      * Track the number of outlineable chunks seen.
1806      */
1807     private int m_totalChunks = 0;
1808 
1809     /**
1810      * Track the number of outlineable chunks started but not yet ended.  Used
1811      * to detect imbalances in byte code generation.
1812      */
1813     private int m_openChunks = 0;
1814 
1815     /**
1816      * Mark the end of the method's
1817      * {@link InstructionList} as the start of an outlineable chunk of code.
1818      * The outlineable chunk begins after the {@link InstructionHandle} that is
1819      * at the end of the method's {@link InstructionList}, or at the start of
1820      * the method if the <code>InstructionList</code> is empty.
1821      * See {@link OutlineableChunkStart} for more information.
1822      */
1823     public void markChunkStart() {
1824         // m_chunkTree.markChunkStart();
1825         getInstructionList()
1826                 .append(OutlineableChunkStart.OUTLINEABLECHUNKSTART);
1827         m_totalChunks++;
1828         m_openChunks++;
1829     }
1830 
1831     /**
1832      * Mark the end of an outlineable chunk of code.  See
1833      * {@link OutlineableChunkStart} for more information.
1834      */
1835     public void markChunkEnd() {
1836         // m_chunkTree.markChunkEnd();
1837         getInstructionList()
1838                 .append(OutlineableChunkEnd.OUTLINEABLECHUNKEND);
1839         m_openChunks--;
1840         if (m_openChunks < 0) {
1841             String msg = (new ErrorMsg(ErrorMsg.OUTLINE_ERR_UNBALANCED_MARKERS))
1842                                  .toString();
1843             throw new InternalError(msg);
1844         }
1845     }
1846 
1847     /**
1848      * <p>Get all {@link Method}s generated by this {@link MethodGenerator}.
1849      * The {@link MethodGen#getMethod()} only returns a single
1850      * <code>Method</code> object.  This method takes into account the Java
1851      * Virtual Machine Specification limit of 64KB on the size of a method, and
1852      * may return more than one <code>Method</code>.</p>
1853      * <p>If the code associated with the <code>MethodGenerator</code> would
1854      * exceed the 64KB limit, this method will attempt to split the code in
1855      * the {@link InstructionList} associated with this
1856      * <code>MethodGenerator</code> into several methods.</p>
1857      * @param classGen the {@link ClassGenerator} of which these methods are
1858      *                 members
1859      * @return an array of all the <code>Method</code>s generated
1860      */
1861     Method[] getGeneratedMethods(ClassGenerator classGen) {
1862         Method[] generatedMethods;
1863         InstructionList il = getInstructionList();
1864         InstructionHandle last = il.getEnd();
1865 
1866         il.setPositions();
1867 
1868         int instructionListSize =
1869                     last.getPosition() + last.getInstruction().getLength();
1870 
1871         // Need to look for any branch target offsets that exceed the range
1872         // [-32768,32767]
1873         if (instructionListSize > MAX_BRANCH_TARGET_OFFSET) {
1874             boolean ilChanged = widenConditionalBranchTargetOffsets();
1875 
1876             // If any branch instructions needed widening, recompute the size
1877             // of the byte code for the method
1878             if (ilChanged) {
1879                 il.setPositions();
1880                 last = il.getEnd();
1881                 instructionListSize =
1882                         last.getPosition() + last.getInstruction().getLength();
1883             }
1884         }
1885 
1886         if (instructionListSize > MAX_METHOD_SIZE) {
1887             generatedMethods = outlineChunks(classGen, instructionListSize);
1888         } else {
1889             generatedMethods = new Method[] {getThisMethod()};
1890         }
1891         return generatedMethods;
1892     }
1893 
1894     protected Method getThisMethod() {
1895         stripAttributes(true);
1896         setMaxLocals();
1897         setMaxStack();
1898         removeNOPs();
1899 
1900         return getMethod();
1901     }
1902     /**
1903      * <p>Rewrites branches to avoid the JVM limits of relative branch
1904      * offsets.  There is no need to invoke this method if the bytecode for the
1905      * {@link MethodGenerator} does not exceed 32KB.</p>
1906      * <p>The Java Virtual Machine Specification permits the code portion of a
1907      * method to be up to 64KB in length.  However, some control transfer
1908      * instructions specify relative offsets as a signed 16-bit quantity,
1909      * limiting the range to a subset of the instructions that might be in a
1910      * method.</p>
1911      * <p>The <code>TABLESWITCH</code> and <code>LOOKUPSWITCH</code>
1912      * instructions always use 32-bit signed relative offsets, so they are
1913      * immune to this problem.</p>
1914      * <p>The <code>GOTO</code> and <code>JSR</code>
1915      * instructions come in two forms, one of which uses 16-bit relative
1916      * offsets, and the other of which uses 32-bit relative offsets.  The BCEL
1917      * library decides whether to use the wide form of <code>GOTO</code> or
1918      * <code>JSR</code>instructions based on the relative offset of the target
1919      * of the instruction without any intervention by the user of the
1920      * library.</p>
1921      * <p>This leaves the various conditional branch instructions,
1922      * <code>IFEQ</code>, <code>IFNULL</code>, <code>IF_ICMPEQ</code>,
1923      * <em>et al.</em>, all of which use 16-bit signed relative offsets, with no
1924      * 32-bit wide form available.</p>
1925      * <p>This method scans the {@link InstructionList} associated with this
1926      * {@link MethodGenerator} and finds all conditional branch instructions
1927      * that might exceed the 16-bit limitation for relative branch offsets.
1928      * The logic of each such instruction is inverted, and made to target the
1929      * instruction which follows it.  An unconditional branch to the original
1930      * target of the instruction is then inserted between the conditional
1931      * branch and the instruction which previously followed it.  The
1932      * unconditional branch is permitted to have a 16-bit or a 32-bit relative
1933      * offset, as described above.  For example,
1934      * <code>
1935      * 1234:   NOP
1936      *          ...
1937      * 55278:  IFEQ -54044
1938      * 55280:  NOP
1939      * </code>
1940      * is rewritten as
1941      * <code>
1942      * 1234:   NOP
1943      *          ...
1944      * 55278:  IFNE 7
1945      * 55280:  GOTO_W -54046
1946      * 55285:  NOP
1947      * </code></p>
1948      * <p><b>Preconditions:</b>
1949      * <ul><li>The {@link InstructionList#setPositions()} has been called for
1950      * the <code>InstructionList</code> associated with this
1951      * <code>MethodGenerator</code>.
1952      * </li></ul></p>
1953      * <p><b>Postconditions:</b>
1954      * <ul><li>Any further changes to the <code>InstructionList</code> for this
1955      * <code>MethodGenerator</code> will invalidate the changes made by this
1956      * method.</li></ul>
1957      * </p>
1958      * @return <code>true</code> if the <code>InstructionList</code> was
1959      * modified; <code>false</code> otherwise
1960      * @see The Java Virtual Machine Specification, Second Edition
1961      */
1962     boolean widenConditionalBranchTargetOffsets() {
1963         boolean ilChanged = false;
1964         int maxOffsetChange = 0;
1965         InstructionList il = getInstructionList();
1966 
1967         // Loop through all the instructions, finding those that would be
1968         // affected by inserting new instructions in the InstructionList, and
1969         // calculating the maximum amount by which the relative offset between
1970         // two instructions could possibly change.
1971         // In part this loop duplicates code in
1972         // org.apache.bcel.generic.InstructionList.setPosition(), which does
1973         // this to determine whether to use 16-bit or 32-bit offsets for GOTO
1974         // and JSR instructions.  Ideally, that method would do the same for
1975         // conditional branch instructions, but it doesn't, so we duplicate the
1976         // processing here.
1977         for (InstructionHandle ih = il.getStart();
1978              ih != null;
1979              ih = ih.getNext()) {
1980             Instruction inst = ih.getInstruction();
1981 
1982             switch (inst.getOpcode()) {
1983                 // Instructions that may have 16-bit or 32-bit branch targets.
1984                 // The size of the branch offset might increase by two bytes.
1985                 case Const.GOTO:
1986                 case Const.JSR:
1987                     maxOffsetChange = maxOffsetChange + 2;
1988                     break;
1989                 // Instructions that contain padding for alignment purposes
1990                 // Up to three bytes of padding might be needed.  For greater
1991                 // accuracy, we should be able to discount any padding already
1992                 // added to these instructions by InstructionList.setPosition(),
1993                 // their APIs do not expose that information.
1994                 case Const.TABLESWITCH:
1995                 case Const.LOOKUPSWITCH:
1996                     maxOffsetChange = maxOffsetChange + 3;
1997                     break;
1998                 // Instructions that might be rewritten by this method as a
1999                 // conditional branch followed by an unconditional branch.
2000                 // The unconditional branch would require five bytes.
2001                 case Const.IF_ACMPEQ:
2002                 case Const.IF_ACMPNE:
2003                 case Const.IF_ICMPEQ:
2004                 case Const.IF_ICMPGE:
2005                 case Const.IF_ICMPGT:
2006                 case Const.IF_ICMPLE:
2007                 case Const.IF_ICMPLT:
2008                 case Const.IF_ICMPNE:
2009                 case Const.IFEQ:
2010                 case Const.IFGE:
2011                 case Const.IFGT:
2012                 case Const.IFLE:
2013                 case Const.IFLT:
2014                 case Const.IFNE:
2015                 case Const.IFNONNULL:
2016                 case Const.IFNULL:
2017                     maxOffsetChange = maxOffsetChange + 5;
2018                     break;
2019             }
2020         }
2021 
2022         // Now that the maximum number of bytes by which the method might grow
2023         // has been determined, look for conditional branches to see which
2024         // might possibly exceed the 16-bit relative offset.
2025         for (InstructionHandle ih = il.getStart();
2026              ih != null;
2027              ih = ih.getNext()) {
2028             Instruction inst = ih.getInstruction();
2029 
2030             if (inst instanceof IfInstruction) {
2031                 IfInstruction oldIfInst = (IfInstruction)inst;
2032                 BranchHandle oldIfHandle = (BranchHandle)ih;
2033                 InstructionHandle target = oldIfInst.getTarget();
2034                 int relativeTargetOffset = target.getPosition()
2035                                                - oldIfHandle.getPosition();
2036 
2037                 // Consider the worst case scenario in which the conditional
2038                 // branch and its target are separated by all the instructions
2039                 // in the method that might increase in size.  If that results
2040                 // in a relative offset that cannot be represented as a 32-bit
2041                 // signed quantity, rewrite the instruction as described above.
2042                 if ((relativeTargetOffset - maxOffsetChange
2043                              < MIN_BRANCH_TARGET_OFFSET)
2044                         || (relativeTargetOffset + maxOffsetChange
2045                                     > MAX_BRANCH_TARGET_OFFSET)) {
2046                     // Invert the logic of the IF instruction, and append
2047                     // that to the InstructionList following the original IF
2048                     // instruction
2049                     InstructionHandle nextHandle = oldIfHandle.getNext();
2050                     IfInstruction invertedIfInst = oldIfInst.negate();
2051                     BranchHandle invertedIfHandle = il.append(oldIfHandle,
2052                                                               invertedIfInst);
2053 
2054                     // Append an unconditional branch to the target of the
2055                     // original IF instruction after the new IF instruction
2056                     BranchHandle gotoHandle = il.append(invertedIfHandle,
2057                                                         new GOTO(target));
2058 
2059                     // If the original IF was the last instruction in
2060                     // InstructionList, add a new no-op to act as the target
2061                     // of the new IF
2062                     if (nextHandle == null) {
2063                         nextHandle = il.append(gotoHandle, InstructionConst.NOP);
2064                     }
2065 
2066                     // Make the new IF instruction branch around the GOTO
2067                     invertedIfHandle.updateTarget(target, nextHandle);
2068 
2069                     // If anything still "points" to the old IF instruction,
2070                     // make adjustments to refer to either the new IF or GOTO
2071                     // instruction
2072                     if (oldIfHandle.hasTargeters()) {
2073                         InstructionTargeter[] targeters =
2074                                                   oldIfHandle.getTargeters();
2075 
2076                         for (int i = 0; i < targeters.length; i++) {
2077                             InstructionTargeter targeter = targeters[i];
2078                             // Ideally, one should simply be able to use
2079                             // InstructionTargeter.updateTarget to change
2080                             // references to the old IF instruction to the new
2081                             // IF instruction.  However, if a LocalVariableGen
2082                             // indicated the old IF marked the end of the range
2083                             // in which the IF variable is in use, the live
2084                             // range of the variable must extend to include the
2085                             // newly created GOTO instruction.  The need for
2086                             // this sort of specific knowledge of an
2087                             // implementor of the InstructionTargeter interface
2088                             // makes the code more fragile.  Future implementors
2089                             // of the interface might have similar requirements
2090                             // which wouldn't be accommodated seemlessly.
2091                             if (targeter instanceof LocalVariableGen) {
2092                                 LocalVariableGen lvg =
2093                                         (LocalVariableGen) targeter;
2094                                 if (lvg.getStart() == oldIfHandle) {
2095                                     lvg.setStart(invertedIfHandle);
2096                                 } else if (lvg.getEnd() == oldIfHandle) {
2097                                     lvg.setEnd(gotoHandle);
2098                                 }
2099                             } else {
2100                                 targeter.updateTarget(oldIfHandle,
2101                                                       invertedIfHandle);
2102                             }
2103                         }
2104                     }
2105 
2106                     try {
2107                         il.delete(oldIfHandle);
2108                     } catch (TargetLostException tle) {
2109                         // This can never happen - we updated the list of
2110                         // instructions that target the deleted instruction
2111                         // prior to deleting it.
2112                         String msg =
2113                             new ErrorMsg(ErrorMsg.OUTLINE_ERR_DELETED_TARGET,
2114                                          tle.getMessage()).toString();
2115                         throw new InternalError(msg);
2116                     }
2117 
2118                     // Adjust the pointer in the InstructionList to point after
2119                     // the newly inserted IF instruction
2120                     ih = gotoHandle;
2121 
2122                     // Indicate that this method rewrote at least one IF
2123                     ilChanged = true;
2124                 }
2125             }
2126         }
2127 
2128         // Did this method rewrite any IF instructions?
2129         return ilChanged;
2130     }
2131 }