提交 51a34466 编写于 作者: K Kevin Halverson

Fix issue with locating source method in optimized code...

We were previously assuming that "this" was always hoisted in async/iterator state machines.  This is not true in optimized code.

(fixes #2406)
上级 e619d015
......@@ -1435,7 +1435,7 @@ private static NamedTypeSymbol GetNonDisplayClassContainer(NamedTypeSymbol type)
/// The symbol of the method that is currently on top of the callstack, with
/// EE type parameters substituted in place of the original type parameters.
/// </param>
/// <param name="hasDisplayClassThis">
/// <param name="sourceMethodMustBeInstance">
/// True if "this" is available via a display class in the current context.
/// </param>
/// <returns>
......@@ -1459,7 +1459,7 @@ private static NamedTypeSymbol GetNonDisplayClassContainer(NamedTypeSymbol type)
/// </remarks>
internal static MethodSymbol GetSubstitutedSourceMethod(
MethodSymbol candidateSubstitutedSourceMethod,
bool hasDisplayClassThis)
bool sourceMethodMustBeInstance)
{
var candidateSubstitutedSourceType = candidateSubstitutedSourceMethod.ContainingType;
......@@ -1477,28 +1477,18 @@ private static NamedTypeSymbol GetNonDisplayClassContainer(NamedTypeSymbol type)
if (GeneratedNames.GetKind(containing.Name) == GeneratedNameKind.LambdaDisplayClass)
{
candidateSubstitutedSourceType = containing;
hasDisplayClassThis = candidateSubstitutedSourceType.MemberNames.Select(GeneratedNames.GetKind).Contains(GeneratedNameKind.ThisProxyField);
sourceMethodMustBeInstance = candidateSubstitutedSourceType.MemberNames.Select(GeneratedNames.GetKind).Contains(GeneratedNameKind.ThisProxyField);
}
}
var desiredTypeParameters = candidateSubstitutedSourceType.OriginalDefinition.TypeParameters;
// We need to use a ThreeState, rather than a bool, because we can't distinguish between
// a roslyn lambda that only captures "this" and a dev12 lambda that captures nothing
// (neither introduces a display class). This is unnecessary in the state machine case,
// because then "this" is hoisted unconditionally.
var isDesiredMethodStatic = hasDisplayClassThis
? ThreeState.False
: (GeneratedNames.GetKind(candidateSubstitutedSourceType.Name) == GeneratedNameKind.StateMachineType)
? ThreeState.True
: ThreeState.Unknown;
// Type containing the original iterator, async, or lambda-containing method.
var substitutedSourceType = GetNonDisplayClassContainer(candidateSubstitutedSourceType);
foreach (var candidateMethod in substitutedSourceType.GetMembers().OfType<MethodSymbol>())
{
if (IsViableSourceMethod(candidateMethod, desiredMethodName, desiredTypeParameters, isDesiredMethodStatic))
if (IsViableSourceMethod(candidateMethod, desiredMethodName, desiredTypeParameters, sourceMethodMustBeInstance))
{
return desiredTypeParameters.Length == 0
? candidateMethod
......@@ -1512,11 +1502,13 @@ private static NamedTypeSymbol GetNonDisplayClassContainer(NamedTypeSymbol type)
return candidateSubstitutedSourceMethod;
}
private static bool IsViableSourceMethod(MethodSymbol candidateMethod, string desiredMethodName, ImmutableArray<TypeParameterSymbol> desiredTypeParameters, ThreeState isDesiredMethodStatic)
private static bool IsViableSourceMethod(
MethodSymbol candidateMethod,
string desiredMethodName, ImmutableArray<TypeParameterSymbol> desiredTypeParameters, bool desiredMethodMustBeInstance)
{
return
!candidateMethod.IsAbstract &&
(isDesiredMethodStatic == ThreeState.Unknown || isDesiredMethodStatic.Value() == candidateMethod.IsStatic) &&
(!(desiredMethodMustBeInstance && candidateMethod.IsStatic)) &&
candidateMethod.Name == desiredMethodName &&
HaveSameConstraints(candidateMethod.TypeParameters, desiredTypeParameters);
}
......
......@@ -6,7 +6,6 @@
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.ExpressionEvaluator;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio.Debugger.Evaluation.ClrCompilation;
using Roslyn.Test.Utilities;
using System;
using System.Collections.Immutable;
......@@ -117,7 +116,7 @@ .maxstack 1
IL_0001: ret
}";
VerifyHasThis(source, "C.<M>b__0_0", "C", expectedIL);
VerifyHasThis(source, "C.<M>b__0_0", "C", expectedIL, thisCanBeElided: false);
}
[Fact]
......@@ -142,7 +141,7 @@ .locals init (int V_0)
IL_0006: ret
}";
VerifyHasThis(source, "C.<F>d__0.MoveNext", "C", expectedIL);
VerifyHasThis(source, "C.<F>d__0.MoveNext", "C", expectedIL, thisCanBeElided: false);
}
[Fact]
......@@ -173,7 +172,7 @@ .maxstack 1
IL_0006: ret
}";
VerifyHasThis(source, "C.<F>d__0.MoveNext", "C", expectedIL);
VerifyHasThis(source, "C.<F>d__0.MoveNext", "C", expectedIL, thisCanBeElided: false);
}
[Fact]
......@@ -200,7 +199,7 @@ .maxstack 1
IL_0006: ret
}";
VerifyHasThis(source, "C.<>c__DisplayClass1_0.<M>b__0", "C", expectedIL);
VerifyHasThis(source, "C.<>c__DisplayClass1_0.<M>b__0", "C", expectedIL, thisCanBeElided: false);
}
[WorkItem(1067379)]
......@@ -227,7 +226,7 @@ .maxstack 1
IL_0001: ret
}";
VerifyHasThis(source, "C.<M>b__1_0", "C", expectedIL);
VerifyHasThis(source, "C.<M>b__1_0", "C", expectedIL, thisCanBeElided: false);
}
[Fact]
......@@ -252,7 +251,7 @@ .locals init (int V_0)
IL_0006: ret
}";
VerifyHasThis(source, "C.<F>d__0.MoveNext", "C<T>", expectedIL);
VerifyHasThis(source, "C.<F>d__0.MoveNext", "C<T>", expectedIL, thisCanBeElided: false);
}
[Fact]
......@@ -283,7 +282,7 @@ .maxstack 1
IL_0006: ret
}";
VerifyHasThis(source, "C.<F>d__0.MoveNext", "C<T>", expectedIL);
VerifyHasThis(source, "C.<F>d__0.MoveNext", "C<T>", expectedIL, thisCanBeElided: false);
}
[Fact]
......@@ -310,7 +309,7 @@ .maxstack 1
IL_0006: ret
}";
VerifyHasThis(source, "C.<>c__DisplayClass1_0.<M>b__0", "C<T>", expectedIL);
VerifyHasThis(source, "C.<>c__DisplayClass1_0.<M>b__0", "C<T>", expectedIL, thisCanBeElided: false);
}
[Fact]
......@@ -340,7 +339,7 @@ .locals init (int V_0)
IL_0006: ret
}";
VerifyHasThis(source, "C.<I-F>d__0.MoveNext", "C", expectedIL);
VerifyHasThis(source, "C.<I-F>d__0.MoveNext", "C", expectedIL, thisCanBeElided: false);
}
[Fact]
......@@ -376,7 +375,7 @@ .maxstack 1
IL_0006: ret
}";
VerifyHasThis(source, "C.<I-F>d__0.MoveNext", "C", expectedIL);
VerifyHasThis(source, "C.<I-F>d__0.MoveNext", "C", expectedIL, thisCanBeElided: false);
}
[Fact]
......@@ -408,7 +407,7 @@ .maxstack 1
IL_0006: ret
}";
VerifyHasThis(source, "C.<>c__DisplayClass1_0.<I.M>b__0", "C", expectedIL);
VerifyHasThis(source, "C.<>c__DisplayClass1_0.<I.M>b__0", "C", expectedIL, thisCanBeElided: false);
}
[Fact]
......@@ -438,7 +437,7 @@ .locals init (int V_0)
IL_0006: ret
}";
VerifyHasThis(source, "C.<I<System-Int32>-F>d__0.MoveNext", "C", expectedIL);
VerifyHasThis(source, "C.<I<System-Int32>-F>d__0.MoveNext", "C", expectedIL, thisCanBeElided: false);
}
[Fact]
......@@ -474,7 +473,7 @@ .maxstack 1
IL_0006: ret
}";
VerifyHasThis(source, "C.<I<System-Int32>-F>d__0.MoveNext", "C", expectedIL);
VerifyHasThis(source, "C.<I<System-Int32>-F>d__0.MoveNext", "C", expectedIL, thisCanBeElided: false);
}
[Fact]
......@@ -506,7 +505,7 @@ .maxstack 1
IL_0006: ret
}";
VerifyHasThis(source, "C.<>c__DisplayClass1_0.<I<System.Int32>.M>b__0", "C", expectedIL);
VerifyHasThis(source, "C.<>c__DisplayClass1_0.<I<System.Int32>.M>b__0", "C", expectedIL, thisCanBeElided: false);
}
[WorkItem(1066489)]
......@@ -948,13 +947,33 @@ static int P
}
}
private void VerifyHasThis(string source, string methodName, string expectedType, string expectedIL)
private void VerifyHasThis(string source, string methodName, string expectedType, string expectedIL, bool thisCanBeElided = true)
{
var comp = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugDll, assemblyName: ExpressionCompilerUtilities.GenerateUniqueName());
var runtime = CreateRuntimeInstance(comp);
var sourceCompilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugDll, assemblyName: ExpressionCompilerUtilities.GenerateUniqueName());
var runtime = CreateRuntimeInstance(sourceCompilation);
var context = CreateMethodContext(runtime, methodName);
VerifyHasThis(context, expectedType, expectedIL);
// Now recompile and test CompileExpression with optimized code.
sourceCompilation = sourceCompilation.WithOptions(sourceCompilation.Options.WithOptimizationLevel(OptimizationLevel.Release));
runtime = CreateRuntimeInstance(sourceCompilation);
context = CreateMethodContext(runtime, methodName);
// In C#, "this" may be optimized away.
if (thisCanBeElided)
{
VerifyNoThis(context);
}
else
{
VerifyHasThis(context, expectedType, expectedIL: null);
}
// Verify that binding a trivial expression succeeds.
string error;
var testData = new CompilationTestData();
context.CompileExpression("42", out error, testData);
Assert.Null(error);
Assert.Equal(1, testData.Methods.Count);
}
private static void VerifyHasThis(EvaluationContext context, string expectedType, string expectedIL)
......@@ -966,14 +985,20 @@ private static void VerifyHasThis(EvaluationContext context, string expectedType
Assert.NotNull(assembly);
Assert.NotEqual(assembly.Count, 0);
var localAndMethod = locals.Single(l => l.LocalName == "this");
VerifyMethodData(testData.Methods.Single(m => m.Key.Contains(localAndMethod.MethodName)).Value, expectedType, expectedIL);
if (expectedIL != null)
{
VerifyMethodData(testData.Methods.Single(m => m.Key.Contains(localAndMethod.MethodName)).Value, expectedType, expectedIL);
}
locals.Free();
string error;
testData = new CompilationTestData();
context.CompileExpression("this", out error, testData);
Assert.Null(error);
VerifyMethodData(testData.Methods.Single(m => m.Key.Contains("<>m0")).Value, expectedType, expectedIL);
if (expectedIL != null)
{
VerifyMethodData(testData.Methods.Single(m => m.Key.Contains("<>m0")).Value, expectedType, expectedIL);
}
}
private static void VerifyMethodData(CompilationTestData.MethodData methodData, string expectedType, string expectedIL)
......@@ -1435,7 +1460,7 @@ private static void CheckIteratorOverloading(string source, Func<MethodSymbol, b
var stateMachineType = originalType.GetMembers().OfType<NamedTypeSymbol>().Single(t => GeneratedNames.GetKind(t.Name) == GeneratedNameKind.StateMachineType);
var moveNextMethod = stateMachineType.GetMember<MethodSymbol>("MoveNext");
var guessedIterator = CompilationContext.GetSubstitutedSourceMethod(moveNextMethod, hasDisplayClassThis: true);
var guessedIterator = CompilationContext.GetSubstitutedSourceMethod(moveNextMethod, sourceMethodMustBeInstance: true);
Assert.Equal(iteratorMethod, guessedIterator.OriginalDefinition);
}
}
......
......@@ -1285,7 +1285,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
''' The symbol of the method that is currently on top of the callstack, with
''' EE type parameters substituted in place of the original type parameters.
''' </param>
''' <param name="hasDisplayClassMe">
''' <param name="sourceMethodMustBeInstance">
''' True if "Me" is available via a display class in the current context
''' </param>
''' <returns>
......@@ -1309,7 +1309,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
''' </remarks>
Friend Shared Function GetSubstitutedSourceMethod(
candidateSubstitutedSourceMethod As MethodSymbol,
hasDisplayClassMe As Boolean) As MethodSymbol
sourceMethodMustBeInstance As Boolean) As MethodSymbol
Dim candidateSubstitutedSourceType = candidateSubstitutedSourceMethod.ContainingType
Dim candidateSourceTypeName = candidateSubstitutedSourceType.Name
......@@ -1326,25 +1326,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
Debug.Assert(containing IsNot Nothing)
If containing.IsClosureType() Then
candidateSubstitutedSourceType = containing
hasDisplayClassMe = candidateSubstitutedSourceType.MemberNames.Contains(StringConstants.HoistedMeName, StringComparer.Ordinal)
sourceMethodMustBeInstance = candidateSubstitutedSourceType.MemberNames.Contains(StringConstants.HoistedMeName, StringComparer.Ordinal)
End If
End If
Dim desiredTypeParameters = candidateSubstitutedSourceType.OriginalDefinition.TypeParameters
' We need to use a ThreeState, rather than a Boolean, because we can't distinguish between
' a roslyn lambda that only captures "Me" and a dev12 lambda that captures nothing (neither
' introduces a display class). This is unnecessary in the state machine case, because then
' "Me" is hoisted unconditionally.
Dim isDesiredMethodShared = If(hasDisplayClassMe,
ThreeState.False,
If(candidateSubstitutedSourceType.IsStateMachineType(), ThreeState.True, ThreeState.Unknown))
' Type containing the original iterator, async, or lambda-containing method.
Dim substitutedSourceType = GetNonClosureOrStateMachineContainer(candidateSubstitutedSourceType)
For Each candidateMethod In substitutedSourceType.GetMembers().OfType(Of MethodSymbol)()
If IsViableSourceMethod(candidateMethod, desiredMethodName, desiredTypeParameters, isDesiredMethodShared) Then
If IsViableSourceMethod(candidateMethod, desiredMethodName, desiredTypeParameters, sourceMethodMustBeInstance) Then
Return If(desiredTypeParameters.Length = 0,
candidateMethod,
candidateMethod.Construct(candidateSubstitutedSourceType.TypeArguments))
......@@ -1374,11 +1366,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
candidateMethod As MethodSymbol,
desiredMethodName As String,
desiredTypeParameters As ImmutableArray(Of TypeParameterSymbol),
desiredMethodIsShared As ThreeState) As Boolean
desiredMethodMustBeInstance As Boolean) As Boolean
Return _
Not candidateMethod.IsMustOverride AndAlso
(desiredMethodIsShared = ThreeState.Unknown OrElse desiredMethodIsShared.Value() = candidateMethod.IsShared) AndAlso
(Not (desiredMethodMustBeInstance AndAlso candidateMethod.IsShared)) AndAlso
(desiredMethodName Is Nothing OrElse desiredMethodName = candidateMethod.Name) AndAlso
HasDesiredConstraints(candidateMethod, desiredTypeParameters)
End Function
......
......@@ -624,14 +624,21 @@ End Module
End Sub
Private Sub VerifyHasMe(source As String, moveNextMethodName As String, expectedType As String, expectedIL As String)
Dim comp = CreateCompilationWithReferences(
Dim sourceCompilation = CreateCompilationWithReferences(
{VisualBasicSyntaxTree.ParseText(source)},
{MscorlibRef_v4_0_30316_17626, SystemRef_v4_0_30319_17929, MsvbRef_v4_0_30319_17929},
TestOptions.DebugDll)
Dim runtime = CreateRuntimeInstance(comp)
Dim runtime = CreateRuntimeInstance(sourceCompilation)
Dim context = CreateMethodContext(runtime, moveNextMethodName)
VerifyHasMe(context, expectedType, expectedIL)
' Now recompile and test CompileExpression with optimized code.
sourceCompilation = sourceCompilation.WithOptions(sourceCompilation.Options.WithOptimizationLevel(OptimizationLevel.Release))
runtime = CreateRuntimeInstance(sourceCompilation)
context = CreateMethodContext(runtime, moveNextMethodName)
' In VB, "Me" is never optimized away.
VerifyHasMe(context, expectedType, expectedIL:=Nothing)
End Sub
Private Sub VerifyHasMe(context As EvaluationContext, expectedType As String, expectedIL As String)
......@@ -642,14 +649,18 @@ End Module
Assert.NotNull(assembly)
Assert.NotEqual(0, assembly.Count)
Dim localAndMethod = locals.Single(Function(l) l.LocalName = "Me")
VerifyMethodData(testData.Methods.Single(Function(m) m.Key.Contains(localAndMethod.MethodName)).Value, expectedType, expectedIL)
If expectedIL IsNot Nothing Then
VerifyMethodData(testData.Methods.Single(Function(m) m.Key.Contains(localAndMethod.MethodName)).Value, expectedType, expectedIL)
End If
locals.Free()
Dim errorMessage As String = Nothing
testData = New CompilationTestData()
context.CompileExpression("Me", errorMessage, testData)
Assert.Null(errorMessage)
VerifyMethodData(testData.Methods.Single(Function(m) m.Key.Contains("<>m0")).Value, expectedType, expectedIL)
If expectedIL IsNot Nothing Then
VerifyMethodData(testData.Methods.Single(Function(m) m.Key.Contains("<>m0")).Value, expectedType, expectedIL)
End If
End Sub
Private Shared Sub VerifyMethodData(methodData As CompilationTestData.MethodData, expectedType As String, expectedIL As String)
......@@ -1150,7 +1161,7 @@ End Class
Dim synthesizedMethod As MethodSymbol = getSynthesizedMethod(originalType)
Dim guessedMethod = CompilationContext.GetSubstitutedSourceMethod(synthesizedMethod, hasDisplayClassMe:=True)
Dim guessedMethod = CompilationContext.GetSubstitutedSourceMethod(synthesizedMethod, sourceMethodMustBeInstance:=True)
Assert.Equal(desiredMethod, guessedMethod.OriginalDefinition)
End Sub
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册