乐趣区

Neo编译器

上一篇区块链研究方案先整理一下 Neo 编译器的知识吧。
项目链接

Neo 本身是开源的,在 github 搜索就可以拿到源码,我为了方便调试,把所有代码都放在一起。https://github.com/benhaben/n…

Neo 是 C# 开发的,大部分代码可以跨平台,但是也有不能再 mac 上运行的代码,比如改装后的 leveldb 的代码,所以最好还是用 VisualStudio 在 windows 上调试研究较好。
本文档主要是总结自己所研究的东西,现在不太规范,主要考虑好理解,并不完善,后面也可以进一步规范化提交到 neo 项目中。

主框架
框架
大家可以看到 Compiler 所处的位置
Compiler 作用

在 Neo 区块链系统中,智能合约是一段代码,可以完成一定的逻辑,最后算出合约的结果。现在已经有很多具体的应用了,感兴趣的可以看一下基于 Neo 做的项目。
如果对比特币或者智能合约不了解,不知道为什么需要有这些代码,可以看一下比特币的白皮书。这里简单说一下,在比特币系统中,当一个“人”(可以看成一个公钥)需要和另一个“人”产生交易的时候,这段代码用来检查身份,分配比特币。具体的了解可以看一下普林斯顿的公开课
下面进入到 Neo compiler 的介绍了,前面所需的基础知识本文不在关注。

Compiler 的框架
下面的图主要显示代码的主要流程:
基本的流程

Neo 可以用各种语言写,不过现在主要是 C#。
Neo 的编译器主要是一个翻译器
C#代码被 C# 编译器编译成 MSIL,对 MSIL 的理解可以查看 Standard ECMA-335 Common Language Infrastructure (CLI)
Neo compiler 使用 Mono.Cecil 读取 IL
Neo 编译器只关注 C# 中的 static function,所以只是 C# 语言的一个超级阉割版
Neo 的编译器遍历 IL,根据语义转成 Neo 虚拟机的 opcode
至于 MSIL 向 neo.vm 的 opcode 怎么转,需要仔细研究 neo.vm 的 opcode 的设计

Compiler 工作一个具体事例
先看一段智能合约代码
这段代码没有什么实际的作用,就是返回 a +b,但是 main 可以接受参数。
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Services.Neo;

public class Sum : SmartContract
{
public static int Main(int a, int b)
{
return a + b;
}
}

MSIL
main function IL code
IL_0000 Nop
IL_0001 Ldarg_0
IL_0002 Ldarg_1
IL_0003 Add
IL_0004 Stloc_0
IL_0005 Br_S
IL_0007 Ldloc_0
IL_0008 Ret

这段代码很简单,就是读取参数 Ldarg_0,Add,返回。可以看到 CLR 的虚拟机也是堆栈虚拟机。关于基于栈的虚拟机和基于寄存器的虚拟机可以看一下这些文章:栈式虚拟机和寄存器式虚拟机?另外还有一篇概念讲解的很详细的文章虚拟机随谈(一):解释器,树遍历解释器,基于栈与基于寄存器,大杂烩
neo.compiler
为了感性的认识 neo 编译器做了什么,我们可以看一下上面的只能合约被翻译成了什么
hex:53-C5-6B-6C-76-6B-00-52-7A-C4-6C-76-6B-51-52-7A-C4-61-6C-76-6B-00-C3-6C-76-6B-51-C3-93-6C-76-6B-52-52-7A-C4-62-03-00-6C-76-6B-52-C3-61-6C-75-66

实际上是一串数字了,每个数字对应一个 vm 的操作码或者是数值,为了更好理解,把汇编代码放出来
PUSH4
PUSH3
RET
PUSH3
NEWARRAY
TOTALSTACK
FROMALSTACK
DUP
TOALTSTACK
PUSH0
PUSH2
ROLL
SETITEM
FROMALSTACK
DUP
TOTALSTACK
PUSH1
PUSH2
ROLL
SETITEM
NOP
FROMALSTACK
DUP
TOTALSTACK
PUSH0
PICKITEM
FROMALSTACK
DUP
TOTALSTACK
PUSH1
PICKITEM
ADD
FROMALSTACK
DUP
TOTALSTACK
PUSH2
PUSH2
ROLL
SETITEM
JMP
FROMALSTACK
DUP
TOTALSTACK
PUSH2
PICKITEM
NOP
FROMALSTACK
DROP
ret

neo 汇编的说明,可以查看这个文档
我们可以发现如下情况:

MSIL 的代码很短,Neo.VM 的代码很长,这是由于虚拟机的指令和能力不同造成的。我们只需要关注,汇编代码处理了局部变量的存贮获取,参数的传递,程序的退出,还有 add 指令。
汇编代码和 compiler 的生成算法相关,需要我们去同时研究 neo 的编译器和虚拟机,才能明白具体的细节。
具体每一行的含义,怎么执行的,可以查看这个文档
后面还会有一个讲解虚拟机的文章,到那个时候在仔细说明

neo.compiler 代码阅读指南
代码阅读还是很头痛的,所以做了两个脑图:

compiler 执行脑图
compiler 对象关系

对象关系

ILModule 是对 MSIL 的一个映射,包含模块,类型,函数,字段,函数中又包含返回值,参数,函数体,可以点开脑图一层一层查看。
Mono.Cecil 是使用来读取 MSIL 的,他也是对 MSIL 的一个映射,由于没有文档,只能看代码知道他的类结构了,这一部分在脑图中没有显示,不过没关系,compiler 会把感兴趣的代码转成 ILModule
ModuleConverter 用来遍历 ILModule,把里面的 MSIL 转成 Neo.VM 的代码,存贮在 NeoModule
具体两边的指令如何对应,真是需要一个一个理解的,非常繁琐,两边都有很多的指令。可以看看这个代码

总结
看完这个文章,并不能了解到具体的细节,具体的细节已经在代码中,这篇文章的主要目的是提供很多资料,提供大的框架,帮助对 Neo.Compiler 感兴趣的程序员加速阅读代码的速度。
作者:沈寅原文链接:https://www.jianshu.com/p/646…

退出移动版