Skip to content

Commit

Permalink
Lambda function support (neo-project#1000)
Browse files Browse the repository at this point in the history
* init

* rename

* format

* revert using

---------

Co-authored-by: Jimmy <[email protected]>
  • Loading branch information
Ashuaidehao and Jim8y authored Mar 25, 2024
1 parent 905246d commit ece89dc
Show file tree
Hide file tree
Showing 13 changed files with 614 additions and 33 deletions.
18 changes: 18 additions & 0 deletions src/Neo.Compiler.CSharp/CompilationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public class CompilationContext
private readonly Dictionary<IFieldSymbol, byte> _staticFields = new(SymbolEqualityComparer.Default);
private readonly System.Collections.Generic.List<byte> _anonymousStaticFields = new();
private readonly Dictionary<ITypeSymbol, byte> _vtables = new(SymbolEqualityComparer.Default);
private readonly Dictionary<ISymbol, byte> _capturedStaticFields = new(SymbolEqualityComparer.Default);
private byte[]? _script;

public bool Success => _diagnostics.All(p => p.Severity != DiagnosticSeverity.Error);
Expand Down Expand Up @@ -519,6 +520,23 @@ internal byte AddAnonymousStaticField()
return index;
}

internal byte GetOrAddCapturedStaticField(ISymbol local)
{
if (_capturedStaticFields.ContainsKey(local))
{
return _capturedStaticFields[local];
}
byte index = (byte)StaticFieldCount;
_anonymousStaticFields.Add(index);
_capturedStaticFields.Add(local, index);
return index;
}

internal bool TryGetCaptruedStaticField(ISymbol local, out byte staticFieldIndex)
{
return _capturedStaticFields.TryGetValue(local, out staticFieldIndex);
}

internal byte AddVTable(ITypeSymbol type)
{
if (!_vtables.TryGetValue(type, out byte index))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,33 +192,31 @@ private void ConvertLocalIdentifierNameCoalesceAssignment(SemanticModel model, I
{
JumpTarget assignmentTarget = new();
JumpTarget endTarget = new();
byte index = _localVariables[left];
AccessSlot(OpCode.LDLOC, index);
LdLocSlot(left);
AddInstruction(OpCode.ISNULL);
Jump(OpCode.JMPIF_L, assignmentTarget);
AccessSlot(OpCode.LDLOC, index);
LdLocSlot(left);
Jump(OpCode.JMP_L, endTarget);
assignmentTarget.Instruction = AddInstruction(OpCode.NOP);
ConvertExpression(model, right);
AddInstruction(OpCode.DUP);
AccessSlot(OpCode.STLOC, index);
StLocSlot(left);
endTarget.Instruction = AddInstruction(OpCode.NOP);
}

private void ConvertParameterIdentifierNameCoalesceAssignment(SemanticModel model, IParameterSymbol left, ExpressionSyntax right)
{
JumpTarget assignmentTarget = new();
JumpTarget endTarget = new();
byte index = _parameters[left];
AccessSlot(OpCode.LDARG, index);
LdArgSlot(left);
AddInstruction(OpCode.ISNULL);
Jump(OpCode.JMPIF_L, assignmentTarget);
AccessSlot(OpCode.LDARG, index);
LdArgSlot(left);
Jump(OpCode.JMP_L, endTarget);
assignmentTarget.Instruction = AddInstruction(OpCode.NOP);
ConvertExpression(model, right);
AddInstruction(OpCode.DUP);
AccessSlot(OpCode.STARG, index);
StArgSlot(left);
endTarget.Instruction = AddInstruction(OpCode.NOP);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,22 +168,20 @@ private void ConvertFieldIdentifierNameComplexAssignment(SemanticModel model, IT

private void ConvertLocalIdentifierNameComplexAssignment(SemanticModel model, ITypeSymbol type, SyntaxToken operatorToken, ILocalSymbol left, ExpressionSyntax right)
{
byte index = _localVariables[left];
AccessSlot(OpCode.LDLOC, index);
LdLocSlot(left);
ConvertExpression(model, right);
EmitComplexAssignmentOperator(type, operatorToken);
AddInstruction(OpCode.DUP);
AccessSlot(OpCode.STLOC, index);
StLocSlot(left);
}

private void ConvertParameterIdentifierNameComplexAssignment(SemanticModel model, ITypeSymbol type, SyntaxToken operatorToken, IParameterSymbol left, ExpressionSyntax right)
{
byte index = _parameters[left];
AccessSlot(OpCode.LDARG, index);
LdArgSlot(left);
ConvertExpression(model, right);
EmitComplexAssignmentOperator(type, operatorToken);
AddInstruction(OpCode.DUP);
AccessSlot(OpCode.STARG, index);
StArgSlot(left);
}

private void ConvertPropertyIdentifierNameComplexAssignment(SemanticModel model, ITypeSymbol type, SyntaxToken operatorToken, IPropertySymbol left, ExpressionSyntax right)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,10 @@ private void ConvertIdentifierNameAssignment(SemanticModel model, IdentifierName
}
break;
case ILocalSymbol local:
AccessSlot(OpCode.STLOC, _localVariables[local]);
StLocSlot(local);
break;
case IParameterSymbol parameter:
AccessSlot(OpCode.STARG, _parameters[parameter]);
StArgSlot(parameter);
break;
case IPropertySymbol property:
// Check if the property is within a constructor and is readonly
Expand Down
56 changes: 56 additions & 0 deletions src/Neo.Compiler.CSharp/MethodConvert/Expression/Expression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using Neo.Cryptography.ECC;
using Neo.IO;
using Neo.Wallets;
using System.Linq;

namespace Neo.Compiler;

Expand Down Expand Up @@ -216,12 +217,67 @@ private void ConvertExpression(SemanticModel model, ExpressionSyntax syntax, Syn
case TupleExpressionSyntax expression:
ConvertTupleExpression(model, expression);
break;
case ParenthesizedLambdaExpressionSyntax expression:
ConvertParenthesizedLambdaExpression(model, expression);
break;
case SimpleLambdaExpressionSyntax expression:
ConvertSimpleLambdaExpression(model, expression);
break;
default:
//Example: typeof(Transaction);
throw new CompilationException(syntax, DiagnosticId.SyntaxNotSupported, $"Unsupported syntax: {syntax}");
}
}

private void ConvertSimpleLambdaExpression(SemanticModel model, SimpleLambdaExpressionSyntax expression)
{
var symbol = (IMethodSymbol)model.GetSymbolInfo(expression).Symbol!;
var mc = _context.ConvertMethod(model, symbol);
ConvertLocalToStaticFields(mc);
AddInstruction(new Instruction
{
OpCode = OpCode.PUSHA,
Target = mc._startTarget
});
}


private void ConvertParenthesizedLambdaExpression(SemanticModel model, ParenthesizedLambdaExpressionSyntax expression)
{
var symbol = (IMethodSymbol)model.GetSymbolInfo(expression).Symbol!;
var mc = _context.ConvertMethod(model, symbol);
ConvertLocalToStaticFields(mc);
AddInstruction(new Instruction
{
OpCode = OpCode.PUSHA,
Target = mc._startTarget
});
}

private void ConvertLocalToStaticFields(MethodConvert mc)
{
if (mc.CapturedLocalSymbols.Count > 0)
{
foreach (var local in mc.CapturedLocalSymbols)
{
//copy captured local variable/parameter value to related static fields
var staticFieldIndex = _context.GetOrAddCapturedStaticField(local);
switch (local)
{
case ILocalSymbol localSymbol:
var localIndex = _localVariables[localSymbol];
AccessSlot(OpCode.LDLOC, localIndex);
break;
case IParameterSymbol parameterSymbol:
var paraIndex = _parameters[parameterSymbol];
AccessSlot(OpCode.LDARG, paraIndex);
break;
}
AccessSlot(OpCode.STSFLD, staticFieldIndex);
}
}
}

/// <summary>
/// Ensures that the value of the incoming integer type is within the specified range.
/// If the type is BigInteger, no range check is performed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ private void ConvertIdentifierNameExpression(SemanticModel model, IdentifierName
if (local.IsConst)
Push(local.ConstantValue);
else
AccessSlot(OpCode.LDLOC, _localVariables[local]);
LdLocSlot(local);
break;
case IMethodSymbol method:
if (!method.IsStatic)
Expand All @@ -79,8 +79,7 @@ private void ConvertIdentifierNameExpression(SemanticModel model, IdentifierName
Jump(OpCode.PUSHA, convert._startTarget);
break;
case IParameterSymbol parameter:
if (!_internalInline)
AccessSlot(OpCode.LDARG, _parameters[parameter]);
if (!_internalInline) LdArgSlot(parameter);
break;
case IPropertySymbol property:
if (property.IsStatic)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,20 +152,18 @@ private void ConvertFieldIdentifierNamePostIncrementOrDecrementExpression(Syntax

private void ConvertLocalIdentifierNamePostIncrementOrDecrementExpression(SyntaxToken operatorToken, ILocalSymbol symbol)
{
byte index = _localVariables[symbol];
AccessSlot(OpCode.LDLOC, index);
LdLocSlot(symbol);
AddInstruction(OpCode.DUP);
EmitIncrementOrDecrement(operatorToken, symbol.Type);
AccessSlot(OpCode.STLOC, index);
StLocSlot(symbol);
}

private void ConvertParameterIdentifierNamePostIncrementOrDecrementExpression(SyntaxToken operatorToken, IParameterSymbol symbol)
{
byte index = _parameters[symbol];
AccessSlot(OpCode.LDARG, index);
LdArgSlot(symbol);
AddInstruction(OpCode.DUP);
EmitIncrementOrDecrement(operatorToken, symbol.Type);
AccessSlot(OpCode.STARG, index);
StArgSlot(symbol);
}

private void ConvertPropertyIdentifierNamePostIncrementOrDecrementExpression(SemanticModel model, SyntaxToken operatorToken, IPropertySymbol symbol)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,20 +169,18 @@ private void ConvertFieldIdentifierNamePreIncrementOrDecrementExpression(SyntaxT

private void ConvertLocalIdentifierNamePreIncrementOrDecrementExpression(SyntaxToken operatorToken, ILocalSymbol symbol)
{
byte index = _localVariables[symbol];
AccessSlot(OpCode.LDLOC, index);
LdLocSlot(symbol);
EmitIncrementOrDecrement(operatorToken, symbol.Type);
AddInstruction(OpCode.DUP);
AccessSlot(OpCode.STLOC, index);
StLocSlot(symbol);
}

private void ConvertParameterIdentifierNamePreIncrementOrDecrementExpression(SyntaxToken operatorToken, IParameterSymbol symbol)
{
byte index = _parameters[symbol];
AccessSlot(OpCode.LDARG, index);
LdArgSlot(symbol);
EmitIncrementOrDecrement(operatorToken, symbol.Type);
AddInstruction(OpCode.DUP);
AccessSlot(OpCode.STARG, index);
StArgSlot(symbol);
}

private void ConvertPropertyIdentifierNamePreIncrementOrDecrementExpression(SemanticModel model, SyntaxToken operatorToken, IPropertySymbol symbol)
Expand Down
103 changes: 102 additions & 1 deletion src/Neo.Compiler.CSharp/MethodConvert/MethodConvert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ partial class MethodConvert
|| (_instructions.Count == 1 && _instructions[^1].OpCode == OpCode.RET)
|| (_instructions.Count == 2 && _instructions[^1].OpCode == OpCode.RET && _instructions[0].OpCode == OpCode.INITSLOT);

/// <summary>
/// captured local variable/parameter symbols when converting current method
/// </summary>
public HashSet<ISymbol> CapturedLocalSymbols { get; } = new(SymbolEqualityComparer.Default);

#endregion

#region Constructors
Expand Down Expand Up @@ -202,7 +207,7 @@ public void Convert(SemanticModel model)
{
byte pc = (byte)_parameters.Count;
byte lc = (byte)_localsCount;
if (!Symbol.IsStatic) pc++;
if (IsInstanceMethod(Symbol)) pc++;
if (pc > 0 || lc > 0)
{
_instructions.Insert(0, new Instruction
Expand Down Expand Up @@ -372,6 +377,102 @@ private void ProcessFieldInitializer(SemanticModel model, IFieldSymbol field, Ac
#endregion

#region Helper

/// <summary>
/// load parameter value
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
private Instruction LdArgSlot(IParameterSymbol parameter)
{
if (_context.TryGetCaptruedStaticField(parameter, out var staticFieldIndex))
{
//using created static fields
return AccessSlot(OpCode.LDSFLD, staticFieldIndex);
}
if (Symbol.MethodKind == MethodKind.AnonymousFunction && !_parameters.ContainsKey(parameter))
{
//create static fields from captrued parameter
var staticIndex = _context.GetOrAddCapturedStaticField(parameter);
CapturedLocalSymbols.Add(parameter);
return AccessSlot(OpCode.LDSFLD, staticIndex);
}
// local parameter in current method
byte index = _parameters[parameter];
return AccessSlot(OpCode.LDARG, index);
}

/// <summary>
/// store value to parameter
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
private Instruction StArgSlot(IParameterSymbol parameter)
{
if (_context.TryGetCaptruedStaticField(parameter, out var staticFieldIndex))
{
//using created static fields
return AccessSlot(OpCode.STSFLD, staticFieldIndex);
}
if (Symbol.MethodKind == MethodKind.AnonymousFunction && !_parameters.ContainsKey(parameter))
{
//create static fields from captrued parameter
var staticIndex = _context.GetOrAddCapturedStaticField(parameter);
CapturedLocalSymbols.Add(parameter);
return AccessSlot(OpCode.STSFLD, staticIndex);
}
// local parameter in current method
byte index = _parameters[parameter];
return AccessSlot(OpCode.STARG, index);
}

/// <summary>
/// load local variable value
/// </summary>
/// <param name="local"></param>
/// <returns></returns>
private Instruction LdLocSlot(ILocalSymbol local)
{
if (_context.TryGetCaptruedStaticField(local, out var staticFieldIndex))
{
//using created static fields
return AccessSlot(OpCode.LDSFLD, staticFieldIndex);
}
if (Symbol.MethodKind == MethodKind.AnonymousFunction && !_localVariables.ContainsKey(local))
{
//create static fields from captrued local
byte staticIndex = _context.GetOrAddCapturedStaticField(local);
CapturedLocalSymbols.Add(local);
return AccessSlot(OpCode.LDSFLD, staticIndex);
}
// local variables in current method
byte index = _localVariables[local];
return AccessSlot(OpCode.LDLOC, index);
}

/// <summary>
/// store value to local variable
/// </summary>
/// <param name="local"></param>
/// <returns></returns>
private Instruction StLocSlot(ILocalSymbol local)
{
if (_context.TryGetCaptruedStaticField(local, out var staticFieldIndex))
{
//using created static fields
return AccessSlot(OpCode.STSFLD, staticFieldIndex);
}
if (Symbol.MethodKind == MethodKind.AnonymousFunction && !_localVariables.ContainsKey(local))
{
//create static fields from captrued local
byte staticIndex = _context.GetOrAddCapturedStaticField(local);
CapturedLocalSymbols.Add(local);
return AccessSlot(OpCode.STSFLD, staticIndex);
}
byte index = _localVariables[local];
return AccessSlot(OpCode.STLOC, index);
}

private Instruction AccessSlot(OpCode opcode, byte index)
{
return index >= 7
Expand Down
Loading

0 comments on commit ece89dc

Please sign in to comment.