提交 5b08d9b3 编写于 作者: A Andrew Casey

Improve EvaluationContext cache invalidation

Ignoring references, which are obviously the high-order bit, the old
strategy was to confirm that the methodToken, methodVersion, and smallest
containing scope were the same as the current context.  This had two
problems:

1) C# hoisted locals are not reflected in scopes and so we were not
invalidating the cache when we stepped across scope boundaries in async
and iterator methods.

2) We had to access the PDB in order to check whether the cache was still
valid (to figure out the smallest containing scope).

We propose a new approach: compute the largest span around the current IL
offset in which there are no scope boundaries (including hoisted local
scopes).  Then, when check cache validity by doing a simple span
containment check (plus methodToken and methodVersion, obviously).

Downside 1: We need all scopes in the method, not just the containing
ones.

Downside 2: If you somehow step around a nested scope (e.g. using
breakpoints), the new approach will invalidate the cache, whereas the old
one did not.
上级 19983a3f
...@@ -26,7 +26,7 @@ internal sealed class EvaluationContext : EvaluationContextBase ...@@ -26,7 +26,7 @@ internal sealed class EvaluationContext : EvaluationContextBase
internal const bool IsLocalScopeEndInclusive = false; internal const bool IsLocalScopeEndInclusive = false;
internal readonly ImmutableArray<MetadataBlock> MetadataBlocks; internal readonly ImmutableArray<MetadataBlock> MetadataBlocks;
internal readonly MethodScope MethodScope; internal readonly MethodContextReuseConstraints? MethodContextReuseConstraints;
internal readonly CSharpCompilation Compilation; internal readonly CSharpCompilation Compilation;
private readonly MetadataDecoder _metadataDecoder; private readonly MetadataDecoder _metadataDecoder;
...@@ -37,7 +37,7 @@ internal sealed class EvaluationContext : EvaluationContextBase ...@@ -37,7 +37,7 @@ internal sealed class EvaluationContext : EvaluationContextBase
private EvaluationContext( private EvaluationContext(
ImmutableArray<MetadataBlock> metadataBlocks, ImmutableArray<MetadataBlock> metadataBlocks,
MethodScope methodScope, MethodContextReuseConstraints? methodContextReuseConstraints,
CSharpCompilation compilation, CSharpCompilation compilation,
MetadataDecoder metadataDecoder, MetadataDecoder metadataDecoder,
MethodSymbol currentFrame, MethodSymbol currentFrame,
...@@ -48,7 +48,7 @@ internal sealed class EvaluationContext : EvaluationContextBase ...@@ -48,7 +48,7 @@ internal sealed class EvaluationContext : EvaluationContextBase
Debug.Assert(inScopeHoistedLocalIndices != null); Debug.Assert(inScopeHoistedLocalIndices != null);
this.MetadataBlocks = metadataBlocks; this.MetadataBlocks = metadataBlocks;
this.MethodScope = methodScope; this.MethodContextReuseConstraints = methodContextReuseConstraints;
this.Compilation = compilation; this.Compilation = compilation;
_metadataDecoder = metadataDecoder; _metadataDecoder = metadataDecoder;
_currentFrame = currentFrame; _currentFrame = currentFrame;
...@@ -121,18 +121,15 @@ internal sealed class EvaluationContext : EvaluationContextBase ...@@ -121,18 +121,15 @@ internal sealed class EvaluationContext : EvaluationContextBase
{ {
Debug.Assert(MetadataTokens.Handle(methodToken).Kind == HandleKind.MethodDefinition); Debug.Assert(MetadataTokens.Handle(methodToken).Kind == HandleKind.MethodDefinition);
var typedSymReader = (ISymUnmanagedReader)symReader;
var scopes = ArrayBuilder<ISymUnmanagedScope>.GetInstance();
typedSymReader.GetScopes(methodToken, methodVersion, ilOffset, IsLocalScopeEndInclusive, scopes);
var scope = scopes.GetMethodScope(methodToken, methodVersion);
// Re-use the previous compilation if possible. // Re-use the previous compilation if possible.
CSharpCompilation compilation; CSharpCompilation compilation;
if (metadataBlocks.HaveNotChanged(previous)) if (metadataBlocks.HaveNotChanged(previous))
{ {
// Re-use entire context if method scope has not changed. // Re-use entire context if method scope has not changed.
var previousContext = previous.EvaluationContext; var previousContext = previous.EvaluationContext;
if ((scope != null) && (previousContext != null) && scope.Equals(previousContext.MethodScope)) if (previousContext != null &&
previousContext.MethodContextReuseConstraints.HasValue &&
previousContext.MethodContextReuseConstraints.GetValueOrDefault().AreSatisfied(methodToken, methodVersion, ilOffset))
{ {
return previousContext; return previousContext;
} }
...@@ -143,10 +140,15 @@ internal sealed class EvaluationContext : EvaluationContextBase ...@@ -143,10 +140,15 @@ internal sealed class EvaluationContext : EvaluationContextBase
compilation = metadataBlocks.ToCompilation(); compilation = metadataBlocks.ToCompilation();
} }
var localNames = scopes.GetLocalNames(); var typedSymReader = (ISymUnmanagedReader)symReader;
var allScopes = ArrayBuilder<ISymUnmanagedScope>.GetInstance();
var containingScopes = ArrayBuilder<ISymUnmanagedScope>.GetInstance();
typedSymReader.GetScopes(methodToken, methodVersion, ilOffset, IsLocalScopeEndInclusive, allScopes, containingScopes);
var methodContextReuseConstraints = allScopes.GetReuseConstraints(methodToken, methodVersion, ilOffset, IsLocalScopeEndInclusive);
allScopes.Free();
var localNames = containingScopes.GetLocalNames();
var dynamicLocalMap = ImmutableDictionary<int, ImmutableArray<bool>>.Empty;
var dynamicLocalConstantMap = ImmutableDictionary<string, ImmutableArray<bool>>.Empty;
var inScopeHoistedLocalIndices = ImmutableSortedSet<int>.Empty; var inScopeHoistedLocalIndices = ImmutableSortedSet<int>.Empty;
var methodDebugInfo = default(MethodDebugInfo); var methodDebugInfo = default(MethodDebugInfo);
...@@ -154,26 +156,9 @@ internal sealed class EvaluationContext : EvaluationContextBase ...@@ -154,26 +156,9 @@ internal sealed class EvaluationContext : EvaluationContextBase
{ {
try try
{ {
var cdi = typedSymReader.GetCustomDebugInfoBytes(methodToken, methodVersion);
if (cdi != null)
{
CustomDebugInfoReader.GetCSharpDynamicLocalInfo(
cdi,
methodToken,
methodVersion,
localNames.FirstOrDefault(),
out dynamicLocalMap,
out dynamicLocalConstantMap);
inScopeHoistedLocalIndices = CustomDebugInfoReader.GetCSharpInScopeHoistedLocalIndices(
cdi,
methodToken,
methodVersion,
ilOffset);
}
// TODO (acasey): switch on the type of typedSymReader and call the appropriate helper. (GH #702) // TODO (acasey): switch on the type of typedSymReader and call the appropriate helper. (GH #702)
methodDebugInfo = typedSymReader.GetMethodDebugInfo(methodToken, methodVersion); methodDebugInfo = typedSymReader.GetMethodDebugInfo(methodToken, methodVersion, localNames.FirstOrDefault());
inScopeHoistedLocalIndices = methodDebugInfo.GetInScopeHoistedLocalIndices(ilOffset, ref methodContextReuseConstraints);
} }
catch (InvalidOperationException) catch (InvalidOperationException)
{ {
...@@ -188,15 +173,15 @@ internal sealed class EvaluationContext : EvaluationContextBase ...@@ -188,15 +173,15 @@ internal sealed class EvaluationContext : EvaluationContextBase
var localInfo = metadataDecoder.GetLocalInfo(localSignatureToken); var localInfo = metadataDecoder.GetLocalInfo(localSignatureToken);
var localBuilder = ArrayBuilder<LocalSymbol>.GetInstance(); var localBuilder = ArrayBuilder<LocalSymbol>.GetInstance();
var sourceAssembly = compilation.SourceAssembly; var sourceAssembly = compilation.SourceAssembly;
GetLocals(localBuilder, currentFrame, localNames, localInfo, dynamicLocalMap, sourceAssembly); GetLocals(localBuilder, currentFrame, localNames, localInfo, methodDebugInfo.DynamicLocalMap, sourceAssembly);
GetConstants(localBuilder, currentFrame, scopes.GetConstantSignatures(), metadataDecoder, dynamicLocalConstantMap, sourceAssembly); GetConstants(localBuilder, currentFrame, containingScopes.GetConstantSignatures(), metadataDecoder, methodDebugInfo.DynamicLocalConstantMap, sourceAssembly);
scopes.Free(); containingScopes.Free();
var locals = localBuilder.ToImmutableAndFree(); var locals = localBuilder.ToImmutableAndFree();
return new EvaluationContext( return new EvaluationContext(
metadataBlocks, metadataBlocks,
scope, methodContextReuseConstraints,
compilation, compilation,
metadataDecoder, metadataDecoder,
currentFrame, currentFrame,
...@@ -478,7 +463,7 @@ internal CompilationContext CreateCompilationContext(CSharpSyntaxNode syntax) ...@@ -478,7 +463,7 @@ internal CompilationContext CreateCompilationContext(CSharpSyntaxNode syntax)
} }
ImmutableArray<bool> dynamicFlags; ImmutableArray<bool> dynamicFlags;
if (dynamicLocalMap.TryGetValue(i, out dynamicFlags)) if (dynamicLocalMap != null && dynamicLocalMap.TryGetValue(i, out dynamicFlags))
{ {
type = DynamicTypeDecoder.TransformTypeWithoutCustomModifierFlags( type = DynamicTypeDecoder.TransformTypeWithoutCustomModifierFlags(
type, type,
...@@ -510,7 +495,7 @@ internal CompilationContext CreateCompilationContext(CSharpSyntaxNode syntax) ...@@ -510,7 +495,7 @@ internal CompilationContext CreateCompilationContext(CSharpSyntaxNode syntax)
var type = info.Type; var type = info.Type;
ImmutableArray<bool> dynamicFlags; ImmutableArray<bool> dynamicFlags;
if (dynamicLocalConstantMap.TryGetValue(constant.Name, out dynamicFlags)) if (dynamicLocalConstantMap != null && dynamicLocalConstantMap.TryGetValue(constant.Name, out dynamicFlags))
{ {
type = DynamicTypeDecoder.TransformTypeWithoutCustomModifierFlags( type = DynamicTypeDecoder.TransformTypeWithoutCustomModifierFlags(
type, type,
......
...@@ -14,7 +14,8 @@ internal static class SymUnmanagedReaderExtensions ...@@ -14,7 +14,8 @@ internal static class SymUnmanagedReaderExtensions
public static MethodDebugInfo GetMethodDebugInfo( public static MethodDebugInfo GetMethodDebugInfo(
this ISymUnmanagedReader reader, this ISymUnmanagedReader reader,
int methodToken, int methodToken,
int methodVersion) int methodVersion,
string firstLocalName)
{ {
ImmutableArray<string> externAliasStrings; ImmutableArray<string> externAliasStrings;
var importStringGroups = reader.GetCSharpGroupedImportStrings(methodToken, methodVersion, out externAliasStrings); var importStringGroups = reader.GetCSharpGroupedImportStrings(methodToken, methodVersion, out externAliasStrings);
...@@ -72,9 +73,37 @@ internal static class SymUnmanagedReaderExtensions ...@@ -72,9 +73,37 @@ internal static class SymUnmanagedReaderExtensions
externAliasRecordBuilder.Add(new NativeExternAliasRecord<AssemblySymbol>(alias, targetIdentity)); externAliasRecordBuilder.Add(new NativeExternAliasRecord<AssemblySymbol>(alias, targetIdentity));
} }
var hoistedLocalScopeRecords = ImmutableArray<HoistedLocalScopeRecord>.Empty;
var dynamicLocalMap = ImmutableDictionary<int, ImmutableArray<bool>>.Empty;
var dynamicLocalConstantMap = ImmutableDictionary<string, ImmutableArray<bool>>.Empty;
byte[] customDebugInfoBytes = reader.GetCustomDebugInfoBytes(methodToken, methodVersion);
if (customDebugInfoBytes != null)
{
var customDebugInfoRecord = CustomDebugInfoReader.TryGetCustomDebugInfoRecord(customDebugInfoBytes, CustomDebugInfoKind.StateMachineHoistedLocalScopes);
if (!customDebugInfoRecord.IsDefault)
{
hoistedLocalScopeRecords = CustomDebugInfoReader.DecodeStateMachineHoistedLocalScopesRecord(customDebugInfoRecord)
.SelectAsArray(s => HoistedLocalScopeRecord.FromNative(s.StartOffset, s.EndOffset));
}
CustomDebugInfoReader.GetCSharpDynamicLocalInfo(
customDebugInfoBytes,
methodToken,
methodVersion,
firstLocalName,
out dynamicLocalMap,
out dynamicLocalConstantMap);
}
return new MethodDebugInfo( return new MethodDebugInfo(
hoistedLocalScopeRecords,
importRecordGroupBuilder.ToImmutableAndFree(), importRecordGroupBuilder.ToImmutableAndFree(),
externAliasRecordBuilder.ToImmutableAndFree(), externAliasRecordBuilder.ToImmutableAndFree(),
dynamicLocalMap,
dynamicLocalConstantMap,
defaultNamespaceName: ""); // Unused in C#. defaultNamespaceName: ""); // Unused in C#.
} }
......
...@@ -106,6 +106,7 @@ ...@@ -106,6 +106,7 @@
<Compile Include="LocalsTests.cs" /> <Compile Include="LocalsTests.cs" />
<Compile Include="NoPIATests.cs" /> <Compile Include="NoPIATests.cs" />
<Compile Include="PseudoVariableTests.cs" /> <Compile Include="PseudoVariableTests.cs" />
<Compile Include="MethodContextReuseConstraintsTests.cs" />
<Compile Include="UsingDebugInfoTests.cs" /> <Compile Include="UsingDebugInfoTests.cs" />
<Compile Include="WinMdTests.cs" /> <Compile Include="WinMdTests.cs" />
</ItemGroup> </ItemGroup>
......
...@@ -355,14 +355,14 @@ static void G() ...@@ -355,14 +355,14 @@ static void G()
Assert.Null(previous); Assert.Null(previous);
previous = new CSharpMetadataContext(context); previous = new CSharpMetadataContext(context);
// At end of outer scope. // At end of outer scope - not reused because of the nested scope.
context = EvaluationContext.CreateMethodContext(previous, methodBlocks, symReader, moduleVersionId, methodToken, methodVersion, endOffset, localSignatureToken); context = EvaluationContext.CreateMethodContext(previous, methodBlocks, symReader, moduleVersionId, methodToken, methodVersion, endOffset, localSignatureToken);
Assert.Equal(context, previous.EvaluationContext); Assert.NotEqual(context, previous.EvaluationContext); // Not required, just documentary.
// At type context. // At type context.
context = EvaluationContext.CreateTypeContext(previous, methodBlocks, moduleVersionId, typeToken); context = EvaluationContext.CreateTypeContext(previous, methodBlocks, moduleVersionId, typeToken);
Assert.NotEqual(context, previous.EvaluationContext); Assert.NotEqual(context, previous.EvaluationContext);
Assert.NotEqual(context.MethodScope, previous.EvaluationContext.MethodScope); Assert.Null(context.MethodContextReuseConstraints);
Assert.Equal(context.Compilation, previous.Compilation); Assert.Equal(context.Compilation, previous.Compilation);
// Step through entire method. // Step through entire method.
...@@ -371,6 +371,12 @@ static void G() ...@@ -371,6 +371,12 @@ static void G()
for (int offset = startOffset; offset <= endOffset; offset++) for (int offset = startOffset; offset <= endOffset; offset++)
{ {
var scope = scopes.GetInnermostScope(offset); var scope = scopes.GetInnermostScope(offset);
var constraints = previous.EvaluationContext.MethodContextReuseConstraints;
if (constraints.HasValue)
{
Assert.Equal(scope == previousScope, constraints.GetValueOrDefault().AreSatisfied(methodToken, methodVersion, offset));
}
context = EvaluationContext.CreateMethodContext(previous, methodBlocks, symReader, moduleVersionId, methodToken, methodVersion, offset, localSignatureToken); context = EvaluationContext.CreateMethodContext(previous, methodBlocks, symReader, moduleVersionId, methodToken, methodVersion, offset, localSignatureToken);
if (scope == previousScope) if (scope == previousScope)
{ {
...@@ -380,9 +386,9 @@ static void G() ...@@ -380,9 +386,9 @@ static void G()
{ {
// Different scope. Should reuse compilation. // Different scope. Should reuse compilation.
Assert.NotEqual(context, previous.EvaluationContext); Assert.NotEqual(context, previous.EvaluationContext);
if (previous != null) if (previous.EvaluationContext != null)
{ {
Assert.NotEqual(context.MethodScope, previous.EvaluationContext.MethodScope); Assert.NotEqual(context.MethodContextReuseConstraints, previous.EvaluationContext.MethodContextReuseConstraints);
Assert.Equal(context.Compilation, previous.Compilation); Assert.Equal(context.Compilation, previous.Compilation);
} }
} }
...@@ -399,7 +405,7 @@ static void G() ...@@ -399,7 +405,7 @@ static void G()
// Different references. No reuse. // Different references. No reuse.
context = EvaluationContext.CreateMethodContext(previous, methodBlocks, symReader, moduleVersionId, methodToken, methodVersion, endOffset, localSignatureToken); context = EvaluationContext.CreateMethodContext(previous, methodBlocks, symReader, moduleVersionId, methodToken, methodVersion, endOffset, localSignatureToken);
Assert.NotEqual(context, previous.EvaluationContext); Assert.NotEqual(context, previous.EvaluationContext);
Assert.Equal(context.MethodScope, previous.EvaluationContext.MethodScope); Assert.True(previous.EvaluationContext.MethodContextReuseConstraints.Value.AreSatisfied(methodToken, methodVersion, endOffset));
Assert.NotEqual(context.Compilation, previous.Compilation); Assert.NotEqual(context.Compilation, previous.Compilation);
previous = new CSharpMetadataContext(context); previous = new CSharpMetadataContext(context);
...@@ -407,7 +413,7 @@ static void G() ...@@ -407,7 +413,7 @@ static void G()
GetContextState(runtime, "C.G", out methodBlocks, out moduleVersionId, out symReader, out methodToken, out localSignatureToken); GetContextState(runtime, "C.G", out methodBlocks, out moduleVersionId, out symReader, out methodToken, out localSignatureToken);
context = EvaluationContext.CreateMethodContext(previous, methodBlocks, symReader, moduleVersionId, methodToken, methodVersion, ilOffset: 0, localSignatureToken: localSignatureToken); context = EvaluationContext.CreateMethodContext(previous, methodBlocks, symReader, moduleVersionId, methodToken, methodVersion, ilOffset: 0, localSignatureToken: localSignatureToken);
Assert.NotEqual(context, previous.EvaluationContext); Assert.NotEqual(context, previous.EvaluationContext);
Assert.NotEqual(context.MethodScope, previous.EvaluationContext.MethodScope); Assert.False(previous.EvaluationContext.MethodContextReuseConstraints.Value.AreSatisfied(methodToken, methodVersion, 0));
Assert.Equal(context.Compilation, previous.Compilation); Assert.Equal(context.Compilation, previous.Compilation);
// No EvaluationContext. Should reuse Compilation // No EvaluationContext. Should reuse Compilation
......
...@@ -1291,7 +1291,7 @@ .maxstack 1 ...@@ -1291,7 +1291,7 @@ .maxstack 1
[WorkItem(1134746, "DevDiv")] [WorkItem(1134746, "DevDiv")]
[Fact] [Fact]
public void Caching() public void CacheInvalidation()
{ {
var source = @" var source = @"
using System.Collections.Generic; using System.Collections.Generic;
......
// 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.ExpressionEvaluator;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
public class MethodContextReuseConstraintsTests : ExpressionCompilerTestBase
{
[Fact]
public void AreSatisfied()
{
const int methodToken = 0x06000001;
const int methodVersion = 1;
const uint startOffset = 1;
const uint endOffsetExclusive = 3;
var constraints = MethodContextReuseConstraints.CreateTestInstance(
methodToken,
methodVersion,
startOffset,
endOffsetExclusive);
Assert.True(constraints.AreSatisfied(methodToken, methodVersion, (int)startOffset));
Assert.True(constraints.AreSatisfied(methodToken, methodVersion, (int)endOffsetExclusive - 1));
Assert.False(constraints.AreSatisfied(methodToken + 1, methodVersion, (int)startOffset));
Assert.False(constraints.AreSatisfied(methodToken, methodVersion + 1, (int)startOffset));
Assert.False(constraints.AreSatisfied(methodToken, methodVersion, (int)startOffset - 1));
Assert.False(constraints.AreSatisfied(methodToken, methodVersion, (int)endOffsetExclusive));
}
[Fact]
public void EndInclusive()
{
const int methodToken = 0x06000001;
const int methodVersion = 1;
var builder = new MethodContextReuseConstraints.Builder(methodToken, methodVersion, ilOffset: 5, areRangesEndInclusive: true);
Assert.True(builder.Build().HasExpectedSpan(0u, uint.MaxValue));
builder.AddRange(1, 9);
Assert.True(builder.Build().HasExpectedSpan(1, 10));
builder.AddRange(2, 8);
Assert.True(builder.Build().HasExpectedSpan(2, 9));
builder.AddRange(1, 3);
Assert.True(builder.Build().HasExpectedSpan(4, 9));
builder.AddRange(7, 9);
Assert.True(builder.Build().HasExpectedSpan(4, 7));
}
[Fact]
public void EndExclusive()
{
const int methodToken = 0x06000001;
const int methodVersion = 1;
var builder = new MethodContextReuseConstraints.Builder(methodToken, methodVersion, ilOffset: 5, areRangesEndInclusive: false);
Assert.True(builder.Build().HasExpectedSpan(0u, uint.MaxValue));
builder.AddRange(1, 9);
Assert.True(builder.Build().HasExpectedSpan(1, 9));
builder.AddRange(2, 8);
Assert.True(builder.Build().HasExpectedSpan(2, 8));
builder.AddRange(1, 3);
Assert.True(builder.Build().HasExpectedSpan(3, 8));
builder.AddRange(7, 9);
Assert.True(builder.Build().HasExpectedSpan(3, 7));
}
[Fact]
public void Cumulative()
{
const int methodToken = 0x06000001;
const int methodVersion = 1;
var builder = new MethodContextReuseConstraints.Builder(methodToken, methodVersion, ilOffset: 5, areRangesEndInclusive: false);
Assert.True(builder.Build().HasExpectedSpan(0u, uint.MaxValue));
builder.AddRange(1, 10);
Assert.True(builder.Build().HasExpectedSpan(1, 10));
builder = new MethodContextReuseConstraints.Builder(builder.Build(), ilOffset: 5, areRangesEndInclusive: true);
builder.AddRange(2, 8);
Assert.True(builder.Build().HasExpectedSpan(2, 9));
builder = new MethodContextReuseConstraints.Builder(builder.Build(), ilOffset: 5, areRangesEndInclusive: false);
builder.AddRange(1, 3);
Assert.True(builder.Build().HasExpectedSpan(3, 9));
builder = new MethodContextReuseConstraints.Builder(builder.Build(), ilOffset: 5, areRangesEndInclusive: true);
builder.AddRange(7, 9);
Assert.True(builder.Build().HasExpectedSpan(3, 7));
}
}
}
\ No newline at end of file
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
<Compile Include="FrameDecoder.cs" /> <Compile Include="FrameDecoder.cs" />
<Compile Include="InstructionDecoder.cs" /> <Compile Include="InstructionDecoder.cs" />
<Compile Include="LanguageInstructionDecoder.cs" /> <Compile Include="LanguageInstructionDecoder.cs" />
<Compile Include="PDB\HoisedLocalScopeRecord.cs" />
<Compile Include="PDB\ExternAliasRecord.cs" /> <Compile Include="PDB\ExternAliasRecord.cs" />
<Compile Include="PDB\MethodDebugInfo.cs" /> <Compile Include="PDB\MethodDebugInfo.cs" />
<Compile Include="PDB\ImportRecord.cs" /> <Compile Include="PDB\ImportRecord.cs" />
...@@ -59,7 +60,7 @@ ...@@ -59,7 +60,7 @@
<Compile Include="MetadataBlock.cs" /> <Compile Include="MetadataBlock.cs" />
<Compile Include="MetadataUtilities.cs" /> <Compile Include="MetadataUtilities.cs" />
<Compile Include="AbstractTypeParameterChecker.cs" /> <Compile Include="AbstractTypeParameterChecker.cs" />
<Compile Include="MethodScope.cs" /> <Compile Include="MethodContextReuseConstraints.cs" />
<Compile Include="NamedLocalConstant.cs" /> <Compile Include="NamedLocalConstant.cs" />
<Compile Include="PseudoVariableUtilities.cs" /> <Compile Include="PseudoVariableUtilities.cs" />
<Compile Include="Resources.Designer.cs"> <Compile Include="Resources.Designer.cs">
......
...@@ -246,7 +246,14 @@ internal static bool IsWindowsAssemblyIdentity(this AssemblyIdentity assemblyIde ...@@ -246,7 +246,14 @@ internal static bool IsWindowsAssemblyIdentity(this AssemblyIdentity assemblyIde
/// Get the set of nested scopes containing the /// Get the set of nested scopes containing the
/// IL offset from outermost scope to innermost. /// IL offset from outermost scope to innermost.
/// </summary> /// </summary>
internal static void GetScopes(this ISymUnmanagedReader symReader, int methodToken, int methodVersion, int ilOffset, bool isScopeEndInclusive, ArrayBuilder<ISymUnmanagedScope> scopes) internal static void GetScopes(
this ISymUnmanagedReader symReader,
int methodToken,
int methodVersion,
int ilOffset,
bool isScopeEndInclusive,
ArrayBuilder<ISymUnmanagedScope> allScopes,
ArrayBuilder<ISymUnmanagedScope> containingScopes)
{ {
if (symReader == null) if (symReader == null)
{ {
...@@ -259,17 +266,17 @@ internal static void GetScopes(this ISymUnmanagedReader symReader, int methodTok ...@@ -259,17 +266,17 @@ internal static void GetScopes(this ISymUnmanagedReader symReader, int methodTok
return; return;
} }
symMethod.GetAllScopes(scopes, ilOffset, isScopeEndInclusive); symMethod.GetAllScopes(allScopes, containingScopes, ilOffset, isScopeEndInclusive);
} }
internal static MethodScope GetMethodScope(this ArrayBuilder<ISymUnmanagedScope> scopes, int methodToken, int methodVersion) internal static MethodContextReuseConstraints GetReuseConstraints(this ArrayBuilder<ISymUnmanagedScope> scopes, int methodToken, int methodVersion, int ilOffset, bool isEndInclusive)
{ {
if (scopes.Count == 0) var builder = new MethodContextReuseConstraints.Builder(methodToken, methodVersion, ilOffset, isEndInclusive);
foreach (ISymUnmanagedScope scope in scopes)
{ {
return null; builder.AddRange((uint)scope.GetStartOffset(), (uint)scope.GetEndOffset());
} }
var scope = scopes.Last(); return builder.Build();
return new MethodScope(methodToken, methodVersion, scope.GetStartOffset(), scope.GetEndOffset());
} }
internal static ImmutableArray<string> GetLocalNames(this ArrayBuilder<ISymUnmanagedScope> scopes) internal static ImmutableArray<string> GetLocalNames(this ArrayBuilder<ISymUnmanagedScope> scopes)
......
// 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 System;
using System.Diagnostics;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
namespace Microsoft.CodeAnalysis.ExpressionEvaluator
{
internal struct MethodContextReuseConstraints
{
private readonly int _methodToken;
private readonly int _methodVersion;
private readonly uint _startOffset;
private readonly uint _endOffsetExclusive;
private MethodContextReuseConstraints(int methodToken, int methodVersion, uint startOffset, uint endOffsetExclusive)
{
Debug.Assert(MetadataTokens.Handle(methodToken).Kind == HandleKind.MethodDefinition);
Debug.Assert(methodVersion >= 1);
Debug.Assert(startOffset <= endOffsetExclusive);
_methodToken = methodToken;
_methodVersion = methodVersion;
_startOffset = startOffset;
_endOffsetExclusive = endOffsetExclusive;
}
public bool AreSatisfied(int methodToken, int methodVersion, int ilOffset)
{
return methodToken == _methodToken &&
methodVersion == _methodVersion &&
ilOffset >= _startOffset &&
ilOffset < _endOffsetExclusive;
}
internal static MethodContextReuseConstraints CreateTestInstance(int methodToken, int methodVersion, uint startOffset, uint endOffsetExclusive)
{
return new MethodContextReuseConstraints(methodToken, methodVersion, startOffset, endOffsetExclusive);
}
internal bool HasExpectedSpan(uint startOffset, uint endOffsetExclusive)
{
return _startOffset == startOffset && _endOffsetExclusive == endOffsetExclusive;
}
public override string ToString()
{
return $"0x{_methodToken:x8}v{_methodVersion} [{_startOffset}, {_endOffsetExclusive})";
}
public class Builder
{
private readonly int _methodToken;
private readonly int _methodVersion;
private readonly int _ilOffset;
private readonly bool _areRangesEndInclusive;
private uint _startOffset;
private uint _endOffsetExclusive;
public Builder(int methodToken, int methodVersion, int ilOffset, bool areRangesEndInclusive)
{
Debug.Assert(MetadataTokens.Handle(methodToken).Kind == HandleKind.MethodDefinition);
Debug.Assert(methodVersion >= 1);
Debug.Assert(ilOffset >= 0);
_methodToken = methodToken;
_methodVersion = methodVersion;
_ilOffset = ilOffset;
_areRangesEndInclusive = areRangesEndInclusive;
_startOffset = 0;
_endOffsetExclusive = uint.MaxValue;
}
public Builder(MethodContextReuseConstraints existingConstraints, int ilOffset, bool areRangesEndInclusive)
{
_methodToken = existingConstraints._methodToken;
_methodVersion = existingConstraints._methodVersion;
_ilOffset = ilOffset;
_areRangesEndInclusive = areRangesEndInclusive;
_startOffset = existingConstraints._startOffset;
_endOffsetExclusive = existingConstraints._endOffsetExclusive;
}
public void AddRange(uint startOffset, uint endOffset)
{
Debug.Assert(startOffset >= 0);
Debug.Assert(startOffset <= endOffset);
Debug.Assert(!_areRangesEndInclusive || endOffset < int.MaxValue);
uint endOffsetExclusive = _areRangesEndInclusive ? (endOffset + 1) : endOffset;
if (_ilOffset < startOffset)
{
_endOffsetExclusive = Math.Min(_endOffsetExclusive, startOffset);
}
else if (_ilOffset >= endOffsetExclusive)
{
_startOffset = Math.Max(_startOffset, endOffsetExclusive);
}
else
{
_startOffset = Math.Max(_startOffset, startOffset);
_endOffsetExclusive = Math.Min(_endOffsetExclusive, endOffsetExclusive);
}
}
public MethodContextReuseConstraints Build()
{
return new MethodContextReuseConstraints(
_methodToken,
_methodVersion,
_startOffset,
_endOffsetExclusive);
}
}
}
}
// 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 System;
using System.Diagnostics;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.ExpressionEvaluator
{
internal sealed class MethodScope : IEquatable<MethodScope>
{
internal readonly int MethodToken;
internal readonly int MethodVersion;
internal readonly int StartOffset;
internal readonly int EndOffset;
internal MethodScope(int methodToken, int methodVersion, int startOffset, int endOffset)
{
Debug.Assert(MetadataTokens.Handle(methodToken).Kind == HandleKind.MethodDefinition);
Debug.Assert((startOffset >= 0) == (endOffset >= 0));
this.MethodToken = methodToken;
this.MethodVersion = methodVersion;
this.StartOffset = startOffset;
this.EndOffset = endOffset;
}
public bool Equals(MethodScope other)
{
if (other == null)
{
return false;
}
return (this.MethodToken == other.MethodToken) &&
(this.MethodVersion == other.MethodVersion) &&
(this.StartOffset == other.StartOffset) &&
(this.EndOffset == other.EndOffset);
}
public override bool Equals(object obj)
{
return Equals(obj as MethodScope);
}
public override int GetHashCode()
{
return Hash.Combine(this.MethodToken, Hash.Combine(this.MethodVersion, Hash.Combine(this.StartOffset, this.EndOffset)));
}
}
}
// 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 System.Collections.Immutable;
using Microsoft.CodeAnalysis;
namespace Microsoft.CodeAnalysis.ExpressionEvaluator
{
internal struct HoistedLocalScopeRecord
{
public readonly uint StartOffset;
public readonly uint Length;
private HoistedLocalScopeRecord(uint startOffset, uint length)
{
this.StartOffset = startOffset;
this.Length = length;
}
public static HoistedLocalScopeRecord FromNative(int startOffset, int endOffsetInclusive)
{
return new HoistedLocalScopeRecord((uint)startOffset, (uint)(endOffsetInclusive - startOffset + 1));
}
public static HoistedLocalScopeRecord FromPortable(uint startOffset, uint length)
{
return new HoistedLocalScopeRecord(startOffset, length);
}
}
}
...@@ -7,22 +7,68 @@ namespace Microsoft.CodeAnalysis.ExpressionEvaluator ...@@ -7,22 +7,68 @@ namespace Microsoft.CodeAnalysis.ExpressionEvaluator
{ {
internal struct MethodDebugInfo internal struct MethodDebugInfo
{ {
public readonly ImmutableArray<HoistedLocalScopeRecord> HoistedLocalScopeRecords;
public readonly ImmutableArray<ImmutableArray<ImportRecord>> ImportRecordGroups; public readonly ImmutableArray<ImmutableArray<ImportRecord>> ImportRecordGroups;
public readonly ImmutableArray<ExternAliasRecord> ExternAliasRecords;
public readonly string DefaultNamespaceName; public readonly ImmutableArray<ExternAliasRecord> ExternAliasRecords; // C# only.
public readonly ImmutableDictionary<int, ImmutableArray<bool>> DynamicLocalMap; // C# only.
public readonly ImmutableDictionary<string, ImmutableArray<bool>> DynamicLocalConstantMap; // C# only.
public readonly string DefaultNamespaceName; // VB only.
public MethodDebugInfo( public MethodDebugInfo(
ImmutableArray<HoistedLocalScopeRecord> hoistedLocalScopeRecords,
ImmutableArray<ImmutableArray<ImportRecord>> importRecordGroups, ImmutableArray<ImmutableArray<ImportRecord>> importRecordGroups,
ImmutableArray<ExternAliasRecord> externAliasRecords, ImmutableArray<ExternAliasRecord> externAliasRecords,
ImmutableDictionary<int, ImmutableArray<bool>> dynamicLocalMap,
ImmutableDictionary<string, ImmutableArray<bool>> dynamicLocalConstantMap,
string defaultNamespaceName) string defaultNamespaceName)
{ {
Debug.Assert(!importRecordGroups.IsDefault); Debug.Assert(!importRecordGroups.IsDefault);
Debug.Assert(!externAliasRecords.IsDefault); Debug.Assert(!externAliasRecords.IsDefault);
Debug.Assert(defaultNamespaceName != null); Debug.Assert(defaultNamespaceName != null);
Debug.Assert(!hoistedLocalScopeRecords.IsDefault);
HoistedLocalScopeRecords = hoistedLocalScopeRecords;
ImportRecordGroups = importRecordGroups; ImportRecordGroups = importRecordGroups;
ExternAliasRecords = externAliasRecords; ExternAliasRecords = externAliasRecords;
DynamicLocalMap = dynamicLocalMap;
DynamicLocalConstantMap = dynamicLocalConstantMap;
DefaultNamespaceName = defaultNamespaceName; DefaultNamespaceName = defaultNamespaceName;
} }
public ImmutableSortedSet<int> GetInScopeHoistedLocalIndices(int ilOffset, ref MethodContextReuseConstraints methodContextReuseConstraints)
{
if (this.HoistedLocalScopeRecords.IsDefault)
{
return ImmutableSortedSet<int>.Empty;
}
var constraintsBuilder =
new MethodContextReuseConstraints.Builder(methodContextReuseConstraints, ilOffset, areRangesEndInclusive: false);
var scopesBuilder = ArrayBuilder<int>.GetInstance();
int i = 0;
foreach (var record in this.HoistedLocalScopeRecords)
{
constraintsBuilder.AddRange(record.StartOffset, record.StartOffset + record.Length);
var delta = ilOffset - record.StartOffset;
if (0 <= delta && delta < record.Length)
{
scopesBuilder.Add(i);
}
i++;
}
methodContextReuseConstraints = constraintsBuilder.Build();
var result = scopesBuilder.ToImmutableSortedSet();
scopesBuilder.Free();
return result;
}
} }
} }
...@@ -6,29 +6,48 @@ namespace Microsoft.CodeAnalysis.ExpressionEvaluator ...@@ -6,29 +6,48 @@ namespace Microsoft.CodeAnalysis.ExpressionEvaluator
{ {
internal static class PdbHelpers internal static class PdbHelpers
{ {
/// <remarks>
/// Test helper.
/// </remarks>
internal static void GetAllScopes(this ISymUnmanagedMethod method, ArrayBuilder<ISymUnmanagedScope> builder) internal static void GetAllScopes(this ISymUnmanagedMethod method, ArrayBuilder<ISymUnmanagedScope> builder)
{ {
GetAllScopes(method, builder, offset: -1, isScopeEndInclusive: false); var unused = ArrayBuilder<ISymUnmanagedScope>.GetInstance();
GetAllScopes(method, builder, unused, offset: -1, isScopeEndInclusive: false);
unused.Free();
} }
internal static void GetAllScopes(this ISymUnmanagedMethod method, ArrayBuilder<ISymUnmanagedScope> builder, int offset, bool isScopeEndInclusive) internal static void GetAllScopes(
this ISymUnmanagedMethod method,
ArrayBuilder<ISymUnmanagedScope> allScopes,
ArrayBuilder<ISymUnmanagedScope> containingScopes,
int offset,
bool isScopeEndInclusive)
{ {
ISymUnmanagedScope scope = method.GetRootScope(); GetAllScopes(method.GetRootScope(), allScopes, containingScopes, offset, isScopeEndInclusive);
GetAllScopes(scope, builder, offset, isScopeEndInclusive);
} }
private static void GetAllScopes(ISymUnmanagedScope scope, ArrayBuilder<ISymUnmanagedScope> builder, int offset, bool isScopeEndInclusive) private static void GetAllScopes(
ISymUnmanagedScope root,
ArrayBuilder<ISymUnmanagedScope> allScopes,
ArrayBuilder<ISymUnmanagedScope> containingScopes,
int offset,
bool isScopeEndInclusive)
{ {
builder.Add(scope); var stack = ArrayBuilder<ISymUnmanagedScope>.GetInstance();
foreach (var nested in scope.GetScopes()) stack.Push(root);
while (stack.Any())
{ {
if ((offset < 0) || nested.IsInScope(offset, isScopeEndInclusive)) var scope = stack.Pop();
allScopes.Add(scope);
if (offset >= 0 && scope.IsInScope(offset, isScopeEndInclusive))
{
containingScopes.Add(scope);
}
foreach (var nested in scope.GetScopes())
{ {
GetAllScopes(nested, builder, offset, isScopeEndInclusive); stack.Push(nested);
if (offset >= 0)
{
return;
}
} }
} }
} }
......
...@@ -29,7 +29,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator ...@@ -29,7 +29,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
Friend Const IsLocalScopeEndInclusive = True Friend Const IsLocalScopeEndInclusive = True
Friend ReadOnly MetadataBlocks As ImmutableArray(Of MetadataBlock) Friend ReadOnly MetadataBlocks As ImmutableArray(Of MetadataBlock)
Friend ReadOnly MethodScope As MethodScope Friend ReadOnly MethodContextReuseConstraints As MethodContextReuseConstraints?
Friend ReadOnly Compilation As VisualBasicCompilation Friend ReadOnly Compilation As VisualBasicCompilation
Private ReadOnly _metadataDecoder As MetadataDecoder Private ReadOnly _metadataDecoder As MetadataDecoder
...@@ -40,7 +40,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator ...@@ -40,7 +40,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
Private Sub New( Private Sub New(
metadataBlocks As ImmutableArray(Of MetadataBlock), metadataBlocks As ImmutableArray(Of MetadataBlock),
methodScope As MethodScope, methodContextReuseConstraints As MethodContextReuseConstraints?,
compilation As VisualBasicCompilation, compilation As VisualBasicCompilation,
metadataDecoder As MetadataDecoder, metadataDecoder As MetadataDecoder,
currentFrame As MethodSymbol, currentFrame As MethodSymbol,
...@@ -49,7 +49,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator ...@@ -49,7 +49,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
methodDebugInfo As MethodDebugInfo) methodDebugInfo As MethodDebugInfo)
Me.MetadataBlocks = metadataBlocks Me.MetadataBlocks = metadataBlocks
Me.MethodScope = methodScope Me.MethodContextReuseConstraints = methodContextReuseConstraints
Me.Compilation = compilation Me.Compilation = compilation
_metadataDecoder = metadataDecoder _metadataDecoder = metadataDecoder
_currentFrame = currentFrame _currentFrame = currentFrame
...@@ -123,17 +123,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator ...@@ -123,17 +123,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
Debug.Assert(MetadataTokens.Handle(methodToken).Kind = HandleKind.MethodDefinition) Debug.Assert(MetadataTokens.Handle(methodToken).Kind = HandleKind.MethodDefinition)
Dim typedSymReader = DirectCast(symReader, ISymUnmanagedReader)
Dim scopes = ArrayBuilder(Of ISymUnmanagedScope).GetInstance()
typedSymReader.GetScopes(methodToken, methodVersion, ilOffset, IsLocalScopeEndInclusive, scopes)
Dim scope = scopes.GetMethodScope(methodToken, methodVersion)
' Re-use the previous compilation if possible. ' Re-use the previous compilation if possible.
Dim compilation As VisualBasicCompilation Dim compilation As VisualBasicCompilation
If metadataBlocks.HaveNotChanged(previous) Then If metadataBlocks.HaveNotChanged(previous) Then
' Re-use entire context if method scope has not changed. ' Re-use entire context if method scope has not changed.
Dim previousContext = previous.EvaluationContext Dim previousContext = previous.EvaluationContext
If (scope IsNot Nothing) AndAlso (previousContext IsNot Nothing) AndAlso scope.Equals(previousContext.MethodScope) Then If previousContext IsNot Nothing AndAlso
previousContext.MethodContextReuseConstraints.HasValue AndAlso
previousContext.MethodContextReuseConstraints.GetValueOrDefault().AreSatisfied(methodToken, methodVersion, ilOffset) Then
Return previousContext Return previousContext
End If End If
compilation = previous.Compilation compilation = previous.Compilation
...@@ -141,18 +138,25 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator ...@@ -141,18 +138,25 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
compilation = metadataBlocks.ToCompilation() compilation = metadataBlocks.ToCompilation()
End If End If
Dim typedSymReader = DirectCast(symReader, ISymUnmanagedReader)
Dim allScopes = ArrayBuilder(Of ISymUnmanagedScope).GetInstance()
Dim containingScopes = ArrayBuilder(Of ISymUnmanagedScope).GetInstance()
typedSymReader.GetScopes(methodToken, methodVersion, ilOffset, IsLocalScopeEndInclusive, allScopes, containingScopes)
Dim reuseConstraints = allScopes.GetReuseConstraints(methodToken, methodVersion, ilOffset, IsLocalScopeEndInclusive)
allScopes.Free()
Dim methodHandle = CType(MetadataTokens.Handle(methodToken), MethodDefinitionHandle) Dim methodHandle = CType(MetadataTokens.Handle(methodToken), MethodDefinitionHandle)
Dim currentFrame = compilation.GetMethod(moduleVersionId, methodHandle) Dim currentFrame = compilation.GetMethod(moduleVersionId, methodHandle)
Debug.Assert(currentFrame IsNot Nothing) Debug.Assert(currentFrame IsNot Nothing)
Dim metadataDecoder = New MetadataDecoder(DirectCast(currentFrame.ContainingModule, PEModuleSymbol), currentFrame) Dim metadataDecoder = New MetadataDecoder(DirectCast(currentFrame.ContainingModule, PEModuleSymbol), currentFrame)
Dim hoistedLocalFieldNames As ImmutableHashSet(Of String) = Nothing Dim hoistedLocalFieldNames As ImmutableHashSet(Of String) = Nothing
Dim localNames = GetLocalNames(scopes, hoistedLocalFieldNames) Dim localNames = GetLocalNames(containingScopes, hoistedLocalFieldNames)
Dim localInfo = metadataDecoder.GetLocalInfo(localSignatureToken) Dim localInfo = metadataDecoder.GetLocalInfo(localSignatureToken)
Dim localBuilder = ArrayBuilder(Of LocalSymbol).GetInstance() Dim localBuilder = ArrayBuilder(Of LocalSymbol).GetInstance()
GetLocals(localBuilder, currentFrame, localNames, localInfo) GetLocals(localBuilder, currentFrame, localNames, localInfo)
GetStaticLocals(localBuilder, currentFrame, methodHandle, metadataDecoder) GetStaticLocals(localBuilder, currentFrame, methodHandle, metadataDecoder)
GetConstants(localBuilder, currentFrame, scopes.GetConstantSignatures(), metadataDecoder) GetConstants(localBuilder, currentFrame, containingScopes.GetConstantSignatures(), metadataDecoder)
scopes.Free() containingScopes.Free()
Dim locals = localBuilder.ToImmutableAndFree() Dim locals = localBuilder.ToImmutableAndFree()
Dim methodDebugInfo As MethodDebugInfo Dim methodDebugInfo As MethodDebugInfo
...@@ -167,7 +171,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator ...@@ -167,7 +171,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
Return New EvaluationContext( Return New EvaluationContext(
metadataBlocks, metadataBlocks,
scope, reuseConstraints,
compilation, compilation,
metadataDecoder, metadataDecoder,
currentFrame, currentFrame,
...@@ -283,7 +287,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator ...@@ -283,7 +287,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
Dim importRecordGroups = ImmutableArray.Create(projectLevelImportRecords, fileLevelImportRecords) Dim importRecordGroups = ImmutableArray.Create(projectLevelImportRecords, fileLevelImportRecords)
Return New MethodDebugInfo(importRecordGroups, ImmutableArray(Of ExternAliasRecord).Empty, defaultNamespaceName:="") Return New MethodDebugInfo(
hoistedLocalScopeRecords:=ImmutableArray(Of HoistedLocalScopeRecord).Empty,
importRecordGroups:=importRecordGroups,
defaultNamespaceName:="",
externAliasRecords:=ImmutableArray(Of ExternAliasRecord).Empty,
dynamicLocalMap:=ImmutableDictionary(Of Integer, ImmutableArray(Of Boolean)).Empty,
dynamicLocalConstantMap:=ImmutableDictionary(Of String, ImmutableArray(Of Boolean)).Empty)
End Function End Function
Friend Function CreateCompilationContext(syntax As ExecutableStatementSyntax) As CompilationContext Friend Function CreateCompilationContext(syntax As ExecutableStatementSyntax) As CompilationContext
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
Imports System.Collections.Immutable Imports System.Collections.Immutable
Imports System.Runtime.CompilerServices Imports System.Runtime.CompilerServices
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.ExpressionEvaluator Imports Microsoft.CodeAnalysis.ExpressionEvaluator
Imports Microsoft.VisualStudio.SymReaderInterop Imports Microsoft.VisualStudio.SymReaderInterop
Imports ImportScope = Microsoft.VisualStudio.SymReaderInterop.ImportScope Imports ImportScope = Microsoft.VisualStudio.SymReaderInterop.ImportScope
...@@ -74,7 +73,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator ...@@ -74,7 +73,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
projectLevelImportRecords.ToImmutableAndFree(), projectLevelImportRecords.ToImmutableAndFree(),
fileLevelImportRecords.ToImmutableAndFree()) fileLevelImportRecords.ToImmutableAndFree())
Return New MethodDebugInfo(importRecordGroups, ImmutableArray(Of ExternAliasRecord).Empty, defaultNamespaceName) ' TODO (acasey): portable format overload (GH #702)
' Somehow construct hoistedLocalScopeRecords.
Dim hoistedLocalScopeRecords = ImmutableArray(Of HoistedLocalScopeRecord).Empty
Return New MethodDebugInfo(
hoistedLocalScopeRecords,
importRecordGroups,
defaultNamespaceName:=defaultNamespaceName,
externAliasRecords:=ImmutableArray(Of ExternAliasRecord).Empty,
dynamicLocalMap:=ImmutableDictionary(Of Integer, ImmutableArray(Of Boolean)).Empty,
dynamicLocalConstantMap:=ImmutableDictionary(Of String, ImmutableArray(Of Boolean)).Empty)
End Function End Function
' TODO (acasey): portable format overload (GH #702) ' TODO (acasey): portable format overload (GH #702)
......
...@@ -258,14 +258,14 @@ End Class" ...@@ -258,14 +258,14 @@ End Class"
Assert.Null(previous) Assert.Null(previous)
previous = new VisualBasicMetadataContext(context) previous = new VisualBasicMetadataContext(context)
' At end of outer scope. ' At end of outer scope - not reused because of the nested scope.
context = EvaluationContext.CreateMethodContext(previous, methodBlocks, MakeDummyLazyAssemblyReaders(), symReader, moduleVersionId, methodToken, methodVersion, endOffset, localSignatureToken) context = EvaluationContext.CreateMethodContext(previous, methodBlocks, MakeDummyLazyAssemblyReaders(), symReader, moduleVersionId, methodToken, methodVersion, endOffset, localSignatureToken)
Assert.Equal(context, previous.EvaluationContext) Assert.NotEqual(context, previous.EvaluationContext) ' Not required, just documentary.
' At type context. ' At type context.
context = EvaluationContext.CreateTypeContext(previous, methodBlocks, moduleVersionId, typeToken) context = EvaluationContext.CreateTypeContext(previous, methodBlocks, moduleVersionId, typeToken)
Assert.NotEqual(context, previous.EvaluationContext) Assert.NotEqual(context, previous.EvaluationContext)
Assert.NotEqual(context.MethodScope, previous.EvaluationContext.MethodScope) Assert.Null(context.MethodContextReuseConstraints)
Assert.Equal(context.Compilation, previous.Compilation) Assert.Equal(context.Compilation, previous.Compilation)
' Step through entire method. ' Step through entire method.
...@@ -273,14 +273,19 @@ End Class" ...@@ -273,14 +273,19 @@ End Class"
previous = new VisualBasicMetadataContext(context) previous = new VisualBasicMetadataContext(context)
For offset = startOffset To endOffset - 1 For offset = startOffset To endOffset - 1
Dim scope = scopes.GetInnermostScope(offset) Dim scope = scopes.GetInnermostScope(offset)
Dim constraints = previous.EvaluationContext.MethodContextReuseConstraints
If constraints.HasValue Then
Assert.Equal(scope Is previousScope, constraints.GetValueOrDefault().AreSatisfied(methodToken, methodVersion, offset))
End If
context = EvaluationContext.CreateMethodContext(previous, methodBlocks, MakeDummyLazyAssemblyReaders(), symReader, moduleVersionId, methodToken, methodVersion, offset, localSignatureToken) context = EvaluationContext.CreateMethodContext(previous, methodBlocks, MakeDummyLazyAssemblyReaders(), symReader, moduleVersionId, methodToken, methodVersion, offset, localSignatureToken)
If scope Is previousScope Then If scope Is previousScope Then
Assert.Equal(context, previous.EvaluationContext) Assert.Equal(context, previous.EvaluationContext)
Else Else
' Different scope. Should reuse compilation. ' Different scope. Should reuse compilation.
Assert.NotEqual(context, previous.EvaluationContext) Assert.NotEqual(context, previous.EvaluationContext)
If previous IsNot Nothing Then If previous.EvaluationContext IsNot Nothing Then
Assert.NotEqual(context.MethodScope, previous.EvaluationContext.MethodScope) Assert.NotEqual(context.MethodContextReuseConstraints, previous.EvaluationContext.MethodContextReuseConstraints)
Assert.Equal(context.Compilation, previous.Compilation) Assert.Equal(context.Compilation, previous.Compilation)
End If End If
End If End If
...@@ -300,9 +305,9 @@ End Class" ...@@ -300,9 +305,9 @@ End Class"
GetContextState(runtime, "C.F", methodBlocks, moduleVersionId, symReader, methodToken, localSignatureToken) GetContextState(runtime, "C.F", methodBlocks, moduleVersionId, symReader, methodToken, localSignatureToken)
' Different references. No reuse. ' Different references. No reuse.
context = EvaluationContext.CreateMethodContext(previous, methodBlocks, MakeDummyLazyAssemblyReaders(), symReader, moduleVersionId, methodToken, methodVersion, endOffset, localSignatureToken) context = EvaluationContext.CreateMethodContext(previous, methodBlocks, MakeDummyLazyAssemblyReaders(), symReader, moduleVersionId, methodToken, methodVersion, endOffset - 1, localSignatureToken)
Assert.NotEqual(context, previous.EvaluationContext) Assert.NotEqual(context, previous.EvaluationContext)
Assert.Equal(context.MethodScope, previous.EvaluationContext.MethodScope) Assert.True(previous.EvaluationContext.MethodContextReuseConstraints.Value.AreSatisfied(methodToken, methodVersion, endOffset - 1))
Assert.NotEqual(context.Compilation, previous.Compilation) Assert.NotEqual(context.Compilation, previous.Compilation)
previous = new VisualBasicMetadataContext(context) previous = new VisualBasicMetadataContext(context)
...@@ -310,7 +315,7 @@ End Class" ...@@ -310,7 +315,7 @@ End Class"
GetContextState(runtime, "C.G", methodBlocks, moduleVersionId, symReader, methodToken, localSignatureToken) GetContextState(runtime, "C.G", methodBlocks, moduleVersionId, symReader, methodToken, localSignatureToken)
context = EvaluationContext.CreateMethodContext(previous, methodBlocks, MakeDummyLazyAssemblyReaders(), symReader, moduleVersionId, methodToken, methodVersion, ilOffset:=0, localSignatureToken:=localSignatureToken) context = EvaluationContext.CreateMethodContext(previous, methodBlocks, MakeDummyLazyAssemblyReaders(), symReader, moduleVersionId, methodToken, methodVersion, ilOffset:=0, localSignatureToken:=localSignatureToken)
Assert.NotEqual(context, previous.EvaluationContext) Assert.NotEqual(context, previous.EvaluationContext)
Assert.NotEqual(context.MethodScope, previous.EvaluationContext.MethodScope) Assert.False(previous.EvaluationContext.MethodContextReuseConstraints.Value.AreSatisfied(methodToken, methodVersion, 0))
Assert.Equal(context.Compilation, previous.Compilation) Assert.Equal(context.Compilation, previous.Compilation)
' No EvaluationContext. Should reuse Compilation ' No EvaluationContext. Should reuse Compilation
......
...@@ -1366,7 +1366,7 @@ End Class ...@@ -1366,7 +1366,7 @@ End Class
<WorkItem(1134746, "DevDiv")> <WorkItem(1134746, "DevDiv")>
<Fact> <Fact>
Public Sub Caching() Public Sub CacheInvalidation()
Const source = " Const source = "
Imports System.Collections.Generic Imports System.Collections.Generic
......
...@@ -464,34 +464,6 @@ public static ImmutableArray<string> GetVisualBasicImportStrings(this ISymUnmana ...@@ -464,34 +464,6 @@ public static ImmutableArray<string> GetVisualBasicImportStrings(this ISymUnmana
return importStrings; return importStrings;
} }
// TODO (acasey): caller should depend on abstraction (GH #702)
public static ImmutableSortedSet<int> GetCSharpInScopeHoistedLocalIndices(byte[] customDebugInfo, int methodToken, int methodVersion, int ilOffset)
{
var record = TryGetCustomDebugInfoRecord(customDebugInfo, CustomDebugInfoKind.StateMachineHoistedLocalScopes);
if (record.IsDefault)
{
return ImmutableSortedSet<int>.Empty;
}
var scopes = DecodeStateMachineHoistedLocalScopesRecord(record);
ArrayBuilder<int> builder = ArrayBuilder<int>.GetInstance();
for (int i = 0; i < scopes.Length; i++)
{
StateMachineHoistedLocalScope scope = scopes[i];
// NB: scopes are end-inclusive.
if (ilOffset >= scope.StartOffset && ilOffset <= scope.EndOffset)
{
builder.Add(i);
}
}
ImmutableSortedSet<int> result = builder.ToImmutableSortedSet();
builder.Free();
return result;
}
// TODO (acasey): caller should depend on abstraction (GH #702) // TODO (acasey): caller should depend on abstraction (GH #702)
/// <exception cref="InvalidOperationException">Bad data.</exception> /// <exception cref="InvalidOperationException">Bad data.</exception>
public static void GetCSharpDynamicLocalInfo( public static void GetCSharpDynamicLocalInfo(
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册