public MethodBody( byte[] ilBits, ushort maxStack, Cci.IMethodDefinition parent, ImmutableArray <LocalDefinition> locals, SequencePointList sequencePoints, DebugDocumentProvider debugDocumentProvider, ImmutableArray <Cci.ExceptionHandlerRegion> exceptionHandlers, ImmutableArray <LocalScope> localScopes, Cci.CustomDebugInfoKind customDebugInfoKind, bool hasDynamicLocalVariables, ImmutableArray <NamespaceScope> namespaceScopes = default(ImmutableArray <NamespaceScope>), string iteratorClassName = null, ImmutableArray <LocalScope> iteratorScopes = default(ImmutableArray <LocalScope>), Cci.AsyncMethodBodyDebugInfo asyncMethodDebugInfo = null) { 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 <NamespaceScope> .Empty : namespaceScopes; this.iteratorClassName = iteratorClassName; this.iteratorScopes = iteratorScopes.IsDefault ? ImmutableArray <LocalScope> .Empty : iteratorScopes; }
public MethodBody( byte[] ilBits, ushort maxStack, Cci.IMethodDefinition parent, ImmutableArray<LocalDefinition> locals, SequencePointList sequencePoints, DebugDocumentProvider debugDocumentProvider, ImmutableArray<Cci.ExceptionHandlerRegion> exceptionHandlers, ImmutableArray<LocalScope> localScopes, Cci.CustomDebugInfoKind customDebugInfoKind, bool hasDynamicLocalVariables, ImmutableArray<NamespaceScope> namespaceScopes = default(ImmutableArray<NamespaceScope>), string iteratorClassName = null, ImmutableArray<LocalScope> iteratorScopes = default(ImmutableArray<LocalScope>), Cci.AsyncMethodBodyDebugInfo asyncMethodDebugInfo = null) { 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<NamespaceScope>.Empty : namespaceScopes; this.iteratorClassName = iteratorClassName; this.iteratorScopes = iteratorScopes.IsDefault ? ImmutableArray<LocalScope>.Empty : iteratorScopes; }
private void SerializeAsyncMethodSteppingInfo(AsyncMethodBodyDebugInfo asyncInfo, MethodDefinitionHandle moveNextMethod) { Debug.Assert(asyncInfo.ResumeOffsets.Length == asyncInfo.YieldOffsets.Length); Debug.Assert(asyncInfo.CatchHandlerOffset >= -1); var writer = new BlobBuilder(); writer.WriteUInt32((uint)((long)asyncInfo.CatchHandlerOffset + 1)); for (int i = 0; i < asyncInfo.ResumeOffsets.Length; i++) { writer.WriteUInt32((uint)asyncInfo.YieldOffsets[i]); writer.WriteUInt32((uint)asyncInfo.ResumeOffsets[i]); writer.WriteCompressedInteger(MetadataTokens.GetRowNumber(moveNextMethod)); } _debugMetadataOpt.AddCustomDebugInformation( parent: moveNextMethod, kind: _debugMetadataOpt.GetOrAddGuid(PortableCustomDebugInfoKinds.AsyncMethodSteppingInformationBlob), value: _debugMetadataOpt.GetOrAddBlob(writer)); }
private void SerializeAsyncMethodSteppingInfo(AsyncMethodBodyDebugInfo asyncInfo, int moveNextMethodRid) { Debug.Assert(asyncInfo.ResumeOffsets.Length == asyncInfo.YieldOffsets.Length); Debug.Assert(asyncInfo.CatchHandlerOffset >= -1); var writer = new BlobBuilder(); writer.WriteUInt32((uint)((long)asyncInfo.CatchHandlerOffset + 1)); for (int i = 0; i < asyncInfo.ResumeOffsets.Length; i++) { writer.WriteUInt32((uint)asyncInfo.YieldOffsets[i]); writer.WriteUInt32((uint)asyncInfo.ResumeOffsets[i]); writer.WriteCompressedInteger((uint)moveNextMethodRid); } _customDebugInformationTable.Add(new CustomDebugInformationRow { Parent = HasCustomDebugInformation(HasCustomDebugInformationTag.MethodDef, moveNextMethodRid), Kind = (uint)_debugHeapsOpt.GetGuidIndex(PortableCustomDebugInfoKinds.AsyncMethodSteppingInformationBlob), Value = _debugHeapsOpt.GetBlobIndex(writer), }); }
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(); } }
public void SerializeDebugInfo(IMethodBody methodBody, uint localSignatureToken, CustomDebugInfoWriter customDebugInfoWriter) { Debug.Assert(_metadataWriter != null); bool isIterator = methodBody.StateMachineTypeName != null; bool emitDebugInfo = isIterator || methodBody.HasAnySequencePoints; if (!emitDebugInfo) { return; } uint methodToken = _metadataWriter.GetMethodToken(methodBody.MethodDefinition); OpenMethod(methodToken); var localScopes = methodBody.LocalScopes; // CCI originally didn't have the notion of the default scope that is open // when a method is opened. In order to reproduce CSC PDBs, this must be added. Otherwise // a seemingly unnecessary scope that contains only other scopes is put in the PDB. if (localScopes.Length > 0) { this.DefineScopeLocals(localScopes[0], localSignatureToken); } // NOTE: This is an attempt to match Dev10's apparent behavior. For iterator methods (i.e. the method // that appears in source, not the synthesized ones), Dev10 only emits the ForwardIterator and IteratorLocal // custom debug info (e.g. there will be no information about the usings that were in scope). if (!isIterator) { IMethodDefinition forwardToMethod; if (customDebugInfoWriter.ShouldForwardNamespaceScopes(Context, methodBody, methodToken, out forwardToMethod)) { if (forwardToMethod != null) { UsingNamespace("@" + _metadataWriter.GetMethodToken(forwardToMethod), methodBody.MethodDefinition); } // otherwise, the forwarding is done via custom debug info } else { this.DefineNamespaceScopes(methodBody); } } DefineLocalScopes(localScopes, localSignatureToken); EmitSequencePoints(methodBody.GetSequencePoints()); AsyncMethodBodyDebugInfo asyncDebugInfo = methodBody.AsyncDebugInfo; if (asyncDebugInfo != null) { SetAsyncInfo( methodToken, _metadataWriter.GetMethodToken(asyncDebugInfo.KickoffMethod), asyncDebugInfo.CatchHandlerOffset, asyncDebugInfo.YieldOffsets, asyncDebugInfo.ResumeOffsets); } var compilationOptions = Context.ModuleBuilder.CommonCompilation.Options; // We need to avoid emitting CDI DynamicLocals = 5 and EditAndContinueLocalSlotMap = 6 for files processed by WinMDExp until // bug #1067635 is fixed and available in SDK. bool suppressNewCustomDebugInfo = !compilationOptions.ExtendedCustomDebugInformation || (compilationOptions.OutputKind == OutputKind.WindowsRuntimeMetadata); bool emitEncInfo = compilationOptions.EnableEditAndContinue && !_metadataWriter.IsFullMetadata; bool emitExternNamespaces; byte[] blob = customDebugInfoWriter.SerializeMethodDebugInfo(Context, methodBody, methodToken, emitEncInfo, suppressNewCustomDebugInfo, out emitExternNamespaces); if (blob != null) { DefineCustomMetadata("MD2", blob); } if (emitExternNamespaces) { this.DefineAssemblyReferenceAliases(); } // TODO: it's not clear why we are closing a scope here with IL length: CloseScope(methodBody.IL.Length); CloseMethod(); }
/// <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) => moduleBuilder.GetOrAddDebugDocument(path, basePath, CreateDebugDocumentForFile); } ILBuilder il = new ILBuilder(moduleBuilder, localSlotManager, optimizations); try { Cci.AsyncMethodBodyDebugInfo asyncDebugInfo = 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 <Cci.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, asyncDebugInfo)); } 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 void SerializeDebugInfo(IMethodBody methodBody, uint localSignatureToken, CustomDebugInfoWriter customDebugInfoWriter) { Debug.Assert(peWriter != null); bool isIterator = methodBody.IteratorClassName != null; bool emitDebugInfo = isIterator || methodBody.HasAnyLocations; if (!emitDebugInfo) { return; } uint methodToken = peWriter.GetMethodToken(methodBody.MethodDefinition); OpenMethod(methodToken); var localScopes = methodBody.LocalScopes; // CCI originally didn't have the notion of the default scope that is open // when a method is opened. In order to reproduce CSC PDBs, this must be added. Otherwise // a seemingly unnecessary scope that contains only other scopes is put in the PDB. if (localScopes.Length > 0) { this.DefineScopeLocals(localScopes[0], localSignatureToken); } // NOTE: This is an attempt to match Dev10's apparent behavior. For iterator methods (i.e. the method // that appears in source, not the synthesized ones), Dev10 only emits the ForwardIterator and IteratorLocal // custom debug info (e.g. there will be no information about the usings that were in scope). if (!isIterator) { IMethodDefinition forwardToMethod; if (customDebugInfoWriter.ShouldForwardNamespaceScopes(methodBody, methodToken, out forwardToMethod)) { if (forwardToMethod != null) { string usingString = "@" + peWriter.GetMethodToken(forwardToMethod); Debug.Assert(!peWriter.IsUsingStringTooLong(usingString)); UsingNamespace(usingString, methodBody.MethodDefinition.Name); } // otherwise, the forwarding is done via custom debug info } else { this.DefineNamespaceScopes(methodBody); } } DefineLocalScopes(localScopes, localSignatureToken); EmitSequencePoints(methodBody.GetSequencePoints()); AsyncMethodBodyDebugInfo asyncDebugInfo = methodBody.AsyncMethodDebugInfo; if (asyncDebugInfo != null) { SetAsyncInfo( methodToken, peWriter.GetMethodToken(asyncDebugInfo.KickoffMethod), asyncDebugInfo.CatchHandlerOffset, asyncDebugInfo.YieldOffsets, asyncDebugInfo.ResumeOffsets); } var module = peWriter.Context.Module; bool emitExternNamespaces; byte[] blob = customDebugInfoWriter.SerializeMethodDebugInfo(module, methodBody, methodToken, out emitExternNamespaces); if (blob != null) { DefineCustomMetadata("MD2", blob); } if (emitExternNamespaces) { this.DefineExternAliases(module); } // TODO: it's not clear why we are closing a scope here with IL length: CloseScope((uint)methodBody.IL.Length); CloseMethod(); }
private void SerializeMethodDebugInfo(IMethodBody bodyOpt, int methodRid, StandaloneSignatureHandle localSignatureHandleOpt, ref LocalVariableHandle lastLocalVariableHandle, ref LocalConstantHandle lastLocalConstantHandle) { if (bodyOpt == null) { _debugMetadataOpt.AddMethodDebugInformation(default(DocumentHandle), default(BlobHandle)); return; } bool isIterator = bodyOpt.StateMachineTypeName != null; bool emitDebugInfo = isIterator || bodyOpt.HasAnySequencePoints; if (!emitDebugInfo) { _debugMetadataOpt.AddMethodDebugInformation(default(DocumentHandle), default(BlobHandle)); return; } MethodDefinitionHandle methodHandle = MetadataTokens.MethodDefinitionHandle(methodRid); IImportScope bodyImportScope = bodyOpt.ImportScope; ImportScopeHandle importScopeHandle = (bodyImportScope != null) ? GetImportScopeIndex(bodyImportScope, _scopeIndex) : default(ImportScopeHandle); // documents & sequence points: ArrayBuilder <Cci.SequencePoint> sequencePoints = ArrayBuilder <Cci.SequencePoint> .GetInstance(); bodyOpt.GetSequencePoints(sequencePoints); BlobHandle sequencePointsBlob = SerializeSequencePoints(localSignatureHandleOpt, sequencePoints.ToImmutableAndFree(), _documentIndex, out DocumentHandle singleDocumentHandle); _debugMetadataOpt.AddMethodDebugInformation(document: singleDocumentHandle, sequencePoints: sequencePointsBlob); // Unlike native PDB we don't emit an empty root scope. // scopes are already ordered by StartOffset ascending then by EndOffset descending (the longest scope first). if (bodyOpt.LocalScopes.Length == 0) { // TODO: the compiler should produce a scope for each debuggable method _debugMetadataOpt.AddLocalScope( method: methodHandle, importScope: importScopeHandle, variableList: NextHandle(lastLocalVariableHandle), constantList: NextHandle(lastLocalConstantHandle), startOffset: 0, length: bodyOpt.IL.Length); } else { foreach (LocalScope scope in bodyOpt.LocalScopes) { _debugMetadataOpt.AddLocalScope( method: methodHandle, importScope: importScopeHandle, variableList: NextHandle(lastLocalVariableHandle), constantList: NextHandle(lastLocalConstantHandle), startOffset: scope.StartOffset, length: scope.Length); foreach (ILocalDefinition local in scope.Variables) { Debug.Assert(local.SlotIndex >= 0); lastLocalVariableHandle = _debugMetadataOpt.AddLocalVariable( attributes: local.PdbAttributes, index: local.SlotIndex, name: _debugMetadataOpt.GetOrAddString(local.Name)); SerializeLocalInfo(local, lastLocalVariableHandle); } foreach (ILocalDefinition constant in scope.Constants) { CodeAnalysis.CodeGen.MetadataConstant mdConstant = constant.CompileTimeValue; Debug.Assert(mdConstant != null); lastLocalConstantHandle = _debugMetadataOpt.AddLocalConstant( name: _debugMetadataOpt.GetOrAddString(constant.Name), signature: SerializeLocalConstantSignature(constant)); SerializeLocalInfo(constant, lastLocalConstantHandle); } } } AsyncMethodBodyDebugInfo asyncDebugInfo = bodyOpt.AsyncDebugInfo; if (asyncDebugInfo != null) { _debugMetadataOpt.AddStateMachineMethod( moveNextMethod: methodHandle, kickoffMethod: GetMethodDefinitionHandle(asyncDebugInfo.KickoffMethod)); SerializeAsyncMethodSteppingInfo(asyncDebugInfo, methodHandle); } SerializeStateMachineLocalScopes(bodyOpt, methodHandle); // delta doesn't need this information - we use information recorded by previous generation emit if (Context.Module.CommonCompilation.Options.EnableEditAndContinue && IsFullMetadata) { SerializeEncMethodDebugInformation(bodyOpt, methodHandle); } }