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 @@ -112,6 +113,19 @@ private void ConvertElementAccessAssignment(SemanticModel model, ElementAccessEx
private void ConvertIdentifierNameAssignment(SemanticModel model, IdentifierNameSyntax left)
{
ISymbol symbol = model.GetSymbolInfo(left).Symbol!;
// Lambda function to check if we're inside a constructor
Func<SyntaxNode, bool> isInConstructor = (node) =>
{
while (node != null)
{
if (node is ConstructorDeclarationSyntax) return true;
node = node.Parent;
}
return false;
};
// Use the lambda function with the current node
bool withinConstructor = isInConstructor(left);

switch (symbol)
{
case IDiscardSymbol:
Expand Down Expand Up @@ -139,8 +153,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 (withinConstructor && property.SetMethod == null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to check that is in a constructor? it will be checked already by the compiler, isn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean syntax analyzer? Maybe, let me check it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the unique way to arrive there is withinConstructor , otherwise you can't compile, isn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already updated.

{
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