Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[Compiler UT] Ut readonly property #984

Merged
merged 15 commits into from
Mar 18, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Neo.VM;
using System;
using System.Linq;
using System.Runtime.InteropServices;

namespace Neo.Compiler;
Expand Down Expand Up @@ -139,8 +140,34 @@ private void ConvertIdentifierNameAssignment(SemanticModel model, IdentifierName
AccessSlot(OpCode.STARG, _parameters[parameter]);
break;
case IPropertySymbol property:
if (!property.IsStatic) AddInstruction(OpCode.LDARG0);
Call(model, property.SetMethod!, CallingConvention.Cdecl);
// Check if the property is within a constructor and is readonly
// C# document here https://learn.microsoft.com/en-us/dotnet/csharp/properties
// example of this syntax:
// public class Person
// {
// public Person(string firstName) => FirstName = firstName;
// // Readonly property
// public string FirstName { get; }
// }
if (property.SetMethod == null)
{
IFieldSymbol[] fields = property.ContainingType.GetAllMembers().OfType<IFieldSymbol>().ToArray();
fields = fields.Where(p => !p.IsStatic).ToArray();
int backingFieldIndex = Array.FindIndex(fields, p => SymbolEqualityComparer.Default.Equals(p.AssociatedSymbol, property));
AccessSlot(OpCode.LDARG, 0);
Push(backingFieldIndex);
AddInstruction(OpCode.ROT);
AddInstruction(OpCode.SETITEM);
}
else if (property.SetMethod != null)
{
if (!property.IsStatic) AddInstruction(OpCode.LDARG0);
Call(model, property.SetMethod, CallingConvention.Cdecl);
}
else
{
throw new CompilationException(left, DiagnosticId.SyntaxNotSupported, $"Property is readonly and not within a constructor: {property.Name}");
}
break;
default:
throw new CompilationException(left, DiagnosticId.SyntaxNotSupported, $"Unsupported symbol: {symbol}");
Expand Down
27 changes: 27 additions & 0 deletions tests/Neo.Compiler.CSharp.TestContracts/Contract_PropertyMethod.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace Neo.Compiler.CSharp.UnitTests.TestClasses;

public class Contract_PropertyMethod : SmartContract.Framework.SmartContract
{
public static (string, int) testProperty()
{
var p = new Person("NEO3", 10);
return (p.Name, p.Age);
}

public static void testProperty2()
{
var p = new Person("NEO3", 10);
}

public class Person
{
public string Name { get; set; }
public int Age { get; }

public Person(string name, int age)
{
Name = name;
Age = age;
}
}
}
39 changes: 39 additions & 0 deletions tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Property_Method.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.SmartContract.TestEngine;
using Neo.VM;
using Neo.VM.Types;

namespace Neo.Compiler.CSharp.UnitTests
{
[TestClass]
public class UnitTest_Property_Method
{
private TestEngine testEngine;

[TestInitialize]
public void Init()
{
testEngine = new TestEngine();
testEngine.AddNoOptimizeEntryScript(Utils.Extensions.TestContractRoot + "Contract_PropertyMethod.cs");
}

[TestMethod]
public void TestPropertyMethod()
{
testEngine.Reset();
var res = testEngine.ExecuteTestCaseStandard("testProperty");
Assert.AreEqual(testEngine.State, VMState.HALT);
var arr = (Array)res.Pop();
Assert.AreEqual(arr[0].GetString(), "NEO3");
Assert.AreEqual(arr[1].GetInteger(), 10);
}

[TestMethod]
public void TestPropertyMethod2()
{
testEngine.Reset();
var res = testEngine.ExecuteTestCaseStandard("testProperty2");
Assert.AreEqual(testEngine.State, VMState.HALT);
}
}
}
Loading