Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

346
LINES

< > BotCompany Repo | #1034309 // MethodMaker [BCEL helper]

JavaX fragment (include) [tags: use-pretranspiled]

Uses 679K of libraries. Click here for Pure Java version (14251L/81K).

!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), "<init>",
      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 extends Instruction> 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));
  }
}

Author comment

Began life as a copy of #1034300

download  show line numbers  debug dex  old transpilations   

Travelled to 4 computer(s): bhatertpkbcr, ekrmjmnbrukm, mowyntqkapby, mqqgnosmbjvj

No comments. add comment

Snippet ID: #1034309
Snippet name: MethodMaker [BCEL helper]
Eternal ID of this version: #1034309/100
Text MD5: 8bc08537462734c591d36bb396d28b74
Transpilation MD5: bba3e38fc737735f2b3463fe3ac01d01
Author: stefan
Category: javax / byte code generation
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2022-12-18 03:55:00
Source code size: 9936 bytes / 346 lines
Pitched / IR pitched: No / No
Views / Downloads: 409 / 991
Version history: 99 change(s)
Referenced in: [show references]