Skip to content

Commit 7364657

Browse files
committed
fixes code generated when referening members of generic types (#334)
1 parent a032dd5 commit 7364657

File tree

6 files changed

+74
-34
lines changed

6 files changed

+74
-34
lines changed

Cecilifier.Core.Tests/Tests/Unit/MemberAccessTests.cs

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -205,14 +205,14 @@ void M() {{ }}
205205
Assert.That(cecilifiedCode, Does.Match(expectedExpressions));
206206
}
207207

208-
[TestCase("where T : struct", "string ConstrainedToStruct() => field.ToString();", "Ldflda, fld_field_4", "Constrained, gp_T_3")]
209-
[TestCase("where T : IFoo", "string ConstrainedToInterfaceCallInterfaceMethod() => field.Get();", "Ldflda, fld_field_4", "Constrained, gp_T_3")]
210-
[TestCase("where T : IFoo", "string ConstrainedToInterfaceCallToString() => field.ToString();", "Ldflda, fld_field_4", "Constrained, gp_T_3")]
211-
[TestCase("", "string Unconstrained() => field.ToString();", "Ldflda, fld_field_4", "Constrained, gp_T_3")]
212-
[TestCase("where T : class", "string ConstrainedToReferenceType() => field.ToString();", "Ldfld, fld_field_4", "Box, gp_T_3")]
213-
[TestCase("where T: Bar", "string ConstrainedToClassCallToString() => field.ToString();", "Ldfld, fld_field_9", "Box, gp_T_8")]
214-
[TestCase("where T: Bar", "void ConstrainedToClassCallClassMethod() => field.M();", "Ldfld, fld_field_9", "Box, gp_T_8")]
215-
public void TestCallOn_TypeParameter_CallOnField(string constraint, string snippet, params string[] expectedExpressions)
208+
[TestCase("where T : struct", "string ConstrainedToStruct() => field.ToString();", "Ldflda", "Constrained, gp_T_3")]
209+
[TestCase("where T : IFoo", "string ConstrainedToInterfaceCallInterfaceMethod() => field.Get();", "Ldflda", "Constrained, gp_T_3")]
210+
[TestCase("where T : IFoo", "string ConstrainedToInterfaceCallToString() => field.ToString();", "Ldflda", "Constrained, gp_T_3")]
211+
[TestCase("", "string Unconstrained() => field.ToString();", "Ldflda", "Constrained, gp_T_3")]
212+
[TestCase("where T : class", "string ConstrainedToReferenceType() => field.ToString();", """Ldfld""", "Box, gp_T_3")]
213+
[TestCase("where T: Bar", "string ConstrainedToClassCallToString() => field.ToString();", "Ldfld", "Box, gp_T_8")]
214+
[TestCase("where T: Bar", "void ConstrainedToClassCallClassMethod() => field.M();", "Ldfld", "Box, gp_T_8")]
215+
public void TestCallOn_TypeParameter_CallOnField(string constraint, string snippet, string expectedLoadOpCode, string expectedConversionExpression)
216216
{
217217
var code = $@"interface IFoo {{ string Get(); }}
218218
class Foo<T> {constraint}
@@ -225,8 +225,8 @@ class Bar {{ public void M() {{ }} }}
225225
";
226226
var result = RunCecilifier(code);
227227
var cecilifiedCode = result.GeneratedCode.ReadToEnd();
228-
foreach (var expectedExpression in expectedExpressions)
229-
Assert.That(cecilifiedCode, Contains.Substring(expectedExpression));
228+
Assert.That(cecilifiedCode, Does.Match($"""{expectedLoadOpCode}, new FieldReference\("field", gp_T_\d+, cls_foo_\d+.MakeGenericInstanceType\(gp_T_\d+\)\)"""));
229+
Assert.That(cecilifiedCode, Does.Match(expectedLoadOpCode));
230230
}
231231

232232
// Issue #187
@@ -290,4 +290,48 @@ public void AccessingMember_ThroughBaseKeyword(string source, bool requiresExtra
290290

291291
Assert.That(cecilifiedCode, Does.Match(pattern));
292292
}
293+
294+
[Test]
295+
public void GenericMemberReferences_Issue334()
296+
{
297+
var result = RunCecilifier("""
298+
using System;
299+
class C<T>
300+
{
301+
T field;
302+
event Action Event;
303+
T Property {get; set; }
304+
305+
public void Set(T toSet) => field = toSet;
306+
// Issue #339
307+
// public void SetEvent(Action toSet) => Event += toSet;
308+
public void SetProperty(T toSet) => Property = toSet;
309+
}
310+
""");
311+
var actual = result.GeneratedCode.ReadToEnd();
312+
Assert.That(actual, Does.Match("""
313+
//field = toSet
314+
(?<emit>\s+il_set_\d+\.Emit\(OpCodes\.)Ldarg_0\);
315+
\k<emit>Ldarg_1\);
316+
\k<emit>Stfld, new FieldReference\("field", (?<typeParam>gp_T_\d+), cls_C_\d+.MakeGenericInstanceType\(\k<typeParam>\)\)\);
317+
"""));
318+
319+
Assert.That(actual, Does.Match("""
320+
//Property = toSet
321+
(?<emit>\s+il_setProperty_\d+\.Emit\(OpCodes\.)Ldarg_0\);
322+
\k<emit>Ldarg_1\);
323+
\s+var (?<setter>r_set_Property_\d+) = new MethodReference\(l_set_\d+.Name, l_set_\d+.ReturnType\) {.+DeclaringType = cls_C_\d+.MakeGenericInstanceType\(gp_T_\d+\).+};
324+
\s+foreach\(var p in l_set_\d+.Parameters\)
325+
\s+{
326+
\s+\k<setter>\.Parameters.Add\(new ParameterDefinition\(p.Name, p.Attributes, p.ParameterType\)\);
327+
\s+}
328+
\k<emit>Call, \k<setter>\);
329+
"""));
330+
331+
// Placeholder for events. Ignore(#339)
332+
// Assert.That(actual, Does.Match("""
333+
// //Event += toSet
334+
// ....
335+
// """));
336+
}
293337
}

Cecilifier.Core.Tests/Tests/Unit/StructSpecificTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ class Field<T> where T : struct, Itf
466466
"""
467467
//field.M\(42\)
468468
\s+il_call_\d+.Emit\(OpCodes\.Ldarg_0\);
469-
\s+il_call_\d+.Emit\(OpCodes\.Ldflda, fld_field_\d+\);
469+
\s+il_call_\d+.Emit\(OpCodes\.Ldflda, new FieldReference\("field", gp_T_\d+, cls_field_\d+.MakeGenericInstanceType\(gp_T_\d+\)\)\);
470470
""",
471471
TestName = "On Field")]
472472
public void CallOnValueType_ThroughInterface_IsConstrained(string t, string loadTargetOfCallIlRegex)

Cecilifier.Core/AST/ExpressionVisitor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1281,7 +1281,7 @@ private void StoreTopOfStackInLocalVariableAndLoad(ExpressionSyntax expressionSy
12811281
if (!HandleLoadAddress(ilVar, type, expressionSyntax, OpCodes.Ldloca_S, tempLocalName))
12821282
{
12831283
// HandleLoadAddress() does not handle scenarios in which a value type instantiation is passed as an
1284-
// 'in parameter' to a method (that method is already complex so I don't want to make it even more
1284+
// 'in parameter' to a method (that method is already complex, so I don't want to make it even more
12851285
// complex)
12861286
Context.EmitCilInstruction(ilVar, RequiresAddressOfValue() ? OpCodes.Ldloca_S : OpCodes.Ldloc, tempLocalName);
12871287
}

Cecilifier.Core/AST/SyntaxWalkerBase.cs

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -210,12 +210,6 @@ private void LoadLiteralToStackHandlingCallOnValueTypeLiterals(string ilVar, ITy
210210
}
211211
}
212212

213-
private void StoreTopOfStackInLocalVariableAndLoadItsAddress(string ilVar, ITypeSymbol type, string variableName = "tmp")
214-
{
215-
var tempLocalName = StoreTopOfStackInLocalVariable(Context, ilVar, variableName, type).VariableName;
216-
Context.EmitCilInstruction(ilVar, OpCodes.Ldloca_S, tempLocalName);
217-
}
218-
219213
protected IMethodSymbol DeclaredSymbolFor<T>(T node) where T : BaseMethodDeclarationSyntax
220214
{
221215
return Context.GetDeclaredSymbol(node);
@@ -402,7 +396,8 @@ protected void ProcessParameter(string ilVar, SimpleNameSyntax node, IParameterS
402396
{
403397
var method = (IMethodSymbol) paramSymbol.ContainingSymbol;
404398
var declaringMethodName = method.ToDisplayString();
405-
if (HandleLoadAddressOnStorage(ilVar, paramSymbol.Type, node, OpCodes.Ldarga, paramSymbol.Name, VariableMemberKind.Parameter, declaringMethodName))
399+
var operand = Context.DefinitionVariables.GetVariable(paramSymbol.Name, VariableMemberKind.Parameter, declaringMethodName).VariableName;
400+
if (HandleLoadAddress(ilVar, paramSymbol.Type, node, OpCodes.Ldarga, operand))
406401
return;
407402

408403
if (InlineArrayProcessor.HandleInlineArrayConversionToSpan(Context, ilVar, paramSymbol.Type, node, OpCodes.Ldarga_S, paramSymbol.Name, VariableMemberKind.Parameter, declaringMethodName))
@@ -431,6 +426,9 @@ protected void ProcessField(string ilVar, SimpleNameSyntax node, IFieldSymbol fi
431426
var nodeParent = (CSharpSyntaxNode) node.Parent;
432427
Debug.Assert(nodeParent != null);
433428

429+
fieldSymbol.EnsureFieldExists(Context, node);
430+
var resolvedFieldVariable = fieldSymbol.FieldResolverExpression(Context);
431+
434432
if (fieldSymbol.HasConstantValue && fieldSymbol.IsConst)
435433
{
436434
LoadLiteralToStackHandlingCallOnValueTypeLiterals(
@@ -446,34 +444,31 @@ protected void ProcessField(string ilVar, SimpleNameSyntax node, IFieldSymbol fi
446444
return;
447445
}
448446

449-
fieldSymbol.EnsureFieldExists(Context, node);
450-
451447
if (!fieldSymbol.IsStatic && node.IsMemberAccessThroughImplicitThis())
452448
Context.EmitCilInstruction(ilVar, OpCodes.Ldarg_0);
453449

454-
if (HandleLoadAddressOnStorage(ilVar, fieldSymbol.Type, node, fieldSymbol.IsStatic ? OpCodes.Ldsflda : OpCodes.Ldflda, fieldSymbol.Name, VariableMemberKind.Field, fieldSymbol.ContainingType.ToDisplayString()))
450+
if (HandleLoadAddress(ilVar, fieldSymbol.Type, node, fieldSymbol.IsStatic ? OpCodes.Ldsflda : OpCodes.Ldflda, resolvedFieldVariable))
455451
{
456452
return;
457453
}
458454

459455
if (fieldSymbol.IsVolatile)
460456
Context.EmitCilInstruction(ilVar, OpCodes.Volatile);
461457

462-
var resolvedField = fieldSymbol.FieldResolverExpression(Context);
463458
var opCode = fieldSymbol.LoadOpCodeForFieldAccess();
464-
Context.EmitCilInstruction(ilVar, opCode, resolvedField);
459+
Context.EmitCilInstruction(ilVar, opCode, resolvedFieldVariable);
465460
HandlePotentialDelegateInvocationOn(node, fieldSymbol.Type, ilVar);
466461
}
467462

468463
protected void ProcessLocalVariable(string ilVar, SimpleNameSyntax localVarSyntax, ILocalSymbol symbol)
469464
{
470-
if (HandleLoadAddressOnStorage(ilVar, symbol.Type, localVarSyntax, OpCodes.Ldloca, symbol.Name, VariableMemberKind.LocalVariable))
465+
var operand = Context.DefinitionVariables.GetVariable(symbol.Name, VariableMemberKind.LocalVariable).VariableName;
466+
if (HandleLoadAddress(ilVar, symbol.Type, localVarSyntax, OpCodes.Ldloca, operand))
471467
return;
472468

473469
if (InlineArrayProcessor.HandleInlineArrayConversionToSpan(Context, ilVar, symbol.Type, localVarSyntax, OpCodes.Ldloca_S, symbol.Name, VariableMemberKind.LocalVariable))
474470
return;
475-
476-
var operand = Context.DefinitionVariables.GetVariable(symbol.Name, VariableMemberKind.LocalVariable).VariableName;
471+
477472
Context.EmitCilInstruction(ilVar, OpCodes.Ldloc, operand);
478473

479474
HandlePotentialDelegateInvocationOn(localVarSyntax, symbol.Type, ilVar);
@@ -488,12 +483,6 @@ private void HandlePotentialFixedLoad(string ilVar, ILocalSymbol symbol)
488483
Context.EmitCilInstruction(ilVar, OpCodes.Conv_U);
489484
}
490485

491-
private bool HandleLoadAddressOnStorage(string ilVar, ITypeSymbol symbol, CSharpSyntaxNode node, OpCode opCode, string symbolName, VariableMemberKind variableMemberKind, string parentName = null)
492-
{
493-
var operand = Context.DefinitionVariables.GetVariable(symbolName, variableMemberKind, parentName).VariableName;
494-
return HandleLoadAddress(ilVar, symbol, node, opCode, operand);
495-
}
496-
497486
protected bool HandleLoadAddress(string ilVar, ITypeSymbol loadedType, CSharpSyntaxNode node, OpCode loadOpCode, string operand)
498487
{
499488
var parentNode = (CSharpSyntaxNode)node.Parent;

Cecilifier.Core/Extensions/FieldExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public static string FieldResolverExpression(this IFieldSymbol field, IVisitorCo
1616
var found = context.DefinitionVariables.GetVariable(field.Name, VariableMemberKind.Field, field.ContainingType.OriginalDefinition.ToDisplayString());
1717
ThrowIfVariableNotFound(found.IsValid);
1818

19-
var resolvedField = field.ContainingType.IsGenericType && !field.ContainingType.IsDefinition
19+
var resolvedField = field.ContainingType.IsGenericType// && !field.ContainingType.IsDefinition
2020
? $$"""new FieldReference("{{field.Name}}", {{context.TypeResolver.Resolve(field.OriginalDefinition.Type)}}, {{context.TypeResolver.Resolve(field.ContainingType)}})"""
2121
: found.VariableName;
2222

Cecilifier.Web/wwwroot/js/cecilifier.snippethandler.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ function cecilifyFromSnippet(counter) {
8484
let savedSnippetSnack = null;
8585
function showListOfLocallyStoredSnippets(editor) {
8686
let snippetNames = [];
87+
if (savedSnippetSnack !== null)
88+
{
89+
return;
90+
}
91+
8792
for(let i = 0; i < window.localStorage.length; i++) {
8893
let key = window.localStorage.key(i);
8994
if (key.startsWith(saved_snippet_prefix)) {
@@ -152,6 +157,8 @@ function setupObserverToResetSavedSnippetListSnackUponClosing(closeSavedSnippetL
152157
savedSnippetSnack = null;
153158
closeSavedSnippetListAction.dispose();
154159
}
160+
if (mutation.attributeName === 'style')
161+
savedSnippetSnack = null;
155162
});
156163
};
157164

0 commit comments

Comments
 (0)