author | joehw |
Sun, 13 Aug 2017 21:10:40 -0700 | |
changeset 46174 | 5611d2529b49 |
parent 44797 | 8b3b3b911b8a |
permissions | -rw-r--r-- |
6 | 1 |
/* |
46174 | 2 |
* Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. |
6 | 3 |
*/ |
44797
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
27537
diff
changeset
|
4 |
/* |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
27537
diff
changeset
|
5 |
* Licensed to the Apache Software Foundation (ASF) under one or more |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
27537
diff
changeset
|
6 |
* contributor license agreements. See the NOTICE file distributed with |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
27537
diff
changeset
|
7 |
* this work for additional information regarding copyright ownership. |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
27537
diff
changeset
|
8 |
* The ASF licenses this file to You under the Apache License, Version 2.0 |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
27537
diff
changeset
|
9 |
* (the "License"); you may not use this file except in compliance with |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
27537
diff
changeset
|
10 |
* the License. You may obtain a copy of the License at |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
27537
diff
changeset
|
11 |
* |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
27537
diff
changeset
|
12 |
* http://www.apache.org/licenses/LICENSE-2.0 |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
27537
diff
changeset
|
13 |
* |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
27537
diff
changeset
|
14 |
* Unless required by applicable law or agreed to in writing, software |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
27537
diff
changeset
|
15 |
* distributed under the License is distributed on an "AS IS" BASIS, |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
27537
diff
changeset
|
16 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
27537
diff
changeset
|
17 |
* See the License for the specific language governing permissions and |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
27537
diff
changeset
|
18 |
* limitations under the License. |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
27537
diff
changeset
|
19 |
*/ |
6 | 20 |
package com.sun.org.apache.bcel.internal.generic; |
21 |
||
46174 | 22 |
import com.sun.org.apache.bcel.internal.Const; |
23 |
import com.sun.org.apache.bcel.internal.classfile.AnnotationEntry; |
|
24 |
import com.sun.org.apache.bcel.internal.classfile.Annotations; |
|
25 |
import com.sun.org.apache.bcel.internal.classfile.Attribute; |
|
26 |
import com.sun.org.apache.bcel.internal.classfile.Code; |
|
27 |
import com.sun.org.apache.bcel.internal.classfile.CodeException; |
|
28 |
import com.sun.org.apache.bcel.internal.classfile.ExceptionTable; |
|
29 |
import com.sun.org.apache.bcel.internal.classfile.LineNumber; |
|
30 |
import com.sun.org.apache.bcel.internal.classfile.LineNumberTable; |
|
31 |
import com.sun.org.apache.bcel.internal.classfile.LocalVariable; |
|
32 |
import com.sun.org.apache.bcel.internal.classfile.LocalVariableTable; |
|
33 |
import com.sun.org.apache.bcel.internal.classfile.LocalVariableTypeTable; |
|
34 |
import com.sun.org.apache.bcel.internal.classfile.Method; |
|
35 |
import com.sun.org.apache.bcel.internal.classfile.ParameterAnnotationEntry; |
|
36 |
import com.sun.org.apache.bcel.internal.classfile.ParameterAnnotations; |
|
37 |
import com.sun.org.apache.bcel.internal.classfile.RuntimeVisibleParameterAnnotations; |
|
38 |
import com.sun.org.apache.bcel.internal.classfile.Utility; |
|
39 |
import com.sun.org.apache.bcel.internal.util.BCELComparator; |
|
40 |
import java.util.ArrayList; |
|
41 |
import java.util.Arrays; |
|
42 |
import java.util.Comparator; |
|
43 |
import java.util.HashMap; |
|
44 |
import java.util.List; |
|
45 |
import java.util.Map; |
|
46 |
import java.util.Stack; |
|
6 | 47 |
|
48 |
/** |
|
49 |
* Template class for building up a method. This is done by defining exception |
|
50 |
* handlers, adding thrown exceptions, local variables and attributes, whereas |
|
51 |
* the `LocalVariableTable' and `LineNumberTable' attributes will be set |
|
52 |
* automatically for the code. Use stripAttributes() if you don't like this. |
|
53 |
* |
|
54 |
* While generating code it may be necessary to insert NOP operations. You can |
|
46174 | 55 |
* use the `removeNOPs' method to get rid off them. The resulting method object |
56 |
* can be obtained via the `getMethod()' method. |
|
6 | 57 |
* |
46174 | 58 |
* @version $Id: MethodGen.java 1749603 2016-06-21 20:50:19Z ggregory $ |
59 |
* @see InstructionList |
|
60 |
* @see Method |
|
6 | 61 |
*/ |
62 |
public class MethodGen extends FieldGenOrMethodGen { |
|
63 |
||
46174 | 64 |
private String class_name; |
65 |
private Type[] arg_types; |
|
66 |
private String[] arg_names; |
|
67 |
private int max_locals; |
|
68 |
private int max_stack; |
|
69 |
private InstructionList il; |
|
70 |
private boolean strip_attributes; |
|
71 |
private final List<LocalVariableGen> variable_vec = new ArrayList<>(); |
|
72 |
private final List<LocalVariableGen> type_vec = new ArrayList<>(); |
|
73 |
private final List<LineNumberGen> line_number_vec = new ArrayList<>(); |
|
74 |
private final List<CodeExceptionGen> exception_vec = new ArrayList<>(); |
|
75 |
private final List<String> throws_vec = new ArrayList<>(); |
|
76 |
private final List<Attribute> code_attrs_vec = new ArrayList<>(); |
|
6 | 77 |
|
46174 | 78 |
private List<AnnotationEntryGen>[] param_annotations; // Array of lists containing AnnotationGen objects |
79 |
private boolean hasParameterAnnotations = false; |
|
80 |
private boolean haveUnpackedParameterAnnotations = false; |
|
6 | 81 |
|
46174 | 82 |
private static BCELComparator bcelComparator = new BCELComparator() { |
6 | 83 |
|
46174 | 84 |
@Override |
85 |
public boolean equals(final Object o1, final Object o2) { |
|
86 |
final MethodGen THIS = (MethodGen) o1; |
|
87 |
final MethodGen THAT = (MethodGen) o2; |
|
88 |
return THIS.getName().equals(THAT.getName()) |
|
89 |
&& THIS.getSignature().equals(THAT.getSignature()); |
|
6 | 90 |
} |
91 |
||
46174 | 92 |
@Override |
93 |
public int hashCode(final Object o) { |
|
94 |
final MethodGen THIS = (MethodGen) o; |
|
95 |
return THIS.getSignature().hashCode() ^ THIS.getName().hashCode(); |
|
96 |
} |
|
97 |
}; |
|
6 | 98 |
|
46174 | 99 |
/** |
100 |
* Declare method. If the method is non-static the constructor automatically |
|
101 |
* declares a local variable `$this' in slot 0. The actual code is contained |
|
102 |
* in the `il' parameter, which may further manipulated by the user. But he |
|
103 |
* must take care not to remove any instruction (handles) that are still |
|
104 |
* referenced from this object. |
|
105 |
* |
|
106 |
* For example one may not add a local variable and later remove the |
|
107 |
* instructions it refers to without causing havoc. It is safe however if |
|
108 |
* you remove that local variable, too. |
|
109 |
* |
|
110 |
* @param access_flags access qualifiers |
|
111 |
* @param return_type method type |
|
112 |
* @param arg_types argument types |
|
113 |
* @param arg_names argument names (if this is null, default names will be |
|
114 |
* provided for them) |
|
115 |
* @param method_name name of method |
|
116 |
* @param class_name class name containing this method (may be null, if you |
|
117 |
* don't care) |
|
118 |
* @param il instruction list associated with this method, may be null only |
|
119 |
* for abstract or native methods |
|
120 |
* @param cp constant pool |
|
121 |
*/ |
|
122 |
public MethodGen(final int access_flags, final Type return_type, final Type[] arg_types, String[] arg_names, |
|
123 |
final String method_name, final String class_name, final InstructionList il, final ConstantPoolGen cp) { |
|
124 |
super(access_flags); |
|
125 |
setType(return_type); |
|
126 |
setArgumentTypes(arg_types); |
|
127 |
setArgumentNames(arg_names); |
|
128 |
setName(method_name); |
|
129 |
setClassName(class_name); |
|
130 |
setInstructionList(il); |
|
131 |
setConstantPool(cp); |
|
132 |
final boolean abstract_ = isAbstract() || isNative(); |
|
133 |
InstructionHandle start = null; |
|
134 |
InstructionHandle end = null; |
|
135 |
if (!abstract_) { |
|
136 |
start = il.getStart(); |
|
137 |
end = il.getEnd(); |
|
138 |
/* Add local variables, namely the implicit `this' and the arguments |
|
139 |
*/ |
|
140 |
if (!isStatic() && (class_name != null)) { // Instance method -> `this' is local var 0 |
|
141 |
addLocalVariable("this", ObjectType.getInstance(class_name), start, end); |
|
6 | 142 |
} |
143 |
} |
|
46174 | 144 |
if (arg_types != null) { |
145 |
final int size = arg_types.length; |
|
146 |
for (final Type arg_type : arg_types) { |
|
147 |
if (Type.VOID == arg_type) { |
|
148 |
throw new ClassGenException("'void' is an illegal argument type for a method"); |
|
149 |
} |
|
150 |
} |
|
151 |
if (arg_names != null) { // Names for variables provided? |
|
152 |
if (size != arg_names.length) { |
|
153 |
throw new ClassGenException("Mismatch in argument array lengths: " + size |
|
154 |
+ " vs. " + arg_names.length); |
|
155 |
} |
|
156 |
} else { // Give them dummy names |
|
157 |
arg_names = new String[size]; |
|
158 |
for (int i = 0; i < size; i++) { |
|
159 |
arg_names[i] = "arg" + i; |
|
160 |
} |
|
161 |
setArgumentNames(arg_names); |
|
162 |
} |
|
163 |
if (!abstract_) { |
|
164 |
for (int i = 0; i < size; i++) { |
|
165 |
addLocalVariable(arg_names[i], arg_types[i], start, end); |
|
166 |
} |
|
167 |
} |
|
168 |
} |
|
6 | 169 |
} |
170 |
||
46174 | 171 |
/** |
172 |
* Instantiate from existing method. |
|
173 |
* |
|
174 |
* @param m method |
|
175 |
* @param class_name class name containing this method |
|
176 |
* @param cp constant pool |
|
177 |
*/ |
|
178 |
public MethodGen(final Method m, final String class_name, final ConstantPoolGen cp) { |
|
179 |
this(m.getAccessFlags(), Type.getReturnType(m.getSignature()), Type.getArgumentTypes(m |
|
180 |
.getSignature()), null /* may be overridden anyway */, m.getName(), class_name, |
|
181 |
((m.getAccessFlags() & (Const.ACC_ABSTRACT | Const.ACC_NATIVE)) == 0) |
|
182 |
? new InstructionList(m.getCode().getCode()) |
|
183 |
: null, cp); |
|
184 |
final Attribute[] attributes = m.getAttributes(); |
|
185 |
for (final Attribute attribute : attributes) { |
|
186 |
Attribute a = attribute; |
|
187 |
if (a instanceof Code) { |
|
188 |
final Code c = (Code) a; |
|
189 |
setMaxStack(c.getMaxStack()); |
|
190 |
setMaxLocals(c.getMaxLocals()); |
|
191 |
final CodeException[] ces = c.getExceptionTable(); |
|
192 |
if (ces != null) { |
|
193 |
for (final CodeException ce : ces) { |
|
194 |
final int type = ce.getCatchType(); |
|
195 |
ObjectType c_type = null; |
|
196 |
if (type > 0) { |
|
197 |
final String cen = m.getConstantPool().getConstantString(type, |
|
198 |
Const.CONSTANT_Class); |
|
199 |
c_type = ObjectType.getInstance(cen); |
|
200 |
} |
|
201 |
final int end_pc = ce.getEndPC(); |
|
202 |
final int length = m.getCode().getCode().length; |
|
203 |
InstructionHandle end; |
|
204 |
if (length == end_pc) { // May happen, because end_pc is exclusive |
|
205 |
end = il.getEnd(); |
|
206 |
} else { |
|
207 |
end = il.findHandle(end_pc); |
|
208 |
end = end.getPrev(); // Make it inclusive |
|
209 |
} |
|
210 |
addExceptionHandler(il.findHandle(ce.getStartPC()), end, il.findHandle(ce |
|
211 |
.getHandlerPC()), c_type); |
|
212 |
} |
|
213 |
} |
|
214 |
final Attribute[] c_attributes = c.getAttributes(); |
|
215 |
for (final Attribute c_attribute : c_attributes) { |
|
216 |
a = c_attribute; |
|
217 |
if (a instanceof LineNumberTable) { |
|
218 |
final LineNumber[] ln = ((LineNumberTable) a).getLineNumberTable(); |
|
219 |
for (final LineNumber l : ln) { |
|
220 |
final InstructionHandle ih = il.findHandle(l.getStartPC()); |
|
221 |
if (ih != null) { |
|
222 |
addLineNumber(ih, l.getLineNumber()); |
|
223 |
} |
|
224 |
} |
|
225 |
} else if (a instanceof LocalVariableTable) { |
|
226 |
final LocalVariable[] lv = ((LocalVariableTable) a).getLocalVariableTable(); |
|
227 |
removeLocalVariables(); |
|
228 |
repairHandles(lv, false); |
|
229 |
} else if (a instanceof LocalVariableTypeTable) { |
|
230 |
LocalVariable[] lv = ((LocalVariableTypeTable) a).getLocalVariableTypeTable(); |
|
231 |
removeLocalVariableTypes(); |
|
232 |
repairHandles(lv, true); |
|
233 |
} else { |
|
234 |
addCodeAttribute(a); |
|
235 |
} |
|
236 |
} |
|
237 |
} else if (a instanceof ExceptionTable) { |
|
238 |
final String[] names = ((ExceptionTable) a).getExceptionNames(); |
|
239 |
for (final String name2 : names) { |
|
240 |
addException(name2); |
|
241 |
} |
|
242 |
} else if (a instanceof Annotations) { |
|
243 |
final Annotations runtimeAnnotations = (Annotations) a; |
|
244 |
final AnnotationEntry[] aes = runtimeAnnotations.getAnnotationEntries(); |
|
245 |
for (final AnnotationEntry element : aes) { |
|
246 |
addAnnotationEntry(new AnnotationEntryGen(element, cp, false)); |
|
247 |
} |
|
248 |
} else { |
|
249 |
addAttribute(a); |
|
250 |
} |
|
251 |
} |
|
6 | 252 |
} |
253 |
||
46174 | 254 |
private void repairHandles(final LocalVariable[] lv, boolean isLVT) { |
255 |
for (int k = 0; k < lv.length; k++) { |
|
256 |
LocalVariable l = lv[k]; |
|
257 |
InstructionHandle start = il.findHandle(l.getStartPC()); |
|
258 |
InstructionHandle end = il.findHandle(l.getStartPC() + l.getLength()); |
|
259 |
// Repair malformed handles |
|
260 |
if (null == start) { |
|
261 |
start = il.getStart(); |
|
262 |
} |
|
263 |
if (null == end) { |
|
264 |
end = il.getEnd(); |
|
265 |
} |
|
266 |
if (isLVT) { |
|
267 |
addLocalVariableType(l.getName(), Type.getType(l.getSignature()), |
|
268 |
l.getIndex(), start, end); |
|
269 |
} else { |
|
270 |
addLocalVariable(l.getName(), Type.getType(l.getSignature()), |
|
271 |
l.getIndex(), start, end); |
|
272 |
} |
|
273 |
} |
|
274 |
} |
|
6 | 275 |
|
46174 | 276 |
/** |
277 |
* Adds a local variable to this method. |
|
278 |
* |
|
279 |
* @param name variable name |
|
280 |
* @param type variable type |
|
281 |
* @param slot the index of the local variable, if type is long or double, |
|
282 |
* the next available index is slot+2 |
|
283 |
* @param start from where the variable is valid |
|
284 |
* @param end until where the variable is valid |
|
285 |
* @return new local variable object |
|
286 |
* @see LocalVariable |
|
287 |
*/ |
|
288 |
public LocalVariableGen addLocalVariable(final String name, final Type type, final int slot, |
|
289 |
final InstructionHandle start, final InstructionHandle end) { |
|
6 | 290 |
|
46174 | 291 |
final byte t = type.getType(); |
292 |
if (t != Const.T_ADDRESS) { |
|
293 |
final int add = type.getSize(); |
|
294 |
if (slot + add > max_locals) { |
|
295 |
max_locals = slot + add; |
|
296 |
} |
|
297 |
final LocalVariableGen l = new LocalVariableGen(slot, name, type, start, end); |
|
298 |
int i; |
|
299 |
if ((i = variable_vec.indexOf(l)) >= 0) { |
|
300 |
variable_vec.set(i, l); |
|
301 |
} else { |
|
302 |
variable_vec.add(l); |
|
303 |
} |
|
304 |
return l; |
|
305 |
} |
|
306 |
throw new IllegalArgumentException("Can not use " + type |
|
307 |
+ " as type for local variable"); |
|
308 |
} |
|
6 | 309 |
|
46174 | 310 |
/** |
311 |
* Adds a local variable to this method and assigns an index automatically. |
|
312 |
* |
|
313 |
* @param name variable name |
|
314 |
* @param type variable type |
|
315 |
* @param start from where the variable is valid, if this is null, it is |
|
316 |
* valid from the start |
|
317 |
* @param end until where the variable is valid, if this is null, it is |
|
318 |
* valid to the end |
|
319 |
* @return new local variable object |
|
320 |
* @see LocalVariable |
|
321 |
*/ |
|
322 |
public LocalVariableGen addLocalVariable(final String name, final Type type, |
|
323 |
final InstructionHandle start, final InstructionHandle end) { |
|
324 |
return addLocalVariable(name, type, max_locals, start, end); |
|
325 |
} |
|
326 |
||
327 |
/** |
|
328 |
* Remove a local variable, its slot will not be reused, if you do not use |
|
329 |
* addLocalVariable with an explicit index argument. |
|
330 |
*/ |
|
331 |
public void removeLocalVariable(final LocalVariableGen l) { |
|
332 |
variable_vec.remove(l); |
|
333 |
} |
|
6 | 334 |
|
46174 | 335 |
/** |
336 |
* Remove all local variables. |
|
337 |
*/ |
|
338 |
public void removeLocalVariables() { |
|
339 |
variable_vec.clear(); |
|
340 |
} |
|
6 | 341 |
|
46174 | 342 |
/* |
343 |
* If the range of the variable has not been set yet, it will be set to be valid from |
|
344 |
* the start to the end of the instruction list. |
|
345 |
* |
|
346 |
* @return array of declared local variables sorted by index |
|
347 |
*/ |
|
348 |
public LocalVariableGen[] getLocalVariables() { |
|
349 |
return getLocalVariableOrTypes(false); |
|
350 |
} |
|
351 |
||
352 |
/* |
|
353 |
* If the range of the variable has not been set yet, it will be set to be |
|
354 |
* valid from the start to the end of the instruction list. |
|
355 |
* |
|
356 |
* @return array of declared local variable types sorted by index |
|
357 |
*/ |
|
358 |
private LocalVariableGen[] getLocalVariableTypes() { |
|
359 |
return getLocalVariableOrTypes(true); |
|
6 | 360 |
} |
361 |
||
46174 | 362 |
/* |
363 |
* If the range of the variable or type has not been set yet, it will be set |
|
364 |
* to be valid from the start to the end of the instruction list. |
|
365 |
* |
|
366 |
* @return array of declared local variables or types sorted by index |
|
367 |
*/ |
|
368 |
private LocalVariableGen[] getLocalVariableOrTypes(boolean isLVT) { |
|
369 |
int size = (isLVT) ? type_vec.size() : variable_vec.size(); |
|
370 |
LocalVariableGen[] lg = new LocalVariableGen[size]; |
|
371 |
if (isLVT) { |
|
372 |
type_vec.toArray(lg); |
|
373 |
} else { |
|
374 |
variable_vec.toArray(lg); |
|
375 |
} |
|
376 |
||
377 |
for (int i = 0; i < size; i++) { |
|
378 |
if (lg[i].getStart() == null) { |
|
379 |
lg[i].setStart(il.getStart()); |
|
380 |
} |
|
381 |
||
382 |
if (lg[i].getEnd() == null) { |
|
383 |
lg[i].setEnd(il.getEnd()); |
|
384 |
} |
|
385 |
} |
|
386 |
||
387 |
if (size > 1) { |
|
388 |
Arrays.sort(lg, new Comparator<LocalVariableGen>() { |
|
389 |
@Override |
|
390 |
public int compare(final LocalVariableGen o1, final LocalVariableGen o2) { |
|
391 |
return o1.getIndex() - o2.getIndex(); |
|
392 |
} |
|
393 |
}); |
|
394 |
} |
|
395 |
||
396 |
return lg; |
|
397 |
} |
|
398 |
||
399 |
/** |
|
400 |
* @return `LocalVariableTable' attribute of all the local variables of this |
|
401 |
* method. |
|
402 |
*/ |
|
403 |
public LocalVariableTable getLocalVariableTable(final ConstantPoolGen cp) { |
|
404 |
final LocalVariableGen[] lg = getLocalVariables(); |
|
405 |
final int size = lg.length; |
|
406 |
final LocalVariable[] lv = new LocalVariable[size]; |
|
407 |
for (int i = 0; i < size; i++) { |
|
408 |
lv[i] = lg[i].getLocalVariable(cp); |
|
409 |
} |
|
410 |
return new LocalVariableTable(cp.addUtf8("LocalVariableTable"), 2 + lv.length * 10, lv, cp |
|
411 |
.getConstantPool()); |
|
412 |
} |
|
413 |
||
414 |
/** |
|
415 |
* @return `LocalVariableTypeTable' attribute of all the local variable |
|
416 |
* types of this method. |
|
417 |
*/ |
|
418 |
public LocalVariableTypeTable getLocalVariableTypeTable(ConstantPoolGen cp) { |
|
419 |
LocalVariableGen[] lg = getLocalVariableTypes(); |
|
420 |
int size = lg.length; |
|
421 |
LocalVariable[] lv = new LocalVariable[size]; |
|
422 |
||
423 |
for (int i = 0; i < size; i++) { |
|
424 |
lv[i] = lg[i].getLocalVariable(cp); |
|
425 |
} |
|
426 |
||
427 |
return new LocalVariableTypeTable(cp.addUtf8("LocalVariableTypeTable"), |
|
428 |
2 + lv.length * 10, lv, cp.getConstantPool()); |
|
429 |
} |
|
6 | 430 |
|
46174 | 431 |
/** |
432 |
* Adds a local variable type to this method. |
|
433 |
* |
|
434 |
* @param name variable name |
|
435 |
* @param type variable type |
|
436 |
* @param slot the index of the local variable, if type is long or double, |
|
437 |
* the next available index is slot+2 |
|
438 |
* @param start from where the variable is valid |
|
439 |
* @param end until where the variable is valid |
|
440 |
* @return new local variable object |
|
441 |
* @see LocalVariable |
|
442 |
*/ |
|
443 |
private LocalVariableGen addLocalVariableType(String name, Type type, int slot, |
|
444 |
InstructionHandle start, |
|
445 |
InstructionHandle end) { |
|
446 |
byte t = type.getType(); |
|
447 |
||
448 |
if (t != Const.T_ADDRESS) { |
|
449 |
int add = type.getSize(); |
|
450 |
||
451 |
if (slot + add > max_locals) { |
|
452 |
max_locals = slot + add; |
|
453 |
} |
|
454 |
||
455 |
LocalVariableGen l = new LocalVariableGen(slot, name, type, start, end); |
|
456 |
int i; |
|
457 |
||
458 |
if ((i = type_vec.indexOf(l)) >= 0) // Overwrite if necessary |
|
459 |
{ |
|
460 |
type_vec.set(i, l); |
|
461 |
} else { |
|
462 |
type_vec.add(l); |
|
463 |
} |
|
464 |
||
465 |
return l; |
|
466 |
} else { |
|
467 |
throw new IllegalArgumentException("Can not use " + type |
|
468 |
+ " as type for local variable"); |
|
469 |
} |
|
470 |
} |
|
471 |
||
472 |
/** |
|
473 |
* Remove all local variable types. |
|
474 |
*/ |
|
475 |
private void removeLocalVariableTypes() { |
|
476 |
type_vec.clear(); |
|
477 |
} |
|
478 |
||
479 |
/** |
|
480 |
* Give an instruction a line number corresponding to the source code line. |
|
481 |
* |
|
482 |
* @param ih instruction to tag |
|
483 |
* @return new line number object |
|
484 |
* @see LineNumber |
|
485 |
*/ |
|
486 |
public LineNumberGen addLineNumber(final InstructionHandle ih, final int src_line) { |
|
487 |
final LineNumberGen l = new LineNumberGen(ih, src_line); |
|
488 |
line_number_vec.add(l); |
|
489 |
return l; |
|
490 |
} |
|
491 |
||
492 |
/** |
|
493 |
* Remove a line number. |
|
494 |
*/ |
|
495 |
public void removeLineNumber(final LineNumberGen l) { |
|
496 |
line_number_vec.remove(l); |
|
497 |
} |
|
498 |
||
499 |
/** |
|
500 |
* Remove all line numbers. |
|
501 |
*/ |
|
502 |
public void removeLineNumbers() { |
|
503 |
line_number_vec.clear(); |
|
504 |
} |
|
6 | 505 |
|
46174 | 506 |
/* |
507 |
* @return array of line numbers |
|
508 |
*/ |
|
509 |
public LineNumberGen[] getLineNumbers() { |
|
510 |
final LineNumberGen[] lg = new LineNumberGen[line_number_vec.size()]; |
|
511 |
line_number_vec.toArray(lg); |
|
512 |
return lg; |
|
513 |
} |
|
514 |
||
515 |
/** |
|
516 |
* @return `LineNumberTable' attribute of all the local variables of this |
|
517 |
* method. |
|
518 |
*/ |
|
519 |
public LineNumberTable getLineNumberTable(final ConstantPoolGen cp) { |
|
520 |
final int size = line_number_vec.size(); |
|
521 |
final LineNumber[] ln = new LineNumber[size]; |
|
522 |
for (int i = 0; i < size; i++) { |
|
523 |
ln[i] = line_number_vec.get(i).getLineNumber(); |
|
524 |
} |
|
525 |
return new LineNumberTable(cp.addUtf8("LineNumberTable"), 2 + ln.length * 4, ln, cp |
|
526 |
.getConstantPool()); |
|
527 |
} |
|
528 |
||
529 |
/** |
|
530 |
* Add an exception handler, i.e., specify region where a handler is active |
|
531 |
* and an instruction where the actual handling is done. |
|
532 |
* |
|
533 |
* @param start_pc Start of region (inclusive) |
|
534 |
* @param end_pc End of region (inclusive) |
|
535 |
* @param handler_pc Where handling is done |
|
536 |
* @param catch_type class type of handled exception or null if any |
|
537 |
* exception is handled |
|
538 |
* @return new exception handler object |
|
539 |
*/ |
|
540 |
public CodeExceptionGen addExceptionHandler(final InstructionHandle start_pc, |
|
541 |
final InstructionHandle end_pc, final InstructionHandle handler_pc, final ObjectType catch_type) { |
|
542 |
if ((start_pc == null) || (end_pc == null) || (handler_pc == null)) { |
|
543 |
throw new ClassGenException("Exception handler target is null instruction"); |
|
544 |
} |
|
545 |
final CodeExceptionGen c = new CodeExceptionGen(start_pc, end_pc, handler_pc, catch_type); |
|
546 |
exception_vec.add(c); |
|
547 |
return c; |
|
548 |
} |
|
549 |
||
550 |
/** |
|
551 |
* Remove an exception handler. |
|
552 |
*/ |
|
553 |
public void removeExceptionHandler(final CodeExceptionGen c) { |
|
554 |
exception_vec.remove(c); |
|
555 |
} |
|
556 |
||
557 |
/** |
|
558 |
* Remove all line numbers. |
|
559 |
*/ |
|
560 |
public void removeExceptionHandlers() { |
|
561 |
exception_vec.clear(); |
|
562 |
} |
|
563 |
||
564 |
/* |
|
565 |
* @return array of declared exception handlers |
|
566 |
*/ |
|
567 |
public CodeExceptionGen[] getExceptionHandlers() { |
|
568 |
final CodeExceptionGen[] cg = new CodeExceptionGen[exception_vec.size()]; |
|
569 |
exception_vec.toArray(cg); |
|
570 |
return cg; |
|
571 |
} |
|
27537
77d9ab3c1028
8064516: BCEL still corrupts generic methods if bytecode offsets are modified
dbuck
parents:
27535
diff
changeset
|
572 |
|
46174 | 573 |
/** |
574 |
* @return code exceptions for `Code' attribute |
|
575 |
*/ |
|
576 |
private CodeException[] getCodeExceptions() { |
|
577 |
final int size = exception_vec.size(); |
|
578 |
final CodeException[] c_exc = new CodeException[size]; |
|
579 |
for (int i = 0; i < size; i++) { |
|
580 |
final CodeExceptionGen c = exception_vec.get(i); |
|
581 |
c_exc[i] = c.getCodeException(super.getConstantPool()); |
|
582 |
} |
|
583 |
return c_exc; |
|
584 |
} |
|
585 |
||
586 |
/** |
|
587 |
* Add an exception possibly thrown by this method. |
|
588 |
* |
|
589 |
* @param class_name (fully qualified) name of exception |
|
590 |
*/ |
|
591 |
public void addException(final String class_name) { |
|
592 |
throws_vec.add(class_name); |
|
593 |
} |
|
594 |
||
595 |
/** |
|
596 |
* Remove an exception. |
|
597 |
*/ |
|
598 |
public void removeException(final String c) { |
|
599 |
throws_vec.remove(c); |
|
600 |
} |
|
601 |
||
602 |
/** |
|
603 |
* Remove all exceptions. |
|
604 |
*/ |
|
605 |
public void removeExceptions() { |
|
606 |
throws_vec.clear(); |
|
607 |
} |
|
27537
77d9ab3c1028
8064516: BCEL still corrupts generic methods if bytecode offsets are modified
dbuck
parents:
27535
diff
changeset
|
608 |
|
46174 | 609 |
/* |
610 |
* @return array of thrown exceptions |
|
611 |
*/ |
|
612 |
public String[] getExceptions() { |
|
613 |
final String[] e = new String[throws_vec.size()]; |
|
614 |
throws_vec.toArray(e); |
|
615 |
return e; |
|
616 |
} |
|
617 |
||
618 |
/** |
|
619 |
* @return `Exceptions' attribute of all the exceptions thrown by this |
|
620 |
* method. |
|
621 |
*/ |
|
622 |
private ExceptionTable getExceptionTable(final ConstantPoolGen cp) { |
|
623 |
final int size = throws_vec.size(); |
|
624 |
final int[] ex = new int[size]; |
|
625 |
for (int i = 0; i < size; i++) { |
|
626 |
ex[i] = cp.addClass(throws_vec.get(i)); |
|
627 |
} |
|
628 |
return new ExceptionTable(cp.addUtf8("Exceptions"), 2 + 2 * size, ex, cp.getConstantPool()); |
|
629 |
} |
|
630 |
||
631 |
/** |
|
632 |
* Add an attribute to the code. Currently, the JVM knows about the |
|
633 |
* LineNumberTable, LocalVariableTable and StackMap attributes, where the |
|
634 |
* former two will be generated automatically and the latter is used for the |
|
635 |
* MIDP only. Other attributes will be ignored by the JVM but do no harm. |
|
636 |
* |
|
637 |
* @param a attribute to be added |
|
638 |
*/ |
|
639 |
public void addCodeAttribute(final Attribute a) { |
|
640 |
code_attrs_vec.add(a); |
|
641 |
} |
|
642 |
||
643 |
/** |
|
644 |
* Remove a code attribute. |
|
645 |
*/ |
|
646 |
public void removeCodeAttribute(final Attribute a) { |
|
647 |
code_attrs_vec.remove(a); |
|
648 |
} |
|
649 |
||
650 |
/** |
|
651 |
* Remove all code attributes. |
|
652 |
*/ |
|
653 |
public void removeCodeAttributes() { |
|
654 |
code_attrs_vec.clear(); |
|
27537
77d9ab3c1028
8064516: BCEL still corrupts generic methods if bytecode offsets are modified
dbuck
parents:
27535
diff
changeset
|
655 |
} |
77d9ab3c1028
8064516: BCEL still corrupts generic methods if bytecode offsets are modified
dbuck
parents:
27535
diff
changeset
|
656 |
|
46174 | 657 |
/** |
658 |
* @return all attributes of this method. |
|
659 |
*/ |
|
660 |
public Attribute[] getCodeAttributes() { |
|
661 |
final Attribute[] attributes = new Attribute[code_attrs_vec.size()]; |
|
662 |
code_attrs_vec.toArray(attributes); |
|
663 |
return attributes; |
|
664 |
} |
|
6 | 665 |
|
46174 | 666 |
/** |
667 |
* @since 6.0 |
|
668 |
*/ |
|
669 |
public void addAnnotationsAsAttribute(final ConstantPoolGen cp) { |
|
670 |
final Attribute[] attrs = AnnotationEntryGen.getAnnotationAttributes(cp, super.getAnnotationEntries()); |
|
671 |
for (final Attribute attr : attrs) { |
|
672 |
addAttribute(attr); |
|
673 |
} |
|
674 |
} |
|
27537
77d9ab3c1028
8064516: BCEL still corrupts generic methods if bytecode offsets are modified
dbuck
parents:
27535
diff
changeset
|
675 |
|
46174 | 676 |
/** |
677 |
* @since 6.0 |
|
678 |
*/ |
|
679 |
public void addParameterAnnotationsAsAttribute(final ConstantPoolGen cp) { |
|
680 |
if (!hasParameterAnnotations) { |
|
681 |
return; |
|
682 |
} |
|
683 |
final Attribute[] attrs = AnnotationEntryGen.getParameterAnnotationAttributes(cp, param_annotations); |
|
684 |
if (attrs != null) { |
|
685 |
for (final Attribute attr : attrs) { |
|
686 |
addAttribute(attr); |
|
687 |
} |
|
688 |
} |
|
689 |
} |
|
27537
77d9ab3c1028
8064516: BCEL still corrupts generic methods if bytecode offsets are modified
dbuck
parents:
27535
diff
changeset
|
690 |
|
46174 | 691 |
/** |
692 |
* Get method object. Never forget to call setMaxStack() or |
|
693 |
* setMaxStack(max), respectively, before calling this method (the same |
|
694 |
* applies for max locals). |
|
695 |
* |
|
696 |
* @return method object |
|
697 |
*/ |
|
698 |
public Method getMethod() { |
|
699 |
final String signature = getSignature(); |
|
700 |
final ConstantPoolGen _cp = super.getConstantPool(); |
|
701 |
final int name_index = _cp.addUtf8(super.getName()); |
|
702 |
final int signature_index = _cp.addUtf8(signature); |
|
703 |
/* Also updates positions of instructions, i.e., their indices |
|
704 |
*/ |
|
705 |
byte[] byte_code = null; |
|
706 |
if (il != null) { |
|
707 |
byte_code = il.getByteCode(); |
|
708 |
} |
|
709 |
LineNumberTable lnt = null; |
|
710 |
LocalVariableTable lvt = null; |
|
711 |
LocalVariableTypeTable lvtt = null; |
|
27537
77d9ab3c1028
8064516: BCEL still corrupts generic methods if bytecode offsets are modified
dbuck
parents:
27535
diff
changeset
|
712 |
|
46174 | 713 |
/* Create LocalVariableTable, LocalvariableTypeTable, and LineNumberTable |
714 |
* attributes (for debuggers, e.g.) |
|
715 |
*/ |
|
716 |
if ((variable_vec.size() > 0) && !strip_attributes) { |
|
717 |
addCodeAttribute(lvt = getLocalVariableTable(_cp)); |
|
718 |
} |
|
6 | 719 |
|
46174 | 720 |
if ((type_vec.size() > 0) && !strip_attributes) { |
721 |
addCodeAttribute(lvtt = getLocalVariableTypeTable(_cp)); |
|
722 |
} |
|
6 | 723 |
|
46174 | 724 |
if ((line_number_vec.size() > 0) && !strip_attributes) { |
725 |
addCodeAttribute(lnt = getLineNumberTable(_cp)); |
|
726 |
} |
|
727 |
final Attribute[] code_attrs = getCodeAttributes(); |
|
728 |
/* Each attribute causes 6 additional header bytes |
|
729 |
*/ |
|
730 |
int attrs_len = 0; |
|
731 |
for (final Attribute code_attr : code_attrs) { |
|
732 |
attrs_len += code_attr.getLength() + 6; |
|
733 |
} |
|
734 |
final CodeException[] c_exc = getCodeExceptions(); |
|
735 |
final int exc_len = c_exc.length * 8; // Every entry takes 8 bytes |
|
736 |
Code code = null; |
|
737 |
if ((il != null) && !isAbstract() && !isNative()) { |
|
738 |
// Remove any stale code attribute |
|
739 |
final Attribute[] attributes = getAttributes(); |
|
740 |
for (final Attribute a : attributes) { |
|
741 |
if (a instanceof Code) { |
|
742 |
removeAttribute(a); |
|
743 |
} |
|
744 |
} |
|
745 |
code = new Code(_cp.addUtf8("Code"), 8 + byte_code.length + // prologue byte code |
|
746 |
2 + exc_len + // exceptions |
|
747 |
2 + attrs_len, // attributes |
|
748 |
max_stack, max_locals, byte_code, c_exc, code_attrs, _cp.getConstantPool()); |
|
749 |
addAttribute(code); |
|
750 |
} |
|
751 |
addAnnotationsAsAttribute(_cp); |
|
752 |
addParameterAnnotationsAsAttribute(_cp); |
|
753 |
ExceptionTable et = null; |
|
754 |
if (throws_vec.size() > 0) { |
|
755 |
addAttribute(et = getExceptionTable(_cp)); |
|
756 |
// Add `Exceptions' if there are "throws" clauses |
|
757 |
} |
|
758 |
final Method m = new Method(super.getAccessFlags(), name_index, signature_index, getAttributes(), _cp |
|
759 |
.getConstantPool()); |
|
760 |
// Undo effects of adding attributes |
|
761 |
if (lvt != null) { |
|
762 |
removeCodeAttribute(lvt); |
|
763 |
} |
|
764 |
if (lvtt != null) { |
|
765 |
removeCodeAttribute(lvtt); |
|
766 |
} |
|
767 |
if (lnt != null) { |
|
768 |
removeCodeAttribute(lnt); |
|
769 |
} |
|
770 |
if (code != null) { |
|
771 |
removeAttribute(code); |
|
772 |
} |
|
773 |
if (et != null) { |
|
774 |
removeAttribute(et); |
|
775 |
} |
|
776 |
return m; |
|
777 |
} |
|
6 | 778 |
|
46174 | 779 |
/** |
780 |
* Remove all NOPs from the instruction list (if possible) and update every |
|
781 |
* object referring to them, i.e., branch instructions, local variables and |
|
782 |
* exception handlers. |
|
783 |
*/ |
|
784 |
public void removeNOPs() { |
|
785 |
if (il != null) { |
|
786 |
InstructionHandle next; |
|
787 |
/* Check branch instructions. |
|
788 |
*/ |
|
789 |
for (InstructionHandle ih = il.getStart(); ih != null; ih = next) { |
|
790 |
next = ih.getNext(); |
|
791 |
if ((next != null) && (ih.getInstruction() instanceof NOP)) { |
|
792 |
try { |
|
793 |
il.delete(ih); |
|
794 |
} catch (final TargetLostException e) { |
|
795 |
for (final InstructionHandle target : e.getTargets()) { |
|
796 |
for (final InstructionTargeter targeter : target.getTargeters()) { |
|
797 |
targeter.updateTarget(target, next); |
|
798 |
} |
|
799 |
} |
|
800 |
} |
|
801 |
} |
|
802 |
} |
|
803 |
} |
|
804 |
} |
|
6 | 805 |
|
46174 | 806 |
/** |
807 |
* Set maximum number of local variables. |
|
808 |
*/ |
|
809 |
public void setMaxLocals(final int m) { |
|
810 |
max_locals = m; |
|
811 |
} |
|
6 | 812 |
|
46174 | 813 |
public int getMaxLocals() { |
814 |
return max_locals; |
|
815 |
} |
|
6 | 816 |
|
46174 | 817 |
/** |
818 |
* Set maximum stack size for this method. |
|
819 |
*/ |
|
820 |
public void setMaxStack(final int m) { // TODO could be package-protected? |
|
821 |
max_stack = m; |
|
822 |
} |
|
823 |
||
824 |
public int getMaxStack() { |
|
825 |
return max_stack; |
|
826 |
} |
|
6 | 827 |
|
46174 | 828 |
/** |
829 |
* @return class that contains this method |
|
830 |
*/ |
|
831 |
public String getClassName() { |
|
832 |
return class_name; |
|
833 |
} |
|
834 |
||
835 |
public void setClassName(final String class_name) { // TODO could be package-protected? |
|
836 |
this.class_name = class_name; |
|
837 |
} |
|
6 | 838 |
|
46174 | 839 |
public void setReturnType(final Type return_type) { |
840 |
setType(return_type); |
|
841 |
} |
|
6 | 842 |
|
46174 | 843 |
public Type getReturnType() { |
844 |
return getType(); |
|
845 |
} |
|
846 |
||
847 |
public void setArgumentTypes(final Type[] arg_types) { |
|
848 |
this.arg_types = arg_types; |
|
849 |
} |
|
6 | 850 |
|
46174 | 851 |
public Type[] getArgumentTypes() { |
852 |
return arg_types.clone(); |
|
853 |
} |
|
6 | 854 |
|
46174 | 855 |
public void setArgumentType(final int i, final Type type) { |
856 |
arg_types[i] = type; |
|
857 |
} |
|
858 |
||
859 |
public Type getArgumentType(final int i) { |
|
860 |
return arg_types[i]; |
|
861 |
} |
|
6 | 862 |
|
46174 | 863 |
public void setArgumentNames(final String[] arg_names) { |
864 |
this.arg_names = arg_names; |
|
865 |
} |
|
866 |
||
867 |
public String[] getArgumentNames() { |
|
868 |
return arg_names.clone(); |
|
869 |
} |
|
6 | 870 |
|
46174 | 871 |
public void setArgumentName(final int i, final String name) { |
872 |
arg_names[i] = name; |
|
873 |
} |
|
6 | 874 |
|
46174 | 875 |
public String getArgumentName(final int i) { |
876 |
return arg_names[i]; |
|
877 |
} |
|
878 |
||
879 |
public InstructionList getInstructionList() { |
|
880 |
return il; |
|
881 |
} |
|
6 | 882 |
|
46174 | 883 |
public void setInstructionList(final InstructionList il) { // TODO could be package-protected? |
884 |
this.il = il; |
|
885 |
} |
|
886 |
||
887 |
@Override |
|
888 |
public String getSignature() { |
|
889 |
return Type.getMethodSignature(super.getType(), arg_types); |
|
890 |
} |
|
6 | 891 |
|
46174 | 892 |
/** |
893 |
* Computes max. stack size by performing control flow analysis. |
|
6 | 894 |
*/ |
46174 | 895 |
public void setMaxStack() { // TODO could be package-protected? (some tests would need repackaging) |
896 |
if (il != null) { |
|
897 |
max_stack = getMaxStack(super.getConstantPool(), il, getExceptionHandlers()); |
|
898 |
} else { |
|
899 |
max_stack = 0; |
|
900 |
} |
|
901 |
} |
|
6 | 902 |
|
46174 | 903 |
/** |
904 |
* Compute maximum number of local variables. |
|
6 | 905 |
*/ |
46174 | 906 |
public void setMaxLocals() { // TODO could be package-protected? (some tests would need repackaging) |
907 |
if (il != null) { |
|
908 |
int max = isStatic() ? 0 : 1; |
|
909 |
if (arg_types != null) { |
|
910 |
for (final Type arg_type : arg_types) { |
|
911 |
max += arg_type.getSize(); |
|
912 |
} |
|
913 |
} |
|
914 |
for (InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) { |
|
915 |
final Instruction ins = ih.getInstruction(); |
|
916 |
if ((ins instanceof LocalVariableInstruction) || (ins instanceof RET) |
|
917 |
|| (ins instanceof IINC)) { |
|
918 |
final int index = ((IndexedInstruction) ins).getIndex() |
|
919 |
+ ((TypedInstruction) ins).getType(super.getConstantPool()).getSize(); |
|
920 |
if (index > max) { |
|
921 |
max = index; |
|
922 |
} |
|
923 |
} |
|
924 |
} |
|
925 |
max_locals = max; |
|
926 |
} else { |
|
927 |
max_locals = 0; |
|
928 |
} |
|
929 |
} |
|
6 | 930 |
|
46174 | 931 |
/** |
932 |
* Do not/Do produce attributes code attributesLineNumberTable and |
|
933 |
* LocalVariableTable, like javac -O |
|
934 |
*/ |
|
935 |
public void stripAttributes(final boolean flag) { |
|
936 |
strip_attributes = flag; |
|
937 |
} |
|
6 | 938 |
|
46174 | 939 |
static final class BranchTarget { |
940 |
||
941 |
final InstructionHandle target; |
|
942 |
final int stackDepth; |
|
6 | 943 |
|
46174 | 944 |
BranchTarget(final InstructionHandle target, final int stackDepth) { |
945 |
this.target = target; |
|
946 |
this.stackDepth = stackDepth; |
|
947 |
} |
|
6 | 948 |
} |
949 |
||
46174 | 950 |
static final class BranchStack { |
6 | 951 |
|
46174 | 952 |
private final Stack<BranchTarget> branchTargets = new Stack<>(); |
953 |
private final Map<InstructionHandle, BranchTarget> visitedTargets = new HashMap<>(); |
|
6 | 954 |
|
46174 | 955 |
public void push(final InstructionHandle target, final int stackDepth) { |
956 |
if (visited(target)) { |
|
957 |
return; |
|
958 |
} |
|
959 |
branchTargets.push(visit(target, stackDepth)); |
|
960 |
} |
|
6 | 961 |
|
46174 | 962 |
public BranchTarget pop() { |
963 |
if (!branchTargets.empty()) { |
|
964 |
final BranchTarget bt = branchTargets.pop(); |
|
965 |
return bt; |
|
6 | 966 |
} |
46174 | 967 |
return null; |
6 | 968 |
} |
969 |
||
46174 | 970 |
private BranchTarget visit(final InstructionHandle target, final int stackDepth) { |
971 |
final BranchTarget bt = new BranchTarget(target, stackDepth); |
|
972 |
visitedTargets.put(target, bt); |
|
973 |
return bt; |
|
974 |
} |
|
6 | 975 |
|
46174 | 976 |
private boolean visited(final InstructionHandle target) { |
977 |
return visitedTargets.get(target) != null; |
|
978 |
} |
|
979 |
} |
|
6 | 980 |
|
46174 | 981 |
/** |
982 |
* Computes stack usage of an instruction list by performing control flow |
|
983 |
* analysis. |
|
984 |
* |
|
985 |
* @return maximum stack depth used by method |
|
986 |
*/ |
|
987 |
public static int getMaxStack(final ConstantPoolGen cp, final InstructionList il, |
|
988 |
final CodeExceptionGen[] et) { |
|
989 |
final BranchStack branchTargets = new BranchStack(); |
|
990 |
/* Initially, populate the branch stack with the exception |
|
991 |
* handlers, because these aren't (necessarily) branched to |
|
992 |
* explicitly. in each case, the stack will have depth 1, |
|
993 |
* containing the exception object. |
|
994 |
*/ |
|
995 |
for (final CodeExceptionGen element : et) { |
|
996 |
final InstructionHandle handler_pc = element.getHandlerPC(); |
|
997 |
if (handler_pc != null) { |
|
998 |
branchTargets.push(handler_pc, 1); |
|
999 |
} |
|
1000 |
} |
|
1001 |
int stackDepth = 0; |
|
1002 |
int maxStackDepth = 0; |
|
1003 |
InstructionHandle ih = il.getStart(); |
|
1004 |
while (ih != null) { |
|
1005 |
final Instruction instruction = ih.getInstruction(); |
|
1006 |
final short opcode = instruction.getOpcode(); |
|
1007 |
final int delta = instruction.produceStack(cp) - instruction.consumeStack(cp); |
|
1008 |
stackDepth += delta; |
|
1009 |
if (stackDepth > maxStackDepth) { |
|
1010 |
maxStackDepth = stackDepth; |
|
1011 |
} |
|
1012 |
// choose the next instruction based on whether current is a branch. |
|
1013 |
if (instruction instanceof BranchInstruction) { |
|
1014 |
final BranchInstruction branch = (BranchInstruction) instruction; |
|
1015 |
if (instruction instanceof Select) { |
|
1016 |
// explore all of the select's targets. the default target is handled below. |
|
1017 |
final Select select = (Select) branch; |
|
1018 |
final InstructionHandle[] targets = select.getTargets(); |
|
1019 |
for (final InstructionHandle target : targets) { |
|
1020 |
branchTargets.push(target, stackDepth); |
|
1021 |
} |
|
1022 |
// nothing to fall through to. |
|
1023 |
ih = null; |
|
1024 |
} else if (!(branch instanceof IfInstruction)) { |
|
1025 |
// if an instruction that comes back to following PC, |
|
1026 |
// push next instruction, with stack depth reduced by 1. |
|
1027 |
if (opcode == Const.JSR || opcode == Const.JSR_W) { |
|
1028 |
branchTargets.push(ih.getNext(), stackDepth - 1); |
|
1029 |
} |
|
1030 |
ih = null; |
|
1031 |
} |
|
1032 |
// for all branches, the target of the branch is pushed on the branch stack. |
|
1033 |
// conditional branches have a fall through case, selects don't, and |
|
1034 |
// jsr/jsr_w return to the next instruction. |
|
1035 |
branchTargets.push(branch.getTarget(), stackDepth); |
|
1036 |
} else { |
|
1037 |
// check for instructions that terminate the method. |
|
1038 |
if (opcode == Const.ATHROW || opcode == Const.RET |
|
1039 |
|| (opcode >= Const.IRETURN && opcode <= Const.RETURN)) { |
|
1040 |
ih = null; |
|
1041 |
} |
|
1042 |
} |
|
1043 |
// normal case, go to the next instruction. |
|
1044 |
if (ih != null) { |
|
1045 |
ih = ih.getNext(); |
|
1046 |
} |
|
1047 |
// if we have no more instructions, see if there are any deferred branches to explore. |
|
1048 |
if (ih == null) { |
|
1049 |
final BranchTarget bt = branchTargets.pop(); |
|
1050 |
if (bt != null) { |
|
1051 |
ih = bt.target; |
|
1052 |
stackDepth = bt.stackDepth; |
|
1053 |
} |
|
1054 |
} |
|
1055 |
} |
|
1056 |
return maxStackDepth; |
|
1057 |
} |
|
6 | 1058 |
|
46174 | 1059 |
private List<MethodObserver> observers; |
6 | 1060 |
|
46174 | 1061 |
/** |
1062 |
* Add observer for this object. |
|
1063 |
*/ |
|
1064 |
public void addObserver(final MethodObserver o) { |
|
1065 |
if (observers == null) { |
|
1066 |
observers = new ArrayList<>(); |
|
6 | 1067 |
} |
46174 | 1068 |
observers.add(o); |
1069 |
} |
|
6 | 1070 |
|
46174 | 1071 |
/** |
1072 |
* Remove observer for this object. |
|
1073 |
*/ |
|
1074 |
public void removeObserver(final MethodObserver o) { |
|
1075 |
if (observers != null) { |
|
1076 |
observers.remove(o); |
|
1077 |
} |
|
1078 |
} |
|
6 | 1079 |
|
46174 | 1080 |
/** |
1081 |
* Call notify() method on all observers. This method is not called |
|
1082 |
* automatically whenever the state has changed, but has to be called by the |
|
1083 |
* user after he has finished editing the object. |
|
1084 |
*/ |
|
1085 |
public void update() { |
|
1086 |
if (observers != null) { |
|
1087 |
for (final MethodObserver observer : observers) { |
|
1088 |
observer.notify(this); |
|
1089 |
} |
|
1090 |
} |
|
6 | 1091 |
} |
1092 |
||
46174 | 1093 |
/** |
1094 |
* Return string representation close to declaration format, e.g. public |
|
1095 |
* static void main(String[]) throws IOException' |
|
1096 |
* |
|
1097 |
* @return String representation of the method. |
|
1098 |
*/ |
|
1099 |
@Override |
|
1100 |
public final String toString() { |
|
1101 |
final String access = Utility.accessToString(super.getAccessFlags()); |
|
1102 |
String signature = Type.getMethodSignature(super.getType(), arg_types); |
|
1103 |
signature = Utility.methodSignatureToString(signature, super.getName(), access, true, |
|
1104 |
getLocalVariableTable(super.getConstantPool())); |
|
1105 |
final StringBuilder buf = new StringBuilder(signature); |
|
1106 |
for (final Attribute a : getAttributes()) { |
|
1107 |
if (!((a instanceof Code) || (a instanceof ExceptionTable))) { |
|
1108 |
buf.append(" [").append(a).append("]"); |
|
1109 |
} |
|
1110 |
} |
|
6 | 1111 |
|
46174 | 1112 |
if (throws_vec.size() > 0) { |
1113 |
for (final String throwsDescriptor : throws_vec) { |
|
1114 |
buf.append("\n\t\tthrows ").append(throwsDescriptor); |
|
1115 |
} |
|
1116 |
} |
|
1117 |
return buf.toString(); |
|
6 | 1118 |
} |
1119 |
||
46174 | 1120 |
/** |
1121 |
* @return deep copy of this method |
|
1122 |
*/ |
|
1123 |
public MethodGen copy(final String class_name, final ConstantPoolGen cp) { |
|
1124 |
final Method m = ((MethodGen) clone()).getMethod(); |
|
1125 |
final MethodGen mg = new MethodGen(m, class_name, super.getConstantPool()); |
|
1126 |
if (super.getConstantPool() != cp) { |
|
1127 |
mg.setConstantPool(cp); |
|
1128 |
mg.getInstructionList().replaceConstantPool(super.getConstantPool(), cp); |
|
1129 |
} |
|
1130 |
return mg; |
|
6 | 1131 |
} |
1132 |
||
46174 | 1133 |
//J5TODO: Should param_annotations be an array of arrays? Rather than an array of lists, this |
1134 |
// is more likely to suggest to the caller it is readonly (which a List does not). |
|
1135 |
/** |
|
1136 |
* Return a list of AnnotationGen objects representing parameter annotations |
|
1137 |
* |
|
1138 |
* @since 6.0 |
|
6 | 1139 |
*/ |
46174 | 1140 |
public List<AnnotationEntryGen> getAnnotationsOnParameter(final int i) { |
1141 |
ensureExistingParameterAnnotationsUnpacked(); |
|
1142 |
if (!hasParameterAnnotations || i > arg_types.length) { |
|
1143 |
return null; |
|
1144 |
} |
|
1145 |
return param_annotations[i]; |
|
6 | 1146 |
} |
1147 |
||
46174 | 1148 |
/** |
1149 |
* Goes through the attributes on the method and identifies any that are |
|
1150 |
* RuntimeParameterAnnotations, extracting their contents and storing them |
|
1151 |
* as parameter annotations. There are two kinds of parameter annotation - |
|
1152 |
* visible and invisible. Once they have been unpacked, these attributes are |
|
1153 |
* deleted. (The annotations will be rebuilt as attributes when someone |
|
1154 |
* builds a Method object out of this MethodGen object). |
|
1155 |
*/ |
|
1156 |
private void ensureExistingParameterAnnotationsUnpacked() { |
|
1157 |
if (haveUnpackedParameterAnnotations) { |
|
1158 |
return; |
|
6 | 1159 |
} |
46174 | 1160 |
// Find attributes that contain parameter annotation data |
1161 |
final Attribute[] attrs = getAttributes(); |
|
1162 |
ParameterAnnotations paramAnnVisAttr = null; |
|
1163 |
ParameterAnnotations paramAnnInvisAttr = null; |
|
1164 |
for (final Attribute attribute : attrs) { |
|
1165 |
if (attribute instanceof ParameterAnnotations) { |
|
1166 |
// Initialize param_annotations |
|
1167 |
if (!hasParameterAnnotations) { |
|
1168 |
@SuppressWarnings("unchecked") // OK |
|
1169 |
final List<AnnotationEntryGen>[] parmList = new List[arg_types.length]; |
|
1170 |
param_annotations = parmList; |
|
1171 |
for (int j = 0; j < arg_types.length; j++) { |
|
1172 |
param_annotations[j] = new ArrayList<>(); |
|
1173 |
} |
|
1174 |
} |
|
1175 |
hasParameterAnnotations = true; |
|
1176 |
final ParameterAnnotations rpa = (ParameterAnnotations) attribute; |
|
1177 |
if (rpa instanceof RuntimeVisibleParameterAnnotations) { |
|
1178 |
paramAnnVisAttr = rpa; |
|
1179 |
} else { |
|
1180 |
paramAnnInvisAttr = rpa; |
|
1181 |
} |
|
1182 |
for (int j = 0; j < arg_types.length; j++) { |
|
1183 |
// This returns Annotation[] ... |
|
1184 |
final ParameterAnnotationEntry immutableArray = rpa |
|
1185 |
.getParameterAnnotationEntries()[j]; |
|
1186 |
// ... which needs transforming into an AnnotationGen[] ... |
|
1187 |
final List<AnnotationEntryGen> mutable |
|
1188 |
= makeMutableVersion(immutableArray.getAnnotationEntries()); |
|
1189 |
// ... then add these to any we already know about |
|
1190 |
param_annotations[j].addAll(mutable); |
|
1191 |
} |
|
1192 |
} |
|
6 | 1193 |
} |
46174 | 1194 |
if (paramAnnVisAttr != null) { |
1195 |
removeAttribute(paramAnnVisAttr); |
|
1196 |
} |
|
1197 |
if (paramAnnInvisAttr != null) { |
|
1198 |
removeAttribute(paramAnnInvisAttr); |
|
1199 |
} |
|
1200 |
haveUnpackedParameterAnnotations = true; |
|
6 | 1201 |
} |
1202 |
||
46174 | 1203 |
private List<AnnotationEntryGen> makeMutableVersion(final AnnotationEntry[] mutableArray) { |
1204 |
final List<AnnotationEntryGen> result = new ArrayList<>(); |
|
1205 |
for (final AnnotationEntry element : mutableArray) { |
|
1206 |
result.add(new AnnotationEntryGen(element, getConstantPool(), |
|
1207 |
false)); |
|
1208 |
} |
|
1209 |
return result; |
|
1210 |
} |
|
6 | 1211 |
|
46174 | 1212 |
public void addParameterAnnotation(final int parameterIndex, |
1213 |
final AnnotationEntryGen annotation) { |
|
1214 |
ensureExistingParameterAnnotationsUnpacked(); |
|
1215 |
if (!hasParameterAnnotations) { |
|
1216 |
@SuppressWarnings("unchecked") // OK |
|
1217 |
final List<AnnotationEntryGen>[] parmList = new List[arg_types.length]; |
|
1218 |
param_annotations = parmList; |
|
1219 |
hasParameterAnnotations = true; |
|
1220 |
} |
|
1221 |
final List<AnnotationEntryGen> existingAnnotations = param_annotations[parameterIndex]; |
|
1222 |
if (existingAnnotations != null) { |
|
1223 |
existingAnnotations.add(annotation); |
|
1224 |
} else { |
|
1225 |
final List<AnnotationEntryGen> l = new ArrayList<>(); |
|
1226 |
l.add(annotation); |
|
1227 |
param_annotations[parameterIndex] = l; |
|
1228 |
} |
|
6 | 1229 |
} |
1230 |
||
46174 | 1231 |
/** |
1232 |
* @return Comparison strategy object |
|
1233 |
*/ |
|
1234 |
public static BCELComparator getComparator() { |
|
1235 |
return bcelComparator; |
|
1236 |
} |
|
6 | 1237 |
|
46174 | 1238 |
/** |
1239 |
* @param comparator Comparison strategy object |
|
1240 |
*/ |
|
1241 |
public static void setComparator(final BCELComparator comparator) { |
|
1242 |
bcelComparator = comparator; |
|
6 | 1243 |
} |
1244 |
||
46174 | 1245 |
/** |
1246 |
* Return value as defined by given BCELComparator strategy. By default two |
|
1247 |
* MethodGen objects are said to be equal when their names and signatures |
|
1248 |
* are equal. |
|
1249 |
* |
|
1250 |
* @see java.lang.Object#equals(java.lang.Object) |
|
1251 |
*/ |
|
1252 |
@Override |
|
1253 |
public boolean equals(final Object obj) { |
|
1254 |
return bcelComparator.equals(this, obj); |
|
1255 |
} |
|
1256 |
||
1257 |
/** |
|
1258 |
* Return value as defined by given BCELComparator strategy. By default |
|
1259 |
* return the hashcode of the method's name XOR signature. |
|
1260 |
* |
|
1261 |
* @see java.lang.Object#hashCode() |
|
1262 |
*/ |
|
1263 |
@Override |
|
1264 |
public int hashCode() { |
|
1265 |
return bcelComparator.hashCode(this); |
|
1266 |
} |
|
6 | 1267 |
} |