1 | /* |
2 | |
3 | Derby - Class org.apache.derby.impl.services.bytecode.CodeChunk |
4 | |
5 | Copyright 1998, 2006 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.impl.services.bytecode; |
22 | |
23 | import org.apache.derby.iapi.services.classfile.CONSTANT_Index_info; |
24 | import org.apache.derby.iapi.services.classfile.CONSTANT_Utf8_info; |
25 | import org.apache.derby.iapi.services.classfile.ClassFormatOutput; |
26 | import org.apache.derby.iapi.services.classfile.ClassHolder; |
27 | import org.apache.derby.iapi.services.classfile.ClassMember; |
28 | import org.apache.derby.iapi.services.classfile.VMDescriptor; |
29 | |
30 | import org.apache.derby.iapi.services.sanity.SanityManager; |
31 | import org.apache.derby.iapi.services.classfile.VMOpcode; |
32 | import org.apache.derby.iapi.services.io.ArrayOutputStream; |
33 | |
34 | import java.io.IOException; |
35 | import java.lang.reflect.Modifier; |
36 | import java.util.Arrays; |
37 | |
38 | /** |
39 | * This class represents a chunk of code in a CodeAttribute. |
40 | * Typically, a CodeAttribute represents the code in a method. |
41 | * If there is a try/catch block, each catch block will get its |
42 | * own code chunk. This allows the catch blocks to all be put at |
43 | * the end of the generated code for a method, which eliminates |
44 | * the need to generate a jump around each catch block, which |
45 | * would be a forward reference. |
46 | */ |
47 | final class CodeChunk { |
48 | |
49 | /** |
50 | * Starting point of the byte code stream in the underlying stream/array. |
51 | */ |
52 | private static final int CODE_OFFSET = 8; |
53 | |
54 | // The use of ILOAD for the non-integer types is correct. |
55 | // We have to assume that the appropriate checks/conversions |
56 | // are defined on math operation results to ensure that |
57 | // the type is preserved when/as needed. |
58 | static final short[] LOAD_VARIABLE = { |
59 | VMOpcode.ILOAD, /* vm_byte */ |
60 | VMOpcode.ILOAD, /* vm_short */ |
61 | VMOpcode.ILOAD, /* vm_int */ |
62 | VMOpcode.LLOAD, /* vm_long */ |
63 | VMOpcode.FLOAD, /* vm_float */ |
64 | VMOpcode.DLOAD, /* vm_double */ |
65 | VMOpcode.ILOAD, /* vm_char */ |
66 | VMOpcode.ALOAD /* vm_reference */ |
67 | }; |
68 | |
69 | static final short[] LOAD_VARIABLE_FAST = { |
70 | VMOpcode.ILOAD_0, /* vm_byte */ |
71 | VMOpcode.ILOAD_0, /* vm_short */ |
72 | VMOpcode.ILOAD_0, /* vm_int */ |
73 | VMOpcode.LLOAD_0, /* vm_long */ |
74 | VMOpcode.FLOAD_0, /* vm_float */ |
75 | VMOpcode.DLOAD_0, /* vm_double */ |
76 | VMOpcode.ILOAD_0, /* vm_char */ |
77 | VMOpcode.ALOAD_0 /* vm_reference */ |
78 | }; |
79 | |
80 | // The ISTOREs for non-int types are how things work. |
81 | // It assumes that the appropriate casts are done |
82 | // on operations on non-ints to ensure that the values |
83 | // remain in the valid ranges. |
84 | static final short[] STORE_VARIABLE = { |
85 | VMOpcode.ISTORE, /* vm_byte */ |
86 | VMOpcode.ISTORE, /* vm_short */ |
87 | VMOpcode.ISTORE, /* vm_int */ |
88 | VMOpcode.LSTORE, /* vm_long */ |
89 | VMOpcode.FSTORE, /* vm_float */ |
90 | VMOpcode.DSTORE, /* vm_double */ |
91 | VMOpcode.ISTORE, /* vm_char */ |
92 | VMOpcode.ASTORE /* vm_reference */ |
93 | }; |
94 | |
95 | static final short[] STORE_VARIABLE_FAST = { |
96 | VMOpcode.ISTORE_0, /* vm_byte */ |
97 | VMOpcode.ISTORE_0, /* vm_short */ |
98 | VMOpcode.ISTORE_0, /* vm_int */ |
99 | VMOpcode.LSTORE_0, /* vm_long */ |
100 | VMOpcode.FSTORE_0, /* vm_float */ |
101 | VMOpcode.DSTORE_0, /* vm_double */ |
102 | VMOpcode.ISTORE_0, /* vm_char */ |
103 | VMOpcode.ASTORE_0 /* vm_reference */ |
104 | }; |
105 | |
106 | static final short ARRAY_ACCESS[] = { |
107 | VMOpcode.BALOAD, /* vm_byte */ |
108 | VMOpcode.SALOAD, /* vm_short */ |
109 | VMOpcode.IALOAD, /* vm_int */ |
110 | VMOpcode.LALOAD, /* vm_long */ |
111 | VMOpcode.FALOAD, /* vm_float */ |
112 | VMOpcode.DALOAD, /* vm_double */ |
113 | VMOpcode.CALOAD, /* vm_char */ |
114 | VMOpcode.AALOAD /* vm_reference */ |
115 | }; |
116 | static final short ARRAY_STORE[] = { |
117 | VMOpcode.BASTORE, /* vm_byte */ |
118 | VMOpcode.SASTORE, /* vm_short */ |
119 | VMOpcode.IASTORE, /* vm_int */ |
120 | VMOpcode.LASTORE, /* vm_long */ |
121 | VMOpcode.FASTORE, /* vm_float */ |
122 | VMOpcode.DASTORE, /* vm_double */ |
123 | VMOpcode.CASTORE, /* vm_char */ |
124 | VMOpcode.AASTORE /* vm_reference */ |
125 | }; |
126 | static final short[] RETURN_OPCODE = { |
127 | VMOpcode.IRETURN, /* 0 = byte */ |
128 | VMOpcode.IRETURN, /* 1 = short */ |
129 | VMOpcode.IRETURN, /* 2 = int */ |
130 | VMOpcode.LRETURN, /* 3 = long */ |
131 | VMOpcode.FRETURN, /* 4 = float */ |
132 | VMOpcode.DRETURN, /* 5 = double */ |
133 | VMOpcode.IRETURN, /* 6 = char */ |
134 | VMOpcode.ARETURN /* 7 = reference */ |
135 | }; |
136 | |
137 | // the first dimension is the current vmTypeId |
138 | // the second dimension is the target vmTypeId |
139 | // |
140 | // the cells of the entry at [current,target] are: |
141 | // 0: operation |
142 | // 1: result type of operation |
143 | // if entry[1] = target, we are done. otherwise, |
144 | // you have to continue with entry[1] as the new current |
145 | // after generating the opcode listed (don't generate if it is NOP). |
146 | // if entry[0] = BAD, we can |
147 | |
148 | static final short CAST_CONVERSION_INFO[][][] = { |
149 | /* current = vm_byte */ |
150 | { |
151 | /* target = vm_byte */ { VMOpcode.NOP, BCExpr.vm_byte }, |
152 | /* target = vm_short */ { VMOpcode.NOP, BCExpr.vm_short }, |
153 | /* target = vm_int */ { VMOpcode.NOP, BCExpr.vm_int }, |
154 | /* target = vm_long */ { VMOpcode.NOP, BCExpr.vm_int }, |
155 | /* target = vm_float */ { VMOpcode.NOP, BCExpr.vm_int }, |
156 | /* target = vm_double */ { VMOpcode.NOP, BCExpr.vm_int }, |
157 | /* target = vm_char */ { VMOpcode.NOP, BCExpr.vm_char }, |
158 | /* target = vm_reference */ { VMOpcode.BAD, BCExpr.vm_reference } |
159 | }, |
160 | /* current = vm_short */ |
161 | { |
162 | /* target = vm_byte */ { VMOpcode.NOP, BCExpr.vm_byte }, |
163 | /* target = vm_short */ { VMOpcode.NOP, BCExpr.vm_short }, |
164 | /* target = vm_int */ { VMOpcode.NOP, BCExpr.vm_int }, |
165 | /* target = vm_long */ { VMOpcode.NOP, BCExpr.vm_int }, |
166 | /* target = vm_float */ { VMOpcode.NOP, BCExpr.vm_int }, |
167 | /* target = vm_double */ { VMOpcode.NOP, BCExpr.vm_int }, |
168 | /* target = vm_char */ { VMOpcode.NOP, BCExpr.vm_char }, |
169 | /* target = vm_reference */ { VMOpcode.BAD, BCExpr.vm_reference } |
170 | }, |
171 | /* current = vm_int */ |
172 | { |
173 | /* target = vm_byte */ { VMOpcode.I2B, BCExpr.vm_byte }, |
174 | /* target = vm_short */ { VMOpcode.I2S, BCExpr.vm_short }, |
175 | /* target = vm_int */ { VMOpcode.NOP, BCExpr.vm_int }, |
176 | /* target = vm_long */ { VMOpcode.I2L, BCExpr.vm_long }, |
177 | /* target = vm_float */ { VMOpcode.I2F, BCExpr.vm_float }, |
178 | /* target = vm_double */ { VMOpcode.I2D, BCExpr.vm_double }, |
179 | /* target = vm_char */ { VMOpcode.I2B, BCExpr.vm_char }, |
180 | /* target = vm_reference */ { VMOpcode.BAD, BCExpr.vm_reference } |
181 | }, |
182 | /* current = vm_long */ |
183 | { |
184 | /* target = vm_byte */ { VMOpcode.L2I, BCExpr.vm_int }, |
185 | /* target = vm_short */ { VMOpcode.L2I, BCExpr.vm_int }, |
186 | /* target = vm_int */ { VMOpcode.L2I, BCExpr.vm_int }, |
187 | /* target = vm_long */ { VMOpcode.NOP, BCExpr.vm_long }, |
188 | /* target = vm_float */ { VMOpcode.L2F, BCExpr.vm_float }, |
189 | /* target = vm_double */ { VMOpcode.L2D, BCExpr.vm_double }, |
190 | /* target = vm_char */ { VMOpcode.L2I, BCExpr.vm_int }, |
191 | /* target = vm_reference */ { VMOpcode.BAD, BCExpr.vm_reference } |
192 | }, |
193 | /* current = vm_float */ |
194 | { |
195 | /* target = vm_byte */ { VMOpcode.F2I, BCExpr.vm_int }, |
196 | /* target = vm_short */ { VMOpcode.F2I, BCExpr.vm_int }, |
197 | /* target = vm_int */ { VMOpcode.F2I, BCExpr.vm_int }, |
198 | /* target = vm_long */ { VMOpcode.F2L, BCExpr.vm_long }, |
199 | /* target = vm_float */ { VMOpcode.NOP, BCExpr.vm_float }, |
200 | /* target = vm_double */ { VMOpcode.F2D, BCExpr.vm_double }, |
201 | /* target = vm_char */ { VMOpcode.F2I, BCExpr.vm_int }, |
202 | /* target = vm_reference */ { VMOpcode.BAD, BCExpr.vm_reference } |
203 | }, |
204 | /* current = vm_double */ |
205 | { |
206 | /* target = vm_byte */ { VMOpcode.D2I, BCExpr.vm_int }, |
207 | /* target = vm_short */ { VMOpcode.D2I, BCExpr.vm_int }, |
208 | /* target = vm_int */ { VMOpcode.D2I, BCExpr.vm_int }, |
209 | /* target = vm_long */ { VMOpcode.D2L, BCExpr.vm_long }, |
210 | /* target = vm_float */ { VMOpcode.D2F, BCExpr.vm_float }, |
211 | /* target = vm_double */ { VMOpcode.NOP, BCExpr.vm_double }, |
212 | /* target = vm_char */ { VMOpcode.D2I, BCExpr.vm_int }, |
213 | /* target = vm_reference */ { VMOpcode.BAD, BCExpr.vm_reference } |
214 | }, |
215 | /* current = vm_char */ |
216 | { |
217 | /* target = vm_byte */ { VMOpcode.NOP, BCExpr.vm_byte }, |
218 | /* target = vm_short */ { VMOpcode.NOP, BCExpr.vm_short }, |
219 | /* target = vm_int */ { VMOpcode.NOP, BCExpr.vm_int }, |
220 | /* target = vm_long */ { VMOpcode.NOP, BCExpr.vm_int }, |
221 | /* target = vm_float */ { VMOpcode.NOP, BCExpr.vm_int }, |
222 | /* target = vm_double */ { VMOpcode.NOP, BCExpr.vm_int }, |
223 | /* target = vm_char */ { VMOpcode.NOP, BCExpr.vm_char }, |
224 | /* target = vm_reference */ { VMOpcode.BAD, BCExpr.vm_reference } |
225 | }, |
226 | /* current = vm_reference */ |
227 | { |
228 | /* target = vm_byte */ { VMOpcode.BAD, BCExpr.vm_byte }, |
229 | /* target = vm_short */ { VMOpcode.BAD, BCExpr.vm_short }, |
230 | /* target = vm_int */ { VMOpcode.BAD, BCExpr.vm_int }, |
231 | /* target = vm_long */ { VMOpcode.BAD, BCExpr.vm_long }, |
232 | /* target = vm_float */ { VMOpcode.BAD, BCExpr.vm_float }, |
233 | /* target = vm_double */ { VMOpcode.BAD, BCExpr.vm_double }, |
234 | /* target = vm_char */ { VMOpcode.BAD, BCExpr.vm_char }, |
235 | /* target = vm_reference */ { VMOpcode.NOP, BCExpr.vm_reference } |
236 | } |
237 | }; |
238 | |
239 | /** |
240 | * Constant used by OPCODE_ACTION to represent the |
241 | * common action of push one word, 1 byte |
242 | * for the instruction. |
243 | */ |
244 | private static final byte[] push1_1i = {1, 1}; |
245 | |
246 | /** |
247 | * Constant used by OPCODE_ACTION to represent the |
248 | * common action of push two words, 1 byte |
249 | * for the instruction. |
250 | */ |
251 | private static final byte[] push2_1i = {2, 1}; |
252 | /** |
253 | * Constant used by OPCODE_ACTION to the opcode is |
254 | * not yet supported. |
255 | */ |
256 | private static final byte[] NS = {0, -1}; |
257 | |
258 | |
259 | /** |
260 | * Value for OPCODE_ACTION[opcode][0] to represent |
261 | * the number of words popped or pushed in variable. |
262 | */ |
263 | private static final byte VARIABLE_STACK = -128; |
264 | |
265 | /** |
266 | * Array that provides two pieces of information about |
267 | * each VM opcode. Each opcode has a two byte array. |
268 | * <P> |
269 | * The first element in the array [0] is the number of |
270 | * stack words (double/long count as two) pushed by the opcode. |
271 | * Will be negative if the opcode pops values. |
272 | * |
273 | * <P> |
274 | * The second element in the array [1] is the number of bytes |
275 | * in the instruction stream that this opcode's instruction |
276 | * takes up, including the opocode. |
277 | */ |
278 | private static final byte[][] OPCODE_ACTION = |
279 | { |
280 | |
281 | /* NOP 0 */ { 0, 1 }, |
282 | |
283 | /* ACONST_NULL 1 */ push1_1i, |
284 | /* ICONST_M1 2 */ push1_1i, |
285 | /* ICONST_0 3 */ push1_1i, |
286 | /* ICONST_1 4 */ push1_1i, |
287 | /* ICONST_2 5 */ push1_1i, |
288 | /* ICONST_3 6 */ push1_1i, |
289 | /* ICONST_4 7 */ push1_1i, |
290 | /* ICONST_5 8 */ push1_1i, |
291 | /* LCONST_0 9 */ push2_1i, |
292 | /* LCONST_1 10 */ push2_1i, |
293 | /* FCONST_0 11 */ push1_1i, |
294 | /* FCONST_1 12 */ push1_1i, |
295 | /* FCONST_2 13 */ push1_1i, |
296 | /* DCONST_0 14 */ push2_1i, |
297 | /* DCONST_1 15 */ push2_1i, |
298 | |
299 | /* BIPUSH 16 */ {1, 2}, |
300 | /* SIPUSH 17 */ {1, 3}, |
301 | /* LDC 18 */ {1, 2}, |
302 | /* LDC_W 19 */ {1, 3}, |
303 | /* LDC2_W 20 */ {2, 3}, |
304 | |
305 | /* ILOAD 21 */ { 1, 2 }, |
306 | /* LLOAD 22 */ { 2, 2 }, |
307 | /* FLOAD 23 */ { 1, 2 }, |
308 | /* DLOAD 24 */ { 2, 2 }, |
309 | /* ALOAD 25 */ { 1, 2 }, |
310 | /* ILOAD_0 26 */ push1_1i, |
311 | /* ILOAD_1 27 */ push1_1i, |
312 | /* ILOAD_2 28 */ push1_1i, |
313 | /* ILOAD_3 29 */ push1_1i, |
314 | /* LLOAD_0 30 */ push2_1i, |
315 | /* LLOAD_1 31 */ push2_1i, |
316 | /* LLOAD_2 32 */ push2_1i, |
317 | /* LLOAD_3 33 */ push2_1i, |
318 | /* FLOAD_0 34 */ push1_1i, |
319 | /* FLOAD_1 35 */ push1_1i, |
320 | /* FLOAD_2 36 */ push1_1i, |
321 | /* FLOAD_3 37 */ push1_1i, |
322 | /* DLOAD_0 38 */ push2_1i, |
323 | /* DLOAD_1 39 */ push2_1i, |
324 | /* DLOAD_2 40 */ push2_1i, |
325 | /* DLOAD_3 41 */ push2_1i, |
326 | /* ALOAD_0 42 */ push1_1i, |
327 | /* ALOAD_1 43 */ push1_1i, |
328 | /* ALOAD_2 44 */ push1_1i, |
329 | /* ALOAD_3 45 */ push1_1i, |
330 | /* IALOAD 46 */ { -1, 1 }, |
331 | /* LALOAD 47 */ { 0, 1 }, |
332 | /* FALOAD 48 */ { -1, 1 }, |
333 | /* DALOAD 49 */ { 0, 1 }, |
334 | /* AALOAD 50 */ { -1, 1 }, |
335 | /* BALOAD 51 */ { -1, 1 }, |
336 | /* CALOAD 52 */ { -1, 1 }, |
337 | |
338 | /* SALOAD 53 */ { -1, 1 }, |
339 | /* ISTORE 54 */ { -1, 2 }, |
340 | /* LSTORE 55 */ { -2, 2 }, |
341 | /* FSTORE 56 */ { -1, 2 }, |
342 | /* DSTORE 57 */ { -2, 2 }, |
343 | /* ASTORE 58 */ { -1, 2 }, |
344 | /* ISTORE_0 59 */ { -1, 1 }, |
345 | /* ISTORE_1 60 */ { -1, 1 }, |
346 | /* ISTORE_2 61 */ { -1, 1 }, |
347 | /* ISTORE_3 62 */ { -1, 1 }, |
348 | /* LSTORE_0 63 */ { -2, 1 }, |
349 | /* LSTORE_1 64 */ { -2, 1 }, |
350 | /* LSTORE_2 65 */ { -2, 1 }, |
351 | /* LSTORE_3 66 */ { -2, 1 }, |
352 | /* FSTORE_0 67 */ { -1, 1 }, |
353 | /* FSTORE_1 68 */ { -1, 1 }, |
354 | /* FSTORE_2 69 */ { -1, 1 }, |
355 | /* FSTORE_3 70 */ { -1, 1 }, |
356 | /* DSTORE_0 71 */ { -2, 1 }, |
357 | /* DSTORE_1 72 */ { -2, 1 }, |
358 | /* DSTORE_2 73 */ { -2, 1 }, |
359 | /* DSTORE_3 74 */ { -2, 1 }, |
360 | /* ASTORE_0 75 */ { -1, 1 }, |
361 | /* ASTORE_1 76 */ { -1, 1 }, |
362 | /* ASTORE_2 77 */ { -1, 1 }, |
363 | /* ASTORE_3 78 */ { -1, 1 }, |
364 | /* IASTORE 79 */ { -3, 1 }, |
365 | /* LASTORE 80 */ { -4, 1 }, |
366 | /* FASTORE 81 */ { -3, 1 }, |
367 | /* DASTORE 82 */ { -4, 1 }, |
368 | /* AASTORE 83 */ { -3, 1 }, |
369 | /* BASTORE 84 */ { -3, 1 }, |
370 | /* CASTORE 85 */ { -3, 1 }, |
371 | /* SASTORE 86 */ { -3, 1 }, |
372 | |
373 | /* POP 87 */ { -1, 1 }, |
374 | /* POP2 88 */ { -2, 1 }, |
375 | /* DUP 89 */ push1_1i, |
376 | /* DUP_X1 90 */ push1_1i, |
377 | /* DUP_X2 91 */ push1_1i, |
378 | /* DUP2 92 */ push2_1i, |
379 | /* DUP2_X1 93 */ push2_1i, |
380 | /* DUP2_X2 94 */ push2_1i, |
381 | /* SWAP 95 */ { 0, 1 }, |
382 | |
383 | /* IADD 96 */ NS, |
384 | /* LADD 97 */ NS, |
385 | /* FADD 98 */ { -1, 1 }, |
386 | /* DADD 99 */ { -2, 1 }, |
387 | /* ISUB 100 */ NS, |
388 | /* LSUB 101 */ NS, |
389 | /* FSUB 102 */ { -1, 1 }, |
390 | /* DSUB 103 */ { -2, 1 }, |
391 | /* IMUL 104 */ NS, |
392 | /* LMUL 105 */ NS, |
393 | /* FMUL 106 */ { -1, 1 }, |
394 | /* DMUL 107 */ { -2, 1 }, |
395 | /* IDIV 108 */ NS, |
396 | /* LDIV 109 */ NS, |
397 | /* FDIV 110 */ { -1, 1 }, |
398 | /* DDIV 111 */ { -2, 1 }, |
399 | /* IREM 112 */ { -1, 1 }, |
400 | /* LREM 113 */ { -2, 1 }, |
401 | /* FREM 114 */ { -1, 1 }, |
402 | /* DREM 115 */ { -2, 1 }, |
403 | /* INEG 116 */ { 0, 1 }, |
404 | /* LNEG 117 */ { 0, 1 }, |
405 | /* FNEG 118 */ { 0, 1 }, |
406 | /* DNEG 119 */ { 0, 1 }, |
407 | /* ISHL 120 */ { -1, 1 }, |
408 | /* LSHL 121 */ NS, |
409 | /* ISHR 122 */ NS, |
410 | /* LSHR 123 */ NS, |
411 | /* IUSHR 124 */ NS, |
412 | /* LUSHR 125 */ NS, |
413 | |
414 | /* IAND 126 */ { -1, 1 }, |
415 | /* LAND 127 */ NS, |
416 | /* IOR 128 */ { -1, 1 }, |
417 | /* LOR 129 */ NS, |
418 | /* IXOR 130 */ NS, |
419 | /* LXOR 131 */ NS, |
420 | /* IINC 132 */ NS, |
421 | |
422 | /* I2L 133 */ push1_1i, |
423 | /* I2F 134 */ { 0, 1 }, |
424 | /* I2D 135 */ push1_1i, |
425 | /* L2I 136 */ { -1, 1 }, |
426 | /* L2F 137 */ { -1, 1 }, |
427 | /* L2D 138 */ { 0, 1 }, |
428 | /* F2I 139 */ { 0, 1 }, |
429 | /* F2L 140 */ push2_1i, |
430 | /* F2D 141 */ push1_1i, |
431 | /* D2I 142 */ { -1, 1 }, |
432 | /* D2L 143 */ { 0, 1 }, |
433 | /* D2F 144 */ { -1, 1 }, |
434 | /* I2B 145 */ { 0, 1 }, |
435 | /* I2C 146 */ { 0, 1 }, |
436 | /* I2S 147 */ { 0, 1 }, |
437 | |
438 | /* LCMP 148 */ NS, |
439 | /* FCMPL 149 */ { -1, 1 }, |
440 | /* FCMPG 150 */ { -1, 1 }, |
441 | /* DCMPL 151 */ { -3, 1 }, |
442 | /* DCMPG 152 */ { -3, 1 }, |
443 | /* IFEQ 153 */ { -1, VMOpcode.IF_INS_LENGTH }, |
444 | /* IFNE 154 */ { -1, VMOpcode.IF_INS_LENGTH }, |
445 | /* IFLT 155 */ { -1, VMOpcode.IF_INS_LENGTH }, |
446 | /* IFGE 156 */ { -1, VMOpcode.IF_INS_LENGTH }, |
447 | /* IFGT 157 */ { -1, VMOpcode.IF_INS_LENGTH }, |
448 | /* IFLE 158 */ { -1, VMOpcode.IF_INS_LENGTH }, |
449 | /* IF_ICMPEQ 159 */ NS, |
450 | /* IF_ICMPNE 160 */ NS, |
451 | /* IF_ICMPLT 161 */ NS, |
452 | /* IF_ICMPGE 162 */ NS, |
453 | /* IF_ICMPGT 163 */ NS, |
454 | /* IF_ICMPLE 164 */ NS, |
455 | /* IF_ACMPEQ 165 */ NS, |
456 | /* IF_ACMPNE 166 */ NS, |
457 | /* GOTO 167 */ { 0, VMOpcode.GOTO_INS_LENGTH }, |
458 | /* JSR 168 */ NS, |
459 | /* RET 169 */ NS, |
460 | /* TABLESWITCH 170 */ NS, |
461 | /* LOOKUPSWITCH 171 */NS, |
462 | |
463 | /* IRETURN 172 */ { -1, 1 }, // strictly speaking all words on the stack are popped. |
464 | /* LRETURN 173 */ { -2, 1 }, // strictly speaking all words on the stack are popped. |
465 | /* FRETURN 174 */ { -1, 1 }, // strictly speaking all words on the stack are popped. |
466 | /* DRETURN 175 */ { -2, 1 }, // strictly speaking all words on the stack are popped. |
467 | /* ARETURN 176 */ { -1, 1 }, // strictly speaking all words on the stack are popped. |
468 | /* RETURN 177 */ { 0, 1 }, // strictly speaking all words on the stack are popped. |
469 | |
470 | /* GETSTATIC 178 */ {VARIABLE_STACK, 3 }, |
471 | /* PUTSTATIC 179 */ {VARIABLE_STACK, 3 }, |
472 | /* GETFIELD 180 */ {VARIABLE_STACK, 3 }, |
473 | /* PUTFIELD 181 */ {VARIABLE_STACK, 3 }, |
474 | /* INVOKEVIRTUAL 182 */ {VARIABLE_STACK, 3 }, |
475 | /* INVOKESPECIAL 183 */ {VARIABLE_STACK, 3 }, |
476 | /* INVOKESTATIC 184 */ {VARIABLE_STACK, 3 }, |
477 | /* INVOKEINTERFACE 185 */ {VARIABLE_STACK, 5 }, |
478 | |
479 | /* XXXUNUSEDXXX 186 */ NS, |
480 | |
481 | /* NEW 187 */ { 1, 3 }, |
482 | /* NEWARRAY 188 */ { 0, 2 }, |
483 | /* ANEWARRAY 189 */ { 0, 3 }, |
484 | /* ARRAYLENGTH 190 */ { 0, 1 }, |
485 | /* ATHROW 191 */ NS, |
486 | /* CHECKCAST 192 */ { 0, 3}, |
487 | /* INSTANCEOF 193 */ { 0, 3 }, |
488 | /* MONITORENTER 194 */ NS, |
489 | /* MONITOREXIT 195 */ NS, |
490 | /* WIDE 196 */ NS, |
491 | /* MULTIANEWARRAY 197 */ NS, |
492 | /* IFNULL 198 */ { -1, VMOpcode.IF_INS_LENGTH }, |
493 | /* IFNONNULL 199 */ { -1, VMOpcode.IF_INS_LENGTH }, |
494 | /* GOTO_W 200 */ {0, VMOpcode.GOTO_W_INS_LENGTH }, |
495 | /* JSR_W 201 */ NS, |
496 | /* BREAKPOINT 202 */ NS, |
497 | |
498 | }; |
499 | |
500 | |
501 | |
502 | /** |
503 | * Add an instruction that has no operand. |
504 | * All opcodes are 1 byte large. |
505 | */ |
506 | void addInstr(short opcode) { |
507 | try { |
508 | cout.putU1(opcode); |
509 | } catch (IOException ioe) { |
510 | } |
511 | |
512 | if (SanityManager.DEBUG) { |
513 | if (OPCODE_ACTION[opcode][1] != 1) |
514 | SanityManager.THROWASSERT("Opcode " + opcode + " incorrect entry in OPCODE_ACTION -" + |
515 | " writing 1 byte - set as " + OPCODE_ACTION[opcode][1]); |
516 | } |
517 | } |
518 | |
519 | /** |
520 | * Add an instruction that has a 16 bit operand. |
521 | */ |
522 | void addInstrU2(short opcode, int operand) { |
523 | try { |
524 | cout.putU1(opcode); |
525 | cout.putU2(operand); |
526 | } catch (IOException ioe) { |
527 | } |
528 | |
529 | if (SanityManager.DEBUG) { |
530 | if (OPCODE_ACTION[opcode][1] != 3) |
531 | SanityManager.THROWASSERT("Opcode " + opcode + " incorrect entry in OPCODE_ACTION -" + |
532 | " writing 3 bytes - set as " + OPCODE_ACTION[opcode][1]); |
533 | } |
534 | } |
535 | |
536 | /** |
537 | * Add an instruction that has a 32 bit operand. |
538 | */ |
539 | void addInstrU4(short opcode, int operand) { |
540 | try { |
541 | cout.putU1(opcode); |
542 | cout.putU4(operand); |
543 | } catch (IOException ioe) { |
544 | } |
545 | if (SanityManager.DEBUG) { |
546 | if (OPCODE_ACTION[opcode][1] != 5) |
547 | SanityManager.THROWASSERT("Opcode " + opcode + " incorrect entry in OPCODE_ACTION -" + |
548 | " writing 5 bytes - set as " + OPCODE_ACTION[opcode][1]); |
549 | } |
550 | } |
551 | |
552 | |
553 | /** |
554 | * Add an instruction that has an 8 bit operand. |
555 | */ |
556 | void addInstrU1(short opcode, int operand) { |
557 | try { |
558 | cout.putU1(opcode); |
559 | cout.putU1(operand); |
560 | } catch (IOException ioe) { |
561 | } |
562 | |
563 | // Only debug code from here. |
564 | if (SanityManager.DEBUG) { |
565 | |
566 | if (OPCODE_ACTION[opcode][1] != 2) |
567 | SanityManager.THROWASSERT("Opcode " + opcode + " incorrect entry in OPCODE_ACTION -" + |
568 | " writing 2 bytes - set as " + OPCODE_ACTION[opcode][1]); |
569 | |
570 | } |
571 | } |
572 | |
573 | /** |
574 | * This takes an instruction that has a narrow |
575 | * and a wide form for CPE access, and |
576 | * generates accordingly the right one. |
577 | * We assume the narrow instruction is what |
578 | * we were given, and that the wide form is |
579 | * the next possible instruction. |
580 | */ |
581 | void addInstrCPE(short opcode, int cpeNum) { |
582 | if (cpeNum < 256) { |
583 | addInstrU1(opcode, cpeNum); |
584 | } |
585 | else { |
586 | addInstrU2((short) (opcode+1), cpeNum); |
587 | } |
588 | } |
589 | |
590 | /** |
591 | * This takes an instruction that can be wrapped in |
592 | * a wide for large variable #s and does so. |
593 | */ |
594 | void addInstrWide(short opcode, int varNum) { |
595 | if (varNum < 256) { |
596 | addInstrU1(opcode, varNum); |
597 | } |
598 | else { |
599 | addInstr(VMOpcode.WIDE); |
600 | addInstrU2(opcode, varNum); |
601 | } |
602 | } |
603 | |
604 | /** |
605 | * For adding an instruction with 3 operands, a U2 and two U1's. |
606 | * So far, this is used by VMOpcode.INVOKEINTERFACE. |
607 | */ |
608 | void addInstrU2U1U1(short opcode, int operand1, short operand2, |
609 | short operand3) { |
610 | try { |
611 | cout.putU1(opcode); |
612 | cout.putU2(operand1); |
613 | cout.putU1(operand2); |
614 | cout.putU1(operand3); |
615 | } catch (IOException ioe) { |
616 | } |
617 | if (SanityManager.DEBUG) { |
618 | if (OPCODE_ACTION[opcode][1] != 5) |
619 | SanityManager.THROWASSERT("Opcode " + opcode + " incorrect entry in OPCODE_ACTION -" + |
620 | " writing 5 bytes - set as " + OPCODE_ACTION[opcode][1]); |
621 | } |
622 | } |
623 | |
624 | /** Get the current program counter */ |
625 | int getPC() { |
626 | return cout.size() + pcDelta; |
627 | } |
628 | |
629 | /** |
630 | * Return the complete instruction length for the |
631 | * passed in opcode. This will include the space for |
632 | * the opcode and its operand. |
633 | */ |
634 | private static int instructionLength(short opcode) |
635 | { |
636 | int instructionLength = OPCODE_ACTION[opcode][1]; |
637 | |
638 | if (SanityManager.DEBUG) |
639 | { |
640 | if (instructionLength < 0) |
641 | SanityManager.THROWASSERT("Opcode without instruction length " + opcode); |
642 | } |
643 | |
644 | return instructionLength; |
645 | } |
646 | |
647 | /** |
648 | * The delta between cout.size() and the pc. |
649 | * For an initial code chunk this is -8 (CODE_OFFSET) |
650 | * since 8 bytes are written. |
651 | * For a nested CodeChunk return by insertCodeSpace the delta |
652 | * corresponds to the original starting pc. |
653 | * @see #insertCodeSpace |
654 | */ |
655 | private final int pcDelta; |
656 | |
657 | CodeChunk() { |
658 | cout = new ClassFormatOutput(); |
659 | try { |
660 | cout.putU2(0); // max_stack, placeholder for now |
661 | cout.putU2(0); // max_locals, placeholder for now |
662 | cout.putU4(0); // code_length, placeholder 4 now |
663 | } catch (IOException ioe) { |
664 | } |
665 | pcDelta = - CodeChunk.CODE_OFFSET; |
666 | } |
667 | |
668 | /** |
669 | * Return a CodeChunk that has limited visibility into |
670 | * this CodeChunk. Used when a caller needs to insert instructions |
671 | * into an existing stream. |
672 | * @param pc |
673 | * @param byteCount |
674 | * @throws IOException |
675 | */ |
676 | private CodeChunk(CodeChunk main, int pc, int byteCount) |
677 | { |
678 | ArrayOutputStream aos = |
679 | new ArrayOutputStream(main.cout.getData()); |
680 | |
681 | try { |
682 | aos.setPosition(CODE_OFFSET + pc); |
683 | aos.setLimit(byteCount); |
684 | } catch (IOException e) { |
685 | } |
686 | |
687 | cout = new ClassFormatOutput(aos); |
688 | pcDelta = pc; |
689 | } |
690 | |
691 | private final ClassFormatOutput cout; |
692 | |
693 | /** |
694 | * now that we have codeBytes, fix the lengths fields in it |
695 | * to reflect what was stored. |
696 | * Limits checked here are from these sections of the JVM spec. |
697 | * <UL> |
698 | * <LI> 4.7.3 The Code Attribute |
699 | * <LI> 4.10 Limitations of the Java Virtual Machine |
700 | * </UL> |
701 | */ |
702 | private void fixLengths(BCMethod mb, int maxStack, int maxLocals, int codeLength) { |
703 | |
704 | byte[] codeBytes = cout.getData(); |
705 | |
706 | // max_stack is in bytes 0-1 |
707 | if (mb != null && maxStack > 65535) |
708 | mb.cb.addLimitExceeded(mb, "max_stack", 65535, maxStack); |
709 | |
710 | codeBytes[0] = (byte)(maxStack >> 8 ); |
711 | codeBytes[1] = (byte)(maxStack ); |
712 | |
713 | // max_locals is in bytes 2-3 |
714 | if (mb != null && maxLocals > 65535) |
715 | mb.cb.addLimitExceeded(mb, "max_locals", 65535, maxLocals); |
716 | codeBytes[2] = (byte)(maxLocals >> 8 ); |
717 | codeBytes[3] = (byte)(maxLocals ); |
718 | |
719 | // code_length is in bytes 4-7 |
720 | if (mb != null && codeLength > VMOpcode.MAX_CODE_LENGTH) |
721 | mb.cb.addLimitExceeded(mb, "code_length", |
722 | VMOpcode.MAX_CODE_LENGTH, codeLength); |
723 | codeBytes[4] = (byte)(codeLength >> 24 ); |
724 | codeBytes[5] = (byte)(codeLength >> 16 ); |
725 | codeBytes[6] = (byte)(codeLength >> 8 ); |
726 | codeBytes[7] = (byte)(codeLength ); |
727 | } |
728 | |
729 | /** |
730 | * wrap up the entry and stuff it in the class, |
731 | * now that it holds all of the instructions and |
732 | * the exception table. |
733 | */ |
734 | void complete(BCMethod mb, ClassHolder ch, |
735 | ClassMember method, int maxStack, int maxLocals) { |
736 | |
737 | int codeLength = getPC(); |
738 | |
739 | ClassFormatOutput out = cout; |
740 | |
741 | try { |
742 | |
743 | out.putU2(0); // exception_table_length |
744 | |
745 | if (SanityManager.DEBUG) { |
746 | if (SanityManager.DEBUG_ON("ClassLineNumbers")) { |
747 | // Add a single attribute - LineNumberTable |
748 | // This add fake line numbers that are the pc offset in the method. |
749 | out.putU2(1); // attributes_count |
750 | |
751 | int cpiUTF = ch.addUtf8("LineNumberTable"); |
752 | |
753 | out.putU2(cpiUTF); |
754 | out.putU4((codeLength * 4) + 2); |
755 | out.putU2(codeLength); |
756 | for (int i = 0; i < codeLength; i++) { |
757 | out.putU2(i); |
758 | out.putU2(i); |
759 | } |
760 | } else { |
761 | out.putU2(0); // attributes_count |
762 | } |
763 | |
764 | } else { |
765 | out.putU2(0); // attributes_count |
766 | // attributes is empty, a 0-element array. |
767 | } |
768 | } catch (IOException ioe) { |
769 | } |
770 | |
771 | fixLengths(mb, maxStack, maxLocals, codeLength); |
772 | method.addAttribute("Code", out); |
773 | |
774 | if (SanityManager.DEBUG) |
775 | { |
776 | // Only validate if the class file format is valid. |
777 | // Ok code length and guaranteed no errors building the class. |
778 | if ((codeLength <= VMOpcode.MAX_CODE_LENGTH) |
779 | && (mb != null && mb.cb.limitMsg == null)) |
780 | { |
781 | // Validate the alternate way to calculate the |
782 | // max stack agrees with the dynamic as the code |
783 | // is built way. |
784 | int walkedMaxStack = findMaxStack(ch, 0, codeLength); |
785 | if (walkedMaxStack != maxStack) |
786 | { |
787 | SanityManager.THROWASSERT("MAX STACK MISMATCH!! " + |
788 | maxStack + " <> " + walkedMaxStack); |
789 | } |
790 | } |
791 | } |
792 | |
793 | } |
794 | /** |
795 | * Return the opcode at the given pc. |
796 | */ |
797 | short getOpcode(int pc) |
798 | { |
799 | return (short) (cout.getData()[CODE_OFFSET + pc] & 0xff); |
800 | } |
801 | |
802 | /** |
803 | * Get the unsigned short value for the opcode at the program |
804 | * counter pc. |
805 | */ |
806 | private int getU2(int pc) |
807 | { |
808 | byte[] codeBytes = cout.getData(); |
809 | |
810 | int u2p = CODE_OFFSET + pc + 1; |
811 | |
812 | return ((codeBytes[u2p] & 0xff) << 8) | (codeBytes[u2p+1] & 0xff); |
813 | } |
814 | |
815 | /** |
816 | * Get the unsigned 32 bit value for the opcode at the program |
817 | * counter pc. |
818 | */ |
819 | private int getU4(int pc) |
820 | { |
821 | byte[] codeBytes = cout.getData(); |
822 | |
823 | int u4p = CODE_OFFSET + pc + 1; |
824 | |
825 | return (((codeBytes[u4p] & 0xff) << 24) | |
826 | ((codeBytes[u4p+1] & 0xff) << 16) | |
827 | ((codeBytes[u4p+2] & 0xff) << 8) | |
828 | ((codeBytes[u4p+3] & 0xff))); |
829 | } |
830 | /** |
831 | * Insert room for byteCount bytes after the instruction at pc |
832 | * and prepare to replace the instruction at pc. The instruction |
833 | * at pc is not modified by this call, space is allocated after it. |
834 | * The newly inserted space will be filled with NOP instructions. |
835 | * |
836 | * Returns a CodeChunk positioned at pc and available to write |
837 | * instructions upto (byteCode + length(existing instruction at pc) bytes. |
838 | * |
839 | * This chunk is left correctly positioned at the end of the code |
840 | * stream, ready to accept more code. Its pc will have increased by |
841 | * additionalBytes. |
842 | * |
843 | * It is the responsibility of the caller to patch up any |
844 | * branches or gotos. |
845 | * |
846 | * @param pc |
847 | * @param additionalBytes |
848 | */ |
849 | CodeChunk insertCodeSpace(int pc, int additionalBytes) |
850 | { |
851 | short existingOpcode = getOpcode(pc); |
852 | |
853 | int lengthOfExistingInstruction |
854 | = instructionLength(existingOpcode); |
855 | |
856 | |
857 | if (additionalBytes > 0) |
858 | { |
859 | // Size of the current code after this pc. |
860 | int sizeToMove = (getPC() - pc) - lengthOfExistingInstruction; |
861 | |
862 | // Increase the code by the number of bytes to be |
863 | // inserted. These NOPs will be overwritten by the |
864 | // moved code by the System.arraycopy below. |
865 | // It's assumed that the number of inserted bytes |
866 | // is small, one or two instructions worth, so it |
867 | // won't be a performance issue. |
868 | for (int i = 0; i < additionalBytes; i++) |
869 | addInstr(VMOpcode.NOP); |
870 | |
871 | // Must get codeBytes here as the array might have re-sized. |
872 | byte[] codeBytes = cout.getData(); |
873 | |
874 | int byteOffset = CODE_OFFSET + pc + lengthOfExistingInstruction; |
875 | |
876 | |
877 | // Shift the existing code stream down |
878 | System.arraycopy( |
879 | codeBytes, byteOffset, |
880 | codeBytes, byteOffset + additionalBytes, |
881 | sizeToMove); |
882 | |
883 | // Place NOPs in the space just freed by the move. |
884 | // This is not required, it ias assumed the caller |
885 | // will overwrite all the bytes they requested, but |
886 | // to be safe fill in with NOPs rather than leaving code |
887 | // that could break the verifier. |
888 | Arrays.fill(codeBytes, byteOffset, byteOffset + additionalBytes, |
889 | (byte) VMOpcode.NOP); |
890 | } |
891 | |
892 | // The caller must overwrite the original instruction |
893 | // at pc, thus increase the range of the limit stream |
894 | // created to include those bytes. |
895 | additionalBytes += lengthOfExistingInstruction; |
896 | |
897 | // Now the caller needs to fill in the instructions |
898 | // that make up the modified byteCount bytes of bytecode stream. |
899 | // Return a CodeChunk that can be used for this and |
900 | // is limited to only those bytes. |
901 | // The pc of the original code chunk is left unchanged. |
902 | |
903 | return new CodeChunk(this, pc, additionalBytes); |
904 | |
905 | } |
906 | |
907 | /* |
908 | * * Methods related to splitting the byte code chunks into sections that |
909 | * fit in the JVM's limits for a single method. |
910 | */ |
911 | |
912 | /** |
913 | * For a block of byte code starting at program counter pc for codeLength |
914 | * bytes return the maximum stack value, assuming a initial stack depth of |
915 | * zero. |
916 | */ |
917 | private int findMaxStack(ClassHolder ch, int pc, int codeLength) { |
918 | |
919 | int endPc = pc + codeLength; |
920 | int stack = 0; |
921 | int maxStack = 0; |
922 | |
923 | for (; pc < endPc;) { |
924 | |
925 | short opcode = getOpcode(pc); |
926 | |
927 | |
928 | int stackDelta = stackWordDelta(ch, pc, opcode); |
929 | |
930 | stack += stackDelta; |
931 | if (stack > maxStack) |
932 | maxStack = stack; |
933 | |
934 | int[] cond_pcs = findConditionalPCs(pc, opcode); |
935 | if (cond_pcs != null) { |
936 | // an else block exists. |
937 | if (cond_pcs[3] != -1) { |
938 | int blockMaxStack = findMaxStack(ch, cond_pcs[1], |
939 | cond_pcs[2]); |
940 | if ((stack + blockMaxStack) > maxStack) |
941 | maxStack = stack + blockMaxStack; |
942 | |
943 | pc = cond_pcs[3]; |
944 | continue; |
945 | } |
946 | } |
947 | |
948 | pc += instructionLength(opcode); |
949 | } |
950 | |
951 | return maxStack; |
952 | } |
953 | |
954 | /** |
955 | * Return the number of stack words pushed (positive) or popped (negative) |
956 | * by this instruction. |
957 | */ |
958 | private int stackWordDelta(ClassHolder ch, int pc, short opcode) { |
959 | if (SanityManager.DEBUG) { |
960 | // this validates the OPCODE_ACTION entry |
961 | instructionLength(opcode); |
962 | } |
963 | |
964 | int stackDelta = OPCODE_ACTION[opcode][0]; |
965 | if (stackDelta == VARIABLE_STACK) { |
966 | stackDelta = getVariableStackDelta(ch, pc, opcode); |
967 | } |
968 | |
969 | return stackDelta; |
970 | } |
971 | |
972 | /** |
973 | * Get the type descriptor in the virtual machine format for the type |
974 | * defined by the constant pool index for the instruction at pc. |
975 | */ |
976 | private String getTypeDescriptor(ClassHolder ch, int pc) { |
977 | int cpi = getU2(pc); |
978 | |
979 | // Field reference or method reference |
980 | CONSTANT_Index_info cii = (CONSTANT_Index_info) ch.getEntry(cpi); |
981 | |
982 | // NameAndType reference |
983 | int nameAndType = cii.getI2(); |
984 | cii = (CONSTANT_Index_info) ch.getEntry(nameAndType); |
985 | |
986 | // UTF8 descriptor |
987 | int descriptor = cii.getI2(); |
988 | CONSTANT_Utf8_info type = (CONSTANT_Utf8_info) ch.getEntry(descriptor); |
989 | |
990 | String vmDescriptor = type.toString(); |
991 | |
992 | return vmDescriptor; |
993 | } |
994 | |
995 | /** |
996 | * Get the word count for a type descriptor in the format of the virual |
997 | * machine. For a method this returns the the word count for the return |
998 | * type. |
999 | */ |
1000 | private static int getDescriptorWordCount(String vmDescriptor) { |
1001 | |
1002 | int width; |
1003 | if (VMDescriptor.DOUBLE.equals(vmDescriptor)) |
1004 | width = 2; |
1005 | else if (VMDescriptor.LONG.equals(vmDescriptor)) |
1006 | width = 2; |
1007 | else if (vmDescriptor.charAt(0) == VMDescriptor.C_METHOD) { |
1008 | switch (vmDescriptor.charAt(vmDescriptor.length() - 1)) { |
1009 | case VMDescriptor.C_DOUBLE: |
1010 | case VMDescriptor.C_LONG: |
1011 | width = 2; |
1012 | break; |
1013 | case VMDescriptor.C_VOID: |
1014 | width = 0; |
1015 | break; |
1016 | default: |
1017 | width = 1; |
1018 | break; |
1019 | } |
1020 | } else |
1021 | width = 1; |
1022 | |
1023 | return width; |
1024 | } |
1025 | |
1026 | /** |
1027 | * Get the number of words pushed (positive) or popped (negative) by this |
1028 | * instruction. The instruction is a get/put field or a method call, thus |
1029 | * the size of the words is defined by the field or method being access. |
1030 | */ |
1031 | private int getVariableStackDelta(ClassHolder ch, int pc, int opcode) { |
1032 | String vmDescriptor = getTypeDescriptor(ch, pc); |
1033 | int width = CodeChunk.getDescriptorWordCount(vmDescriptor); |
1034 | |
1035 | int stackDelta = 0; |
1036 | // Stack delta depends on context. |
1037 | switch (opcode) { |
1038 | case VMOpcode.GETSTATIC: |
1039 | stackDelta = width; |
1040 | break; |
1041 | |
1042 | case VMOpcode.GETFIELD: |
1043 | stackDelta = width - 1; // one for popped object ref |
1044 | break; |
1045 | |
1046 | case VMOpcode.PUTSTATIC: |
1047 | stackDelta = -width; |
1048 | break; |
1049 | |
1050 | case VMOpcode.PUTFIELD: |
1051 | stackDelta = -width - 1; // one for pop object ref |
1052 | break; |
1053 | |
1054 | case VMOpcode.INVOKEVIRTUAL: |
1055 | case VMOpcode.INVOKESPECIAL: |
1056 | stackDelta = -1; // for instance reference for method call. |
1057 | case VMOpcode.INVOKESTATIC: |
1058 | stackDelta += (width - CodeChunk.parameterWordCount(vmDescriptor)); |
1059 | // System.out.println("invoked non-interface " + stackDelta); |
1060 | break; |
1061 | |
1062 | case VMOpcode.INVOKEINTERFACE: |
1063 | // third byte contains the number of arguments to be popped |
1064 | stackDelta = width - getOpcode(pc + 3); |
1065 | // System.out.println("invoked interface " + stackDelta); |
1066 | break; |
1067 | default: |
1068 | System.out.println("WHO IS THIS "); |
1069 | break; |
1070 | |
1071 | } |
1072 | |
1073 | return stackDelta; |
1074 | } |
1075 | |
1076 | /** |
1077 | * Calculate the number of stack words in the arguments pushed for this |
1078 | * method descriptor. |
1079 | */ |
1080 | private static int parameterWordCount(String methodDescriptor) { |
1081 | int wordCount = 0; |
1082 | for (int i = 1;; i++) { |
1083 | switch (methodDescriptor.charAt(i)) { |
1084 | case VMDescriptor.C_ENDMETHOD: |
1085 | return wordCount; |
1086 | case VMDescriptor.C_DOUBLE: |
1087 | case VMDescriptor.C_LONG: |
1088 | wordCount += 2; |
1089 | break; |
1090 | case VMDescriptor.C_ARRAY: |
1091 | // skip while there are array symbols. |
1092 | do { |
1093 | i++; |
1094 | } while (methodDescriptor.charAt(i) == VMDescriptor.C_ARRAY); |
1095 | if (methodDescriptor.charAt(i) != VMDescriptor.C_CLASS) { |
1096 | // an array is a reference, even an array of doubles. |
1097 | wordCount += 1; |
1098 | break; |
1099 | } |
1100 | |
1101 | // fall through to skip the Lclassname; after the array. |
1102 | |
1103 | case VMDescriptor.C_CLASS: |
1104 | // skip until ; |
1105 | do { |
1106 | i++; |
1107 | } while (methodDescriptor.charAt(i) != VMDescriptor.C_ENDCLASS); |
1108 | wordCount += 1; |
1109 | break; |
1110 | default: |
1111 | wordCount += 1; |
1112 | break; |
1113 | } |
1114 | } |
1115 | } |
1116 | |
1117 | /** |
1118 | * Find the limits of a conditional block starting at the instruction with |
1119 | * the given opcode at the program counter pc. |
1120 | * <P> |
1121 | * Returns a six element integer array of program counters and lengths. |
1122 | * <code. [0] - program counter of the IF opcode (passed in as pc) [1] - |
1123 | * program counter of the start of the then block [2] - length of the then |
1124 | * block [3] - program counter of the else block, -1 if no else block |
1125 | * exists. [4] - length of of the else block, -1 if no else block exists. |
1126 | * [5] - program counter of the common end point. </code> |
1127 | * |
1128 | * Looks for and handles conditionals that are written by the Conditional |
1129 | * class. |
1130 | * |
1131 | * @return Null if the opcode is not the start of a conditional otherwise |
1132 | * the array of values. |
1133 | */ |
1134 | private int[] findConditionalPCs(int pc, short opcode) { |
1135 | switch (opcode) { |
1136 | default: |
1137 | return null; |
1138 | case VMOpcode.IFNONNULL: |
1139 | case VMOpcode.IFNULL: |
1140 | case VMOpcode.IFEQ: |
1141 | case VMOpcode.IFNE: |
1142 | break; |
1143 | } |
1144 | |
1145 | int then_pc; |
1146 | int else_pc; |
1147 | int if_off = getU2(pc); |
1148 | |
1149 | if ((if_off == 8) |
1150 | && (getOpcode(pc + VMOpcode.IF_INS_LENGTH) == VMOpcode.GOTO_W)) { |
1151 | // 32 bit branch |
1152 | then_pc = pc + VMOpcode.IF_INS_LENGTH + VMOpcode.GOTO_W_INS_LENGTH; |
1153 | |
1154 | // Get else PC from the 32 bit offset within the GOTO_W |
1155 | // instruction remembering to add it to the pc of that |
1156 | // instruction, not the original IF. |
1157 | else_pc = pc + VMOpcode.IF_INS_LENGTH |
1158 | + getU4(pc + VMOpcode.IF_INS_LENGTH); |
1159 | |
1160 | } else { |
1161 | then_pc = pc + VMOpcode.IF_INS_LENGTH; |
1162 | else_pc = pc + if_off; |
1163 | } |
1164 | |
1165 | // Need to look for the goto or goto_w at the |
1166 | // end of the then block. There might not be |
1167 | // one for the case when there is no else block. |
1168 | // In that case the then block will just run into |
1169 | // what we currently think the else pc. |
1170 | |
1171 | int end_pc = -1; |
1172 | for (int tpc = then_pc; tpc < else_pc;) { |
1173 | short opc = getOpcode(tpc); |
1174 | |
1175 | // need to handle conditionals |
1176 | int[] innerCond = findConditionalPCs(tpc, opc); |
1177 | if (innerCond != null) { |
1178 | // skip to the end of this conditional |
1179 | tpc = innerCond[5]; // end_pc |
1180 | continue; |
1181 | } |
1182 | |
1183 | if (opc == VMOpcode.GOTO) { |
1184 | // not at the end of the then block |
1185 | // so not our goto. Shouldn't see this |
1186 | // with the current code due to the |
1187 | // skipping of the conditional above. |
1188 | // But safe defensive programming. |
1189 | if (tpc != (else_pc - VMOpcode.GOTO_INS_LENGTH)) |
1190 | continue; |
1191 | |
1192 | end_pc = tpc + getU2(tpc); |
1193 | break; |
1194 | } else if (opc == VMOpcode.GOTO_W) { |
1195 | // not at the end of the then block |
1196 | // so not our goto. SHouldn't see this |
1197 | // with the current code due to the |
1198 | // skipping of the conditional above. |
1199 | // But safe defensive programming. |
1200 | if (tpc != (else_pc - VMOpcode.GOTO_W_INS_LENGTH)) |
1201 | continue; |
1202 | |
1203 | end_pc = tpc + getU4(tpc); |
1204 | break; |
1205 | |
1206 | } |
1207 | tpc += instructionLength(opc); |
1208 | } |
1209 | |
1210 | int else_len; |
1211 | int then_len; |
1212 | if (end_pc == -1) { |
1213 | // no else block; |
1214 | end_pc = else_pc; |
1215 | else_pc = -1; |
1216 | |
1217 | then_len = end_pc - then_pc; |
1218 | else_len = -1; |
1219 | } else { |
1220 | then_len = else_pc - then_pc; |
1221 | else_len = end_pc - else_pc; |
1222 | } |
1223 | |
1224 | int[] ret = new int[6]; |
1225 | |
1226 | ret[0] = pc; |
1227 | ret[1] = then_pc; |
1228 | ret[2] = then_len; |
1229 | ret[3] = else_pc; |
1230 | ret[4] = else_len; |
1231 | ret[5] = end_pc; |
1232 | |
1233 | return ret; |
1234 | } |
1235 | |
1236 | /** |
1237 | * Attempt to split the current method by pushing a chunk of |
1238 | * its code into a sub-method. The starting point of the split |
1239 | * (split_pc) must correspond to a stack depth of zero. It is the |
1240 | * reponsibility of the caller to ensure this. |
1241 | * Split is only made if there exists a chunk of code starting at |
1242 | * pc=split_pc, whose stack depth upon termination is zero. |
1243 | * The method will try to split a code section greater than |
1244 | * optimalMinLength but may split earlier if no such block exists. |
1245 | * <P> |
1246 | * The method is aimed at splitting methods that contain |
1247 | * many independent statements. |
1248 | * <P> |
1249 | * If a split is possible this method will perform the split and |
1250 | * create a void sub method, and move the code into the sub-method |
1251 | * and setup this method to call the sub-method before continuing. |
1252 | * This method's max stack and current pc will be correctly set |
1253 | * as though the method had just been created. |
1254 | * |
1255 | * @param mb Method for this chunk. |
1256 | * @param ch Class definition |
1257 | * @param codeLength codeLength to check |
1258 | * @param optimalMinLength minimum length required for split |
1259 | */ |
1260 | final int splitZeroStack(BCMethod mb, ClassHolder ch, final int split_pc, |
1261 | final int codeLength, final int optimalMinLength) { |
1262 | int stack = 0; |
1263 | |
1264 | // maximum possible split seen that is less than |
1265 | // the minimum. |
1266 | int possibleSplitLength = -1; |
1267 | |
1268 | // do not split until at least this point (inclusive) |
1269 | // used to ensure no split occurs in the middle of |
1270 | // a conditional. |
1271 | int outerConditionalEnd_pc = -1; |
1272 | |
1273 | int end_pc = split_pc + codeLength; |
1274 | for (int pc = split_pc; pc < end_pc;) { |
1275 | |
1276 | short opcode = getOpcode(pc); |
1277 | |
1278 | int stackDelta = stackWordDelta(ch, pc, opcode); |
1279 | |
1280 | stack += stackDelta; |
1281 | |
1282 | // Cannot split a conditional but need to calculate |
1283 | // the stack depth at the end of the conditional. |
1284 | // Each path through the conditional will have the |
1285 | // same stack depth. |
1286 | int[] cond_pcs = findConditionalPCs(pc, opcode); |
1287 | if (cond_pcs != null) { |
1288 | // an else block exists, skip the then block. |
1289 | if (cond_pcs[3] != -1) { |
1290 | pc = cond_pcs[3]; |
1291 | continue; |
1292 | } |
1293 | |
1294 | if (SanityManager.DEBUG) { |
1295 | if (outerConditionalEnd_pc != -1) { |
1296 | if (cond_pcs[5] >= outerConditionalEnd_pc) |
1297 | SanityManager.THROWASSERT("NESTED CONDITIONALS!"); |
1298 | } |
1299 | } |
1300 | |
1301 | if (outerConditionalEnd_pc == -1) { |
1302 | outerConditionalEnd_pc = cond_pcs[5]; |
1303 | } |
1304 | } |
1305 | |
1306 | pc += instructionLength(opcode); |
1307 | |
1308 | // Don't split in the middle of a conditional |
1309 | if (outerConditionalEnd_pc != -1) { |
1310 | if (pc > outerConditionalEnd_pc) { |
1311 | // passed the outermost conditional |
1312 | outerConditionalEnd_pc = -1; |
1313 | } |
1314 | continue; |
1315 | } |
1316 | |
1317 | if (stack != 0) |
1318 | continue; |
1319 | |
1320 | int splitLength = pc - split_pc; |
1321 | |
1322 | if (splitLength < optimalMinLength) { |
1323 | // record we do have a possible split. |
1324 | possibleSplitLength = splitLength; |
1325 | continue; |
1326 | } |
1327 | |
1328 | // no point splitting to a method bigger |
1329 | // than the VM can handle. Save one for |
1330 | // return instruction. |
1331 | if (splitLength > BCMethod.CODE_SPLIT_LENGTH - 1) { |
1332 | if (possibleSplitLength == -1) |
1333 | return -1; |
1334 | |
1335 | // Decide if the earlier possible split is |
1336 | // worth it. 100 is an arbitary number, |
1337 | // a real low limit would be the number of |
1338 | // bytes of instructions required to call |
1339 | // the sub-method, four I think. |
1340 | if (possibleSplitLength < 100) |
1341 | return -1; |
1342 | |
1343 | // OK go with the earlier split |
1344 | splitLength = possibleSplitLength; |
1345 | |
1346 | } |
1347 | |
1348 | // Yes, we can split this big method into a smaller method!! |
1349 | |
1350 | BCMethod subMethod = startSubMethod(mb, "void", split_pc, |
1351 | splitLength); |
1352 | |
1353 | CodeChunk subChunk = subMethod.myCode; |
1354 | |
1355 | byte[] codeBytes = cout.getData(); |
1356 | |
1357 | // the code to be moved into the sub method |
1358 | // as a block. This will correctly increase the |
1359 | // program counter. |
1360 | try { |
1361 | subChunk.cout.write(codeBytes, CODE_OFFSET + split_pc, |
1362 | splitLength); |
1363 | } catch (IOException ioe) { |
1364 | // writing to a byte array |
1365 | } |
1366 | |
1367 | // Just cause the sub-method to return, |
1368 | // fix up its maxStack and then complete it. |
1369 | subChunk.addInstr(VMOpcode.RETURN); |
1370 | subMethod.maxStack = subChunk.findMaxStack(ch, 0, subChunk.getPC()); |
1371 | subMethod.complete(); |
1372 | |
1373 | return removePushedCode(mb, ch, subMethod, split_pc, splitLength, |
1374 | codeLength); |
1375 | } |
1376 | return -1; |
1377 | } |
1378 | |
1379 | /** |
1380 | * Start a sub method that we will split the portion of our current code to, |
1381 | * starting from start_pc and including codeLength bytes of code. |
1382 | * |
1383 | * Return a BCMethod obtained from BCMethod.getNewSubMethod with the passed |
1384 | * in return type and same parameters as mb if the code block to be moved |
1385 | * uses parameters. |
1386 | */ |
1387 | private BCMethod startSubMethod(BCMethod mb, String returnType, |
1388 | int split_pc, int codeLength) { |
1389 | |
1390 | boolean needParameters = usesParameters(mb, split_pc, codeLength); |
1391 | |
1392 | return mb.getNewSubMethod(returnType, needParameters); |
1393 | } |
1394 | |
1395 | /** |
1396 | * Does a section of code use parameters. |
1397 | * Any load, exception ALOAD_0 in an instance method, is |
1398 | * seen as using parameters, as this complete byte code |
1399 | * implementation does not use local variables. |
1400 | * |
1401 | */ |
1402 | private boolean usesParameters(BCMethod mb, int pc, int codeLength) { |
1403 | |
1404 | // does the method even have parameters? |
1405 | if (mb.parameters == null) |
1406 | return false; |
1407 | |
1408 | boolean isStatic = (mb.myEntry.getModifier() & Modifier.STATIC) != 0; |
1409 | |
1410 | int endPc = pc + codeLength; |
1411 | |
1412 | for (; pc < endPc;) { |
1413 | short opcode = getOpcode(pc); |
1414 | switch (opcode) { |
1415 | case VMOpcode.ILOAD_0: |
1416 | case VMOpcode.LLOAD_0: |
1417 | case VMOpcode.FLOAD_0: |
1418 | case VMOpcode.DLOAD_0: |
1419 | return true; |
1420 | |
1421 | case VMOpcode.ALOAD_0: |
1422 | if (isStatic) |
1423 | return true; |
1424 | break; |
1425 | |
1426 | case VMOpcode.ILOAD_1: |
1427 | case VMOpcode.LLOAD_1: |
1428 | case VMOpcode.FLOAD_1: |
1429 | case VMOpcode.DLOAD_1: |
1430 | case VMOpcode.ALOAD_1: |
1431 | return true; |
1432 | |
1433 | case VMOpcode.ILOAD_2: |
1434 | case VMOpcode.LLOAD_2: |
1435 | case VMOpcode.FLOAD_2: |
1436 | case VMOpcode.DLOAD_2: |
1437 | case VMOpcode.ALOAD_2: |
1438 | return true; |
1439 | |
1440 | case VMOpcode.ILOAD_3: |
1441 | case VMOpcode.LLOAD_3: |
1442 | case VMOpcode.FLOAD_3: |
1443 | case VMOpcode.DLOAD_3: |
1444 | case VMOpcode.ALOAD_3: |
1445 | return true; |
1446 | |
1447 | case VMOpcode.ILOAD: |
1448 | case VMOpcode.LLOAD: |
1449 | case VMOpcode.FLOAD: |
1450 | case VMOpcode.DLOAD: |
1451 | case VMOpcode.ALOAD: |
1452 | return true; |
1453 | default: |
1454 | break; |
1455 | |
1456 | } |
1457 | pc += instructionLength(opcode); |
1458 | } |
1459 | return false; |
1460 | } |
1461 | |
1462 | /** |
1463 | * Remove a block of code from this method that was |
1464 | * pushed into a sub-method and call the sub-method. |
1465 | * |
1466 | * Returns the pc of this method just after the call |
1467 | * to the sub-method. |
1468 | |
1469 | * @param mb My method |
1470 | * @param ch My class |
1471 | * @param subMethod Sub-method code was pushed into |
1472 | * @param split_pc Program counter the split started at |
1473 | * @param splitLength Length of code split |
1474 | * @param codeLength Length of code before split |
1475 | */ |
1476 | private int removePushedCode(BCMethod mb, ClassHolder ch, |
1477 | BCMethod subMethod, int split_pc, int splitLength, int codeLength) { |
1478 | // now need to fix up this method, create |
1479 | // a new CodeChunk just to be clearer than |
1480 | // trying to modify this chunk directly. |
1481 | CodeChunk replaceChunk = new CodeChunk(); |
1482 | mb.myCode = replaceChunk; |
1483 | mb.maxStack = 0; |
1484 | |
1485 | byte[] codeBytes = cout.getData(); |
1486 | |
1487 | // write any existing code before the split point |
1488 | // into the replacement chunk. |
1489 | if (split_pc != 0) { |
1490 | try { |
1491 | replaceChunk.cout.write(codeBytes, CODE_OFFSET, split_pc); |
1492 | } catch (IOException ioe) { |
1493 | // writing to a byte array |
1494 | } |
1495 | } |
1496 | |
1497 | // Call the sub method, will write into replaceChunk. |
1498 | mb.callSubMethod(subMethod); |
1499 | |
1500 | int postSplit_pc = replaceChunk.getPC(); |
1501 | |
1502 | // Write the code remaining in this method into the replacement chunk |
1503 | |
1504 | int remainingCodeLength = codeLength - splitLength; |
1505 | try { |
1506 | replaceChunk.cout.write(codeBytes, CODE_OFFSET + split_pc |
1507 | + splitLength, remainingCodeLength); |
1508 | } catch (IOException ioe) { |
1509 | // writing to a byte array |
1510 | } |
1511 | |
1512 | mb.maxStack = replaceChunk.findMaxStack(ch, 0, replaceChunk.getPC()); |
1513 | |
1514 | return postSplit_pc; |
1515 | } |
1516 | } |