手拉手教你实现一门编程语言 Enkel, 系列 16
2018-09-08 本文已影响0人
KevinOfNeu
本文系 Creating JVM language 翻译的第 16 篇。
原文中的代码和原文有不一致的地方均在新的代码仓库中更正过,建议参考新的代码仓库。
源码
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");
}
}