This article is the 14th English translation of Creating JVM Language. The inconsistency between the original code and the original code has been corrected in the new code repository. We suggest you refer to the new code repository.

The source code

Github

Support for new types

So far Enkel only supports integer and string types. It’s time to support other primitive types. This is also a preparation for creating object-oriented features.

Instruction set abstraction

There are many bytecode instructions that differ only in their data type. Take the return instruction for example:

  • Return – Method returns
  • Ireturn – Returns the interger
  • Freturn – Returns float
  • Dreturn – Returns double
  • Lreturn – Returns long
  • Areturn – Return reference

Bytecode generation, we can write a little bit more case statement can be implemented, but very ugly. So I use the TypeSpecificOpcodes enumeration to store the bytecode instructions corresponding to all types:

public enum TypeSpecificOpcodes { INT (ILOAD, ISTORE, IRETURN,IADD,ISUB,IMUL,IDIV), / / values (127127) - one byte) LONG (LLOAD LSTORE, LRETURN, LADD, LSUB, LMUL, LDIV), FLOAT (FLOAD FSTORE, FRETURN FADD, FSUB FMUL, FDIV), DOUBLE (DLOAD, DSTORE DRETURN, DADD, DSUB, DMUL, DDIV), VOID (,0,0,0 ALOAD, ASTORE, RETURN, 0). OBJECT (ALOAD, ASTORE, ARETURN, 0,0,0,0); TypeSpecificOpcodes(int load, int store, int ret, int add, int sub, int mul, int div) { //assign each parameter to the field } //gettersCopy the code

Type specific bytecode instructions, currently we use:

  • Load – Loads variables from a local variable table
  • Store – Stores to local variable tables
  • Ret – return
  • Add – Add two numbers in the operand stack
  • Subtraction of operands in a sub-stack
  • Mul – Multiplication of operands in the stack
  • Div – Division of operands on the stack

TypeSpecificOpcodes are in the BultInType class:

public enum BultInType implements Type {
    BOOLEAN("bool",boolean.class,"Z", TypeSpecificOpcodes.INT), //other members BultInType(String name, Class<? >typeClass, String descriptor, TypeSpecificOpcodes opcodes) {
        //assign to fields
    }
    
    @Override
    public int getMultiplyOpcode() {
        return opcodes.getMultiply();
    }
Copy the code

Whenever two numbers are multiplied, just know the type and never look up the bytecode instructions for the type:

public void generate(Multiplication expression) {
    evaluateArthimeticComponents(expression);
    Type type = expression.getType();
    methodVisitor.visitInsn(type.getMultiplyOpcode());
}
Copy the code

The sample

Enkel code:

main(string[] args) {
        var stringVar = "str"
        var booleanVar = true
        var integerVar = 2745 + 33 Var doublevars = 23.0 + doubleVar}Copy the code

Compiled bytecode:

public class AllPrimitiveTypes {
  public static void main(java.lang.String[]);
    Code:
       0: ldc           #8 // String str
       2: astore_1                          //store it variable
       3: ldc           #9 // int 1 - bool values are represented as ints in JVM
       5: istore_2                          //store as int 
       6: ldc           #10 // int 2745
       8: ldc           #11 // int 33
      10: iadd                              // iadd - add integers
      11: istore_3                          //store result in integer varaible
      12: ldc           # 12 / / float 2343.05 f
      14: fstore        4                   //store in float variable
      16: ldc           # 13 / / float 23.0 f
      18: fload         4                   //load integer varaible (from index 4)
      20: fadd                              //add float variables
      21: fstore        5                   //store float result
      23: return
}
Copy the code