/// <summary> /// Work-around for a bug in diasymreader. /// </summary> /// <param name="rootScope"></param> /// <remarks> /// There should always be at least one explicit scope written, since this is how the writer determines /// the end offset for the root scope (and hence the maximum offset permitted by any sequence point - /// other will be silently removed). But if a scope has no locals or child scopes in it, diasymreader will /// omit it, and we won't see it in the reader. So if we read a root scope with nothing inside of it, /// we'll reconstruct the child scope we know must have originally been written there. /// </remarks> private void WorkAroundDiasymreaderScopeBug(Scope rootScope) { if ((rootScope.scopes.Count > 0) || (rootScope.locals.Count > 0) || (rootScope.usedNamespaces.Count > 0) || (rootScope.constants.Count > 0)) { // There is something inside this scope - there shouldn't be any problem with diasymreader // removing it. return; } // We've got a root scope with nothing in it. Reconstruct the explicit scope we know must have // been written here. Scope child = new Scope(); child.startOffset = rootScope.startOffset; child.endOffset = rootScope.endOffset; child.isReconstructedDueToDiasymreaderBug = true; rootScope.scopes.Add(child); }
private void WriteScopesAndLocals(Scope scope, SymbolToken? localSigToken, Method method) { // If this scope is marked implicit then we won't explicitly write it out // This is usually used for the root scope if (!scope.isImplicit) m_writer.OpenScope(scope.startOffset); foreach (Variable localVar in scope.locals) { // Note that local variables can have a start/end offset within their enclosing scope. // Infortunately ISymUnmanagedVariable.GetStartOffset() returns E_NOTIMPL, so we // can't tell whether this was used or not. Passing 0 says to use the start/end for // the entire scope (which is probably usually the case anyway). // Diasymreader.dll always stores local signatures by token, and if we use the V1 // API will attempt to emit a new token to the metadata (which we don't support here). // IldbSymbols.dll on the other hand always stores signatures inline, and doesn't support // the V2 API. RefEmit uses the V1 API, so it's fine to use it here for ILDB too. if (m_symFormat == SymbolFormat.PDB) { if (localSigToken.HasValue) { m_writer.DefineLocalVariable( localVar.name, localVar.attributes, localSigToken.Value, (int)SymAddressKind.ILOffset, localVar.ilIndex, 0, 0, 0, 0); // start/end offsets - get from current scope } else { // We want to handle this case as gracefully since there is a bug in the C# compiler that // can prevent us from getting the local var sig token. // Note that we don't want to just use the V1 API with PDBs because we're not saving the // metadata, and so the call to ISymUnmanagedVariable::GetSignature will just fail because // the token will be invalid. if (!method.hasInvalidMethodBody) throw new FormatException("Missing localVarsigToken in a method without hasInvalidMethodBody set"); } } else { byte[] sig = Util.ToByteArray(localVar.signature); m_writer.DefineLocalVariable( localVar.name, (FieldAttributes)localVar.attributes, sig, SymAddressKind.ILOffset, localVar.ilIndex, 0, 0, 0, 0); // start/end offsets - get from current scope } } foreach (Constant constant in scope.constants) { m_writer.DefineConstant(constant.name, constant.value, Util.ToByteArray(constant.signature)); } // Now recursively write out any child scopes foreach (Scope childScope in scope.scopes) { WriteScopesAndLocals(childScope, localSigToken, method); } foreach (Namespace ns in scope.usedNamespaces) { m_writer.UsingNamespace(ns.name); } if (!scope.isImplicit) m_writer.CloseScope(scope.endOffset); }
private Scope ReadScope(ISymbolScope scope) { Scope scopeData = new Scope(); // If this is the root scope, then it was created implicitly and should not be explicitly // opened by a writer if (scope.Parent == null) { scopeData.isImplicit = true; } scopeData.startOffset = scope.StartOffset; scopeData.endOffset = scope.EndOffset; // Read the locals, constants and namespaces in this scope (may be empty) scopeData.locals = ReadLocals(scope); scopeData.constants = ReadConstants(scope); scopeData.usedNamespaces = ReadUsedNamespaces(scope); // Read the child scopes recursively scopeData.scopes = new List<Scope>(); foreach (ISymbolScope child in scope.GetChildren()) { Scope childData = ReadScope(child); scopeData.scopes.Add(childData); } return scopeData; }