Skip to content

Commit

Permalink
Add comment and UT (neo-project#999)
Browse files Browse the repository at this point in the history
* update

* Update SystemCall.cs

* Update SystemOperators.cs

* ut

* UT for CheckStandards
  • Loading branch information
chenzhitong authored Mar 22, 2024
1 parent 0d3cbc9 commit a47fb44
Show file tree
Hide file tree
Showing 9 changed files with 296 additions and 88 deletions.
17 changes: 17 additions & 0 deletions src/Neo.Compiler.CSharp/MethodConvert/Expression/Expression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ private void ConvertExpression(SemanticModel model, ExpressionSyntax syntax, Syn

switch (fullName)
{
//complex types like UInt160 at compile time to avoid runtime overhead.
case "Neo.SmartContract.Framework.UInt160":
var strValue = (string)value;
value = (UInt160.TryParse(strValue, out var hash)
Expand All @@ -68,6 +69,7 @@ private void ConvertExpression(SemanticModel model, ExpressionSyntax syntax, Syn
strValue = (string)value;
value = ECPoint.Parse(strValue, ECCurve.Secp256r1).EncodePoint(true);
break;
//This type no longer exists.
case "Neo.SmartContract.Framework.ByteArray":
strValue = (string)value;
value = strValue.HexToBytes(true);
Expand Down Expand Up @@ -220,6 +222,11 @@ private void ConvertExpression(SemanticModel model, ExpressionSyntax syntax, Syn
}
}

/// <summary>
/// Ensures that the value of the incoming integer type is within the specified range.
/// If the type is BigInteger, no range check is performed.
/// </summary>
/// <param name="type">The integer type to be checked.</param>
private void EnsureIntegerInRange(ITypeSymbol type)
{
if (type.Name == "BigInteger") return;
Expand Down Expand Up @@ -266,6 +273,16 @@ private void EnsureIntegerInRange(ITypeSymbol type)
endTarget.Instruction = AddInstruction(OpCode.NOP);
}

/// <summary>
/// Converts an object to a string. Different conversion methods are used based on the type of the object.
/// </summary>
/// <param name="model">The semantic model used to obtain type information of the expression.</param>
/// <param name="expression">The expression to be converted to a string.</param>
/// <remarks>
/// For integer types and BigInteger type, call the itoa method of NativeContract.StdLib.Hash for conversion.
/// For string type and specific types in Neo.SmartContract.Framework, directly perform expression conversion.
/// </remarks>
/// <exception cref="CompilationException">For unsupported types, throw a compilation exception.</exception>
private void ConvertObjectToString(SemanticModel model, ExpressionSyntax expression)
{
ITypeSymbol? type = ModelExtensions.GetTypeInfo(model, expression).Type;
Expand Down
249 changes: 167 additions & 82 deletions src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.cs

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions src/Neo.Compiler.CSharp/MethodConvert/System/SystemOperators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,45 @@ namespace Neo.Compiler;

partial class MethodConvert
{
/// <summary>
/// Attempts to process system operators. Performs different processing operations based on the method symbol.
/// </summary>
/// <param name="model">The semantic model used to obtain detailed information about the symbol.</param>
/// <param name="symbol">The method symbol to be processed.</param>
/// <param name="arguments">An array of expression parameters.</param>
/// <returns>True if system operators are successfully processed; otherwise, false.</returns>
private bool TryProcessSystemOperators(SemanticModel model, IMethodSymbol symbol, params ExpressionSyntax[] arguments)
{
switch (symbol.ToString())
{
//Handles cases of equality operator (==), comparing whether two objects or strings are equal.
case "object.operator ==(object, object)":
case "string.operator ==(string, string)":
ConvertExpression(model, arguments[0]);
ConvertExpression(model, arguments[1]);
AddInstruction(OpCode.EQUAL);
return true;
//Handles cases of inequality operator (!=), comparing whether two objects are not equal.
case "object.operator !=(object, object)":
ConvertExpression(model, arguments[0]);
ConvertExpression(model, arguments[1]);
AddInstruction(OpCode.NOTEQUAL);
return true;
//Handles cases of string concatenation operator (+), concatenating two strings into one.
case "string.operator +(string, string)":
ConvertExpression(model, arguments[0]);
ConvertExpression(model, arguments[1]);
AddInstruction(OpCode.CAT);
ChangeType(VM.Types.StackItemType.ByteString);
return true;
//Handles cases of string concatenation operator (+), concatenating a string with an object.
case "string.operator +(string, object)":
ConvertExpression(model, arguments[0]);
ConvertObjectToString(model, arguments[1]);
AddInstruction(OpCode.CAT);
ChangeType(VM.Types.StackItemType.ByteString);
return true;
//Handles cases of string concatenation operator (+), concatenating an object with a string.
case "string.operator +(object, string)":
ConvertObjectToString(model, arguments[0]);
ConvertExpression(model, arguments[1]);
Expand Down
31 changes: 26 additions & 5 deletions src/Neo.Compiler.CSharp/Optimizer/Analysers/EntryPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@ public enum EntryType
public static class EntryPoint
{
/// <summary>
///
/// Gets a dictionary of method entry points based on the contract manifest and debug information.
/// </summary>
/// <param name="nef"></param>
/// <param name="manifest"></param>
/// <param name="debugInfo"></param>
/// <returns>(addr -> EntryType, hasCallA)</returns>
/// <param name="manifest">The contract manifest.</param>
/// <param name="debugInfo">The debug information.</param>
/// <returns>A dictionary containing method entry points. (addr -> EntryType, hasCallA)</returns>
public static Dictionary<int, EntryType> EntryPointsByMethod(ContractManifest manifest, JToken debugInfo)
{
Dictionary<int, EntryType> result = new();
Expand All @@ -47,6 +46,11 @@ public static Dictionary<int, EntryType> EntryPointsByMethod(ContractManifest ma
return result;
}

/// <summary>
/// Gets a dictionary of entry points based on the CALLA instruction.
/// </summary>
/// <param name="nef">The NEF file.</param>
/// <returns>A dictionary containing entry points.</returns>
public static Dictionary<int, EntryType> EntryPointsByCallA(NefFile nef)
{
Dictionary<int, EntryType> result = new();
Expand All @@ -64,6 +68,11 @@ public static Dictionary<int, EntryType> EntryPointsByCallA(NefFile nef)
return result;
}

/// <summary>
/// Checks if the list of instructions contains the CALLA instruction.
/// </summary>
/// <param name="instructions">The list of instructions.</param>
/// <returns>True if the CALLA instruction exists; otherwise, false.</returns>
public static bool HasCallA(List<(int, Instruction)> instructions)
{
bool hasCallA = false;
Expand All @@ -76,12 +85,24 @@ public static bool HasCallA(List<(int, Instruction)> instructions)
return hasCallA;
}

/// <summary>
/// Checks if the NEF file contains the CALLA instruction.
/// </summary>
/// <param name="nef">The NEF file.</param>
/// <returns>True if the NEF file contains the CALLA instruction; otherwise, false.</returns>
public static bool HasCallA(NefFile nef)
{
Script script = nef.Script;
return HasCallA(script.EnumerateInstructions().ToList());
}

/// <summary>
/// Gets a dictionary of all entry points, including those calculated based on the CALLA instruction and methods.
/// </summary>
/// <param name="nef">The NEF file.</param>
/// <param name="manifest">The contract manifest.</param>
/// <param name="debugInfo">The debug information.</param>
/// <returns>A dictionary containing all entry points.</returns>
public static Dictionary<int, EntryType> AllEntryPoints(NefFile nef, ContractManifest manifest, JToken debugInfo)
=> EntryPointsByCallA(nef).Concat(EntryPointsByMethod(manifest, debugInfo)).ToDictionary(kv => kv.Key, kv => kv.Value);
}
Expand Down
29 changes: 29 additions & 0 deletions tests/Neo.Compiler.CSharp.TestContracts/Contract_BigInteger.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Services;
using System.Numerics;

namespace Neo.Compiler.CSharp.UnitTests.TestClasses
Expand Down Expand Up @@ -124,6 +125,21 @@ public static bool testIsEven(BigInteger input)
return input.IsEven;
}

public static bool testIsZero(BigInteger input)
{
return input.IsZero;
}

public static bool testIsOne(BigInteger input)
{
return input.IsOne;
}

public static int testSign(BigInteger input)
{
return input.Sign;
}

public static BigInteger TestAdd(BigInteger x, BigInteger y)
{
return BigInteger.Add(x, y);
Expand Down Expand Up @@ -163,5 +179,18 @@ public static BigInteger TestGreatestCommonDivisor(BigInteger x, BigInteger y)
{
return BigInteger.GreatestCommonDivisor(x, y);
}

public static bool TestEquals(BigInteger x, BigInteger y)
{
return x.Equals(y);
}

public static void TestModPow()
{
BigInteger number = 10;
int exponent = 3;
BigInteger modulus = 30;
Runtime.Log($"({number}^{exponent}) Mod {modulus} = {BigInteger.ModPow(number, exponent, modulus)}");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Attributes;
using System.Collections;
using System.Numerics;

namespace Neo.Compiler.CSharp.UnitTests.TestClasses
Expand All @@ -24,7 +25,7 @@ public class Contract_DirectInit : SmartContract.Framework.SmartContract

/// <summary>
/// A static field of type UInt160 initialized directly from a hex string. This allows for compile-time
/// initialization of blockchain-specific types like addresses, represented here as Hash256.
/// initialization of blockchain-specific types like txid/blockhash, represented here as Hash256.
/// </summary>
// [ByteArray("edcf8679104ec2911a4fe29ad7db232a493e5b990fb1da7af0c7b989948c8925")]
private static readonly UInt256 validUInt256 = "edcf8679104ec2911a4fe29ad7db232a493e5b990fb1da7af0c7b989948c8925";
Expand Down
16 changes: 16 additions & 0 deletions tests/Neo.Compiler.CSharp.TestContracts/Contract_NEP11.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Attributes;

namespace Neo.Compiler.CSharp.UnitTests.TestClasses
{
[SupportedStandards("NEP-11")]
public class Contract_NEP11 : Nep11Token<TokenState>
{
public override string Symbol { [Safe] get => "TEST"; }
}

public class TokenState : Nep11TokenState
{

}
}
13 changes: 13 additions & 0 deletions tests/Neo.Compiler.CSharp.TestContracts/Contract_NEP17.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Attributes;

namespace Neo.Compiler.CSharp.UnitTests.TestClasses
{
[SupportedStandards("NEP-17")]
public class Contract_NEP17 : Nep17Token
{
public override byte Decimals { [Safe] get => 8; }

public override string Symbol { [Safe] get => "TEST"; }
}
}
14 changes: 14 additions & 0 deletions tests/Neo.Compiler.CSharp.TestContracts/Contract_String.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,19 @@ public static void TestMain()
var timestamp = Ledger.GetBlock(Ledger.CurrentHash).Timestamp;
Runtime.Log($"Hello, {firstName} {lastName}! Current timestamp is {timestamp}.");
}

public static void TestEqual()
{
var str = "hello";
var str2 = "hello";
Runtime.Log(str.Equals(str2).ToString());
}

public static void TestSubstring()
{
var str = "01234567";
Runtime.Log(str.Substring(1));
Runtime.Log(str.Substring(1, 4));
}
}
}

0 comments on commit a47fb44

Please sign in to comment.