From 749ffaed0465f23faf2bd6e632b05b8864dc9be3 Mon Sep 17 00:00:00 2001 From: nan01ab Date: Thu, 3 Oct 2024 10:40:45 +0800 Subject: [PATCH 1/3] fix: string.Trim only trim ' ' --- .../MethodConvert/System/SystemCall.String.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.String.cs b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.String.cs index 7d1aae642..8b2f4c790 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.String.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.String.cs @@ -318,8 +318,16 @@ private static void HandleStringTrim(MethodConvert methodConvert, SemanticModel methodConvert.AddInstruction(OpCode.DUP); // Duplicate the index methodConvert.AddInstruction(OpCode.LDARG0); // Load the string methodConvert.AddInstruction(OpCode.PICKITEM); // Get the character at the current index + + methodConvert.AddInstruction(OpCode.DUP); + methodConvert.Push((ushort)'\t'); + methodConvert.Push((ushort)'\r' + 1); + methodConvert.AddInstruction(OpCode.WITHIN); // check if '\t' <= c <= '\r' + methodConvert.AddInstruction(OpCode.SWAP); + methodConvert.Push((ushort)' '); // Push space character methodConvert.AddInstruction(OpCode.EQUAL); // Check if character is a space + methodConvert.AddInstruction(OpCode.BOOLOR); // check if '\t' <= c <= '\r' or ' ' == c methodConvert.Jump(OpCode.JMPIFNOT, loopEnd); // If not, exit the loop methodConvert.AddInstruction(OpCode.INC); // Increment the index @@ -344,8 +352,16 @@ private static void HandleStringTrim(MethodConvert methodConvert, SemanticModel methodConvert.AddInstruction(OpCode.DUP); // Duplicate the index methodConvert.AddInstruction(OpCode.LDARG0); // Load the string methodConvert.AddInstruction(OpCode.PICKITEM); // Get the character at the current index + + methodConvert.AddInstruction(OpCode.DUP); + methodConvert.Push((ushort)'\t'); + methodConvert.Push((ushort)'\r' + 1); + methodConvert.AddInstruction(OpCode.WITHIN); // check if '\t' <= c <= '\r' + methodConvert.AddInstruction(OpCode.SWAP); + methodConvert.Push((ushort)' '); // Push space character methodConvert.AddInstruction(OpCode.EQUAL); // Check if character is a space + methodConvert.AddInstruction(OpCode.BOOLOR); // check if '\t' <= c <= '\r' or ' ' == c methodConvert.Jump(OpCode.JMPIFNOT, loopEnd2); // If not, exit the loop methodConvert.AddInstruction(OpCode.DEC); // Decrement the index From d9841fed42ef8f293e1d26bc54b0e46a1b4aa354 Mon Sep 17 00:00:00 2001 From: Jim8y Date: Thu, 3 Oct 2024 22:21:42 +0800 Subject: [PATCH 2/3] add unit test --- .../StringMethodUsageAnalyzer.cs | 2 +- .../Contract_String.cs | 4 +++ .../TestingArtifacts/Contract_String.cs | 10 +++++-- .../UnitTest_String.cs | 26 +++++++++++++++++++ 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/Neo.SmartContract.Analyzer/StringMethodUsageAnalyzer.cs b/src/Neo.SmartContract.Analyzer/StringMethodUsageAnalyzer.cs index a2f059333..24177bb80 100644 --- a/src/Neo.SmartContract.Analyzer/StringMethodUsageAnalyzer.cs +++ b/src/Neo.SmartContract.Analyzer/StringMethodUsageAnalyzer.cs @@ -23,7 +23,7 @@ public class StringMethodUsageAnalyzer : DiagnosticAnalyzer "Normalize", "PadLeft", "PadRight", "Remove", "Replace", "Split", "ToCharArray", "ToLower", "ToLowerInvariant", - "ToUpper", "ToUpperInvariant", "Trim", "TrimEnd", + "ToUpper", "ToUpperInvariant", "TrimEnd", "TrimStart" }; diff --git a/tests/Neo.Compiler.CSharp.TestContracts/Contract_String.cs b/tests/Neo.Compiler.CSharp.TestContracts/Contract_String.cs index 1fd6b19c7..48bb6a6ae 100644 --- a/tests/Neo.Compiler.CSharp.TestContracts/Contract_String.cs +++ b/tests/Neo.Compiler.CSharp.TestContracts/Contract_String.cs @@ -74,5 +74,9 @@ public static string TestInterpolatedStringHandler() $"ECPoint: {ecPointValue}, ByteString: {byteStringValue}, Bool: {boolValue}"; return str; } + public static string TestTrim(string str) + { + return str.Trim(); + } } } diff --git a/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_String.cs b/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_String.cs index 8d65af839..a0fa909c5 100644 --- a/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_String.cs +++ b/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_String.cs @@ -10,12 +10,12 @@ public abstract class Contract_String(Neo.SmartContract.Testing.SmartContractIni { #region Compiled data - public static Neo.SmartContract.Manifest.ContractManifest Manifest => Neo.SmartContract.Manifest.ContractManifest.Parse(@"{""name"":""Contract_String"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""testMain"",""parameters"":[],""returntype"":""Void"",""offset"":0,""safe"":false},{""name"":""testEqual"",""parameters"":[],""returntype"":""Void"",""offset"":82,""safe"":false},{""name"":""testSubstring"",""parameters"":[],""returntype"":""Void"",""offset"":127,""safe"":false},{""name"":""testEmpty"",""parameters"":[],""returntype"":""String"",""offset"":163,""safe"":false},{""name"":""testIsNullOrEmpty"",""parameters"":[{""name"":""str"",""type"":""String""}],""returntype"":""Boolean"",""offset"":166,""safe"":false},{""name"":""testEndWith"",""parameters"":[{""name"":""str"",""type"":""String""}],""returntype"":""Boolean"",""offset"":181,""safe"":false},{""name"":""testContains"",""parameters"":[{""name"":""str"",""type"":""String""}],""returntype"":""Boolean"",""offset"":220,""safe"":false},{""name"":""testIndexOf"",""parameters"":[{""name"":""str"",""type"":""String""}],""returntype"":""Integer"",""offset"":237,""safe"":false},{""name"":""testInterpolatedStringHandler"",""parameters"":[],""returntype"":""String"",""offset"":252,""safe"":false}],""events"":[]},""permissions"":[{""contract"":""0xacce6fd80d44e1796aa0c2c625e9e4e0ce39efc0"",""methods"":[""itoa"",""memorySearch""]},{""contract"":""0xda65b600f7124ce6c79950c1772a36403104f2be"",""methods"":[""currentHash"",""getBlock""]}],""trusts"":[],""extra"":{""nef"":{""optimization"":""All""}}}"); + public static Neo.SmartContract.Manifest.ContractManifest Manifest => Neo.SmartContract.Manifest.ContractManifest.Parse(@"{""name"":""Contract_String"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""testMain"",""parameters"":[],""returntype"":""Void"",""offset"":0,""safe"":false},{""name"":""testEqual"",""parameters"":[],""returntype"":""Void"",""offset"":82,""safe"":false},{""name"":""testSubstring"",""parameters"":[],""returntype"":""Void"",""offset"":127,""safe"":false},{""name"":""testEmpty"",""parameters"":[],""returntype"":""String"",""offset"":163,""safe"":false},{""name"":""testIsNullOrEmpty"",""parameters"":[{""name"":""str"",""type"":""String""}],""returntype"":""Boolean"",""offset"":166,""safe"":false},{""name"":""testEndWith"",""parameters"":[{""name"":""str"",""type"":""String""}],""returntype"":""Boolean"",""offset"":181,""safe"":false},{""name"":""testContains"",""parameters"":[{""name"":""str"",""type"":""String""}],""returntype"":""Boolean"",""offset"":220,""safe"":false},{""name"":""testIndexOf"",""parameters"":[{""name"":""str"",""type"":""String""}],""returntype"":""Integer"",""offset"":237,""safe"":false},{""name"":""testInterpolatedStringHandler"",""parameters"":[],""returntype"":""String"",""offset"":252,""safe"":false},{""name"":""testTrim"",""parameters"":[{""name"":""str"",""type"":""String""}],""returntype"":""String"",""offset"":579,""safe"":false}],""events"":[]},""permissions"":[{""contract"":""0xacce6fd80d44e1796aa0c2c625e9e4e0ce39efc0"",""methods"":[""itoa"",""memorySearch""]},{""contract"":""0xda65b600f7124ce6c79950c1772a36403104f2be"",""methods"":[""currentHash"",""getBlock""]}],""trusts"":[],""extra"":{""nef"":{""optimization"":""All""}}}"); /// /// Optimization: "All" /// - public static Neo.SmartContract.NefFile Nef => Neo.IO.Helper.AsSerializable(Convert.FromBase64String(@"TkVGM1Rlc3RpbmdFbmdpbmUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS+8gQxQDYqd8FQmcfmTBL3ALZl2ghnZXRCbG9jawEAAQ++8gQxQDYqd8FQmcfmTBL3ALZl2gtjdXJyZW50SGFzaAAAAQ/A7znO4OTpJcbCoGp54UQN2G/OrARpdG9hAQABD8DvOc7g5OklxsKgannhRA3Yb86sDG1lbW9yeVNlYXJjaAIAAQ8AAP1DAlcDAAwETWFya3AMAHE3AQA3AAAUznIMB0hlbGxvLCBoiwwBIItpiwwXISBDdXJyZW50IHRpbWVzdGFtcCBpcyCLajcCAIsMAS6L2yhBz+dHlkBXAgAMBWhlbGxvcAwFaGVsbG9xaGmXJAsMBUZhbHNlIggMBFRydWVBz+dHlkBXAQAMCDAxMjM0NTY3cGgRS8pLn4xBz+dHlmgRFIxBz+dHlkAMAEBXAAF4StgkBsoQs0BFCEBXAAEMBXdvcmxkeErKUUrKShNSUJ9KECwIRUVFRQlAE1JTjNsol0BXAAEMBXdvcmxkeDcDABC4QFcAAQwFd29ybGR4NwMAQFcEAAQAAKDexa3JNTYAAAAAAAAAcAwiTlhWN1poSGl5TTFhSFh3cFZzUlpDNkJ3TkZQMmpnaFhBcXEMAwECA9swcgwHU0J5dGU6IADWNwIAiwwILCBCeXRlOiCLACo3AgCLDAosIFVTaG9ydDogiwHoAzcCAIsMAiwgi9soDAZVSW50OiACQEIPADcCAIsMCSwgVUxvbmc6IIsDABCl1OgAAAA3AgCLDAIsIIvbKIvbKAwMQmlnSW50ZWdlcjogaDcCAIsMCCwgQ2hhcjogiwBB2yiLDAosIFN0cmluZzogiwwFSGVsbG+LDAIsIIvbKIvbKAwJRUNQb2ludDogaYsMDiwgQnl0ZVN0cmluZzogiwwNU3lzdGVtLkJ5dGVbXYsMCCwgQm9vbDogiwgmCgwEVHJ1ZSIJDAVGYWxzZYvbKIvbKHNrQENTLRg=")); + public static Neo.SmartContract.NefFile Nef => Neo.IO.Helper.AsSerializable(Convert.FromBase64String(@"TkVGM1Rlc3RpbmdFbmdpbmUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS+8gQxQDYqd8FQmcfmTBL3ALZl2ghnZXRCbG9jawEAAQ++8gQxQDYqd8FQmcfmTBL3ALZl2gtjdXJyZW50SGFzaAAAAQ/A7znO4OTpJcbCoGp54UQN2G/OrARpdG9hAQABD8DvOc7g5OklxsKgannhRA3Yb86sDG1lbW9yeVNlYXJjaAIAAQ8AAP1+AlcDAAwETWFya3AMAHE3AQA3AAAUznIMB0hlbGxvLCBoiwwBIItpiwwXISBDdXJyZW50IHRpbWVzdGFtcCBpcyCLajcCAIsMAS6L2yhBz+dHlkBXAgAMBWhlbGxvcAwFaGVsbG9xaGmXJAsMBUZhbHNlIggMBFRydWVBz+dHlkBXAQAMCDAxMjM0NTY3cGgRS8pLn4xBz+dHlmgRFIxBz+dHlkAMAEBXAAF4StgkBsoQs0BFCEBXAAEMBXdvcmxkeErKUUrKShNSUJ9KECwIRUVFRQlAE1JTjNsol0BXAAEMBXdvcmxkeDcDABC4QFcAAQwFd29ybGR4NwMAQFcEAAQAAKDexa3JNTYAAAAAAAAAcAwiTlhWN1poSGl5TTFhSFh3cFZzUlpDNkJ3TkZQMmpnaFhBcXEMAwECA9swcgwHU0J5dGU6IADWNwIAiwwILCBCeXRlOiCLACo3AgCLDAosIFVTaG9ydDogiwHoAzcCAIsMAiwgi9soDAZVSW50OiACQEIPADcCAIsMCSwgVUxvbmc6IIsDABCl1OgAAAA3AgCLDAIsIIvbKIvbKAwMQmlnSW50ZWdlcjogaDcCAIsMCCwgQ2hhcjogiwBB2yiLDAosIFN0cmluZzogiwwFSGVsbG+LDAIsIIvbKIvbKAwJRUNQb2ludDogaYsMDiwgQnl0ZVN0cmluZzogiwwNU3lzdGVtLkJ5dGVbXYsMCCwgQm9vbDogiwgmCgwEVHJ1ZSIJDAVGYWxzZYvbKIvbKHNrQFcAAXhKEEp4yrUmE0p4zkoZHrtQACCXrCYFnCLrjErKnUoQtyYTSnjOShkeu1AAIJesJgWdIuyM2yhAxxhSIg==")); #endregion @@ -75,6 +75,12 @@ public abstract class Contract_String(Neo.SmartContract.Testing.SmartContractIni [DisplayName("testSubstring")] public abstract void TestSubstring(); + /// + /// Unsafe method + /// + [DisplayName("testTrim")] + public abstract string? TestTrim(string? str); + #endregion } diff --git a/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_String.cs b/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_String.cs index aad529e80..c459b0f62 100644 --- a/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_String.cs +++ b/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_String.cs @@ -122,5 +122,31 @@ public void Test_TestInterpolatedStringHandler() Assert.AreEqual("SByte: -42, Byte: 42, UShort: 1000, UInt: 1000000, ULong: 1000000000000, BigInteger: 1000000000000000000000, Char: A, String: Hello, ECPoint: NXV7ZhHiyM1aHXwpVsRZC6BwNFP2jghXAq, ByteString: System.Byte[], Bool: True", Contract.TestInterpolatedStringHandler()); Assert.AreEqual(11313480, Engine.FeeConsumed.Value); } + + [TestMethod] + public void Test_TestTrim() + { + Assert.AreEqual("Hello, World!", Contract.TestTrim(" Hello, World! ")); + AssertGasConsumed(1357650); + + Assert.AreEqual("No Trim", Contract.TestTrim("No Trim")); + AssertGasConsumed(1357650); + + Assert.AreEqual("", Contract.TestTrim(" ")); + AssertGasConsumed(1357650); + + Assert.AreEqual(null, Contract.TestTrim(null)); + AssertGasConsumed(1047300); + + // Test various whitespace characters + Assert.AreEqual("Trim Test", Contract.TestTrim("\t\n\r Trim Test \t\n\r")); + AssertGasConsumed(1357650); + + Assert.AreEqual("Multiple Spaces", Contract.TestTrim(" Multiple Spaces ")); + AssertGasConsumed(1357650); + + Assert.AreEqual("Mix of Whitespace", Contract.TestTrim(" \t \n \r Mix of Whitespace \r \n \t ")); + AssertGasConsumed(1357650); + } } } From 2015a6063d02fa3346a6018ef97ad22908485c9a Mon Sep 17 00:00:00 2001 From: Jim8y Date: Fri, 4 Oct 2024 15:49:35 +0800 Subject: [PATCH 3/3] fix trim --- .../MethodConvert/System/SystemCall.String.cs | 178 ++++++++++++------ .../TestingArtifacts/Contract_String.cs | 2 +- .../UnitTest_String.cs | 16 +- 3 files changed, 125 insertions(+), 71 deletions(-) diff --git a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.String.cs b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.String.cs index 8b2f4c790..4c5059027 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.String.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.String.cs @@ -300,77 +300,135 @@ private static void HandleStringTrim(MethodConvert methodConvert, SemanticModel if (arguments is not null) methodConvert.PrepareArgumentsForMethod(model, symbol, arguments); - methodConvert.AddInstruction(OpCode.LDARG0); // Load the string + var strLen = methodConvert.AddAnonymousVariable(); + var startIndex = methodConvert.AddAnonymousVariable(); + var endIndex = methodConvert.AddAnonymousVariable(); - // Trim leading whitespace - methodConvert.AddInstruction(OpCode.DUP); // Duplicate the string - methodConvert.AddInstruction(OpCode.PUSH0); // Push the initial index (0) + InitStrLen(); // strLen = string.Length + InitStartIndex(); // startIndex = 0 + InitEndIndex(); // endIndex = string.Length - 1 + + // loop to trim leading whitespace var loopStart = new JumpTarget(); var loopEnd = new JumpTarget(); 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 - methodConvert.AddInstruction(OpCode.LDARG0); // Load the string - methodConvert.AddInstruction(OpCode.PICKITEM); // Get the character at the current index - - methodConvert.AddInstruction(OpCode.DUP); - methodConvert.Push((ushort)'\t'); - methodConvert.Push((ushort)'\r' + 1); - methodConvert.AddInstruction(OpCode.WITHIN); // check if '\t' <= c <= '\r' - methodConvert.AddInstruction(OpCode.SWAP); - - methodConvert.Push((ushort)' '); // Push space character - methodConvert.AddInstruction(OpCode.EQUAL); // Check if character is a space - methodConvert.AddInstruction(OpCode.BOOLOR); // check if '\t' <= c <= '\r' or ' ' == c - methodConvert.Jump(OpCode.JMPIFNOT, loopEnd); // If not, exit the loop - - methodConvert.AddInstruction(OpCode.INC); // Increment the index - methodConvert.Jump(OpCode.JMP, loopStart); // Jump to the start of the loop - + CheckStartIndex(loopEnd); + PickCharStart(); // pick a char to check + CheckWithin(loopEnd); + MoveStartIndexAndLoop(loopStart); loopEnd.Instruction = methodConvert.AddInstruction(OpCode.NOP); - methodConvert.AddInstruction(OpCode.SUBSTR); // Get the substring from the first non-space character - // Trim trailing whitespace - methodConvert.AddInstruction(OpCode.DUP); // Duplicate the string - methodConvert.AddInstruction(OpCode.SIZE); // Get the length of the string - methodConvert.AddInstruction(OpCode.DEC); // Decrement the length to get the last index + // done processing leading whitespace, start processing trailing whitespace var loopStart2 = new JumpTarget(); var loopEnd2 = new JumpTarget(); loopStart2.Instruction = methodConvert.AddInstruction(OpCode.NOP); - - methodConvert.AddInstruction(OpCode.DUP); // Duplicate the index - methodConvert.AddInstruction(OpCode.PUSH0); // Push 0 - methodConvert.AddInstruction(OpCode.GT); // Check if index > 0 - methodConvert.Jump(OpCode.JMPIFNOT, loopEnd2); // If not, exit the loop - - methodConvert.AddInstruction(OpCode.DUP); // Duplicate the index - methodConvert.AddInstruction(OpCode.LDARG0); // Load the string - methodConvert.AddInstruction(OpCode.PICKITEM); // Get the character at the current index - - methodConvert.AddInstruction(OpCode.DUP); - methodConvert.Push((ushort)'\t'); - methodConvert.Push((ushort)'\r' + 1); - methodConvert.AddInstruction(OpCode.WITHIN); // check if '\t' <= c <= '\r' - methodConvert.AddInstruction(OpCode.SWAP); - - methodConvert.Push((ushort)' '); // Push space character - methodConvert.AddInstruction(OpCode.EQUAL); // Check if character is a space - methodConvert.AddInstruction(OpCode.BOOLOR); // check if '\t' <= c <= '\r' or ' ' == c - methodConvert.Jump(OpCode.JMPIFNOT, loopEnd2); // If not, exit the loop - - methodConvert.AddInstruction(OpCode.DEC); // Decrement the index - methodConvert.Jump(OpCode.JMP, loopStart2); // Jump to the start of the loop - + CheckEndIndex(loopEnd2); + PickCharEnd(); // pick a char to check + CheckWithin(loopEnd2); + MoveEndIndexAndLoop(loopStart2); loopEnd2.Instruction = methodConvert.AddInstruction(OpCode.NOP); + GetString(); + GetStartIndex(); + GetEndIndex(); + GetStartIndex(); + methodConvert.AddInstruction(OpCode.SUB); + methodConvert.AddInstruction(OpCode.INC); methodConvert.AddInstruction(OpCode.SUBSTR); // Get the substring up to the last non-space character - - methodConvert.ChangeType(VM.Types.StackItemType.ByteString); // Convert the array to a byte string + return; + + void InitStrLen() + { + GetString(); + methodConvert.AddInstruction(OpCode.SIZE); + methodConvert.AccessSlot(OpCode.STLOC, strLen); + } + + // Local function to get strLen + void GetStrLen() => methodConvert.AccessSlot(OpCode.LDLOC, strLen); + + void InitStartIndex() + { + methodConvert.Push(0); + methodConvert.AccessSlot(OpCode.STLOC, startIndex); + } + + void InitEndIndex() + { + GetStrLen(); + methodConvert.AddInstruction(OpCode.DEC); // len-1 + methodConvert.AccessSlot(OpCode.STLOC, endIndex); + } + + // Local function to get endIndex + void GetEndIndex() => methodConvert.AccessSlot(OpCode.LDLOC, endIndex); + + void GetString() => methodConvert.AddInstruction(OpCode.LDARG0); // Load the string + + // Local function to get startIndex + void GetStartIndex() => methodConvert.AccessSlot(OpCode.LDLOC, startIndex); + + void CheckStartIndex(JumpTarget loopEnd) + { + GetStartIndex(); + GetStrLen(); + methodConvert.AddInstruction(OpCode.LT); // Check if index < length + methodConvert.Jump(OpCode.JMPIFNOT, loopEnd); // If not, exit the loop + } + + // Local function to set startIndex + void MoveStartIndexAndLoop(JumpTarget loopStart) + { + methodConvert.AccessSlot(OpCode.LDLOC, startIndex); + methodConvert.AddInstruction(OpCode.INC); + methodConvert.AccessSlot(OpCode.STLOC, startIndex); + methodConvert.Jump(OpCode.JMP, loopStart); + } + + void PickCharStart() + { + GetString(); + GetStartIndex(); + methodConvert.AddInstruction(OpCode.PICKITEM); // Get the character at the current index + } + + // Local function to set endIndex + void MoveEndIndexAndLoop(JumpTarget loopStart) + { + methodConvert.AccessSlot(OpCode.LDLOC, endIndex); + methodConvert.AddInstruction(OpCode.DEC); + methodConvert.AccessSlot(OpCode.STLOC, endIndex); + methodConvert.Jump(OpCode.JMP, loopStart); + } + + void CheckWithin(JumpTarget loopEnd) + { + methodConvert.AddInstruction(OpCode.DUP); + methodConvert.Push((ushort)'\t'); + methodConvert.Push((ushort)'\r' + 1); + methodConvert.AddInstruction(OpCode.WITHIN); // check if '\t' <= c <= '\r' + methodConvert.AddInstruction(OpCode.SWAP); + + methodConvert.Push((ushort)' '); // Push space character + methodConvert.AddInstruction(OpCode.EQUAL); // Check if character is a space + methodConvert.AddInstruction(OpCode.BOOLOR); // check if '\t' <= c <= '\r' or ' ' == c + + methodConvert.Jump(OpCode.JMPIFNOT, loopEnd); // If not, exit the loop + } + + void CheckEndIndex(JumpTarget loopEnd) + { + GetEndIndex(); + GetStartIndex(); + methodConvert.AddInstruction(OpCode.GT); // Check if index > start + methodConvert.Jump(OpCode.JMPIFNOT, loopEnd); // If not, exit the loop + } + + void PickCharEnd() + { + GetString(); + GetEndIndex(); + methodConvert.AddInstruction(OpCode.PICKITEM); // Get the character at the current index + } } private static void HandleStringTrimChar(MethodConvert methodConvert, SemanticModel model, IMethodSymbol symbol, ExpressionSyntax? instanceExpression, IReadOnlyList? arguments) diff --git a/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_String.cs b/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_String.cs index a0fa909c5..71e53f22e 100644 --- a/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_String.cs +++ b/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_String.cs @@ -15,7 +15,7 @@ public abstract class Contract_String(Neo.SmartContract.Testing.SmartContractIni /// /// Optimization: "All" /// - public static Neo.SmartContract.NefFile Nef => Neo.IO.Helper.AsSerializable(Convert.FromBase64String(@"TkVGM1Rlc3RpbmdFbmdpbmUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS+8gQxQDYqd8FQmcfmTBL3ALZl2ghnZXRCbG9jawEAAQ++8gQxQDYqd8FQmcfmTBL3ALZl2gtjdXJyZW50SGFzaAAAAQ/A7znO4OTpJcbCoGp54UQN2G/OrARpdG9hAQABD8DvOc7g5OklxsKgannhRA3Yb86sDG1lbW9yeVNlYXJjaAIAAQ8AAP1+AlcDAAwETWFya3AMAHE3AQA3AAAUznIMB0hlbGxvLCBoiwwBIItpiwwXISBDdXJyZW50IHRpbWVzdGFtcCBpcyCLajcCAIsMAS6L2yhBz+dHlkBXAgAMBWhlbGxvcAwFaGVsbG9xaGmXJAsMBUZhbHNlIggMBFRydWVBz+dHlkBXAQAMCDAxMjM0NTY3cGgRS8pLn4xBz+dHlmgRFIxBz+dHlkAMAEBXAAF4StgkBsoQs0BFCEBXAAEMBXdvcmxkeErKUUrKShNSUJ9KECwIRUVFRQlAE1JTjNsol0BXAAEMBXdvcmxkeDcDABC4QFcAAQwFd29ybGR4NwMAQFcEAAQAAKDexa3JNTYAAAAAAAAAcAwiTlhWN1poSGl5TTFhSFh3cFZzUlpDNkJ3TkZQMmpnaFhBcXEMAwECA9swcgwHU0J5dGU6IADWNwIAiwwILCBCeXRlOiCLACo3AgCLDAosIFVTaG9ydDogiwHoAzcCAIsMAiwgi9soDAZVSW50OiACQEIPADcCAIsMCSwgVUxvbmc6IIsDABCl1OgAAAA3AgCLDAIsIIvbKIvbKAwMQmlnSW50ZWdlcjogaDcCAIsMCCwgQ2hhcjogiwBB2yiLDAosIFN0cmluZzogiwwFSGVsbG+LDAIsIIvbKIvbKAwJRUNQb2ludDogaYsMDiwgQnl0ZVN0cmluZzogiwwNU3lzdGVtLkJ5dGVbXYsMCCwgQm9vbDogiwgmCgwEVHJ1ZSIJDAVGYWxzZYvbKIvbKHNrQFcAAXhKEEp4yrUmE0p4zkoZHrtQACCXrCYFnCLrjErKnUoQtyYTSnjOShkeu1AAIJesJgWdIuyM2yhAxxhSIg==")); + public static Neo.SmartContract.NefFile Nef => Neo.IO.Helper.AsSerializable(Convert.FromBase64String(@"TkVGM1Rlc3RpbmdFbmdpbmUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS+8gQxQDYqd8FQmcfmTBL3ALZl2ghnZXRCbG9jawEAAQ++8gQxQDYqd8FQmcfmTBL3ALZl2gtjdXJyZW50SGFzaAAAAQ/A7znO4OTpJcbCoGp54UQN2G/OrARpdG9hAQABD8DvOc7g5OklxsKgannhRA3Yb86sDG1lbW9yeVNlYXJjaAIAAQ8AAP2GAlcDAAwETWFya3AMAHE3AQA3AAAUznIMB0hlbGxvLCBoiwwBIItpiwwXISBDdXJyZW50IHRpbWVzdGFtcCBpcyCLajcCAIsMAS6L2yhBz+dHlkBXAgAMBWhlbGxvcAwFaGVsbG9xaGmXJAsMBUZhbHNlIggMBFRydWVBz+dHlkBXAQAMCDAxMjM0NTY3cGgRS8pLn4xBz+dHlmgRFIxBz+dHlkAMAEBXAAF4StgkBsoQs0BFCEBXAAEMBXdvcmxkeErKUUrKShNSUJ9KECwIRUVFRQlAE1JTjNsol0BXAAEMBXdvcmxkeDcDABC4QFcAAQwFd29ybGR4NwMAQFcEAAQAAKDexa3JNTYAAAAAAAAAcAwiTlhWN1poSGl5TTFhSFh3cFZzUlpDNkJ3TkZQMmpnaFhBcXEMAwECA9swcgwHU0J5dGU6IADWNwIAiwwILCBCeXRlOiCLACo3AgCLDAosIFVTaG9ydDogiwHoAzcCAIsMAiwgi9soDAZVSW50OiACQEIPADcCAIsMCSwgVUxvbmc6IIsDABCl1OgAAAA3AgCLDAIsIIvbKIvbKAwMQmlnSW50ZWdlcjogaDcCAIsMCCwgQ2hhcjogiwBB2yiLDAosIFN0cmluZzogiwwFSGVsbG+LDAIsIIvbKIvbKAwJRUNQb2ludDogaYsMDiwgQnl0ZVN0cmluZzogiwwNU3lzdGVtLkJ5dGVbXYsMCCwgQm9vbDogiwgmCgwEVHJ1ZSIJDAVGYWxzZYvbKIvbKHNrQFcDAXjKcBBxaJ1yaWi1JhV4ac5KGR67UAAgl6wmB2mccSLqamm3JhV4as5KGR67UAAgl6wmB2qdciLqeGlqaZ+cjEAbrJCF")); #endregion diff --git a/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_String.cs b/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_String.cs index c459b0f62..baeb86646 100644 --- a/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_String.cs +++ b/tests/Neo.Compiler.CSharp.UnitTests/UnitTest_String.cs @@ -127,26 +127,22 @@ public void Test_TestInterpolatedStringHandler() public void Test_TestTrim() { Assert.AreEqual("Hello, World!", Contract.TestTrim(" Hello, World! ")); - AssertGasConsumed(1357650); + AssertGasConsumed(1136010); Assert.AreEqual("No Trim", Contract.TestTrim("No Trim")); - AssertGasConsumed(1357650); + AssertGasConsumed(1118130); Assert.AreEqual("", Contract.TestTrim(" ")); - AssertGasConsumed(1357650); - - Assert.AreEqual(null, Contract.TestTrim(null)); - AssertGasConsumed(1047300); + AssertGasConsumed(1124040); // Test various whitespace characters Assert.AreEqual("Trim Test", Contract.TestTrim("\t\n\r Trim Test \t\n\r")); - AssertGasConsumed(1357650); - + AssertGasConsumed(1153890); Assert.AreEqual("Multiple Spaces", Contract.TestTrim(" Multiple Spaces ")); - AssertGasConsumed(1357650); + AssertGasConsumed(1144950); Assert.AreEqual("Mix of Whitespace", Contract.TestTrim(" \t \n \r Mix of Whitespace \r \n \t ")); - AssertGasConsumed(1357650); + AssertGasConsumed(1180710); } } }