手拉手教你实现一门编程语言 Enkel, 系列 16

本文系 Creating JVM language 翻译的第 16 篇。
原文中的代码和原文有不一致的地方均在新的代码仓库中更正过,建议参考新的代码仓库。

源码

Github

OOP 和 statics

面向对象语言中最大的优点是啥?我个人认为是多态, 如何实现多态,使用继承呗,可以在静态语义下使用继承么?肯定不行呀。

在我认为,静态语义违反了面向对象的思想,不应该出现在面向对象中,为了避免使用多态,你可以用单例呀。

因此,为何有 static 情况下,Java 还声称自己是面向对象的呢?我认为,Java 是为了迎合 C++ 更加方便的接纳 Java 才引入的历史因素。

去掉 static

直到上一篇博客,Enkel 中一直存在 static。包括 main 方法和其他的静态方法,这么是为了方便实现语言的其他特性,比如变量,条件表达式,循环,方法调用,然后才转成 OO。

那么我们来实现没有静态的 OO 吧。

main 方法

static main 方法需要 Java 程序员手动编写。Enkel 是这样处理的:

  • 编译器自动生成
  • 在 main 方法中,用默认的构造器创建一个对象
  • 然后调用 start 方法
  • 程序员需要提供 start 方法定义
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);
 }

start 方法是非静态的,但其实是 main 方法的一种变体。

INVOSTATIC vs INVOKEVIRTUAL

在第七部分,我用了 INVOKESTATIC 来做方法调用,是时候换成 INVOKEVIRTUAL 了。

INVOKEVIRTUAL 有一点和 INVOKESTATIC 有很大的差异,INVOKEVIRTUAL 需要一个所有者,INVOKESTATIC 从栈中出栈参数,INVOKEVIRTUAL 首先是把所有者出栈,然后才是出栈参数。

如果没有显示的提供所有者信息,默认用 this。


//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 
}
//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); 
}

示例

如下 Enkel 代码:

HelloStart {

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

生成字节码:

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."<init>":()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 "<init>":()V - call constructor
       7: invokevirtual #27                 // Method start:()V
      10: return
}

与之对应 Java 类如下:

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");
    }

}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,839评论 18 399
  • 第6章类文件结构 6.1 概述 6.2 无关性基石 6.3 Class类文件的结构 java虚拟机不和包括java...
    kennethan阅读 995评论 0 2
  • 成长就像是一片郁葱的森林。里面有让人喜爱的草林;有让人恐惧的凶猛怪兽;也有迷惑我们的樱树花。一旦进入这座森林,就会...
    凉字拖阅读 137评论 0 0
  • 你以为你有很多路可以选择,但是在你周围有很多看不见的墙。 ——今何在 山桃熟了几次,海...
    y居合细雪阅读 262评论 0 2
  • 1893年12月28日 新中国第一任大法官张志让诞辰 1895年12月28日 世界第一部电影诞生 1903年12月...
    逸香居主白龍阅读 270评论 0 0