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