diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Statements/FixedStatement.cs.il.txt b/Cecilifier.Core.Tests/TestResources/Integration/Statements/FixedStatement.cs.il.txt new file mode 100644 index 00000000..1f4dffcf --- /dev/null +++ b/Cecilifier.Core.Tests/TestResources/Integration/Statements/FixedStatement.cs.il.txt @@ -0,0 +1,7 @@ +.locals (System.Int32& V_0) +IL_0000: ldarg.0 +IL_0001: ldflda System.Int32 FixedStatementTest::i +IL_0006: stloc V_0 +IL_000a: ldloc V_0 +IL_000e: conv.u +IL_000f: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Statements/FixedStatement.cs.txt b/Cecilifier.Core.Tests/TestResources/Integration/Statements/FixedStatement.cs.txt index 214979a7..87026393 100644 --- a/Cecilifier.Core.Tests/TestResources/Integration/Statements/FixedStatement.cs.txt +++ b/Cecilifier.Core.Tests/TestResources/Integration/Statements/FixedStatement.cs.txt @@ -1,6 +1,11 @@ - -unsafe void FixedStatement() +class FixedStatementTest { - int i = 0; - fixed (int* p = &i) { } + public int i; + unsafe int* FixedStatement() + { + fixed (int* p = &i) + { + return p; + } + } } \ No newline at end of file diff --git a/Cecilifier.Core.Tests/Tests/Integration/StatementTests.cs b/Cecilifier.Core.Tests/Tests/Integration/StatementTests.cs new file mode 100644 index 00000000..40288b40 --- /dev/null +++ b/Cecilifier.Core.Tests/Tests/Integration/StatementTests.cs @@ -0,0 +1,14 @@ +using NUnit.Framework; + +namespace Cecilifier.Core.Tests.Integration +{ + [TestFixture] + public class StatementTests : IntegrationResourceBasedTest + { + [Test] + public void TestFixedStatement() + { + AssertResourceTestWithExplicitExpectation(@"Statements/FixedStatement", "System.Int32* FixedStatementTest::FixedStatement()"); + } + } +} diff --git a/Cecilifier.Core/AST/ExpressionVisitor.cs b/Cecilifier.Core/AST/ExpressionVisitor.cs index dc882eea..ad26d940 100644 --- a/Cecilifier.Core/AST/ExpressionVisitor.cs +++ b/Cecilifier.Core/AST/ExpressionVisitor.cs @@ -501,7 +501,7 @@ private void ProcessProperty(SimpleNameSyntax node, IPropertySymbol propertySymb } } - private void ProcessField(IFieldSymbol fieldSymbol) + private void ProcessField(SimpleNameSyntax node, IFieldSymbol fieldSymbol) { if (fieldSymbol.IsStatic && fieldSymbol.IsDefinedInCurrentType(Context)) { @@ -509,6 +509,11 @@ private void ProcessField(IFieldSymbol fieldSymbol) } AddCilInstruction(ilVar, OpCodes.Ldarg_0); + + if (HandleLoadAddress(ilVar, fieldSymbol.Type, (CSharpSyntaxNode) node.Parent, OpCodes.Ldflda, fieldSymbol.Name, MemberKind.Field, fieldSymbol.ContainingType.Name)) + { + return; + } AddCilInstruction(ilVar, OpCodes.Ldfld, Context.DefinitionVariables.GetVariable(fieldSymbol.Name, MemberKind.Field, fieldSymbol.ContainingType.Name).VariableName); } @@ -523,6 +528,15 @@ private void ProcessLocalVariable(SimpleNameSyntax localVar, SymbolInfo varInfo) AddCilInstruction(ilVar, OpCodes.Ldloc, Context.DefinitionVariables.GetVariable(symbol.Name, MemberKind.LocalVariable).VariableName); HandlePotentialDelegateInvocationOn(localVar, symbol.Type, ilVar); + HandlePotentialFixedLoad(localVar, symbol); + } + + private void HandlePotentialFixedLoad(SyntaxNode localVar, ILocalSymbol symbol) + { + if (!symbol.IsFixed) + return; + + AddCilInstruction(ilVar, OpCodes.Conv_U); } private void ProcessMethodCall(SimpleNameSyntax node, IMethodSymbol method) @@ -653,7 +667,7 @@ private void HandleIdentifier(SimpleNameSyntax node) break; case SymbolKind.Field: - ProcessField(member.Symbol as IFieldSymbol); + ProcessField(node, member.Symbol as IFieldSymbol); break; case SymbolKind.Property: diff --git a/Cecilifier.Core/AST/IVisitorContext.cs b/Cecilifier.Core/AST/IVisitorContext.cs index b9819497..ae7426d4 100644 --- a/Cecilifier.Core/AST/IVisitorContext.cs +++ b/Cecilifier.Core/AST/IVisitorContext.cs @@ -296,6 +296,12 @@ internal interface IVisitorContext void TriggerInstructionAdded(string instVar); ITypeResolver TypeResolver { get; } + + #region Flags Handling + IDisposable WithFlag(string name); + bool HasFlag(string name); + + #endregion } internal interface ITypeResolver diff --git a/Cecilifier.Core/AST/StatementVisitor.cs b/Cecilifier.Core/AST/StatementVisitor.cs index fd6b294c..6a7c1b43 100644 --- a/Cecilifier.Core/AST/StatementVisitor.cs +++ b/Cecilifier.Core/AST/StatementVisitor.cs @@ -52,6 +52,16 @@ public override void VisitUnsafeStatement(UnsafeStatementSyntax node) LogUnsupportedSyntax(node); } + public override void VisitFixedStatement(FixedStatementSyntax node) + { + using (Context.WithFlag("fixed")) + { + HandleVariableDeclaration(node.Declaration); + } + + Visit(node.Statement); + } + public override void VisitBlock(BlockSyntax node) { using (Context.DefinitionVariables.EnterScope()) @@ -78,12 +88,7 @@ public override void VisitIfStatement(IfStatementSyntax node) public override void VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax node) { - var methodVar = Context.DefinitionVariables.GetLastOf(MemberKind.Method).VariableName; - foreach (var localVar in node.Declaration.Variables) - { - AddLocalVariable(node.Declaration.Type, localVar, methodVar); - ProcessVariableInitialization(localVar); - } + HandleVariableDeclaration(node.Declaration); } public override void VisitTryStatement(TryStatementSyntax node) @@ -194,10 +199,22 @@ void SetFinallyStart(string instVar) private void AddLocalVariable(TypeSyntax type, VariableDeclaratorSyntax localVar, string methodVar) { + var isFixedStatement = Context.HasFlag("fixed"); + if (isFixedStatement) + { + type = ((PointerTypeSyntax) type).ElementType; + } + var resolvedVarType = type.IsVar ? ResolveExpressionType(localVar.Initializer.Value) : ResolveType(type); + + if (isFixedStatement) + { + resolvedVarType = $"{resolvedVarType}.MakeByReferenceType()"; + } + var cecilVarDeclName = TempLocalVar($"lv_{localVar.Identifier.ValueText}"); AddCecilExpression("var {0} = new VariableDefinition({1});", cecilVarDeclName, resolvedVarType); AddCecilExpression("{0}.Body.Variables.Add({1});", methodVar, cecilVarDeclName); @@ -216,6 +233,16 @@ private void ProcessVariableInitialization(VariableDeclaratorSyntax localVar) AddCilInstruction(_ilVar, OpCodes.Stloc, localVarDef.VariableName); } + private void HandleVariableDeclaration(VariableDeclarationSyntax declaration) + { + var methodVar = Context.DefinitionVariables.GetLastOf(MemberKind.Method).VariableName; + foreach (var localVar in declaration.Variables) + { + AddLocalVariable(declaration.Type, localVar, methodVar); + ProcessVariableInitialization(localVar); + } + } + private struct ExceptionHandlerEntry { public ExceptionHandlerType Kind; diff --git a/Cecilifier.Core/AST/SyntaxWalkerBase.cs b/Cecilifier.Core/AST/SyntaxWalkerBase.cs index 5006ee14..bb6e41ae 100644 --- a/Cecilifier.Core/AST/SyntaxWalkerBase.cs +++ b/Cecilifier.Core/AST/SyntaxWalkerBase.cs @@ -319,12 +319,12 @@ protected void ProcessParameter(string ilVar, SimpleNameSyntax node, IParameterS } } - protected bool HandleLoadAddress(string ilVar, ITypeSymbol symbol, CSharpSyntaxNode parent, OpCode opCode, string symbolName, MemberKind memberKind) + protected bool HandleLoadAddress(string ilVar, ITypeSymbol symbol, CSharpSyntaxNode parent, OpCode opCode, string symbolName, MemberKind memberKind, string parentName = null) { if ((symbol.IsValueType && parent.Accept(new UsageVisitor()) == UsageKind.CallTarget) || parent.IsKind(SyntaxKind.AddressOfExpression)) { - AddCilInstruction(ilVar, opCode, Context.DefinitionVariables.GetVariable(symbolName, memberKind).VariableName); - if (parent.IsKind(SyntaxKind.AddressOfExpression)) + AddCilInstruction(ilVar, opCode, Context.DefinitionVariables.GetVariable(symbolName, memberKind, parentName).VariableName); + if (!Context.HasFlag("fixed") && parent.IsKind(SyntaxKind.AddressOfExpression)) AddCilInstruction(ilVar, OpCodes.Conv_U); return true; diff --git a/Cecilifier.Core/Misc/CecilifierContext.cs b/Cecilifier.Core/Misc/CecilifierContext.cs index 8ad361c7..3818f883 100644 --- a/Cecilifier.Core/Misc/CecilifierContext.cs +++ b/Cecilifier.Core/Misc/CecilifierContext.cs @@ -10,6 +10,7 @@ namespace Cecilifier.Core.Misc { internal class CecilifierContext : IVisitorContext { + private readonly ISet flags = new HashSet(); private readonly LinkedList output = new LinkedList(); private int currentFieldId; @@ -103,6 +104,26 @@ public void TriggerInstructionAdded(string instVar) { InstructionAdded?.Invoke(instVar); } + + public IDisposable WithFlag(string name) + { + return new ContextFlagReseter(this, name); + } + + public bool HasFlag(string name) + { + return flags.Contains(name); + } + + internal void SetFlag(string name) + { + flags.Add(name); + } + + internal void ClearFlag(string name) + { + flags.Remove(name); + } } internal class TypeResolverImpl : ITypeResolver diff --git a/Cecilifier.Core/Misc/ContextFlagReseter.cs b/Cecilifier.Core/Misc/ContextFlagReseter.cs new file mode 100644 index 00000000..3a48707c --- /dev/null +++ b/Cecilifier.Core/Misc/ContextFlagReseter.cs @@ -0,0 +1,22 @@ +using System; + +namespace Cecilifier.Core.Misc +{ + internal class ContextFlagReseter : IDisposable + { + private readonly CecilifierContext _context; + private readonly string _flagName; + + public ContextFlagReseter(CecilifierContext context, string flagName) + { + _context = context; + _flagName = flagName; + _context.SetFlag(flagName); + } + + public void Dispose() + { + _context.ClearFlag(_flagName); + } + } +}