!include once #1019934 // BCEL sclass MethodMaker { replace Type with org.apache.bcel.generic.Type. ClassGen cg; MethodGen mg; new InstructionList il; ConstantPoolGen cp; InstructionFactory factory; int frameSize; Type[] argTypes; int[] argStackIndex; settable bool verboseAdd; delegate none, objValue, intValue, doubleValue to JVMStackCellType. bool classConstantWorkaround; *(ClassMaker classMaker, Class returnType, S methodName, Class... argumentTypes) { this(classMaker.cg, returnType, methodName, argumentTypes); } *(ClassGen cg, Class returnType, S methodName, Class... argumentTypes) { this(cg, Const.ACC_PUBLIC, returnType, methodName, argumentTypes); } // types: Class or S *(ClassGen *cg, short modifiers, O returnType, S methodName, O... argumentTypes) { cp = cg.getConstantPool(); factory = new InstructionFactory(cg); argTypes = wrapTypes(argumentTypes); mg = new MethodGen(modifiers, wrapType(returnType), argTypes, null /* argNames */, methodName, cg.getClassName(), il, cp); layoutStack(); } *(ClassGen *cg, short modifiers, Class returnType, S methodName, Class... argumentTypes) { cp = cg.getConstantPool(); factory = new InstructionFactory(cg); argTypes = wrapTypes(argumentTypes); mg = new MethodGen(modifiers, wrapType(returnType), argTypes, null /* argNames */, methodName, cg.getClassName(), il, cp); layoutStack(); } void layoutStack { frameSize = 1; // assume method is not static argStackIndex = new int[l(argTypes)]; for i over argTypes: { argStackIndex[i] = frameSize; frameSize += eqOneOf(argTypes[i], Type.LONG, Type.DOUBLE) ? 2 : 1; } } static Type[] wrapTypes(O[] classes) { Type[] types = new[l(classes)]; for i over classes: types[i] = wrapType(classes[i]); ret types; } static Type wrapType(O o) { if (o cast Class) ret classToBCELType(o); if (o cast S) { Class c = parsePrimitiveType(o); if (c != null) ret classToBCELType(c); ret new ObjectType(o); } null; } // create local variable and return its index int newLocalVar() { ret frameSize++; } selfType newObject(Class c, Class... argTypes) { il.append(factory.createNew(className(c))); il.append(InstructionConst.DUP); invokeConstructor(c, argTypes); this; } selfType invokeConstructor(Class c, Class... argTypes) { Constructor ctor = findConstructor_precise_onTypes(c, argTypes); il.append(factory.createInvoke(className(c), "", Type.VOID, wrapTypes(ctor.getParameterTypes()), Const.INVOKESPECIAL)); this; } selfType dup() { il.append(InstructionConst.DUP); this; } // store object in local variable selfType astore(int var) { il.append(new ASTORE(var)); this; } int argIdx(int iArg) { ret argStackIndex[iArg]; } selfType aloadArgWithAutoboxing(int iArg) { var type = argTypes[iArg]; int stackIdx = argStackIndex[iArg]; if (type == Type.BYTE) { il.append(new ILOAD(stackIdx)); invokeStatic(Byte.class, Byte.class, "valueOf", byte.class); this; } if (type == Type.BOOLEAN) { il.append(new ILOAD(stackIdx)); invokeStatic(Bool.class, Bool.class, "valueOf", bool.class); this; } if (type == Type.CHAR) { il.append(new ILOAD(stackIdx)); invokeStatic(Char.class, Char.class, "valueOf", char.class); this; } if (type == Type.SHORT) { il.append(new ILOAD(stackIdx)); invokeStatic(Short.class, Short.class, "valueOf", short.class); this; } if (type == Type.INT) { il.append(new ILOAD(stackIdx)); invokeStatic(Int.class, Int.class, "valueOf", int.class); this; } if (type == Type.LONG) { il.append(new LLOAD(stackIdx)); invokeStatic(Long.class, Long.class, "valueOf", long.class); this; } if (type == Type.FLOAT) { il.append(new LLOAD(stackIdx)); invokeStatic(Float.class, Float.class, "valueOf", float.class); this; } if (type == Type.DOUBLE) { il.append(new DLOAD(stackIdx)); invokeStatic(Double.class, Double.class, "valueOf", double.class); this; } ret aload(stackIdx); } selfType aload(int stackIdx) { il.append(new ALOAD(stackIdx)); this; } selfType stringConstant(S s) { il.append(new PUSH(cp, s)); this; } selfType classConstant(Class c) { if (classConstantWorkaround) { stringConstant(c.getName()); invokeStatic(Class.class, Class.class, "forName", S); } else { var ldc = new LDC(classRef(c)); assertEquals("classConstant", ldc.getValue(cp), wrapType(c)); il.append(ldc); } this; } selfType intConstant aka intConst(int i) { if (i >= -1 && i <= 5) ret add(new ICONST(i)); if (i == (byte) i) ret add(new BIPUSH((byte) i)); if (i == (short) i) ret add(new SIPUSH((short) i)); ret add(new LDC(cp.addInteger(i))); } selfType doubleConstant aka doubleConst(double d) { ret add(new LDC2_W(cp.addDouble(d))); } selfType boolConstant(bool b) { ret intConstant(b ? 1 : 0); } selfType invokeVirtual(Class c, Class returnType, S methodName, Class... argTypes) { Method m = findNonStaticMethod_precise_onTypes(c, methodName, argTypes); if (m == null) fail("Method not found: " + className(c) + "." + formatFunctionCall(methodName, argTypes) + " returning " + className(returnType)); il.append(factory.createInvoke(className(c), methodName, wrapType(m.getReturnType()), wrapTypes(m.getParameterTypes()), Const.INVOKEVIRTUAL)); this; } selfType invokeInterface(Class c, Class returnType, S methodName, Class... argTypes) { Method m = mostApplicableMethod_onTypes( filter(nonDefaultInterfaceMethods(c), _m -> _m.getName().equals(methodName)), argTypes); if (m == null) fail("Method not found: " + className(c) + "." + formatFunctionCall(methodName, argTypes) + " returning " + className(returnType)); il.append(factory.createInvoke(className(c), methodName, wrapType(m.getReturnType()), wrapTypes(m.getParameterTypes()), Const.INVOKEINTERFACE)); this; } selfType invokeStatic(Class c, Class returnType, S methodName, Class... argTypes) { Method m = findMethod_precise_onTypes(c, methodName, argTypes); if (m == null) fail("Method not found: " + className(c) + "." + formatFunctionCall(methodName, argTypes) + " returning " + className(returnType)); il.append(factory.createInvoke(className(c), methodName, wrapType(m.getReturnType()), wrapTypes(m.getParameterTypes()), Const.INVOKESTATIC)); this; } selfType areturn() { il.append(InstructionConst.ARETURN); this; } selfType _return() { il.append(InstructionConst.RETURN); this; } selfType returnPrimitive(Class type) { il.append(primitiveReturnInstruction(type)); this; } ReturnInstruction primitiveReturnInstruction(Class type) { assertTrue(isPrimitiveType(type)); if (type == long.class) ret InstructionConst.LRETURN; if (type == float.class) ret InstructionConst.FRETURN; if (type == double.class) ret InstructionConst.DRETURN; ret InstructionConst.IRETURN; } meta-for Instruction also as BranchInstruction { selfType add(Instruction i) { il.append(i); if (verboseAdd) print("> " + i); this; } A addAndReturn(A i) { add(i); ret i; } } void done() { mg.stripAttributes(true); mg.setMaxStack(); mg.setMaxLocals(); cg.addMethod(mg.getMethod()); } JVMStackCellType convertToObject(JVMStackCellType stackTop) { if (stackTop == objValue) {} else if (stackTop == JVMStackCellType.intValue) invokeStatic(Int, Int, "valueOf", int.class); else if (stackTop == JVMStackCellType.doubleValue) invokeStatic(Double.class, Double.class, "valueOf", double.class); else if (stackTop == JVMStackCellType.none) add(new ACONST_NULL); else fail("TODO: add conversion for stack cell type: " + stackTop); ret objValue; } void discardStackTop(JVMStackCellType stackTop) { if (stackTop == JVMStackCellType.none) {} else if (stackTop == JVMStackCellType.doubleValue || stackTop == JVMStackCellType.longValue) add(new POP2); else add(new POP); } int classRef(O c) { ret cp.addClass((ObjectType) wrapType(assertNotNull(c))); } selfType checkCast(O c) { ret add(new CHECKCAST(classRef(c))); } selfType loadNull() { ret add(new ACONST_NULL); } InstructionHandle here() { ret il.append(new NOP); } GOTO forwardGoto() { ret addAndReturn(new GOTO(null)); } void returnWithType(JVMStackCellType stackTop) { if (stackTop == JVMStackCellType.objValue) areturn(); else if (stackTop == JVMStackCellType.intValue) add(new IRETURN); else if (stackTop == JVMStackCellType.doubleValue) add(new DRETURN); else if (stackTop == JVMStackCellType.none) _return(); else fail("TODO: add return for stack cell type: " + stackTop); } void getStaticField(S className, S fieldName, Class type) { il.append(factory.createGetStatic(className, fieldName, wrapType(type)); } }