一、啰嗦两句
第一节的时候咱说了C#编译完了之后,就该NEO的编译器搞事情了。我们完全可以按这个节奏搞,手动用NEO的编译器neon编译dll文件生成指令码文件.avm。但是NEO团队给我们写智能合约提供了个插件,咱们就先看看这玩意怎么玩,然后简单的介绍一下NEO的编译器源码。
二、NeoContractPlugin
咱在VS里【工具】-》【扩展和更新】里搜下NeoContractPlugin,安装这个模板。
完事新建项目时候就能新建一个NeoContract的项目了
创建完之后,就是这样滴。
好,按NEO的模板创建出来的东东,多了build.tasks和Neo.ConvertTask.dll这俩文件,除此之外,在项目文件里还导入了一个project和AfterBuild之后的任务。这里做的事情,其实就是告诉项目编译完了之后跑一个ConvertTask的任务,看到项目里的Neo.ConvertTask.dll了么,后面我们介绍它。
ConvertTask这个任务是干嘛的,我们来看一眼源码,源码在项目neo-devpack-dotnet中
using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using System.Diagnostics;namespace Neo {/// <summary>/// this class generate config / xml file by template/// </summary>public class ConvertTask : Task{[Required]public ITaskItem DataSource { get; set; }/// <summary>/// execute replace logic/// </summary>/// <returns>ture successful, false failure</returns>public override bool Execute(){var srcdll = this.DataSource.ToString();string dllname = System.IO.Path.GetFileName(srcdll);ProcessStartInfo pinfo = new ProcessStartInfo();pinfo.FileName = "cmd.exe";pinfo.WorkingDirectory = System.IO.Path.GetDirectoryName(srcdll);pinfo.UseShellExecute = false;pinfo.RedirectStandardInput = true;pinfo.RedirectStandardOutput = true;pinfo.CreateNoWindow = true;pinfo.StandardOutputEncoding = System.Text.Encoding.UTF8;Process p = Process.Start(pinfo);p.StandardInput.AutoFlush = true;p.StandardInput.WriteLine("neon " + dllname);p.StandardInput.WriteLine("exit");//前四行后一行不要string lastline = null;int count = 0;bool bSucc = false;while (p.StandardOutput.EndOfStream == false){var line = p.StandardOutput.ReadLine();count++;if (count <= 4) continue;if (lastline != null && lastline.Length > 0){if (lastline[0] == '<'){if (lastline.IndexOf("<WARN>") == 0){this.Log.LogWarning(lastline.Substring(6));lastline = line;continue;}else if (lastline.IndexOf("<ERR>") == 0){this.Log.LogError(lastline.Substring(5));lastline = line;continue;}else if (lastline.IndexOf("<WARN|") == 0){var l = lastline.Substring(6);var ine = lastline.IndexOf(">");var text = lastline.Substring(ine + 1);var file = lastline.Substring(6, ine - 6);var lines = file.Split(new char[] { '(', ')' });int _line = 0;if (lines.Length > 1){int.TryParse(lines[1], out _line);}this.Log.LogWarning("", "", "", lines[0], _line, 0, 0, 0, text);lastline = line;continue;}else if (lastline.IndexOf("<ERR|") == 0){var l = lastline.Substring(5);var ine = lastline.IndexOf(">");var text = lastline.Substring(ine + 1);var file = lastline.Substring(5, ine - 5);var lines = file.Split(new char[] { '(', ')' });int _line = 0;if (lines.Length > 1){int.TryParse(lines[1], out _line);}this.Log.LogWarning("", "", "", lines[0], _line, 0, 0, 0, text);lastline = line;continue;}}if (lastline.IndexOf("SUCC") == 0){bSucc = true;}this.Log.LogMessageFromText(lastline, MessageImportance.High);}lastline = line;//lines.Add(line); }//this.Log.LogMessageFromText(lastline, MessageImportance.High); p.WaitForExit();return bSucc;}}}
好,先不管细节,就是起了一个cmd进程跑neon,其实就是帮我们调了一下NEO的编译器,妥了,那咱们就看看编译器搞了些撒事情吧。
三、NEO编译器
这一部分最好还是看看源码,我就简单介绍一下。这里有三个类比较关键:
1. ModuleDefination: 这个类是用的库Mono.cecil,这个库可以查看或者修改IL文件,这里是官方地址:http://www.mono-project.com/docs/tools+libraries/libraries/Mono.Cecil/
2. ILModule: 这个是编译器里定义的一个类,类的主要方法就是LoadModule,一看就明白了,其实现正是使用ModuleDefination加载并读取IL文件,将其转化为ILModule。这里其实还是用一种数据类型记录的智能合约代码的元数据信息,方法、字段、属性等的描述信息,可以理解成我们用反射操作类型。
3. NeoModule: 这个是另外一个数据结构,通过一个ModuleConverter的类从ILModule逐条指令转化进NeoModule,这个结构里保持了一个有序字典,value就是转化后的指令码,也就是我们说的OpCode。之后就是把这个NeoModule Build成字节数组写入.avm文件。
理解这三个类的含义就明白编译器的流程了。这里的难点是指令码,非常多,所以需要详细了解的自己看下源码吧。
四、小结
好了,今天就简单讲了一下NEO的智能合约模板背后都搞了啥,以及NEO编译器大概怎么编译的。通过前面几讲,大伙儿应该从工程的角度对NEO的智能合约这部分有些了解了。
后续,准备从NEO的命令行全节点Neo-cli入手讲讲NEO的内部机制。