Skip to content
Merged
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 @@ -96,6 +97,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 @@ -123,8 +137,44 @@ 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();
if (Symbol.IsStatic)
{
IFieldSymbol backingField = Array.Find(fields, p => SymbolEqualityComparer.Default.Equals(p.AssociatedSymbol, property))!;
byte backingFieldIndex = _context.AddStaticField(backingField);
if (!_inline) AccessSlot(OpCode.LDARG, 0);
AccessSlot(OpCode.STSFLD, backingFieldIndex);
}
else
{
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);
}
}
}