/// <summary> /// Create a context for evaluating expressions at a type scope. /// </summary> /// <param name="previous">Previous context, if any, for possible re-use.</param> /// <param name="metadataBlocks">Module metadata</param> /// <param name="moduleVersionId">Module containing type</param> /// <param name="typeToken">Type metadata token</param> /// <returns>Evaluation context</returns> /// <remarks> /// No locals since locals are associated with methods, not types. /// </remarks> internal static EvaluationContext CreateTypeContext( CSharpMetadataContext previous, ImmutableArray <MetadataBlock> metadataBlocks, Guid moduleVersionId, int typeToken) { Debug.Assert(MetadataTokens.Handle(typeToken).Kind == HandleKind.TypeDefinition); // Re-use the previous compilation if possible. var compilation = metadataBlocks.HaveNotChanged(previous) ? previous.Compilation : metadataBlocks.ToCompilation(); MetadataDecoder metadataDecoder; var currentType = compilation.GetType(moduleVersionId, typeToken, out metadataDecoder); Debug.Assert((object)currentType != null); Debug.Assert(metadataDecoder != null); var currentFrame = new SynthesizedContextMethodSymbol(currentType); return(new EvaluationContext( metadataBlocks, null, compilation, metadataDecoder, currentFrame, default(ImmutableArray <LocalSymbol>), ImmutableSortedSet <int> .Empty, default(MethodDebugInfo))); }
/// <summary> /// Create a context for evaluating expressions at a type scope. /// </summary> /// <param name="previous">Previous context, if any, for possible re-use.</param> /// <param name="metadataBlocks">Module metadata</param> /// <param name="moduleVersionId">Module containing type</param> /// <param name="typeToken">Type metadata token</param> /// <returns>Evaluation context</returns> /// <remarks> /// No locals since locals are associated with methods, not types. /// </remarks> internal static EvaluationContext CreateTypeContext( CSharpMetadataContext previous, ImmutableArray<MetadataBlock> metadataBlocks, Guid moduleVersionId, int typeToken) { Debug.Assert(MetadataTokens.Handle(typeToken).Kind == HandleKind.TypeDefinition); // Re-use the previous compilation if possible. var compilation = metadataBlocks.HaveNotChanged(previous) ? previous.Compilation : metadataBlocks.ToCompilation(); MetadataDecoder metadataDecoder; var currentType = compilation.GetType(moduleVersionId, typeToken, out metadataDecoder); Debug.Assert((object)currentType != null); Debug.Assert(metadataDecoder != null); var currentFrame = new SynthesizedContextMethodSymbol(currentType); return new EvaluationContext( metadataBlocks, null, compilation, metadataDecoder, currentFrame, default(ImmutableArray<LocalSymbol>), ImmutableSortedSet<int>.Empty, default(ImmutableArray<ImmutableArray<string>>), default(ImmutableArray<string>)); }
/// <summary> /// Create a context for evaluating expressions at a type scope. /// </summary> /// <param name="previous">Previous context, if any, for possible re-use.</param> /// <param name="metadataBlocks">Module metadata</param> /// <param name="moduleVersionId">Module containing type</param> /// <param name="typeToken">Type metadata token</param> /// <returns>Evaluation context</returns> /// <remarks> /// No locals since locals are associated with methods, not types. /// </remarks> internal static EvaluationContext CreateTypeContext( CSharpMetadataContext previous, ImmutableArray<MetadataBlock> metadataBlocks, Guid moduleVersionId, int typeToken) { // Re-use the previous compilation if possible. var compilation = previous.Matches(metadataBlocks) ? previous.Compilation : metadataBlocks.ToCompilation(); return CreateTypeContext(compilation, moduleVersionId, typeToken); }
/// <summary> /// Create a context for evaluating expressions at a type scope. /// </summary> /// <param name="previous">Previous context, if any, for possible re-use.</param> /// <param name="metadataBlocks">Module metadata</param> /// <param name="moduleVersionId">Module containing type</param> /// <param name="typeToken">Type metadata token</param> /// <returns>Evaluation context</returns> /// <remarks> /// No locals since locals are associated with methods, not types. /// </remarks> internal static EvaluationContext CreateTypeContext( CSharpMetadataContext previous, ImmutableArray <MetadataBlock> metadataBlocks, Guid moduleVersionId, int typeToken) { // Re-use the previous compilation if possible. var compilation = previous.Matches(metadataBlocks) ? previous.Compilation : metadataBlocks.ToCompilation(); return(CreateTypeContext(compilation, moduleVersionId, typeToken)); }
internal static EvaluationContext CreateTypeContext <TAppDomain>( TAppDomain appDomain, GetMetadataContextDelegate <TAppDomain> getMetadataContext, ImmutableArray <MetadataBlock> metadataBlocks, Guid moduleVersionId, int typeToken, MakeAssemblyReferencesKind kind) { CSharpCompilation?compilation; if (kind == MakeAssemblyReferencesKind.DirectReferencesOnly) { // Avoid using the cache for referenced assemblies only // since this should be the exceptional case. compilation = metadataBlocks.ToCompilationReferencedModulesOnly(moduleVersionId); return(EvaluationContext.CreateTypeContext( compilation, moduleVersionId, typeToken)); } var contextId = MetadataContextId.GetContextId(moduleVersionId, kind); var previous = getMetadataContext(appDomain); CSharpMetadataContext previousMetadataContext = default; if (previous.Matches(metadataBlocks)) { previous.AssemblyContexts.TryGetValue(contextId, out previousMetadataContext); } // Re-use the previous compilation if possible. compilation = previousMetadataContext.Compilation; if (compilation == null) { compilation = metadataBlocks.ToCompilation(moduleVersionId, kind); } var context = EvaluationContext.CreateTypeContext( compilation, moduleVersionId, typeToken); // New type context is not attached to the AppDomain since it is less // re-usable than the previous attached method context. (We could hold // on to it if we don't have a previous method context but it's unlikely // that we evaluated a type-level expression before a method-level.) Debug.Assert(context != previousMetadataContext.EvaluationContext); return(context); }
/// <summary> /// Create a context for evaluating expressions within a method scope. /// </summary> /// <param name="previous">Previous context, if any, for possible re-use.</param> /// <param name="metadataBlocks">Module metadata</param> /// <param name="symReader"><see cref="ISymUnmanagedReader"/> for PDB associated with <paramref name="moduleVersionId"/></param> /// <param name="moduleVersionId">Module containing method</param> /// <param name="methodToken">Method metadata token</param> /// <param name="methodVersion">Method version.</param> /// <param name="ilOffset">IL offset of instruction pointer in method</param> /// <param name="localSignatureToken">Method local signature token</param> /// <returns>Evaluation context</returns> internal static EvaluationContext CreateMethodContext( CSharpMetadataContext previous, ImmutableArray <MetadataBlock> metadataBlocks, object symReader, Guid moduleVersionId, int methodToken, int methodVersion, uint ilOffset, int localSignatureToken) { var offset = NormalizeILOffset(ilOffset); // Re-use the previous compilation if possible. CSharpCompilation compilation; if (previous.Matches(metadataBlocks)) { // Re-use entire context if method scope has not changed. var previousContext = previous.EvaluationContext; if (previousContext != null && previousContext.MethodContextReuseConstraints.HasValue && previousContext.MethodContextReuseConstraints.GetValueOrDefault().AreSatisfied(moduleVersionId, methodToken, methodVersion, offset)) { return(previousContext); } compilation = previous.Compilation; } else { compilation = metadataBlocks.ToCompilation(); } return(CreateMethodContext( compilation, symReader, moduleVersionId, methodToken, methodVersion, offset, localSignatureToken)); }
internal override CSharpCompilation GetCompilation(DkmClrModuleInstance moduleInstance) { var appDomain = moduleInstance.AppDomain; var previous = appDomain.GetDataItem <CSharpMetadataContext>(); var metadataBlocks = moduleInstance.RuntimeInstance.GetMetadataBlocks(appDomain); CSharpCompilation compilation; if (metadataBlocks.HaveNotChanged(previous)) { compilation = previous.Compilation; } else { var dataItem = new CSharpMetadataContext(metadataBlocks); appDomain.SetDataItem(DkmDataCreationDisposition.CreateAlways, dataItem); compilation = dataItem.Compilation; } return(compilation); }
internal override PEMethodSymbol GetMethod(DkmClrInstructionAddress instructionAddress) { var moduleInstance = instructionAddress.ModuleInstance; var appDomain = moduleInstance.AppDomain; var previous = appDomain.GetDataItem <CSharpMetadataContext>(); var metadataBlocks = instructionAddress.Process.GetMetadataBlocks(appDomain); CSharpCompilation compilation; if (metadataBlocks.HaveNotChanged(previous)) { compilation = previous.Compilation; } else { var dataItem = new CSharpMetadataContext(metadataBlocks); appDomain.SetDataItem(DkmDataCreationDisposition.CreateAlways, dataItem); compilation = dataItem.Compilation; } return(compilation.GetSourceMethod(moduleInstance.Mvid, instructionAddress.MethodId.Token)); }
/// <summary> /// Create a context for evaluating expressions within a method scope. /// </summary> /// <param name="previous">Previous context, if any, for possible re-use.</param> /// <param name="metadataBlocks">Module metadata</param> /// <param name="symReader"><see cref="ISymUnmanagedReader"/> for PDB associated with <paramref name="moduleVersionId"/></param> /// <param name="moduleVersionId">Module containing method</param> /// <param name="methodToken">Method metadata token</param> /// <param name="methodVersion">Method version.</param> /// <param name="ilOffset">IL offset of instruction pointer in method</param> /// <param name="localSignatureToken">Method local signature token</param> /// <returns>Evaluation context</returns> internal static EvaluationContext CreateMethodContext( CSharpMetadataContext previous, ImmutableArray <MetadataBlock> metadataBlocks, object symReader, Guid moduleVersionId, int methodToken, int methodVersion, uint ilOffset, int localSignatureToken) { var offset = NormalizeILOffset(ilOffset); CSharpCompilation compilation = metadataBlocks.ToCompilation(default(Guid), MakeAssemblyReferencesKind.AllAssemblies); return(CreateMethodContext( compilation, symReader, moduleVersionId, methodToken, methodVersion, offset, localSignatureToken)); }
/// <summary> /// Create a context for evaluating expressions within a method scope. /// </summary> /// <param name="previous">Previous context, if any, for possible re-use.</param> /// <param name="metadataBlocks">Module metadata</param> /// <param name="symReader"><see cref="ISymUnmanagedReader"/> for PDB associated with <paramref name="moduleVersionId"/></param> /// <param name="moduleVersionId">Module containing method</param> /// <param name="methodToken">Method metadata token</param> /// <param name="methodVersion">Method version.</param> /// <param name="ilOffset">IL offset of instruction pointer in method</param> /// <param name="localSignatureToken">Method local signature token</param> /// <returns>Evaluation context</returns> internal static EvaluationContext CreateMethodContext( CSharpMetadataContext previous, ImmutableArray<MetadataBlock> metadataBlocks, object symReader, Guid moduleVersionId, int methodToken, int methodVersion, int ilOffset, int localSignatureToken) { // Re-use the previous compilation if possible. CSharpCompilation compilation; if (previous.Matches(metadataBlocks)) { // Re-use entire context if method scope has not changed. var previousContext = previous.EvaluationContext; if (previousContext != null && previousContext.MethodContextReuseConstraints.HasValue && previousContext.MethodContextReuseConstraints.GetValueOrDefault().AreSatisfied(moduleVersionId, methodToken, methodVersion, ilOffset)) { return previousContext; } compilation = previous.Compilation; } else { compilation = metadataBlocks.ToCompilation(); } return CreateMethodContext( compilation, symReader, moduleVersionId, methodToken, methodVersion, ilOffset, localSignatureToken); }
/// <summary> /// Create a context for evaluating expressions within a method scope. /// </summary> /// <param name="previous">Previous context, if any, for possible re-use.</param> /// <param name="metadataBlocks">Module metadata</param> /// <param name="symReader"><see cref="ISymUnmanagedReader"/> for PDB associated with <paramref name="moduleVersionId"/></param> /// <param name="moduleVersionId">Module containing method</param> /// <param name="methodToken">Method metadata token</param> /// <param name="methodVersion">Method version.</param> /// <param name="ilOffset">IL offset of instruction pointer in method</param> /// <param name="localSignatureToken">Method local signature token</param> /// <returns>Evaluation context</returns> internal static EvaluationContext CreateMethodContext( CSharpMetadataContext previous, ImmutableArray <MetadataBlock> metadataBlocks, object symReader, Guid moduleVersionId, int methodToken, int methodVersion, int ilOffset, int localSignatureToken) { 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. 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)) { return(previousContext); } compilation = previous.Compilation; } else { compilation = metadataBlocks.ToCompilation(); } var localNames = scopes.GetLocalNames(); var dynamicLocalMap = ImmutableDictionary <int, ImmutableArray <bool> > .Empty; var dynamicLocalConstantMap = ImmutableDictionary <string, ImmutableArray <bool> > .Empty; var inScopeHoistedLocalIndices = ImmutableSortedSet <int> .Empty; var methodDebugInfo = default(MethodDebugInfo); if (typedSymReader != null) { 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); } catch (InvalidOperationException) { // bad CDI, ignore } } var methodHandle = (MethodDefinitionHandle)MetadataTokens.Handle(methodToken); var currentFrame = compilation.GetMethod(moduleVersionId, methodHandle); Debug.Assert((object)currentFrame != null); var metadataDecoder = new MetadataDecoder((PEModuleSymbol)currentFrame.ContainingModule, currentFrame); var localInfo = metadataDecoder.GetLocalInfo(localSignatureToken); var localBuilder = ArrayBuilder <LocalSymbol> .GetInstance(); var sourceAssembly = compilation.SourceAssembly; GetLocals(localBuilder, currentFrame, localNames, localInfo, dynamicLocalMap, sourceAssembly); GetConstants(localBuilder, currentFrame, scopes.GetConstantSignatures(), metadataDecoder, dynamicLocalConstantMap, sourceAssembly); scopes.Free(); var locals = localBuilder.ToImmutableAndFree(); return(new EvaluationContext( metadataBlocks, scope, compilation, metadataDecoder, currentFrame, locals, inScopeHoistedLocalIndices, methodDebugInfo)); }
/// <summary> /// Create a context for evaluating expressions within a method scope. /// </summary> /// <param name="previous">Previous context, if any, for possible re-use.</param> /// <param name="metadataBlocks">Module metadata</param> /// <param name="symReader"><see cref="ISymUnmanagedReader"/> for PDB associated with <paramref name="moduleVersionId"/></param> /// <param name="moduleVersionId">Module containing method</param> /// <param name="methodToken">Method metadata token</param> /// <param name="methodVersion">Method version.</param> /// <param name="ilOffset">IL offset of instruction pointer in method</param> /// <param name="localSignatureToken">Method local signature token</param> /// <returns>Evaluation context</returns> internal static EvaluationContext CreateMethodContext( CSharpMetadataContext previous, ImmutableArray<MetadataBlock> metadataBlocks, object symReader, Guid moduleVersionId, int methodToken, int methodVersion, int ilOffset, int localSignatureToken) { 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. 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)) { return previousContext; } compilation = previous.Compilation; } else { compilation = metadataBlocks.ToCompilation(); } var localNames = scopes.GetLocalNames(); var dynamicLocalMap = ImmutableDictionary<int, ImmutableArray<bool>>.Empty; var dynamicLocalConstantMap = ImmutableDictionary<string, ImmutableArray<bool>>.Empty; var inScopeHoistedLocalIndices = ImmutableSortedSet<int>.Empty; var groupedImportStrings = default(ImmutableArray<ImmutableArray<string>>); var externAliasStrings = default(ImmutableArray<string>); if (typedSymReader != null) { try { var cdi = typedSymReader.GetCustomDebugInfo(methodToken, methodVersion); if (cdi != null) { CustomDebugInfoReader.GetCSharpDynamicLocalInfo( cdi, methodToken, methodVersion, localNames.FirstOrDefault(), out dynamicLocalMap, out dynamicLocalConstantMap); inScopeHoistedLocalIndices = CustomDebugInfoReader.GetCSharpInScopeHoistedLocalIndices( cdi, methodToken, methodVersion, ilOffset); } groupedImportStrings = typedSymReader.GetCSharpGroupedImportStrings(methodToken, methodVersion, out externAliasStrings); } catch (InvalidOperationException) { // bad CDI, ignore } } var methodHandle = (MethodDefinitionHandle)MetadataTokens.Handle(methodToken); var currentFrame = compilation.GetMethod(moduleVersionId, methodHandle); Debug.Assert((object)currentFrame != null); var metadataDecoder = new MetadataDecoder((PEModuleSymbol)currentFrame.ContainingModule, currentFrame); var localInfo = metadataDecoder.GetLocalInfo(localSignatureToken); var localBuilder = ArrayBuilder<LocalSymbol>.GetInstance(); var sourceAssembly = compilation.SourceAssembly; GetLocals(localBuilder, currentFrame, localNames, localInfo, dynamicLocalMap, sourceAssembly); GetConstants(localBuilder, currentFrame, scopes.GetConstantSignatures(), metadataDecoder, dynamicLocalConstantMap, sourceAssembly); scopes.Free(); var locals = localBuilder.ToImmutableAndFree(); return new EvaluationContext( metadataBlocks, scope, compilation, metadataDecoder, currentFrame, locals, inScopeHoistedLocalIndices, groupedImportStrings, externAliasStrings); }
public void ReuseEvaluationContext() { var sourceA = @"public interface I { }"; var sourceB = @"class C { static void F(I o) { object x = 1; if (o == null) { object y = 2; y = x; } else { object z; } x = 3; } static void G() { } }"; var compilationA = CreateCompilationWithMscorlib( sourceA, options: TestOptions.DebugDll, assemblyName: ExpressionCompilerUtilities.GenerateUniqueName()); var referenceA = compilationA.EmitToImageReference(); var compilationB = CreateCompilationWithMscorlib( sourceB, options: TestOptions.DebugDll, assemblyName: ExpressionCompilerUtilities.GenerateUniqueName(), references: new MetadataReference[] { referenceA }); byte[] exeBytes; byte[] pdbBytes; ImmutableArray<MetadataReference> references; compilationB.EmitAndGetReferences(out exeBytes, out pdbBytes, out references); const int methodVersion = 1; CSharpMetadataContext previous = default(CSharpMetadataContext); int startOffset; int endOffset; var runtime = CreateRuntimeInstance(ExpressionCompilerUtilities.GenerateUniqueName(), references, exeBytes, new SymReader(pdbBytes)); ImmutableArray<MetadataBlock> typeBlocks; ImmutableArray<MetadataBlock> methodBlocks; Guid moduleVersionId; ISymUnmanagedReader symReader; int typeToken; int methodToken; int localSignatureToken; GetContextState(runtime, "C", out typeBlocks, out moduleVersionId, out symReader, out typeToken, out localSignatureToken); GetContextState(runtime, "C.F", out methodBlocks, out moduleVersionId, out symReader, out methodToken, out localSignatureToken); // Get non-empty scopes. var scopes = symReader.GetScopes(methodToken, methodVersion, EvaluationContext.IsLocalScopeEndInclusive).WhereAsArray(s => s.Locals.Length > 0); Assert.True(scopes.Length >= 3); var outerScope = scopes.First(s => s.Locals.Contains("x")); startOffset = outerScope.StartOffset; endOffset = outerScope.EndOffset - 1; // At start of outer scope. var context = EvaluationContext.CreateMethodContext(previous, methodBlocks, symReader, moduleVersionId, methodToken, methodVersion, (uint)startOffset, localSignatureToken); Assert.Equal(default(CSharpMetadataContext), previous); previous = new CSharpMetadataContext(methodBlocks, context); // At end of outer scope - not reused because of the nested scope. context = EvaluationContext.CreateMethodContext(previous, methodBlocks, symReader, moduleVersionId, methodToken, methodVersion, (uint)endOffset, localSignatureToken); Assert.NotEqual(context, previous.EvaluationContext); // Not required, just documentary. // At type context. context = EvaluationContext.CreateTypeContext(previous, typeBlocks, moduleVersionId, typeToken); Assert.NotEqual(context, previous.EvaluationContext); Assert.Null(context.MethodContextReuseConstraints); Assert.Equal(context.Compilation, previous.Compilation); // Step through entire method. var previousScope = (Scope)null; previous = new CSharpMetadataContext(typeBlocks, context); 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(moduleVersionId, methodToken, methodVersion, offset)); } context = EvaluationContext.CreateMethodContext(previous, methodBlocks, symReader, moduleVersionId, methodToken, methodVersion, (uint)offset, localSignatureToken); if (scope == previousScope) { Assert.Equal(context, previous.EvaluationContext); } else { // Different scope. Should reuse compilation. Assert.NotEqual(context, previous.EvaluationContext); if (previous.EvaluationContext != null) { Assert.NotEqual(context.MethodContextReuseConstraints, previous.EvaluationContext.MethodContextReuseConstraints); Assert.Equal(context.Compilation, previous.Compilation); } } previousScope = scope; previous = new CSharpMetadataContext(methodBlocks, context); } // With different references. var fewerReferences = references.Remove(referenceA); Assert.Equal(fewerReferences.Length, references.Length - 1); runtime = CreateRuntimeInstance(ExpressionCompilerUtilities.GenerateUniqueName(), fewerReferences, exeBytes, new SymReader(pdbBytes)); GetContextState(runtime, "C.F", out methodBlocks, out moduleVersionId, out symReader, out methodToken, out localSignatureToken); // Different references. No reuse. context = EvaluationContext.CreateMethodContext(previous, methodBlocks, symReader, moduleVersionId, methodToken, methodVersion, (uint)endOffset, localSignatureToken); Assert.NotEqual(context, previous.EvaluationContext); Assert.True(previous.EvaluationContext.MethodContextReuseConstraints.Value.AreSatisfied(moduleVersionId, methodToken, methodVersion, endOffset)); Assert.NotEqual(context.Compilation, previous.Compilation); previous = new CSharpMetadataContext(methodBlocks, context); // Different method. Should reuse Compilation. 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.False(previous.EvaluationContext.MethodContextReuseConstraints.Value.AreSatisfied(moduleVersionId, methodToken, methodVersion, 0)); Assert.Equal(context.Compilation, previous.Compilation); // No EvaluationContext. Should reuse Compilation previous = new CSharpMetadataContext(previous.MetadataBlocks, previous.Compilation); context = EvaluationContext.CreateMethodContext(previous, methodBlocks, symReader, moduleVersionId, methodToken, methodVersion, ilOffset: 0, localSignatureToken: localSignatureToken); Assert.Null(previous.EvaluationContext); Assert.NotNull(context); Assert.Equal(context.Compilation, previous.Compilation); }
/// <summary> /// Create a context for evaluating expressions within a method scope. /// </summary> /// <param name="previous">Previous context, if any, for possible re-use.</param> /// <param name="metadataBlocks">Module metadata</param> /// <param name="symReader"><see cref="ISymUnmanagedReader"/> for PDB associated with <paramref name="moduleVersionId"/></param> /// <param name="moduleVersionId">Module containing method</param> /// <param name="methodToken">Method metadata token</param> /// <param name="methodVersion">Method version.</param> /// <param name="ilOffset">IL offset of instruction pointer in method</param> /// <param name="localSignatureToken">Method local signature token</param> /// <returns>Evaluation context</returns> internal static EvaluationContext CreateMethodContext( CSharpMetadataContext previous, ImmutableArray <MetadataBlock> metadataBlocks, object symReader, Guid moduleVersionId, int methodToken, int methodVersion, int ilOffset, int localSignatureToken) { Debug.Assert(MetadataTokens.Handle(methodToken).Kind == HandleKind.MethodDefinition); // 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 (previousContext != null && previousContext.MethodContextReuseConstraints.HasValue && previousContext.MethodContextReuseConstraints.GetValueOrDefault().AreSatisfied(methodToken, methodVersion, ilOffset)) { return(previousContext); } compilation = previous.Compilation; } else { compilation = metadataBlocks.ToCompilation(); } 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 inScopeHoistedLocalIndices = ImmutableSortedSet <int> .Empty; var methodDebugInfo = default(MethodDebugInfo); if (typedSymReader != null) { try { // TODO (https://github.com/dotnet/roslyn/issues/702): switch on the type of typedSymReader and call the appropriate helper. methodDebugInfo = typedSymReader.GetMethodDebugInfo(methodToken, methodVersion, localNames.FirstOrDefault()); inScopeHoistedLocalIndices = methodDebugInfo.GetInScopeHoistedLocalIndices(ilOffset, ref methodContextReuseConstraints); } catch (InvalidOperationException) { // bad CDI, ignore } } var methodHandle = (MethodDefinitionHandle)MetadataTokens.Handle(methodToken); var currentFrame = compilation.GetMethod(moduleVersionId, methodHandle); Debug.Assert((object)currentFrame != null); var metadataDecoder = new MetadataDecoder((PEModuleSymbol)currentFrame.ContainingModule, currentFrame); var localInfo = metadataDecoder.GetLocalInfo(localSignatureToken); var localBuilder = ArrayBuilder <LocalSymbol> .GetInstance(); var sourceAssembly = compilation.SourceAssembly; 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, methodContextReuseConstraints, compilation, metadataDecoder, currentFrame, locals, inScopeHoistedLocalIndices, methodDebugInfo)); }
public void AssemblyDuplicateReferences() { var sourceA = @"public class A { }"; var sourceB = @"public class B { public A F = new A(); }"; var sourceC = @"class C { private B F = new B(); static void M() { } }"; // Assembly A, multiple versions, strong name. var assemblyNameA = ExpressionCompilerUtilities.GenerateUniqueName(); var publicKeyA = ImmutableArray.CreateRange(new byte[] { 0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x52, 0x53, 0x41, 0x31, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0xED, 0xD3, 0x22, 0xCB, 0x6B, 0xF8, 0xD4, 0xA2, 0xFC, 0xCC, 0x87, 0x37, 0x04, 0x06, 0x04, 0xCE, 0xE7, 0xB2, 0xA6, 0xF8, 0x4A, 0xEE, 0xF3, 0x19, 0xDF, 0x5B, 0x95, 0xE3, 0x7A, 0x6A, 0x28, 0x24, 0xA4, 0x0A, 0x83, 0x83, 0xBD, 0xBA, 0xF2, 0xF2, 0x52, 0x20, 0xE9, 0xAA, 0x3B, 0xD1, 0xDD, 0xE4, 0x9A, 0x9A, 0x9C, 0xC0, 0x30, 0x8F, 0x01, 0x40, 0x06, 0xE0, 0x2B, 0x95, 0x62, 0x89, 0x2A, 0x34, 0x75, 0x22, 0x68, 0x64, 0x6E, 0x7C, 0x2E, 0x83, 0x50, 0x5A, 0xCE, 0x7B, 0x0B, 0xE8, 0xF8, 0x71, 0xE6, 0xF7, 0x73, 0x8E, 0xEB, 0x84, 0xD2, 0x73, 0x5D, 0x9D, 0xBE, 0x5E, 0xF5, 0x90, 0xF9, 0xAB, 0x0A, 0x10, 0x7E, 0x23, 0x48, 0xF4, 0xAD, 0x70, 0x2E, 0xF7, 0xD4, 0x51, 0xD5, 0x8B, 0x3A, 0xF7, 0xCA, 0x90, 0x4C, 0xDC, 0x80, 0x19, 0x26, 0x65, 0xC9, 0x37, 0xBD, 0x52, 0x81, 0xF1, 0x8B, 0xCD }); var compilationAS1 = CreateCompilation( new AssemblyIdentity(assemblyNameA, new Version(1, 1, 1, 1), cultureName: "", publicKeyOrToken: publicKeyA, hasPublicKey: true), new[] { sourceA }, references: new[] { MscorlibRef }, options: TestOptions.DebugDll.WithDelaySign(true)); var referenceAS1 = compilationAS1.EmitToImageReference(); var identityAS1 = referenceAS1.GetAssemblyIdentity(); var compilationAS2 = CreateCompilation( new AssemblyIdentity(assemblyNameA, new Version(2, 1, 1, 1), cultureName: "", publicKeyOrToken: publicKeyA, hasPublicKey: true), new[] { sourceA }, references: new[] { MscorlibRef }, options: TestOptions.DebugDll.WithDelaySign(true)); var referenceAS2 = compilationAS2.EmitToImageReference(); var identityAS2 = referenceAS2.GetAssemblyIdentity(); // Assembly B, multiple versions, not strong name. var assemblyNameB = ExpressionCompilerUtilities.GenerateUniqueName(); var compilationBN1 = CreateCompilation( new AssemblyIdentity(assemblyNameB, new Version(1, 1, 1, 1)), new[] { sourceB }, references: new[] { MscorlibRef, referenceAS1 }, options: TestOptions.DebugDll); var referenceBN1 = compilationBN1.EmitToImageReference(); var identityBN1 = referenceBN1.GetAssemblyIdentity(); var compilationBN2 = CreateCompilation( new AssemblyIdentity(assemblyNameB, new Version(2, 2, 2, 1)), new[] { sourceB }, references: new[] { MscorlibRef, referenceAS1 }, options: TestOptions.DebugDll); var referenceBN2 = compilationBN2.EmitToImageReference(); var identityBN2 = referenceBN2.GetAssemblyIdentity(); // Assembly B, multiple versions, strong name. var publicKeyB = ImmutableArray.CreateRange(new byte[] { 0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x53, 0x52, 0x41, 0x31, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0xED, 0xD3, 0x22, 0xCB, 0x6B, 0xF8, 0xD4, 0xA2, 0xFC, 0xCC, 0x87, 0x37, 0x04, 0x06, 0x04, 0xCE, 0xE7, 0xB2, 0xA6, 0xF8, 0x4A, 0xEE, 0xF3, 0x19, 0xDF, 0x5B, 0x95, 0xE3, 0x7A, 0x6A, 0x28, 0x24, 0xA4, 0x0A, 0x83, 0x83, 0xBD, 0xBA, 0xF2, 0xF2, 0x52, 0x20, 0xE9, 0xAA, 0x3B, 0xD1, 0xDD, 0xE4, 0x9A, 0x9A, 0x9C, 0xC0, 0x30, 0x8F, 0x01, 0x40, 0x06, 0xE0, 0x2B, 0x95, 0x62, 0x89, 0x2A, 0x34, 0x75, 0x22, 0x68, 0x64, 0x6E, 0x7C, 0x2E, 0x83, 0x50, 0x5A, 0xCE, 0x7B, 0x0B, 0xE8, 0xF8, 0x71, 0xE6, 0xF7, 0x73, 0x8E, 0xEB, 0x84, 0xD2, 0x73, 0x5D, 0x9D, 0xBE, 0x5E, 0xF5, 0x90, 0xF9, 0xAB, 0x0A, 0x10, 0x7E, 0x23, 0x48, 0xF4, 0xAD, 0x70, 0x2E, 0xF7, 0xD4, 0x51, 0xD5, 0x8B, 0x3A, 0xF7, 0xCA, 0x90, 0x4C, 0xDC, 0x80, 0x19, 0x26, 0x65, 0xC9, 0x37, 0xBD, 0x52, 0x81, 0xF1, 0x8B, 0xCD }); var compilationBS1 = CreateCompilation( new AssemblyIdentity(assemblyNameB, new Version(1, 1, 1, 1), cultureName: "", publicKeyOrToken: publicKeyB, hasPublicKey: true), new[] { sourceB }, references: new[] { MscorlibRef, referenceAS1 }, options: TestOptions.DebugDll.WithDelaySign(true)); var referenceBS1 = compilationBS1.EmitToImageReference(); var identityBS1 = referenceBS1.GetAssemblyIdentity(); var compilationBS2 = CreateCompilation( new AssemblyIdentity(assemblyNameB, new Version(2, 2, 2, 1), cultureName: "", publicKeyOrToken: publicKeyB, hasPublicKey: true), new[] { sourceB }, references: new[] { MscorlibRef, referenceAS2 }, options: TestOptions.DebugDll.WithDelaySign(true)); var referenceBS2 = compilationBS2.EmitToImageReference(); var identityBS2 = referenceBS2.GetAssemblyIdentity(); var mscorlibIdentity = MscorlibRef.GetAssemblyIdentity(); var mscorlib20Identity = MscorlibRef_v20.GetAssemblyIdentity(); var systemRefIdentity = SystemRef.GetAssemblyIdentity(); var systemRef20Identity = SystemRef_v20.GetAssemblyIdentity(); // No duplicates. VerifyAssemblyReferences( referenceBN1, ImmutableArray.Create(MscorlibRef, referenceAS1, referenceBN1), ImmutableArray.Create(mscorlibIdentity, identityAS1, identityBN1)); // No duplicates, extra references. VerifyAssemblyReferences( referenceAS1, ImmutableArray.Create(MscorlibRef, referenceBN1, referenceAS1, referenceBS2), ImmutableArray.Create(mscorlibIdentity, identityAS1)); // Strong-named, non-strong-named, and framework duplicates, same version (no aliases). VerifyAssemblyReferences( referenceBN2, ImmutableArray.Create(MscorlibRef, referenceAS1, MscorlibRef, referenceBN2, referenceBN2, referenceAS1, referenceAS1), ImmutableArray.Create(mscorlibIdentity, identityAS1, identityBN2)); // Strong-named, non-strong-named, and framework duplicates, different versions. VerifyAssemblyReferences( referenceBN1, ImmutableArray.Create(MscorlibRef, referenceAS1, MscorlibRef_v20, referenceAS2, referenceBN2, referenceBN1, referenceAS2, referenceAS1, referenceBN1), ImmutableArray.Create(mscorlibIdentity, identityAS2, identityBN2)); VerifyAssemblyReferences( referenceBN2, ImmutableArray.Create(MscorlibRef, referenceAS1, MscorlibRef_v20, referenceAS2, referenceBN2, referenceBN1, referenceAS2, referenceAS1, referenceBN1), ImmutableArray.Create(mscorlibIdentity, identityAS2, identityBN2)); // Strong-named, different versions. VerifyAssemblyReferences( referenceBS1, ImmutableArray.Create(MscorlibRef, referenceAS1, referenceAS2, referenceBS2, referenceBS1, referenceAS2, referenceAS1, referenceBS1), ImmutableArray.Create(mscorlibIdentity, identityAS2, identityBS2)); VerifyAssemblyReferences( referenceBS2, ImmutableArray.Create(MscorlibRef, referenceAS1, referenceAS2, referenceBS2, referenceBS1, referenceAS2, referenceAS1, referenceBS1), ImmutableArray.Create(mscorlibIdentity, identityAS2, identityBS2)); // Assembly C, multiple versions, not strong name. var assemblyNameC = ExpressionCompilerUtilities.GenerateUniqueName(); var compilationCN1 = CreateCompilation( new AssemblyIdentity(assemblyNameC, new Version(1, 1, 1, 1)), new[] { sourceC }, references: new[] { MscorlibRef, referenceBS1 }, options: TestOptions.DebugDll); byte[] exeBytesC1; byte[] pdbBytesC1; ImmutableArray<MetadataReference> references; compilationCN1.EmitAndGetReferences(out exeBytesC1, out pdbBytesC1, out references); var compilationCN2 = CreateCompilation( new AssemblyIdentity(assemblyNameC, new Version(2, 1, 1, 1)), new[] { sourceC }, references: new[] { MscorlibRef, referenceBS2 }, options: TestOptions.DebugDll); byte[] exeBytesC2; byte[] pdbBytesC2; compilationCN1.EmitAndGetReferences(out exeBytesC2, out pdbBytesC2, out references); // Duplicate assemblies, target module referencing BS1. using (var runtime = CreateRuntimeInstance( assemblyNameC, ImmutableArray.Create(MscorlibRef, referenceAS1, referenceAS2, referenceBS2, referenceBS1, referenceBS2), exeBytesC1, new SymReader(pdbBytesC1))) { ImmutableArray<MetadataBlock> typeBlocks; ImmutableArray<MetadataBlock> methodBlocks; Guid moduleVersionId; ISymUnmanagedReader symReader; int typeToken; int methodToken; int localSignatureToken; GetContextState(runtime, "C", out typeBlocks, out moduleVersionId, out symReader, out typeToken, out localSignatureToken); GetContextState(runtime, "C.M", out methodBlocks, out moduleVersionId, out symReader, out methodToken, out localSignatureToken); int ilOffset = ExpressionCompilerTestHelpers.GetOffset(methodToken, symReader); // Compile expression with type context with all modules. var context = EvaluationContext.CreateTypeContext( default(CSharpMetadataContext), typeBlocks, moduleVersionId, typeToken); string error; // A is ambiguous. var testData = new CompilationTestData(); context.CompileExpression("new A()", out error, testData); Assert.True(error.StartsWith("error CS0433: The type 'A' exists in both ")); testData = new CompilationTestData(); // B is ambiguous. context.CompileExpression("new B()", out error, testData); Assert.True(error.StartsWith("error CS0433: The type 'B' exists in both ")); var previous = new CSharpMetadataContext(typeBlocks, context); // Compile expression with type context with referenced modules only. context = EvaluationContext.CreateTypeContext( typeBlocks.ToCompilationReferencedModulesOnly(moduleVersionId), moduleVersionId, typeToken); // A is unrecognized since there were no direct references to AS1 or AS2. testData = new CompilationTestData(); context.CompileExpression("new A()", out error, testData); Assert.Equal(error, "error CS0246: The type or namespace name 'A' could not be found (are you missing a using directive or an assembly reference?)"); testData = new CompilationTestData(); // B should be resolved to BS2. context.CompileExpression("new B()", out error, testData); var methodData = testData.GetMethodData("<>x.<>m0"); methodData.VerifyIL( @"{ // Code size 6 (0x6) .maxstack 1 IL_0000: newobj ""B..ctor()"" IL_0005: ret }"); Assert.Equal(methodData.Method.ReturnType.ContainingAssembly.ToDisplayString(), identityBS2.GetDisplayName()); // B.F should result in missing assembly AS2 since there were no direct references to AS2. ResultProperties resultProperties; ImmutableArray<AssemblyIdentity> missingAssemblyIdentities; testData = new CompilationTestData(); context.CompileExpression( InspectionContextFactory.Empty, "(new B()).F", DkmEvaluationFlags.None, DiagnosticFormatter.Instance, out resultProperties, out error, out missingAssemblyIdentities, EnsureEnglishUICulture.PreferredOrNull, testData); AssertEx.Equal(missingAssemblyIdentities, ImmutableArray.Create(identityAS2)); // Compile expression with method context with all modules. context = EvaluationContext.CreateMethodContext( previous, methodBlocks, symReader, moduleVersionId, methodToken: methodToken, methodVersion: 1, ilOffset: ilOffset, localSignatureToken: localSignatureToken); Assert.Equal(previous.Compilation, context.Compilation); // re-use type context compilation testData = new CompilationTestData(); // A is ambiguous. testData = new CompilationTestData(); context.CompileExpression("new A()", out error, testData); Assert.True(error.StartsWith("error CS0433: The type 'A' exists in both ")); testData = new CompilationTestData(); // B is ambiguous. context.CompileExpression("new B()", out error, testData); Assert.True(error.StartsWith("error CS0433: The type 'B' exists in both ")); // Compile expression with method context with referenced modules only. context = EvaluationContext.CreateMethodContext( methodBlocks.ToCompilationReferencedModulesOnly(moduleVersionId), symReader, moduleVersionId, methodToken: methodToken, methodVersion: 1, ilOffset: ilOffset, localSignatureToken: localSignatureToken); // A is unrecognized since there were no direct references to AS1 or AS2. testData = new CompilationTestData(); context.CompileExpression("new A()", out error, testData); Assert.Equal(error, "error CS0246: The type or namespace name 'A' could not be found (are you missing a using directive or an assembly reference?)"); testData = new CompilationTestData(); // B should be resolved to BS2. context.CompileExpression("new B()", out error, testData); methodData = testData.GetMethodData("<>x.<>m0"); methodData.VerifyIL( @"{ // Code size 6 (0x6) .maxstack 1 IL_0000: newobj ""B..ctor()"" IL_0005: ret }"); Assert.Equal(methodData.Method.ReturnType.ContainingAssembly.ToDisplayString(), identityBS2.GetDisplayName()); // B.F should result in missing assembly AS2 since there were no direct references to AS2. testData = new CompilationTestData(); context.CompileExpression( InspectionContextFactory.Empty, "(new B()).F", DkmEvaluationFlags.None, DiagnosticFormatter.Instance, out resultProperties, out error, out missingAssemblyIdentities, EnsureEnglishUICulture.PreferredOrNull, testData); AssertEx.Equal(missingAssemblyIdentities, ImmutableArray.Create(identityAS2)); } }
internal static EvaluationContext CreateMethodContext( RuntimeInstance runtime, string methodName, int atLineNumber = -1, CSharpMetadataContext previous = null) { ImmutableArray<MetadataBlock> blocks; Guid moduleVersionId; ISymUnmanagedReader symReader; int methodToken; int localSignatureToken; GetContextState(runtime, methodName, out blocks, out moduleVersionId, out symReader, out methodToken, out localSignatureToken); int ilOffset = ExpressionCompilerTestHelpers.GetOffset(methodToken, symReader, atLineNumber); return EvaluationContext.CreateMethodContext( previous, blocks, symReader, moduleVersionId, methodToken: methodToken, methodVersion: 1, ilOffset: ilOffset, localSignatureToken: localSignatureToken); }