提交 a5b9196b 编写于 作者: C Charles Stoner

Include captured symbols from display classes passed as arguments

上级 014308ab
......@@ -93,7 +93,7 @@ internal sealed class CompilationContext
// Assert that the cheap check for "this" is equivalent to the expensive check for "this".
Debug.Assert(
_displayClassVariables.ContainsKey(GeneratedNames.ThisProxyFieldName()) ==
(GetThisProxy(_displayClassVariables) != null) ==
_displayClassVariables.Values.Any(v => v.Kind == DisplayClassVariableKind.This));
}
......@@ -180,7 +180,7 @@ internal sealed class CompilationContext
this,
(EEMethodSymbol method, DiagnosticBag diags, out ImmutableArray<LocalSymbol> declaredLocals, out ResultProperties properties) =>
{
var hasDisplayClassThis = _displayClassVariables.ContainsKey(GeneratedNames.ThisProxyFieldName());
var hasDisplayClassThis = GetThisProxy(_displayClassVariables) != null;
var binder = ExtendBinderChain(
syntax,
aliases,
......@@ -218,7 +218,7 @@ internal sealed class CompilationContext
this,
(EEMethodSymbol method, DiagnosticBag diags, out ImmutableArray<LocalSymbol> declaredLocals, out ResultProperties properties) =>
{
var hasDisplayClassThis = _displayClassVariables.ContainsKey(GeneratedNames.ThisProxyFieldName());
var hasDisplayClassThis = GetThisProxy(_displayClassVariables) != null;
var binder = ExtendBinderChain(
syntax,
aliases,
......@@ -349,7 +349,7 @@ private static string GetNextMethodName(ArrayBuilder<MethodSymbol> builder)
// "this" for non-static methods that are not display class methods or
// display class methods where the display class contains "<>4__this".
if (!m.IsStatic && (!IsDisplayClassType(m.ContainingType) || _displayClassVariables.ContainsKey(GeneratedNames.ThisProxyFieldName())))
if ((!m.IsStatic && !IsDisplayClassType(m.ContainingType)) || GetThisProxy( _displayClassVariables) != null)
{
var methodName = GetNextMethodName(methodBuilder);
var method = this.GetThisMethod(container, methodName);
......@@ -381,7 +381,9 @@ private static string GetNextMethodName(ArrayBuilder<MethodSymbol> builder)
foreach (var parameter in m.Parameters)
{
var parameterName = parameter.Name;
if (!_hoistedParameterNames.Contains(parameterName) && GeneratedNames.GetKind(parameterName) == GeneratedNameKind.None)
if (!_hoistedParameterNames.Contains(parameterName) &&
GeneratedNames.GetKind(parameterName) == GeneratedNameKind.None &&
!IsDisplayClassParameter(parameter))
{
AppendParameterAndMethod(localBuilder, methodBuilder, parameter, container, parameterIndex);
}
......@@ -1212,7 +1214,8 @@ private static DkmClrCompilationResultFlags GetLocalResultFlags(LocalSymbol loca
foreach (var parameter in method.Parameters)
{
if (GeneratedNames.GetKind(parameter.Name) == GeneratedNameKind.TransparentIdentifier)
if (GeneratedNames.GetKind(parameter.Name) == GeneratedNameKind.TransparentIdentifier ||
IsDisplayClassParameter(parameter))
{
var instance = new DisplayClassInstanceFromParameter(parameter);
displayClassTypes.Add(instance.Type);
......@@ -1335,53 +1338,65 @@ private static DkmClrCompilationResultFlags GetLocalResultFlags(LocalSymbol loca
{
// Display class instance. The display class fields are variables.
int n = 0;
foreach (var member in instance.Type.GetMembers())
{
if (member.Kind != SymbolKind.Field)
{
continue;
}
var field = (FieldSymbol)member;
var fieldType = field.Type;
var fieldKind = GeneratedNames.GetKind(field.Name);
if (fieldKind == GeneratedNameKind.DisplayClassLocalOrField ||
fieldKind == GeneratedNameKind.TransparentIdentifier ||
IsTransparentIdentifierFieldInAnonymousType(field) ||
(fieldKind == GeneratedNameKind.ThisProxyField && GeneratedNames.GetKind(fieldType.Name) == GeneratedNameKind.LambdaDisplayClass)) // Async lambda case.
var fieldName = field.Name;
TryParseGeneratedName(fieldName, out var fieldKind, out var part);
switch (fieldKind)
{
Debug.Assert(!field.IsStatic);
// A local that is itself a display class instance.
if (displayClassTypes.Add((NamedTypeSymbol)fieldType))
{
var other = instance.FromField(field);
displayClassInstances.Add(other);
n++;
}
case GeneratedNameKind.DisplayClassLocalOrField:
case GeneratedNameKind.TransparentIdentifier:
break;
case GeneratedNameKind.AnonymousTypeField:
if (GeneratedNames.GetKind(part) != GeneratedNameKind.TransparentIdentifier)
{
continue;
}
break;
case GeneratedNameKind.ThisProxyField:
if (GeneratedNames.GetKind(fieldType.Name) != GeneratedNameKind.LambdaDisplayClass)
{
continue;
}
// Async lambda case.
break;
default:
continue;
}
}
return n;
}
private static bool IsTransparentIdentifierFieldInAnonymousType(FieldSymbol field)
{
string fieldName = field.Name;
Debug.Assert(!field.IsStatic);
if (GeneratedNames.GetKind(fieldName) != GeneratedNameKind.AnonymousTypeField)
{
return false;
}
GeneratedNameKind kind;
int openBracketOffset;
int closeBracketOffset;
if (!GeneratedNames.TryParseGeneratedName(fieldName, out kind, out openBracketOffset, out closeBracketOffset))
{
return false;
// A hoisted local that is itself a display class instance.
if (displayClassTypes.Add((NamedTypeSymbol)field.Type))
{
var other = instance.FromField(field);
displayClassInstances.Add(other);
n++;
}
}
fieldName = fieldName.Substring(openBracketOffset + 1, closeBracketOffset - openBracketOffset - 1);
return n;
}
return GeneratedNames.GetKind(fieldName) == GeneratedNameKind.TransparentIdentifier;
/// <summary>
/// Returns true if the parameter is a synthesized parameter representing
/// a display class instance (used to pass hoisted symbols to local functions).
/// </summary>
private static bool IsDisplayClassParameter(ParameterSymbol parameter)
{
var type = parameter.Type;
var result = type.Kind == SymbolKind.NamedType && IsDisplayClassType((NamedTypeSymbol)type);
Debug.Assert(!result || parameter.MetadataName == "");
return result;
}
private static void GetDisplayClassVariables(
......@@ -1407,16 +1422,13 @@ private static bool IsTransparentIdentifierFieldInAnonymousType(FieldSymbol fiel
DisplayClassVariableKind variableKind;
string variableName;
GeneratedNameKind fieldKind;
int openBracketOffset;
int closeBracketOffset;
GeneratedNames.TryParseGeneratedName(fieldName, out fieldKind, out openBracketOffset, out closeBracketOffset);
TryParseGeneratedName(fieldName, out var fieldKind, out var part);
switch (fieldKind)
{
case GeneratedNameKind.AnonymousTypeField:
Debug.Assert(fieldName == field.Name); // This only happens once.
fieldName = fieldName.Substring(openBracketOffset + 1, closeBracketOffset - openBracketOffset - 1);
fieldName = part;
goto REPARSE;
case GeneratedNameKind.TransparentIdentifier:
// A transparent identifier (field) in an anonymous type synthesized for a transparent identifier.
......@@ -1435,13 +1447,13 @@ private static bool IsTransparentIdentifierFieldInAnonymousType(FieldSymbol fiel
continue;
}
variableName = fieldName.Substring(openBracketOffset + 1, closeBracketOffset - openBracketOffset - 1);
variableName = part;
variableKind = DisplayClassVariableKind.Local;
Debug.Assert(!field.IsStatic);
break;
case GeneratedNameKind.ThisProxyField:
// A reference to "this".
variableName = fieldName;
variableName = ""; // Should not be referenced by name.
variableKind = DisplayClassVariableKind.This;
Debug.Assert(!field.IsStatic);
break;
......@@ -1487,6 +1499,22 @@ private static bool IsTransparentIdentifierFieldInAnonymousType(FieldSymbol fiel
}
}
private static bool TryParseGeneratedName(string name, out GeneratedNameKind kind, out string part)
{
bool result = GeneratedNames.TryParseGeneratedName(name, out kind, out int openBracketOffset, out int closeBracketOffset);
switch (kind)
{
case GeneratedNameKind.AnonymousTypeField:
case GeneratedNameKind.HoistedLocalField:
part = name.Substring(openBracketOffset + 1, closeBracketOffset - openBracketOffset - 1);
break;
default:
part = null;
break;
}
return result;
}
private static bool IsDisplayClassType(NamedTypeSymbol type)
{
switch (GeneratedNames.GetKind(type.Name))
......@@ -1499,6 +1527,11 @@ private static bool IsDisplayClassType(NamedTypeSymbol type)
}
}
internal static DisplayClassVariable GetThisProxy(ImmutableDictionary<string, DisplayClassVariable> displayClassVariables)
{
return displayClassVariables.Values.FirstOrDefault(v => v.Kind == DisplayClassVariableKind.This);
}
private static NamedTypeSymbol GetNonDisplayClassContainer(NamedTypeSymbol type)
{
// 1) Display class and state machine types are always nested within the types
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Roslyn.Utilities;
using System.Collections.Generic;
using System.Collections.Immutable;
......@@ -12,28 +11,28 @@ namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator
internal sealed class CapturedVariableRewriter : BoundTreeRewriterWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator
{
internal static BoundNode Rewrite(
ParameterSymbol targetMethodThisParameter,
GenerateThisReference getThisReference,
Conversions conversions,
ImmutableDictionary<string, DisplayClassVariable> displayClassVariables,
BoundNode node,
DiagnosticBag diagnostics)
{
var rewriter = new CapturedVariableRewriter(targetMethodThisParameter, conversions, displayClassVariables, diagnostics);
var rewriter = new CapturedVariableRewriter(getThisReference, conversions, displayClassVariables, diagnostics);
return rewriter.Visit(node);
}
private readonly ParameterSymbol _targetMethodThisParameter;
private readonly GenerateThisReference _getThisReference;
private readonly Conversions _conversions;
private readonly ImmutableDictionary<string, DisplayClassVariable> _displayClassVariables;
private readonly DiagnosticBag _diagnostics;
private CapturedVariableRewriter(
ParameterSymbol targetMethodThisParameter,
GenerateThisReference getThisReference,
Conversions conversions,
ImmutableDictionary<string, DisplayClassVariable> displayClassVariables,
DiagnosticBag diagnostics)
{
_targetMethodThisParameter = targetMethodThisParameter;
_getThisReference = getThisReference;
_conversions = conversions;
_displayClassVariables = displayClassVariables;
_diagnostics = diagnostics;
......@@ -65,7 +64,13 @@ public override BoundNode VisitLocal(BoundLocal node)
public override BoundNode VisitParameter(BoundParameter node)
{
return RewriteParameter(node.Syntax, node.ParameterSymbol, node);
var parameter = node.ParameterSymbol;
var variable = this.GetVariable(parameter.Name);
if (variable == null)
{
return node;
}
return variable.ToBoundExpression(node.Syntax);
}
public override BoundNode VisitMethodGroup(BoundMethodGroup node)
......@@ -75,24 +80,25 @@ public override BoundNode VisitMethodGroup(BoundMethodGroup node)
public override BoundNode VisitThisReference(BoundThisReference node)
{
return RewriteParameter(node.Syntax, _targetMethodThisParameter, node);
var rewrittenThis = GenerateThisReference(node);
Debug.Assert(rewrittenThis.Type.Equals(node.Type, TypeCompareKind.IgnoreDynamicAndTupleNames));
return rewrittenThis;
}
public override BoundNode VisitBaseReference(BoundBaseReference node)
{
var syntax = node.Syntax;
var rewrittenParameter = RewriteParameter(syntax, _targetMethodThisParameter, node);
var rewrittenThis = GenerateThisReference(node);
var baseType = node.Type;
HashSet<DiagnosticInfo> unusedUseSiteDiagnostics = null;
var conversion = _conversions.ClassifyImplicitConversionFromExpression(rewrittenParameter, baseType, ref unusedUseSiteDiagnostics);
var conversion = _conversions.ClassifyImplicitConversionFromExpression(rewrittenThis, baseType, ref unusedUseSiteDiagnostics);
Debug.Assert(unusedUseSiteDiagnostics == null || !conversion.IsValid || unusedUseSiteDiagnostics.All(d => d.Severity < DiagnosticSeverity.Error));
// It would be nice if we could just call BoundConversion.Synthesized, but it doesn't seem worthwhile to
// introduce a bunch of new overloads to accommodate isBaseConversion.
return new BoundConversion(
syntax,
rewrittenParameter,
rewrittenThis,
conversion,
isBaseConversion: true,
@checked: false,
......@@ -103,50 +109,21 @@ public override BoundNode VisitBaseReference(BoundBaseReference node)
{ WasCompilerGenerated = true };
}
private BoundExpression RewriteParameter(SyntaxNode syntax, ParameterSymbol symbol, BoundExpression node)
private BoundExpression GenerateThisReference(BoundExpression node)
{
// This can happen in error scenarios (e.g. user binds "this" in a lambda in a static method).
if ((object)symbol == null)
{
ReportMissingThis(node.Kind, syntax);
return node;
}
var variable = this.GetVariable(symbol.Name);
if (variable == null)
var syntax = node.Syntax;
var rewrittenThis = _getThisReference(syntax);
if (rewrittenThis != null)
{
var typeNameKind = GeneratedNames.GetKind(symbol.Type.Name);
if (typeNameKind != GeneratedNameKind.None &&
typeNameKind != GeneratedNameKind.AnonymousType)
{
// The state machine case is for async lambdas. The state machine
// will have a hoisted "this" field if it needs to access the
// containing display class, but the display class may not have a
// "this" field.
Debug.Assert(typeNameKind == GeneratedNameKind.LambdaDisplayClass ||
typeNameKind == GeneratedNameKind.StateMachineType,
$"Unexpected typeNameKind '{typeNameKind}'");
ReportMissingThis(node.Kind, syntax);
return node;
}
return (node as BoundParameter) ?? new BoundParameter(syntax, symbol);
return rewrittenThis;
}
var result = variable.ToBoundExpression(syntax);
Debug.Assert(node.Kind == BoundKind.BaseReference
? result.Type.BaseType.Equals(node.Type, TypeCompareKind.IgnoreDynamicAndTupleNames)
: result.Type.Equals(node.Type, TypeCompareKind.IgnoreDynamicAndTupleNames));
return result;
}
private void ReportMissingThis(BoundKind boundKind, SyntaxNode syntax)
{
var boundKind = node.Kind;
Debug.Assert(boundKind == BoundKind.ThisReference || boundKind == BoundKind.BaseReference);
var errorCode = boundKind == BoundKind.BaseReference
? ErrorCode.ERR_BaseInBadContext
: ErrorCode.ERR_ThisInBadContext;
_diagnostics.Add(new CSDiagnostic(new CSDiagnosticInfo(errorCode), syntax.Location));
return node;
}
private DisplayClassVariable GetVariable(string name)
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.CSharp.Symbols;
using System.Diagnostics;
using System;
using System.Diagnostics;
namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator
{
......@@ -60,6 +60,8 @@ internal DisplayClassInstanceFromParameter(ParameterSymbol parameter)
{
Debug.Assert((object)parameter != null);
Debug.Assert(parameter.Name.EndsWith("this", StringComparison.Ordinal) ||
parameter.Name.Equals("", StringComparison.Ordinal) || // unnamed
parameter.Name.Equals("value", StringComparison.Ordinal) || // display class instance passed to local function as parameter
GeneratedNames.GetKind(parameter.Name) == GeneratedNameKind.TransparentIdentifier);
this.Parameter = parameter;
}
......
......@@ -13,6 +13,8 @@
namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator
{
internal delegate BoundExpression GenerateThisReference(SyntaxNode syntax);
internal delegate BoundStatement GenerateMethodBody(
EEMethodSymbol method,
DiagnosticBag diagnostics,
......@@ -576,7 +578,7 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState,
// Rewrite "this" and "base" references to parameter in this method.
// Rewrite variables within body to reference existing display classes.
body = (BoundStatement)CapturedVariableRewriter.Rewrite(
this.SubstitutedSourceMethod.IsStatic ? null : _parameters[0],
this.GenerateThisReference,
compilation.Conversions,
_displayClassVariables,
body,
......@@ -647,6 +649,28 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState,
}
}
private BoundExpression GenerateThisReference(SyntaxNode syntax)
{
var thisProxy = CompilationContext.GetThisProxy(_displayClassVariables);
if (thisProxy != null)
{
return thisProxy.ToBoundExpression(syntax);
}
if ((object)_thisParameter != null)
{
var typeNameKind = GeneratedNames.GetKind(_thisParameter.Type.Name);
if (typeNameKind != GeneratedNameKind.None && typeNameKind != GeneratedNameKind.AnonymousType)
{
Debug.Assert(typeNameKind == GeneratedNameKind.LambdaDisplayClass ||
typeNameKind == GeneratedNameKind.StateMachineType,
$"Unexpected typeNameKind '{typeNameKind}'");
return null;
}
return new BoundParameter(syntax, _thisParameter);
}
return null;
}
private static TypeSymbol CalculateReturnType(CSharpCompilation compilation, BoundStatement bodyOpt)
{
if (bodyOpt == null)
......
......@@ -82,6 +82,7 @@
<Compile Include="DebuggerDisplayAttributeTests.cs" />
<Compile Include="DeclarationTests.cs" />
<Compile Include="ExpressionCompilerTestBase.cs" />
<Compile Include="LocalFunctionTests.cs" />
<Compile Include="MissingAssemblyTests.cs" />
<Compile Include="InstructionDecoderTests.cs" />
<Compile Include="HoistedStateMachineLocalTests.cs" />
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.ExpressionEvaluator;
using Microsoft.CodeAnalysis.ExpressionEvaluator.UnitTests;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.UnitTests
{
public class LocalFunctionTests : ExpressionCompilerTestBase
{
[Fact]
public void NoLocals()
{
var source =
@"class C
{
void F(int x)
{
int y = x + 1;
int G()
{
return 0;
};
int z = G();
}
}";
var compilation0 = CreateCompilationWithMscorlib(source, options: TestOptions.DebugDll);
WithRuntimeInstance(compilation0, runtime =>
{
var context = CreateMethodContext(runtime, "C.<F>g__G0_0");
var testData = new CompilationTestData();
var locals = ArrayBuilder<LocalAndMethod>.GetInstance();
string typeName;
var assembly = context.CompileGetLocals(locals, argumentsOnly: false, typeName: out typeName, testData: testData);
Assert.NotNull(assembly);
Assert.Equal(assembly.Count, 0);
Assert.Equal(locals.Count, 0);
locals.Free();
});
}
[Fact]
public void Locals()
{
var source =
@"class C
{
void F(int x)
{
int G(int y)
{
int z = y + 1;
return z;
};
G(x + 1);
}
}";
var compilation0 = CreateCompilationWithMscorlib(source, options: TestOptions.DebugDll);
WithRuntimeInstance(compilation0, runtime =>
{
var context = CreateMethodContext(runtime, "C.<F>g__G0_0");
var testData = new CompilationTestData();
var locals = ArrayBuilder<LocalAndMethod>.GetInstance();
string typeName;
var assembly = context.CompileGetLocals(locals, argumentsOnly: false, typeName: out typeName, testData: testData);
Assert.Equal(locals.Count, 2);
VerifyLocal(testData, typeName, locals[0], "<>m0", "y", expectedILOpt:
@"{
// Code size 2 (0x2)
.maxstack 1
.locals init (int V_0, //z
int V_1)
IL_0000: ldarg.0
IL_0001: ret
}");
VerifyLocal(testData, typeName, locals[1], "<>m1", "z", expectedILOpt:
@"{
// Code size 2 (0x2)
.maxstack 1
.locals init (int V_0, //z
int V_1)
IL_0000: ldloc.0
IL_0001: ret
}");
locals.Free();
string error;
context.CompileExpression("this.F(1)", out error, testData);
Assert.Equal(error, "error CS0027: Keyword 'this' is not available in the current context");
});
}
[Fact]
public void CapturedVariable()
{
var source =
@"class C
{
int x;
void F(int y)
{
int G()
{
return x + y;
};
int z = G();
}
}";
var compilation0 = CreateCompilationWithMscorlib(source, options: TestOptions.DebugDll);
WithRuntimeInstance(compilation0, runtime =>
{
var context = CreateMethodContext(runtime, "C.<F>g__G1_0");
var testData = new CompilationTestData();
var locals = ArrayBuilder<LocalAndMethod>.GetInstance();
string typeName;
var assembly = context.CompileGetLocals(locals, argumentsOnly: false, typeName: out typeName, testData: testData);
Assert.Equal(locals.Count, 2);
VerifyLocal(testData, typeName, locals[0], "<>m0", "this", expectedILOpt:
@"{
// Code size 7 (0x7)
.maxstack 1
.locals init (int V_0)
IL_0000: ldarg.0
IL_0001: ldfld ""C C.<>c__DisplayClass1_0.<>4__this""
IL_0006: ret
}");
VerifyLocal(testData, typeName, locals[1], "<>m1", "y", expectedILOpt:
@"{
// Code size 7 (0x7)
.maxstack 1
.locals init (int V_0)
IL_0000: ldarg.0
IL_0001: ldfld ""int C.<>c__DisplayClass1_0.y""
IL_0006: ret
}");
locals.Free();
testData = new CompilationTestData();
string error;
context.CompileExpression("this.F(1)", out error, testData);
Assert.Null(error);
testData.GetMethodData("<>x.<>m0").VerifyIL(
@"{
// Code size 13 (0xd)
.maxstack 2
.locals init (int V_0)
IL_0000: ldarg.0
IL_0001: ldfld ""C C.<>c__DisplayClass1_0.<>4__this""
IL_0006: ldc.i4.1
IL_0007: callvirt ""void C.F(int)""
IL_000c: ret
}");
});
}
[Fact]
public void MultipleDisplayClasses()
{
var source =
@"class C
{
void F1(int x)
{
int F2(int y)
{
int F3() => x + y;
return F3();
};
F2(1);
}
}";
var compilation0 = CreateCompilationWithMscorlib(source, options: TestOptions.DebugDll);
WithRuntimeInstance(compilation0, runtime =>
{
var context = CreateMethodContext(runtime, "C.<F1>g__F30_1");
var testData = new CompilationTestData();
var locals = ArrayBuilder<LocalAndMethod>.GetInstance();
string typeName;
var assembly = context.CompileGetLocals(locals, argumentsOnly: false, typeName: out typeName, testData: testData);
Assert.Equal(locals.Count, 2);
VerifyLocal(testData, typeName, locals[0], "<>m0", "x", expectedILOpt:
@"{
// Code size 7 (0x7)
.maxstack 1
IL_0000: ldarg.0
IL_0001: ldfld ""int C.<>c__DisplayClass0_0.x""
IL_0006: ret
}");
VerifyLocal(testData, typeName, locals[1], "<>m1", "y", expectedILOpt:
@"{
// Code size 7 (0x7)
.maxstack 1
IL_0000: ldarg.1
IL_0001: ldfld ""int C.<>c__DisplayClass0_1.y""
IL_0006: ret
}");
locals.Free();
testData = new CompilationTestData();
string error;
context.CompileExpression("x + y", out error, testData);
Assert.Null(error);
testData.GetMethodData("<>x.<>m0").VerifyIL(
@"{
// Code size 14 (0xe)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldfld ""int C.<>c__DisplayClass0_0.x""
IL_0006: ldarg.1
IL_0007: ldfld ""int C.<>c__DisplayClass0_1.y""
IL_000c: add
IL_000d: ret
}");
});
}
// Should not bind to unnamed display class parameters
// (unnamed parameters are treated as named "value").
[Fact]
public void CapturedVariableNamedValue()
{
var source =
@"class C
{
void F(int value)
{
int G()
{
return value + 1;
};
G();
}
}";
var compilation0 = CreateCompilationWithMscorlib(source, options: TestOptions.DebugDll);
WithRuntimeInstance(compilation0, runtime =>
{
var context = CreateMethodContext(runtime, "C.<F>g__G0_0");
var testData = new CompilationTestData();
var locals = ArrayBuilder<LocalAndMethod>.GetInstance();
string typeName;
var assembly = context.CompileGetLocals(locals, argumentsOnly: false, typeName: out typeName, testData: testData);
Assert.Equal(locals.Count, 1);
VerifyLocal(testData, typeName, locals[0], "<>m0", "value", expectedILOpt:
@"{
// Code size 7 (0x7)
.maxstack 1
.locals init (int V_0)
IL_0000: ldarg.0
IL_0001: ldfld ""int C.<>c__DisplayClass0_0.value""
IL_0006: ret
}");
locals.Free();
testData = new CompilationTestData();
string error;
context.CompileExpression("value", out error, testData);
Assert.Null(error);
testData.GetMethodData("<>x.<>m0").VerifyIL(
@"{
// Code size 7 (0x7)
.maxstack 1
.locals init (int V_0)
IL_0000: ldarg.0
IL_0001: ldfld ""int C.<>c__DisplayClass0_0.value""
IL_0006: ret
}");
});
}
// Should not bind to unnamed display class parameters
// (unnamed parameters are treated as named "value").
[WorkItem(18426, "https://github.com/dotnet/roslyn/issues/18426")]
[Fact(Skip = "18426")]
public void DisplayClassParameter()
{
var source =
@"class C
{
void F(int x)
{
int G()
{
return x + 1;
};
G();
}
}";
var compilation0 = CreateCompilationWithMscorlib(source, options: TestOptions.DebugDll);
WithRuntimeInstance(compilation0, runtime =>
{
var context = CreateMethodContext(runtime, "C.<F>g__G0_0");
var testData = new CompilationTestData();
string error;
context.CompileExpression("value", out error, testData);
Assert.Equal(error, "error CS0103: The name 'value' does not exist in the current context");
});
}
}
}
\ No newline at end of file
......@@ -41,6 +41,7 @@ static void M()
Assert.NotNull(assembly);
Assert.Equal(assembly.Count, 0);
Assert.Equal(locals.Count, 0);
locals.Free();
});
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册