Mercurial > lasercutter
view src/clojure/asm/commons/GeneratorAdapter.java @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
line wrap: on
line source
1 /***2 * ASM: a very small and fast Java bytecode manipulation framework3 * Copyright (c) 2000-2005 INRIA, France Telecom4 * All rights reserved.5 *6 * Redistribution and use in source and binary forms, with or without7 * modification, are permitted provided that the following conditions8 * are met:9 * 1. Redistributions of source code must retain the above copyright10 * notice, this list of conditions and the following disclaimer.11 * 2. Redistributions in binary form must reproduce the above copyright12 * notice, this list of conditions and the following disclaimer in the13 * documentation and/or other materials provided with the distribution.14 * 3. Neither the name of the copyright holders nor the names of its15 * contributors may be used to endorse or promote products derived from16 * this software without specific prior written permission.17 *18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF28 * THE POSSIBILITY OF SUCH DAMAGE.29 */30 package clojure.asm.commons;32 import java.util.ArrayList;33 import java.util.Arrays;34 import java.util.List;36 import clojure.asm.ClassVisitor;37 import clojure.asm.Label;38 import clojure.asm.MethodVisitor;39 import clojure.asm.Opcodes;40 import clojure.asm.Type;42 /**43 * A {@link clojure.asm.MethodAdapter} with convenient methods to generate44 * code. For example, using this adapter, the class below45 * <p/>46 * <pre>47 * public class Example {48 * public static void main(String[] args) {49 * System.out.println("Hello world!");50 * }51 * }52 * </pre>53 * <p/>54 * can be generated as follows:55 * <p/>56 * <pre>57 * ClassWriter cw = new ClassWriter(true);58 * cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null);59 * <p/>60 * Method m = Method.getMethod("void <init> ()");61 * GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw);62 * mg.loadThis();63 * mg.invokeConstructor(Type.getType(Object.class), m);64 * mg.returnValue();65 * mg.endMethod();66 * <p/>67 * m = Method.getMethod("void main (String[])");68 * mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw);69 * mg.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class));70 * mg.push("Hello world!");71 * mg.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod("void println (String)"));72 * mg.returnValue();73 * mg.endMethod();74 * <p/>75 * cw.visitEnd();76 * </pre>77 *78 * @author Juozas Baliuka79 * @author Chris Nokleberg80 * @author Eric Bruneton81 */82 public class GeneratorAdapter extends LocalVariablesSorter{84 private final static Type BYTE_TYPE = Type.getObjectType("java/lang/Byte");86 private final static Type BOOLEAN_TYPE = Type.getObjectType("java/lang/Boolean");88 private final static Type SHORT_TYPE = Type.getObjectType("java/lang/Short");90 private final static Type CHARACTER_TYPE = Type.getObjectType("java/lang/Character");92 private final static Type INTEGER_TYPE = Type.getObjectType("java/lang/Integer");94 private final static Type FLOAT_TYPE = Type.getObjectType("java/lang/Float");96 private final static Type LONG_TYPE = Type.getObjectType("java/lang/Long");98 private final static Type DOUBLE_TYPE = Type.getObjectType("java/lang/Double");100 private final static Type NUMBER_TYPE = Type.getObjectType("java/lang/Number");102 private final static Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");104 private final static Method BOOLEAN_VALUE = Method.getMethod("boolean booleanValue()");106 private final static Method CHAR_VALUE = Method.getMethod("char charValue()");108 private final static Method INT_VALUE = Method.getMethod("int intValue()");110 private final static Method FLOAT_VALUE = Method.getMethod("float floatValue()");112 private final static Method LONG_VALUE = Method.getMethod("long longValue()");114 private final static Method DOUBLE_VALUE = Method.getMethod("double doubleValue()");116 /**117 * Constant for the {@link #math math} method.118 */119 public final static int ADD = Opcodes.IADD;121 /**122 * Constant for the {@link #math math} method.123 */124 public final static int SUB = Opcodes.ISUB;126 /**127 * Constant for the {@link #math math} method.128 */129 public final static int MUL = Opcodes.IMUL;131 /**132 * Constant for the {@link #math math} method.133 */134 public final static int DIV = Opcodes.IDIV;136 /**137 * Constant for the {@link #math math} method.138 */139 public final static int REM = Opcodes.IREM;141 /**142 * Constant for the {@link #math math} method.143 */144 public final static int NEG = Opcodes.INEG;146 /**147 * Constant for the {@link #math math} method.148 */149 public final static int SHL = Opcodes.ISHL;151 /**152 * Constant for the {@link #math math} method.153 */154 public final static int SHR = Opcodes.ISHR;156 /**157 * Constant for the {@link #math math} method.158 */159 public final static int USHR = Opcodes.IUSHR;161 /**162 * Constant for the {@link #math math} method.163 */164 public final static int AND = Opcodes.IAND;166 /**167 * Constant for the {@link #math math} method.168 */169 public final static int OR = Opcodes.IOR;171 /**172 * Constant for the {@link #math math} method.173 */174 public final static int XOR = Opcodes.IXOR;176 /**177 * Constant for the {@link #ifCmp ifCmp} method.178 */179 public final static int EQ = Opcodes.IFEQ;181 /**182 * Constant for the {@link #ifCmp ifCmp} method.183 */184 public final static int NE = Opcodes.IFNE;186 /**187 * Constant for the {@link #ifCmp ifCmp} method.188 */189 public final static int LT = Opcodes.IFLT;191 /**192 * Constant for the {@link #ifCmp ifCmp} method.193 */194 public final static int GE = Opcodes.IFGE;196 /**197 * Constant for the {@link #ifCmp ifCmp} method.198 */199 public final static int GT = Opcodes.IFGT;201 /**202 * Constant for the {@link #ifCmp ifCmp} method.203 */204 public final static int LE = Opcodes.IFLE;206 /**207 * Access flags of the method visited by this adapter.208 */209 private final int access;211 /**212 * Return type of the method visited by this adapter.213 */214 private final Type returnType;216 /**217 * Argument types of the method visited by this adapter.218 */219 private final Type[] argumentTypes;221 /**222 * Types of the local variables of the method visited by this adapter.223 */224 private final List localTypes = new ArrayList();226 /**227 * Creates a new {@link GeneratorAdapter}.228 *229 * @param mv the method visitor to which this adapter delegates calls.230 * @param access the method's access flags (see {@link Opcodes}).231 * @param name the method's name.232 * @param desc the method's descriptor (see {@link Type Type}).233 */234 public GeneratorAdapter(235 final MethodVisitor mv,236 final int access,237 final String name,238 final String desc){239 super(access, desc, mv);240 this.access = access;241 this.returnType = Type.getReturnType(desc);242 this.argumentTypes = Type.getArgumentTypes(desc);243 }245 /**246 * Creates a new {@link GeneratorAdapter}.247 *248 * @param access access flags of the adapted method.249 * @param method the adapted method.250 * @param mv the method visitor to which this adapter delegates calls.251 */252 public GeneratorAdapter(253 final int access,254 final Method method,255 final MethodVisitor mv){256 super(access, method.getDescriptor(), mv);257 this.access = access;258 this.returnType = method.getReturnType();259 this.argumentTypes = method.getArgumentTypes();260 }262 /**263 * Creates a new {@link GeneratorAdapter}.264 *265 * @param access access flags of the adapted method.266 * @param method the adapted method.267 * @param signature the signature of the adapted method (may be268 * <tt>null</tt>).269 * @param exceptions the exceptions thrown by the adapted method (may be270 * <tt>null</tt>).271 * @param cv the class visitor to which this adapter delegates calls.272 */273 public GeneratorAdapter(274 final int access,275 final Method method,276 final String signature,277 final Type[] exceptions,278 final ClassVisitor cv){279 this(access, method, cv.visitMethod(access,280 method.getName(),281 method.getDescriptor(),282 signature,283 getInternalNames(exceptions)));284 }286 /**287 * Returns the internal names of the given types.288 *289 * @param types a set of types.290 * @return the internal names of the given types.291 */292 private static String[] getInternalNames(final Type[] types){293 if(types == null)294 {295 return null;296 }297 String[] names = new String[types.length];298 for(int i = 0; i < names.length; ++i)299 {300 names[i] = types[i].getInternalName();301 }302 return names;303 }305 // ------------------------------------------------------------------------306 // Instructions to push constants on the stack307 // ------------------------------------------------------------------------309 /**310 * Generates the instruction to push the given value on the stack.311 *312 * @param value the value to be pushed on the stack.313 */314 public void push(final boolean value){315 push(value ? 1 : 0);316 }318 /**319 * Generates the instruction to push the given value on the stack.320 *321 * @param value the value to be pushed on the stack.322 */323 public void push(final int value){324 if(value >= -1 && value <= 5)325 {326 mv.visitInsn(Opcodes.ICONST_0 + value);327 }328 else if(value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE)329 {330 mv.visitIntInsn(Opcodes.BIPUSH, value);331 }332 else if(value >= Short.MIN_VALUE && value <= Short.MAX_VALUE)333 {334 mv.visitIntInsn(Opcodes.SIPUSH, value);335 }336 else337 {338 mv.visitLdcInsn(new Integer(value));339 }340 }342 /**343 * Generates the instruction to push the given value on the stack.344 *345 * @param value the value to be pushed on the stack.346 */347 public void push(final long value){348 if(value == 0L || value == 1L)349 {350 mv.visitInsn(Opcodes.LCONST_0 + (int) value);351 }352 else353 {354 mv.visitLdcInsn(new Long(value));355 }356 }358 /**359 * Generates the instruction to push the given value on the stack.360 *361 * @param value the value to be pushed on the stack.362 */363 public void push(final float value){364 int bits = Float.floatToIntBits(value);365 if(bits == 0L || bits == 0x3f800000 || bits == 0x40000000)366 { // 0..2367 mv.visitInsn(Opcodes.FCONST_0 + (int) value);368 }369 else370 {371 mv.visitLdcInsn(new Float(value));372 }373 }375 /**376 * Generates the instruction to push the given value on the stack.377 *378 * @param value the value to be pushed on the stack.379 */380 public void push(final double value){381 long bits = Double.doubleToLongBits(value);382 if(bits == 0L || bits == 0x3ff0000000000000L)383 { // +0.0d and 1.0d384 mv.visitInsn(Opcodes.DCONST_0 + (int) value);385 }386 else387 {388 mv.visitLdcInsn(new Double(value));389 }390 }392 /**393 * Generates the instruction to push the given value on the stack.394 *395 * @param value the value to be pushed on the stack. May be <tt>null</tt>.396 */397 public void push(final String value){398 if(value == null)399 {400 mv.visitInsn(Opcodes.ACONST_NULL);401 }402 else403 {404 mv.visitLdcInsn(value);405 }406 }408 /**409 * Generates the instruction to push the given value on the stack.410 *411 * @param value the value to be pushed on the stack.412 */413 public void push(final Type value){414 if(value == null)415 {416 mv.visitInsn(Opcodes.ACONST_NULL);417 }418 else419 {420 mv.visitLdcInsn(value);421 }422 }424 // ------------------------------------------------------------------------425 // Instructions to load and store method arguments426 // ------------------------------------------------------------------------428 /**429 * Returns the index of the given method argument in the frame's local430 * variables array.431 *432 * @param arg the index of a method argument.433 * @return the index of the given method argument in the frame's local434 * variables array.435 */436 private int getArgIndex(final int arg){437 int index = (access & Opcodes.ACC_STATIC) == 0 ? 1 : 0;438 for(int i = 0; i < arg; i++)439 {440 index += argumentTypes[i].getSize();441 }442 return index;443 }445 /**446 * Generates the instruction to push a local variable on the stack.447 *448 * @param type the type of the local variable to be loaded.449 * @param index an index in the frame's local variables array.450 */451 private void loadInsn(final Type type, final int index){452 mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), index);453 }455 /**456 * Generates the instruction to store the top stack value in a local457 * variable.458 *459 * @param type the type of the local variable to be stored.460 * @param index an index in the frame's local variables array.461 */462 private void storeInsn(final Type type, final int index){463 mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), index);464 }466 /**467 * Generates the instruction to load 'this' on the stack.468 */469 public void loadThis(){470 if((access & Opcodes.ACC_STATIC) != 0)471 {472 throw new IllegalStateException("no 'this' pointer within static method");473 }474 mv.visitVarInsn(Opcodes.ALOAD, 0);475 }477 /**478 * Generates the instruction to load the given method argument on the stack.479 *480 * @param arg the index of a method argument.481 */482 public void loadArg(final int arg){483 loadInsn(argumentTypes[arg], getArgIndex(arg));484 }486 /**487 * Generates the instructions to load the given method arguments on the488 * stack.489 *490 * @param arg the index of the first method argument to be loaded.491 * @param count the number of method arguments to be loaded.492 */493 public void loadArgs(final int arg, final int count){494 int index = getArgIndex(arg);495 for(int i = 0; i < count; ++i)496 {497 Type t = argumentTypes[arg + i];498 loadInsn(t, index);499 index += t.getSize();500 }501 }503 /**504 * Generates the instructions to load all the method arguments on the stack.505 */506 public void loadArgs(){507 loadArgs(0, argumentTypes.length);508 }510 /**511 * Generates the instructions to load all the method arguments on the stack,512 * as a single object array.513 */514 public void loadArgArray(){515 push(argumentTypes.length);516 newArray(OBJECT_TYPE);517 for(int i = 0; i < argumentTypes.length; i++)518 {519 dup();520 push(i);521 loadArg(i);522 box(argumentTypes[i]);523 arrayStore(OBJECT_TYPE);524 }525 }527 /**528 * Generates the instruction to store the top stack value in the given529 * method argument.530 *531 * @param arg the index of a method argument.532 */533 public void storeArg(final int arg){534 storeInsn(argumentTypes[arg], getArgIndex(arg));535 }537 // ------------------------------------------------------------------------538 // Instructions to load and store local variables539 // ------------------------------------------------------------------------541 /**542 * Returns the type of the given local variable.543 *544 * @param local a local variable identifier, as returned by545 * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.546 * @return the type of the given local variable.547 */548 public Type getLocalType(final int local){549 return (Type) localTypes.get(local - firstLocal);550 }552 protected void setLocalType(final int local, final Type type){553 int index = local - firstLocal;554 while(localTypes.size() < index + 1)555 {556 localTypes.add(null);557 }558 localTypes.set(index, type);559 }561 /**562 * Generates the instruction to load the given local variable on the stack.563 *564 * @param local a local variable identifier, as returned by565 * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.566 */567 public void loadLocal(final int local){568 loadInsn(getLocalType(local), local);569 }571 /**572 * Generates the instruction to load the given local variable on the stack.573 *574 * @param local a local variable identifier, as returned by575 * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.576 * @param type the type of this local variable.577 */578 public void loadLocal(final int local, final Type type){579 setLocalType(local, type);580 loadInsn(type, local);581 }583 /**584 * Generates the instruction to store the top stack value in the given local585 * variable.586 *587 * @param local a local variable identifier, as returned by588 * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.589 */590 public void storeLocal(final int local){591 storeInsn(getLocalType(local), local);592 }594 /**595 * Generates the instruction to store the top stack value in the given local596 * variable.597 *598 * @param local a local variable identifier, as returned by599 * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.600 * @param type the type of this local variable.601 */602 public void storeLocal(final int local, final Type type){603 setLocalType(local, type);604 storeInsn(type, local);605 }607 /**608 * Generates the instruction to load an element from an array.609 *610 * @param type the type of the array element to be loaded.611 */612 public void arrayLoad(final Type type){613 mv.visitInsn(type.getOpcode(Opcodes.IALOAD));614 }616 /**617 * Generates the instruction to store an element in an array.618 *619 * @param type the type of the array element to be stored.620 */621 public void arrayStore(final Type type){622 mv.visitInsn(type.getOpcode(Opcodes.IASTORE));623 }625 // ------------------------------------------------------------------------626 // Instructions to manage the stack627 // ------------------------------------------------------------------------629 /**630 * Generates a POP instruction.631 */632 public void pop(){633 mv.visitInsn(Opcodes.POP);634 }636 /**637 * Generates a POP2 instruction.638 */639 public void pop2(){640 mv.visitInsn(Opcodes.POP2);641 }643 /**644 * Generates a DUP instruction.645 */646 public void dup(){647 mv.visitInsn(Opcodes.DUP);648 }650 /**651 * Generates a DUP2 instruction.652 */653 public void dup2(){654 mv.visitInsn(Opcodes.DUP2);655 }657 /**658 * Generates a DUP_X1 instruction.659 */660 public void dupX1(){661 mv.visitInsn(Opcodes.DUP_X1);662 }664 /**665 * Generates a DUP_X2 instruction.666 */667 public void dupX2(){668 mv.visitInsn(Opcodes.DUP_X2);669 }671 /**672 * Generates a DUP2_X1 instruction.673 */674 public void dup2X1(){675 mv.visitInsn(Opcodes.DUP2_X1);676 }678 /**679 * Generates a DUP2_X2 instruction.680 */681 public void dup2X2(){682 mv.visitInsn(Opcodes.DUP2_X2);683 }685 /**686 * Generates a SWAP instruction.687 */688 public void swap(){689 mv.visitInsn(Opcodes.SWAP);690 }692 /**693 * Generates the instructions to swap the top two stack values.694 *695 * @param prev type of the top - 1 stack value.696 * @param type type of the top stack value.697 */698 public void swap(final Type prev, final Type type){699 if(type.getSize() == 1)700 {701 if(prev.getSize() == 1)702 {703 swap(); // same as dupX1(), pop();704 }705 else706 {707 dupX2();708 pop();709 }710 }711 else712 {713 if(prev.getSize() == 1)714 {715 dup2X1();716 pop2();717 }718 else719 {720 dup2X2();721 pop2();722 }723 }724 }726 // ------------------------------------------------------------------------727 // Instructions to do mathematical and logical operations728 // ------------------------------------------------------------------------730 /**731 * Generates the instruction to do the specified mathematical or logical732 * operation.733 *734 * @param op a mathematical or logical operation. Must be one of ADD, SUB,735 * MUL, DIV, REM, NEG, SHL, SHR, USHR, AND, OR, XOR.736 * @param type the type of the operand(s) for this operation.737 */738 public void math(final int op, final Type type){739 mv.visitInsn(type.getOpcode(op));740 }742 /**743 * Generates the instructions to compute the bitwise negation of the top744 * stack value.745 */746 public void not(){747 mv.visitInsn(Opcodes.ICONST_1);748 mv.visitInsn(Opcodes.IXOR);749 }751 /**752 * Generates the instruction to increment the given local variable.753 *754 * @param local the local variable to be incremented.755 * @param amount the amount by which the local variable must be incremented.756 */757 public void iinc(final int local, final int amount){758 mv.visitIincInsn(local, amount);759 }761 /**762 * Generates the instructions to cast a numerical value from one type to763 * another.764 *765 * @param from the type of the top stack value766 * @param to the type into which this value must be cast.767 */768 public void cast(final Type from, final Type to){769 if(from != to)770 {771 if(from == Type.DOUBLE_TYPE)772 {773 if(to == Type.FLOAT_TYPE)774 {775 mv.visitInsn(Opcodes.D2F);776 }777 else if(to == Type.LONG_TYPE)778 {779 mv.visitInsn(Opcodes.D2L);780 }781 else782 {783 mv.visitInsn(Opcodes.D2I);784 cast(Type.INT_TYPE, to);785 }786 }787 else if(from == Type.FLOAT_TYPE)788 {789 if(to == Type.DOUBLE_TYPE)790 {791 mv.visitInsn(Opcodes.F2D);792 }793 else if(to == Type.LONG_TYPE)794 {795 mv.visitInsn(Opcodes.F2L);796 }797 else798 {799 mv.visitInsn(Opcodes.F2I);800 cast(Type.INT_TYPE, to);801 }802 }803 else if(from == Type.LONG_TYPE)804 {805 if(to == Type.DOUBLE_TYPE)806 {807 mv.visitInsn(Opcodes.L2D);808 }809 else if(to == Type.FLOAT_TYPE)810 {811 mv.visitInsn(Opcodes.L2F);812 }813 else814 {815 mv.visitInsn(Opcodes.L2I);816 cast(Type.INT_TYPE, to);817 }818 }819 else820 {821 if(to == Type.BYTE_TYPE)822 {823 mv.visitInsn(Opcodes.I2B);824 }825 else if(to == Type.CHAR_TYPE)826 {827 mv.visitInsn(Opcodes.I2C);828 }829 else if(to == Type.DOUBLE_TYPE)830 {831 mv.visitInsn(Opcodes.I2D);832 }833 else if(to == Type.FLOAT_TYPE)834 {835 mv.visitInsn(Opcodes.I2F);836 }837 else if(to == Type.LONG_TYPE)838 {839 mv.visitInsn(Opcodes.I2L);840 }841 else if(to == Type.SHORT_TYPE)842 {843 mv.visitInsn(Opcodes.I2S);844 }845 }846 }847 }849 // ------------------------------------------------------------------------850 // Instructions to do boxing and unboxing operations851 // ------------------------------------------------------------------------853 /**854 * Generates the instructions to box the top stack value. This value is855 * replaced by its boxed equivalent on top of the stack.856 *857 * @param type the type of the top stack value.858 */859 public void box(final Type type){860 if(type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY)861 {862 return;863 }864 if(type == Type.VOID_TYPE)865 {866 push((String) null);867 }868 else869 {870 Type boxed = type;871 switch(type.getSort())872 {873 case Type.BYTE:874 boxed = BYTE_TYPE;875 break;876 case Type.BOOLEAN:877 boxed = BOOLEAN_TYPE;878 break;879 case Type.SHORT:880 boxed = SHORT_TYPE;881 break;882 case Type.CHAR:883 boxed = CHARACTER_TYPE;884 break;885 case Type.INT:886 boxed = INTEGER_TYPE;887 break;888 case Type.FLOAT:889 boxed = FLOAT_TYPE;890 break;891 case Type.LONG:892 boxed = LONG_TYPE;893 break;894 case Type.DOUBLE:895 boxed = DOUBLE_TYPE;896 break;897 }898 newInstance(boxed);899 if(type.getSize() == 2)900 {901 // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o902 dupX2();903 dupX2();904 pop();905 }906 else907 {908 // p -> po -> opo -> oop -> o909 dupX1();910 swap();911 }912 invokeConstructor(boxed, new Method("<init>",913 Type.VOID_TYPE,914 new Type[]{type}));915 }916 }918 /**919 * Generates the instructions to unbox the top stack value. This value is920 * replaced by its unboxed equivalent on top of the stack.921 *922 * @param type the type of the top stack value.923 */924 public void unbox(final Type type){925 Type t = NUMBER_TYPE;926 Method sig = null;927 switch(type.getSort())928 {929 case Type.VOID:930 return;931 case Type.CHAR:932 t = CHARACTER_TYPE;933 sig = CHAR_VALUE;934 break;935 case Type.BOOLEAN:936 t = BOOLEAN_TYPE;937 sig = BOOLEAN_VALUE;938 break;939 case Type.DOUBLE:940 sig = DOUBLE_VALUE;941 break;942 case Type.FLOAT:943 sig = FLOAT_VALUE;944 break;945 case Type.LONG:946 sig = LONG_VALUE;947 break;948 case Type.INT:949 case Type.SHORT:950 case Type.BYTE:951 sig = INT_VALUE;952 }953 if(sig == null)954 {955 checkCast(type);956 }957 else958 {959 checkCast(t);960 invokeVirtual(t, sig);961 }962 }964 // ------------------------------------------------------------------------965 // Instructions to jump to other instructions966 // ------------------------------------------------------------------------968 /**969 * Creates a new {@link Label}.970 *971 * @return a new {@link Label}.972 */973 public Label newLabel(){974 return new Label();975 }977 /**978 * Marks the current code position with the given label.979 *980 * @param label a label.981 */982 public void mark(final Label label){983 mv.visitLabel(label);984 }986 /**987 * Marks the current code position with a new label.988 *989 * @return the label that was created to mark the current code position.990 */991 public Label mark(){992 Label label = new Label();993 mv.visitLabel(label);994 return label;995 }997 /**998 * Generates the instructions to jump to a label based on the comparison of999 * the top two stack values.1000 *1001 * @param type the type of the top two stack values.1002 * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT,1003 * LE.1004 * @param label where to jump if the comparison result is <tt>true</tt>.1005 */1006 public void ifCmp(final Type type, final int mode, final Label label){1007 int intOp = -1;1008 switch(type.getSort())1009 {1010 case Type.LONG:1011 mv.visitInsn(Opcodes.LCMP);1012 break;1013 case Type.DOUBLE:1014 mv.visitInsn(Opcodes.DCMPG);1015 break;1016 case Type.FLOAT:1017 mv.visitInsn(Opcodes.FCMPG);1018 break;1019 case Type.ARRAY:1020 case Type.OBJECT:1021 switch(mode)1022 {1023 case EQ:1024 mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label);1025 return;1026 case NE:1027 mv.visitJumpInsn(Opcodes.IF_ACMPNE, label);1028 return;1029 }1030 throw new IllegalArgumentException("Bad comparison for type "1031 + type);1032 default:1033 switch(mode)1034 {1035 case EQ:1036 intOp = Opcodes.IF_ICMPEQ;1037 break;1038 case NE:1039 intOp = Opcodes.IF_ICMPNE;1040 break;1041 case GE:1042 intOp = Opcodes.IF_ICMPGE;1043 break;1044 case LT:1045 intOp = Opcodes.IF_ICMPLT;1046 break;1047 case LE:1048 intOp = Opcodes.IF_ICMPLE;1049 break;1050 case GT:1051 intOp = Opcodes.IF_ICMPGT;1052 break;1053 }1054 mv.visitJumpInsn(intOp, label);1055 return;1056 }1057 int jumpMode = mode;1058 switch(mode)1059 {1060 case GE:1061 jumpMode = LT;1062 break;1063 case LE:1064 jumpMode = GT;1065 break;1066 }1067 mv.visitJumpInsn(jumpMode, label);1068 }1070 /**1071 * Generates the instructions to jump to a label based on the comparison of1072 * the top two integer stack values.1073 *1074 * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT,1075 * LE.1076 * @param label where to jump if the comparison result is <tt>true</tt>.1077 */1078 public void ifICmp(final int mode, final Label label){1079 ifCmp(Type.INT_TYPE, mode, label);1080 }1082 /**1083 * Generates the instructions to jump to a label based on the comparison of1084 * the top integer stack value with zero.1085 *1086 * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT,1087 * LE.1088 * @param label where to jump if the comparison result is <tt>true</tt>.1089 */1090 public void ifZCmp(final int mode, final Label label){1091 mv.visitJumpInsn(mode, label);1092 }1094 /**1095 * Generates the instruction to jump to the given label if the top stack1096 * value is null.1097 *1098 * @param label where to jump if the condition is <tt>true</tt>.1099 */1100 public void ifNull(final Label label){1101 mv.visitJumpInsn(Opcodes.IFNULL, label);1102 }1104 /**1105 * Generates the instruction to jump to the given label if the top stack1106 * value is not null.1107 *1108 * @param label where to jump if the condition is <tt>true</tt>.1109 */1110 public void ifNonNull(final Label label){1111 mv.visitJumpInsn(Opcodes.IFNONNULL, label);1112 }1114 /**1115 * Generates the instruction to jump to the given label.1116 *1117 * @param label where to jump if the condition is <tt>true</tt>.1118 */1119 public void goTo(final Label label){1120 mv.visitJumpInsn(Opcodes.GOTO, label);1121 }1123 /**1124 * Generates a RET instruction.1125 *1126 * @param local a local variable identifier, as returned by1127 * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.1128 */1129 public void ret(final int local){1130 mv.visitVarInsn(Opcodes.RET, local);1131 }1133 /**1134 * Generates the instructions for a switch statement.1135 *1136 * @param keys the switch case keys.1137 * @param generator a generator to generate the code for the switch cases.1138 */1139 public void tableSwitch(1140 final int[] keys,1141 final TableSwitchGenerator generator){1142 float density;1143 if(keys.length == 0)1144 {1145 density = 0;1146 }1147 else1148 {1149 density = (float) keys.length1150 / (keys[keys.length - 1] - keys[0] + 1);1151 }1152 tableSwitch(keys, generator, density >= 0.5f);1153 }1155 /**1156 * Generates the instructions for a switch statement.1157 *1158 * @param keys the switch case keys.1159 * @param generator a generator to generate the code for the switch cases.1160 * @param useTable <tt>true</tt> to use a TABLESWITCH instruction, or1161 * <tt>false</tt> to use a LOOKUPSWITCH instruction.1162 */1163 public void tableSwitch(1164 final int[] keys,1165 final TableSwitchGenerator generator,1166 final boolean useTable){1167 for(int i = 1; i < keys.length; ++i)1168 {1169 if(keys[i] < keys[i - 1])1170 {1171 throw new IllegalArgumentException("keys must be sorted ascending");1172 }1173 }1174 Label def = newLabel();1175 Label end = newLabel();1176 if(keys.length > 0)1177 {1178 int len = keys.length;1179 int min = keys[0];1180 int max = keys[len - 1];1181 int range = max - min + 1;1182 if(useTable)1183 {1184 Label[] labels = new Label[range];1185 Arrays.fill(labels, def);1186 for(int i = 0; i < len; ++i)1187 {1188 labels[keys[i] - min] = newLabel();1189 }1190 mv.visitTableSwitchInsn(min, max, def, labels);1191 for(int i = 0; i < range; ++i)1192 {1193 Label label = labels[i];1194 if(label != def)1195 {1196 mark(label);1197 generator.generateCase(i + min, end);1198 }1199 }1200 }1201 else1202 {1203 Label[] labels = new Label[len];1204 for(int i = 0; i < len; ++i)1205 {1206 labels[i] = newLabel();1207 }1208 mv.visitLookupSwitchInsn(def, keys, labels);1209 for(int i = 0; i < len; ++i)1210 {1211 mark(labels[i]);1212 generator.generateCase(keys[i], end);1213 }1214 }1215 }1216 mark(def);1217 generator.generateDefault();1218 mark(end);1219 }1221 /**1222 * Generates the instruction to return the top stack value to the caller.1223 */1224 public void returnValue(){1225 mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN));1226 }1228 // ------------------------------------------------------------------------1229 // Instructions to load and store fields1230 // ------------------------------------------------------------------------1232 /**1233 * Generates a get field or set field instruction.1234 *1235 * @param opcode the instruction's opcode.1236 * @param ownerType the class in which the field is defined.1237 * @param name the name of the field.1238 * @param fieldType the type of the field.1239 */1240 private void fieldInsn(1241 final int opcode,1242 final Type ownerType,1243 final String name,1244 final Type fieldType){1245 mv.visitFieldInsn(opcode,1246 ownerType.getInternalName(),1247 name,1248 fieldType.getDescriptor());1249 }1251 /**1252 * Generates the instruction to push the value of a static field on the1253 * stack.1254 *1255 * @param owner the class in which the field is defined.1256 * @param name the name of the field.1257 * @param type the type of the field.1258 */1259 public void getStatic(final Type owner, final String name, final Type type){1260 fieldInsn(Opcodes.GETSTATIC, owner, name, type);1261 }1263 /**1264 * Generates the instruction to store the top stack value in a static field.1265 *1266 * @param owner the class in which the field is defined.1267 * @param name the name of the field.1268 * @param type the type of the field.1269 */1270 public void putStatic(final Type owner, final String name, final Type type){1271 fieldInsn(Opcodes.PUTSTATIC, owner, name, type);1272 }1274 /**1275 * Generates the instruction to push the value of a non static field on the1276 * stack.1277 *1278 * @param owner the class in which the field is defined.1279 * @param name the name of the field.1280 * @param type the type of the field.1281 */1282 public void getField(final Type owner, final String name, final Type type){1283 fieldInsn(Opcodes.GETFIELD, owner, name, type);1284 }1286 /**1287 * Generates the instruction to store the top stack value in a non static1288 * field.1289 *1290 * @param owner the class in which the field is defined.1291 * @param name the name of the field.1292 * @param type the type of the field.1293 */1294 public void putField(final Type owner, final String name, final Type type){1295 fieldInsn(Opcodes.PUTFIELD, owner, name, type);1296 }1298 // ------------------------------------------------------------------------1299 // Instructions to invoke methods1300 // ------------------------------------------------------------------------1302 /**1303 * Generates an invoke method instruction.1304 *1305 * @param opcode the instruction's opcode.1306 * @param type the class in which the method is defined.1307 * @param method the method to be invoked.1308 */1309 private void invokeInsn(1310 final int opcode,1311 final Type type,1312 final Method method){1313 String owner = type.getSort() == Type.ARRAY1314 ? type.getDescriptor()1315 : type.getInternalName();1316 mv.visitMethodInsn(opcode,1317 owner,1318 method.getName(),1319 method.getDescriptor());1320 }1322 /**1323 * Generates the instruction to invoke a normal method.1324 *1325 * @param owner the class in which the method is defined.1326 * @param method the method to be invoked.1327 */1328 public void invokeVirtual(final Type owner, final Method method){1329 invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method);1330 }1332 /**1333 * Generates the instruction to invoke a constructor.1334 *1335 * @param type the class in which the constructor is defined.1336 * @param method the constructor to be invoked.1337 */1338 public void invokeConstructor(final Type type, final Method method){1339 invokeInsn(Opcodes.INVOKESPECIAL, type, method);1340 }1342 /**1343 * Generates the instruction to invoke a static method.1344 *1345 * @param owner the class in which the method is defined.1346 * @param method the method to be invoked.1347 */1348 public void invokeStatic(final Type owner, final Method method){1349 invokeInsn(Opcodes.INVOKESTATIC, owner, method);1350 }1352 /**1353 * Generates the instruction to invoke an interface method.1354 *1355 * @param owner the class in which the method is defined.1356 * @param method the method to be invoked.1357 */1358 public void invokeInterface(final Type owner, final Method method){1359 invokeInsn(Opcodes.INVOKEINTERFACE, owner, method);1360 }1362 // ------------------------------------------------------------------------1363 // Instructions to create objects and arrays1364 // ------------------------------------------------------------------------1366 /**1367 * Generates a type dependent instruction.1368 *1369 * @param opcode the instruction's opcode.1370 * @param type the instruction's operand.1371 */1372 private void typeInsn(final int opcode, final Type type){1373 String desc;1374 if(type.getSort() == Type.ARRAY)1375 {1376 desc = type.getDescriptor();1377 }1378 else1379 {1380 desc = type.getInternalName();1381 }1382 mv.visitTypeInsn(opcode, desc);1383 }1385 /**1386 * Generates the instruction to create a new object.1387 *1388 * @param type the class of the object to be created.1389 */1390 public void newInstance(final Type type){1391 typeInsn(Opcodes.NEW, type);1392 }1394 /**1395 * Generates the instruction to create a new array.1396 *1397 * @param type the type of the array elements.1398 */1399 public void newArray(final Type type){1400 int typ;1401 switch(type.getSort())1402 {1403 case Type.BOOLEAN:1404 typ = Opcodes.T_BOOLEAN;1405 break;1406 case Type.CHAR:1407 typ = Opcodes.T_CHAR;1408 break;1409 case Type.BYTE:1410 typ = Opcodes.T_BYTE;1411 break;1412 case Type.SHORT:1413 typ = Opcodes.T_SHORT;1414 break;1415 case Type.INT:1416 typ = Opcodes.T_INT;1417 break;1418 case Type.FLOAT:1419 typ = Opcodes.T_FLOAT;1420 break;1421 case Type.LONG:1422 typ = Opcodes.T_LONG;1423 break;1424 case Type.DOUBLE:1425 typ = Opcodes.T_DOUBLE;1426 break;1427 default:1428 typeInsn(Opcodes.ANEWARRAY, type);1429 return;1430 }1431 mv.visitIntInsn(Opcodes.NEWARRAY, typ);1432 }1434 // ------------------------------------------------------------------------1435 // Miscelaneous instructions1436 // ------------------------------------------------------------------------1438 /**1439 * Generates the instruction to compute the length of an array.1440 */1441 public void arrayLength(){1442 mv.visitInsn(Opcodes.ARRAYLENGTH);1443 }1445 /**1446 * Generates the instruction to throw an exception.1447 */1448 public void throwException(){1449 mv.visitInsn(Opcodes.ATHROW);1450 }1452 /**1453 * Generates the instructions to create and throw an exception. The1454 * exception class must have a constructor with a single String argument.1455 *1456 * @param type the class of the exception to be thrown.1457 * @param msg the detailed message of the exception.1458 */1459 public void throwException(final Type type, final String msg){1460 newInstance(type);1461 dup();1462 push(msg);1463 invokeConstructor(type, Method.getMethod("void <init> (String)"));1464 throwException();1465 }1467 /**1468 * Generates the instruction to check that the top stack value is of the1469 * given type.1470 *1471 * @param type a class or interface type.1472 */1473 public void checkCast(final Type type){1474 if(!type.equals(OBJECT_TYPE))1475 {1476 typeInsn(Opcodes.CHECKCAST, type);1477 }1478 }1480 /**1481 * Generates the instruction to test if the top stack value is of the given1482 * type.1483 *1484 * @param type a class or interface type.1485 */1486 public void instanceOf(final Type type){1487 typeInsn(Opcodes.INSTANCEOF, type);1488 }1490 /**1491 * Generates the instruction to get the monitor of the top stack value.1492 */1493 public void monitorEnter(){1494 mv.visitInsn(Opcodes.MONITORENTER);1495 }1497 /**1498 * Generates the instruction to release the monitor of the top stack value.1499 */1500 public void monitorExit(){1501 mv.visitInsn(Opcodes.MONITOREXIT);1502 }1504 // ------------------------------------------------------------------------1505 // Non instructions1506 // ------------------------------------------------------------------------1508 /**1509 * Marks the end of the visited method.1510 */1511 public void endMethod(){1512 if((access & Opcodes.ACC_ABSTRACT) == 0)1513 {1514 mv.visitMaxs(0, 0);1515 }1516 mv.visitEnd();1517 }1519 /**1520 * Marks the start of an exception handler.1521 *1522 * @param start beginning of the exception handler's scope (inclusive).1523 * @param end end of the exception handler's scope (exclusive).1524 * @param exception internal name of the type of exceptions handled by the1525 * handler.1526 */1527 public void catchException(1528 final Label start,1529 final Label end,1530 final Type exception){1531 mv.visitTryCatchBlock(start, end, mark(), exception.getInternalName());1532 }1533 }