前言
把修改后的上传到了我的仓库
https://github.com/Wooyme/NMSL
顺便取了个意义不明的名字Naive Mind Specialize Language
,简称nmsl。
这篇先写一点原理相关的东西,然后就是lambda。
编译期与运行期
开发语言和开发应用最大的区别就是语言开发中我们要预想的情况并不是程序的运行情况而是语言编译的情况。把运行和编译的逻辑理清楚后就能理解很多东西了。
英语比较好的话可以看一下
https://www.youtube.com/watch?v=FJY96_6Y3a4
《 One VM to Rule Them All, One VM to Bind Them》
是Oracle Labs的人做的演讲。比较尴尬的是没有英文字幕,只能完全靠听力了。
Node
首先我们看到parser
包下的SLNodeFactory
里有大量的createXXX
的方法,而在nodes
目录下都是继承了Node
的类。了解过编译原理的朋友肯定知道编译器首先要解析语法然后生成相应的语法树,那么既然有树就肯定会有节点,而Node
类代表的就是这些节点。
SimpleLanguage
中有builtin
,expression
,controlflow
,local
这4个包,分别包含了内置函数,表达式,控制流和局部读写的节点。
Expression
Expression
是比较简单的一种节点,功能上和builtin
类似,但是与语法解析过程的联系更加紧密一些。
首先以SLBigIntegerLiteralNode.java
为例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @NodeInfo(shortName = "const") public final class SLBigIntegerLiteralNode extends SLExpressionNode {
private final SLBigNumber value;
public SLBigIntegerLiteralNode(BigDecimal value) { this.value = new SLBigNumber(value); }
@Override public SLBigNumber executeGeneric(VirtualFrame frame) { return value; } }
|
这个是涉及code generate最少的一个类了,所以就以它为例。SLBigIntegerLiteralNode
总共就一个构造函数和一个executeGeneric
方法。这里就出现了一个生命周期的问题。SLBigIntegerLiteralNode在生成语法树的过程中被实例化而executeGeneric是在代码运行的时候被调用
所以就有了SLBigIntegerLiteralNode
中的操作,在生成语法树的过程中把常量value
保存在类里,然后在executeGeneric
中直接返回这个保存的值。
更复杂的Expression
以SLWritePropertyNode
为例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| @NodeInfo(shortName = ".=") @NodeChild("receiverNode") @NodeChild("nameNode") @NodeChild("valueNode") public abstract class SLWritePropertyNode extends SLExpressionNode {
static final int LIBRARY_LIMIT = 3;
@Specialization(guards = "arrays.hasArrayElements(receiver)", limit = "LIBRARY_LIMIT") protected Object write(Object receiver, Object index, Object value, @CachedLibrary("receiver") InteropLibrary arrays, @CachedLibrary("index") InteropLibrary numbers) { try { arrays.writeArrayElement(receiver, numbers.asLong(index), value); } catch (UnsupportedMessageException | UnsupportedTypeException | InvalidArrayIndexException e) { throw SLUndefinedNameException.undefinedProperty(this, index); } return value; }
@Specialization(limit = "LIBRARY_LIMIT") protected Object write(Object receiver, Object name, Object value, @CachedLibrary("receiver") InteropLibrary objectLibrary, @Cached SLToMemberNode asMember) { try { objectLibrary.writeMember(receiver, asMember.execute(name), value); } catch (UnsupportedMessageException | UnknownIdentifierException | UnsupportedTypeException e) { throw SLUndefinedNameException.undefinedProperty(this, name); } return value; } }
|
首先看到的是多了几个NodeChild
的类注解以及Specialization
的方法注解。刚刚也提到了code generate,所以这两个注解很明显就是和代码生成相关的。比较一下和上面的区别。发现SLWritePropertyNode
是abstract的也没有实现executeGeneric
方法,这就说明生成的代码为我们做了不少事情。
代码生成器生成的代码就不贴了,转述一下《 One VM to Rule Them All, One VM to Bind Them》里的话:
@NodeChild("receiverNode")
会生成private final Node receiverNode;
同时会生成create(Node receiverNode)
方法,而三个NodeChild
自然就会生成三个这样的属性和三个create的参数。
与这些node相对的是带有Specialization
注解的方法的参数,这个对应关系只与顺序相关,与名称是无关的。在运行过程中,生成的代码会调用三个子Node的executeGeneric
方法得到值,然后传入到带有Specialization
注解的方法中。
Node总结
与ExpressionNode
相同,其他几类Node的生命周期也是这样。不过有些细节上的区别。比如Builtins
会在SLContext
里被提前install。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| private void installBuiltins() { installBuiltin(SLReadlnBuiltinFactory.getInstance()); installBuiltin(SLPrintBuiltinFactory.getInstance()); installBuiltin(SLNanoTimeBuiltinFactory.getInstance()); installBuiltin(SLDefineFunctionBuiltinFactory.getInstance()); installBuiltin(SLStackTraceBuiltinFactory.getInstance()); installBuiltin(SLNewObjectBuiltinFactory.getInstance()); installBuiltin(SLEvalBuiltinFactory.getInstance()); installBuiltin(SLGetSizeBuiltinFactory.getInstance()); installBuiltin(SLHasSizeBuiltinFactory.getInstance()); installBuiltin(SLIsExecutableBuiltinFactory.getInstance()); installBuiltin(SLIsNullBuiltinFactory.getInstance()); installBuiltin(SLWrapPrimitiveBuiltinFactory.getInstance()); installBuiltin(SLMembersBuiltinFactory.getInstance()); installBuiltin(SLOpenBuiltinFactory.getInstance()); installBuiltin(SLWriteBuiltinFactory.getInstance()); installBuiltin(SLCloseBuiltinFactory.getInstance()); installBuiltin(SLReadBuiltinFactory.getInstance()); installBuiltin(SLSleepBuiltinFactory.getInstance()); installBuiltin(SLInterfaceBuiltinFactory.getInstance()); installBuiltin(SLToIntNodeFactory.getInstance()); installBuiltin(SLFromProxyBuiltinFactory.getInstance()); installBuiltin(SLUnInterfaceBuiltinFactory.getInstance()); }
|
运行期
可以看到在SLWritePropertyNode
中有这样一个方法。
1 2 3 4 5 6 7 8 9 10 11
| @Specialization(guards = "arrays.hasArrayElements(receiver)", limit = "LIBRARY_LIMIT") protected Object write(Object receiver, Object index, Object value, @CachedLibrary("receiver") InteropLibrary arrays, @CachedLibrary("index") InteropLibrary numbers) { try { arrays.writeArrayElement(receiver, numbers.asLong(index), value); } catch (UnsupportedMessageException | UnsupportedTypeException | InvalidArrayIndexException e) { throw SLUndefinedNameException.undefinedProperty(this, index); } return value; }
|
这里出现了InteropLibrary
这个类型,根据注解可以看出来,InteropLibrary
和前面的Object
是有关的。这里具体是什么关系很难直接说明,但是可以看到在SLObjectType
与SLBigNumber
这两个类的注解中均出现了InteropLibrary.class
。这就是Truffle API让人用着很不爽的地方,它大量的使用了注解和代码生成,让代码结构非常混乱,而且还找不到文档。如果不看完那3个小时的视频很多地方就很难理解。
现在已知的是,可以从receiver
和index
中产生它们对应的InteropLibrary
,也就是这里的arrays
和numbers
。
然后在@Specialization
的注解中可以看到guards
属性,从字面意思上就可以看出这个属性会生成用于保证方法正确运行的代码。因为这个write
方法是要向array写入,所以这里的receiver
就必须是array,于是就有了guards里的代码,保证receiver是有数组元素的。
但是!事实上,我们想要的往往是List
,而不是一个Array
,所以为了实现往List
里写入元素只要加入这样一个方法。
1 2 3 4 5 6 7 8 9 10
| @Specialization(limit = "LIBRARY_LIMIT") protected Object write(List receiver, Object index, Object value, @CachedLibrary("index") InteropLibrary numbers){ try { receiver.set(numbers.asInt(index),value); } catch (UnsupportedMessageException | IndexOutOfBoundsException e) { throw SLUndefinedNameException.undefinedProperty(this, index); } return value; }
|
这里问题就来了,List
是一个已知的类型,而前面的Object
传进来的很明显也应该是一个已知的类型。那么这个Object
会是什么呢?
DynamicObject
事实上,这个Object对应的是DynamicObject
,而DynamicObject
的最终实现则是runtime
包下的SLObjectType
。能看出SLObjectType
即是DynamicObject
的最直接证据就是SLObjectType
的类注解
1
| @ExportLibrary(value = InteropLibrary.class, receiverType = DynamicObject.class)
|
这个连接过程有大量的代码生成来实现。
可以看到SLObjectType
实现了hasArrayElements
,hasMembers
等方法,还有GetMembers
,ReadMember
,WriteMember
这些子类。从字面意思上都可以看出这些东西的功能,但是在代码上完全看不出它们与DynamicObject
或是InteropLibrary
的关系。
如果想要知道SLObjectType
中这些方法与InteropLibrary
的关系,可以看com.oracle.truffle.api.interop.InteropLibaray
的源码。
小结
花了很大的篇幅写ExpressionNode,所以Lambda放到下一篇。