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