本文系 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 方法定义
1
2
3
4
5
6
7
8
9
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。

1
2
3
4
5
6
7
8
9
10
11
12
13

//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
}
1
2
3
4
5
6
7
8
9
10
//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 代码:

1
2
3
4
5
6
HelloStart {

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

生成字节码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 类如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
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");
}

}