Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added some conversions #474

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 50 additions & 64 deletions src/Neo.Compiler.MSIL/MSIL/Conv_Multi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;

namespace Neo.Compiler.MSIL
Expand Down Expand Up @@ -240,14 +241,15 @@ public bool IsNonCall(Mono.Cecil.MethodDefinition defs)
return false;
}

public bool IsMixAttribute(Mono.Cecil.MethodDefinition defs, out VM.OpCode[] opcodes, out string[] opdata)
public bool IsMixAttribute(MethodDefinition defs, out VM.OpCode[] opcodes, out string[] opdata, out bool rightToLeft)
{
// ============================================
// Integrates attributes: OpCode/Syscall/Script
// ============================================

opcodes = null;
opdata = null;
rightToLeft = true;

if (defs == null) return false;

Expand All @@ -263,6 +265,7 @@ public bool IsMixAttribute(Mono.Cecil.MethodDefinition defs, out VM.OpCode[] opc

if (count_attrs == 0) return false; // no OpCode/Syscall/Script Attribute

bool rightToLeftSet = false;
opcodes = new VM.OpCode[count_attrs];
opdata = new string[count_attrs];

Expand Down Expand Up @@ -298,6 +301,12 @@ public bool IsMixAttribute(Mono.Cecil.MethodDefinition defs, out VM.OpCode[] opc

i++;
}
else if (attr.AttributeType.FullName == "Neo.SmartContract.Framework.RightToLeftAttribute")
{
rightToLeft = (bool)attr.ConstructorArguments[0].Value;
rightToLeftSet = true;
ext++;
}

if (attr.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
ext++;
Expand All @@ -306,6 +315,11 @@ public bool IsMixAttribute(Mono.Cecil.MethodDefinition defs, out VM.OpCode[] opc
if ((count_attrs + ext) == defs.CustomAttributes.Count)
{
// all attributes are OpCode or Syscall or Script (plus ExtensionAttribute which is automatic)

if (!rightToLeftSet && count_attrs == 1 && opcodes[0] != VM.OpCode.SYSCALL)
{
rightToLeft = false;
}
return true;
}
else
Expand Down Expand Up @@ -368,6 +382,7 @@ private int ConvertCall(OpCode src, NeoMethod to, List<MethodToken> methodTokens
UInt160 callhash = null;
VM.OpCode[] callcodes = null;
string[] calldata = null;
bool rightToLeft = true;

Mono.Cecil.MethodDefinition defs = null;
Exception defError = null;
Expand Down Expand Up @@ -417,16 +432,12 @@ private int ConvertCall(OpCode src, NeoMethod to, List<MethodToken> methodTokens
//{
// calltype = 3;
//}
else if (IsMixAttribute(defs, out callcodes, out calldata))
else if (IsMixAttribute(defs, out callcodes, out calldata, out rightToLeft))
{
//only syscall, need to reverse arguments
//only opcall, no matter what arguments
calltype = 7;

if (callcodes.Length == 1 && callcodes[0] != VM.OpCode.SYSCALL)
{
calltype = 2;
}
calltype = 7;
}
else if (IsContractCall(defs, out callhash))
{
Expand Down Expand Up @@ -670,8 +681,7 @@ private int ConvertCall(OpCode src, NeoMethod to, List<MethodToken> methodTokens
}
else if (src.tokenMethod == "System.Char System.String::get_Chars(System.Int32)")
{
ConvertPushNumber(1, src, to);
Convert1by1(VM.OpCode.SUBSTR, null, to);
Convert1by1(VM.OpCode.PICKITEM, src, to);
return 0;
}
else if (src.tokenMethod == "System.String System.String::Substring(System.Int32)")
Expand Down Expand Up @@ -789,41 +799,41 @@ private int ConvertCall(OpCode src, NeoMethod to, List<MethodToken> methodTokens
}
else
{
// reverse the arguments order
if (rightToLeft)
{
// reverse the arguments order

//this become very diffcult
//this become very diffcult

// because opcode donot need to flip params
// but syscall need
// calltype7 is opcode? or is syscall?
// because opcode donot need to flip params
// but syscall need
// calltype7 is opcode? or is syscall?

// i will make calltype7 =calltype3 , you can add flip opcode if you need.
if (havethis && calltype == 7) // is syscall
pcount++;
//if ((calltype == 3) || ((calltype == 7) && (callcodes[0] == VM.OpCode.SYSCALL)))
// pcount++;
// calltype == 3 does not exist anymore
// i will make calltype7 =calltype3 , you can add flip opcode if you need.
if (havethis && calltype == 7) // is syscall
pcount++;
//if ((calltype == 3) || ((calltype == 7) && (callcodes[0] == VM.OpCode.SYSCALL)))
// pcount++;
// calltype == 3 does not exist anymore

Convert1by1(VM.OpCode.NOP, src, to);
if (pcount <= 1)
{
}
else if (pcount == 2)
{
Insert1(VM.OpCode.SWAP, "swap 2 param", to);
}
else if (pcount == 3)
{
Insert1(VM.OpCode.REVERSE3, "", to);
}
else if (pcount == 4)
{
Insert1(VM.OpCode.REVERSE4, "", to);
}
else
{
InsertPush(pcount, "swap" + pcount, to);
Insert1(VM.OpCode.REVERSEN, "", to);
Convert1by1(VM.OpCode.NOP, src, to);
if (pcount == 2)
{
Insert1(VM.OpCode.SWAP, "swap 2 param", to);
}
else if (pcount == 3)
{
Insert1(VM.OpCode.REVERSE3, "", to);
}
else if (pcount == 4)
{
Insert1(VM.OpCode.REVERSE4, "", to);
}
else if (pcount > 4)
{
InsertPush(pcount, "swap" + pcount, to);
Insert1(VM.OpCode.REVERSEN, "", to);
}
}
}
if (calltype == 1)
Expand All @@ -837,29 +847,6 @@ private int ConvertCall(OpCode src, NeoMethod to, List<MethodToken> methodTokens
{
Convert1by1(callcodes[0], src, to, Helper.OpDataToBytes(calldata[0]));
}

/*
else if (calltype == 3)
{
byte[] bytes = null;
if (this.outModule.option.useSysCallInteropHash)
{
//now neovm use ineropMethod hash for syscall.
bytes = BitConverter.GetBytes(callname.ToInteropMethodHash());
}
else
{
bytes = System.Text.Utility.StrictUTF8.GetBytes(callname);
if (bytes.Length > 252) throw new Exception("string is to long");
}
byte[] outbytes = new byte[bytes.Length + 1];
outbytes[0] = (byte)bytes.Length;
Array.Copy(bytes, 0, outbytes, 1, bytes.Length);
//bytes.Prepend 函数在 dotnet framework 4.6 编译不过
_Convert1by1(VM.OpCode.SYSCALL, null, to, outbytes);
return 0;
}
*/
else if (calltype == 7)
{
for (var j = 0; j < callcodes.Length; j++)
Expand All @@ -872,7 +859,6 @@ private int ConvertCall(OpCode src, NeoMethod to, List<MethodToken> methodTokens
else
{
byte[] opdata = Helper.OpDataToBytes(calldata[j]);

Convert1by1(callcodes[j], src, to, opdata);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Neo.Compiler.MSIL/MSIL/Converter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public NeoModule Convert(ILModule _in, ConvOption option = null)
continue;
if (IsNonCall(m.Value.method))
continue;
if (IsMixAttribute(m.Value.method, out _, out _))
if (IsMixAttribute(m.Value.method, out _, out _, out _))
continue;

if (m.Key.Contains("::Main("))
Expand Down
11 changes: 11 additions & 0 deletions src/Neo.SmartContract.Framework/Helper.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Numerics;
using System.Runtime.InteropServices;

namespace Neo.SmartContract.Framework
{
Expand Down Expand Up @@ -174,12 +175,22 @@ public static byte ToByte(this int source)
[OpCode(OpCode.LEFT)]
public extern static byte[] Take(this byte[] source, int count);
erikzhang marked this conversation as resolved.
Show resolved Hide resolved

[RightToLeft(false)]
[OpCode(OpCode.LEFT)]
[OpCode(OpCode.CONVERT, StackItemType.ByteString)]
public extern static string Take(this string source, int count);

/// <summary>
/// Returns byte[] with last 'count' elements from 'source'. Faults if count < 0
/// </summary>
[OpCode(OpCode.RIGHT)]
public extern static byte[] Last(this byte[] source, int count);

[RightToLeft(false)]
[OpCode(OpCode.RIGHT)]
[OpCode(OpCode.CONVERT, StackItemType.ByteString)]
public extern static string Last(this string source, int count);

/// <summary>
/// Returns a reversed copy of parameter 'source'.
/// Example: [0a,0b,0c,0d,0e] -> [0e,0d,0c,0b,0a]
Expand Down
15 changes: 15 additions & 0 deletions src/Neo.SmartContract.Framework/RightToLeftAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;

namespace Neo.SmartContract.Framework
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class RightToLeftAttribute : Attribute
{
public bool RightToLeft { get; private set; }

public RightToLeftAttribute(bool rightToLeft)
{
RightToLeft = rightToLeft;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Text;
using System.Numerics;
using System.Runtime.CompilerServices;
using Neo.SmartContract.Framework;

namespace Neo.Compiler.MSIL.UnitTests.TestClasses
{
class Contract_Types_String : SmartContract.Framework.SmartContract
{
public static char checkIndex(string cad, int pos) { return cad[pos]; }
public static string checkRange(string cad, int pos, int count) { return cad.Substring(pos, count); }
public static string checkTake(string cad, int count) { return cad.Take(count); }
public static string checkLast(string cad, int count) { return cad.Last(count); }
}
}
34 changes: 34 additions & 0 deletions tests/Neo.Compiler.MSIL.UnitTests/UnitTest_Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -521,5 +521,39 @@ public void UInt160_byte_array_construct()
var received = new UInt160(((ByteString)item).GetSpan());
Assert.AreEqual(received, notZero);
}

[TestMethod]
public void String_Methods()
{
using var testengine = new TestEngine();
testengine.AddEntryScript("./TestClasses/Contract_Types_String.cs");

var result = testengine.ExecuteTestCaseStandard("checkIndex", "hello", 4);
Assert.AreEqual(1, result.Count);
var item = result.Pop();
Assert.IsInstanceOfType(item, typeof(Integer));
Assert.AreEqual((byte)'o', item.GetInteger());

testengine.Reset();
result = testengine.ExecuteTestCaseStandard("checkTake", "hello", 4);
Assert.AreEqual(1, result.Count);
item = result.Pop();
Assert.IsInstanceOfType(item, typeof(ByteString));
Assert.AreEqual("hell", item.GetString());

testengine.Reset();
result = testengine.ExecuteTestCaseStandard("checkLast", "hello", 2);
Assert.AreEqual(1, result.Count);
item = result.Pop();
Assert.IsInstanceOfType(item, typeof(ByteString));
Assert.AreEqual("lo", item.GetString());

testengine.Reset();
result = testengine.ExecuteTestCaseStandard("checkRange", "hello", 1, 2);
Assert.AreEqual(1, result.Count);
item = result.Pop();
Assert.IsInstanceOfType(item, typeof(ByteString));
Assert.AreEqual("el", item.GetString());
}
}
}