diff --git a/src/Neo.Compiler.MSIL/FuncExport.cs b/src/Neo.Compiler.MSIL/FuncExport.cs index 1472025c0..1869df095 100644 --- a/src/Neo.Compiler.MSIL/FuncExport.cs +++ b/src/Neo.Compiler.MSIL/FuncExport.cs @@ -81,19 +81,18 @@ public static MyJson.JsonNode_Object Export(NeoModule module, byte[] script) outjson.SetDictValue("hash", sb.ToString()); //entrypoint - outjson.SetDictValue("entrypoint", "Main"); + var entryPoint = "Main"; var mainmethod = module.mapMethods[module.mainMethod]; if (mainmethod != null) { - var name = mainmethod.displayName; - outjson.SetDictValue("entrypoint", name); + entryPoint = mainmethod.displayName; } + //functions - var funcsigns = new MyJson.JsonNode_Array(); - outjson["functions"] = funcsigns; + var methods = new MyJson.JsonNode_Array(); + outjson["methods"] = methods; List names = new List(); - foreach (var function in module.mapMethods) { var mm = function.Value; @@ -101,15 +100,16 @@ public static MyJson.JsonNode_Object Export(NeoModule module, byte[] script) continue; if (mm.isPublic == false) continue; - var ps = mm.name.Split(new char[] { ' ', '(' }, StringSplitOptions.RemoveEmptyEntries); - var funcsign = new MyJson.JsonNode_Object(); - funcsigns.Add(funcsign); - var funcname = ps[1]; - if (funcname.IndexOf("::") > 0) + var funcsign = new MyJson.JsonNode_Object(); + if (function.Value.displayName == entryPoint) { - var sps = funcname.Split(new string[] { "::" }, StringSplitOptions.RemoveEmptyEntries); - funcname = sps.Last(); + // This is the entryPoint + outjson.SetDictValue("entryPoint", funcsign); + } + else + { + methods.Add(funcsign); } funcsign.SetDictValue("name", function.Value.displayName); if (names.Contains(function.Value.displayName)) @@ -133,7 +133,8 @@ public static MyJson.JsonNode_Object Export(NeoModule module, byte[] script) } var rtype = ConvType(mm.returntype); - funcsign.SetDictValue("returntype", rtype); + funcsign.SetDictValue("returnType", rtype); + funcsign.SetDictValue("readOnly", function.Value.isReadOnly); } //events @@ -142,10 +143,7 @@ public static MyJson.JsonNode_Object Export(NeoModule module, byte[] script) foreach (var events in module.mapEvents) { var mm = events.Value; - - var ps = mm.name.Split(new char[] { ' ', '(' }, StringSplitOptions.RemoveEmptyEntries); var funcsign = new MyJson.JsonNode_Object(); - eventsigns.Add(funcsign); funcsign.SetDictValue("name", events.Value.displayName); diff --git a/src/Neo.Compiler.MSIL/MSIL/Conv_Multi.cs b/src/Neo.Compiler.MSIL/MSIL/Conv_Multi.cs index 14e58d966..9e3d46ba4 100644 --- a/src/Neo.Compiler.MSIL/MSIL/Conv_Multi.cs +++ b/src/Neo.Compiler.MSIL/MSIL/Conv_Multi.cs @@ -984,7 +984,6 @@ private bool TryInsertMethod(NeoModule outModule, Mono.Cecil.MethodDefinition me var _method = type.methods[method.FullName]; try { - NeoMethod nm = new NeoMethod(); if (method.FullName.Contains(".cctor")) { CctorSubVM.Parse(_method, this.outModule); @@ -996,22 +995,10 @@ private bool TryInsertMethod(NeoModule outModule, Mono.Cecil.MethodDefinition me return false; //continue; } - nm._namespace = method.DeclaringType.FullName; - nm.name = method.FullName; - nm.displayName = method.Name; - Mono.Collections.Generic.Collection ca = method.CustomAttributes; - foreach (var attr in ca) - { - if (attr.AttributeType.Name == "DisplayNameAttribute") - { - nm.displayName = (string)attr.ConstructorArguments[0].Value; - } - } - nm.inSmartContract = method.DeclaringType.BaseType.Name == "SmartContract"; - nm.isPublic = method.IsPublic; + + NeoMethod nm = new NeoMethod(method); this.methodLink[_method] = nm; outModule.mapMethods[nm.name] = nm; - ConvertMethod(_method, nm); return true; } diff --git a/src/Neo.Compiler.MSIL/MSIL/Converter.cs b/src/Neo.Compiler.MSIL/MSIL/Converter.cs index 8f61c9ed3..1d9b20de1 100644 --- a/src/Neo.Compiler.MSIL/MSIL/Converter.cs +++ b/src/Neo.Compiler.MSIL/MSIL/Converter.cs @@ -85,27 +85,14 @@ public NeoModule Convert(ILModule _in, ConvOption option = null) if (m.Value.method == null) continue; if (m.Value.method.IsAddOn || m.Value.method.IsRemoveOn) continue;//event 自动生成的代码,不要 - NeoMethod nm = new NeoMethod(); if (m.Key.Contains(".cctor")) { CctorSubVM.Parse(m.Value, this.outModule); continue; } if (m.Value.method.IsConstructor) continue; - nm._namespace = m.Value.method.DeclaringType.FullName; - nm.name = m.Value.method.FullName; - nm.displayName = m.Value.method.Name; - Mono.Collections.Generic.Collection ca = m.Value.method.CustomAttributes; - foreach (var attr in ca) - { - if (attr.AttributeType.Name == "DisplayNameAttribute") - { - nm.displayName = (string)attr.ConstructorArguments[0].Value; - } - } - nm.inSmartContract = m.Value.method.DeclaringType.BaseType.Name == "SmartContract"; - nm.isPublic = m.Value.method.IsPublic; + NeoMethod nm = new NeoMethod(m.Value.method); this.methodLink[m.Value] = nm; outModule.mapMethods[nm.name] = nm; } @@ -114,14 +101,7 @@ public NeoModule Convert(ILModule _in, ConvOption option = null) { if (e.Value.isEvent) { - NeoEvent ae = new NeoEvent - { - _namespace = e.Value.field.DeclaringType.FullName, - name = e.Value.field.DeclaringType.FullName + "::" + e.Key, - displayName = e.Value.displayName, - returntype = e.Value.returntype, - paramtypes = e.Value.paramtypes - }; + NeoEvent ae = new NeoEvent(e.Value); outModule.mapEvents[ae.name] = ae; } else if (e.Value.field.IsStatic) diff --git a/src/Neo.Compiler.MSIL/MSIL/ILModule.cs b/src/Neo.Compiler.MSIL/MSIL/ILModule.cs index c44c1b62c..54ffc8e27 100644 --- a/src/Neo.Compiler.MSIL/MSIL/ILModule.cs +++ b/src/Neo.Compiler.MSIL/MSIL/ILModule.cs @@ -55,7 +55,6 @@ public void LoadModule(System.IO.Stream dllStream, System.IO.Stream pdbStream) } } - } } } @@ -91,7 +90,6 @@ public ILType(ILModule module, Mono.Cecil.TypeDefinition type, ILogger logger) } } } - } public class ILField diff --git a/src/Neo.Compiler.MSIL/NeoModule.cs b/src/Neo.Compiler.MSIL/NeoModule.cs index d61d2fad6..5e57f87f9 100644 --- a/src/Neo.Compiler.MSIL/NeoModule.cs +++ b/src/Neo.Compiler.MSIL/NeoModule.cs @@ -1,3 +1,5 @@ +using Mono.Cecil; +using Neo.Compiler.MSIL; using System.Collections.Generic; using System.Text; @@ -5,12 +7,17 @@ namespace Neo.Compiler { public class NeoModule { - public NeoModule(ILogger logger) - { - } + public NeoModule(ILogger logger) { } + public string mainMethod; + public ConvOption option; + public Dictionary mapMethods = new Dictionary(); + public Dictionary mapEvents = new Dictionary(); + public Dictionary mapFields = new Dictionary(); + public Dictionary staticfields = new Dictionary(); //小蚁没类型,只有方法 public SortedDictionary total_Codes = new SortedDictionary(); + public byte[] Build() { List bytes = new List(); @@ -26,13 +33,7 @@ public byte[] Build() return bytes.ToArray(); //将body链接,生成this.code byte[] //并计算 this.codehash byte[] - } - public string mainMethod; - public ConvOption option; - public Dictionary mapMethods = new Dictionary(); - public Dictionary mapEvents = new Dictionary(); - public Dictionary mapFields = new Dictionary(); - //public Dictionary codes = new Dictionary(); + } //public Dictionary codes = new Dictionary(); //public byte[] GetScript(byte[] script_hash) //{ // string strhash = ""; @@ -71,21 +72,19 @@ public string GenJson() methodinfo[m.Key] = m.Value.GenJson(); } - StringBuilder sb = new StringBuilder(); json.ConvertToStringWithFormat(sb, 4); return sb.ToString(); } - public void FromJson(string json) - { - - } - - public Dictionary staticfields = new Dictionary(); } public class NeoMethod { + public string lastsfieldname = null;//最后一个加载的静态成员的名字,仅event使用 + + public int lastparam = -1;//最后一个加载的参数对应 + public int lastCast = -1; + public bool isEntry = false; public string _namespace; public string name; @@ -93,6 +92,7 @@ public class NeoMethod public List paramtypes = new List(); public string returntype; public bool isPublic = true; + public bool isReadOnly = false; public bool inSmartContract; //临时变量 public List body_Variables = new List(); @@ -118,10 +118,6 @@ public MyJson.JsonNode_Object GenJson() return json; } - public void FromJson(MyJson.JsonNode_Object json) - { - } - //public byte[] Build() //{ // List bytes = new List(); @@ -138,10 +134,46 @@ public void FromJson(MyJson.JsonNode_Object json) // //将body链接,生成this.code byte[] // //并计算 this.codehash byte[] //} - public string lastsfieldname = null;//最后一个加载的静态成员的名字,仅event使用 - public int lastparam = -1;//最后一个加载的参数对应 - public int lastCast = -1; + /// + /// Constructor + /// + public NeoMethod() { } + + /// + /// Constructor + /// + /// Method + public NeoMethod(MethodDefinition method) + { + _namespace = method.DeclaringType.FullName; + name = method.FullName; + displayName = method.Name; + inSmartContract = method.DeclaringType.BaseType.Name == "SmartContract"; + isPublic = method.IsPublic; + + foreach (var attr in method.CustomAttributes) + { + ProcessAttribute(attr); + } + } + + private void ProcessAttribute(CustomAttribute attr) + { + switch (attr.AttributeType.Name) + { + case "DisplayNameAttribute": + { + displayName = (string)attr.ConstructorArguments[0].Value; + break; + } + case "ReadOnlyAttribute": + { + isReadOnly = (bool)attr.ConstructorArguments[0].Value; + break; + } + } + } } public class NeoEvent { @@ -150,6 +182,15 @@ public class NeoEvent public string displayName; public List paramtypes = new List(); public string returntype; + + public NeoEvent(ILField value) + { + _namespace = value.field.DeclaringType.FullName; + name = value.field.DeclaringType.FullName + "::" + value.field.Name; + displayName = value.displayName; + returntype = value.returntype; + paramtypes = value.paramtypes; + } } public class NeoCode @@ -224,41 +265,25 @@ public MyJson.JsonNode_ValueString GenJson() } return new MyJson.JsonNode_ValueString(info); } - - public void FromJson(MyJson.JsonNode_Object json) - { - } } public class NeoField : NeoParam { + public int index { get; private set; } public NeoField(string name, string type, int index) : base(name, type) { this.index = index; } - public int index - { - get; - private set; - } } public class NeoParam { + public string name { get; private set; } + public string type { get; private set; } public NeoParam(string name, string type) { this.name = name; this.type = type; } - public string name - { - get; - private set; - } - public string type - { - get; - private set; - } public override string ToString() { return type + " " + name; diff --git a/tests/Neo.Compiler.MSIL.UnitTests/Neo.Compiler.MSIL.UnitTests.csproj b/tests/Neo.Compiler.MSIL.UnitTests/Neo.Compiler.MSIL.UnitTests.csproj index 285fab5c7..9a83b13d7 100644 --- a/tests/Neo.Compiler.MSIL.UnitTests/Neo.Compiler.MSIL.UnitTests.csproj +++ b/tests/Neo.Compiler.MSIL.UnitTests/Neo.Compiler.MSIL.UnitTests.csproj @@ -1,27 +1,28 @@ - - - - netcoreapp2.1 - false - Neo.Compiler.MSIL - - - - - - - - - - - - - - - - - PreserveNewest - + + + + netcoreapp2.1 + false + Neo.Compiler.MSIL + + + + + + + + + + + + + - + + + PreserveNewest + + + + diff --git a/tests/Neo.Compiler.MSIL.UnitTests/TestClasses/Contract_Event.cs b/tests/Neo.Compiler.MSIL.UnitTests/TestClasses/Contract_Abi.cs similarity index 53% rename from tests/Neo.Compiler.MSIL.UnitTests/TestClasses/Contract_Event.cs rename to tests/Neo.Compiler.MSIL.UnitTests/TestClasses/Contract_Abi.cs index 8edc65c10..2d03258da 100644 --- a/tests/Neo.Compiler.MSIL.UnitTests/TestClasses/Contract_Event.cs +++ b/tests/Neo.Compiler.MSIL.UnitTests/TestClasses/Contract_Abi.cs @@ -4,11 +4,19 @@ namespace Neo.Compiler.MSIL.TestClasses { - public class Contract_Event : SmartContract.Framework.SmartContract + public class Contract_Abi : SmartContract.Framework.SmartContract { [DisplayName("transfer")] public static event Action Transferred; public static void Main(string method, object[] args) { } + + [ReadOnly(true)] + public static void readOnlyTrue() { } + + [ReadOnly(false)] + public static void readOnlyFalse1() { } + + public static void readOnlyFalse2() { } } } diff --git a/tests/Neo.Compiler.MSIL.UnitTests/UnitTest_ABI.cs b/tests/Neo.Compiler.MSIL.UnitTests/UnitTest_ABI.cs new file mode 100644 index 000000000..89d75003f --- /dev/null +++ b/tests/Neo.Compiler.MSIL.UnitTests/UnitTest_ABI.cs @@ -0,0 +1,69 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Compiler.MSIL.Utils; +using Neo.IO.Json; + +namespace Neo.Compiler.MSIL +{ + [TestClass] + public class UnitTest_ABI + { + [TestMethod] + public void Test_ABI() + { + var testengine = new TestEngine(); + testengine.AddEntryScript("./TestClasses/Contract_Abi.cs"); + string expectABI = @"{""hash"":""0x77811b3127dea2df1a18230f91396fbcf8c648f4"",""methods"":[{""name"":""readOnlyTrue"",""parameters"":[],""returnType"":""Void"",""readOnly"":true},{""name"":""readOnlyFalse1"",""parameters"":[],""returnType"":""Void"",""readOnly"":false},{""name"":""readOnlyFalse2"",""parameters"":[],""returnType"":""Void"",""readOnly"":false}],""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""method"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Void"",""readOnly"":false},""events"":[{""name"":""transfer"",""parameters"":[{""name"":""arg1"",""type"":""ByteArray""},{""name"":""arg2"",""type"":""ByteArray""},{""name"":""arg3"",""type"":""Integer""}]}]}"; + Assert.AreEqual(testengine.ScriptEntry.finialABI.ToString(), expectABI); + + // Check with the real class + + var abi = SmartContract.Manifest.ContractAbi.FromJson(JObject.Parse(expectABI)); + + // Hash + + Assert.AreEqual("0x77811b3127dea2df1a18230f91396fbcf8c648f4", abi.Hash.ToString()); + + // Entry Point + + Assert.AreEqual("Main", abi.EntryPoint.Name); + Assert.AreEqual(2, abi.EntryPoint.Parameters.Length); + Assert.AreEqual("method", abi.EntryPoint.Parameters[0].Name); + Assert.AreEqual(SmartContract.ContractParameterType.String, abi.EntryPoint.Parameters[0].Type); + Assert.AreEqual("args", abi.EntryPoint.Parameters[1].Name); + Assert.AreEqual(SmartContract.ContractParameterType.Array, abi.EntryPoint.Parameters[1].Type); + Assert.AreEqual(SmartContract.ContractParameterType.Void, abi.EntryPoint.ReturnType); + Assert.IsFalse(abi.EntryPoint.ReadOnly); + + // Methods + + Assert.AreEqual(3, abi.Methods.Length); + + Assert.AreEqual("readOnlyTrue", abi.Methods[0].Name); + Assert.AreEqual(0, abi.Methods[0].Parameters.Length); + Assert.AreEqual(SmartContract.ContractParameterType.Void, abi.Methods[0].ReturnType); + Assert.IsTrue(abi.Methods[0].ReadOnly); + + Assert.AreEqual("readOnlyFalse1", abi.Methods[1].Name); + Assert.AreEqual(0, abi.Methods[1].Parameters.Length); + Assert.AreEqual(SmartContract.ContractParameterType.Void, abi.Methods[1].ReturnType); + Assert.IsFalse(abi.Methods[0].ReadOnly); + + Assert.AreEqual("readOnlyFalse2", abi.Methods[2].Name); + Assert.AreEqual(0, abi.Methods[2].Parameters.Length); + Assert.AreEqual(SmartContract.ContractParameterType.Void, abi.Methods[2].ReturnType); + Assert.IsFalse(abi.Methods[0].ReadOnly); + + // Events + + Assert.AreEqual(1, abi.Events.Length); + Assert.AreEqual("transfer", abi.Events[0].Name); + Assert.AreEqual(3, abi.Events[0].Parameters.Length); + Assert.AreEqual("arg1", abi.Events[0].Parameters[0].Name); + Assert.AreEqual(SmartContract.ContractParameterType.ByteArray, abi.Events[0].Parameters[0].Type); + Assert.AreEqual("arg2", abi.Events[0].Parameters[1].Name); + Assert.AreEqual(SmartContract.ContractParameterType.ByteArray, abi.Events[0].Parameters[1].Type); + Assert.AreEqual("arg3", abi.Events[0].Parameters[2].Name); + Assert.AreEqual(SmartContract.ContractParameterType.Integer, abi.Events[0].Parameters[2].Type); + } + } +} diff --git a/tests/Neo.Compiler.MSIL.UnitTests/UnitTest_ABI_Event.cs b/tests/Neo.Compiler.MSIL.UnitTests/UnitTest_ABI_Event.cs deleted file mode 100644 index 6d66a52e1..000000000 --- a/tests/Neo.Compiler.MSIL.UnitTests/UnitTest_ABI_Event.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Compiler.MSIL.Utils; -using Neo.VM; -using System; - -namespace Neo.Compiler.MSIL -{ - [TestClass] - public class UnitTest_ABI_Event - { - [TestMethod] - public void Test_ABI_Event() - { - var testengine = new TestEngine(); - testengine.AddEntryScript("./TestClasses/Contract_Event.cs"); - var abi = testengine.ScriptEntry.finialABI; - Console.WriteLine("abi=" + abi.ToString()); - var events = abi["events"].AsList()[0].ToString(); - Console.WriteLine("event abi info =" + events); - - string expecteventabi = @"{""name"":""transfer"",""parameters"":[{""name"":""arg1"",""type"":""ByteArray""},{""name"":""arg2"",""type"":""ByteArray""},{""name"":""arg3"",""type"":""Integer""}]}"; - Assert.AreEqual(events, expecteventabi); - } - } -}