This article is the 16th 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

The OOP and the statics

What are the biggest advantages of object-oriented languages? I personally think polymorphism, how do you implement polymorphism, use inheritance, can you use inheritance in static semantics? Certainly not.

In my opinion, static semantics violate object orientation and should not be used in object orientation. To avoid using polymorphism, you can use singletons.

So why does Java claim to be object-oriented when it is static? I think Java is a historical element that was introduced to cater to the more convenient adoption of Java by C++.

Get rid of the static

Up until the last blog post, static was present in Enkel. Including main methods and other static methods to make it easier to implement other features of the language, such as variables, conditional expressions, loops, and method calls, before transiting to OO.

So let’s implement OO without static.

The main method

The static main method requires Java programmers to write it manually. Enkel deals with it like this:

  • Automatically generated by the compiler
  • In the main method, an object is created with the default constructor
  • The start method is then called
  • The programmer needs to provide the start method definition
private Function getGeneratedMainMethod() {
     FunctionParameter args = new FunctionParameter("args", BultInType.STRING_ARR, Optional.empty());
     FunctionSignature functionSignature = new FunctionSignature("main", Collections.singletonList(args), BultInType.VOID);
     ConstructorCall constructorCall = new ConstructorCall(scope.getClassName());
     FunctionSignature startFunSignature = new FunctionSignature("start", Collections.emptyList(), BultInType.VOID);
     FunctionCall startFunctionCall = new FunctionCall(startFunSignature, Collections.emptyList(), scope.getClassType());
     Block block = new Block(new Scope(scope), Arrays.asList(constructorCall,startFunctionCall));
     return new Function(functionSignature, block);
 }
Copy the code

The start method is non-static, but is actually a variation of the main method.

INVOSTATIC vs INVOKEVIRTUAL

In Part 7, I used INVOKESTATIC for method calls, so it’s time to switch to INVOKEVIRTUAL.

INVOKEVIRTUAL is very different from INVOKESTATIC in that it requires an owner. INVOKESTATIC pushes the owner off the stack first, and then INVOKESTATIC pushes the owner off the stack.

If no owner information is displayed, this is used by default.

//Mapping antlr generated FunctionCallContext to FunctionCall @Override public Expression visitFunctionCall(@NotNull EnkelParser.FunctionCallContext ctx) { //other stuff boolean ownerIsExplicit = ctx.owner ! = null;if(ownerIsExplicit) {
        Expression owner = ctx.owner.accept(this);
        return new FunctionCall(signature, arguments, owner);
    }
    ClassType thisType = new ClassType(scope.getClassName());
    return new FunctionCall(signature, arguments, new VarReference("this",thisType)); //pass "this" as a owner 
}
Copy the code
//Generating bytecode using mapped FunctionCall object
public void generate(FunctionCall functionCall) {
    functionCall.getOwner().accept(this); //generate owner (pushses it onto stack)
    generateArguments(functionCall);  //generate arguments
    String functionName = functionCall.getIdentifier();
    String methodDescriptor = DescriptorFactory.getMethodDescriptor(functionCall.getSignature());
    String ownerDescriptor = functionCall.getOwnerType().getInternalName();
    //Consumes owner and arguments off the stack
    methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, ownerDescriptor, functionName, methodDescriptor, false); 
}
Copy the code

The sample

Enkel code:

HelloStart {

    start {
        print "Hey I am non-static 'start' method"}}Copy the code

Generate bytecode:

public class HelloStart {
  public void start();
    Code:
       0: getstatic     #12 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #14 // String Hey I am non-static 'start' method
       5: invokevirtual #19 // Method "Ljava/io/PrintStream;" .println:(Ljava/lang/String;) V
       8: return

  //Constructor
  public HelloStart();
    Code:
       0: aload_0   //get "this"
       1: invokespecial #22 // Method java/lang/Object."
      
       ":()V - call super
      
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2 // class HelloStart - create new object
       3: dup       //duplicate new object so that invokespecial does not consumes it
       4: invokespecial #25 // Method "
      
       ":()V - call constructor
      
       7: invokevirtual #27 // Method start:()V
      10: return
}
Copy the code

The corresponding Java class is as follows:

public class HelloStart {
    public HelloStart() {
    }

    public static void main(String[] var0) {
        (new HelloStart()).start();
    }
    
    public void start() {
        System.out.println("Hey I am non-static \'start\' method"); }}Copy the code