Mercurial > hg > blitz_condensed
comparison src/com/go/trove/classfile/CodeBuilder.java @ 0:3dc0c5604566
Initial checkin of blitz 2.0 fcs - no installer yet.
author | Dan Creswell <dan.creswell@gmail.com> |
---|---|
date | Sat, 21 Mar 2009 11:00:06 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:3dc0c5604566 |
---|---|
1 /* ==================================================================== | |
2 * Trove - Copyright (c) 1997-2000 Walt Disney Internet Group | |
3 * ==================================================================== | |
4 * The Tea Software License, Version 1.1 | |
5 * | |
6 * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved. | |
7 * | |
8 * Redistribution and use in source and binary forms, with or without | |
9 * modification, are permitted provided that the following conditions | |
10 * are met: | |
11 * | |
12 * 1. Redistributions of source code must retain the above copyright | |
13 * notice, this list of conditions and the following disclaimer. | |
14 * | |
15 * 2. Redistributions in binary form must reproduce the above copyright | |
16 * notice, this list of conditions and the following disclaimer in | |
17 * the documentation and/or other materials provided with the | |
18 * distribution. | |
19 * | |
20 * 3. The end-user documentation included with the redistribution, | |
21 * if any, must include the following acknowledgment: | |
22 * "This product includes software developed by the | |
23 * Walt Disney Internet Group (http://opensource.go.com/)." | |
24 * Alternately, this acknowledgment may appear in the software itself, | |
25 * if and wherever such third-party acknowledgments normally appear. | |
26 * | |
27 * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must | |
28 * not be used to endorse or promote products derived from this | |
29 * software without prior written permission. For written | |
30 * permission, please contact opensource@dig.com. | |
31 * | |
32 * 5. Products derived from this software may not be called "Tea", | |
33 * "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet", | |
34 * "Kettle", "Trove" or "BeanDoc" appear in their name, without prior | |
35 * written permission of the Walt Disney Internet Group. | |
36 * | |
37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |
38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
40 * DISCLAIMED. IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS | |
41 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
42 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
43 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
44 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
45 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
46 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
47 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
48 * ==================================================================== | |
49 * | |
50 * For more information about Tea, please see http://opensource.go.com/. | |
51 */ | |
52 | |
53 package com.go.trove.classfile; | |
54 | |
55 import java.lang.reflect.*; | |
56 | |
57 /****************************************************************************** | |
58 * This class is used as an aid in generating code for a method. | |
59 * It controls the max stack, local variable allocation, labels and bytecode. | |
60 * | |
61 * @author Brian S O'Neill | |
62 * @version | |
63 * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $--> | |
64 */ | |
65 public class CodeBuilder implements CodeBuffer, CodeAssembler { | |
66 private CodeAttr mCodeAttr; | |
67 private ClassFile mClassFile; | |
68 private ConstantPool mCp; | |
69 | |
70 private InstructionList mInstructions = new InstructionList(); | |
71 | |
72 private LocalVariable mThisReference; | |
73 private LocalVariable[] mParameters; | |
74 | |
75 private boolean mSaveLineNumberInfo; | |
76 private boolean mSaveLocalVariableInfo; | |
77 | |
78 /** | |
79 * Construct a CodeBuilder for the CodeAttr of the given MethodInfo. The | |
80 * CodeBuffer for the CodeAttr is automatically set to this CodeBuilder. | |
81 */ | |
82 public CodeBuilder(MethodInfo info) { | |
83 this(info, true, false); | |
84 } | |
85 | |
86 /** | |
87 * Construct a CodeBuilder for the CodeAttr of the given MethodInfo. The | |
88 * CodeBuffer for the CodeAttr is automatically set to this CodeBuilder. | |
89 * | |
90 * @param saveLineNumberInfo When set false, all calls to mapLineNumber | |
91 * are ignored. By default, this value is true. | |
92 * | |
93 * @param saveLocalVariableInfo When set true, all local variable | |
94 * usage information is saved in the ClassFile. By default, this value | |
95 * is false. | |
96 * | |
97 * @see #mapLineNumber | |
98 */ | |
99 public CodeBuilder(MethodInfo info, boolean saveLineNumberInfo, | |
100 boolean saveLocalVariableInfo) { | |
101 mCodeAttr = info.getCodeAttr(); | |
102 mClassFile = info.getClassFile(); | |
103 mCp = mClassFile.getConstantPool(); | |
104 | |
105 mCodeAttr.setCodeBuffer(this); | |
106 | |
107 mSaveLineNumberInfo = saveLineNumberInfo; | |
108 mSaveLocalVariableInfo = saveLocalVariableInfo; | |
109 | |
110 // Create LocalVariable references for "this" reference and other | |
111 // passed in parameters. | |
112 | |
113 LocalVariable localVar; | |
114 int varNum = 0; | |
115 | |
116 if (!info.getAccessFlags().isStatic()) { | |
117 localVar = mInstructions.createLocalParameter | |
118 ("this", mClassFile.getType(), varNum++); | |
119 mThisReference = localVar; | |
120 | |
121 if (saveLocalVariableInfo) { | |
122 mCodeAttr.localVariableUse(localVar); | |
123 } | |
124 } | |
125 | |
126 TypeDescriptor[] paramTypes = | |
127 info.getMethodDescriptor().getParameterTypes(); | |
128 int paramSize = paramTypes.length; | |
129 | |
130 mParameters = new LocalVariable[paramSize]; | |
131 | |
132 for (int i = 0; i<paramTypes.length; i++) { | |
133 localVar = mInstructions.createLocalParameter | |
134 (null, paramTypes[i], varNum); | |
135 varNum += (localVar.isDoubleWord() ? 2 : 1); | |
136 mParameters[i] = localVar; | |
137 | |
138 if (saveLocalVariableInfo) { | |
139 mCodeAttr.localVariableUse(localVar); | |
140 } | |
141 } | |
142 } | |
143 | |
144 public int getMaxStackDepth() { | |
145 return mInstructions.getMaxStackDepth(); | |
146 } | |
147 | |
148 public int getMaxLocals() { | |
149 return mInstructions.getMaxLocals(); | |
150 } | |
151 | |
152 public byte[] getByteCodes() { | |
153 return mInstructions.getByteCodes(); | |
154 } | |
155 | |
156 public ExceptionHandler[] getExceptionHandlers() { | |
157 return mInstructions.getExceptionHandlers(); | |
158 } | |
159 | |
160 private void addCode(int stackAdjust, byte opcode) { | |
161 mInstructions.new CodeInstruction(stackAdjust, new byte[] {opcode}); | |
162 } | |
163 | |
164 private void addCode(int stackAdjust, byte opcode, byte operand) { | |
165 mInstructions.new CodeInstruction | |
166 (stackAdjust, new byte[] {opcode, operand}); | |
167 } | |
168 | |
169 private void addCode(int stackAdjust, byte opcode, short operand) { | |
170 mInstructions.new CodeInstruction | |
171 (stackAdjust, | |
172 new byte[] {opcode, (byte)(operand >> 8), (byte)operand}); | |
173 } | |
174 | |
175 private void addCode(int stackAdjust, byte opcode, int operand) { | |
176 byte[] bytes = new byte[5]; | |
177 | |
178 bytes[0] = opcode; | |
179 bytes[1] = (byte)(operand >> 24); | |
180 bytes[2] = (byte)(operand >> 16); | |
181 bytes[3] = (byte)(operand >> 8); | |
182 bytes[4] = (byte)operand; | |
183 | |
184 mInstructions.new CodeInstruction(stackAdjust, bytes); | |
185 } | |
186 | |
187 private void addCode(int stackAdjust, byte opcode, ConstantInfo info) { | |
188 // The zeros get filled in later, when the ConstantInfo index | |
189 // is resolved. | |
190 mInstructions.new ConstantOperandInstruction | |
191 (stackAdjust, | |
192 new byte[] {opcode, (byte)0, (byte)0}, info); | |
193 } | |
194 | |
195 /** | |
196 * Returns LocalVariable references for all the parameters passed into | |
197 * the method being assembled, not including any "this" reference. | |
198 * Returns a zero-length array if there are no passed in parameters. | |
199 * | |
200 * <p>The names of the LocalVariables returned by this method are initially | |
201 * set to null. It is encouraged that a name be provided. | |
202 */ | |
203 public LocalVariable[] getParameters() { | |
204 return (LocalVariable[])mParameters.clone(); | |
205 } | |
206 | |
207 /** | |
208 * Creates a LocalVariable reference from a name and type. Although name | |
209 * is optional, it is encouraged that a name be provided. Names do not | |
210 * need to be unique. | |
211 * | |
212 * @param name Optional name for the LocalVariable. | |
213 * @param type The type of data that the requested LocalVariable can | |
214 * store. | |
215 */ | |
216 public LocalVariable createLocalVariable(String name, | |
217 TypeDescriptor type) { | |
218 LocalVariable localVar = mInstructions.createLocalVariable(name, type); | |
219 | |
220 if (mSaveLocalVariableInfo) { | |
221 mCodeAttr.localVariableUse(localVar); | |
222 } | |
223 | |
224 return localVar; | |
225 } | |
226 | |
227 /** | |
228 * Creates a label, whose location must be set. To create a label and | |
229 * locate it here, the following example demonstrates how the call to | |
230 * setLocation can be chained: | |
231 * | |
232 * <pre> | |
233 * CodeBuilder builder; | |
234 * ... | |
235 * Label label = builder.createLabel().setLocation(); | |
236 * </pre> | |
237 * | |
238 * @see Label#setLocation | |
239 */ | |
240 public Label createLabel() { | |
241 return mInstructions.new LabelInstruction(); | |
242 } | |
243 | |
244 /** | |
245 * Sets up an exception handler located here, the location of the next | |
246 * code to be generated. | |
247 * | |
248 * @param startLocation Location or label at the start of the section of | |
249 * code to be wrapped by an exception handler. | |
250 * @param endLocation Location or label directly after the end of the | |
251 * section of code. | |
252 * @param catchClassName The name of the type of exception to be caught; | |
253 * if null, then catch every object. | |
254 */ | |
255 public void exceptionHandler(Location startLocation, | |
256 Location endLocation, | |
257 String catchClassName) { | |
258 Location catchLocation = createLabel().setLocation(); | |
259 | |
260 ConstantClassInfo catchClass; | |
261 if (catchClassName == null) { | |
262 catchClass = null; | |
263 } | |
264 else { | |
265 catchClass = ConstantClassInfo.make(mCp, catchClassName); | |
266 } | |
267 | |
268 ExceptionHandler handler = | |
269 new ExceptionHandler(startLocation, endLocation, | |
270 catchLocation, catchClass); | |
271 | |
272 mInstructions.addExceptionHandler(handler); | |
273 } | |
274 | |
275 /** | |
276 * Map the location of the next code to be generated to a line number | |
277 * in source code. This enables line numbers in a stack trace from the | |
278 * generated code. | |
279 */ | |
280 public void mapLineNumber(int lineNumber) { | |
281 if (mSaveLineNumberInfo) { | |
282 mCodeAttr.mapLineNumber(createLabel().setLocation(), lineNumber); | |
283 } | |
284 } | |
285 | |
286 // load-constant-to-stack style instructions | |
287 | |
288 /** | |
289 * Generates code that loads a constant string value onto the stack. | |
290 * If value is null, the generated code loads a null onto the stack. | |
291 * Strings that exceed 65535 UTF encoded bytes in length are loaded by | |
292 * creating a StringBuffer, appending substrings, and then converting to a | |
293 * String. | |
294 */ | |
295 public void loadConstant(String value) { | |
296 if (value == null) { | |
297 addCode(1, Opcode.ACONST_NULL); | |
298 return; | |
299 } | |
300 | |
301 int strlen = value.length(); | |
302 | |
303 if (strlen <= (65535 / 3)) { | |
304 // Guaranteed to fit in a Java UTF encoded string. | |
305 ConstantInfo info = ConstantStringInfo.make(mCp, value); | |
306 mInstructions.new LoadConstantInstruction(1, info); | |
307 return; | |
308 } | |
309 | |
310 // Compute actual UTF length. | |
311 | |
312 int utflen = 0; | |
313 | |
314 for (int i=0; i<strlen; i++) { | |
315 int c = value.charAt(i); | |
316 if ((c >= 0x0001) && (c <= 0x007F)) { | |
317 utflen++; | |
318 } | |
319 else if (c > 0x07FF) { | |
320 utflen += 3; | |
321 } | |
322 else { | |
323 utflen += 2; | |
324 } | |
325 } | |
326 | |
327 if (utflen <= 65535) { | |
328 ConstantInfo info = ConstantStringInfo.make(mCp, value); | |
329 mInstructions.new LoadConstantInstruction(1, info); | |
330 return; | |
331 } | |
332 | |
333 // Break string up into chunks and construct in a StringBuffer. | |
334 | |
335 TypeDescriptor stringBufferDesc = | |
336 new TypeDescriptor(StringBuffer.class); | |
337 | |
338 TypeDescriptor intDesc = new TypeDescriptor(int.class); | |
339 TypeDescriptor stringDesc = new TypeDescriptor(String.class); | |
340 TypeDescriptor[] stringParam = new TypeDescriptor[] {stringDesc}; | |
341 | |
342 newObject(stringBufferDesc); | |
343 dup(); | |
344 loadConstant(strlen); | |
345 invokeConstructor("java.lang.StringBuffer", | |
346 new TypeDescriptor[] {intDesc}); | |
347 | |
348 int beginIndex; | |
349 int endIndex = 0; | |
350 | |
351 while (endIndex < strlen) { | |
352 beginIndex = endIndex; | |
353 | |
354 // Make each chunk as large as possible. | |
355 utflen = 0; | |
356 for (; endIndex < strlen; endIndex++) { | |
357 int c = value.charAt(endIndex); | |
358 int size; | |
359 if ((c >= 0x0001) && (c <= 0x007F)) { | |
360 size = 1; | |
361 } | |
362 else if (c > 0x07FF) { | |
363 size = 3; | |
364 } | |
365 else { | |
366 size = 2; | |
367 } | |
368 | |
369 if ((utflen + size) > 65535) { | |
370 break; | |
371 } | |
372 else { | |
373 utflen += size; | |
374 } | |
375 } | |
376 | |
377 String substr = value.substring(beginIndex, endIndex); | |
378 | |
379 ConstantInfo info = ConstantStringInfo.make(mCp, substr); | |
380 mInstructions.new LoadConstantInstruction(1, info); | |
381 | |
382 invokeVirtual("java.lang.StringBuffer", "append", | |
383 stringBufferDesc, stringParam); | |
384 } | |
385 | |
386 invokeVirtual("java.lang.StringBuffer", "toString", | |
387 stringDesc, null); | |
388 } | |
389 | |
390 /** | |
391 * Generates code that loads a constant boolean value onto the stack. | |
392 */ | |
393 public void loadConstant(boolean value) { | |
394 loadConstant(value?1:0); | |
395 } | |
396 | |
397 /** | |
398 * Generates code that loads a constant int, char, short or byte value | |
399 * onto the stack. | |
400 */ | |
401 public void loadConstant(int value) { | |
402 if (-1 <= value && value <= 5) { | |
403 byte op; | |
404 | |
405 switch(value) { | |
406 case -1: | |
407 op = Opcode.ICONST_M1; | |
408 break; | |
409 case 0: | |
410 op = Opcode.ICONST_0; | |
411 break; | |
412 case 1: | |
413 op = Opcode.ICONST_1; | |
414 break; | |
415 case 2: | |
416 op = Opcode.ICONST_2; | |
417 break; | |
418 case 3: | |
419 op = Opcode.ICONST_3; | |
420 break; | |
421 case 4: | |
422 op = Opcode.ICONST_4; | |
423 break; | |
424 case 5: | |
425 op = Opcode.ICONST_5; | |
426 break; | |
427 default: | |
428 op = Opcode.NOP; | |
429 } | |
430 | |
431 addCode(1, op); | |
432 } | |
433 else if (-128 <= value && value <= 127) { | |
434 addCode(1, Opcode.BIPUSH, (byte)value); | |
435 } | |
436 else if (-32768 <= value && value <= 32767) { | |
437 addCode(1, Opcode.SIPUSH, (short)value); | |
438 } | |
439 else { | |
440 ConstantInfo info = ConstantIntegerInfo.make(mCp, value); | |
441 mInstructions.new LoadConstantInstruction(1, info); | |
442 } | |
443 } | |
444 | |
445 /** | |
446 * Generates code that loads a constant long value onto the stack. | |
447 */ | |
448 public void loadConstant(long value) { | |
449 if (value == 0) { | |
450 addCode(2, Opcode.LCONST_0); | |
451 } | |
452 else if (value == 1) { | |
453 addCode(2, Opcode.LCONST_1); | |
454 } | |
455 else { | |
456 ConstantInfo info = ConstantLongInfo.make(mCp, value); | |
457 mInstructions.new LoadConstantInstruction(2, info, true); | |
458 } | |
459 } | |
460 | |
461 /** | |
462 * Generates code that loads a constant float value onto the stack. | |
463 */ | |
464 public void loadConstant(float value) { | |
465 if (value == 0) { | |
466 addCode(1, Opcode.FCONST_0); | |
467 } | |
468 else if (value == 1) { | |
469 addCode(1, Opcode.FCONST_1); | |
470 } | |
471 else if (value == 2) { | |
472 addCode(1, Opcode.FCONST_2); | |
473 } | |
474 else { | |
475 ConstantInfo info = ConstantFloatInfo.make(mCp, value); | |
476 mInstructions.new LoadConstantInstruction(1, info); | |
477 } | |
478 } | |
479 | |
480 /** | |
481 * Generates code that loads a constant double value onto the stack. | |
482 */ | |
483 public void loadConstant(double value) { | |
484 if (value == 0) { | |
485 addCode(2, Opcode.DCONST_0); | |
486 } | |
487 else if (value == 1) { | |
488 addCode(2, Opcode.DCONST_1); | |
489 } | |
490 else { | |
491 ConstantInfo info = ConstantDoubleInfo.make(mCp, value); | |
492 mInstructions.new LoadConstantInstruction(2, info, true); | |
493 } | |
494 } | |
495 | |
496 // load-local-to-stack style instructions | |
497 | |
498 /** | |
499 * Generates code that loads a local variable onto the stack. Parameters | |
500 * passed to a method and the "this" reference are all considered local | |
501 * variables, as well as any that were created. | |
502 * | |
503 * @param local The local variable reference | |
504 * @see #getParameters | |
505 * @see #createLocalVariable | |
506 */ | |
507 public void loadLocal(LocalVariable local) { | |
508 if (local == null) { | |
509 throw new NullPointerException("No local variable specified"); | |
510 } | |
511 | |
512 TypeDescriptor type = local.getType(); | |
513 Class clazz = type.getClassArg(); | |
514 | |
515 int stackAdjust = 1; | |
516 | |
517 if (clazz != null && type.getDimensions() == 0 && | |
518 (clazz == long.class || clazz == double.class)) { | |
519 | |
520 stackAdjust++; | |
521 } | |
522 | |
523 mInstructions.new LoadLocalInstruction(stackAdjust, local); | |
524 } | |
525 | |
526 /** | |
527 * Loads a reference to "this" onto the stack. Static methods have no | |
528 * "this" reference, and an exception is thrown when attempting to | |
529 * generate "this" in a static method. | |
530 */ | |
531 public void loadThis() { | |
532 if (mThisReference != null) { | |
533 loadLocal(mThisReference); | |
534 } | |
535 else { | |
536 throw new RuntimeException | |
537 ("Attempt to load \"this\" reference in a static method"); | |
538 } | |
539 } | |
540 | |
541 // store-from-stack-to-local style instructions | |
542 | |
543 /** | |
544 * Generates code that pops a value off of the stack into a local variable. | |
545 * Parameters passed to a method and the "this" reference are all | |
546 * considered local variables, as well as any that were created. | |
547 * | |
548 * @param local The local variable reference | |
549 * @see #getParameters | |
550 * @see #createLocalVariable | |
551 */ | |
552 public void storeLocal(LocalVariable local) { | |
553 if (local == null) { | |
554 throw new NullPointerException("No local variable specified"); | |
555 } | |
556 | |
557 TypeDescriptor type = local.getType(); | |
558 Class clazz = type.getClassArg(); | |
559 | |
560 int stackAdjust = -1; | |
561 | |
562 if (clazz != null && type.getDimensions() == 0 && | |
563 (clazz == long.class || clazz == double.class)) { | |
564 | |
565 stackAdjust--; | |
566 } | |
567 | |
568 mInstructions.new StoreLocalInstruction(stackAdjust, local); | |
569 } | |
570 | |
571 // load-to-stack-from-array style instructions | |
572 | |
573 /** | |
574 * Generates code that loads a value from an array. An array | |
575 * reference followed by an index must be on the stack. The array | |
576 * reference and index are replaced by the value retrieved from the array | |
577 * after the generated instruction has executed. | |
578 * | |
579 * <p>The type doesn't need to be an exact match for objects. Object.class | |
580 * works fine for all objects. For primitive types, use the class that | |
581 * matches that type. For an int the type is int.class. | |
582 * | |
583 * @param type The type of data stored in the array. | |
584 */ | |
585 public void loadFromArray(Class type) { | |
586 byte op; | |
587 int stackAdjust; | |
588 | |
589 if (type == int.class) { | |
590 stackAdjust = -1; | |
591 op = Opcode.IALOAD; | |
592 } | |
593 else if (type == boolean.class || type == byte.class) { | |
594 stackAdjust = -1; | |
595 op = Opcode.BALOAD; | |
596 } | |
597 else if (type == short.class) { | |
598 stackAdjust = -1; | |
599 op = Opcode.SALOAD; | |
600 } | |
601 else if (type == char.class) { | |
602 stackAdjust = -1; | |
603 op = Opcode.CALOAD; | |
604 } | |
605 else if (type == long.class) { | |
606 stackAdjust = 0; | |
607 op = Opcode.LALOAD; | |
608 } | |
609 else if (type == float.class) { | |
610 stackAdjust = -1; | |
611 op = Opcode.FALOAD; | |
612 } | |
613 else if (type == double.class) { | |
614 stackAdjust = 0; | |
615 op = Opcode.DALOAD; | |
616 } | |
617 else { | |
618 stackAdjust = -1; | |
619 op = Opcode.AALOAD; | |
620 } | |
621 | |
622 addCode(stackAdjust, op); | |
623 } | |
624 | |
625 // store-to-array-from-stack style instructions | |
626 | |
627 /** | |
628 * Generates code that stores a value to an array. An array | |
629 * reference followed by an index, followed by a value (or two if a long | |
630 * or double) must be on the stack. All items on the stack are gone | |
631 * after the generated instruction has executed. | |
632 * | |
633 * <p>The type doesn't need to be an exact match for objects. Object.class | |
634 * works fine for all objects. For primitive types, use the class that | |
635 * matches that type. For an int the type is int.class. | |
636 * | |
637 * @param type The type of data stored in the array. | |
638 */ | |
639 public void storeToArray(Class type) { | |
640 byte op; | |
641 int stackAdjust; | |
642 | |
643 if (type == int.class) { | |
644 stackAdjust = -3; | |
645 op = Opcode.IASTORE; | |
646 } | |
647 else if (type == boolean.class || type == byte.class) { | |
648 stackAdjust = -3; | |
649 op = Opcode.BASTORE; | |
650 } | |
651 else if (type == short.class) { | |
652 stackAdjust = -3; | |
653 op = Opcode.SASTORE; | |
654 } | |
655 else if (type == char.class) { | |
656 stackAdjust = -3; | |
657 op = Opcode.CASTORE; | |
658 } | |
659 else if (type == long.class) { | |
660 stackAdjust = -4; | |
661 op = Opcode.LASTORE; | |
662 } | |
663 else if (type == float.class) { | |
664 stackAdjust = -3; | |
665 op = Opcode.FASTORE; | |
666 } | |
667 else if (type == double.class) { | |
668 stackAdjust = -4; | |
669 op = Opcode.DASTORE; | |
670 } | |
671 else { | |
672 stackAdjust = -3; | |
673 op = Opcode.AASTORE; | |
674 } | |
675 | |
676 addCode(stackAdjust, op); | |
677 } | |
678 | |
679 // load-field-to-stack style instructions | |
680 | |
681 /** | |
682 * Generates code that loads a value from a field from this class. | |
683 * An object reference must be on the stack. After the generated code | |
684 * has executed, the object reference is replaced by the value retrieved | |
685 * from the field. | |
686 */ | |
687 public void loadField(String fieldName, | |
688 TypeDescriptor type) { | |
689 getfield(0, Opcode.GETFIELD, constantField(fieldName, type), type); | |
690 } | |
691 | |
692 /** | |
693 * Generates code that loads a value from a field from any class. | |
694 * An object reference must be on the stack. After the generated code | |
695 * has executed, the object reference is replaced by the value retrieved | |
696 * from the field. | |
697 */ | |
698 public void loadField(String className, | |
699 String fieldName, | |
700 TypeDescriptor type) { | |
701 | |
702 getfield(0, Opcode.GETFIELD, | |
703 mCp.addConstantField(className, fieldName, type), | |
704 type); | |
705 } | |
706 | |
707 /** | |
708 * Generates code that loads a value from a static field from this class. | |
709 * After the generated code has executed, the value retrieved is placed | |
710 * on the stack. | |
711 */ | |
712 public void loadStaticField(String fieldName, | |
713 TypeDescriptor type) { | |
714 | |
715 getfield(1, Opcode.GETSTATIC, constantField(fieldName, type), type); | |
716 } | |
717 | |
718 /** | |
719 * Generates code that loads a value from a static field from any class. | |
720 * After the generated code has executed, the value retrieved is placed | |
721 * on the stack. | |
722 */ | |
723 public void loadStaticField(String className, | |
724 String fieldName, | |
725 TypeDescriptor type) { | |
726 | |
727 getfield(1, Opcode.GETSTATIC, | |
728 mCp.addConstantField(className, fieldName, type), | |
729 type); | |
730 } | |
731 | |
732 private void getfield(int stackAdjust, byte opcode, ConstantInfo info, | |
733 TypeDescriptor type) { | |
734 | |
735 Class clazz = type.getClassArg(); | |
736 if (clazz != null && type.getDimensions() == 0 && | |
737 (clazz == long.class || clazz == double.class)) { | |
738 stackAdjust++; | |
739 } | |
740 | |
741 addCode(stackAdjust, opcode, info); | |
742 } | |
743 | |
744 private ConstantFieldInfo constantField(String fieldName, | |
745 TypeDescriptor type) { | |
746 return mCp.addConstantField | |
747 (mClassFile.getClassName(), fieldName, type); | |
748 } | |
749 | |
750 // store-to-field-from-stack style instructions | |
751 | |
752 /** | |
753 * Generates code that stores a value into a field from this class. | |
754 * An object reference and value must be on the stack. After the generated | |
755 * code has executed, the object reference and value are gone from | |
756 * the stack. | |
757 */ | |
758 public void storeField(String fieldName, | |
759 TypeDescriptor type) { | |
760 | |
761 putfield(-1, Opcode.PUTFIELD, constantField(fieldName, type), type); | |
762 } | |
763 | |
764 /** | |
765 * Generates code that stores a value into a field from any class. | |
766 * An object reference and value must be on the stack. After the generated | |
767 * code has executed, the object reference and value are gone from | |
768 * the stack. | |
769 */ | |
770 public void storeField(String className, | |
771 String fieldName, | |
772 TypeDescriptor type) { | |
773 | |
774 putfield(-1, Opcode.PUTFIELD, | |
775 mCp.addConstantField(className, fieldName, type), | |
776 type); | |
777 } | |
778 | |
779 /** | |
780 * Generates code that stores a value into a field from this class. | |
781 * A value must be on the stack. After the generated | |
782 * code has executed, the value is gone from the stack. | |
783 */ | |
784 public void storeStaticField(String fieldName, | |
785 TypeDescriptor type) { | |
786 | |
787 putfield(0, Opcode.PUTSTATIC, constantField(fieldName, type), type); | |
788 } | |
789 | |
790 /** | |
791 * Generates code that stores a value into a field from any class. | |
792 * A value must be on the stack. After the generated | |
793 * code has executed, the value is gone from the stack. | |
794 */ | |
795 public void storeStaticField(String className, | |
796 String fieldName, | |
797 TypeDescriptor type) { | |
798 | |
799 putfield(0, Opcode.PUTSTATIC, | |
800 mCp.addConstantField(className, fieldName, type), | |
801 type); | |
802 } | |
803 | |
804 private void putfield(int stackAdjust, byte opcode, ConstantInfo info, | |
805 TypeDescriptor type) { | |
806 | |
807 Class clazz = type.getClassArg(); | |
808 if (clazz != null && type.getDimensions() == 0 && | |
809 (clazz == long.class || clazz == double.class)) { | |
810 | |
811 stackAdjust -= 2; | |
812 } | |
813 else { | |
814 stackAdjust--; | |
815 } | |
816 | |
817 addCode(stackAdjust, opcode, info); | |
818 } | |
819 | |
820 // return style instructions | |
821 | |
822 /** | |
823 * Generates code that returns void. | |
824 */ | |
825 public void returnVoid() { | |
826 addCode(0, Opcode.RETURN); | |
827 } | |
828 | |
829 /** | |
830 * Generates code that returns an object or primitive type. The value to | |
831 * return must be on the stack. | |
832 * | |
833 * <p>The type doesn't need to be an exact match for objects. Object.class | |
834 * works fine for all objects. For primitive types, use the class that | |
835 * matches that type. For an int the type is int.class. | |
836 */ | |
837 public void returnValue(Class type) { | |
838 int stackAdjust = -1; | |
839 byte op; | |
840 | |
841 if (type == int.class || | |
842 type == boolean.class || | |
843 type == byte.class || | |
844 type == short.class || | |
845 type == char.class) { | |
846 | |
847 op = Opcode.IRETURN; | |
848 } | |
849 else if (type == long.class) { | |
850 stackAdjust--; | |
851 op = Opcode.LRETURN; | |
852 } | |
853 else if (type == float.class) { | |
854 op = Opcode.FRETURN; | |
855 } | |
856 else if (type == double.class) { | |
857 stackAdjust--; | |
858 op = Opcode.DRETURN; | |
859 } | |
860 else if (type == void.class) { | |
861 stackAdjust++; | |
862 op = Opcode.RETURN; | |
863 } | |
864 else { | |
865 op = Opcode.ARETURN; | |
866 } | |
867 | |
868 addCode(stackAdjust, op); | |
869 } | |
870 | |
871 // numerical conversion style instructions | |
872 | |
873 /** | |
874 * Generates code that converts the value of a primitive type already | |
875 * on the stack. | |
876 */ | |
877 public void convert(Class fromType, Class toType) { | |
878 int stackAdjust = 0; | |
879 byte op; | |
880 | |
881 if (fromType == int.class || | |
882 fromType == boolean.class || | |
883 fromType == byte.class || | |
884 fromType == short.class || | |
885 fromType == char.class) { | |
886 | |
887 if (toType == byte.class) { | |
888 op = Opcode.I2B; | |
889 } | |
890 else if (toType == short.class) { | |
891 op = Opcode.I2S; | |
892 } | |
893 else if (toType == char.class) { | |
894 op = Opcode.I2C; | |
895 } | |
896 else if (toType == float.class) { | |
897 op = Opcode.I2F; | |
898 } | |
899 else if (toType == long.class) { | |
900 stackAdjust = 1; | |
901 op = Opcode.I2L; | |
902 } | |
903 else if (toType == double.class) { | |
904 stackAdjust = 1; | |
905 op = Opcode.I2D; | |
906 } | |
907 else if (toType == int.class) { | |
908 return; | |
909 } | |
910 else { | |
911 throw new RuntimeException("Invalid conversion: int to " + | |
912 toType); | |
913 } | |
914 | |
915 addCode(stackAdjust, op); | |
916 return; | |
917 } | |
918 else if (fromType == long.class) { | |
919 if (toType == int.class) { | |
920 stackAdjust = -1; | |
921 op = Opcode.L2I; | |
922 } | |
923 else if (toType == float.class) { | |
924 stackAdjust = -1; | |
925 op = Opcode.L2F; | |
926 } | |
927 else if (toType == double.class) { | |
928 op = Opcode.L2D; | |
929 } | |
930 else if (toType == byte.class || | |
931 toType == char.class || | |
932 toType == short.class) { | |
933 | |
934 convert(fromType, int.class); | |
935 convert(int.class, toType); | |
936 return; | |
937 } | |
938 else if (toType == long.class) { | |
939 return; | |
940 } | |
941 else { | |
942 throw new RuntimeException("Invalid conversion: long to " + | |
943 toType); | |
944 } | |
945 | |
946 addCode(stackAdjust, op); | |
947 return; | |
948 } | |
949 else if (fromType == float.class) { | |
950 if (toType == int.class) { | |
951 op = Opcode.F2I; | |
952 } | |
953 else if (toType == long.class) { | |
954 stackAdjust = 1; | |
955 op = Opcode.F2L; | |
956 } | |
957 else if (toType == double.class) { | |
958 stackAdjust = 1; | |
959 op = Opcode.F2D; | |
960 } | |
961 else if (toType == byte.class || | |
962 toType == char.class || | |
963 toType == short.class) { | |
964 | |
965 convert(fromType, int.class); | |
966 convert(int.class, toType); | |
967 return; | |
968 } | |
969 else if (toType == float.class) { | |
970 return; | |
971 } | |
972 else { | |
973 throw new RuntimeException("Invalid conversion: float to " + | |
974 toType); | |
975 } | |
976 | |
977 addCode(stackAdjust, op); | |
978 return; | |
979 } | |
980 else if (fromType == double.class) { | |
981 if (toType == int.class) { | |
982 stackAdjust = -1; | |
983 op = Opcode.D2I; | |
984 } | |
985 else if (toType == float.class) { | |
986 stackAdjust = -1; | |
987 op = Opcode.D2F; | |
988 } | |
989 else if (toType == long.class) { | |
990 op = Opcode.D2L; | |
991 } | |
992 else if (toType == byte.class || | |
993 toType == char.class || | |
994 toType == short.class) { | |
995 | |
996 convert(fromType, int.class); | |
997 convert(int.class, toType); | |
998 return; | |
999 } | |
1000 else if (toType == double.class) { | |
1001 return; | |
1002 } | |
1003 else { | |
1004 throw new RuntimeException("Invalid conversion: double to " + | |
1005 toType); | |
1006 } | |
1007 | |
1008 addCode(stackAdjust, op); | |
1009 return; | |
1010 } | |
1011 else { | |
1012 throw new RuntimeException("Invalid conversion: " + fromType + | |
1013 " to " + toType); | |
1014 } | |
1015 } | |
1016 | |
1017 // invocation style instructions | |
1018 | |
1019 /** | |
1020 * Generates code to invoke a method in any class. If the method is | |
1021 * non-static, the object reference and the method's argument(s) must be | |
1022 * on the stack. If the method is static and has any arguments, just | |
1023 * the method's arguments must be on the stack. | |
1024 */ | |
1025 public void invoke(Method method) { | |
1026 TypeDescriptor ret = new TypeDescriptor(method.getReturnType()); | |
1027 | |
1028 Class[] paramClasses = method.getParameterTypes(); | |
1029 TypeDescriptor[] params = new TypeDescriptor[paramClasses.length]; | |
1030 for (int i=0; i<params.length; i++) { | |
1031 params[i] = new TypeDescriptor(paramClasses[i]); | |
1032 } | |
1033 | |
1034 Class clazz = method.getDeclaringClass(); | |
1035 | |
1036 if (Modifier.isStatic(method.getModifiers())) { | |
1037 invokeStatic(clazz.getName(), | |
1038 method.getName(), | |
1039 ret, | |
1040 params); | |
1041 } | |
1042 else if (clazz.isInterface()) { | |
1043 invokeInterface(clazz.getName(), | |
1044 method.getName(), | |
1045 ret, | |
1046 params); | |
1047 } | |
1048 else { | |
1049 invokeVirtual(clazz.getName(), | |
1050 method.getName(), | |
1051 ret, | |
1052 params); | |
1053 } | |
1054 } | |
1055 | |
1056 /** | |
1057 * Generates code to invoke a class constructor in any class. The object | |
1058 * reference and the constructor's argument(s) must be on the stack. | |
1059 */ | |
1060 public void invoke(Constructor constructor) { | |
1061 Class[] paramClasses = constructor.getParameterTypes(); | |
1062 TypeDescriptor[] params = new TypeDescriptor[paramClasses.length]; | |
1063 for (int i=0; i<params.length; i++) { | |
1064 params[i] = new TypeDescriptor(paramClasses[i]); | |
1065 } | |
1066 | |
1067 invokeConstructor(constructor.getDeclaringClass().toString(), params); | |
1068 } | |
1069 | |
1070 /** | |
1071 * Generates code to invoke a virtual method in this class. The object | |
1072 * reference and the method's argument(s) must be on the stack. | |
1073 * | |
1074 * @param ret May be null if method returns void. | |
1075 * @param params May be null if method takes no parameters. | |
1076 */ | |
1077 public void invokeVirtual(String methodName, | |
1078 TypeDescriptor ret, | |
1079 TypeDescriptor[] params) { | |
1080 | |
1081 ConstantInfo info = mCp.addConstantMethod | |
1082 (mClassFile.getClassName(), methodName, ret, params); | |
1083 | |
1084 int stackAdjust = returnSize(ret) - 1; | |
1085 if (params != null) { | |
1086 stackAdjust -= argSize(params); | |
1087 } | |
1088 | |
1089 addCode(stackAdjust, Opcode.INVOKEVIRTUAL, info); | |
1090 } | |
1091 | |
1092 /** | |
1093 * Generates code to invoke a virtual method in any class. The object | |
1094 * reference and the method's argument(s) must be on the stack. | |
1095 * | |
1096 * @param ret May be null if method returns void. | |
1097 * @param params May be null if method takes no parameters. | |
1098 */ | |
1099 public void invokeVirtual(String className, | |
1100 String methodName, | |
1101 TypeDescriptor ret, | |
1102 TypeDescriptor[] params) { | |
1103 ConstantInfo info = | |
1104 mCp.addConstantMethod(className, methodName, ret, params); | |
1105 | |
1106 int stackAdjust = returnSize(ret) - 1; | |
1107 if (params != null) { | |
1108 stackAdjust -= argSize(params); | |
1109 } | |
1110 | |
1111 addCode(stackAdjust, Opcode.INVOKEVIRTUAL, info); | |
1112 } | |
1113 | |
1114 /** | |
1115 * Generates code to invoke a static method in this class. The method's | |
1116 * argument(s) must be on the stack. | |
1117 * | |
1118 * @param ret May be null if method returns void. | |
1119 * @param params May be null if method takes no parameters. | |
1120 */ | |
1121 public void invokeStatic(String methodName, | |
1122 TypeDescriptor ret, | |
1123 TypeDescriptor[] params) { | |
1124 ConstantInfo info = mCp.addConstantMethod | |
1125 (mClassFile.getClassName(), methodName, ret, params); | |
1126 | |
1127 int stackAdjust = returnSize(ret) - 0; | |
1128 if (params != null) { | |
1129 stackAdjust -= argSize(params); | |
1130 } | |
1131 | |
1132 addCode(stackAdjust, Opcode.INVOKESTATIC, info); | |
1133 } | |
1134 | |
1135 /** | |
1136 * Generates code to invoke a static method in any class. The method's | |
1137 * argument(s) must be on the stack. | |
1138 * | |
1139 * @param ret May be null if method returns void. | |
1140 * @param params May be null if method takes no parameters. | |
1141 */ | |
1142 public void invokeStatic(String className, | |
1143 String methodName, | |
1144 TypeDescriptor ret, | |
1145 TypeDescriptor[] params) { | |
1146 ConstantInfo info = | |
1147 mCp.addConstantMethod(className, methodName, ret, params); | |
1148 | |
1149 int stackAdjust = returnSize(ret) - 0; | |
1150 if (params != null) { | |
1151 stackAdjust -= argSize(params); | |
1152 } | |
1153 | |
1154 addCode(stackAdjust, Opcode.INVOKESTATIC, info); | |
1155 } | |
1156 | |
1157 /** | |
1158 * Generates code to invoke an interface method in any class. The object | |
1159 * reference and the method's argument(s) must be on the stack. | |
1160 * | |
1161 * @param ret May be null if method returns void. | |
1162 * @param params May be null if method takes no parameters. | |
1163 */ | |
1164 public void invokeInterface(String className, | |
1165 String methodName, | |
1166 TypeDescriptor ret, | |
1167 TypeDescriptor[] params) { | |
1168 | |
1169 ConstantInfo info = | |
1170 mCp.addConstantInterfaceMethod(className, methodName, ret, params); | |
1171 | |
1172 int paramCount = 1; | |
1173 if (params != null) { | |
1174 paramCount += argSize(params); | |
1175 } | |
1176 | |
1177 int stackAdjust = returnSize(ret) - paramCount; | |
1178 | |
1179 byte[] bytes = new byte[5]; | |
1180 | |
1181 bytes[0] = Opcode.INVOKEINTERFACE; | |
1182 bytes[1] = (byte)0; | |
1183 bytes[2] = (byte)0; | |
1184 bytes[3] = (byte)paramCount; | |
1185 bytes[4] = (byte)0; | |
1186 | |
1187 mInstructions.new ConstantOperandInstruction(stackAdjust, bytes, info); | |
1188 } | |
1189 | |
1190 /** | |
1191 * Generates code to invoke a private method in this class. | |
1192 * The object reference and the method's argument(s) must be on the stack. | |
1193 * | |
1194 * @param ret May be null if method returns void. | |
1195 * @param params May be null if method takes no parameters. | |
1196 */ | |
1197 public void invokePrivate(String methodName, | |
1198 TypeDescriptor ret, | |
1199 TypeDescriptor[] params) { | |
1200 ConstantInfo info = mCp.addConstantMethod | |
1201 (mClassFile.getClassName(), methodName, ret, params); | |
1202 | |
1203 int stackAdjust = returnSize(ret) - 1; | |
1204 if (params != null) { | |
1205 stackAdjust -= argSize(params); | |
1206 } | |
1207 | |
1208 addCode(stackAdjust, Opcode.INVOKESPECIAL, info); | |
1209 } | |
1210 | |
1211 /** | |
1212 * Generates code to invoke a method in the super class. | |
1213 * The object reference and the method's argument(s) must be on the stack. | |
1214 * | |
1215 * @param ret May be null if method returns void. | |
1216 * @param params May be null if method takes no parameters. | |
1217 */ | |
1218 public void invokeSuper(String superClassName, | |
1219 String methodName, | |
1220 TypeDescriptor ret, | |
1221 TypeDescriptor[] params) { | |
1222 ConstantInfo info = | |
1223 mCp.addConstantMethod(superClassName, methodName, ret, params); | |
1224 | |
1225 int stackAdjust = returnSize(ret) - 1; | |
1226 if (params != null) { | |
1227 stackAdjust -= argSize(params); | |
1228 } | |
1229 | |
1230 addCode(stackAdjust, Opcode.INVOKESPECIAL, info); | |
1231 } | |
1232 | |
1233 /** | |
1234 * Generates code to invoke a method in the super class. | |
1235 * The object reference and the method's argument(s) must be on the stack. | |
1236 */ | |
1237 public void invokeSuper(Method method) { | |
1238 TypeDescriptor ret = new TypeDescriptor(method.getReturnType()); | |
1239 | |
1240 Class[] paramClasses = method.getParameterTypes(); | |
1241 TypeDescriptor[] params = new TypeDescriptor[paramClasses.length]; | |
1242 for (int i=0; i<params.length; i++) { | |
1243 params[i] = new TypeDescriptor(paramClasses[i]); | |
1244 } | |
1245 | |
1246 invokeSuper(method.getDeclaringClass().getName(), | |
1247 method.getName(), | |
1248 ret, | |
1249 params); | |
1250 } | |
1251 | |
1252 /** | |
1253 * Generates code to invoke a class constructor in this class. The object | |
1254 * reference and the constructor's argument(s) must be on the stack. | |
1255 * | |
1256 * @param params May be null if constructor takes no parameters. | |
1257 */ | |
1258 public void invokeConstructor(TypeDescriptor[] params) { | |
1259 ConstantInfo info = | |
1260 mCp.addConstantConstructor(mClassFile.getClassName(), params); | |
1261 | |
1262 int stackAdjust = -1; | |
1263 if (params != null) { | |
1264 stackAdjust -= argSize(params); | |
1265 } | |
1266 | |
1267 addCode(stackAdjust, Opcode.INVOKESPECIAL, info); | |
1268 } | |
1269 | |
1270 /** | |
1271 * Generates code to invoke a class constructor in any class. The object | |
1272 * reference and the constructor's argument(s) must be on the stack. | |
1273 * | |
1274 * @param params May be null if constructor takes no parameters. | |
1275 */ | |
1276 public void invokeConstructor(String className, TypeDescriptor[] params) { | |
1277 ConstantInfo info = mCp.addConstantConstructor(className, params); | |
1278 | |
1279 int stackAdjust = -1; | |
1280 if (params != null) { | |
1281 stackAdjust -= argSize(params); | |
1282 } | |
1283 | |
1284 addCode(stackAdjust, Opcode.INVOKESPECIAL, info); | |
1285 } | |
1286 | |
1287 /** | |
1288 * Generates code to invoke a super class constructor. The object | |
1289 * reference and the constructor's argument(s) must be on the stack. | |
1290 * | |
1291 * @param params May be null if constructor takes no parameters. | |
1292 */ | |
1293 public void invokeSuperConstructor(TypeDescriptor[] params) { | |
1294 invokeConstructor(mClassFile.getSuperClassName(), params); | |
1295 } | |
1296 | |
1297 /** | |
1298 * Generates code to invoke a super class constructor. The object | |
1299 * reference and the constructor's argument(s) must be on the stack. | |
1300 */ | |
1301 public void invokeSuper(Constructor constructor) { | |
1302 Class[] paramClasses = constructor.getParameterTypes(); | |
1303 TypeDescriptor[] params = new TypeDescriptor[paramClasses.length]; | |
1304 for (int i=0; i<params.length; i++) { | |
1305 params[i] = new TypeDescriptor(paramClasses[i]); | |
1306 } | |
1307 | |
1308 invokeSuperConstructor(params); | |
1309 } | |
1310 | |
1311 private int returnSize(TypeDescriptor ret) { | |
1312 if (ret == null) return 0; | |
1313 | |
1314 String className = ret.getClassName(); | |
1315 | |
1316 if (className.equals(void.class.getName())) { | |
1317 return 0; | |
1318 } | |
1319 else if (className.equals(long.class.getName()) || | |
1320 className.equals(double.class.getName())) { | |
1321 return 2; | |
1322 } | |
1323 else { | |
1324 return 1; | |
1325 } | |
1326 } | |
1327 | |
1328 private int argSize(TypeDescriptor[] params) { | |
1329 int size = 0; | |
1330 if (params != null) { | |
1331 for (int i=0; i<params.length; i++) { | |
1332 String className = params[i].getClassName(); | |
1333 if (params[i].getDimensions() == 0 && | |
1334 (className.equals(long.class.getName()) || | |
1335 className.equals(double.class.getName()))) { | |
1336 size += 2; | |
1337 } | |
1338 else { | |
1339 size++; | |
1340 } | |
1341 } | |
1342 } | |
1343 | |
1344 return size; | |
1345 } | |
1346 | |
1347 // creation style instructions | |
1348 | |
1349 /** | |
1350 * Generates code to create a new object. Unless the new object is an | |
1351 * array, it is invalid until a constructor method is invoked on it. | |
1352 * When creating arrays, the size for each dimension must be on the | |
1353 * operand stack. | |
1354 * | |
1355 * @see #invokeConstructor | |
1356 */ | |
1357 public void newObject(TypeDescriptor type) { | |
1358 int dim = type.getSpecifiedDimensions(); | |
1359 | |
1360 if (dim == 0) { | |
1361 ConstantInfo info = mCp.addConstantClass(type); | |
1362 addCode(1, Opcode.NEW, info); | |
1363 return; | |
1364 } | |
1365 | |
1366 TypeDescriptor componentType = type.getComponentType(); | |
1367 | |
1368 if (dim == 1) { | |
1369 if (componentType.getDimensions() == 0) { | |
1370 Class clazz = componentType.getClassArg(); | |
1371 | |
1372 if (clazz != null && clazz.isPrimitive()) { | |
1373 byte atype = (byte)0; | |
1374 | |
1375 if (clazz == int.class) { | |
1376 atype = (byte)10; | |
1377 } | |
1378 else if (clazz == byte.class) { | |
1379 atype = (byte)8; | |
1380 } | |
1381 else if (clazz == boolean.class) { | |
1382 atype = (byte)4; | |
1383 } | |
1384 else if (clazz == char.class) { | |
1385 atype = (byte)5; | |
1386 } | |
1387 else if (clazz == float.class) { | |
1388 atype = (byte)6; | |
1389 } | |
1390 else if (clazz == double.class) { | |
1391 atype = (byte)7; | |
1392 } | |
1393 else if (clazz == short.class) { | |
1394 atype = (byte)9; | |
1395 } | |
1396 else if (clazz == long.class) { | |
1397 atype = (byte)11; | |
1398 } | |
1399 | |
1400 addCode(0, Opcode.NEWARRAY, atype); | |
1401 return; | |
1402 } | |
1403 } | |
1404 | |
1405 ConstantInfo info = mCp.addConstantClass(componentType); | |
1406 addCode(0, Opcode.ANEWARRAY, info); | |
1407 return; | |
1408 } | |
1409 | |
1410 // multidimensional | |
1411 int stackAdjust = -(dim - 1); | |
1412 | |
1413 ConstantInfo info = mCp.addConstantClass(componentType); | |
1414 | |
1415 byte[] bytes = new byte[4]; | |
1416 | |
1417 bytes[0] = Opcode.MULTIANEWARRAY; | |
1418 bytes[1] = (byte)0; | |
1419 bytes[2] = (byte)0; | |
1420 bytes[3] = (byte)dim; | |
1421 | |
1422 mInstructions.new ConstantOperandInstruction(stackAdjust, bytes, info); | |
1423 } | |
1424 | |
1425 // stack operation style instructions | |
1426 | |
1427 /** | |
1428 * Generates code for the dup instruction. | |
1429 */ | |
1430 public void dup() { | |
1431 addCode(1, Opcode.DUP); | |
1432 } | |
1433 | |
1434 /** | |
1435 * Generates code for the dup_x1 instruction. | |
1436 */ | |
1437 public void dupX1() { | |
1438 addCode(1, Opcode.DUP_X1); | |
1439 } | |
1440 | |
1441 /** | |
1442 * Generates code for the dup_x2 instruction. | |
1443 */ | |
1444 public void dupX2() { | |
1445 addCode(1, Opcode.DUP_X2); | |
1446 } | |
1447 | |
1448 /** | |
1449 * Generates code for the dup2 instruction. | |
1450 */ | |
1451 public void dup2() { | |
1452 addCode(2, Opcode.DUP2); | |
1453 } | |
1454 | |
1455 /** | |
1456 * Generates code for the dup2_x1 instruction. | |
1457 */ | |
1458 public void dup2X1() { | |
1459 addCode(2, Opcode.DUP2_X1); | |
1460 } | |
1461 | |
1462 /** | |
1463 * Generates code for the dup2_x2 instruction. | |
1464 */ | |
1465 public void dup2X2() { | |
1466 addCode(2, Opcode.DUP2_X2); | |
1467 } | |
1468 | |
1469 /** | |
1470 * Generates code for the pop instruction. | |
1471 */ | |
1472 public void pop() { | |
1473 addCode(-1, Opcode.POP); | |
1474 } | |
1475 | |
1476 /** | |
1477 * Generates code for the pop2 instruction. | |
1478 */ | |
1479 public void pop2() { | |
1480 addCode(-2, Opcode.POP2); | |
1481 } | |
1482 | |
1483 /** | |
1484 * Generates code for the swap instruction. | |
1485 */ | |
1486 public void swap() { | |
1487 addCode(0, Opcode.SWAP); | |
1488 } | |
1489 | |
1490 /** | |
1491 * Generates code for a swap2 instruction. | |
1492 */ | |
1493 public void swap2() { | |
1494 dup2X2(); | |
1495 pop2(); | |
1496 } | |
1497 | |
1498 // flow control instructions | |
1499 | |
1500 private void branch(int stackAdjust, Location location, byte opcode) { | |
1501 mInstructions.new BranchInstruction(stackAdjust, opcode, location); | |
1502 } | |
1503 | |
1504 /** | |
1505 * Generates code that performs an unconditional branch to the specified | |
1506 * location or label. | |
1507 * | |
1508 * @param location The location or label to branch to | |
1509 */ | |
1510 public void branch(Location location) { | |
1511 branch(0, location, Opcode.GOTO); | |
1512 } | |
1513 | |
1514 /** | |
1515 * Generates code that performs a conditional branch based on the | |
1516 * value of an object on the stack. A branch is performed based on whether | |
1517 * the object reference on the stack is null or not. | |
1518 * | |
1519 * @param location The location or label to branch to | |
1520 * @param choice If true, do branch when null, else branch when not null | |
1521 */ | |
1522 public void ifNullBranch(Location location, boolean choice) { | |
1523 branch(-1, location, choice ? Opcode.IFNULL : Opcode.IFNONNULL); | |
1524 } | |
1525 | |
1526 | |
1527 /** | |
1528 * Generates code that performs a conditional branch based on the value of | |
1529 * two object references on the stack. A branch is performed based on | |
1530 * whether the two objects are equal. | |
1531 * | |
1532 * @param location The location or label to branch to | |
1533 * @param choice If true, branch when equal, else branch when not equal | |
1534 */ | |
1535 public void ifEqualBranch(Location location, boolean choice) { | |
1536 branch(-2, location, choice ? Opcode.IF_ACMPEQ : Opcode.IF_ACMPNE); | |
1537 } | |
1538 | |
1539 /** | |
1540 * Generates code the performs a conditional branch based on a comparison | |
1541 * between an int value on the stack and zero. The int value on the | |
1542 * stack is on the left side of the comparison expression. | |
1543 * | |
1544 * @param location The location or label to branch to | |
1545 * @param choice One of "==", "!=", "<", ">=", ">" or "<=" | |
1546 * @exception IllegalArgumentException When the choice is not valid | |
1547 */ | |
1548 public void ifZeroComparisonBranch(Location location, String choice) | |
1549 throws IllegalArgumentException { | |
1550 | |
1551 choice = choice.intern(); | |
1552 | |
1553 byte opcode; | |
1554 if (choice == "==") { | |
1555 opcode = Opcode.IFEQ; | |
1556 } | |
1557 else if (choice == "!=") { | |
1558 opcode = Opcode.IFNE; | |
1559 } | |
1560 else if (choice == "<") { | |
1561 opcode = Opcode.IFLT; | |
1562 } | |
1563 else if (choice == ">=") { | |
1564 opcode = Opcode.IFGE; | |
1565 } | |
1566 else if (choice == ">") { | |
1567 opcode = Opcode.IFGT; | |
1568 } | |
1569 else if (choice == "<=") { | |
1570 opcode = Opcode.IFLE; | |
1571 } | |
1572 else { | |
1573 throw new IllegalArgumentException | |
1574 ("Invalid comparision choice: " + choice); | |
1575 } | |
1576 | |
1577 branch(-1, location, opcode); | |
1578 } | |
1579 | |
1580 /** | |
1581 * Generates code the performs a conditional branch based on a comparison | |
1582 * between two int values on the stack. The first int value on the stack | |
1583 * is on the left side of the comparison expression. | |
1584 * | |
1585 * @param location The location or label to branch to | |
1586 * @param choice One of "==", "!=", "<", ">=", ">" or "<=" | |
1587 * @exception IllegalArgumentException When the choice is not valid | |
1588 */ | |
1589 public void ifComparisonBranch(Location location, String choice) | |
1590 throws IllegalArgumentException { | |
1591 | |
1592 choice = choice.intern(); | |
1593 | |
1594 byte opcode; | |
1595 if (choice == "==") { | |
1596 opcode = Opcode.IF_ICMPEQ; | |
1597 } | |
1598 else if (choice == "!=") { | |
1599 opcode = Opcode.IF_ICMPNE; | |
1600 } | |
1601 else if (choice == "<") { | |
1602 opcode = Opcode.IF_ICMPLT; | |
1603 } | |
1604 else if (choice == ">=") { | |
1605 opcode = Opcode.IF_ICMPGE; | |
1606 } | |
1607 else if (choice == ">") { | |
1608 opcode = Opcode.IF_ICMPGT; | |
1609 } | |
1610 else if (choice == "<=") { | |
1611 opcode = Opcode.IF_ICMPLE; | |
1612 } | |
1613 else { | |
1614 throw new IllegalArgumentException | |
1615 ("Invalid comparision choice: " + choice); | |
1616 } | |
1617 | |
1618 branch(-2, location, opcode); | |
1619 } | |
1620 | |
1621 /** | |
1622 * Generates code for a switch statement. The generated code is either a | |
1623 * lookupswitch or tableswitch. The choice of which switch type to generate | |
1624 * is made based on the amount of bytes to be generated. A tableswitch | |
1625 * is usually smaller, unless the cases are sparse. | |
1626 * | |
1627 * <p>The key value to switch on must already be on the stack when this | |
1628 * instruction executes. | |
1629 * | |
1630 * @param cases The values to match on. The array length must be the same | |
1631 * as for locations. | |
1632 * @param locations The locations or labels to branch to for each case. | |
1633 * The array length must be the same as for cases. | |
1634 * @param defaultLocation The location or label to branch to if the key on | |
1635 * the stack was not matched. | |
1636 */ | |
1637 public void switchBranch(int[] cases, | |
1638 Location[] locations, Location defaultLocation) { | |
1639 | |
1640 mInstructions.new SwitchInstruction(cases, locations, defaultLocation); | |
1641 } | |
1642 | |
1643 /** | |
1644 * Generates code that performs a subroutine branch to the specified | |
1645 * location. The instruction generated is either jsr or jsr_w. It is most | |
1646 * often used for implementing a finally block. | |
1647 * | |
1648 * @param location The location or label to branch to | |
1649 */ | |
1650 public void jsr(Location location) { | |
1651 // Adjust the stack by one to make room for the return address. | |
1652 branch(1, location, Opcode.JSR); | |
1653 } | |
1654 | |
1655 /** | |
1656 * Generates code that returns from a subroutine invoked by jsr. | |
1657 * | |
1658 * @param local The local variable reference that contains the return | |
1659 * address. The local variable must be of an object type. | |
1660 */ | |
1661 public void ret(LocalVariable local) { | |
1662 if (local == null) { | |
1663 throw new NullPointerException("No local variable specified"); | |
1664 } | |
1665 | |
1666 mInstructions.new RetInstruction(local); | |
1667 } | |
1668 | |
1669 // math instructions | |
1670 | |
1671 /** | |
1672 * Generates code for either a unary or binary math operation on one | |
1673 * or two values pushed on the stack. | |
1674 * | |
1675 * <p>Pass in an opcode from the the Opcode class. The only valid math | |
1676 * opcodes are: | |
1677 * | |
1678 * <pre> | |
1679 * IADD, ISUB, IMUL, IDIV, IREM, INEG, IAND, IOR, IXOR, ISHL, ISHR, IUSHR | |
1680 * LADD, LSUB, LMUL, LDIV, LREM, LNEG, LAND, LOR, LXOR, LSHL, LSHR, LUSHR | |
1681 * FADD, FSUB, FMUL, FDIV, FREM, FNEG | |
1682 * DADD, DSUB, DMUL, DDIV, DREM, DNEG | |
1683 * | |
1684 * LCMP | |
1685 * FCMPG, FCMPL | |
1686 * DCMPG, DCMPL | |
1687 * </pre> | |
1688 * | |
1689 * A not operation (~) is performed by doing a loadConstant with either | |
1690 * -1 or -1L followed by math(Opcode.IXOR) or math(Opcode.LXOR). | |
1691 * | |
1692 * @param opcode An opcode from the Opcode class. | |
1693 * @exception IllegalArgumentException When the opcode selected is not | |
1694 * a math operation. | |
1695 * @see Opcode | |
1696 */ | |
1697 public void math(byte opcode) { | |
1698 int stackAdjust; | |
1699 | |
1700 switch(opcode) { | |
1701 case Opcode.INEG: | |
1702 case Opcode.LNEG: | |
1703 case Opcode.FNEG: | |
1704 case Opcode.DNEG: | |
1705 stackAdjust = 0; | |
1706 break; | |
1707 case Opcode.IADD: | |
1708 case Opcode.ISUB: | |
1709 case Opcode.IMUL: | |
1710 case Opcode.IDIV: | |
1711 case Opcode.IREM: | |
1712 case Opcode.IAND: | |
1713 case Opcode.IOR: | |
1714 case Opcode.IXOR: | |
1715 case Opcode.ISHL: | |
1716 case Opcode.ISHR: | |
1717 case Opcode.IUSHR: | |
1718 case Opcode.FADD: | |
1719 case Opcode.FSUB: | |
1720 case Opcode.FMUL: | |
1721 case Opcode.FDIV: | |
1722 case Opcode.FREM: | |
1723 case Opcode.FCMPG: | |
1724 case Opcode.FCMPL: | |
1725 stackAdjust = -1; | |
1726 break; | |
1727 case Opcode.LADD: | |
1728 case Opcode.LSUB: | |
1729 case Opcode.LMUL: | |
1730 case Opcode.LDIV: | |
1731 case Opcode.LREM: | |
1732 case Opcode.LAND: | |
1733 case Opcode.LOR: | |
1734 case Opcode.LXOR: | |
1735 case Opcode.LSHL: | |
1736 case Opcode.LSHR: | |
1737 case Opcode.LUSHR: | |
1738 case Opcode.DADD: | |
1739 case Opcode.DSUB: | |
1740 case Opcode.DMUL: | |
1741 case Opcode.DDIV: | |
1742 case Opcode.DREM: | |
1743 stackAdjust = -2; | |
1744 break; | |
1745 case Opcode.LCMP: | |
1746 case Opcode.DCMPG: | |
1747 case Opcode.DCMPL: | |
1748 stackAdjust = -3; | |
1749 break; | |
1750 default: | |
1751 throw new IllegalArgumentException | |
1752 ("Not a math opcode: " + Opcode.getMnemonic(opcode)); | |
1753 } | |
1754 | |
1755 addCode(stackAdjust, opcode); | |
1756 } | |
1757 | |
1758 // miscellaneous instructions | |
1759 | |
1760 /** | |
1761 * Generates code for an arraylength instruction. The object to get the | |
1762 * length from must already be on the stack. | |
1763 */ | |
1764 public void arrayLength() { | |
1765 addCode(0, Opcode.ARRAYLENGTH); | |
1766 } | |
1767 | |
1768 /** | |
1769 * Generates code that throws an exception. The object to throw must | |
1770 * already be on the stack. | |
1771 */ | |
1772 public void throwObject() { | |
1773 addCode(-1, Opcode.ATHROW); | |
1774 } | |
1775 | |
1776 /** | |
1777 * Generates code that performs an object cast operation. The object | |
1778 * to check must already be on the stack. | |
1779 */ | |
1780 public void checkCast(TypeDescriptor type) { | |
1781 ConstantInfo info = mCp.addConstantClass(type); | |
1782 addCode(0, Opcode.CHECKCAST, info); | |
1783 } | |
1784 | |
1785 /** | |
1786 * Generates code that performs an instanceof operation. The object to | |
1787 * check must already be on the stack. | |
1788 */ | |
1789 public void instanceOf(TypeDescriptor type) { | |
1790 ConstantInfo info = mCp.addConstantClass(type); | |
1791 addCode(0, Opcode.INSTANCEOF, info); | |
1792 } | |
1793 | |
1794 /** | |
1795 * Generates code that increments a local integer variable by a signed | |
1796 * constant amount. | |
1797 */ | |
1798 public void integerIncrement(LocalVariable local, int amount) { | |
1799 if (local == null) { | |
1800 throw new NullPointerException("No local variable specified"); | |
1801 } | |
1802 | |
1803 if (-32768 <= amount && amount <= 32767) { | |
1804 mInstructions.new ShortIncrementInstruction(local, (short)amount); | |
1805 } | |
1806 else { | |
1807 // Amount can't possibly fit in a 16-bit value, so use regular | |
1808 // instructions instead. | |
1809 | |
1810 loadLocal(local); | |
1811 loadConstant(amount); | |
1812 math(Opcode.IADD); | |
1813 storeLocal(local); | |
1814 } | |
1815 } | |
1816 | |
1817 /** | |
1818 * Generates code to enter the monitor on an object loaded on the stack. | |
1819 */ | |
1820 public void monitorEnter() { | |
1821 addCode(-1, Opcode.MONITORENTER); | |
1822 } | |
1823 | |
1824 /** | |
1825 * Generates code to exit the monitor on an object loaded on the stack. | |
1826 */ | |
1827 public void monitorExit() { | |
1828 addCode(-1, Opcode.MONITOREXIT); | |
1829 } | |
1830 | |
1831 /** | |
1832 * Generates an instruction that does nothing. (No-OPeration) | |
1833 */ | |
1834 public void nop() { | |
1835 addCode(0, Opcode.NOP); | |
1836 } | |
1837 | |
1838 /** | |
1839 * Generates a breakpoint instruction for use in a debugging environment. | |
1840 */ | |
1841 public void breakpoint() { | |
1842 addCode(0, Opcode.BREAKPOINT); | |
1843 } | |
1844 } |