package org.simantics.scl.compiler.internal.codegen.utils; import org.cojen.classfile.MethodDesc; import org.cojen.classfile.TypeDesc; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; public class MethodBuilderBase { private ClassBuilder classBuilder; private MethodVisitor methodVisitor; private LocalVariable[] parameters; private int localVariableCount = 0; public MethodBuilderBase(ClassBuilder classBuilder, boolean isStatic, MethodVisitor methodVisitor, TypeDesc[] parameterTypes) { this.classBuilder = classBuilder; this.methodVisitor = methodVisitor; this.parameters = new LocalVariable[parameterTypes.length]; if(!isStatic) ++localVariableCount; for(int i=0;i= -1 && value <= 5) { methodVisitor.visitInsn(Opcodes.ICONST_0 + value); } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { methodVisitor.visitIntInsn(Opcodes.BIPUSH, value); } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { methodVisitor.visitIntInsn(Opcodes.SIPUSH, value); } else { methodVisitor.visitLdcInsn(value); } } public void loadConstant(long value) { if(value == 0L) methodVisitor.visitInsn(Opcodes.LCONST_0); else if(value == 1L) methodVisitor.visitInsn(Opcodes.LCONST_1); else methodVisitor.visitLdcInsn(value); } public void loadConstant(float value) { if(value == 0.0f) methodVisitor.visitInsn(Opcodes.FCONST_0); else if(value == 1.0f) methodVisitor.visitInsn(Opcodes.FCONST_1); else methodVisitor.visitLdcInsn(value); } public void loadConstant(double value) { if(value == 0.0) methodVisitor.visitInsn(Opcodes.DCONST_0); else if(value == 1.0) methodVisitor.visitInsn(Opcodes.DCONST_1); else methodVisitor.visitLdcInsn(value); } public void loadConstant(String value) { methodVisitor.visitLdcInsn(value); } public void loadConstant(TypeDesc value) { if(value.isPrimitive()) loadStaticField(getClassName(value.toObjectType()), "TYPE", Constants.CLASS); else methodVisitor.visitLdcInsn(Type.getType(value.getDescriptor())); } public void dup() { methodVisitor.visitInsn(Opcodes.DUP); } public void dup2() { methodVisitor.visitInsn(Opcodes.DUP2); } public void dupX1() { methodVisitor.visitInsn(Opcodes.DUP_X1); } public void swap() { methodVisitor.visitInsn(Opcodes.SWAP); } public void pop() { methodVisitor.visitInsn(Opcodes.POP); } public void pop2() { methodVisitor.visitInsn(Opcodes.POP2); } public void convert(TypeDesc fromType, TypeDesc toType) { switch(fromType.getTypeCode() + 16*toType.getTypeCode()) { case TypeDesc.OBJECT_CODE + 16*TypeDesc.BOOLEAN_CODE: methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false); return; case TypeDesc.OBJECT_CODE + 16*TypeDesc.INT_CODE: methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false); return; case TypeDesc.OBJECT_CODE + 16*TypeDesc.FLOAT_CODE: methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F", false); return; case TypeDesc.OBJECT_CODE + 16*TypeDesc.DOUBLE_CODE: methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false); return; case TypeDesc.OBJECT_CODE + 16*TypeDesc.CHAR_CODE: methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false); return; case TypeDesc.OBJECT_CODE + 16*TypeDesc.LONG_CODE: methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false); return; case TypeDesc.OBJECT_CODE + 16*TypeDesc.SHORT_CODE: methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false); return; case TypeDesc.OBJECT_CODE + 16*TypeDesc.BYTE_CODE: methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B", false); return; case TypeDesc.DOUBLE_CODE + 16*TypeDesc.FLOAT_CODE: methodVisitor.visitInsn(Opcodes.D2F); return; case TypeDesc.FLOAT_CODE + 16*TypeDesc.DOUBLE_CODE: methodVisitor.visitInsn(Opcodes.F2D); return; case TypeDesc.INT_CODE + 16*TypeDesc.DOUBLE_CODE: methodVisitor.visitInsn(Opcodes.I2D); return; case TypeDesc.DOUBLE_CODE + 16*TypeDesc.INT_CODE: methodVisitor.visitInsn(Opcodes.D2I); return; case TypeDesc.INT_CODE + 16*TypeDesc.FLOAT_CODE: methodVisitor.visitInsn(Opcodes.I2F); return; case TypeDesc.FLOAT_CODE + 16*TypeDesc.INT_CODE: methodVisitor.visitInsn(Opcodes.F2I); return; case TypeDesc.LONG_CODE + 16*TypeDesc.FLOAT_CODE: methodVisitor.visitInsn(Opcodes.L2F); return; case TypeDesc.FLOAT_CODE + 16*TypeDesc.LONG_CODE: methodVisitor.visitInsn(Opcodes.F2L); return; case TypeDesc.LONG_CODE + 16*TypeDesc.DOUBLE_CODE: methodVisitor.visitInsn(Opcodes.L2D); return; case TypeDesc.DOUBLE_CODE + 16*TypeDesc.LONG_CODE: methodVisitor.visitInsn(Opcodes.D2L); return; case TypeDesc.INT_CODE + 16*TypeDesc.LONG_CODE: methodVisitor.visitInsn(Opcodes.I2L); return; case TypeDesc.LONG_CODE + 16*TypeDesc.INT_CODE: methodVisitor.visitInsn(Opcodes.L2I); return; case TypeDesc.INT_CODE + 16*TypeDesc.SHORT_CODE: methodVisitor.visitInsn(Opcodes.I2S); return; case TypeDesc.SHORT_CODE + 16*TypeDesc.INT_CODE: case TypeDesc.BOOLEAN_CODE + 16*TypeDesc.INT_CODE: // NOP return; case TypeDesc.INT_CODE + 16*TypeDesc.BYTE_CODE: methodVisitor.visitInsn(Opcodes.I2B); return; case TypeDesc.BYTE_CODE + 16*TypeDesc.INT_CODE: // NOP return; case TypeDesc.INT_CODE + 16*TypeDesc.CHAR_CODE: methodVisitor.visitInsn(Opcodes.I2C); return; case TypeDesc.CHAR_CODE + 16*TypeDesc.INT_CODE: // NOP return; default: System.out.println("convert: " + fromType + " -> " + toType); } } public void convert(TypeDesc fromType, TypeDesc toType, int convertFpNormal) { convert(fromType, toType); } public LocalVariable createLocalVariable(String name, TypeDesc type) { int index = localVariableCount; localVariableCount += type == TypeDesc.DOUBLE || type == TypeDesc.LONG ? 2 : 1; return new LocalVariable(index, type); } public void storeLocal(LocalVariable local) { switch(local.type.getTypeCode()) { case TypeDesc.BOOLEAN_CODE: case TypeDesc.BYTE_CODE: case TypeDesc.CHAR_CODE: case TypeDesc.SHORT_CODE: case TypeDesc.INT_CODE: methodVisitor.visitVarInsn(Opcodes.ISTORE, local.index); break; case TypeDesc.FLOAT_CODE: methodVisitor.visitVarInsn(Opcodes.FSTORE, local.index); break; case TypeDesc.DOUBLE_CODE: methodVisitor.visitVarInsn(Opcodes.DSTORE, local.index); break; case TypeDesc.LONG_CODE: methodVisitor.visitVarInsn(Opcodes.LSTORE, local.index); break; case TypeDesc.OBJECT_CODE: methodVisitor.visitVarInsn(Opcodes.ASTORE, local.index); break; default: throw new IllegalArgumentException(); } } public void ifZeroComparisonBranch(Label location, String choice) { int opcode; if(choice.equals("!=")) opcode = Opcodes.IFNE; else if(choice.equals("==")) opcode = Opcodes.IFEQ; else if(choice.equals("<")) opcode = Opcodes.IFLT; else if(choice.equals(">")) opcode = Opcodes.IFGT; else if(choice.equals("<=")) opcode = Opcodes.IFLE; else if(choice.equals(">=")) opcode = Opcodes.IFGE; else throw new IllegalArgumentException("Invalid choice \"" + choice + "\"."); methodVisitor.visitJumpInsn(opcode, location); } public void ifComparisonBranch(Label location, String choice, TypeDesc type) { switch(type.getTypeCode()) { case TypeDesc.BOOLEAN_CODE: case TypeDesc.BYTE_CODE: case TypeDesc.CHAR_CODE: case TypeDesc.SHORT_CODE: case TypeDesc.INT_CODE: { int opcode; if(choice.equals("!=")) opcode = Opcodes.IF_ICMPNE; else if(choice.equals("==")) opcode = Opcodes.IF_ICMPEQ; else if(choice.equals("<")) opcode = Opcodes.IF_ICMPLT; else if(choice.equals(">")) opcode = Opcodes.IF_ICMPGT; else if(choice.equals("<=")) opcode = Opcodes.IF_ICMPLE; else if(choice.equals(">=")) opcode = Opcodes.IF_ICMPGE; else throw new IllegalArgumentException("Invalid choice \"" + choice + "\"."); methodVisitor.visitJumpInsn(opcode, location); } break; case TypeDesc.FLOAT_CODE: methodVisitor.visitInsn(Opcodes.FCMPL); ifZeroComparisonBranch(location, choice); break; case TypeDesc.DOUBLE_CODE: methodVisitor.visitInsn(Opcodes.DCMPL); ifZeroComparisonBranch(location, choice); break; case TypeDesc.LONG_CODE: methodVisitor.visitInsn(Opcodes.LCMP); ifZeroComparisonBranch(location, choice); break; case TypeDesc.OBJECT_CODE: { int opcode; if(choice.equals("!=")) opcode = Opcodes.IF_ACMPNE; else if(choice.equals("==")) opcode = Opcodes.IF_ACMPEQ; else throw new IllegalArgumentException("Invalid choice \"" + choice + "\"."); methodVisitor.visitJumpInsn(opcode, location); } break; default: throw new IllegalArgumentException(); } } public void ifNullBranch(Label location, boolean choice) { methodVisitor.visitJumpInsn(choice ? Opcodes.IFNULL : Opcodes.IFNONNULL, location); } public void branch(Label location) { methodVisitor.visitJumpInsn(Opcodes.GOTO, location); } public Label createLabel() { return new Label(); } public void setLocation(Label label) { methodVisitor.visitLabel(label); } public void loadLocal(LocalVariable local) { switch(local.type.getTypeCode()) { case TypeDesc.BOOLEAN_CODE: case TypeDesc.BYTE_CODE: case TypeDesc.CHAR_CODE: case TypeDesc.SHORT_CODE: case TypeDesc.INT_CODE: methodVisitor.visitVarInsn(Opcodes.ILOAD, local.index); break; case TypeDesc.FLOAT_CODE: methodVisitor.visitVarInsn(Opcodes.FLOAD, local.index); break; case TypeDesc.DOUBLE_CODE: methodVisitor.visitVarInsn(Opcodes.DLOAD, local.index); break; case TypeDesc.LONG_CODE: methodVisitor.visitVarInsn(Opcodes.LLOAD, local.index); break; case TypeDesc.OBJECT_CODE: methodVisitor.visitVarInsn(Opcodes.ALOAD, local.index); break; default: throw new IllegalArgumentException(); } } public void loadNull() { methodVisitor.visitInsn(Opcodes.ACONST_NULL); } public void loadFromArray(TypeDesc type) { switch(type.getTypeCode()) { case TypeDesc.BOOLEAN_CODE: methodVisitor.visitInsn(Opcodes.BALOAD); break; case TypeDesc.BYTE_CODE: methodVisitor.visitInsn(Opcodes.BALOAD); break; case TypeDesc.CHAR_CODE: methodVisitor.visitInsn(Opcodes.CALOAD); break; case TypeDesc.SHORT_CODE: methodVisitor.visitInsn(Opcodes.SALOAD); break; case TypeDesc.INT_CODE: methodVisitor.visitInsn(Opcodes.IALOAD); break; case TypeDesc.FLOAT_CODE: methodVisitor.visitInsn(Opcodes.FALOAD); break; case TypeDesc.DOUBLE_CODE: methodVisitor.visitInsn(Opcodes.DALOAD); break; case TypeDesc.LONG_CODE: methodVisitor.visitInsn(Opcodes.LALOAD); break; case TypeDesc.OBJECT_CODE: methodVisitor.visitInsn(Opcodes.AALOAD); break; default: throw new IllegalArgumentException(); } } public void invokeInterface(String className, String methodName, TypeDesc ret, TypeDesc[] params) { checkClassName(className); checkParameters(params); methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, className, methodName, MethodDesc.forArguments(ret, params).getDescriptor(), true); } public void invokeInterface(TypeDesc className, String methodName, TypeDesc ret, TypeDesc[] params) { invokeInterface(getClassName(className), methodName, ret, params); } public void invokeVirtual(String className, String methodName, TypeDesc ret, TypeDesc[] params) { checkClassName(className); checkParameters(params); methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, methodName, MethodDesc.forArguments(ret, params).getDescriptor(), false); } public void invokeVirtual(TypeDesc className, String methodName, TypeDesc ret, TypeDesc[] params) { invokeVirtual(getClassName(className), methodName, ret, params); } public void invokeConstructor(String className, TypeDesc[] params) { checkClassName(className); checkParameters(params); methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, className, "", MethodDesc.forArguments(TypeDesc.VOID, params).getDescriptor(), false); } public void invokeConstructor(TypeDesc className, TypeDesc[] params) { invokeConstructor(getClassName(className), params); } public void invokeSuperConstructor(TypeDesc[] params) { invokeConstructor(classBuilder.getSuperClassName(), params); } public void invokeStatic(String className, String methodName, TypeDesc ret, TypeDesc[] params) { checkClassName(className); checkParameters(params); methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, className, methodName, MethodDesc.forArguments(ret, params).getDescriptor(), false); } public void invokeStatic(TypeDesc className, String methodName, TypeDesc ret, TypeDesc[] params) { invokeStatic(getClassName(className), methodName, ret, params); } public void newObject(TypeDesc type) { methodVisitor.visitTypeInsn(Opcodes.NEW, getClassName(type)); } public void newObject(String type) { methodVisitor.visitTypeInsn(Opcodes.NEW, type); } public void newObject(TypeDesc type, int dimensions) { methodVisitor.visitMultiANewArrayInsn(type.getDescriptor(), dimensions); } public void loadStaticField(String className, String fieldName, TypeDesc type) { checkClassName(className); methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, className, fieldName, type.getDescriptor()); } public void loadStaticField(TypeDesc className, String fieldName, TypeDesc type) { loadStaticField(getClassName(className), fieldName, type); } public void loadField(String className, String fieldName, TypeDesc type) { checkClassName(className); methodVisitor.visitFieldInsn(Opcodes.GETFIELD, className, fieldName, type.getDescriptor()); } public void storeStaticField(String className, String fieldName, TypeDesc type) { checkClassName(className); methodVisitor.visitFieldInsn(Opcodes.PUTSTATIC, className, fieldName, type.getDescriptor()); } public void storeField(String className, String fieldName, TypeDesc type) { checkClassName(className); methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, className, fieldName, type.getDescriptor()); } public void storeToArray(TypeDesc type) { switch(type.getTypeCode()) { case TypeDesc.BOOLEAN_CODE: methodVisitor.visitInsn(Opcodes.BASTORE); break; case TypeDesc.BYTE_CODE: methodVisitor.visitInsn(Opcodes.BASTORE); break; case TypeDesc.CHAR_CODE: methodVisitor.visitInsn(Opcodes.CASTORE); break; case TypeDesc.SHORT_CODE: methodVisitor.visitInsn(Opcodes.SASTORE); break; case TypeDesc.INT_CODE: methodVisitor.visitInsn(Opcodes.IASTORE); break; case TypeDesc.FLOAT_CODE: methodVisitor.visitInsn(Opcodes.FASTORE); break; case TypeDesc.DOUBLE_CODE: methodVisitor.visitInsn(Opcodes.DASTORE); break; case TypeDesc.LONG_CODE: methodVisitor.visitInsn(Opcodes.LASTORE); break; case TypeDesc.OBJECT_CODE: methodVisitor.visitInsn(Opcodes.AASTORE); break; default: throw new IllegalArgumentException(); } } public void math(int opcode) { methodVisitor.visitInsn(opcode); } public void throwObject() { methodVisitor.visitInsn(Opcodes.ATHROW); } public void checkCast(TypeDesc type) { methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, getClassName(type)); } public void instanceOf(TypeDesc type) { methodVisitor.visitTypeInsn(Opcodes.INSTANCEOF, getClassName(type)); } public void arrayLength() { methodVisitor.visitInsn(Opcodes.ARRAYLENGTH); } public void loadThis() { methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); } public LocalVariable getParameter(int index) { return parameters[index]; } public void returnVoid() { methodVisitor.visitInsn(Opcodes.RETURN); } public void returnValue(TypeDesc type) { switch(type.getTypeCode()) { case TypeDesc.VOID_CODE: methodVisitor.visitInsn(Opcodes.RETURN); break; case TypeDesc.BOOLEAN_CODE: case TypeDesc.BYTE_CODE: case TypeDesc.CHAR_CODE: case TypeDesc.SHORT_CODE: case TypeDesc.INT_CODE: methodVisitor.visitInsn(Opcodes.IRETURN); break; case TypeDesc.FLOAT_CODE: methodVisitor.visitInsn(Opcodes.FRETURN); break; case TypeDesc.DOUBLE_CODE: methodVisitor.visitInsn(Opcodes.DRETURN); break; case TypeDesc.LONG_CODE: methodVisitor.visitInsn(Opcodes.LRETURN); break; case TypeDesc.OBJECT_CODE: methodVisitor.visitInsn(Opcodes.ARETURN); break; default: throw new IllegalArgumentException(); } } public void finish() { methodVisitor.visitMaxs(0, 0); methodVisitor.visitEnd(); } public String getClassName() { return classBuilder.className; } public static String getClassName(TypeDesc typeDesc) { if(typeDesc.isArray()) return typeDesc.getDescriptor(); else return typeDesc.getFullName().replace('.', '/'); } private static void checkClassName(String className) { ClassBuilder.checkClassName(className); } public static String getClassName(Class clazz) { return clazz.getName().replace('.', '/'); } public void switch_(int[] values, Label[] labels, Label defaultLabel) { int lo = values[0]; int hi = values[values.length-1]; long table_space_cost = 4 + ((long) hi - lo + 1); // words long table_time_cost = 3; // comparisons long lookup_space_cost = 3 + 2 * (long) values.length; long lookup_time_cost = values.length; if(values.length > 0 && table_space_cost + 3 * table_time_cost <= lookup_space_cost + 3 * lookup_time_cost) { Label[] table = new Label[hi - lo + 1]; for(int i=0,j=0;i