From 71f6ec9cd3c6a3bec4bd19edfe2d03a36c495297 Mon Sep 17 00:00:00 2001 From: Jim8y Date: Mon, 16 Sep 2024 22:49:05 +0800 Subject: [PATCH 01/11] working version --- .../Diagnostic/DiagnosticId.cs | 3 + .../MethodConvert/Expression/Expression.cs | 16 + .../MethodConvert/Helpers/StackHelpers.cs | 2 + .../MethodConvert/StackHelpers.OpCodes.cs | 11 + .../MethodConvert/System/SystemCall.Enum.cs | 706 ++++++++++++++++++ .../System/SystemCall.Register.cs | 117 ++- .../MethodConvert/System/SystemCall.cs | 21 +- .../Contract_Enum.cs | 67 ++ .../TestingArtifacts/Contract_Enum.cs | 86 +++ .../UnitTest_Enum.cs | 143 ++++ 10 files changed, 1166 insertions(+), 6 deletions(-) create mode 100644 src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Enum.cs create mode 100644 tests/Neo.Compiler.CSharp.TestContracts/Contract_Enum.cs create mode 100644 tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_Enum.cs create mode 100644 tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Enum.cs diff --git a/src/Neo.Compiler.CSharp/Diagnostic/DiagnosticId.cs b/src/Neo.Compiler.CSharp/Diagnostic/DiagnosticId.cs index 0d2600b2f..0ed8c130f 100644 --- a/src/Neo.Compiler.CSharp/Diagnostic/DiagnosticId.cs +++ b/src/Neo.Compiler.CSharp/Diagnostic/DiagnosticId.cs @@ -36,5 +36,8 @@ static class DiagnosticId public const string InvalidInitialValue = "NC3005"; public const string IncorrectNEPStandard = "NC3006"; public const string CapturedStaticFieldNotFound = "NC3007"; + + public const string InvalidType = "NC3008"; + public const string InvalidArgument = "NC3009"; } } diff --git a/src/Neo.Compiler.CSharp/MethodConvert/Expression/Expression.cs b/src/Neo.Compiler.CSharp/MethodConvert/Expression/Expression.cs index fa381c7df..42ed689f1 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/Expression/Expression.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/Expression/Expression.cs @@ -192,11 +192,27 @@ private void ConvertNonConstantExpression(SemanticModel model, ExpressionSyntax // Example: 42 or "Hello" ConvertLiteralExpression(model, expression); break; + case TypeOfExpressionSyntax expression: + // Example: typeof(int) + // Note: Neo currently does not support the Type type of C#. The typeof operator here + // will only return the string name of the class/type. This support is added + // to ensure we can process enum parse methods. + ConvertTypeOfExpression(model, expression); + break; default: throw new CompilationException(syntax, DiagnosticId.SyntaxNotSupported, $"Unsupported syntax: {syntax}"); } } + private void ConvertTypeOfExpression(SemanticModel model, TypeOfExpressionSyntax expression) + { + var typeInfo = model.GetTypeInfo(expression.Type); + if (typeInfo.Type == null) + throw new CompilationException(expression, DiagnosticId.InvalidType, $"Invalid type in typeof expression: {expression.Type}"); + + Push(typeInfo.Type.Name); + } + private static ITypeSymbol? GetTypeSymbol(SyntaxNode? syntaxNode, SemanticModel model) { return syntaxNode switch diff --git a/src/Neo.Compiler.CSharp/MethodConvert/Helpers/StackHelpers.cs b/src/Neo.Compiler.CSharp/MethodConvert/Helpers/StackHelpers.cs index 283704b5a..0c5cdf5c2 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/Helpers/StackHelpers.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/Helpers/StackHelpers.cs @@ -68,6 +68,8 @@ private void Push(bool value) AddInstruction(value ? OpCode.PUSHT : OpCode.PUSHF); } + private Instruction Ret() => AddInstruction(OpCode.RET); + private Instruction Push(BigInteger number) { if (number >= -1 && number <= 16) return AddInstruction(number == -1 ? OpCode.PUSHM1 : OpCode.PUSH0 + (byte)(int)number); diff --git a/src/Neo.Compiler.CSharp/MethodConvert/StackHelpers.OpCodes.cs b/src/Neo.Compiler.CSharp/MethodConvert/StackHelpers.OpCodes.cs index b13666f18..c975b41e6 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/StackHelpers.OpCodes.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/StackHelpers.OpCodes.cs @@ -541,5 +541,16 @@ private Instruction Assertmsg() { return AddInstruction(OpCode.ASSERTMSG); } + + private Instruction JumpIfNot(JumpTarget target) + { + return Jump(OpCode.JMPIFNOT, target); + } + + private Instruction Jump(JumpTarget target) + { + return Jump(OpCode.JMP, target); + } + #endregion } diff --git a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Enum.cs b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Enum.cs new file mode 100644 index 000000000..589c72bbd --- /dev/null +++ b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Enum.cs @@ -0,0 +1,706 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// The Neo.Compiler.CSharp is free software distributed under the MIT +// software license, see the accompanying file LICENSE in the main directory +// of the project or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Neo.SmartContract.Native; +using Neo.VM; + +namespace Neo.Compiler; + +internal partial class MethodConvert +{ + private static void HandleEnumParse(MethodConvert methodConvert, SemanticModel model, IMethodSymbol symbol, + ExpressionSyntax? instanceExpression, IReadOnlyList? arguments) + { + if (instanceExpression is not null) + methodConvert.ConvertExpression(model, instanceExpression); + if (arguments is not null) + methodConvert.PrepareArgumentsForMethod(model, symbol, arguments, CallingConvention.StdCall); + + // Get the enum type from the first argument (typeof(enum)) + ITypeSymbol? enumTypeSymbol = null; + if (arguments is { Count: > 0 }) + { + if ((arguments[0] as ArgumentSyntax).Expression is TypeOfExpressionSyntax typeOfExpression) + { + var typeInfo = model.GetTypeInfo(typeOfExpression.Type); + enumTypeSymbol = typeInfo.Type; + } + else + { + throw new CompilationException(arguments[0], DiagnosticId.InvalidArgument, "First argument must be a typeof(enum)"); + } + } + else + { + throw new CompilationException(symbol.Locations.FirstOrDefault().MetadataModule, DiagnosticId.InvalidArgument, "Enum.Parse requires at least two arguments"); + } + + if (enumTypeSymbol is not { TypeKind: TypeKind.Enum }) + { + throw new CompilationException(arguments![0], DiagnosticId.InvalidType, "Unable to determine enum type from first argument"); + } + + var enumMembers = enumTypeSymbol.GetMembers().OfType() + .Where(field => field is { HasConstantValue: true, IsImplicitlyDeclared: false }).ToArray(); + + foreach (var t in enumMembers) + { + // Duplicate inputString + methodConvert.Dup(); // Stack: [type, inputString,inputString] + + // Push enum name + methodConvert.Push(t.Name); // Stack: [type, inputString,inputString, enumName] + + // Equal comparison + methodConvert.Equal(); // Stack: [type,inputString, isEqual] + + var nextCheck = new JumpTarget(); + // If not equal, discard duplicated inputString and proceed to next + methodConvert.Jump(OpCode.JMPIFNOT, nextCheck); + + // If equal: + // Push enum value + methodConvert.Push(t.ConstantValue); // Stack: [type, inputString, enumValue] + methodConvert.Reverse3(); + methodConvert.Drop(2); + methodConvert.AddInstruction(OpCode.RET); + + nextCheck.Instruction = methodConvert.AddInstruction(OpCode.NOP); + } + + // No match found + // Remove the inputString from the stack + methodConvert.Drop(); + methodConvert.Push("No such enum value"); + methodConvert.Throw(); + } + + private static void HandleEnumParseIgnoreCase(MethodConvert methodConvert, SemanticModel model, IMethodSymbol symbol, + ExpressionSyntax? instanceExpression, IReadOnlyList? arguments) + { + if (instanceExpression is not null) + methodConvert.ConvertExpression(model, instanceExpression); + if (arguments is not null) + methodConvert.PrepareArgumentsForMethod(model, symbol, arguments, CallingConvention.StdCall); + + // Get the enum type from the first argument (typeof(enum)) + ITypeSymbol? enumTypeSymbol = null; + if (arguments is { Count: > 0 }) + { + if ((arguments[0] as ArgumentSyntax).Expression is TypeOfExpressionSyntax typeOfExpression) + { + var typeInfo = model.GetTypeInfo(typeOfExpression.Type); + enumTypeSymbol = typeInfo.Type; + } + else + { + throw new CompilationException(arguments[0], DiagnosticId.InvalidArgument, "First argument must be a typeof(enum)"); + } + } + else + { + throw new CompilationException(symbol.Locations.FirstOrDefault().MetadataModule, DiagnosticId.InvalidArgument, "Enum.Parse requires at least two arguments"); + } + + if (enumTypeSymbol is not { TypeKind: TypeKind.Enum }) + { + throw new CompilationException(arguments![0], DiagnosticId.InvalidType, "Unable to determine enum type from first argument"); + } + + var enumMembers = enumTypeSymbol.GetMembers().OfType() + .Where(field => field is { HasConstantValue: true, IsImplicitlyDeclared: false }).ToArray(); + + var ignoreCase = new JumpTarget(); + var ignoreCase2 = new JumpTarget(); + methodConvert.JumpIfNot(ignoreCase); + ConvertToUpper(methodConvert); // Convert inputString to upper case + ignoreCase.Instruction = methodConvert.Nop(); + foreach (var t in enumMembers) + { + // Duplicate inputString + methodConvert.Dup(); // Stack: [..., inputString, inputString] + methodConvert.AddInstruction(OpCode.LDARG1); + methodConvert.JumpIfNot(ignoreCase2); + JumpTarget endCase = new JumpTarget(); + // Push enum name + methodConvert.Push(t.Name.ToUpper()); // Stack: [..., inputString, inputString, enumName] + methodConvert.Jump(endCase); + ignoreCase2.Instruction = methodConvert.Nop(); + methodConvert.Push(t.Name); + endCase.Instruction = methodConvert.Nop(); + + // Equal comparison + methodConvert.Equal(); // Stack: [..., inputString, isEqual] + + var nextCheck = new JumpTarget(); + // If not equal, discard duplicated inputString and proceed to next + methodConvert.Jump(OpCode.JMPIFNOT, nextCheck); + + // If equal: + // Remove the duplicated inputString from the stack + methodConvert.Drop(3); + // Push enum value + methodConvert.Push(t.ConstantValue); + methodConvert.AddInstruction(OpCode.RET); + + nextCheck.Instruction = methodConvert.AddInstruction(OpCode.NOP); + } + + // No match found + // Remove the inputString from the stack + methodConvert.Drop(2); + methodConvert.Push("No such enum value"); + methodConvert.Throw(); + } + + private static void HandleEnumTryParseIgnoreCase(MethodConvert methodConvert, SemanticModel model, IMethodSymbol symbol, + ExpressionSyntax? instanceExpression, IReadOnlyList? arguments) + { + if (instanceExpression is not null) + methodConvert.ConvertExpression(model, instanceExpression); + if (arguments is not null) + methodConvert.PrepareArgumentsForMethod(model, symbol, arguments, CallingConvention.StdCall); + + // Get the enum type from the first argument (typeof(enum)) + ITypeSymbol? enumTypeSymbol = null; + if (arguments is { Count: > 2 }) + { + if ((arguments[0] as ArgumentSyntax).Expression is TypeOfExpressionSyntax typeOfExpression) + { + var typeInfo = model.GetTypeInfo(typeOfExpression.Type); + enumTypeSymbol = typeInfo.Type; + } + else + { + throw new CompilationException(arguments[0], DiagnosticId.InvalidArgument, "First argument must be a typeof(enum)"); + } + } + else + { + throw new CompilationException(symbol.Locations.FirstOrDefault().MetadataModule, DiagnosticId.InvalidArgument, "Enum.TryParse requires at least three arguments"); + } + + if (enumTypeSymbol is not { TypeKind: TypeKind.Enum }) + { + throw new CompilationException(arguments![0], DiagnosticId.InvalidType, "Unable to determine enum type from first argument"); + } + + if (!methodConvert._context.TryGetCapturedStaticField(symbol.Parameters[3], out var index)) + throw new CompilationException(symbol, DiagnosticId.SyntaxNotSupported, "Out parameter must be captured in a static field."); + + var enumMembers = enumTypeSymbol.GetMembers().OfType() + .Where(field => field is { HasConstantValue: true, IsImplicitlyDeclared: false }).ToArray(); + var ignoreCase = new JumpTarget(); + var ignoreCase2 = new JumpTarget(); + methodConvert.Drop(); + methodConvert.JumpIfNot(ignoreCase); + methodConvert.Swap(); + methodConvert.Drop(); + ConvertToUpper(methodConvert); // Convert inputString to upper case + ignoreCase.Instruction = methodConvert.Nop(); + foreach (var t in enumMembers) + { + // Duplicate inputString + methodConvert.Dup(); // Stack: [..., inputString, inputString] + methodConvert.AddInstruction(OpCode.LDARG1); + methodConvert.JumpIfNot(ignoreCase2); + JumpTarget endCase = new JumpTarget(); + // Push enum name + methodConvert.Push(t.Name.ToUpper()); // Stack: [..., inputString, inputString, enumName] + methodConvert.Jump(endCase); + ignoreCase2.Instruction = methodConvert.Nop(); + methodConvert.Push(t.Name); + endCase.Instruction = methodConvert.Nop(); + + // Equal comparison + methodConvert.Equal(); // Stack: [..., inputString, isEqual] + + var nextCheck = new JumpTarget(); + // If not equal, discard duplicated inputString and proceed to next + methodConvert.Jump(OpCode.JMPIFNOT, nextCheck); + + // If equal: + // Remove the duplicated inputString from the stack + methodConvert.Drop(2); + // Push enum value + methodConvert.Push(t.ConstantValue); + // Store the result in the out parameter + methodConvert.AccessSlot(OpCode.STSFLD, index); + // Push true to indicate success + methodConvert.Push(true); + methodConvert.AddInstruction(OpCode.RET); + + nextCheck.Instruction = methodConvert.AddInstruction(OpCode.NOP); + } + + // No match found + // Remove the inputString from the stack + methodConvert.Drop(2); + // Push default value (0) for the out parameter + methodConvert.Push(0); + methodConvert.AccessSlot(OpCode.STSFLD, index); + // Push false to indicate failure + methodConvert.Push(false); + methodConvert.AddInstruction(OpCode.RET); + } + + private static void ConvertToUpper(MethodConvert methodConvert) + { + var loopStart = new JumpTarget(); + var loopEnd = new JumpTarget(); + var charIsLower = new JumpTarget(); + methodConvert.Push(""); // Create an empty ByteString + + // methodConvert.AddInstruction(OpCode.LDARG0); // Load the string | arr str + methodConvert.AddInstruction(OpCode.PUSH0); // Push the initial index (0) arr str 0 + loopStart.Instruction = methodConvert.AddInstruction(OpCode.NOP); + + methodConvert.AddInstruction(OpCode.DUP); // Duplicate the index + methodConvert.AddInstruction(OpCode.LDARG0); // Load the string + methodConvert.AddInstruction(OpCode.SIZE); // Get the length of the string + methodConvert.AddInstruction(OpCode.LT); // Check if index < length + methodConvert.Jump(OpCode.JMPIFNOT, loopEnd); // If not, exit the loop + + methodConvert.AddInstruction(OpCode.DUP); // Duplicate the index | arr str 0 0 + methodConvert.AddInstruction(OpCode.LDARG0); // Load the string + methodConvert.Swap(); + methodConvert.AddInstruction(OpCode.PICKITEM); // Get the character at the current index + methodConvert.AddInstruction(OpCode.DUP); // Duplicate the character + methodConvert.Push((ushort)'a'); // Push 'a' + methodConvert.Push((ushort)'z' + 1); // Push 'z' + 1 + methodConvert.AddInstruction(OpCode.WITHIN); // Check if character is within 'a' to 'z' + methodConvert.Jump(OpCode.JMPIF, charIsLower); // If true, jump to charIsLower + methodConvert.Rot(); + methodConvert.Swap(); + methodConvert.AddInstruction(OpCode.CAT); // Append the original character to the array + methodConvert.Swap(); + methodConvert.Inc(); + methodConvert.Jump(OpCode.JMP, loopStart); // Jump to the start of the loop + + charIsLower.Instruction = methodConvert.AddInstruction(OpCode.NOP); + methodConvert.Push((ushort)'a'); // Push 'a' + methodConvert.AddInstruction(OpCode.SUB); // Subtract 'a' from the character + methodConvert.Push((ushort)'A'); // Push 'A' + methodConvert.AddInstruction(OpCode.ADD); // Add 'A' to the result + methodConvert.Rot(); + methodConvert.Swap(); + methodConvert.AddInstruction(OpCode.CAT); // Append the upper case character to the array + methodConvert.Swap(); + methodConvert.Inc(); + methodConvert.Jump(OpCode.JMP, loopStart); // Jump to the start of the loop + + loopEnd.Instruction = methodConvert.AddInstruction(OpCode.NOP); + methodConvert.Drop(); + methodConvert.ChangeType(VM.Types.StackItemType.ByteString); // Convert the array to a byte string + } + + private static void HandleEnumIsDefinedByName(MethodConvert methodConvert, SemanticModel model, IMethodSymbol symbol, + ExpressionSyntax? instanceExpression, IReadOnlyList? arguments) + { + if (instanceExpression is not null) + methodConvert.ConvertExpression(model, instanceExpression); + if (arguments is not null) + methodConvert.PrepareArgumentsForMethod(model, symbol, arguments, CallingConvention.StdCall); + + // Get the enum type from the first argument (typeof(enum)) + ITypeSymbol? enumTypeSymbol = null; + if (arguments is { Count: > 0 }) + { + if ((arguments[0] as ArgumentSyntax).Expression is TypeOfExpressionSyntax typeOfExpression) + { + var typeInfo = model.GetTypeInfo(typeOfExpression.Type); + enumTypeSymbol = typeInfo.Type; + } + else + { + throw new CompilationException(arguments[0], DiagnosticId.InvalidArgument, "First argument must be a typeof(enum)"); + } + } + else + { + throw new CompilationException(symbol.Locations.FirstOrDefault().MetadataModule, DiagnosticId.InvalidArgument, "Enum.IsDefined requires at least two arguments"); + } + + if (enumTypeSymbol is not { TypeKind: TypeKind.Enum }) + { + throw new CompilationException(arguments![0], DiagnosticId.InvalidType, "Unable to determine enum type from first argument"); + } + + var enumMembers = enumTypeSymbol.GetMembers().OfType() + .Where(field => field is { HasConstantValue: true, IsImplicitlyDeclared: false }).ToArray(); + + foreach (var t in enumMembers) + { + methodConvert.Dup(); // Duplicate input name + methodConvert.Push(t.Name); // Push enum name + methodConvert.Equal(); // Compare strings + + var nextCheck = new JumpTarget(); + methodConvert.JumpIfNot(nextCheck); // If not equal, check next + + methodConvert.Drop(); // Remove the duplicated input name + methodConvert.Push(true); // Push true (enum name is defined) + methodConvert.AddInstruction(OpCode.RET); // Return true + + nextCheck.Instruction = methodConvert.AddInstruction(OpCode.NOP); + } + + // No match found + methodConvert.Drop(); // Remove the input name + methodConvert.Push(false); // Push false (enum name is not defined) + methodConvert.AddInstruction(OpCode.RET); // Return false + } + + private static void HandleEnumGetName(MethodConvert methodConvert, SemanticModel model, IMethodSymbol symbol, + ExpressionSyntax? instanceExpression, IReadOnlyList? arguments) + { + if (instanceExpression is not null) + methodConvert.ConvertExpression(model, instanceExpression); + if (arguments is not null) + methodConvert.PrepareArgumentsForMethod(model, symbol, arguments, CallingConvention.StdCall); + + // Get the enum type from the Enum value + var enumType = symbol.Parameters[0].Type; + + if (enumType is not INamedTypeSymbol enumTypeSymbol) + { + throw new CompilationException(symbol.Locations.FirstOrDefault().MetadataModule, DiagnosticId.InvalidType, "Unable to determine enum type"); + } + + var enumMembers = enumTypeSymbol.GetMembers().OfType() + .Where(field => field is { HasConstantValue: true, IsImplicitlyDeclared: false }).ToArray(); + + foreach (var t in enumMembers) + { + methodConvert.Dup(); // Duplicate input value + methodConvert.Push(t.ConstantValue); // Push enum value + methodConvert.Equal(); // Compare values + + var nextCheck = new JumpTarget(); + methodConvert.JumpIfNot(nextCheck); // If not equal, check next + + methodConvert.Drop(); // Remove the duplicated input value + methodConvert.Push(t.Name); // Push enum name + methodConvert.AddInstruction(OpCode.RET); // Return enum name + + nextCheck.Instruction = methodConvert.AddInstruction(OpCode.NOP); + } + + // No match found + methodConvert.Drop(); // Remove the input value + methodConvert.AddInstruction(OpCode.PUSHNULL); // Push null (no matching enum name) + methodConvert.AddInstruction(OpCode.RET); // Return null + } + + private static void HandleEnumGetNameWithType(MethodConvert methodConvert, SemanticModel model, IMethodSymbol symbol, + ExpressionSyntax? instanceExpression, IReadOnlyList? arguments) + { + if (instanceExpression is not null) + methodConvert.ConvertExpression(model, instanceExpression); + if (arguments is not null) + methodConvert.PrepareArgumentsForMethod(model, symbol, arguments, CallingConvention.StdCall); + + // Get the enum type from the first argument (typeof(enum)) + ITypeSymbol? enumTypeSymbol = null; + if (arguments is { Count: > 0 }) + { + if ((arguments[0] as ArgumentSyntax).Expression is TypeOfExpressionSyntax typeOfExpression) + { + var typeInfo = model.GetTypeInfo(typeOfExpression.Type); + enumTypeSymbol = typeInfo.Type; + } + else + { + throw new CompilationException(arguments[0], DiagnosticId.InvalidArgument, "First argument must be a typeof(enum)"); + } + } + else + { + throw new CompilationException(symbol.Locations.FirstOrDefault().MetadataModule, DiagnosticId.InvalidArgument, "Enum.GetName requires at least two arguments"); + } + + if (enumTypeSymbol is not { TypeKind: TypeKind.Enum }) + { + throw new CompilationException(arguments![0], DiagnosticId.InvalidType, "Unable to determine enum type from first argument"); + } + + var enumMembers = enumTypeSymbol.GetMembers().OfType() + .Where(field => field is { HasConstantValue: true, IsImplicitlyDeclared: false }).ToArray(); + + foreach (var t in enumMembers) + { + methodConvert.Dup(); // Duplicate input value + methodConvert.Push(t.ConstantValue); // Push enum value + methodConvert.Equal(); // Compare values + + var nextCheck = new JumpTarget(); + methodConvert.JumpIfNot(nextCheck); // If not equal, check next + + methodConvert.Drop(2); // Remove the duplicated input value + methodConvert.Push(t.Name); // Push enum name + methodConvert.AddInstruction(OpCode.RET); // Return enum name + + nextCheck.Instruction = methodConvert.AddInstruction(OpCode.NOP); + } + + // No match found + methodConvert.Drop(2); // Remove the input value + methodConvert.AddInstruction(OpCode.PUSHNULL); // Push null (no matching enum name) + methodConvert.AddInstruction(OpCode.RET); // Return null + } + + private static void HandleEnumTryParse(MethodConvert methodConvert, SemanticModel model, IMethodSymbol symbol, + ExpressionSyntax? instanceExpression, IReadOnlyList? arguments) + { + if (instanceExpression is not null) + methodConvert.ConvertExpression(model, instanceExpression); + if (arguments is not null) + methodConvert.PrepareArgumentsForMethod(model, symbol, arguments, CallingConvention.StdCall); + + if (!methodConvert._context.TryGetCapturedStaticField(symbol.Parameters[2], out var index)) + throw new CompilationException(symbol, DiagnosticId.SyntaxNotSupported, "Out parameter must be captured in a static field."); + + // Get the enum type from the first argument (typeof(enum)) + ITypeSymbol? enumTypeSymbol = null; + if (arguments is { Count: > 0 }) + { + if ((arguments[0] as ArgumentSyntax).Expression is TypeOfExpressionSyntax typeOfExpression) + { + var typeInfo = model.GetTypeInfo(typeOfExpression.Type); + enumTypeSymbol = typeInfo.Type; + } + else + { + throw new CompilationException(arguments[0], DiagnosticId.InvalidArgument, "First argument must be a typeof(enum)"); + } + } + else + { + throw new CompilationException(symbol.Locations.FirstOrDefault().MetadataModule, DiagnosticId.InvalidArgument, "Enum.TryParse requires at least three arguments"); + } + + if (enumTypeSymbol is not { TypeKind: TypeKind.Enum }) + { + throw new CompilationException(arguments[0], DiagnosticId.InvalidType, "Unable to determine enum type from first argument"); + } + + var enumMembers = enumTypeSymbol.GetMembers().OfType() + .Where(field => field is { HasConstantValue: true, IsImplicitlyDeclared: false }).ToArray(); + + JumpTarget endTarget = new(); + methodConvert.Drop(); + foreach (var t in enumMembers) + { + methodConvert.Dup(); // Stack: [..., inputString, inputString] + methodConvert.Push(t.Name); // Stack: [..., inputString, inputString, enumName] + + // Equal comparison + methodConvert.Equal(); // Stack: [..., inputString, isEqual] + + JumpTarget nextCheck = new(); + methodConvert.JumpIfNot(nextCheck); + + // If equal: + methodConvert.Drop(2); // Remove the inputString + methodConvert.Push(t.ConstantValue); // Stack: [..., enumValue] + methodConvert.AccessSlot(OpCode.STSFLD, index); // Store enum value in out parameter + methodConvert.Push(true); // Stack: [..., true] + methodConvert.Ret(); + + nextCheck.Instruction = methodConvert.AddInstruction(OpCode.NOP); + } + + // No match found + methodConvert.Drop(2); // Remove the inputString + methodConvert.Push(0); // Default enum value + methodConvert.AccessSlot(OpCode.STSFLD, index); // Store default value in out parameter + methodConvert.Push(false); // Success flag set to false + + endTarget.Instruction = methodConvert.AddInstruction(OpCode.NOP); + } + + private static void HandleEnumGetNames(MethodConvert methodConvert, SemanticModel model, IMethodSymbol symbol, + ExpressionSyntax? instanceExpression, IReadOnlyList? arguments) + { + if (instanceExpression is not null) + methodConvert.ConvertExpression(model, instanceExpression); + if (arguments is not null) + methodConvert.PrepareArgumentsForMethod(model, symbol, arguments, CallingConvention.StdCall); + + // Get the enum type from the first argument (typeof(enum)) + ITypeSymbol? enumTypeSymbol = null; + if (arguments is { Count: > 0 }) + { + if ((arguments[0] as ArgumentSyntax).Expression is TypeOfExpressionSyntax typeOfExpression) + { + var typeInfo = model.GetTypeInfo(typeOfExpression.Type); + enumTypeSymbol = typeInfo.Type; + } + else + { + throw new CompilationException(arguments[0], DiagnosticId.InvalidArgument, "First argument must be a typeof(enum)"); + } + } + else + { + throw new CompilationException(symbol.Locations.FirstOrDefault().MetadataModule, DiagnosticId.InvalidArgument, "Enum.GetNames requires one argument"); + } + + if (enumTypeSymbol is not { TypeKind: TypeKind.Enum }) + { + throw new CompilationException(arguments![0], DiagnosticId.InvalidType, "Unable to determine enum type from first argument"); + } + + var enumMembers = enumTypeSymbol.GetMembers().OfType() + .Where(field => field is { HasConstantValue: true, IsImplicitlyDeclared: false }).ToArray(); + + // Create an array of names + methodConvert.Push(enumMembers.Length); // Stack: [..., size] + methodConvert.AddInstruction(OpCode.NEWARRAY); // Stack: [..., array] + + for (int i = 0; i < enumMembers.Length; i++) + { + methodConvert.Dup(); // Duplicate array reference + methodConvert.Push(i); // Index + methodConvert.Push(enumMembers[i].Name); // Name + methodConvert.AddInstruction(OpCode.SETITEM); // array[i] = name + } + + methodConvert.AddInstruction(OpCode.RET); + } + + private static void HandleEnumGetValues(MethodConvert methodConvert, SemanticModel model, IMethodSymbol symbol, + ExpressionSyntax? instanceExpression, IReadOnlyList? arguments) + { + if (instanceExpression is not null) + methodConvert.ConvertExpression(model, instanceExpression); + if (arguments is not null) + methodConvert.PrepareArgumentsForMethod(model, symbol, arguments, CallingConvention.StdCall); + + // Get the enum type from the first argument (typeof(enum)) + ITypeSymbol? enumTypeSymbol = null; + if (arguments is { Count: > 0 }) + { + if ((arguments[0] as ArgumentSyntax).Expression is TypeOfExpressionSyntax typeOfExpression) + { + var typeInfo = model.GetTypeInfo(typeOfExpression.Type); + enumTypeSymbol = typeInfo.Type; + } + else + { + throw new CompilationException(arguments[0], DiagnosticId.InvalidArgument, "First argument must be a typeof(enum)"); + } + } + else + { + throw new CompilationException(symbol.Locations.FirstOrDefault().MetadataModule, DiagnosticId.InvalidArgument, "Enum.GetValues requires one argument"); + } + + if (enumTypeSymbol is not { TypeKind: TypeKind.Enum }) + { + throw new CompilationException(arguments![0], DiagnosticId.InvalidType, "Unable to determine enum type from first argument"); + } + + var enumMembers = enumTypeSymbol.GetMembers().OfType() + .Where(field => field is { HasConstantValue: true, IsImplicitlyDeclared: false }).ToArray(); + + // Create an array of values + methodConvert.Push(enumMembers.Length); // Stack: [..., size] + methodConvert.AddInstruction(OpCode.NEWARRAY); // Stack: [..., array] + + for (int i = 0; i < enumMembers.Length; i++) + { + methodConvert.Dup(); // Duplicate array reference + methodConvert.Push(i); // Index + methodConvert.Push(enumMembers[i].ConstantValue); // Value + methodConvert.AddInstruction(OpCode.SETITEM); // array[i] = value + } + + methodConvert.AddInstruction(OpCode.RET); + } + + private static void HandleEnumIsDefined(MethodConvert methodConvert, SemanticModel model, IMethodSymbol symbol, + ExpressionSyntax? instanceExpression, IReadOnlyList? arguments) + { + if (instanceExpression is not null) + methodConvert.ConvertExpression(model, instanceExpression); + if (arguments is not null) + methodConvert.PrepareArgumentsForMethod(model, symbol, arguments, CallingConvention.StdCall); + + // Get the enum type from the first argument (typeof(enum)) + ITypeSymbol? enumTypeSymbol = null; + if (arguments is { Count: > 0 }) + { + if ((arguments[0] as ArgumentSyntax).Expression is TypeOfExpressionSyntax typeOfExpression) + { + var typeInfo = model.GetTypeInfo(typeOfExpression.Type); + enumTypeSymbol = typeInfo.Type; + } + else + { + throw new CompilationException(arguments[0], DiagnosticId.InvalidArgument, "First argument must be a typeof(enum)"); + } + } + else + { + throw new CompilationException(symbol.Locations.FirstOrDefault().MetadataModule, DiagnosticId.InvalidArgument, "Enum.IsDefined requires at least two arguments"); + } + + if (enumTypeSymbol is not { TypeKind: TypeKind.Enum }) + { + throw new CompilationException(arguments![0], DiagnosticId.InvalidType, "Unable to determine enum type from first argument"); + } + + var enumMembers = enumTypeSymbol.GetMembers().OfType() + .Where(field => field is { HasConstantValue: true, IsImplicitlyDeclared: false }).ToArray(); + + var valueType = model.GetTypeInfo((arguments[1] as ArgumentSyntax)?.Expression).Type; + + // We need to compare the input value against the enum values + var endLabel = new JumpTarget(); + // here add check logic to verify if valueType is string + var isName = valueType is INamedTypeSymbol { Name: "String" }; + + foreach (var t in enumMembers) + { + methodConvert.Dup(); // Duplicate value to compare + if (isName) + { + methodConvert.Push(t.Name); + } + else + { + methodConvert.Push(t.ConstantValue); + } + + // Equal comparison + methodConvert.Equal(); // Stack: [..., isEqual] + + var nextCheck = new JumpTarget(); + methodConvert.JumpIfNot(nextCheck); + + // If equal, set result to true + methodConvert.Drop(); + methodConvert.Drop(); // Remove the false + methodConvert.Push(true); // Set result to true + methodConvert.Ret(); + nextCheck.Instruction = methodConvert.AddInstruction(OpCode.NOP); + } + methodConvert.Drop(); + methodConvert.Drop(); // Remove the duplicated value + methodConvert.Push(false); + } +} diff --git a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs index ed610ca9b..bc65b310b 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Globalization; using System.Numerics; @@ -35,6 +36,13 @@ private static void RegisterSystemCallHandlers() // BitOperations handlers RegisterBitOperationsHandlers(); + + // Add new handler registrations + RegisterBigIntegerAdditionalHandlers(); + RegisterArrayAdditionalHandlers(); + RegisterMathAdditionalHandlers(); + RegisterConvertHandlers(); + RegisterEnumHandlers(); } private static void RegisterBigIntegerHandlers() @@ -584,6 +592,13 @@ private static void RegisterStringHandlers() RegisterHandler((string s, char trimChar) => s.Trim(trimChar), HandleStringTrimChar); RegisterHandler((string s, string oldValue, string newValue) => s.Replace(oldValue, newValue), HandleStringReplace); RegisterHandler((string s) => s.Length, HandleStringLength); + + // RegisterHandler((string s, char[] separator, StringSplitOptions options) => s.Split(separator, options), HandleStringSplit); + // RegisterHandler((string separator, IEnumerable values) => string.Join(separator, values), HandleStringJoin); + // RegisterHandler((string s, string value) => s.StartsWith(value), HandleStringStartsWith); + // RegisterHandler((string s, int totalWidth, char paddingChar) => s.PadLeft(totalWidth, paddingChar), HandleStringPadLeft); + // RegisterHandler((string s, int totalWidth, char paddingChar) => s.PadRight(totalWidth, paddingChar), HandleStringPadRight); + // RegisterHandler((string s, char[] trimChars) => s.Trim(trimChars), HandleStringTrimWithCharArray); } private static void RegisterArrayHandlers() @@ -618,12 +633,14 @@ private static void RegisterCharHandlers() RegisterHandler((char c) => char.IsLetterOrDigit(c), HandleCharIsLetterOrDigit); RegisterHandler((char x, char min, char max) => char.IsBetween(x, min, max), HandleCharIsBetween); - // Add missing char methods RegisterHandler((char c) => char.ToLowerInvariant(c), HandleCharToLowerInvariant); RegisterHandler((char c) => char.ToUpperInvariant(c), HandleCharToUpperInvariant); RegisterHandler((char c) => char.IsAscii(c), HandleCharIsAscii); RegisterHandler((char c) => char.IsAsciiDigit(c), HandleCharIsAsciiDigit); RegisterHandler((char c) => char.IsAsciiLetter(c), HandleCharIsAsciiLetter); + RegisterHandler((string s) => char.Parse(s), HandleCharParse); + // RegisterHandler((string s, char result) => char.TryParse(s, out result), HandleCharTryParse); + } private static void RegisterNullableTypeHandlers() @@ -730,6 +747,20 @@ private static void RegisterNullableTypeHandlers() // Bool RegisterHandler((string? value, bool result) => bool.TryParse(value, out result), HandleBoolTryParseWithOut); #endregion + + #region GetValueOrDefault + // RegisterHandler((sbyte? x, sbyte defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableSByteGetValueOrDefaultWithParam); + // RegisterHandler((byte? x, byte defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableByteGetValueOrDefaultWithParam); + // RegisterHandler((short? x, short defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableShortGetValueOrDefaultWithParam); + // RegisterHandler((ushort? x, ushort defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableUShortGetValueOrDefaultWithParam); + // RegisterHandler((int? x, int defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableIntGetValueOrDefaultWithParam); + // RegisterHandler((uint? x, uint defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableUIntGetValueOrDefaultWithParam); + // RegisterHandler((long? x, long defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableLongGetValueOrDefaultWithParam); + // RegisterHandler((ulong? x, ulong defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableULongGetValueOrDefaultWithParam); + // RegisterHandler((BigInteger? x, BigInteger defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableBigIntegerGetValueOrDefaultWithParam); + // RegisterHandler((char? x, char defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableCharGetValueOrDefaultWithParam); + // RegisterHandler((bool? x, bool defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableBoolGetValueOrDefaultWithParam); + #endregion GetValueOrDefault } private static void RegisterBitOperationsHandlers() @@ -745,6 +776,90 @@ private static void RegisterBitOperationsHandlers() RegisterHandler((ulong value, int offset) => BitOperations.RotateLeft(value, offset), HandleULongRotateLeft); RegisterHandler((uint value, int offset) => BitOperations.RotateRight(value, offset), HandleUIntRotateRight); RegisterHandler((ulong value, int offset) => BitOperations.RotateRight(value, offset), HandleULongRotateRight); + // RegisterHandler((sbyte value) => BitOperations.TrailingZeroCount(value), HandleSByteTrailingZeroCount); + // RegisterHandler((byte value) => BitOperations.TrailingZeroCount(value), HandleByteTrailingZeroCount); + // RegisterHandler((short value) => BitOperations.TrailingZeroCount(value), HandleShortTrailingZeroCount); + // RegisterHandler((ushort value) => BitOperations.TrailingZeroCount(value), HandleUShortTrailingZeroCount); + // RegisterHandler((int value) => BitOperations.TrailingZeroCount(value), HandleIntTrailingZeroCount); + // RegisterHandler((uint value) => BitOperations.TrailingZeroCount(value), HandleUIntTrailingZeroCount); + // RegisterHandler((long value) => BitOperations.TrailingZeroCount(value), HandleLongTrailingZeroCount); + // RegisterHandler((ulong value) => BitOperations.TrailingZeroCount(value), HandleULongTrailingZeroCount); + } + + private static void RegisterBigIntegerAdditionalHandlers() + { + // RegisterHandler((string s, NumberStyles style, IFormatProvider? provider) => BigInteger.Parse(s, style, provider), HandleBigIntegerParseWithStyleAndProvider); + // RegisterHandler((string s, BigInteger result) => BigInteger.TryParse(s, out result), HandleBigIntegerTryParse); + // RegisterHandler((BigInteger value, string? format, IFormatProvider? provider) => value.ToString(format, provider), HandleBigIntegerToStringWithFormatAndProvider); + } + + private static void RegisterArrayAdditionalHandlers() + { + // RegisterHandler((Array sourceArray, Array destinationArray, int length) => Array.Copy(sourceArray, destinationArray, length), HandleArrayCopy); + // RegisterHandler((Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) => Array.Copy(sourceArray, sourceIndex, destinationArray, destinationIndex, length), HandleArrayCopyWithIndices); + // RegisterHandler((Array array) => Array.Clear(array), HandleArrayClear); + // RegisterHandler((Array array, int index, int length) => Array.Clear(array, index, length), HandleArrayClearWithRange); + // RegisterHandler((Array array) => Array.Reverse(array), HandleArrayReverse); + // RegisterHandler((Array array, int index, int length) => Array.Reverse(array, index, length), HandleArrayReverseWithRange); + // RegisterHandler((Array array) => Array.Sort(array), HandleArraySort); + // RegisterHandler((Array keys, Array? items) => Array.Sort(keys, items), HandleArraySortWithItems); + // RegisterHandler((Array array, int index, int length) => Array.Sort(array, index, length), HandleArraySortWithRange); + // RegisterHandler((Array keys, Array? items, int index, int length) => Array.Sort(keys, items, index, length), HandleArraySortWithItemsAndRange); + // RegisterHandler((Array array, object value) => Array.BinarySearch(array, value), HandleArrayBinarySearch); + // RegisterHandler((Array array, int index, int length, object value) => Array.BinarySearch(array, index, length, value), HandleArrayBinarySearchWithRange); + // RegisterHandler((Array array) => Array.IndexOf(array, null), HandleArrayIndexOf); + // RegisterHandler((Array array, object? value) => Array.IndexOf(array, value), HandleArrayIndexOfWithValue); + // RegisterHandler((Array array, object? value, int startIndex) => Array.IndexOf(array, value, startIndex), HandleArrayIndexOfWithValueAndStart); + // RegisterHandler((Array array, object? value, int startIndex, int count) => Array.IndexOf(array, value, startIndex, count), HandleArrayIndexOfWithValueStartAndCount); + // RegisterHandler((Array array) => Array.LastIndexOf(array, null), HandleArrayLastIndexOf); + // RegisterHandler((Array array, object? value) => Array.LastIndexOf(array, value), HandleArrayLastIndexOfWithValue); + // RegisterHandler((Array array, object? value, int startIndex) => Array.LastIndexOf(array, value, startIndex), HandleArrayLastIndexOfWithValueAndStart); + // RegisterHandler((Array array, object? value, int startIndex, int count) => Array.LastIndexOf(array, value, startIndex, count), HandleArrayLastIndexOfWithValueStartAndCount); + // RegisterHandler((Array array) => Array.Exists(array, _ => true), HandleArrayExists); + // RegisterHandler((Array array) => Array.TrueForAll(array, _ => true), HandleArrayTrueForAll); + // RegisterHandler((Array array) => Array.ConvertAll(array, o => o), HandleArrayConvertAll); + } + + private static void RegisterMathAdditionalHandlers() + { + // RegisterHandler((long a) => Math.Sin(a), HandleMathSin); + // RegisterHandler((long a) => Math.Cos(a), HandleMathCos); + // RegisterHandler((long a) => Math.Tan(a), HandleMathTan); + // RegisterHandler((long a) => Math.Log(a), HandleMathLog); + // RegisterHandler((long a) => Math.Log10(a), HandleMathLog10); } + private static void RegisterConvertHandlers() + { + // RegisterHandler((object value) => System.Convert.ToInt32(value), HandleConvertToInt32); + // RegisterHandler((object value) => System.Convert.ToString(value), HandleConvertToString); + // RegisterHandler((object value) => System.Convert.ToBoolean(value), HandleConvertToBoolean); + // RegisterHandler((object value) => System.Convert.ToByte(value), HandleConvertToByte); + // RegisterHandler((object value) => System.Convert.ToChar(value), HandleConvertToChar); + // RegisterHandler((object value) => System.Convert.ToInt16(value), HandleConvertToInt16); + // RegisterHandler((object value) => System.Convert.ToInt64(value), HandleConvertToInt64); + // RegisterHandler((object value) => System.Convert.ToSByte(value), HandleConvertToSByte); + // RegisterHandler((object value) => System.Convert.ToSingle(value), HandleConvertToSingle); + // RegisterHandler((object value) => System.Convert.ToUInt16(value), HandleConvertToUInt16); + // RegisterHandler((object value) => System.Convert.ToUInt32(value), HandleConvertToUInt32); + // RegisterHandler((object value) => System.Convert.ToUInt64(value), HandleConvertToUInt64); + } + + private static void RegisterEnumHandlers() + { + RegisterHandler((Type enumType, string value) => Enum.Parse(enumType, value), HandleEnumParse); + RegisterHandler((Type enumType, string value, bool ignoreCase) => Enum.Parse(enumType, value, ignoreCase), HandleEnumParseIgnoreCase); + RegisterHandler((Type enumType, string value, object result) => Enum.TryParse(enumType, value, out result), HandleEnumTryParse); + RegisterHandler((Type enumType, string value, bool ignoreCase, object result) => Enum.TryParse(enumType, value, ignoreCase, out result), HandleEnumTryParseIgnoreCase); + RegisterHandler((Type enumType) => Enum.GetNames(enumType), HandleEnumGetNames); + RegisterHandler((Type enumType) => Enum.GetValues(enumType), HandleEnumGetValues); + RegisterHandler((Type enumType, object value) => Enum.IsDefined(enumType, value), HandleEnumIsDefined); + RegisterHandler((Type enumType, string name) => Enum.IsDefined(enumType, name), HandleEnumIsDefinedByName); + RegisterHandler((Enum value) => Enum.GetName(value.GetType(), value), HandleEnumGetName,"System.Enum.GetName<>()"); + RegisterHandler((Type enumType, object value) => Enum.GetName(enumType, value), HandleEnumGetNameWithType, "System.Enum.GetName()"); + // RegisterHandler((Enum value) => Enum.Format(value.GetType(), value, "G"), HandleEnumFormat); + // RegisterHandler((Type enumType, object value, string format) => Enum.Format(enumType, value, format), HandleEnumFormatWithType); + // RegisterHandler((Enum value) => Enum.GetUnderlyingType(value.GetType()), HandleEnumGetUnderlyingType); + // RegisterHandler((Type enumType) => Enum.GetUnderlyingType(enumType), HandleEnumGetUnderlyingTypeWithType); + } } diff --git a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.cs b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.cs index e3280b1dc..948049989 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.cs @@ -41,15 +41,15 @@ private static void RegisterHandler(Expression> expressio SystemCallHandlers[key] = handler; } - private static void RegisterHandler(Expression> expression, SystemCallHandler handler) + private static void RegisterHandler(Expression> expression, SystemCallHandler handler, string? key = null) { - var key = GetKeyFromExpression(expression, typeof(T)); + key = key ?? GetKeyFromExpression(expression, typeof(T)); SystemCallHandlers[key] = handler; } - private static void RegisterHandler(Expression> expression, SystemCallHandler handler) + private static void RegisterHandler(Expression> expression, SystemCallHandler handler, string? key = null) { - var key = GetKeyFromExpression(expression, typeof(T1), typeof(T2)); + key = key ?? GetKeyFromExpression(expression, typeof(T1), typeof(T2)); SystemCallHandlers[key] = handler; } @@ -60,10 +60,17 @@ private static void RegisterHandler(Expression(Expression> expression, SystemCallHandler handler) { - var key = GetKeyFromExpression(expression); + var key = GetKeyFromExpression(expression, typeof(T1), typeof(T2), typeof(T3), typeof(T4)); + SystemCallHandlers[key] = handler; + } + + private static void RegisterHandler(Expression> expression, SystemCallHandler handler) + { + var key = GetKeyFromExpression(expression, typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5)); SystemCallHandlers[key] = handler; } + private static void RegisterHandler(Expression> expression, SystemCallHandler handler) { var key = GetKeyFromExpression(expression, typeof(T)); @@ -200,6 +207,8 @@ private static string GetShortTypeName(Type type) _ when type == typeof(BigInteger) => "System.Numerics.BigInteger", _ when type == typeof(Array) => "System.Array", _ when type == typeof(Math) => "System.Math", + _ when type == typeof(Type) => "System.Type", + _ when type == typeof(Enum) => "System.Enum", _ when type.IsGenericType => $"{type.Name.Split('`')[0]}<{string.Join(", ", type.GetGenericArguments().Select(GetShortTypeName))}>", _ => type.Name, }; @@ -263,6 +272,8 @@ private bool TryProcessSystemMethods(SemanticModel model, IMethodSymbol symbol, var key = symbol.ToString()!.Replace("out ", ""); key = (from parameter in symbol.Parameters let parameterType = parameter.Type.ToString() where !parameter.Type.IsValueType && parameterType!.EndsWith('?') select parameterType).Aggregate(key, (current, parameterType) => current.Replace(parameterType, parameterType[..^1])); if (key == "string.ToString()") key = "object.ToString()"; + if (key.Contains("System.Enum.GetName<")) key = "System.Enum.GetName<>()"; + if (key.Contains("System.Enum.GetName(")) key = "System.Enum.GetName()"; if (!SystemCallHandlers.TryGetValue(key, out var handler)) return false; handler(this, model, symbol, instanceExpression, arguments); return true; diff --git a/tests/Neo.Compiler.CSharp.TestContracts/Contract_Enum.cs b/tests/Neo.Compiler.CSharp.TestContracts/Contract_Enum.cs new file mode 100644 index 000000000..0b0bde00e --- /dev/null +++ b/tests/Neo.Compiler.CSharp.TestContracts/Contract_Enum.cs @@ -0,0 +1,67 @@ +using Neo.SmartContract.Framework.Attributes; +using System.Collections; +using System.Numerics; + +namespace Neo.Compiler.CSharp.TestContracts +{ + + public class Contract_Enum : SmartContract.Framework.SmartContract + { + public enum TestEnum + { + Value1 = 1, + Value2 = 2, + Value3 = 3 + } + + public static object TestEnumParse(string value) + { + return System.Enum.Parse(typeof(TestEnum), value); + } + + public static object TestEnumParseIgnoreCase(string value, bool ignoreCase) + { + return System.Enum.Parse(typeof(TestEnum), value, ignoreCase); + } + + public static bool TestEnumTryParse(string value) + { + return System.Enum.TryParse(typeof(TestEnum), value, out object result); + } + + public static bool TestEnumTryParseIgnoreCase(string value, bool ignoreCase) + { + return System.Enum.TryParse(typeof(TestEnum), value, ignoreCase, out object result); + } + + public static string[] TestEnumGetNames() + { + return System.Enum.GetNames(typeof(TestEnum)); + } + + public static TestEnum[] TestEnumGetValues() + { + return (TestEnum[])System.Enum.GetValues(typeof(TestEnum)); + } + + public static bool TestEnumIsDefined(object value) + { + return System.Enum.IsDefined(typeof(TestEnum), value); + } + + public static bool TestEnumIsDefinedByName(string name) + { + return System.Enum.IsDefined(typeof(TestEnum), name); + } + + public static string TestEnumGetName(TestEnum value) + { + return System.Enum.GetName(value); + } + + public static string TestEnumGetNameWithType(object value) + { + return System.Enum.GetName(typeof(TestEnum), value); + } + } +} diff --git a/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_Enum.cs b/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_Enum.cs new file mode 100644 index 000000000..64b566588 --- /dev/null +++ b/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_Enum.cs @@ -0,0 +1,86 @@ +using Neo.Cryptography.ECC; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Numerics; + +namespace Neo.SmartContract.Testing; + +public abstract class Contract_Enum(Neo.SmartContract.Testing.SmartContractInitialize initialize) : Neo.SmartContract.Testing.SmartContract(initialize), IContractInfo +{ + #region Compiled data + + public static Neo.SmartContract.Manifest.ContractManifest Manifest => Neo.SmartContract.Manifest.ContractManifest.Parse(@"{""name"":""Contract_Enum"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""testEnumParse"",""parameters"":[{""name"":""value"",""type"":""String""}],""returntype"":""Any"",""offset"":0,""safe"":false},{""name"":""testEnumParseIgnoreCase"",""parameters"":[{""name"":""value"",""type"":""String""},{""name"":""ignoreCase"",""type"":""Boolean""}],""returntype"":""Any"",""offset"":87,""safe"":false},{""name"":""testEnumTryParse"",""parameters"":[{""name"":""value"",""type"":""String""}],""returntype"":""Boolean"",""offset"":241,""safe"":false},{""name"":""testEnumTryParseIgnoreCase"",""parameters"":[{""name"":""value"",""type"":""String""},{""name"":""ignoreCase"",""type"":""Boolean""}],""returntype"":""Boolean"",""offset"":319,""safe"":false},{""name"":""testEnumGetNames"",""parameters"":[],""returntype"":""Array"",""offset"":475,""safe"":false},{""name"":""testEnumGetValues"",""parameters"":[],""returntype"":""Array"",""offset"":521,""safe"":false},{""name"":""testEnumIsDefined"",""parameters"":[{""name"":""value"",""type"":""Any""}],""returntype"":""Boolean"",""offset"":546,""safe"":false},{""name"":""testEnumIsDefinedByName"",""parameters"":[{""name"":""name"",""type"":""String""}],""returntype"":""Boolean"",""offset"":591,""safe"":false},{""name"":""testEnumGetName"",""parameters"":[{""name"":""value"",""type"":""Integer""}],""returntype"":""String"",""offset"":657,""safe"":false},{""name"":""testEnumGetNameWithType"",""parameters"":[{""name"":""value"",""type"":""Any""}],""returntype"":""String"",""offset"":709,""safe"":false},{""name"":""_initialize"",""parameters"":[],""returntype"":""Void"",""offset"":775,""safe"":false}],""events"":[]},""permissions"":[],""trusts"":[],""extra"":{""nef"":{""optimization"":""All""}}}"); + + /// + /// Optimization: "All" + /// + public static Neo.SmartContract.NefFile Nef => Neo.IO.Helper.AsSerializable(Convert.FromBase64String(@"TkVGM1Rlc3RpbmdFbmdpbmUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP0KA1cAAQwIVGVzdEVudW14SgwGVmFsdWUxlyYHEVNFRUBKDAZWYWx1ZTKXJgcSU0VFQEoMBlZhbHVlM5cmBxNTRUVARQwSTm8gc3VjaCBlbnVtIHZhbHVlOlcAAgwIVGVzdEVudW14eSYuDAAQSnjKtSYiSnhQzkoAYQB7uyQJUVCLUJwi6QBhnwBBnlFQi1CcItxF2yhKeSY0DAZWQUxVRTGXJgdFRUURQEp5JiAMBlZBTFVFMpcmB0VFRRJASnkmDAwGVkFMVUUzIgoMBlZhbHVlM5cmB0VFRRNARUUMEk5vIHN1Y2ggZW51bSB2YWx1ZTpXAAELYAwIVGVzdEVudW14WEVKDAZWYWx1ZTGXJghFRRFgCEBKDAZWYWx1ZTKXJghFRRJgCEBKDAZWYWx1ZTOXJghFRRNgCEBFRRBgCUBXAAILYQwIVGVzdEVudW14eVl5Ji4MABBKeMq1JiJKeFDOSgBhAHu7JAlRUItQnCLpAGGfAEGeUVCLUJwi3EXbKEp5JjwMBlZBTFVFMZcmC0VFRUVFEWEIQEp5JiQMBlZBTFVFMpcmC0VFRUVFEmEIQEp5JgwMBlZBTFVFMyIKDAZWYWx1ZTOXJgtFRUVFRRNhCEBFRUVFRRBhCUAMCFRlc3RFbnVtE8NKEAwGVmFsdWUx0EoRDAZWYWx1ZTLQShIMBlZhbHVlM9BADAhUZXN0RW51bRPDShAR0EoREtBKEhPQQFcAAQwIVGVzdEVudW14ShGXJgZFRQhAShKXJgZFRQhAShOXJgZFRQhARUUJQFcAAQwIVGVzdEVudW14SgwGVmFsdWUxlyYGRUUIQEoMBlZhbHVlMpcmBkVFCEBKDAZWYWx1ZTOXJgZFRQhARUUJQFcAAXhKEZcmDEUMBlZhbHVlMUBKEpcmDEUMBlZhbHVlMkBKE5cmDEUMBlZhbHVlM0BFC0BXAAEMCFRlc3RFbnVteEoRlyYNRUUMBlZhbHVlMUBKEpcmDUVFDAZWYWx1ZTJAShOXJg1FRQwGVmFsdWUzQEVFC0BWAkCtEllC")); + + #endregion + + #region Unsafe methods + + /// + /// Unsafe method + /// + [DisplayName("testEnumGetName")] + public abstract string? TestEnumGetName(BigInteger? value); + + /// + /// Unsafe method + /// + [DisplayName("testEnumGetNames")] + public abstract IList? TestEnumGetNames(); + + /// + /// Unsafe method + /// + [DisplayName("testEnumGetNameWithType")] + public abstract string? TestEnumGetNameWithType(object? value = null); + + /// + /// Unsafe method + /// + [DisplayName("testEnumGetValues")] + public abstract IList? TestEnumGetValues(); + + /// + /// Unsafe method + /// + [DisplayName("testEnumIsDefined")] + public abstract bool? TestEnumIsDefined(object? value = null); + + /// + /// Unsafe method + /// + [DisplayName("testEnumIsDefinedByName")] + public abstract bool? TestEnumIsDefinedByName(string? name); + + /// + /// Unsafe method + /// + [DisplayName("testEnumParse")] + public abstract object? TestEnumParse(string? value); + + /// + /// Unsafe method + /// + [DisplayName("testEnumParseIgnoreCase")] + public abstract object? TestEnumParseIgnoreCase(string? value, bool? ignoreCase); + + /// + /// Unsafe method + /// + [DisplayName("testEnumTryParse")] + public abstract bool? TestEnumTryParse(string? value); + + /// + /// Unsafe method + /// + [DisplayName("testEnumTryParseIgnoreCase")] + public abstract bool? TestEnumTryParseIgnoreCase(string? value, bool? ignoreCase); + + #endregion + +} diff --git a/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Enum.cs b/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Enum.cs new file mode 100644 index 000000000..b0bfe34f7 --- /dev/null +++ b/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Enum.cs @@ -0,0 +1,143 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract.Testing; +using Neo.SmartContract.Testing.Exceptions; +using Neo.VM.Types; + +namespace Neo.Compiler.CSharp.UnitTests +{ + [TestClass] + public class UnitTest_Enum : DebugAndTestBase + { + [TestMethod] + public void TestEnumParse() + { + Assert.AreEqual(new Integer(1), Contract.TestEnumParse("Value1")); + AssertGasConsumed(989700); + Assert.AreEqual(new Integer(2), Contract.TestEnumParse("Value2")); + AssertGasConsumed(989700); + Assert.AreEqual(new Integer(3), Contract.TestEnumParse("Value3")); + AssertGasConsumed(989700); + Assert.ThrowsException(() => Contract.TestEnumParse("InvalidValue")); + AssertGasConsumed(989700); + } + + [TestMethod] + public void TestEnumParseIgnoreCase() + { + Assert.AreEqual(new Integer(1), Contract.TestEnumParseIgnoreCase("value1", true)); + AssertGasConsumed(989700); + Assert.AreEqual(new Integer(2), Contract.TestEnumParseIgnoreCase("VALUE2", true)); + AssertGasConsumed(989700); + Assert.AreEqual(new Integer(3), Contract.TestEnumParseIgnoreCase("VaLuE3", true)); + AssertGasConsumed(989700); + Assert.ThrowsException(() => Contract.TestEnumParseIgnoreCase("value1", false)); + AssertGasConsumed(989700); + Assert.ThrowsException(() => Contract.TestEnumParseIgnoreCase("InvalidValue", true)); + AssertGasConsumed(989700); + } + + [TestMethod] + public void TestEnumTryParse() + { + Assert.IsTrue(Contract.TestEnumTryParse("Value1")); + AssertGasConsumed(989700); + Assert.IsTrue(Contract.TestEnumTryParse("Value2")); + AssertGasConsumed(989700); + Assert.IsTrue(Contract.TestEnumTryParse("Value3")); + AssertGasConsumed(989700); + Assert.IsFalse(Contract.TestEnumTryParse("InvalidValue")); + AssertGasConsumed(989700); + } + + [TestMethod] + public void TestEnumTryParseIgnoreCase() + { + Assert.IsTrue(Contract.TestEnumTryParseIgnoreCase("value1", true)); + AssertGasConsumed(989700); + Assert.IsTrue(Contract.TestEnumTryParseIgnoreCase("VALUE2", true)); + AssertGasConsumed(989700); + Assert.IsTrue(Contract.TestEnumTryParseIgnoreCase("VaLuE3", true)); + AssertGasConsumed(989700); + Assert.IsFalse(Contract.TestEnumTryParseIgnoreCase("value1", false)); + AssertGasConsumed(989700); + Assert.IsFalse(Contract.TestEnumTryParseIgnoreCase("InvalidValue", true)); + AssertGasConsumed(989700); + } + + // [TestMethod] + // public void TestEnumGetNames() + // { + // var names = Contract.TestEnumGetNames(); + // AssertGasConsumed(989700); + // CollectionAssert.AreEqual(new[] { "Value1", "Value2", "Value3" }, names); + // } + // + // [TestMethod] + // public void TestEnumGetValues() + // { + // var values = Contract.TestEnumGetValues(); + // AssertGasConsumed(989700); + // CollectionAssert.AreEqual(new[] { 1, 2, 3 }, values); + // } + + [TestMethod] + public void TestEnumIsDefined() + { + Assert.IsTrue(Contract.TestEnumIsDefined(1)); + AssertGasConsumed(989700); + Assert.IsTrue(Contract.TestEnumIsDefined(2)); + AssertGasConsumed(989700); + Assert.IsTrue(Contract.TestEnumIsDefined(3)); + AssertGasConsumed(989700); + Assert.IsFalse(Contract.TestEnumIsDefined(0)); + AssertGasConsumed(989700); + Assert.IsFalse(Contract.TestEnumIsDefined(4)); + AssertGasConsumed(989700); + } + + [TestMethod] + public void TestEnumIsDefinedByName() + { + Assert.IsTrue(Contract.TestEnumIsDefinedByName("Value1")); + AssertGasConsumed(989700); + Assert.IsTrue(Contract.TestEnumIsDefinedByName("Value2")); + AssertGasConsumed(989700); + Assert.IsTrue(Contract.TestEnumIsDefinedByName("Value3")); + AssertGasConsumed(989700); + Assert.IsFalse(Contract.TestEnumIsDefinedByName("value1")); + AssertGasConsumed(989700); + Assert.IsFalse(Contract.TestEnumIsDefinedByName("InvalidValue")); + AssertGasConsumed(989700); + } + + [TestMethod] + public void TestEnumGetName() + { + Assert.AreEqual("Value1", Contract.TestEnumGetName(1)); + AssertGasConsumed(989700); + Assert.AreEqual("Value2", Contract.TestEnumGetName(2)); + AssertGasConsumed(989700); + Assert.AreEqual("Value3", Contract.TestEnumGetName(3)); + AssertGasConsumed(989700); + Assert.IsNull(Contract.TestEnumGetName(0)); + AssertGasConsumed(989700); + Assert.IsNull(Contract.TestEnumGetName(4)); + AssertGasConsumed(989700); + } + + [TestMethod] + public void TestEnumGetNameWithType() + { + Assert.AreEqual("Value1", Contract.TestEnumGetNameWithType(1)); + AssertGasConsumed(989700); + Assert.AreEqual("Value2", Contract.TestEnumGetNameWithType(2)); + AssertGasConsumed(989700); + Assert.AreEqual("Value3", Contract.TestEnumGetNameWithType(3)); + AssertGasConsumed(989700); + Assert.IsNull(Contract.TestEnumGetNameWithType(0)); + AssertGasConsumed(989700); + Assert.IsNull(Contract.TestEnumGetNameWithType(4)); + AssertGasConsumed(989700); + } + } +} From 5b463e40752b3a75929d37ccc2d4199058190e81 Mon Sep 17 00:00:00 2001 From: Jim8y Date: Mon, 16 Sep 2024 23:08:39 +0800 Subject: [PATCH 02/11] remove unnecessary --- .../MethodConvert/System/SystemCall.Enum.cs | 2 +- .../System/SystemCall.Register.cs | 81 ++----------------- 2 files changed, 8 insertions(+), 75 deletions(-) diff --git a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Enum.cs b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Enum.cs index 589c72bbd..d3f0b1736 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Enum.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Enum.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2015-2024 The Neo Project. +// Copyright (C) 2015-2024 The Neo Project. // // The Neo.Compiler.CSharp is free software distributed under the MIT // software license, see the accompanying file LICENSE in the main directory diff --git a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs index bc65b310b..730a95fea 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs @@ -36,12 +36,7 @@ private static void RegisterSystemCallHandlers() // BitOperations handlers RegisterBitOperationsHandlers(); - - // Add new handler registrations - RegisterBigIntegerAdditionalHandlers(); - RegisterArrayAdditionalHandlers(); - RegisterMathAdditionalHandlers(); - RegisterConvertHandlers(); + RegisterEnumHandlers(); } @@ -776,73 +771,6 @@ private static void RegisterBitOperationsHandlers() RegisterHandler((ulong value, int offset) => BitOperations.RotateLeft(value, offset), HandleULongRotateLeft); RegisterHandler((uint value, int offset) => BitOperations.RotateRight(value, offset), HandleUIntRotateRight); RegisterHandler((ulong value, int offset) => BitOperations.RotateRight(value, offset), HandleULongRotateRight); - // RegisterHandler((sbyte value) => BitOperations.TrailingZeroCount(value), HandleSByteTrailingZeroCount); - // RegisterHandler((byte value) => BitOperations.TrailingZeroCount(value), HandleByteTrailingZeroCount); - // RegisterHandler((short value) => BitOperations.TrailingZeroCount(value), HandleShortTrailingZeroCount); - // RegisterHandler((ushort value) => BitOperations.TrailingZeroCount(value), HandleUShortTrailingZeroCount); - // RegisterHandler((int value) => BitOperations.TrailingZeroCount(value), HandleIntTrailingZeroCount); - // RegisterHandler((uint value) => BitOperations.TrailingZeroCount(value), HandleUIntTrailingZeroCount); - // RegisterHandler((long value) => BitOperations.TrailingZeroCount(value), HandleLongTrailingZeroCount); - // RegisterHandler((ulong value) => BitOperations.TrailingZeroCount(value), HandleULongTrailingZeroCount); - } - - private static void RegisterBigIntegerAdditionalHandlers() - { - // RegisterHandler((string s, NumberStyles style, IFormatProvider? provider) => BigInteger.Parse(s, style, provider), HandleBigIntegerParseWithStyleAndProvider); - // RegisterHandler((string s, BigInteger result) => BigInteger.TryParse(s, out result), HandleBigIntegerTryParse); - // RegisterHandler((BigInteger value, string? format, IFormatProvider? provider) => value.ToString(format, provider), HandleBigIntegerToStringWithFormatAndProvider); - } - - private static void RegisterArrayAdditionalHandlers() - { - // RegisterHandler((Array sourceArray, Array destinationArray, int length) => Array.Copy(sourceArray, destinationArray, length), HandleArrayCopy); - // RegisterHandler((Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) => Array.Copy(sourceArray, sourceIndex, destinationArray, destinationIndex, length), HandleArrayCopyWithIndices); - // RegisterHandler((Array array) => Array.Clear(array), HandleArrayClear); - // RegisterHandler((Array array, int index, int length) => Array.Clear(array, index, length), HandleArrayClearWithRange); - // RegisterHandler((Array array) => Array.Reverse(array), HandleArrayReverse); - // RegisterHandler((Array array, int index, int length) => Array.Reverse(array, index, length), HandleArrayReverseWithRange); - // RegisterHandler((Array array) => Array.Sort(array), HandleArraySort); - // RegisterHandler((Array keys, Array? items) => Array.Sort(keys, items), HandleArraySortWithItems); - // RegisterHandler((Array array, int index, int length) => Array.Sort(array, index, length), HandleArraySortWithRange); - // RegisterHandler((Array keys, Array? items, int index, int length) => Array.Sort(keys, items, index, length), HandleArraySortWithItemsAndRange); - // RegisterHandler((Array array, object value) => Array.BinarySearch(array, value), HandleArrayBinarySearch); - // RegisterHandler((Array array, int index, int length, object value) => Array.BinarySearch(array, index, length, value), HandleArrayBinarySearchWithRange); - // RegisterHandler((Array array) => Array.IndexOf(array, null), HandleArrayIndexOf); - // RegisterHandler((Array array, object? value) => Array.IndexOf(array, value), HandleArrayIndexOfWithValue); - // RegisterHandler((Array array, object? value, int startIndex) => Array.IndexOf(array, value, startIndex), HandleArrayIndexOfWithValueAndStart); - // RegisterHandler((Array array, object? value, int startIndex, int count) => Array.IndexOf(array, value, startIndex, count), HandleArrayIndexOfWithValueStartAndCount); - // RegisterHandler((Array array) => Array.LastIndexOf(array, null), HandleArrayLastIndexOf); - // RegisterHandler((Array array, object? value) => Array.LastIndexOf(array, value), HandleArrayLastIndexOfWithValue); - // RegisterHandler((Array array, object? value, int startIndex) => Array.LastIndexOf(array, value, startIndex), HandleArrayLastIndexOfWithValueAndStart); - // RegisterHandler((Array array, object? value, int startIndex, int count) => Array.LastIndexOf(array, value, startIndex, count), HandleArrayLastIndexOfWithValueStartAndCount); - // RegisterHandler((Array array) => Array.Exists(array, _ => true), HandleArrayExists); - // RegisterHandler((Array array) => Array.TrueForAll(array, _ => true), HandleArrayTrueForAll); - // RegisterHandler((Array array) => Array.ConvertAll(array, o => o), HandleArrayConvertAll); - } - - private static void RegisterMathAdditionalHandlers() - { - // RegisterHandler((long a) => Math.Sin(a), HandleMathSin); - // RegisterHandler((long a) => Math.Cos(a), HandleMathCos); - // RegisterHandler((long a) => Math.Tan(a), HandleMathTan); - // RegisterHandler((long a) => Math.Log(a), HandleMathLog); - // RegisterHandler((long a) => Math.Log10(a), HandleMathLog10); - } - - private static void RegisterConvertHandlers() - { - // RegisterHandler((object value) => System.Convert.ToInt32(value), HandleConvertToInt32); - // RegisterHandler((object value) => System.Convert.ToString(value), HandleConvertToString); - // RegisterHandler((object value) => System.Convert.ToBoolean(value), HandleConvertToBoolean); - // RegisterHandler((object value) => System.Convert.ToByte(value), HandleConvertToByte); - // RegisterHandler((object value) => System.Convert.ToChar(value), HandleConvertToChar); - // RegisterHandler((object value) => System.Convert.ToInt16(value), HandleConvertToInt16); - // RegisterHandler((object value) => System.Convert.ToInt64(value), HandleConvertToInt64); - // RegisterHandler((object value) => System.Convert.ToSByte(value), HandleConvertToSByte); - // RegisterHandler((object value) => System.Convert.ToSingle(value), HandleConvertToSingle); - // RegisterHandler((object value) => System.Convert.ToUInt16(value), HandleConvertToUInt16); - // RegisterHandler((object value) => System.Convert.ToUInt32(value), HandleConvertToUInt32); - // RegisterHandler((object value) => System.Convert.ToUInt64(value), HandleConvertToUInt64); } private static void RegisterEnumHandlers() @@ -855,11 +783,16 @@ private static void RegisterEnumHandlers() RegisterHandler((Type enumType) => Enum.GetValues(enumType), HandleEnumGetValues); RegisterHandler((Type enumType, object value) => Enum.IsDefined(enumType, value), HandleEnumIsDefined); RegisterHandler((Type enumType, string name) => Enum.IsDefined(enumType, name), HandleEnumIsDefinedByName); - RegisterHandler((Enum value) => Enum.GetName(value.GetType(), value), HandleEnumGetName,"System.Enum.GetName<>()"); + RegisterHandler((Enum value) => Enum.GetName(value.GetType(), value), HandleEnumGetName, "System.Enum.GetName<>()"); RegisterHandler((Type enumType, object value) => Enum.GetName(enumType, value), HandleEnumGetNameWithType, "System.Enum.GetName()"); + + // these two methods will not be supported, since we cannot apply format logic. // RegisterHandler((Enum value) => Enum.Format(value.GetType(), value, "G"), HandleEnumFormat); // RegisterHandler((Type enumType, object value, string format) => Enum.Format(enumType, value, format), HandleEnumFormatWithType); + + // these two methods will not be supported, since we don't have `Type` class support in neo csharp. // RegisterHandler((Enum value) => Enum.GetUnderlyingType(value.GetType()), HandleEnumGetUnderlyingType); // RegisterHandler((Type enumType) => Enum.GetUnderlyingType(enumType), HandleEnumGetUnderlyingTypeWithType); + } } From 014337679ca6276674661f5f0075d931d7b4bb64 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Mon, 16 Sep 2024 23:10:16 +0800 Subject: [PATCH 03/11] Update src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs --- .../MethodConvert/System/SystemCall.Register.cs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs index 730a95fea..2b092dc32 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs @@ -742,20 +742,6 @@ private static void RegisterNullableTypeHandlers() // Bool RegisterHandler((string? value, bool result) => bool.TryParse(value, out result), HandleBoolTryParseWithOut); #endregion - - #region GetValueOrDefault - // RegisterHandler((sbyte? x, sbyte defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableSByteGetValueOrDefaultWithParam); - // RegisterHandler((byte? x, byte defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableByteGetValueOrDefaultWithParam); - // RegisterHandler((short? x, short defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableShortGetValueOrDefaultWithParam); - // RegisterHandler((ushort? x, ushort defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableUShortGetValueOrDefaultWithParam); - // RegisterHandler((int? x, int defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableIntGetValueOrDefaultWithParam); - // RegisterHandler((uint? x, uint defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableUIntGetValueOrDefaultWithParam); - // RegisterHandler((long? x, long defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableLongGetValueOrDefaultWithParam); - // RegisterHandler((ulong? x, ulong defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableULongGetValueOrDefaultWithParam); - // RegisterHandler((BigInteger? x, BigInteger defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableBigIntegerGetValueOrDefaultWithParam); - // RegisterHandler((char? x, char defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableCharGetValueOrDefaultWithParam); - // RegisterHandler((bool? x, bool defaultValue) => x.GetValueOrDefault(defaultValue), HandleNullableBoolGetValueOrDefaultWithParam); - #endregion GetValueOrDefault } private static void RegisterBitOperationsHandlers() From 8e87d3d9e5da79c63a71644ea0b524a1f6efd50e Mon Sep 17 00:00:00 2001 From: Jimmy Date: Mon, 16 Sep 2024 23:10:51 +0800 Subject: [PATCH 04/11] Update src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs --- .../MethodConvert/System/SystemCall.Register.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs index 2b092dc32..1dfa75e15 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs @@ -633,9 +633,6 @@ private static void RegisterCharHandlers() RegisterHandler((char c) => char.IsAscii(c), HandleCharIsAscii); RegisterHandler((char c) => char.IsAsciiDigit(c), HandleCharIsAsciiDigit); RegisterHandler((char c) => char.IsAsciiLetter(c), HandleCharIsAsciiLetter); - RegisterHandler((string s) => char.Parse(s), HandleCharParse); - // RegisterHandler((string s, char result) => char.TryParse(s, out result), HandleCharTryParse); - } private static void RegisterNullableTypeHandlers() From 7d518df077f32525cb0a4b2ecba455633eccd385 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Mon, 16 Sep 2024 23:11:13 +0800 Subject: [PATCH 05/11] Update src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs --- .../MethodConvert/System/SystemCall.Register.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs index 1dfa75e15..b1544bc78 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs @@ -587,13 +587,6 @@ private static void RegisterStringHandlers() RegisterHandler((string s, char trimChar) => s.Trim(trimChar), HandleStringTrimChar); RegisterHandler((string s, string oldValue, string newValue) => s.Replace(oldValue, newValue), HandleStringReplace); RegisterHandler((string s) => s.Length, HandleStringLength); - - // RegisterHandler((string s, char[] separator, StringSplitOptions options) => s.Split(separator, options), HandleStringSplit); - // RegisterHandler((string separator, IEnumerable values) => string.Join(separator, values), HandleStringJoin); - // RegisterHandler((string s, string value) => s.StartsWith(value), HandleStringStartsWith); - // RegisterHandler((string s, int totalWidth, char paddingChar) => s.PadLeft(totalWidth, paddingChar), HandleStringPadLeft); - // RegisterHandler((string s, int totalWidth, char paddingChar) => s.PadRight(totalWidth, paddingChar), HandleStringPadRight); - // RegisterHandler((string s, char[] trimChars) => s.Trim(trimChars), HandleStringTrimWithCharArray); } private static void RegisterArrayHandlers() From 0b0ee9586693142a6c9235bc02c90b2556f9b814 Mon Sep 17 00:00:00 2001 From: Jim8y Date: Mon, 16 Sep 2024 23:15:30 +0800 Subject: [PATCH 06/11] format update --- src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Enum.cs | 2 +- .../MethodConvert/System/SystemCall.Register.cs | 2 +- tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Enum.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Enum.cs b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Enum.cs index d3f0b1736..589c72bbd 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Enum.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Enum.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2015-2024 The Neo Project. +// Copyright (C) 2015-2024 The Neo Project. // // The Neo.Compiler.CSharp is free software distributed under the MIT // software license, see the accompanying file LICENSE in the main directory diff --git a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs index b1544bc78..1a6aaed9b 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs @@ -36,7 +36,7 @@ private static void RegisterSystemCallHandlers() // BitOperations handlers RegisterBitOperationsHandlers(); - + RegisterEnumHandlers(); } diff --git a/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Enum.cs b/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Enum.cs index b0bfe34f7..995cdd730 100644 --- a/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Enum.cs +++ b/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Enum.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract.Testing; using Neo.SmartContract.Testing.Exceptions; using Neo.VM.Types; From b9032a44699f72f8e768737435e32f5c8c49e901 Mon Sep 17 00:00:00 2001 From: Jim8y Date: Mon, 16 Sep 2024 23:27:23 +0800 Subject: [PATCH 07/11] update gas --- .../TestingArtifacts/Contract_Enum.cs | 4 +- .../UnitTest_Enum.cs | 92 ++++++++----------- 2 files changed, 40 insertions(+), 56 deletions(-) diff --git a/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_Enum.cs b/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_Enum.cs index 64b566588..bb118b4b4 100644 --- a/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_Enum.cs +++ b/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_Enum.cs @@ -10,12 +10,12 @@ public abstract class Contract_Enum(Neo.SmartContract.Testing.SmartContractIniti { #region Compiled data - public static Neo.SmartContract.Manifest.ContractManifest Manifest => Neo.SmartContract.Manifest.ContractManifest.Parse(@"{""name"":""Contract_Enum"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""testEnumParse"",""parameters"":[{""name"":""value"",""type"":""String""}],""returntype"":""Any"",""offset"":0,""safe"":false},{""name"":""testEnumParseIgnoreCase"",""parameters"":[{""name"":""value"",""type"":""String""},{""name"":""ignoreCase"",""type"":""Boolean""}],""returntype"":""Any"",""offset"":87,""safe"":false},{""name"":""testEnumTryParse"",""parameters"":[{""name"":""value"",""type"":""String""}],""returntype"":""Boolean"",""offset"":241,""safe"":false},{""name"":""testEnumTryParseIgnoreCase"",""parameters"":[{""name"":""value"",""type"":""String""},{""name"":""ignoreCase"",""type"":""Boolean""}],""returntype"":""Boolean"",""offset"":319,""safe"":false},{""name"":""testEnumGetNames"",""parameters"":[],""returntype"":""Array"",""offset"":475,""safe"":false},{""name"":""testEnumGetValues"",""parameters"":[],""returntype"":""Array"",""offset"":521,""safe"":false},{""name"":""testEnumIsDefined"",""parameters"":[{""name"":""value"",""type"":""Any""}],""returntype"":""Boolean"",""offset"":546,""safe"":false},{""name"":""testEnumIsDefinedByName"",""parameters"":[{""name"":""name"",""type"":""String""}],""returntype"":""Boolean"",""offset"":591,""safe"":false},{""name"":""testEnumGetName"",""parameters"":[{""name"":""value"",""type"":""Integer""}],""returntype"":""String"",""offset"":657,""safe"":false},{""name"":""testEnumGetNameWithType"",""parameters"":[{""name"":""value"",""type"":""Any""}],""returntype"":""String"",""offset"":709,""safe"":false},{""name"":""_initialize"",""parameters"":[],""returntype"":""Void"",""offset"":775,""safe"":false}],""events"":[]},""permissions"":[],""trusts"":[],""extra"":{""nef"":{""optimization"":""All""}}}"); + public static Neo.SmartContract.Manifest.ContractManifest Manifest => Neo.SmartContract.Manifest.ContractManifest.Parse(@"{""name"":""Contract_Enum"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""testEnumParse"",""parameters"":[{""name"":""value"",""type"":""String""}],""returntype"":""Any"",""offset"":0,""safe"":false},{""name"":""testEnumParseIgnoreCase"",""parameters"":[{""name"":""value"",""type"":""String""},{""name"":""ignoreCase"",""type"":""Boolean""}],""returntype"":""Any"",""offset"":87,""safe"":false},{""name"":""testEnumTryParse"",""parameters"":[{""name"":""value"",""type"":""String""}],""returntype"":""Boolean"",""offset"":241,""safe"":false},{""name"":""testEnumTryParseIgnoreCase"",""parameters"":[{""name"":""value"",""type"":""String""},{""name"":""ignoreCase"",""type"":""Boolean""}],""returntype"":""Boolean"",""offset"":319,""safe"":false},{""name"":""testEnumGetNames"",""parameters"":[],""returntype"":""Array"",""offset"":465,""safe"":false},{""name"":""testEnumGetValues"",""parameters"":[],""returntype"":""Array"",""offset"":511,""safe"":false},{""name"":""testEnumIsDefined"",""parameters"":[{""name"":""value"",""type"":""Any""}],""returntype"":""Boolean"",""offset"":536,""safe"":false},{""name"":""testEnumIsDefinedByName"",""parameters"":[{""name"":""name"",""type"":""String""}],""returntype"":""Boolean"",""offset"":581,""safe"":false},{""name"":""testEnumGetName"",""parameters"":[{""name"":""value"",""type"":""Integer""}],""returntype"":""String"",""offset"":647,""safe"":false},{""name"":""testEnumGetNameWithType"",""parameters"":[{""name"":""value"",""type"":""Any""}],""returntype"":""String"",""offset"":699,""safe"":false},{""name"":""_initialize"",""parameters"":[],""returntype"":""Void"",""offset"":765,""safe"":false}],""events"":[]},""permissions"":[],""trusts"":[],""extra"":{""nef"":{""optimization"":""All""}}}"); /// /// Optimization: "All" /// - public static Neo.SmartContract.NefFile Nef => Neo.IO.Helper.AsSerializable(Convert.FromBase64String(@"TkVGM1Rlc3RpbmdFbmdpbmUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP0KA1cAAQwIVGVzdEVudW14SgwGVmFsdWUxlyYHEVNFRUBKDAZWYWx1ZTKXJgcSU0VFQEoMBlZhbHVlM5cmBxNTRUVARQwSTm8gc3VjaCBlbnVtIHZhbHVlOlcAAgwIVGVzdEVudW14eSYuDAAQSnjKtSYiSnhQzkoAYQB7uyQJUVCLUJwi6QBhnwBBnlFQi1CcItxF2yhKeSY0DAZWQUxVRTGXJgdFRUURQEp5JiAMBlZBTFVFMpcmB0VFRRJASnkmDAwGVkFMVUUzIgoMBlZhbHVlM5cmB0VFRRNARUUMEk5vIHN1Y2ggZW51bSB2YWx1ZTpXAAELYAwIVGVzdEVudW14WEVKDAZWYWx1ZTGXJghFRRFgCEBKDAZWYWx1ZTKXJghFRRJgCEBKDAZWYWx1ZTOXJghFRRNgCEBFRRBgCUBXAAILYQwIVGVzdEVudW14eVl5Ji4MABBKeMq1JiJKeFDOSgBhAHu7JAlRUItQnCLpAGGfAEGeUVCLUJwi3EXbKEp5JjwMBlZBTFVFMZcmC0VFRUVFEWEIQEp5JiQMBlZBTFVFMpcmC0VFRUVFEmEIQEp5JgwMBlZBTFVFMyIKDAZWYWx1ZTOXJgtFRUVFRRNhCEBFRUVFRRBhCUAMCFRlc3RFbnVtE8NKEAwGVmFsdWUx0EoRDAZWYWx1ZTLQShIMBlZhbHVlM9BADAhUZXN0RW51bRPDShAR0EoREtBKEhPQQFcAAQwIVGVzdEVudW14ShGXJgZFRQhAShKXJgZFRQhAShOXJgZFRQhARUUJQFcAAQwIVGVzdEVudW14SgwGVmFsdWUxlyYGRUUIQEoMBlZhbHVlMpcmBkVFCEBKDAZWYWx1ZTOXJgZFRQhARUUJQFcAAXhKEZcmDEUMBlZhbHVlMUBKEpcmDEUMBlZhbHVlMkBKE5cmDEUMBlZhbHVlM0BFC0BXAAEMCFRlc3RFbnVteEoRlyYNRUUMBlZhbHVlMUBKEpcmDUVFDAZWYWx1ZTJAShOXJg1FRQwGVmFsdWUzQEVFC0BWAkCtEllC")); + public static Neo.SmartContract.NefFile Nef => Neo.IO.Helper.AsSerializable(Convert.FromBase64String(@"TkVGM1Rlc3RpbmdFbmdpbmUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP0AA1cAAQwIVGVzdEVudW14SgwGVmFsdWUxlyYHEVNFRUBKDAZWYWx1ZTKXJgcSU0VFQEoMBlZhbHVlM5cmBxNTRUVARQwSTm8gc3VjaCBlbnVtIHZhbHVlOlcAAgwIVGVzdEVudW14eSYuDAAQSnjKtSYiSnhQzkoAYQB7uyQJUVCLUJwi6QBhnwBBnlFQi1CcItxF2yhKeSY0DAZWQUxVRTGXJgdFRUURQEp5JiAMBlZBTFVFMpcmB0VFRRJASnkmDAwGVkFMVUUzIgoMBlZhbHVlM5cmB0VFRRNARUUMEk5vIHN1Y2ggZW51bSB2YWx1ZTpXAAELYAwIVGVzdEVudW14WEVKDAZWYWx1ZTGXJghFRRFgCEBKDAZWYWx1ZTKXJghFRRJgCEBKDAZWYWx1ZTOXJghFRRNgCEBFRRBgCUBXAAILYQwIVGVzdEVudW14eVlFJjBQRQwAEEp4yrUmIkp4UM5KAGEAe7skCVFQi1CcIukAYZ8AQZ5RUItQnCLcRdsoSnkmNgwGVkFMVUUxlyYIRUURYQhASnkmIQwGVkFMVUUylyYIRUUSYQhASnkmDAwGVkFMVUUzIgoMBlZhbHVlM5cmCEVFE2EIQEVFEGEJQAwIVGVzdEVudW0Tw0oQDAZWYWx1ZTHQShEMBlZhbHVlMtBKEgwGVmFsdWUz0EAMCFRlc3RFbnVtE8NKEBHQShES0EoSE9BAVwABDAhUZXN0RW51bXhKEZcmBkVFCEBKEpcmBkVFCEBKE5cmBkVFCEBFRQlAVwABDAhUZXN0RW51bXhKDAZWYWx1ZTGXJgZFRQhASgwGVmFsdWUylyYGRUUIQEoMBlZhbHVlM5cmBkVFCEBFRQlAVwABeEoRlyYMRQwGVmFsdWUxQEoSlyYMRQwGVmFsdWUyQEoTlyYMRQwGVmFsdWUzQEULQFcAAQwIVGVzdEVudW14ShGXJg1FRQwGVmFsdWUxQEoSlyYNRUUMBlZhbHVlMkBKE5cmDUVFDAZWYWx1ZTNARUULQFYCQCsBkiY=")); #endregion diff --git a/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Enum.cs b/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Enum.cs index 995cdd730..196e65fed 100644 --- a/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Enum.cs +++ b/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Enum.cs @@ -12,132 +12,116 @@ public class UnitTest_Enum : DebugAndTestBase public void TestEnumParse() { Assert.AreEqual(new Integer(1), Contract.TestEnumParse("Value1")); - AssertGasConsumed(989700); + AssertGasConsumed(1049490); Assert.AreEqual(new Integer(2), Contract.TestEnumParse("Value2")); - AssertGasConsumed(989700); + AssertGasConsumed(1050810); Assert.AreEqual(new Integer(3), Contract.TestEnumParse("Value3")); - AssertGasConsumed(989700); + AssertGasConsumed(1052130); Assert.ThrowsException(() => Contract.TestEnumParse("InvalidValue")); - AssertGasConsumed(989700); + AssertGasConsumed(1067580); } [TestMethod] public void TestEnumParseIgnoreCase() { Assert.AreEqual(new Integer(1), Contract.TestEnumParseIgnoreCase("value1", true)); - AssertGasConsumed(989700); + AssertGasConsumed(1688250); Assert.AreEqual(new Integer(2), Contract.TestEnumParseIgnoreCase("VALUE2", true)); - AssertGasConsumed(989700); + AssertGasConsumed(1686990); Assert.AreEqual(new Integer(3), Contract.TestEnumParseIgnoreCase("VaLuE3", true)); - AssertGasConsumed(989700); + AssertGasConsumed(1689570); Assert.ThrowsException(() => Contract.TestEnumParseIgnoreCase("value1", false)); - AssertGasConsumed(989700); + AssertGasConsumed(1065270); Assert.ThrowsException(() => Contract.TestEnumParseIgnoreCase("InvalidValue", true)); - AssertGasConsumed(989700); + AssertGasConsumed(2098560); } [TestMethod] public void TestEnumTryParse() { Assert.IsTrue(Contract.TestEnumTryParse("Value1")); - AssertGasConsumed(989700); + AssertGasConsumed(1049730); Assert.IsTrue(Contract.TestEnumTryParse("Value2")); - AssertGasConsumed(989700); + AssertGasConsumed(1051050); Assert.IsTrue(Contract.TestEnumTryParse("Value3")); - AssertGasConsumed(989700); + AssertGasConsumed(1052370); Assert.IsFalse(Contract.TestEnumTryParse("InvalidValue")); - AssertGasConsumed(989700); + AssertGasConsumed(1052370); } [TestMethod] public void TestEnumTryParseIgnoreCase() { Assert.IsTrue(Contract.TestEnumTryParseIgnoreCase("value1", true)); - AssertGasConsumed(989700); + AssertGasConsumed(1688610); Assert.IsTrue(Contract.TestEnumTryParseIgnoreCase("VALUE2", true)); - AssertGasConsumed(989700); + AssertGasConsumed(1687350); Assert.IsTrue(Contract.TestEnumTryParseIgnoreCase("VaLuE3", true)); - AssertGasConsumed(989700); + AssertGasConsumed(1689930); Assert.IsFalse(Contract.TestEnumTryParseIgnoreCase("value1", false)); - AssertGasConsumed(989700); + AssertGasConsumed(1050000); Assert.IsFalse(Contract.TestEnumTryParseIgnoreCase("InvalidValue", true)); - AssertGasConsumed(989700); + AssertGasConsumed(2083410); } - // [TestMethod] - // public void TestEnumGetNames() - // { - // var names = Contract.TestEnumGetNames(); - // AssertGasConsumed(989700); - // CollectionAssert.AreEqual(new[] { "Value1", "Value2", "Value3" }, names); - // } - // - // [TestMethod] - // public void TestEnumGetValues() - // { - // var values = Contract.TestEnumGetValues(); - // AssertGasConsumed(989700); - // CollectionAssert.AreEqual(new[] { 1, 2, 3 }, values); - // } - [TestMethod] public void TestEnumIsDefined() { Assert.IsTrue(Contract.TestEnumIsDefined(1)); - AssertGasConsumed(989700); + AssertGasConsumed(1049010); Assert.IsTrue(Contract.TestEnumIsDefined(2)); - AssertGasConsumed(989700); + AssertGasConsumed(1050120); Assert.IsTrue(Contract.TestEnumIsDefined(3)); - AssertGasConsumed(989700); + AssertGasConsumed(1051230); Assert.IsFalse(Contract.TestEnumIsDefined(0)); - AssertGasConsumed(989700); + AssertGasConsumed(1051230); Assert.IsFalse(Contract.TestEnumIsDefined(4)); - AssertGasConsumed(989700); + AssertGasConsumed(1051230); } [TestMethod] public void TestEnumIsDefinedByName() { Assert.IsTrue(Contract.TestEnumIsDefinedByName("Value1")); - AssertGasConsumed(989700); + AssertGasConsumed(1049430); Assert.IsTrue(Contract.TestEnumIsDefinedByName("Value2")); - AssertGasConsumed(989700); + AssertGasConsumed(1050750); Assert.IsTrue(Contract.TestEnumIsDefinedByName("Value3")); - AssertGasConsumed(989700); + AssertGasConsumed(1052070); Assert.IsFalse(Contract.TestEnumIsDefinedByName("value1")); - AssertGasConsumed(989700); + AssertGasConsumed(1052070); Assert.IsFalse(Contract.TestEnumIsDefinedByName("InvalidValue")); - AssertGasConsumed(989700); + AssertGasConsumed(1052070); } [TestMethod] public void TestEnumGetName() { Assert.AreEqual("Value1", Contract.TestEnumGetName(1)); - AssertGasConsumed(989700); + AssertGasConsumed(1048920); Assert.AreEqual("Value2", Contract.TestEnumGetName(2)); - AssertGasConsumed(989700); + AssertGasConsumed(1050030); Assert.AreEqual("Value3", Contract.TestEnumGetName(3)); - AssertGasConsumed(989700); + AssertGasConsumed(1051140); Assert.IsNull(Contract.TestEnumGetName(0)); - AssertGasConsumed(989700); + AssertGasConsumed(1050930); Assert.IsNull(Contract.TestEnumGetName(4)); - AssertGasConsumed(989700); + AssertGasConsumed(1050930); } [TestMethod] public void TestEnumGetNameWithType() { Assert.AreEqual("Value1", Contract.TestEnumGetNameWithType(1)); - AssertGasConsumed(989700); + AssertGasConsumed(1049220); Assert.AreEqual("Value2", Contract.TestEnumGetNameWithType(2)); - AssertGasConsumed(989700); + AssertGasConsumed(1050330); Assert.AreEqual("Value3", Contract.TestEnumGetNameWithType(3)); - AssertGasConsumed(989700); + AssertGasConsumed(1051440); Assert.IsNull(Contract.TestEnumGetNameWithType(0)); - AssertGasConsumed(989700); + AssertGasConsumed(1051230); Assert.IsNull(Contract.TestEnumGetNameWithType(4)); - AssertGasConsumed(989700); + AssertGasConsumed(1051230); } } } From 3e525de13365e3b6b7bba6696f3f5cefc8873a6e Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 17 Sep 2024 01:56:59 -0700 Subject: [PATCH 08/11] Update src/Neo.Compiler.CSharp/Diagnostic/DiagnosticId.cs --- src/Neo.Compiler.CSharp/Diagnostic/DiagnosticId.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Neo.Compiler.CSharp/Diagnostic/DiagnosticId.cs b/src/Neo.Compiler.CSharp/Diagnostic/DiagnosticId.cs index 0ed8c130f..c614f5be1 100644 --- a/src/Neo.Compiler.CSharp/Diagnostic/DiagnosticId.cs +++ b/src/Neo.Compiler.CSharp/Diagnostic/DiagnosticId.cs @@ -36,7 +36,6 @@ static class DiagnosticId public const string InvalidInitialValue = "NC3005"; public const string IncorrectNEPStandard = "NC3006"; public const string CapturedStaticFieldNotFound = "NC3007"; - public const string InvalidType = "NC3008"; public const string InvalidArgument = "NC3009"; } From 231d76259eaeac688beaf1b73c249a8a03c1463a Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 17 Sep 2024 01:58:09 -0700 Subject: [PATCH 09/11] Update tests/Neo.Compiler.CSharp.TestContracts/Contract_Enum.cs --- tests/Neo.Compiler.CSharp.TestContracts/Contract_Enum.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Neo.Compiler.CSharp.TestContracts/Contract_Enum.cs b/tests/Neo.Compiler.CSharp.TestContracts/Contract_Enum.cs index 0b0bde00e..b08d3ce51 100644 --- a/tests/Neo.Compiler.CSharp.TestContracts/Contract_Enum.cs +++ b/tests/Neo.Compiler.CSharp.TestContracts/Contract_Enum.cs @@ -4,7 +4,6 @@ namespace Neo.Compiler.CSharp.TestContracts { - public class Contract_Enum : SmartContract.Framework.SmartContract { public enum TestEnum From 0f989e0b80f7910d79c4cb93548261693fc673db Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 17 Sep 2024 01:59:31 -0700 Subject: [PATCH 10/11] Apply suggestions from code review --- .../MethodConvert/System/SystemCall.Register.cs | 1 - src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs index 1a6aaed9b..b0253f5e9 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Register.cs @@ -769,6 +769,5 @@ private static void RegisterEnumHandlers() // these two methods will not be supported, since we don't have `Type` class support in neo csharp. // RegisterHandler((Enum value) => Enum.GetUnderlyingType(value.GetType()), HandleEnumGetUnderlyingType); // RegisterHandler((Type enumType) => Enum.GetUnderlyingType(enumType), HandleEnumGetUnderlyingTypeWithType); - } } diff --git a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.cs b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.cs index 948049989..b04392fe0 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.cs @@ -70,7 +70,6 @@ private static void RegisterHandler(Expression(Expression> expression, SystemCallHandler handler) { var key = GetKeyFromExpression(expression, typeof(T)); From 316ce52d45daf7a7cebe62816dd5c064af53d441 Mon Sep 17 00:00:00 2001 From: Jim8y Date: Tue, 17 Sep 2024 21:41:10 +0800 Subject: [PATCH 11/11] apply shargon suggestion --- .../MethodConvert/System/SystemCall.cs | 4 +- .../AnalyzerReleases.Unshipped.md | 1 + .../EnumMethodsUsageAnalyzer.cs | 54 +++++++++++++ .../EnumMethodsUsageAnalyzerUnitTests.cs | 75 +++++++++++++++++++ 4 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 src/Neo.SmartContract.Analyzer/EnumMethodsUsageAnalyzer.cs create mode 100644 tests/Neo.SmartContract.Analyzer.UnitTests/EnumMethodsUsageAnalyzerUnitTests.cs diff --git a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.cs b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.cs index b04392fe0..2af124b3f 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.cs @@ -271,8 +271,8 @@ private bool TryProcessSystemMethods(SemanticModel model, IMethodSymbol symbol, var key = symbol.ToString()!.Replace("out ", ""); key = (from parameter in symbol.Parameters let parameterType = parameter.Type.ToString() where !parameter.Type.IsValueType && parameterType!.EndsWith('?') select parameterType).Aggregate(key, (current, parameterType) => current.Replace(parameterType, parameterType[..^1])); if (key == "string.ToString()") key = "object.ToString()"; - if (key.Contains("System.Enum.GetName<")) key = "System.Enum.GetName<>()"; - if (key.Contains("System.Enum.GetName(")) key = "System.Enum.GetName()"; + if (key.StartsWith("System.Enum.GetName<")) key = "System.Enum.GetName<>()"; + if (key.StartsWith("System.Enum.GetName(")) key = "System.Enum.GetName()"; if (!SystemCallHandlers.TryGetValue(key, out var handler)) return false; handler(this, model, symbol, instanceExpression, arguments); return true; diff --git a/src/Neo.SmartContract.Analyzer/AnalyzerReleases.Unshipped.md b/src/Neo.SmartContract.Analyzer/AnalyzerReleases.Unshipped.md index 98696f2c2..4daf45bbd 100644 --- a/src/Neo.SmartContract.Analyzer/AnalyzerReleases.Unshipped.md +++ b/src/Neo.SmartContract.Analyzer/AnalyzerReleases.Unshipped.md @@ -24,3 +24,4 @@ NC4021 | Usage | Warning | SupportedStandardsAnalyzer NC4022 | Usage | Warning | BigIntegerUsingUsageAnalyzer NC4023 | Usage | Error | StaticFieldInitializationAnalyzer NC4024 | Usage | Error | MultipleCatchBlockAnalyzer +NC4025 | Method | Error | EnumMethodsUsageAnalyzer diff --git a/src/Neo.SmartContract.Analyzer/EnumMethodsUsageAnalyzer.cs b/src/Neo.SmartContract.Analyzer/EnumMethodsUsageAnalyzer.cs new file mode 100644 index 000000000..89d285c6e --- /dev/null +++ b/src/Neo.SmartContract.Analyzer/EnumMethodsUsageAnalyzer.cs @@ -0,0 +1,54 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; +using System.Linq; + +namespace Neo.SmartContract.Analyzer +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class EnumMethodsUsageAnalyzer : DiagnosticAnalyzer + { + public const string DiagnosticId = "NC4025"; + + private readonly string[] _unsupportedEnumMethods = { + "Format", + "GetUnderlyingType" + }; + + private static readonly DiagnosticDescriptor Rule = new( + DiagnosticId, + "Unsupported Enum method is used", + "Unsupported Enum method: {0}", + "Method", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics { get; } = + ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterSyntaxNodeAction(AnalyzeSyntax, SyntaxKind.InvocationExpression); + } + + private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) + { + if (context.Node is not InvocationExpressionSyntax invocationExpression) return; + + var methodSymbol = context.SemanticModel.GetSymbolInfo(invocationExpression).Symbol as IMethodSymbol; + + if (methodSymbol is not { ContainingType.SpecialType: SpecialType.System_Enum } || + !_unsupportedEnumMethods.Contains(methodSymbol.Name)) return; + + var diagnostic = Diagnostic.Create(Rule, + invocationExpression.GetLocation(), + methodSymbol.Name); + + context.ReportDiagnostic(diagnostic); + } + } +} diff --git a/tests/Neo.SmartContract.Analyzer.UnitTests/EnumMethodsUsageAnalyzerUnitTests.cs b/tests/Neo.SmartContract.Analyzer.UnitTests/EnumMethodsUsageAnalyzerUnitTests.cs new file mode 100644 index 000000000..18a003314 --- /dev/null +++ b/tests/Neo.SmartContract.Analyzer.UnitTests/EnumMethodsUsageAnalyzerUnitTests.cs @@ -0,0 +1,75 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Threading.Tasks; +using VerifyCS = Microsoft.CodeAnalysis.CSharp.Testing.XUnit.AnalyzerVerifier< + Neo.SmartContract.Analyzer.EnumMethodsUsageAnalyzer>; + +namespace Neo.SmartContract.Analyzer.UnitTests +{ + [TestClass] + public class EnumMethodsUsageAnalyzerUnitTests + { + private const string DiagnosticId = EnumMethodsUsageAnalyzer.DiagnosticId; + + [TestMethod] + public async Task UnsupportedEnumFormat_ReportsDiagnostic() + { + var test = @" +using System; + +class TestClass +{ + void TestMethod() + { + var result = Enum.Format(typeof(DayOfWeek), DayOfWeek.Monday, ""G""); + } +}"; + + var expected = VerifyCS.Diagnostic(DiagnosticId) + .WithLocation(8, 22) // Line 8, column 22 + .WithArguments("Format"); + await VerifyCS.VerifyAnalyzerAsync(test, expected); + } + + [TestMethod] + public async Task UnsupportedEnumGetUnderlyingType_ReportsDiagnostic() + { + var test = @" +using System; + +class TestClass +{ + void TestMethod() + { + var result = Enum.GetUnderlyingType(typeof(DayOfWeek)); + } +}"; + + var expected = VerifyCS.Diagnostic(DiagnosticId) + .WithSpan(8, 22, 8, 63) + .WithArguments("GetUnderlyingType"); + await VerifyCS.VerifyAnalyzerAsync(test, expected); + } + + [TestMethod] + public async Task SupportedEnumMethods_NoDiagnostic() + { + var test = @" +using System; + +class TestClass +{ + void TestMethod() + { + var parsed = Enum.Parse(typeof(DayOfWeek), ""Monday""); + var tryParsed = Enum.TryParse(typeof(DayOfWeek), ""Tuesday"", out var result); + var names = Enum.GetNames(typeof(DayOfWeek)); + var values = Enum.GetValues(typeof(DayOfWeek)); + var isDefined = Enum.IsDefined(typeof(DayOfWeek), ""Wednesday""); + var name = Enum.GetName(typeof(DayOfWeek), DayOfWeek.Thursday); + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(test); + } + } +}