diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs index 4d278cdcb1cdd8d696664179830e7c23cbbfa6d1..c197a710f6c3617720418bc84d684212d47f1b80 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs @@ -26,7 +26,7 @@ internal sealed class EvaluationContext : EvaluationContextBase internal const bool IsLocalScopeEndInclusive = false; internal readonly ImmutableArray MetadataBlocks; - internal readonly MethodScope MethodScope; + internal readonly MethodContextReuseConstraints? MethodContextReuseConstraints; internal readonly CSharpCompilation Compilation; private readonly MetadataDecoder _metadataDecoder; @@ -37,7 +37,7 @@ internal sealed class EvaluationContext : EvaluationContextBase private EvaluationContext( ImmutableArray metadataBlocks, - MethodScope methodScope, + MethodContextReuseConstraints? methodContextReuseConstraints, CSharpCompilation compilation, MetadataDecoder metadataDecoder, MethodSymbol currentFrame, @@ -48,7 +48,7 @@ internal sealed class EvaluationContext : EvaluationContextBase Debug.Assert(inScopeHoistedLocalIndices != null); this.MetadataBlocks = metadataBlocks; - this.MethodScope = methodScope; + this.MethodContextReuseConstraints = methodContextReuseConstraints; this.Compilation = compilation; _metadataDecoder = metadataDecoder; _currentFrame = currentFrame; @@ -121,18 +121,15 @@ internal sealed class EvaluationContext : EvaluationContextBase { Debug.Assert(MetadataTokens.Handle(methodToken).Kind == HandleKind.MethodDefinition); - var typedSymReader = (ISymUnmanagedReader)symReader; - var scopes = ArrayBuilder.GetInstance(); - typedSymReader.GetScopes(methodToken, methodVersion, ilOffset, IsLocalScopeEndInclusive, scopes); - var scope = scopes.GetMethodScope(methodToken, methodVersion); - // Re-use the previous compilation if possible. CSharpCompilation compilation; if (metadataBlocks.HaveNotChanged(previous)) { // Re-use entire context if method scope has not changed. 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; } @@ -143,10 +140,15 @@ internal sealed class EvaluationContext : EvaluationContextBase compilation = metadataBlocks.ToCompilation(); } - var localNames = scopes.GetLocalNames(); + var typedSymReader = (ISymUnmanagedReader)symReader; + var allScopes = ArrayBuilder.GetInstance(); + var containingScopes = ArrayBuilder.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>.Empty; - var dynamicLocalConstantMap = ImmutableDictionary>.Empty; var inScopeHoistedLocalIndices = ImmutableSortedSet.Empty; var methodDebugInfo = default(MethodDebugInfo); @@ -154,26 +156,9 @@ internal sealed class EvaluationContext : EvaluationContextBase { 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) - methodDebugInfo = typedSymReader.GetMethodDebugInfo(methodToken, methodVersion); + methodDebugInfo = typedSymReader.GetMethodDebugInfo(methodToken, methodVersion, localNames.FirstOrDefault()); + inScopeHoistedLocalIndices = methodDebugInfo.GetInScopeHoistedLocalIndices(ilOffset, ref methodContextReuseConstraints); } catch (InvalidOperationException) { @@ -188,15 +173,15 @@ internal sealed class EvaluationContext : EvaluationContextBase var localInfo = metadataDecoder.GetLocalInfo(localSignatureToken); var localBuilder = ArrayBuilder.GetInstance(); var sourceAssembly = compilation.SourceAssembly; - GetLocals(localBuilder, currentFrame, localNames, localInfo, dynamicLocalMap, sourceAssembly); - GetConstants(localBuilder, currentFrame, scopes.GetConstantSignatures(), metadataDecoder, dynamicLocalConstantMap, sourceAssembly); - scopes.Free(); + GetLocals(localBuilder, currentFrame, localNames, localInfo, methodDebugInfo.DynamicLocalMap, sourceAssembly); + GetConstants(localBuilder, currentFrame, containingScopes.GetConstantSignatures(), metadataDecoder, methodDebugInfo.DynamicLocalConstantMap, sourceAssembly); + containingScopes.Free(); var locals = localBuilder.ToImmutableAndFree(); return new EvaluationContext( metadataBlocks, - scope, + methodContextReuseConstraints, compilation, metadataDecoder, currentFrame, @@ -478,7 +463,7 @@ internal CompilationContext CreateCompilationContext(CSharpSyntaxNode syntax) } ImmutableArray dynamicFlags; - if (dynamicLocalMap.TryGetValue(i, out dynamicFlags)) + if (dynamicLocalMap != null && dynamicLocalMap.TryGetValue(i, out dynamicFlags)) { type = DynamicTypeDecoder.TransformTypeWithoutCustomModifierFlags( type, @@ -510,7 +495,7 @@ internal CompilationContext CreateCompilationContext(CSharpSyntaxNode syntax) var type = info.Type; ImmutableArray dynamicFlags; - if (dynamicLocalConstantMap.TryGetValue(constant.Name, out dynamicFlags)) + if (dynamicLocalConstantMap != null && dynamicLocalConstantMap.TryGetValue(constant.Name, out dynamicFlags)) { type = DynamicTypeDecoder.TransformTypeWithoutCustomModifierFlags( type, diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SymUnmanagedReaderExtensions.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SymUnmanagedReaderExtensions.cs index 2fd13305aa733dab4b049aac576ed02dc3c464d3..ae84620f5c3819f3002bbce278675e0c0a03c264 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SymUnmanagedReaderExtensions.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SymUnmanagedReaderExtensions.cs @@ -14,7 +14,8 @@ internal static class SymUnmanagedReaderExtensions public static MethodDebugInfo GetMethodDebugInfo( this ISymUnmanagedReader reader, int methodToken, - int methodVersion) + int methodVersion, + string firstLocalName) { ImmutableArray externAliasStrings; var importStringGroups = reader.GetCSharpGroupedImportStrings(methodToken, methodVersion, out externAliasStrings); @@ -72,9 +73,37 @@ internal static class SymUnmanagedReaderExtensions externAliasRecordBuilder.Add(new NativeExternAliasRecord(alias, targetIdentity)); } + var hoistedLocalScopeRecords = ImmutableArray.Empty; + var dynamicLocalMap = ImmutableDictionary>.Empty; + var dynamicLocalConstantMap = ImmutableDictionary>.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( + hoistedLocalScopeRecords, importRecordGroupBuilder.ToImmutableAndFree(), externAliasRecordBuilder.ToImmutableAndFree(), + dynamicLocalMap, + dynamicLocalConstantMap, defaultNamespaceName: ""); // Unused in C#. } diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/CSharpExpressionCompilerTest.csproj b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/CSharpExpressionCompilerTest.csproj index b2e819c5214c73afb949aaa70a08af18d1ec7ace..f9c8466ac9cb9f83917d1d9c2f293fb58831c47d 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/CSharpExpressionCompilerTest.csproj +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/CSharpExpressionCompilerTest.csproj @@ -106,6 +106,7 @@ + diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs index 7b1a89bcf722ef19635dd04b65024e8a9e1236da..a72a5afeb1d93280be2a5fe96432a91007226849 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs @@ -355,14 +355,14 @@ static void G() Assert.Null(previous); 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); - Assert.Equal(context, previous.EvaluationContext); + Assert.NotEqual(context, previous.EvaluationContext); // Not required, just documentary. // At type context. context = EvaluationContext.CreateTypeContext(previous, methodBlocks, moduleVersionId, typeToken); Assert.NotEqual(context, previous.EvaluationContext); - Assert.NotEqual(context.MethodScope, previous.EvaluationContext.MethodScope); + Assert.Null(context.MethodContextReuseConstraints); Assert.Equal(context.Compilation, previous.Compilation); // Step through entire method. @@ -371,6 +371,12 @@ static void G() for (int offset = startOffset; offset <= endOffset; 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); if (scope == previousScope) { @@ -380,9 +386,9 @@ static void G() { // Different scope. Should reuse compilation. 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); } } @@ -399,7 +405,7 @@ static void G() // Different references. No reuse. context = EvaluationContext.CreateMethodContext(previous, methodBlocks, symReader, moduleVersionId, methodToken, methodVersion, endOffset, localSignatureToken); 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); previous = new CSharpMetadataContext(context); @@ -407,7 +413,7 @@ static void G() 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); 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); // No EvaluationContext. Should reuse Compilation diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/HoistedStateMachineLocalTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/HoistedStateMachineLocalTests.cs index 62474bbef86d4103fed880935a3c513e9cc2f28e..5e4e71b7a2813edea079f6d3d0f5b6bbe395313e 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/HoistedStateMachineLocalTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/HoistedStateMachineLocalTests.cs @@ -1291,7 +1291,7 @@ .maxstack 1 [WorkItem(1134746, "DevDiv")] [Fact] - public void Caching() + public void CacheInvalidation() { var source = @" using System.Collections.Generic; diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/MethodContextReuseConstraintsTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/MethodContextReuseConstraintsTests.cs new file mode 100644 index 0000000000000000000000000000000000000000..b657062365b247ee66b4a0a6d3c93adae7fb03d3 --- /dev/null +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/MethodContextReuseConstraintsTests.cs @@ -0,0 +1,102 @@ +// 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 diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/ExpressionCompiler.csproj b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/ExpressionCompiler.csproj index 14264596282d741bd58a2bead2574346a38f0bd4..d43054062bdba66d1fd2ff5def6dbca35dae1cde 100644 --- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/ExpressionCompiler.csproj +++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/ExpressionCompiler.csproj @@ -39,6 +39,7 @@ + @@ -59,7 +60,7 @@ - + diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/MetadataUtilities.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/MetadataUtilities.cs index 5e29b8b0007df842e72b60997ce6cc113a3299be..34e0538c6e4d0d32ba04efa2da8d4f3c76cdc326 100644 --- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/MetadataUtilities.cs +++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/MetadataUtilities.cs @@ -246,7 +246,14 @@ internal static bool IsWindowsAssemblyIdentity(this AssemblyIdentity assemblyIde /// Get the set of nested scopes containing the /// IL offset from outermost scope to innermost. /// - internal static void GetScopes(this ISymUnmanagedReader symReader, int methodToken, int methodVersion, int ilOffset, bool isScopeEndInclusive, ArrayBuilder scopes) + internal static void GetScopes( + this ISymUnmanagedReader symReader, + int methodToken, + int methodVersion, + int ilOffset, + bool isScopeEndInclusive, + ArrayBuilder allScopes, + ArrayBuilder containingScopes) { if (symReader == null) { @@ -259,17 +266,17 @@ internal static void GetScopes(this ISymUnmanagedReader symReader, int methodTok return; } - symMethod.GetAllScopes(scopes, ilOffset, isScopeEndInclusive); + symMethod.GetAllScopes(allScopes, containingScopes, ilOffset, isScopeEndInclusive); } - internal static MethodScope GetMethodScope(this ArrayBuilder scopes, int methodToken, int methodVersion) + internal static MethodContextReuseConstraints GetReuseConstraints(this ArrayBuilder 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 new MethodScope(methodToken, methodVersion, scope.GetStartOffset(), scope.GetEndOffset()); + return builder.Build(); } internal static ImmutableArray GetLocalNames(this ArrayBuilder scopes) diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/MethodContextReuseConstraints.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/MethodContextReuseConstraints.cs new file mode 100644 index 0000000000000000000000000000000000000000..dd211fbdd3eca8017900f27be970ec56ea482635 --- /dev/null +++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/MethodContextReuseConstraints.cs @@ -0,0 +1,121 @@ +// 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); + } + } + } +} diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/MethodScope.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/MethodScope.cs deleted file mode 100644 index 28e1423e46b339ba55430e34f29e85806c932043..0000000000000000000000000000000000000000 --- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/MethodScope.cs +++ /dev/null @@ -1,51 +0,0 @@ -// 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 - { - 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))); - } - } -} diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/PDB/HoisedLocalScopeRecord.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/PDB/HoisedLocalScopeRecord.cs new file mode 100644 index 0000000000000000000000000000000000000000..71633ab734e0c6442456f3572806af2194e5d5af --- /dev/null +++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/PDB/HoisedLocalScopeRecord.cs @@ -0,0 +1,29 @@ +// 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); + } + } +} diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/PDB/MethodDebugInfo.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/PDB/MethodDebugInfo.cs index ee48e8a8bd41c3947b23f29dea3d9b26d9c5737e..a8480cdab7aa07bcef49aa0a6d8f9fadd31aa0f6 100644 --- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/PDB/MethodDebugInfo.cs +++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/PDB/MethodDebugInfo.cs @@ -7,22 +7,68 @@ namespace Microsoft.CodeAnalysis.ExpressionEvaluator { internal struct MethodDebugInfo { + public readonly ImmutableArray HoistedLocalScopeRecords; public readonly ImmutableArray> ImportRecordGroups; - public readonly ImmutableArray ExternAliasRecords; - public readonly string DefaultNamespaceName; + + public readonly ImmutableArray ExternAliasRecords; // C# only. + public readonly ImmutableDictionary> DynamicLocalMap; // C# only. + public readonly ImmutableDictionary> DynamicLocalConstantMap; // C# only. + + public readonly string DefaultNamespaceName; // VB only. public MethodDebugInfo( + ImmutableArray hoistedLocalScopeRecords, ImmutableArray> importRecordGroups, ImmutableArray externAliasRecords, + ImmutableDictionary> dynamicLocalMap, + ImmutableDictionary> dynamicLocalConstantMap, string defaultNamespaceName) { Debug.Assert(!importRecordGroups.IsDefault); Debug.Assert(!externAliasRecords.IsDefault); Debug.Assert(defaultNamespaceName != null); + Debug.Assert(!hoistedLocalScopeRecords.IsDefault); + HoistedLocalScopeRecords = hoistedLocalScopeRecords; ImportRecordGroups = importRecordGroups; + ExternAliasRecords = externAliasRecords; + DynamicLocalMap = dynamicLocalMap; + DynamicLocalConstantMap = dynamicLocalConstantMap; + DefaultNamespaceName = defaultNamespaceName; } + + public ImmutableSortedSet GetInScopeHoistedLocalIndices(int ilOffset, ref MethodContextReuseConstraints methodContextReuseConstraints) + { + if (this.HoistedLocalScopeRecords.IsDefault) + { + return ImmutableSortedSet.Empty; + } + + var constraintsBuilder = + new MethodContextReuseConstraints.Builder(methodContextReuseConstraints, ilOffset, areRangesEndInclusive: false); + + var scopesBuilder = ArrayBuilder.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; + } } } diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/PDB/PdbHelpers.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/PDB/PdbHelpers.cs index 657413f9ab874787beba0e46d46ede4bba382c5a..be68f56de34abfdb11d62937a91df13ae75c1437 100644 --- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/PDB/PdbHelpers.cs +++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/PDB/PdbHelpers.cs @@ -6,29 +6,48 @@ namespace Microsoft.CodeAnalysis.ExpressionEvaluator { internal static class PdbHelpers { + /// + /// Test helper. + /// internal static void GetAllScopes(this ISymUnmanagedMethod method, ArrayBuilder builder) { - GetAllScopes(method, builder, offset: -1, isScopeEndInclusive: false); + var unused = ArrayBuilder.GetInstance(); + GetAllScopes(method, builder, unused, offset: -1, isScopeEndInclusive: false); + unused.Free(); } - internal static void GetAllScopes(this ISymUnmanagedMethod method, ArrayBuilder builder, int offset, bool isScopeEndInclusive) + internal static void GetAllScopes( + this ISymUnmanagedMethod method, + ArrayBuilder allScopes, + ArrayBuilder containingScopes, + int offset, + bool isScopeEndInclusive) { - ISymUnmanagedScope scope = method.GetRootScope(); - GetAllScopes(scope, builder, offset, isScopeEndInclusive); + GetAllScopes(method.GetRootScope(), allScopes, containingScopes, offset, isScopeEndInclusive); } - private static void GetAllScopes(ISymUnmanagedScope scope, ArrayBuilder builder, int offset, bool isScopeEndInclusive) + private static void GetAllScopes( + ISymUnmanagedScope root, + ArrayBuilder allScopes, + ArrayBuilder containingScopes, + int offset, + bool isScopeEndInclusive) { - builder.Add(scope); - foreach (var nested in scope.GetScopes()) + var stack = ArrayBuilder.GetInstance(); + 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); - if (offset >= 0) - { - return; - } + stack.Push(nested); } } } diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb index 7ba5f7bc991862192b97af981b7b672f68abd245..df752ac8b4ed7aed612efc772b9322fd5d915c45 100644 --- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb +++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb @@ -29,7 +29,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator Friend Const IsLocalScopeEndInclusive = True Friend ReadOnly MetadataBlocks As ImmutableArray(Of MetadataBlock) - Friend ReadOnly MethodScope As MethodScope + Friend ReadOnly MethodContextReuseConstraints As MethodContextReuseConstraints? Friend ReadOnly Compilation As VisualBasicCompilation Private ReadOnly _metadataDecoder As MetadataDecoder @@ -40,7 +40,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator Private Sub New( metadataBlocks As ImmutableArray(Of MetadataBlock), - methodScope As MethodScope, + methodContextReuseConstraints As MethodContextReuseConstraints?, compilation As VisualBasicCompilation, metadataDecoder As MetadataDecoder, currentFrame As MethodSymbol, @@ -49,7 +49,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator methodDebugInfo As MethodDebugInfo) Me.MetadataBlocks = metadataBlocks - Me.MethodScope = methodScope + Me.MethodContextReuseConstraints = methodContextReuseConstraints Me.Compilation = compilation _metadataDecoder = metadataDecoder _currentFrame = currentFrame @@ -123,17 +123,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator 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. Dim compilation As VisualBasicCompilation If metadataBlocks.HaveNotChanged(previous) Then ' Re-use entire context if method scope has not changed. 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 End If compilation = previous.Compilation @@ -141,18 +138,25 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator compilation = metadataBlocks.ToCompilation() 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 currentFrame = compilation.GetMethod(moduleVersionId, methodHandle) Debug.Assert(currentFrame IsNot Nothing) Dim metadataDecoder = New MetadataDecoder(DirectCast(currentFrame.ContainingModule, PEModuleSymbol), currentFrame) Dim hoistedLocalFieldNames As ImmutableHashSet(Of String) = Nothing - Dim localNames = GetLocalNames(scopes, hoistedLocalFieldNames) + Dim localNames = GetLocalNames(containingScopes, hoistedLocalFieldNames) Dim localInfo = metadataDecoder.GetLocalInfo(localSignatureToken) Dim localBuilder = ArrayBuilder(Of LocalSymbol).GetInstance() GetLocals(localBuilder, currentFrame, localNames, localInfo) GetStaticLocals(localBuilder, currentFrame, methodHandle, metadataDecoder) - GetConstants(localBuilder, currentFrame, scopes.GetConstantSignatures(), metadataDecoder) - scopes.Free() + GetConstants(localBuilder, currentFrame, containingScopes.GetConstantSignatures(), metadataDecoder) + containingScopes.Free() Dim locals = localBuilder.ToImmutableAndFree() Dim methodDebugInfo As MethodDebugInfo @@ -167,7 +171,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator Return New EvaluationContext( metadataBlocks, - scope, + reuseConstraints, compilation, metadataDecoder, currentFrame, @@ -283,7 +287,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator 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 Friend Function CreateCompilationContext(syntax As ExecutableStatementSyntax) As CompilationContext diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/SymUnmanagedReaderExtensions.vb b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/SymUnmanagedReaderExtensions.vb index 7fb3c4b605e21a52b7297442bf96349530055e19..d016f9421ebb4c004e84488296676a1f69d7d54d 100644 --- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/SymUnmanagedReaderExtensions.vb +++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/SymUnmanagedReaderExtensions.vb @@ -2,7 +2,6 @@ Imports System.Collections.Immutable Imports System.Runtime.CompilerServices -Imports System.Runtime.InteropServices Imports Microsoft.CodeAnalysis.ExpressionEvaluator Imports Microsoft.VisualStudio.SymReaderInterop Imports ImportScope = Microsoft.VisualStudio.SymReaderInterop.ImportScope @@ -74,7 +73,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator projectLevelImportRecords.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 ' TODO (acasey): portable format overload (GH #702) diff --git a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/ExpressionCompilerTests.vb b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/ExpressionCompilerTests.vb index 89a58ac8c2ac146a046b7a106398686c4a428508..72d3a30a8e5e7827641e914fbff4dde441497705 100644 --- a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/ExpressionCompilerTests.vb +++ b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/ExpressionCompilerTests.vb @@ -258,14 +258,14 @@ End Class" Assert.Null(previous) 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) - Assert.Equal(context, previous.EvaluationContext) + Assert.NotEqual(context, previous.EvaluationContext) ' Not required, just documentary. ' At type context. context = EvaluationContext.CreateTypeContext(previous, methodBlocks, moduleVersionId, typeToken) Assert.NotEqual(context, previous.EvaluationContext) - Assert.NotEqual(context.MethodScope, previous.EvaluationContext.MethodScope) + Assert.Null(context.MethodContextReuseConstraints) Assert.Equal(context.Compilation, previous.Compilation) ' Step through entire method. @@ -273,14 +273,19 @@ End Class" previous = new VisualBasicMetadataContext(context) For offset = startOffset To endOffset - 1 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) If scope Is previousScope Then Assert.Equal(context, previous.EvaluationContext) Else ' Different scope. Should reuse compilation. Assert.NotEqual(context, previous.EvaluationContext) - If previous IsNot Nothing Then - Assert.NotEqual(context.MethodScope, previous.EvaluationContext.MethodScope) + If previous.EvaluationContext IsNot Nothing Then + Assert.NotEqual(context.MethodContextReuseConstraints, previous.EvaluationContext.MethodContextReuseConstraints) Assert.Equal(context.Compilation, previous.Compilation) End If End If @@ -300,9 +305,9 @@ End Class" GetContextState(runtime, "C.F", methodBlocks, moduleVersionId, symReader, methodToken, localSignatureToken) ' 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.Equal(context.MethodScope, previous.EvaluationContext.MethodScope) + Assert.True(previous.EvaluationContext.MethodContextReuseConstraints.Value.AreSatisfied(methodToken, methodVersion, endOffset - 1)) Assert.NotEqual(context.Compilation, previous.Compilation) previous = new VisualBasicMetadataContext(context) @@ -310,7 +315,7 @@ End Class" GetContextState(runtime, "C.G", methodBlocks, moduleVersionId, symReader, methodToken, localSignatureToken) context = EvaluationContext.CreateMethodContext(previous, methodBlocks, MakeDummyLazyAssemblyReaders(), symReader, moduleVersionId, methodToken, methodVersion, ilOffset:=0, localSignatureToken:=localSignatureToken) 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) ' No EvaluationContext. Should reuse Compilation diff --git a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/HoistedStateMachineLocalTests.vb b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/HoistedStateMachineLocalTests.vb index 7c9e11b7e4a1aa4b8fe351103c6a0ef566b97fa7..7b80f8e1fbb2f7fabbaaa2085b878ea8d44275a9 100644 --- a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/HoistedStateMachineLocalTests.vb +++ b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/HoistedStateMachineLocalTests.vb @@ -1366,7 +1366,7 @@ End Class - Public Sub Caching() + Public Sub CacheInvalidation() Const source = " Imports System.Collections.Generic diff --git a/src/Test/PdbUtilities/Shared/CustomDebugInfoReader.cs b/src/Test/PdbUtilities/Shared/CustomDebugInfoReader.cs index 8a2b11a9e8215cf96abd088899d2abbddc29c0fc..9f1c2d192954ef1999ab09e4af108e57be709c43 100644 --- a/src/Test/PdbUtilities/Shared/CustomDebugInfoReader.cs +++ b/src/Test/PdbUtilities/Shared/CustomDebugInfoReader.cs @@ -464,34 +464,6 @@ public static ImmutableArray GetVisualBasicImportStrings(this ISymUnmana return importStrings; } - // TODO (acasey): caller should depend on abstraction (GH #702) - public static ImmutableSortedSet GetCSharpInScopeHoistedLocalIndices(byte[] customDebugInfo, int methodToken, int methodVersion, int ilOffset) - { - var record = TryGetCustomDebugInfoRecord(customDebugInfo, CustomDebugInfoKind.StateMachineHoistedLocalScopes); - if (record.IsDefault) - { - return ImmutableSortedSet.Empty; - } - - var scopes = DecodeStateMachineHoistedLocalScopesRecord(record); - - ArrayBuilder builder = ArrayBuilder.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 result = builder.ToImmutableSortedSet(); - builder.Free(); - return result; - } - // TODO (acasey): caller should depend on abstraction (GH #702) /// Bad data. public static void GetCSharpDynamicLocalInfo(