| /* |
| * Copyright 2003,2004 The Apache Software Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.mockito.cglib.core; |
| |
| import java.io.*; |
| import java.util.*; |
| |
| import org.mockito.asm.*; |
| |
| /** |
| * @author Juozas Baliuka, Chris Nokleberg |
| */ |
| public class CodeEmitter extends LocalVariablesSorter { |
| private static final Signature BOOLEAN_VALUE = |
| TypeUtils.parseSignature("boolean booleanValue()"); |
| private static final Signature CHAR_VALUE = |
| TypeUtils.parseSignature("char charValue()"); |
| private static final Signature LONG_VALUE = |
| TypeUtils.parseSignature("long longValue()"); |
| private static final Signature DOUBLE_VALUE = |
| TypeUtils.parseSignature("double doubleValue()"); |
| private static final Signature FLOAT_VALUE = |
| TypeUtils.parseSignature("float floatValue()"); |
| private static final Signature INT_VALUE = |
| TypeUtils.parseSignature("int intValue()"); |
| private static final Signature CSTRUCT_NULL = |
| TypeUtils.parseConstructor(""); |
| private static final Signature CSTRUCT_STRING = |
| TypeUtils.parseConstructor("String"); |
| |
| public static final int ADD = Constants.IADD; |
| public static final int MUL = Constants.IMUL; |
| public static final int XOR = Constants.IXOR; |
| public static final int USHR = Constants.IUSHR; |
| public static final int SUB = Constants.ISUB; |
| public static final int DIV = Constants.IDIV; |
| public static final int NEG = Constants.INEG; |
| public static final int REM = Constants.IREM; |
| public static final int AND = Constants.IAND; |
| public static final int OR = Constants.IOR; |
| |
| public static final int GT = Constants.IFGT; |
| public static final int LT = Constants.IFLT; |
| public static final int GE = Constants.IFGE; |
| public static final int LE = Constants.IFLE; |
| public static final int NE = Constants.IFNE; |
| public static final int EQ = Constants.IFEQ; |
| |
| private ClassEmitter ce; |
| private State state; |
| |
| private static class State |
| extends MethodInfo |
| { |
| ClassInfo classInfo; |
| int access; |
| Signature sig; |
| Type[] argumentTypes; |
| int localOffset; |
| Type[] exceptionTypes; |
| |
| State(ClassInfo classInfo, int access, Signature sig, Type[] exceptionTypes) { |
| this.classInfo = classInfo; |
| this.access = access; |
| this.sig = sig; |
| this.exceptionTypes = exceptionTypes; |
| localOffset = TypeUtils.isStatic(access) ? 0 : 1; |
| argumentTypes = sig.getArgumentTypes(); |
| } |
| |
| public ClassInfo getClassInfo() { |
| return classInfo; |
| } |
| |
| public int getModifiers() { |
| return access; |
| } |
| |
| public Signature getSignature() { |
| return sig; |
| } |
| |
| public Type[] getExceptionTypes() { |
| return exceptionTypes; |
| } |
| |
| public Attribute getAttribute() { |
| // TODO |
| return null; |
| } |
| } |
| |
| CodeEmitter(ClassEmitter ce, MethodVisitor mv, int access, Signature sig, Type[] exceptionTypes) { |
| super(access, sig.getDescriptor(), mv); |
| this.ce = ce; |
| state = new State(ce.getClassInfo(), access, sig, exceptionTypes); |
| } |
| |
| public CodeEmitter(CodeEmitter wrap) { |
| super(wrap); |
| this.ce = wrap.ce; |
| this.state = wrap.state; |
| } |
| |
| public boolean isStaticHook() { |
| return false; |
| } |
| |
| public Signature getSignature() { |
| return state.sig; |
| } |
| |
| public Type getReturnType() { |
| return state.sig.getReturnType(); |
| } |
| |
| public MethodInfo getMethodInfo() { |
| return state; |
| } |
| |
| public ClassEmitter getClassEmitter() { |
| return ce; |
| } |
| |
| public void end_method() { |
| visitMaxs(0, 0); |
| } |
| |
| public Block begin_block() { |
| return new Block(this); |
| } |
| |
| public void catch_exception(Block block, Type exception) { |
| if (block.getEnd() == null) { |
| throw new IllegalStateException("end of block is unset"); |
| } |
| mv.visitTryCatchBlock(block.getStart(), |
| block.getEnd(), |
| mark(), |
| exception.getInternalName()); |
| } |
| |
| public void goTo(Label label) { mv.visitJumpInsn(Constants.GOTO, label); } |
| public void ifnull(Label label) { mv.visitJumpInsn(Constants.IFNULL, label); } |
| public void ifnonnull(Label label) { mv.visitJumpInsn(Constants.IFNONNULL, label); } |
| |
| public void if_jump(int mode, Label label) { |
| mv.visitJumpInsn(mode, label); |
| } |
| |
| public void if_icmp(int mode, Label label) { |
| if_cmp(Type.INT_TYPE, mode, label); |
| } |
| |
| public void if_cmp(Type type, int mode, Label label) { |
| int intOp = -1; |
| int jumpmode = mode; |
| switch (mode) { |
| case GE: jumpmode = LT; break; |
| case LE: jumpmode = GT; break; |
| } |
| switch (type.getSort()) { |
| case Type.LONG: |
| mv.visitInsn(Constants.LCMP); |
| break; |
| case Type.DOUBLE: |
| mv.visitInsn(Constants.DCMPG); |
| break; |
| case Type.FLOAT: |
| mv.visitInsn(Constants.FCMPG); |
| break; |
| case Type.ARRAY: |
| case Type.OBJECT: |
| switch (mode) { |
| case EQ: |
| mv.visitJumpInsn(Constants.IF_ACMPEQ, label); |
| return; |
| case NE: |
| mv.visitJumpInsn(Constants.IF_ACMPNE, label); |
| return; |
| } |
| throw new IllegalArgumentException("Bad comparison for type " + type); |
| default: |
| switch (mode) { |
| case EQ: intOp = Constants.IF_ICMPEQ; break; |
| case NE: intOp = Constants.IF_ICMPNE; break; |
| case GE: swap(); /* fall through */ |
| case LT: intOp = Constants.IF_ICMPLT; break; |
| case LE: swap(); /* fall through */ |
| case GT: intOp = Constants.IF_ICMPGT; break; |
| } |
| mv.visitJumpInsn(intOp, label); |
| return; |
| } |
| if_jump(jumpmode, label); |
| } |
| |
| public void pop() { mv.visitInsn(Constants.POP); } |
| public void pop2() { mv.visitInsn(Constants.POP2); } |
| public void dup() { mv.visitInsn(Constants.DUP); } |
| public void dup2() { mv.visitInsn(Constants.DUP2); } |
| public void dup_x1() { mv.visitInsn(Constants.DUP_X1); } |
| public void dup_x2() { mv.visitInsn(Constants.DUP_X2); } |
| public void dup2_x1() { mv.visitInsn(Constants.DUP2_X1); } |
| public void dup2_x2() { mv.visitInsn(Constants.DUP2_X2); } |
| public void swap() { mv.visitInsn(Constants.SWAP); } |
| public void aconst_null() { mv.visitInsn(Constants.ACONST_NULL); } |
| |
| public void swap(Type prev, Type type) { |
| if (type.getSize() == 1) { |
| if (prev.getSize() == 1) { |
| swap(); // same as dup_x1(), pop(); |
| } else { |
| dup_x2(); |
| pop(); |
| } |
| } else { |
| if (prev.getSize() == 1) { |
| dup2_x1(); |
| pop2(); |
| } else { |
| dup2_x2(); |
| pop2(); |
| } |
| } |
| } |
| |
| public void monitorenter() { mv.visitInsn(Constants.MONITORENTER); } |
| public void monitorexit() { mv.visitInsn(Constants.MONITOREXIT); } |
| |
| public void math(int op, Type type) { mv.visitInsn(type.getOpcode(op)); } |
| |
| public void array_load(Type type) { mv.visitInsn(type.getOpcode(Constants.IALOAD)); } |
| public void array_store(Type type) { mv.visitInsn(type.getOpcode(Constants.IASTORE)); } |
| |
| /** |
| * Casts from one primitive numeric type to another |
| */ |
| public void cast_numeric(Type from, Type to) { |
| if (from != to) { |
| if (from == Type.DOUBLE_TYPE) { |
| if (to == Type.FLOAT_TYPE) { |
| mv.visitInsn(Constants.D2F); |
| } else if (to == Type.LONG_TYPE) { |
| mv.visitInsn(Constants.D2L); |
| } else { |
| mv.visitInsn(Constants.D2I); |
| cast_numeric(Type.INT_TYPE, to); |
| } |
| } else if (from == Type.FLOAT_TYPE) { |
| if (to == Type.DOUBLE_TYPE) { |
| mv.visitInsn(Constants.F2D); |
| } else if (to == Type.LONG_TYPE) { |
| mv.visitInsn(Constants.F2L); |
| } else { |
| mv.visitInsn(Constants.F2I); |
| cast_numeric(Type.INT_TYPE, to); |
| } |
| } else if (from == Type.LONG_TYPE) { |
| if (to == Type.DOUBLE_TYPE) { |
| mv.visitInsn(Constants.L2D); |
| } else if (to == Type.FLOAT_TYPE) { |
| mv.visitInsn(Constants.L2F); |
| } else { |
| mv.visitInsn(Constants.L2I); |
| cast_numeric(Type.INT_TYPE, to); |
| } |
| } else { |
| if (to == Type.BYTE_TYPE) { |
| mv.visitInsn(Constants.I2B); |
| } else if (to == Type.CHAR_TYPE) { |
| mv.visitInsn(Constants.I2C); |
| } else if (to == Type.DOUBLE_TYPE) { |
| mv.visitInsn(Constants.I2D); |
| } else if (to == Type.FLOAT_TYPE) { |
| mv.visitInsn(Constants.I2F); |
| } else if (to == Type.LONG_TYPE) { |
| mv.visitInsn(Constants.I2L); |
| } else if (to == Type.SHORT_TYPE) { |
| mv.visitInsn(Constants.I2S); |
| } |
| } |
| } |
| } |
| |
| public void push(int i) { |
| if (i < -1) { |
| mv.visitLdcInsn(new Integer(i)); |
| } else if (i <= 5) { |
| mv.visitInsn(TypeUtils.ICONST(i)); |
| } else if (i <= Byte.MAX_VALUE) { |
| mv.visitIntInsn(Constants.BIPUSH, i); |
| } else if (i <= Short.MAX_VALUE) { |
| mv.visitIntInsn(Constants.SIPUSH, i); |
| } else { |
| mv.visitLdcInsn(new Integer(i)); |
| } |
| } |
| |
| public void push(long value) { |
| if (value == 0L || value == 1L) { |
| mv.visitInsn(TypeUtils.LCONST(value)); |
| } else { |
| mv.visitLdcInsn(new Long(value)); |
| } |
| } |
| |
| public void push(float value) { |
| if (value == 0f || value == 1f || value == 2f) { |
| mv.visitInsn(TypeUtils.FCONST(value)); |
| } else { |
| mv.visitLdcInsn(new Float(value)); |
| } |
| } |
| public void push(double value) { |
| if (value == 0d || value == 1d) { |
| mv.visitInsn(TypeUtils.DCONST(value)); |
| } else { |
| mv.visitLdcInsn(new Double(value)); |
| } |
| } |
| |
| public void push(String value) { |
| mv.visitLdcInsn(value); |
| } |
| |
| public void newarray() { |
| newarray(Constants.TYPE_OBJECT); |
| } |
| |
| public void newarray(Type type) { |
| if (TypeUtils.isPrimitive(type)) { |
| mv.visitIntInsn(Constants.NEWARRAY, TypeUtils.NEWARRAY(type)); |
| } else { |
| emit_type(Constants.ANEWARRAY, type); |
| } |
| } |
| |
| public void arraylength() { |
| mv.visitInsn(Constants.ARRAYLENGTH); |
| } |
| |
| public void load_this() { |
| if (TypeUtils.isStatic(state.access)) { |
| throw new IllegalStateException("no 'this' pointer within static method"); |
| } |
| mv.visitVarInsn(Constants.ALOAD, 0); |
| } |
| |
| /** |
| * Pushes all of the arguments of the current method onto the stack. |
| */ |
| public void load_args() { |
| load_args(0, state.argumentTypes.length); |
| } |
| |
| /** |
| * Pushes the specified argument of the current method onto the stack. |
| * @param index the zero-based index into the argument list |
| */ |
| public void load_arg(int index) { |
| load_local(state.argumentTypes[index], |
| state.localOffset + skipArgs(index)); |
| } |
| |
| // zero-based (see load_this) |
| public void load_args(int fromArg, int count) { |
| int pos = state.localOffset + skipArgs(fromArg); |
| for (int i = 0; i < count; i++) { |
| Type t = state.argumentTypes[fromArg + i]; |
| load_local(t, pos); |
| pos += t.getSize(); |
| } |
| } |
| |
| private int skipArgs(int numArgs) { |
| int amount = 0; |
| for (int i = 0; i < numArgs; i++) { |
| amount += state.argumentTypes[i].getSize(); |
| } |
| return amount; |
| } |
| |
| private void load_local(Type t, int pos) { |
| // TODO: make t == null ok? |
| mv.visitVarInsn(t.getOpcode(Constants.ILOAD), pos); |
| } |
| |
| private void store_local(Type t, int pos) { |
| // TODO: make t == null ok? |
| mv.visitVarInsn(t.getOpcode(Constants.ISTORE), pos); |
| } |
| |
| public void iinc(Local local, int amount) { |
| mv.visitIincInsn(local.getIndex(), amount); |
| } |
| |
| public void store_local(Local local) { |
| store_local(local.getType(), local.getIndex()); |
| } |
| |
| public void load_local(Local local) { |
| load_local(local.getType(), local.getIndex()); |
| } |
| |
| public void return_value() { |
| mv.visitInsn(state.sig.getReturnType().getOpcode(Constants.IRETURN)); |
| } |
| |
| public void getfield(String name) { |
| ClassEmitter.FieldInfo info = ce.getFieldInfo(name); |
| int opcode = TypeUtils.isStatic(info.access) ? Constants.GETSTATIC : Constants.GETFIELD; |
| emit_field(opcode, ce.getClassType(), name, info.type); |
| } |
| |
| public void putfield(String name) { |
| ClassEmitter.FieldInfo info = ce.getFieldInfo(name); |
| int opcode = TypeUtils.isStatic(info.access) ? Constants.PUTSTATIC : Constants.PUTFIELD; |
| emit_field(opcode, ce.getClassType(), name, info.type); |
| } |
| |
| public void super_getfield(String name, Type type) { |
| emit_field(Constants.GETFIELD, ce.getSuperType(), name, type); |
| } |
| |
| public void super_putfield(String name, Type type) { |
| emit_field(Constants.PUTFIELD, ce.getSuperType(), name, type); |
| } |
| |
| public void super_getstatic(String name, Type type) { |
| emit_field(Constants.GETSTATIC, ce.getSuperType(), name, type); |
| } |
| |
| public void super_putstatic(String name, Type type) { |
| emit_field(Constants.PUTSTATIC, ce.getSuperType(), name, type); |
| } |
| |
| public void getfield(Type owner, String name, Type type) { |
| emit_field(Constants.GETFIELD, owner, name, type); |
| } |
| |
| public void putfield(Type owner, String name, Type type) { |
| emit_field(Constants.PUTFIELD, owner, name, type); |
| } |
| |
| public void getstatic(Type owner, String name, Type type) { |
| emit_field(Constants.GETSTATIC, owner, name, type); |
| } |
| |
| public void putstatic(Type owner, String name, Type type) { |
| emit_field(Constants.PUTSTATIC, owner, name, type); |
| } |
| |
| // package-protected for EmitUtils, try to fix |
| void emit_field(int opcode, Type ctype, String name, Type ftype) { |
| mv.visitFieldInsn(opcode, |
| ctype.getInternalName(), |
| name, |
| ftype.getDescriptor()); |
| } |
| |
| public void super_invoke() { |
| super_invoke(state.sig); |
| } |
| |
| public void super_invoke(Signature sig) { |
| emit_invoke(Constants.INVOKESPECIAL, ce.getSuperType(), sig); |
| } |
| |
| public void invoke_constructor(Type type) { |
| invoke_constructor(type, CSTRUCT_NULL); |
| } |
| |
| public void super_invoke_constructor() { |
| invoke_constructor(ce.getSuperType()); |
| } |
| |
| public void invoke_constructor_this() { |
| invoke_constructor(ce.getClassType()); |
| } |
| |
| private void emit_invoke(int opcode, Type type, Signature sig) { |
| if (sig.getName().equals(Constants.CONSTRUCTOR_NAME) && |
| ((opcode == Constants.INVOKEVIRTUAL) || |
| (opcode == Constants.INVOKESTATIC))) { |
| // TODO: error |
| } |
| mv.visitMethodInsn(opcode, |
| type.getInternalName(), |
| sig.getName(), |
| sig.getDescriptor()); |
| } |
| |
| public void invoke_interface(Type owner, Signature sig) { |
| emit_invoke(Constants.INVOKEINTERFACE, owner, sig); |
| } |
| |
| public void invoke_virtual(Type owner, Signature sig) { |
| emit_invoke(Constants.INVOKEVIRTUAL, owner, sig); |
| } |
| |
| public void invoke_static(Type owner, Signature sig) { |
| emit_invoke(Constants.INVOKESTATIC, owner, sig); |
| } |
| |
| public void invoke_virtual_this(Signature sig) { |
| invoke_virtual(ce.getClassType(), sig); |
| } |
| |
| public void invoke_static_this(Signature sig) { |
| invoke_static(ce.getClassType(), sig); |
| } |
| |
| public void invoke_constructor(Type type, Signature sig) { |
| emit_invoke(Constants.INVOKESPECIAL, type, sig); |
| } |
| |
| public void invoke_constructor_this(Signature sig) { |
| invoke_constructor(ce.getClassType(), sig); |
| } |
| |
| public void super_invoke_constructor(Signature sig) { |
| invoke_constructor(ce.getSuperType(), sig); |
| } |
| |
| public void new_instance_this() { |
| new_instance(ce.getClassType()); |
| } |
| |
| public void new_instance(Type type) { |
| emit_type(Constants.NEW, type); |
| } |
| |
| private void emit_type(int opcode, Type type) { |
| String desc; |
| if (TypeUtils.isArray(type)) { |
| desc = type.getDescriptor(); |
| } else { |
| desc = type.getInternalName(); |
| } |
| mv.visitTypeInsn(opcode, desc); |
| } |
| |
| public void aaload(int index) { |
| push(index); |
| aaload(); |
| } |
| |
| public void aaload() { mv.visitInsn(Constants.AALOAD); } |
| public void aastore() { mv.visitInsn(Constants.AASTORE); } |
| public void athrow() { mv.visitInsn(Constants.ATHROW); } |
| |
| public Label make_label() { |
| return new Label(); |
| } |
| |
| public Local make_local() { |
| return make_local(Constants.TYPE_OBJECT); |
| } |
| |
| public Local make_local(Type type) { |
| return new Local(newLocal(type.getSize()), type); |
| } |
| |
| public void checkcast_this() { |
| checkcast(ce.getClassType()); |
| } |
| |
| public void checkcast(Type type) { |
| if (!type.equals(Constants.TYPE_OBJECT)) { |
| emit_type(Constants.CHECKCAST, type); |
| } |
| } |
| |
| public void instance_of(Type type) { |
| emit_type(Constants.INSTANCEOF, type); |
| } |
| |
| public void instance_of_this() { |
| instance_of(ce.getClassType()); |
| } |
| |
| public void process_switch(int[] keys, ProcessSwitchCallback callback) { |
| float density; |
| if (keys.length == 0) { |
| density = 0; |
| } else { |
| density = (float)keys.length / (keys[keys.length - 1] - keys[0] + 1); |
| } |
| process_switch(keys, callback, density >= 0.5f); |
| } |
| |
| public void process_switch(int[] keys, ProcessSwitchCallback callback, boolean useTable) { |
| if (!isSorted(keys)) |
| throw new IllegalArgumentException("keys to switch must be sorted ascending"); |
| Label def = make_label(); |
| Label end = make_label(); |
| |
| try { |
| if (keys.length > 0) { |
| int len = keys.length; |
| int min = keys[0]; |
| int max = keys[len - 1]; |
| int range = max - min + 1; |
| |
| if (useTable) { |
| Label[] labels = new Label[range]; |
| Arrays.fill(labels, def); |
| for (int i = 0; i < len; i++) { |
| labels[keys[i] - min] = make_label(); |
| } |
| mv.visitTableSwitchInsn(min, max, def, labels); |
| for (int i = 0; i < range; i++) { |
| Label label = labels[i]; |
| if (label != def) { |
| mark(label); |
| callback.processCase(i + min, end); |
| } |
| } |
| } else { |
| Label[] labels = new Label[len]; |
| for (int i = 0; i < len; i++) { |
| labels[i] = make_label(); |
| } |
| mv.visitLookupSwitchInsn(def, keys, labels); |
| for (int i = 0; i < len; i++) { |
| mark(labels[i]); |
| callback.processCase(keys[i], end); |
| } |
| } |
| } |
| |
| mark(def); |
| callback.processDefault(); |
| mark(end); |
| |
| } catch (RuntimeException e) { |
| throw e; |
| } catch (Error e) { |
| throw e; |
| } catch (Exception e) { |
| throw new CodeGenerationException(e); |
| } |
| } |
| |
| private static boolean isSorted(int[] keys) { |
| for (int i = 1; i < keys.length; i++) { |
| if (keys[i] < keys[i - 1]) |
| return false; |
| } |
| return true; |
| } |
| |
| public void mark(Label label) { |
| mv.visitLabel(label); |
| } |
| |
| Label mark() { |
| Label label = make_label(); |
| mv.visitLabel(label); |
| return label; |
| } |
| |
| public void push(boolean value) { |
| push(value ? 1 : 0); |
| } |
| |
| /** |
| * Toggles the integer on the top of the stack from 1 to 0 or vice versa |
| */ |
| public void not() { |
| push(1); |
| math(XOR, Type.INT_TYPE); |
| } |
| |
| public void throw_exception(Type type, String msg) { |
| new_instance(type); |
| dup(); |
| push(msg); |
| invoke_constructor(type, CSTRUCT_STRING); |
| athrow(); |
| } |
| |
| /** |
| * If the argument is a primitive class, replaces the primitive value |
| * on the top of the stack with the wrapped (Object) equivalent. For |
| * example, char -> Character. |
| * If the class is Void, a null is pushed onto the stack instead. |
| * @param type the class indicating the current type of the top stack value |
| */ |
| public void box(Type type) { |
| if (TypeUtils.isPrimitive(type)) { |
| if (type == Type.VOID_TYPE) { |
| aconst_null(); |
| } else { |
| Type boxed = TypeUtils.getBoxedType(type); |
| new_instance(boxed); |
| if (type.getSize() == 2) { |
| // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o |
| dup_x2(); |
| dup_x2(); |
| pop(); |
| } else { |
| // p -> po -> opo -> oop -> o |
| dup_x1(); |
| swap(); |
| } |
| invoke_constructor(boxed, new Signature(Constants.CONSTRUCTOR_NAME, Type.VOID_TYPE, new Type[]{ type })); |
| } |
| } |
| } |
| |
| /** |
| * If the argument is a primitive class, replaces the object |
| * on the top of the stack with the unwrapped (primitive) |
| * equivalent. For example, Character -> char. |
| * @param type the class indicating the desired type of the top stack value |
| * @return true if the value was unboxed |
| */ |
| public void unbox(Type type) { |
| Type t = Constants.TYPE_NUMBER; |
| Signature sig = null; |
| switch (type.getSort()) { |
| case Type.VOID: |
| return; |
| case Type.CHAR: |
| t = Constants.TYPE_CHARACTER; |
| sig = CHAR_VALUE; |
| break; |
| case Type.BOOLEAN: |
| t = Constants.TYPE_BOOLEAN; |
| sig = BOOLEAN_VALUE; |
| break; |
| case Type.DOUBLE: |
| sig = DOUBLE_VALUE; |
| break; |
| case Type.FLOAT: |
| sig = FLOAT_VALUE; |
| break; |
| case Type.LONG: |
| sig = LONG_VALUE; |
| break; |
| case Type.INT: |
| case Type.SHORT: |
| case Type.BYTE: |
| sig = INT_VALUE; |
| } |
| |
| if (sig == null) { |
| checkcast(type); |
| } else { |
| checkcast(t); |
| invoke_virtual(t, sig); |
| } |
| } |
| |
| /** |
| * Allocates and fills an Object[] array with the arguments to the |
| * current method. Primitive values are inserted as their boxed |
| * (Object) equivalents. |
| */ |
| public void create_arg_array() { |
| /* generates: |
| Object[] args = new Object[]{ arg1, new Integer(arg2) }; |
| */ |
| |
| push(state.argumentTypes.length); |
| newarray(); |
| for (int i = 0; i < state.argumentTypes.length; i++) { |
| dup(); |
| push(i); |
| load_arg(i); |
| box(state.argumentTypes[i]); |
| aastore(); |
| } |
| } |
| |
| |
| /** |
| * Pushes a zero onto the stack if the argument is a primitive class, or a null otherwise. |
| */ |
| public void zero_or_null(Type type) { |
| if (TypeUtils.isPrimitive(type)) { |
| switch (type.getSort()) { |
| case Type.DOUBLE: |
| push(0d); |
| break; |
| case Type.LONG: |
| push(0L); |
| break; |
| case Type.FLOAT: |
| push(0f); |
| break; |
| case Type.VOID: |
| aconst_null(); |
| default: |
| push(0); |
| } |
| } else { |
| aconst_null(); |
| } |
| } |
| |
| /** |
| * Unboxes the object on the top of the stack. If the object is null, the |
| * unboxed primitive value becomes zero. |
| */ |
| public void unbox_or_zero(Type type) { |
| if (TypeUtils.isPrimitive(type)) { |
| if (type != Type.VOID_TYPE) { |
| Label nonNull = make_label(); |
| Label end = make_label(); |
| dup(); |
| ifnonnull(nonNull); |
| pop(); |
| zero_or_null(type); |
| goTo(end); |
| mark(nonNull); |
| unbox(type); |
| mark(end); |
| } |
| } else { |
| checkcast(type); |
| } |
| } |
| |
| public void visitMaxs(int maxStack, int maxLocals) { |
| if (!TypeUtils.isAbstract(state.access)) { |
| mv.visitMaxs(0, 0); |
| } |
| } |
| |
| public void invoke(MethodInfo method, Type virtualType) { |
| ClassInfo classInfo = method.getClassInfo(); |
| Type type = classInfo.getType(); |
| Signature sig = method.getSignature(); |
| if (sig.getName().equals(Constants.CONSTRUCTOR_NAME)) { |
| invoke_constructor(type, sig); |
| } else if (TypeUtils.isInterface(classInfo.getModifiers())) { |
| invoke_interface(type, sig); |
| } else if (TypeUtils.isStatic(method.getModifiers())) { |
| invoke_static(type, sig); |
| } else { |
| invoke_virtual(virtualType, sig); |
| } |
| } |
| |
| public void invoke(MethodInfo method) { |
| invoke(method, method.getClassInfo().getType()); |
| } |
| } |