NEO从入门到开窗 - NEO编译器

Posted Dexter Di

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NEO从入门到开窗 - NEO编译器相关的知识,希望对你有一定的参考价值。

一、啰嗦两句

第一节的时候咱说了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;
        }
    }

}
ConvertTask

好,先不管细节,就是起了一个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的内部机制。

 

以上是关于NEO从入门到开窗 - NEO编译器的主要内容,如果未能解决你的问题,请参考以下文章

NEO C# 合约编译器原理解析

Neo4J入门笔记[2]---导出数据为CSV

Neo编译器

Neo4j:具有多种关系的二部到单部投影

从 MySQL 导出到 Neo4j

neo4j入门