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