private DynamicAnalysisInjector(MethodSymbol method, BoundStatement methodBody, SyntheticBoundNodeFactory methodBodyFactory, MethodSymbol createPayload, DiagnosticBag diagnostics, DebugDocumentProvider debugDocumentProvider, Instrumenter previous) : base(previous) { _createPayload = createPayload; _method = method; _methodBody = methodBody; _spansBuilder = ArrayBuilder <SourceSpan> .GetInstance(); TypeSymbol payloadElementType = methodBodyFactory.SpecialType(SpecialType.System_Boolean); _payloadType = ArrayTypeSymbol.CreateCSharpArray(methodBodyFactory.Compilation.Assembly, payloadElementType); _methodPayload = methodBodyFactory.SynthesizedLocal(_payloadType, kind: SynthesizedLocalKind.InstrumentationPayload, syntax: methodBody.Syntax); _diagnostics = diagnostics; _debugDocumentProvider = debugDocumentProvider; _methodHasExplicitBlock = MethodHasExplicitBlock(method); _methodBodyFactory = methodBodyFactory; // The first point indicates entry into the method and has the span of the method definition. SyntaxNode syntax = MethodDeclarationIfAvailable(methodBody.Syntax); if (!method.IsImplicitlyDeclared) { _methodEntryInstrumentation = AddAnalysisPoint(syntax, SkipAttributes(syntax), methodBodyFactory); } }
public static DynamicAnalysisInjector TryCreate(MethodSymbol method, BoundStatement methodBody, SyntheticBoundNodeFactory methodBodyFactory, DiagnosticBag diagnostics, DebugDocumentProvider debugDocumentProvider, Instrumenter previous) { // Do not instrument implicitly-declared methods, except for constructors. // Instrument implicit constructors in order to instrument member initializers. if (method.IsImplicitlyDeclared && !method.IsImplicitConstructor) { return null; } // Do not instrument methods marked with or in scope of ExcludeFromCodeCoverageAttribute. if (IsExcludedFromCodeCoverage(method)) { return null; } MethodSymbol createPayload = GetCreatePayload(methodBodyFactory.Compilation, methodBody.Syntax, diagnostics); // Do not instrument any methods if CreatePayload is not present. if ((object)createPayload == null) { return null; } // Do not instrument CreatePayload if it is part of the current compilation (which occurs only during testing). // CreatePayload will fail at run time with an infinite recursion if it is instrumented. if (method.Equals(createPayload)) { return null; } return new DynamicAnalysisInjector(method, methodBody, methodBodyFactory, createPayload, diagnostics, debugDocumentProvider, previous); }
public MethodBody( byte[] ilBits, ushort maxStack, Cci.IMethodDefinition parent, ImmutableArray<Cci.ILocalDefinition> locals, SequencePointList sequencePoints, DebugDocumentProvider debugDocumentProvider, ImmutableArray<Cci.ExceptionHandlerRegion> exceptionHandlers, ImmutableArray<Cci.LocalScope> localScopes, Cci.CustomDebugInfoKind customDebugInfoKind, bool hasDynamicLocalVariables, ImmutableArray<Cci.NamespaceScope> namespaceScopes = default(ImmutableArray<Cci.NamespaceScope>), string iteratorClassName = null, ImmutableArray<Cci.LocalScope> iteratorScopes = default(ImmutableArray<Cci.LocalScope>), Cci.AsyncMethodBodyDebugInfo asyncMethodDebugInfo = null) { Debug.Assert(!locals.IsDefault); Debug.Assert(!exceptionHandlers.IsDefault); Debug.Assert(!localScopes.IsDefault); this.ilBits = ilBits; this.asyncMethodDebugInfo = asyncMethodDebugInfo; this.maxStack = maxStack; this.parent = parent; this.locals = locals; this.sequencePoints = sequencePoints; this.debugDocumentProvider = debugDocumentProvider; this.exceptionHandlers = exceptionHandlers; this.localScopes = localScopes; this.customDebugInfoKind = customDebugInfoKind; this.hasDynamicLocalVariables = hasDynamicLocalVariables; this.namespaceScopes = namespaceScopes.IsDefault ? ImmutableArray<Cci.NamespaceScope>.Empty : namespaceScopes; this.iteratorClassName = iteratorClassName; this.iteratorScopes = iteratorScopes.IsDefault ? ImmutableArray<Cci.LocalScope>.Empty : iteratorScopes; }
public MethodBody( byte[] ilBits, ushort maxStack, Cci.IMethodDefinition parent, ImmutableArray <Cci.ILocalDefinition> locals, SequencePointList sequencePoints, DebugDocumentProvider debugDocumentProvider, ImmutableArray <Cci.ExceptionHandlerRegion> exceptionHandlers, ImmutableArray <Cci.LocalScope> localScopes, Cci.CustomDebugInfoKind customDebugInfoKind, bool hasDynamicLocalVariables, ImmutableArray <Cci.NamespaceScope> namespaceScopes = default(ImmutableArray <Cci.NamespaceScope>), string iteratorClassName = null, ImmutableArray <Cci.LocalScope> iteratorScopes = default(ImmutableArray <Cci.LocalScope>), Cci.AsyncMethodBodyDebugInfo asyncMethodDebugInfo = null) { Debug.Assert(!locals.IsDefault); Debug.Assert(!exceptionHandlers.IsDefault); Debug.Assert(!localScopes.IsDefault); this.ilBits = ilBits; this.asyncMethodDebugInfo = asyncMethodDebugInfo; this.maxStack = maxStack; this.parent = parent; this.locals = locals; this.sequencePoints = sequencePoints; this.debugDocumentProvider = debugDocumentProvider; this.exceptionHandlers = exceptionHandlers; this.localScopes = localScopes; this.customDebugInfoKind = customDebugInfoKind; this.hasDynamicLocalVariables = hasDynamicLocalVariables; this.namespaceScopes = namespaceScopes.IsDefault ? ImmutableArray <Cci.NamespaceScope> .Empty : namespaceScopes; this.iteratorClassName = iteratorClassName; this.iteratorScopes = iteratorScopes.IsDefault ? ImmutableArray <Cci.LocalScope> .Empty : iteratorScopes; }
private DynamicAnalysisInjector( MethodSymbol method, BoundStatement methodBody, SyntheticBoundNodeFactory methodBodyFactory, MethodSymbol createPayloadForMethodsSpanningSingleFile, MethodSymbol createPayloadForMethodsSpanningMultipleFiles, BindingDiagnosticBag diagnostics, DebugDocumentProvider debugDocumentProvider, Instrumenter previous ) : base(previous) { _createPayloadForMethodsSpanningSingleFile = createPayloadForMethodsSpanningSingleFile; _createPayloadForMethodsSpanningMultipleFiles = createPayloadForMethodsSpanningMultipleFiles; _method = method; _methodBody = methodBody; _spansBuilder = ArrayBuilder <SourceSpan> .GetInstance(); TypeSymbol payloadElementType = methodBodyFactory.SpecialType( SpecialType.System_Boolean ); _payloadType = ArrayTypeSymbol.CreateCSharpArray( methodBodyFactory.Compilation.Assembly, TypeWithAnnotations.Create(payloadElementType) ); _diagnostics = diagnostics; _debugDocumentProvider = debugDocumentProvider; _methodBodyFactory = methodBodyFactory; // Set the factory context to generate nodes for the current method var oldMethod = methodBodyFactory.CurrentFunction; methodBodyFactory.CurrentFunction = method; _methodPayload = methodBodyFactory.SynthesizedLocal( _payloadType, kind: SynthesizedLocalKind.InstrumentationPayload, syntax: methodBody.Syntax ); // The first point indicates entry into the method and has the span of the method definition. SyntaxNode syntax = MethodDeclarationIfAvailable(methodBody.Syntax); if ( !method.IsImplicitlyDeclared && !(method is SynthesizedSimpleProgramEntryPointSymbol) ) { _methodEntryInstrumentation = AddAnalysisPoint( syntax, SkipAttributes(syntax), methodBodyFactory ); } // Restore context methodBodyFactory.CurrentFunction = oldMethod; }
/// <summary> /// Lower a block of code by performing local rewritings. /// </summary> public static BoundStatement Rewrite( CSharpCompilation compilation, MethodSymbol method, int methodOrdinal, NamedTypeSymbol containingType, BoundStatement statement, TypeCompilationState compilationState, SynthesizedSubmissionFields previousSubmissionFields, bool allowOmissionOfConditionalCalls, bool instrumentForDynamicAnalysis, ref ImmutableArray <SourceSpan> dynamicAnalysisSpans, DebugDocumentProvider debugDocumentProvider, DiagnosticBag diagnostics, out bool sawLambdas, out bool sawLocalFunctions, out bool sawAwaitInExceptionHandler, // @MattWindsor91 (Concept-C# 2017) // // Sending concept witnesses to hoist to method compiler. // TODO: need to work out better way to do this out SmallDictionary <TypeSymbol, LocalSymbol> conceptWitnessesToHoist) { Debug.Assert(statement != null); Debug.Assert(compilationState != null); try { var factory = new SyntheticBoundNodeFactory(method, statement.Syntax, compilationState, diagnostics); DynamicAnalysisInjector dynamicInstrumenter = instrumentForDynamicAnalysis ? DynamicAnalysisInjector.TryCreate(method, statement, factory, diagnostics, debugDocumentProvider, Instrumenter.NoOp) : null; // We don’t want IL to differ based upon whether we write the PDB to a file/stream or not. // Presence of sequence points in the tree affects final IL, therefore, we always generate them. var localRewriter = new LocalRewriter(compilation, method, methodOrdinal, statement, containingType, factory, previousSubmissionFields, allowOmissionOfConditionalCalls, diagnostics, dynamicInstrumenter != null ? new DebugInfoInjector(dynamicInstrumenter) : DebugInfoInjector.Singleton); var loweredStatement = (BoundStatement)localRewriter.Visit(statement); sawLambdas = localRewriter._sawLambdas; sawLocalFunctions = localRewriter._sawLocalFunctions; sawAwaitInExceptionHandler = localRewriter._sawAwaitInExceptionHandler; if (dynamicInstrumenter != null) { dynamicAnalysisSpans = dynamicInstrumenter.DynamicAnalysisSpans; } conceptWitnessesToHoist = localRewriter._conceptWitnessesToHoist; // @MattWindsor91 (Concept-C# 2017) return(loweredStatement); } catch (SyntheticBoundNodeFactory.MissingPredefinedMember ex) { diagnostics.Add(ex.Diagnostic); sawLambdas = sawLocalFunctions = sawAwaitInExceptionHandler = false; conceptWitnessesToHoist = null; // @MattWindsor91 (Concept-C# 2017) return(new BoundBadStatement(statement.Syntax, ImmutableArray.Create <BoundNode>(statement), hasErrors: true)); } }
/// <summary> /// Lower a block of code by performing local rewritings. /// </summary> public static BoundStatement Rewrite( CSharpCompilation compilation, MethodSymbol method, int methodOrdinal, NamedTypeSymbol containingType, BoundStatement statement, TypeCompilationState compilationState, SynthesizedSubmissionFields previousSubmissionFields, bool allowOmissionOfConditionalCalls, bool instrumentForDynamicAnalysis, ref ImmutableArray <SourceSpan> dynamicAnalysisSpans, DebugDocumentProvider debugDocumentProvider, BindingDiagnosticBag diagnostics, out bool sawLambdas, out bool sawLocalFunctions, out bool sawAwaitInExceptionHandler) { Debug.Assert(statement != null); Debug.Assert(compilationState != null); try { var factory = new SyntheticBoundNodeFactory(method, statement.Syntax, compilationState, diagnostics); DynamicAnalysisInjector?dynamicInstrumenter = instrumentForDynamicAnalysis ? DynamicAnalysisInjector.TryCreate(method, statement, factory, diagnostics, debugDocumentProvider, Instrumenter.NoOp) : null; // We don’t want IL to differ based upon whether we write the PDB to a file/stream or not. // Presence of sequence points in the tree affects final IL, therefore, we always generate them. var localRewriter = new LocalRewriter(compilation, method, methodOrdinal, statement, containingType, factory, previousSubmissionFields, allowOmissionOfConditionalCalls, diagnostics, dynamicInstrumenter != null ? new DebugInfoInjector(dynamicInstrumenter) : DebugInfoInjector.Singleton); statement.CheckLocalsDefined(); var loweredStatement = localRewriter.VisitStatement(statement); Debug.Assert(loweredStatement is { }); loweredStatement.CheckLocalsDefined(); sawLambdas = localRewriter._sawLambdas; sawLocalFunctions = localRewriter._availableLocalFunctionOrdinal != 0; sawAwaitInExceptionHandler = localRewriter._sawAwaitInExceptionHandler; if (localRewriter._needsSpilling && !loweredStatement.HasErrors) { // Move spill sequences to a top-level statement. This handles "lifting" await and the switch expression. var spilledStatement = SpillSequenceSpiller.Rewrite(loweredStatement, method, compilationState, diagnostics); spilledStatement.CheckLocalsDefined(); loweredStatement = spilledStatement; } if (dynamicInstrumenter != null) { dynamicAnalysisSpans = dynamicInstrumenter.DynamicAnalysisSpans; } #if DEBUG LocalRewritingValidator.Validate(loweredStatement); localRewriter.AssertNoPlaceholderReplacements(); #endif return(loweredStatement); }
public static DynamicAnalysisInjector TryCreate( MethodSymbol method, BoundStatement methodBody, SyntheticBoundNodeFactory methodBodyFactory, DiagnosticBag diagnostics, DebugDocumentProvider debugDocumentProvider, Instrumenter previous) { // Do not instrument implicitly-declared methods, except for constructors. // Instrument implicit constructors in order to instrument member initializers. if (method.IsImplicitlyDeclared && !method.IsImplicitConstructor) { return(null); } // Do not instrument methods marked with or in scope of ExcludeFromCodeCoverageAttribute. if (IsExcludedFromCodeCoverage(method)) { return(null); } MethodSymbol createPayloadForMethodsSpanningSingleFile = GetCreatePayloadOverload( methodBodyFactory.Compilation, WellKnownMember.Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayloadForMethodsSpanningSingleFile, methodBody.Syntax, diagnostics); MethodSymbol createPayloadForMethodsSpanningMultipleFiles = GetCreatePayloadOverload( methodBodyFactory.Compilation, WellKnownMember.Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayloadForMethodsSpanningMultipleFiles, methodBody.Syntax, diagnostics); // Do not instrument any methods if CreatePayload is not present. if ((object)createPayloadForMethodsSpanningSingleFile == null || (object)createPayloadForMethodsSpanningMultipleFiles == null) { return(null); } // Do not instrument CreatePayload if it is part of the current compilation (which occurs only during testing). // CreatePayload will fail at run time with an infinite recursion if it is instrumented. if (method.Equals(createPayloadForMethodsSpanningSingleFile) || method.Equals(createPayloadForMethodsSpanningMultipleFiles)) { return(null); } return(new DynamicAnalysisInjector( method, methodBody, methodBodyFactory, createPayloadForMethodsSpanningSingleFile, createPayloadForMethodsSpanningMultipleFiles, diagnostics, debugDocumentProvider, previous)); }
private static Cci.DebugSourceDocument GetSourceDocument(DebugDocumentProvider debugDocumentProvider, SyntaxNode syntax, FileLinePositionSpan span) { string path = span.Path; // If the path for the syntax node is empty, try the path for the entire syntax tree. if (path.Length == 0) { path = syntax.SyntaxTree.FilePath; } return(debugDocumentProvider.Invoke(path, basePath: "")); }
public MethodBody( ImmutableArray <byte> ilBits, ushort maxStack, Cci.IMethodDefinition parent, DebugId methodId, ImmutableArray <Cci.ILocalDefinition> locals, SequencePointList sequencePoints, DebugDocumentProvider debugDocumentProvider, ImmutableArray <Cci.ExceptionHandlerRegion> exceptionHandlers, bool areLocalsZeroed, bool hasStackalloc, ImmutableArray <Cci.LocalScope> localScopes, bool hasDynamicLocalVariables, Cci.IImportScope importScopeOpt, ImmutableArray <LambdaDebugInfo> lambdaDebugInfo, ImmutableArray <ClosureDebugInfo> closureDebugInfo, string stateMachineTypeNameOpt, ImmutableArray <StateMachineHoistedLocalScope> stateMachineHoistedLocalScopes, ImmutableArray <EncHoistedLocalInfo> stateMachineHoistedLocalSlots, ImmutableArray <Cci.ITypeReference?> stateMachineAwaiterSlots, StateMachineStatesDebugInfo stateMachineStatesDebugInfo, StateMachineMoveNextBodyDebugInfo stateMachineMoveNextDebugInfoOpt, DynamicAnalysisMethodBodyData dynamicAnalysisDataOpt) { Debug.Assert(!locals.IsDefault); Debug.Assert(!exceptionHandlers.IsDefault); Debug.Assert(!localScopes.IsDefault); _ilBits = ilBits; _maxStack = maxStack; _parent = parent; _methodId = methodId; _locals = locals; _exceptionHandlers = exceptionHandlers; _areLocalsZeroed = areLocalsZeroed; HasStackalloc = hasStackalloc; _localScopes = localScopes; _hasDynamicLocalVariables = hasDynamicLocalVariables; _importScopeOpt = importScopeOpt; _lambdaDebugInfo = lambdaDebugInfo; _closureDebugInfo = closureDebugInfo; _stateMachineTypeNameOpt = stateMachineTypeNameOpt; _stateMachineHoistedLocalScopes = stateMachineHoistedLocalScopes; _stateMachineHoistedLocalSlots = stateMachineHoistedLocalSlots; _stateMachineAwaiterSlots = stateMachineAwaiterSlots; _stateMachineStatesDebugInfo = stateMachineStatesDebugInfo; _stateMachineMoveNextDebugInfoOpt = stateMachineMoveNextDebugInfoOpt; _dynamicAnalysisDataOpt = dynamicAnalysisDataOpt; _sequencePoints = GetSequencePoints(sequencePoints, debugDocumentProvider); }
private static ImmutableArray <Cci.SequencePoint> GetSequencePoints( SequencePointList?sequencePoints, DebugDocumentProvider debugDocumentProvider ) { if (sequencePoints == null || sequencePoints.IsEmpty) { return(ImmutableArray <Cci.SequencePoint> .Empty); } var sequencePointsBuilder = ArrayBuilder <Cci.SequencePoint> .GetInstance(); sequencePoints.GetSequencePoints(debugDocumentProvider, sequencePointsBuilder); return(sequencePointsBuilder.ToImmutableAndFree()); }
public MethodBody( ImmutableArray<byte> ilBits, ushort maxStack, Cci.IMethodDefinition parent, DebugId methodId, ImmutableArray<Cci.ILocalDefinition> locals, SequencePointList sequencePoints, DebugDocumentProvider debugDocumentProvider, ImmutableArray<Cci.ExceptionHandlerRegion> exceptionHandlers, ImmutableArray<Cci.LocalScope> localScopes, bool hasDynamicLocalVariables, Cci.IImportScope importScopeOpt, ImmutableArray<LambdaDebugInfo> lambdaDebugInfo, ImmutableArray<ClosureDebugInfo> closureDebugInfo, string stateMachineTypeNameOpt, ImmutableArray<Cci.StateMachineHoistedLocalScope> stateMachineHoistedLocalScopes, ImmutableArray<EncHoistedLocalInfo> stateMachineHoistedLocalSlots, ImmutableArray<Cci.ITypeReference> stateMachineAwaiterSlots, Cci.AsyncMethodBodyDebugInfo asyncMethodDebugInfo, DynamicAnalysisMethodBodyData dynamicAnalysisDataOpt) { Debug.Assert(!locals.IsDefault); Debug.Assert(!exceptionHandlers.IsDefault); Debug.Assert(!localScopes.IsDefault); _ilBits = ilBits; _asyncMethodDebugInfo = asyncMethodDebugInfo; _maxStack = maxStack; _parent = parent; _methodId = methodId; _locals = locals; _sequencePoints = sequencePoints; _debugDocumentProvider = debugDocumentProvider; _exceptionHandlers = exceptionHandlers; _localScopes = localScopes; _hasDynamicLocalVariables = hasDynamicLocalVariables; _importScopeOpt = importScopeOpt; _lambdaDebugInfo = lambdaDebugInfo; _closureDebugInfo = closureDebugInfo; _stateMachineTypeNameOpt = stateMachineTypeNameOpt; _stateMachineHoistedLocalScopes = stateMachineHoistedLocalScopes; _stateMachineHoistedLocalSlots = stateMachineHoistedLocalSlots; _stateMachineAwaiterSlots = stateMachineAwaiterSlots; _dynamicAnalysisDataOpt = dynamicAnalysisDataOpt; }
public MethodBody( byte[] ilBits, ushort maxStack, Cci.IMethodDefinition parent, int methodOrdinal, ImmutableArray <Cci.ILocalDefinition> locals, SequencePointList sequencePoints, DebugDocumentProvider debugDocumentProvider, ImmutableArray <Cci.ExceptionHandlerRegion> exceptionHandlers, ImmutableArray <Cci.LocalScope> localScopes, bool hasDynamicLocalVariables, ImmutableArray <Cci.NamespaceScope> namespaceScopes, Cci.NamespaceScopeEncoding namespaceScopeEncoding, ImmutableArray <LambdaDebugInfo> lambdaDebugInfo, ImmutableArray <ClosureDebugInfo> closureDebugInfo, string stateMachineTypeNameOpt, ImmutableArray <Cci.StateMachineHoistedLocalScope> stateMachineHoistedLocalScopes, ImmutableArray <EncHoistedLocalInfo> stateMachineHoistedLocalSlots, ImmutableArray <Cci.ITypeReference> stateMachineAwaiterSlots, Cci.AsyncMethodBodyDebugInfo asyncMethodDebugInfo) { Debug.Assert(!locals.IsDefault); Debug.Assert(!exceptionHandlers.IsDefault); Debug.Assert(!localScopes.IsDefault); _ilBits = ilBits; _asyncMethodDebugInfo = asyncMethodDebugInfo; _maxStack = maxStack; _parent = parent; _methodOrdinal = methodOrdinal; _locals = locals; _sequencePoints = sequencePoints; _debugDocumentProvider = debugDocumentProvider; _exceptionHandlers = exceptionHandlers; _localScopes = localScopes; _namespaceScopeEncoding = namespaceScopeEncoding; _hasDynamicLocalVariables = hasDynamicLocalVariables; _namespaceScopes = namespaceScopes.IsDefault ? ImmutableArray <Cci.NamespaceScope> .Empty : namespaceScopes; _lambdaDebugInfo = lambdaDebugInfo; _closureDebugInfo = closureDebugInfo; _stateMachineTypeNameOpt = stateMachineTypeNameOpt; _stateMachineHoistedLocalScopes = stateMachineHoistedLocalScopes; _stateMachineHoistedLocalSlots = stateMachineHoistedLocalSlots; _stateMachineAwaiterSlots = stateMachineAwaiterSlots; }
public static DynamicAnalysisInjector TryCreate(MethodSymbol method, BoundStatement methodBody, SyntheticBoundNodeFactory methodBodyFactory, DiagnosticBag diagnostics, DebugDocumentProvider debugDocumentProvider, Instrumenter previous) { // Do not instrument implicitly-declared methods. if (!method.IsImplicitlyDeclared) { MethodSymbol createPayload = GetCreatePayload(methodBodyFactory.Compilation, methodBody.Syntax, diagnostics); // Do not instrument any methods if CreatePayload is not present. // Do not instrument CreatePayload if it is part of the current compilation (which occurs only during testing). // CreatePayload will fail at run time with an infinite recursion if it Is instrumented. if ((object)createPayload != null && !method.Equals(createPayload)) { return new DynamicAnalysisInjector(method, methodBody, methodBodyFactory, createPayload, diagnostics, debugDocumentProvider, previous); } } return null; }
private DynamicAnalysisInjector(MethodSymbol method, BoundStatement methodBody, SyntheticBoundNodeFactory methodBodyFactory, MethodSymbol createPayload, DiagnosticBag diagnostics, DebugDocumentProvider debugDocumentProvider, Instrumenter previous) : base(previous) { _createPayload = createPayload; _method = method; _methodBody = methodBody; _spansBuilder = ArrayBuilder<SourceSpan>.GetInstance(); TypeSymbol payloadElementType = methodBodyFactory.SpecialType(SpecialType.System_Boolean); _payloadType = ArrayTypeSymbol.CreateCSharpArray(methodBodyFactory.Compilation.Assembly, payloadElementType); _methodPayload = methodBodyFactory.SynthesizedLocal(_payloadType, kind: SynthesizedLocalKind.InstrumentationPayload, syntax: methodBody.Syntax); _diagnostics = diagnostics; _debugDocumentProvider = debugDocumentProvider; _methodHasExplicitBlock = MethodHasExplicitBlock(method); _methodBodyFactory = methodBodyFactory; // The first point indicates entry into the method and has the span of the method definition. _methodEntryInstrumentation = AddAnalysisPoint(MethodDeclarationIfAvailable(methodBody.Syntax), methodBodyFactory); }
// Internal for testing only. internal MethodBodyCompiler(CSharpCompilation compilation, PEModuleBuilder moduleBeingBuilt, bool generateDebugInfo, bool hasDeclarationErrors, DiagnosticBag diagnostics, Predicate <Symbol> filter, CancellationToken cancellationToken) { this.compilation = compilation; this.moduleBeingBuilt = moduleBeingBuilt; this.generateDebugInfo = generateDebugInfo; this.cancellationToken = cancellationToken; this.diagnostics = diagnostics; this.optimize = compilation.Options.Optimize; this.filter = filter; this.hasDeclarationErrors = hasDeclarationErrors; SetGlobalErrorIfTrue(hasDeclarationErrors); if (generateDebugInfo) { this.debugDocumentProvider = (path, basePath) => moduleBeingBuilt.GetOrAddDebugDocument(path, basePath, CreateDebugDocumentForFile); this.namespaceScopeBuilder = new NamespaceScopeBuilder(compilation); } }
// Find the document for the first non-hidden sequence point (issue #4370) // Returns null if a real sequence point was found. private FileLinePositionSpan?FindFirstRealSequencePoint(DebugDocumentProvider documentProvider) { SequencePointList current = this; while (current != null) { foreach (var offsetAndSpan in current._points) { TextSpan span = offsetAndSpan.Span; bool isHidden = span == RawSequencePoint.HiddenSequencePointSpan; if (!isHidden) { FileLinePositionSpan fileLinePositionSpan = this._tree.GetMappedLineSpanAndVisibility(span, out isHidden); if (!isHidden) { return(fileLinePositionSpan); } } } current = current._next; } return(null); }
private static Cci.DebugSourceDocument GetSourceDocument(DebugDocumentProvider debugDocumentProvider, SyntaxNode syntax) { return(GetSourceDocument(debugDocumentProvider, syntax, syntax.GetLocation().GetMappedLineSpan())); }
private static BoundExpressionStatement GetCreatePayloadStatement( ImmutableArray <SourceSpan> dynamicAnalysisSpans, SyntaxNode methodBodySyntax, LocalSymbol methodPayload, MethodSymbol createPayloadForMethodsSpanningSingleFile, MethodSymbol createPayloadForMethodsSpanningMultipleFiles, BoundExpression mvid, BoundExpression methodToken, BoundExpression payloadSlot, SyntheticBoundNodeFactory methodBodyFactory, DebugDocumentProvider debugDocumentProvider) { MethodSymbol createPayloadOverload; BoundExpression fileIndexOrIndicesArgument; if (dynamicAnalysisSpans.IsEmpty) { createPayloadOverload = createPayloadForMethodsSpanningSingleFile; // For a compiler generated method that has no 'real' spans, we emit the index for // the document corresponding to the syntax node that is associated with its bound node. var document = GetSourceDocument(debugDocumentProvider, methodBodySyntax); fileIndexOrIndicesArgument = methodBodyFactory.SourceDocumentIndex(document); } else { var documents = PooledHashSet <DebugSourceDocument> .GetInstance(); var fileIndices = ArrayBuilder <BoundExpression> .GetInstance(); foreach (var span in dynamicAnalysisSpans) { var document = span.Document; if (documents.Add(document)) { fileIndices.Add(methodBodyFactory.SourceDocumentIndex(document)); } } documents.Free(); // At this point, we should have at least one document since we have already // handled the case where method has no 'real' spans (and therefore no documents) above. if (fileIndices.Count == 1) { createPayloadOverload = createPayloadForMethodsSpanningSingleFile; fileIndexOrIndicesArgument = fileIndices.Single(); } else { createPayloadOverload = createPayloadForMethodsSpanningMultipleFiles; // Order of elements in fileIndices should be deterministic because these // elements were added based on order of spans in dynamicAnalysisSpans above. fileIndexOrIndicesArgument = methodBodyFactory.Array( methodBodyFactory.SpecialType(SpecialType.System_Int32), fileIndices.ToImmutable()); } fileIndices.Free(); } return(methodBodyFactory.Assignment( methodBodyFactory.Local(methodPayload), methodBodyFactory.Call( null, createPayloadOverload, mvid, methodToken, fileIndexOrIndicesArgument, payloadSlot, methodBodyFactory.Literal(dynamicAnalysisSpans.Length)))); }
// Find the document for the first non-hidden sequence point (issue #4370) // Returns null if a real sequence point was found. private FileLinePositionSpan? FindFirstRealSequencePoint(DebugDocumentProvider documentProvider) { SequencePointList current = this; while (current != null) { foreach (var offsetAndSpan in current._points) { TextSpan span = offsetAndSpan.Span; bool isHidden = span == RawSequencePoint.HiddenSequencePointSpan; if (!isHidden) { FileLinePositionSpan fileLinePositionSpan = this._tree.GetMappedLineSpanAndVisibility(span, out isHidden); if (!isHidden) { return fileLinePositionSpan; } } } current = current._next; } return null; }
/// <summary> /// Get all the sequence points, possibly mapping them using #line/ExternalSource directives, and mapping /// file names to debug documents with the given mapping function. /// </summary> /// <param name="documentProvider">Function that maps file paths to CCI debug documents</param> /// <param name="builder">where sequence points should be deposited</param> public void GetSequencePoints( DebugDocumentProvider documentProvider, ArrayBuilder <Cci.SequencePoint> builder) { bool lastPathIsMapped = false; string lastPath = null; Cci.DebugSourceDocument lastDebugDocument = null; FileLinePositionSpan?firstReal = FindFirstRealSequencePoint(); if (!firstReal.HasValue) { return; } lastPath = firstReal.Value.Path; lastPathIsMapped = firstReal.Value.HasMappedPath; lastDebugDocument = documentProvider(lastPath, basePath: lastPathIsMapped ? this._tree.FilePath : null); SequencePointList current = this; while (current != null) { SyntaxTree currentTree = current._tree; foreach (OffsetAndSpan offsetAndSpan in current._points) { TextSpan span = offsetAndSpan.Span; // if it's a hidden sequence point, or a sequence point with syntax that points to a position that is inside // of a hidden region (can be defined with #line hidden (C#) or implicitly by #ExternalSource (VB), make it // a hidden sequence point. bool isHidden = span == RawSequencePoint.HiddenSequencePointSpan; FileLinePositionSpan fileLinePositionSpan = default(FileLinePositionSpan); if (!isHidden) { fileLinePositionSpan = currentTree.GetMappedLineSpanAndVisibility(span, out isHidden); } if (isHidden) { if (lastPath == null) { lastPath = currentTree.FilePath; lastDebugDocument = documentProvider(lastPath, basePath: null); } if (lastDebugDocument != null) { builder.Add(new Cci.SequencePoint( lastDebugDocument, offset: offsetAndSpan.Offset, startLine: HiddenSequencePointLine, startColumn: 0, endLine: HiddenSequencePointLine, endColumn: 0)); } } else { if (lastPath != fileLinePositionSpan.Path || lastPathIsMapped != fileLinePositionSpan.HasMappedPath) { lastPath = fileLinePositionSpan.Path; lastPathIsMapped = fileLinePositionSpan.HasMappedPath; lastDebugDocument = documentProvider(lastPath, basePath: lastPathIsMapped ? currentTree.FilePath : null); } if (lastDebugDocument != null) { builder.Add(new Cci.SequencePoint( lastDebugDocument, offset: offsetAndSpan.Offset, startLine: (fileLinePositionSpan.StartLinePosition.Line == -1) ? 0 : fileLinePositionSpan.StartLinePosition.Line + 1, startColumn: fileLinePositionSpan.StartLinePosition.Character + 1, endLine: (fileLinePositionSpan.EndLinePosition.Line == -1) ? 0 : fileLinePositionSpan.EndLinePosition.Line + 1, endColumn: fileLinePositionSpan.EndLinePosition.Character + 1 )); } } } current = current._next; } }
/// <summary> /// Generates method body that calls another method. /// Used for wrapping a method call into a method, e.g. an entry point. /// </summary> internal static MethodBody GenerateMethodBody( PEModuleBuilder moduleBuilder, MethodSymbol routine, Action <ILBuilder> builder, VariableSlotAllocator variableSlotAllocatorOpt, DiagnosticBag diagnostics, bool emittingPdb) { var compilation = moduleBuilder.Compilation; var localSlotManager = new LocalSlotManager(variableSlotAllocatorOpt); var optimizations = compilation.Options.OptimizationLevel; DebugDocumentProvider debugDocumentProvider = null; if (emittingPdb) { debugDocumentProvider = (path, basePath) => { if (path.IsPharFile()) { path = PhpFileUtilities.BuildPharStubFileName(path); } return(moduleBuilder.DebugDocumentsBuilder.GetOrAddDebugDocument( path, basePath, normalizedPath => CreateDebugSourceDocument(normalizedPath, routine))); }; } ILBuilder il = new ILBuilder(moduleBuilder, localSlotManager, optimizations.AsOptimizationLevel()); try { StateMachineMoveNextBodyDebugInfo stateMachineMoveNextDebugInfo = null; builder(il); // il.Realize(); // var localVariables = il.LocalSlotManager.LocalsInOrder(); if (localVariables.Length > 0xFFFE) { //diagnosticsForThisMethod.Add(ErrorCode.ERR_TooManyLocals, method.Locations.First()); } //if (diagnosticsForThisMethod.HasAnyErrors()) //{ // // we are done here. Since there were errors we should not emit anything. // return null; //} //// We will only save the IL builders when running tests. //if (moduleBuilder.SaveTestData) //{ // moduleBuilder.SetMethodTestData(method, builder.GetSnapshot()); //} // Only compiler-generated MoveNext methods have iterator scopes. See if this is one. var stateMachineHoistedLocalScopes = default(ImmutableArray <StateMachineHoistedLocalScope>); //if (isStateMachineMoveNextMethod) //{ // stateMachineHoistedLocalScopes = builder.GetHoistedLocalScopes(); //} var stateMachineHoistedLocalSlots = default(ImmutableArray <EncHoistedLocalInfo>); var stateMachineAwaiterSlots = default(ImmutableArray <Cci.ITypeReference>); //if (optimizations == OptimizationLevel.Debug && stateMachineTypeOpt != null) //{ // Debug.Assert(method.IsAsync || method.IsIterator); // GetStateMachineSlotDebugInfo(moduleBuilder, moduleBuilder.GetSynthesizedFields(stateMachineTypeOpt), variableSlotAllocatorOpt, diagnosticsForThisMethod, out stateMachineHoistedLocalSlots, out stateMachineAwaiterSlots); // Debug.Assert(!diagnostics.HasAnyErrors()); //} return(new MethodBody( il.RealizedIL, il.MaxStack, (Cci.IMethodDefinition)routine.PartialDefinitionPart ?? routine, variableSlotAllocatorOpt?.MethodId ?? new DebugId(0, moduleBuilder.CurrentGenerationOrdinal), localVariables, il.RealizedSequencePoints, debugDocumentProvider, il.RealizedExceptionHandlers, il.GetAllScopes(), il.HasDynamicLocal, null, // importScopeOpt, ImmutableArray <LambdaDebugInfo> .Empty, // lambdaDebugInfo, ImmutableArray <ClosureDebugInfo> .Empty, // closureDebugInfo, null, //stateMachineTypeOpt?.Name, stateMachineHoistedLocalScopes, stateMachineHoistedLocalSlots, stateMachineAwaiterSlots, stateMachineMoveNextDebugInfo, null)); // dynamicAnalysisDataOpt } finally { // Basic blocks contain poolable builders for IL and sequence points. Free those back // to their pools. il.FreeBasicBlocks(); //// Remember diagnostics. //diagnostics.AddRange(diagnosticsForThisMethod); //diagnosticsForThisMethod.Free(); } }
public static DynamicAnalysisInjector TryCreate(MethodSymbol method, BoundStatement methodBody, SyntheticBoundNodeFactory methodBodyFactory, DiagnosticBag diagnostics, DebugDocumentProvider debugDocumentProvider, Instrumenter previous) { // Do not instrument implicitly-declared methods. if (!method.IsImplicitlyDeclared) { MethodSymbol createPayload = GetCreatePayload(methodBodyFactory.Compilation, methodBody.Syntax, diagnostics); // Do not instrument any methods if CreatePayload is not present. // Do not instrument CreatePayload if it is part of the current compilation (which occurs only during testing). // CreatePayload will fail at run time with an infinite recursion if it Is instrumented. if ((object)createPayload != null && !method.Equals(createPayload)) { return(new DynamicAnalysisInjector(method, methodBody, methodBodyFactory, createPayload, diagnostics, debugDocumentProvider, previous)); } } return(null); }
/// <summary> /// Get all the sequence points, possibly mapping them using #line/ExternalSource directives, and mapping /// file names to debug documents with the given mapping function. /// </summary> /// <param name="documentProvider">Function that maps file paths to CCI debug documents</param> /// <param name="builder">where sequence points should be deposited</param> public void GetSequencePoints( DebugDocumentProvider documentProvider, ArrayBuilder <Cci.SequencePoint> builder) { bool lastPathIsMapped = false; string lastPath = null; Cci.DebugSourceDocument lastDebugDocument = null; FileLinePositionSpan?firstReal = FindFirstRealSequencePoint(); if (!firstReal.HasValue) { return; } lastPath = firstReal.Value.Path; lastPathIsMapped = firstReal.Value.HasMappedPath; lastDebugDocument = documentProvider(lastPath, basePath: lastPathIsMapped ? this._tree.FilePath : null); SequencePointList current = this; while (current != null) { SyntaxTree currentTree = current._tree; foreach (var offsetAndSpan in current._points) { TextSpan span = offsetAndSpan.Span; // if it's a hidden sequence point, or a sequence point with syntax that points to a position that is inside // of a hidden region (can be defined with #line hidden (C#) or implicitly by #ExternalSource (VB), make it // a hidden sequence point. bool isHidden = span == RawSequencePoint.HiddenSequencePointSpan; FileLinePositionSpan fileLinePositionSpan = default; if (!isHidden) { fileLinePositionSpan = currentTree.GetMappedLineSpanAndVisibility(span, out isHidden); } if (isHidden) { if (lastPath == null) { lastPath = currentTree.FilePath; lastDebugDocument = documentProvider(lastPath, basePath: null); } if (lastDebugDocument != null) { builder.Add(new Cci.SequencePoint( lastDebugDocument, offset: offsetAndSpan.Offset, startLine: Cci.SequencePoint.HiddenLine, startColumn: 0, endLine: Cci.SequencePoint.HiddenLine, endColumn: 0)); } } else { if (lastPath != fileLinePositionSpan.Path || lastPathIsMapped != fileLinePositionSpan.HasMappedPath) { lastPath = fileLinePositionSpan.Path; lastPathIsMapped = fileLinePositionSpan.HasMappedPath; lastDebugDocument = documentProvider(lastPath, basePath: lastPathIsMapped ? currentTree.FilePath : null); } if (lastDebugDocument != null) { int startLine = (fileLinePositionSpan.StartLinePosition.Line == -1) ? 0 : fileLinePositionSpan.StartLinePosition.Line + 1; int endLine = (fileLinePositionSpan.EndLinePosition.Line == -1) ? 0 : fileLinePositionSpan.EndLinePosition.Line + 1; int startColumn = fileLinePositionSpan.StartLinePosition.Character + 1; int endColumn = fileLinePositionSpan.EndLinePosition.Character + 1; // Trim column number if necessary. // Column must be in range [0, 0xffff) and end column must be greater than start column if on the same line. // The Portable PDB specifies 0x10000, but StarkPlatform.Reflection.Metadata reader has an off-by-one error. // Windows PDBs allow the same range. const int MaxColumn = ushort.MaxValue - 1; if (startColumn > MaxColumn) { startColumn = (startLine == endLine) ? MaxColumn - 1 : MaxColumn; } if (endColumn > MaxColumn) { endColumn = MaxColumn; } builder.Add(new Cci.SequencePoint( lastDebugDocument, offset: offsetAndSpan.Offset, startLine: startLine, startColumn: (ushort)startColumn, endLine: endLine, endColumn: (ushort)endColumn )); } } } current = current._next; } }
internal static MethodBody GenerateMethodBody(TypeCompilationState compilationState, MethodSymbol method, BoundStatement block, DiagnosticBag diagnostics, bool optimize, DebugDocumentProvider debugDocumentProvider, ImmutableArray <NamespaceScope> namespaceScopes) { // Note: don't call diagnostics.HasAnyErrors() in release; could be expensive if compilation has many warnings. Debug.Assert(!diagnostics.HasAnyErrors(), "Running code generator when errors exist might be dangerous; code generator not well hardened"); bool emitSequencePoints = !namespaceScopes.IsDefault && !method.IsAsync; var module = compilationState.ModuleBuilder; var compilation = module.Compilation; var localSlotManager = module.CreateLocalSlotManager(method); ILBuilder builder = new ILBuilder(module, localSlotManager, optimize); DiagnosticBag diagnosticsForThisMethod = DiagnosticBag.GetInstance(); try { AsyncMethodBodyDebugInfo asyncDebugInfo = null; if ((object)method.AsyncKickoffMethod == null) // is this the MoveNext of an async method? { CodeGen.CodeGenerator.Run( method, block, builder, module, diagnosticsForThisMethod, optimize, emitSequencePoints); } else { int asyncCatchHandlerOffset; ImmutableArray <int> asyncYieldPoints; ImmutableArray <int> asyncResumePoints; CodeGen.CodeGenerator.Run( method, block, builder, module, diagnosticsForThisMethod, optimize, emitSequencePoints, out asyncCatchHandlerOffset, out asyncYieldPoints, out asyncResumePoints); asyncDebugInfo = new AsyncMethodBodyDebugInfo(method.AsyncKickoffMethod, asyncCatchHandlerOffset, asyncYieldPoints, asyncResumePoints); } var localVariables = builder.LocalSlotManager.LocalsInOrder(); if (localVariables.Length > 0xFFFE) { diagnosticsForThisMethod.Add(ErrorCode.ERR_TooManyLocals, method.Locations.First()); } if (diagnosticsForThisMethod.HasAnyErrors()) { // we are done here. Since there were errors we should not emit anything. return(null); } // We will only save the IL builders when running tests. if (module.SaveTestData) { module.SetMethodTestData(method, builder.GetSnapshot()); } // Only compiler-generated MoveNext methods have iterator scopes. See if this is one. bool hasIteratorScopes = method.Locations.IsEmpty && method.Name == "MoveNext" && (method.ExplicitInterfaceImplementations.Contains(compilation.GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__MoveNext) as MethodSymbol) || method.ExplicitInterfaceImplementations.Contains(compilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_IAsyncStateMachine_MoveNext) as MethodSymbol)); var iteratorScopes = hasIteratorScopes ? builder.GetIteratorScopes() : ImmutableArray <LocalScope> .Empty; var iteratorOrAsyncImplementation = compilationState.GetIteratorOrAsyncImplementationClass(method); return(new MethodBody( builder.RealizedIL, builder.MaxStack, method, localVariables, builder.RealizedSequencePoints, debugDocumentProvider, builder.RealizedExceptionHandlers, builder.GetAllScopes(), Microsoft.Cci.CustomDebugInfoKind.CSharpStyle, builder.HasDynamicLocal, namespaceScopes, (object)iteratorOrAsyncImplementation == null ? null : iteratorOrAsyncImplementation.MetadataName, iteratorScopes, asyncMethodDebugInfo: asyncDebugInfo )); } finally { // Basic blocks contain poolable builders for IL and sequence points. Free those back // to their pools. builder.FreeBasicBlocks(); // Remember diagnostics. diagnostics.AddRange(diagnosticsForThisMethod); diagnosticsForThisMethod.Free(); } }
internal static MethodBody GenerateMethodBody(TypeCompilationState compilationState, MethodSymbol method, BoundStatement block, DiagnosticBag diagnostics, bool optimize, DebugDocumentProvider debugDocumentProvider, ImmutableArray<NamespaceScope> namespaceScopes) { // Note: don't call diagnostics.HasAnyErrors() in release; could be expensive if compilation has many warnings. Debug.Assert(!diagnostics.HasAnyErrors(), "Running code generator when errors exist might be dangerous; code generator not well hardened"); bool emitSequencePoints = !namespaceScopes.IsDefault && !method.IsAsync; var module = compilationState.ModuleBuilder; var compilation = module.Compilation; var localSlotManager = module.CreateLocalSlotManager(method); ILBuilder builder = new ILBuilder(module, localSlotManager, optimize); DiagnosticBag diagnosticsForThisMethod = DiagnosticBag.GetInstance(); try { AsyncMethodBodyDebugInfo asyncDebugInfo = null; if ((object)method.AsyncKickoffMethod == null) // is this the MoveNext of an async method? { CodeGen.CodeGenerator.Run( method, block, builder, module, diagnosticsForThisMethod, optimize, emitSequencePoints); } else { int asyncCatchHandlerOffset; ImmutableArray<int> asyncYieldPoints; ImmutableArray<int> asyncResumePoints; CodeGen.CodeGenerator.Run( method, block, builder, module, diagnosticsForThisMethod, optimize, emitSequencePoints, out asyncCatchHandlerOffset, out asyncYieldPoints, out asyncResumePoints); asyncDebugInfo = new AsyncMethodBodyDebugInfo(method.AsyncKickoffMethod, asyncCatchHandlerOffset, asyncYieldPoints, asyncResumePoints); } var localVariables = builder.LocalSlotManager.LocalsInOrder(); if (localVariables.Length > 0xFFFE) { diagnosticsForThisMethod.Add(ErrorCode.ERR_TooManyLocals, method.Locations.First()); } if (diagnosticsForThisMethod.HasAnyErrors()) { // we are done here. Since there were errors we should not emit anything. return null; } // We will only save the IL builders when running tests. if (module.SaveTestData) { module.SetMethodTestData(method, builder.GetSnapshot()); } // Only compiler-generated MoveNext methods have iterator scopes. See if this is one. bool hasIteratorScopes = method.Locations.IsEmpty && method.Name == "MoveNext" && (method.ExplicitInterfaceImplementations.Contains(compilation.GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__MoveNext) as MethodSymbol) || method.ExplicitInterfaceImplementations.Contains(compilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_IAsyncStateMachine_MoveNext) as MethodSymbol)); var iteratorScopes = hasIteratorScopes ? builder.GetIteratorScopes() : ImmutableArray<LocalScope>.Empty; var iteratorOrAsyncImplementation = compilationState.GetIteratorOrAsyncImplementationClass(method); return new MethodBody( builder.RealizedIL, builder.MaxStack, method, localVariables, builder.RealizedSequencePoints, debugDocumentProvider, builder.RealizedExceptionHandlers, builder.GetAllScopes(), Microsoft.Cci.CustomDebugInfoKind.CSharpStyle, builder.HasDynamicLocal, namespaceScopes, (object)iteratorOrAsyncImplementation == null ? null : iteratorOrAsyncImplementation.MetadataName, iteratorScopes, asyncMethodDebugInfo: asyncDebugInfo ); } finally { // Basic blocks contain poolable builders for IL and sequence points. Free those back // to their pools. builder.FreeBasicBlocks(); // Remember diagnostics. diagnostics.AddRange(diagnosticsForThisMethod); diagnosticsForThisMethod.Free(); } }
/// <summary> /// Get all the sequence points, possibly mapping them using #line/ExternalSource directives, and mapping /// file names to debug documents with the given mapping function. /// </summary> /// <param name="documentProvider">Function that maps file paths to CCI debug documents</param> public ImmutableArray<Cci.SequencePoint> GetSequencePoints(DebugDocumentProvider documentProvider) { bool lastPathIsMapped = false; string lastPath = null; Cci.DebugSourceDocument lastDebugDocument = null; // First, count the number of sequence points. int count = 0; SequencePointList current = this; while (current != null) { count += current._points.Length; current = current._next; } ArrayBuilder<Cci.SequencePoint> result = ArrayBuilder<Cci.SequencePoint>.GetInstance(count); current = this; while (current != null) { SyntaxTree currentTree = current._tree; foreach (var offsetAndSpan in current._points) { TextSpan span = offsetAndSpan.Span; // if it's a hidden sequence point, or a sequence point with syntax that points to a position that is inside // of a hidden region (can be defined with #line hidden (C#) or implicitly by #ExternalSource (VB), make it // a hidden sequence point. bool isHidden = span == RawSequencePoint.HiddenSequencePointSpan; FileLinePositionSpan fileLinePositionSpan = default(FileLinePositionSpan); if (!isHidden) { fileLinePositionSpan = currentTree.GetMappedLineSpanAndVisibility(span, out isHidden); } if (isHidden) { if (lastPath == null) { lastPath = currentTree.FilePath; lastDebugDocument = documentProvider(lastPath, basePath: null); } if (lastDebugDocument != null) { result.Add(new Cci.SequencePoint( lastDebugDocument, offset: offsetAndSpan.Offset, startLine: HiddenSequencePointLine, startColumn: 0, endLine: HiddenSequencePointLine, endColumn: 0)); } } else { if (lastPath != fileLinePositionSpan.Path || lastPathIsMapped != fileLinePositionSpan.HasMappedPath) { lastPath = fileLinePositionSpan.Path; lastPathIsMapped = fileLinePositionSpan.HasMappedPath; lastDebugDocument = documentProvider(lastPath, basePath: lastPathIsMapped ? currentTree.FilePath : null); } if (lastDebugDocument != null) { result.Add(new Cci.SequencePoint( lastDebugDocument, offset: offsetAndSpan.Offset, startLine: (fileLinePositionSpan.StartLinePosition.Line == -1) ? 0 : fileLinePositionSpan.StartLinePosition.Line + 1, startColumn: fileLinePositionSpan.StartLinePosition.Character + 1, endLine: (fileLinePositionSpan.EndLinePosition.Line == -1) ? 0 : fileLinePositionSpan.EndLinePosition.Line + 1, endColumn: fileLinePositionSpan.EndLinePosition.Character + 1 )); } } } current = current._next; } return result.ToImmutableAndFree(); }
/// <summary> /// Get all the sequence points, possibly mapping them using #line/ExternalSource directives, and mapping /// file names to debug documents with the given mapping function. /// </summary> /// <param name="documentProvider">Function that maps file paths to CCI debug documents</param> public ImmutableArray <Cci.SequencePoint> GetSequencePoints(DebugDocumentProvider documentProvider) { bool lastPathIsMapped = false; string lastPath = null; Cci.DebugSourceDocument lastDebugDocument = null; // First, count the number of sequence points. int count = 0; SequencePointList current = this; while (current != null) { count += current._points.Length; current = current._next; } ArrayBuilder <Cci.SequencePoint> result = ArrayBuilder <Cci.SequencePoint> .GetInstance(count); current = this; while (current != null) { SyntaxTree currentTree = current._tree; foreach (var offsetAndSpan in current._points) { TextSpan span = offsetAndSpan.Span; // if it's a hidden sequence point, or a sequence point with syntax that points to a position that is inside // of a hidden region (can be defined with #line hidden (C#) or implicitly by #ExternalSource (VB), make it // a hidden sequence point. bool isHidden = span == RawSequencePoint.HiddenSequencePointSpan; FileLinePositionSpan fileLinePositionSpan = default(FileLinePositionSpan); if (!isHidden) { fileLinePositionSpan = currentTree.GetMappedLineSpanAndVisibility(span, out isHidden); } if (isHidden) { if (lastPath == null) { lastPath = currentTree.FilePath; lastDebugDocument = documentProvider(lastPath, basePath: null); } if (lastDebugDocument != null) { result.Add(new Cci.SequencePoint( lastDebugDocument, offset: offsetAndSpan.Offset, startLine: HiddenSequencePointLine, startColumn: 0, endLine: HiddenSequencePointLine, endColumn: 0)); } } else { if (lastPath != fileLinePositionSpan.Path || lastPathIsMapped != fileLinePositionSpan.HasMappedPath) { lastPath = fileLinePositionSpan.Path; lastPathIsMapped = fileLinePositionSpan.HasMappedPath; lastDebugDocument = documentProvider(lastPath, basePath: lastPathIsMapped ? currentTree.FilePath : null); } if (lastDebugDocument != null) { result.Add(new Cci.SequencePoint( lastDebugDocument, offset: offsetAndSpan.Offset, startLine: (fileLinePositionSpan.StartLinePosition.Line == -1) ? 0 : fileLinePositionSpan.StartLinePosition.Line + 1, startColumn: fileLinePositionSpan.StartLinePosition.Character + 1, endLine: (fileLinePositionSpan.EndLinePosition.Line == -1) ? 0 : fileLinePositionSpan.EndLinePosition.Line + 1, endColumn: fileLinePositionSpan.EndLinePosition.Character + 1 )); } } } current = current._next; } return(result.ToImmutableAndFree()); }