/// <summary>
        /// Returns symbols for the locals emitted in the original method,
        /// based on the local signatures from the IL and the names and
        /// slots from the PDB. The actual locals are needed to ensure the
        /// local slots in the generated method match the original.
        /// </summary>
        public static void GetLocals(
            ArrayBuilder <TLocalSymbol> builder,
            EESymbolProvider <TTypeSymbol, TLocalSymbol> symbolProvider,
            ImmutableArray <string> names,
            ImmutableArray <LocalInfo <TTypeSymbol> > localInfo,
            ImmutableDictionary <int, ImmutableArray <bool> > dynamicLocalMapOpt,
            ImmutableDictionary <int, ImmutableArray <string> > tupleLocalConstantMapOpt)
        {
            if (localInfo.Length == 0)
            {
                // When debugging a .dmp without a heap, localInfo will be empty although
                // names may be non-empty if there is a PDB. Since there's no type info, the
                // locals are dropped. Note this means the local signature of any generated
                // method will not match the original signature, so new locals will overlap
                // original locals. That is ok since there is no live process for the debugger
                // to update (any modified values exist in the debugger only).
                return;
            }

            Debug.Assert(localInfo.Length >= names.Length);

            for (int i = 0; i < localInfo.Length; i++)
            {
                string name = (i < names.Length) ? names[i] : null;

                var dynamicFlags = default(ImmutableArray <bool>);
                dynamicLocalMapOpt?.TryGetValue(i, out dynamicFlags);

                var tupleElementNames = default(ImmutableArray <string>);
                tupleLocalConstantMapOpt?.TryGetValue(i, out tupleElementNames);

                builder.Add(symbolProvider.GetLocalVariable(name, i, localInfo[i], dynamicFlags, tupleElementNames));
            }
        }
        private static bool TryCreateImportRecordFromCSharpImportString(EESymbolProvider <TTypeSymbol, TLocalSymbol> symbolProvider, string importString, out ImportRecord record)
        {
            ImportTargetKind targetKind;
            string           externAlias;
            string           alias;
            string           targetString;

            if (CustomDebugInfoReader.TryParseCSharpImportString(importString, out alias, out externAlias, out targetString, out targetKind))
            {
                ITypeSymbol type = null;
                if (targetKind == ImportTargetKind.Type)
                {
                    type         = symbolProvider.GetTypeSymbolForSerializedType(targetString);
                    targetString = null;
                }

                record = new ImportRecord(
                    targetKind: targetKind,
                    alias: alias,
                    targetType: type,
                    targetString: targetString,
                    targetAssembly: null,
                    targetAssemblyAlias: externAlias);

                return(true);
            }

            record = default(ImportRecord);
            return(false);
        }
Beispiel #3
0
        public static MethodDebugInfo <TTypeSymbol, TLocalSymbol> ReadFromPortable(
            MetadataReader reader,
            int methodToken,
            int ilOffset,
            EESymbolProvider <TTypeSymbol, TLocalSymbol>?symbolProvider,
            bool isVisualBasicMethod)
        {
            ImmutableDictionary <int, ImmutableArray <bool> >?   dynamicLocalMap;
            ImmutableDictionary <int, ImmutableArray <string?> >?tupleLocalMap;
            ImmutableArray <ImmutableArray <ImportRecord> >      importGroups;
            ImmutableArray <ExternAliasRecord> externAliases;
            ImmutableArray <string>            localVariableNames;
            ImmutableArray <TLocalSymbol>      localConstants;
            ILSpan reuseSpan;

            var methodHandle = GetDeltaRelativeMethodDefinitionHandle(reader, methodToken);

            // TODO: only null in DTEE case where we looking for default namesapace
            if (symbolProvider != null)
            {
                ReadLocalScopeInformation(
                    reader,
                    methodHandle,
                    ilOffset,
                    symbolProvider,
                    isVisualBasicMethod,
                    out importGroups,
                    out externAliases,
                    out localVariableNames,
                    out dynamicLocalMap,
                    out tupleLocalMap,
                    out localConstants,
                    out reuseSpan);
            }
            else
            {
                dynamicLocalMap    = null;
                tupleLocalMap      = null;
                importGroups       = ImmutableArray <ImmutableArray <ImportRecord> > .Empty;
                externAliases      = ImmutableArray <ExternAliasRecord> .Empty;
                localVariableNames = ImmutableArray <string> .Empty;
                localConstants     = ImmutableArray <TLocalSymbol> .Empty;
                reuseSpan          = ILSpan.MaxValue;
            }

            ReadMethodCustomDebugInformation(reader, methodHandle, out var hoistedLocalScopes, out var defaultNamespace);

            return(new MethodDebugInfo <TTypeSymbol, TLocalSymbol>(
                       hoistedLocalScopes,
                       importGroups,
                       externAliases,
                       dynamicLocalMap,
                       tupleLocalMap,
                       defaultNamespace,
                       localVariableNames,
                       localConstants,
                       reuseSpan));
        }
        private static void GetConstants(
            ArrayBuilder <TLocalSymbol> builder,
            EESymbolProvider <TTypeSymbol, TLocalSymbol> symbolProvider,
            ArrayBuilder <ISymUnmanagedScope> scopes,
            ImmutableDictionary <string, ImmutableArray <bool> > dynamicLocalConstantMapOpt,
            ImmutableDictionary <LocalNameAndScope, ImmutableArray <string> > tupleLocalConstantMapOpt)
        {
            foreach (var scope in scopes)
            {
                foreach (var constant in scope.GetConstants())
                {
                    string name      = constant.GetName();
                    object rawValue  = constant.GetValue();
                    var    signature = constant.GetSignature().ToImmutableArray();

                    TTypeSymbol type;
                    try
                    {
                        type = symbolProvider.DecodeLocalVariableType(signature);
                    }
                    catch (Exception e) when(e is UnsupportedSignatureContent || e is BadImageFormatException)
                    {
                        // ignore
                        continue;
                    }

                    if (type.Kind == SymbolKind.ErrorType)
                    {
                        continue;
                    }

                    ConstantValue constantValue = PdbHelpers.GetSymConstantValue(type, rawValue);

                    // TODO (https://github.com/dotnet/roslyn/issues/1815): report error properly when the symbol is used
                    if (constantValue.IsBad)
                    {
                        continue;
                    }

                    var dynamicFlags = default(ImmutableArray <bool>);
                    if (dynamicLocalConstantMapOpt != null)
                    {
                        dynamicLocalConstantMapOpt.TryGetValue(name, out dynamicFlags);
                    }

                    var tupleElementNames = default(ImmutableArray <string>);
                    if (tupleLocalConstantMapOpt != null)
                    {
                        int scopeStart = scope.GetStartOffset();
                        int scopeEnd   = scope.GetEndOffset();
                        tupleLocalConstantMapOpt.TryGetValue(new LocalNameAndScope(name, scopeStart, scopeEnd), out tupleElementNames);
                    }

                    builder.Add(symbolProvider.GetLocalConstant(name, type, constantValue, dynamicFlags, tupleElementNames));
                }
            }
        }
        private static void PopulateImports(
            MetadataReader reader,
            ImportScopeHandle handle,
            EESymbolProvider <TTypeSymbol, TLocalSymbol> symbolProvider,
            bool isVisualBasicMethod,
            ArrayBuilder <ImmutableArray <ImportRecord> > importGroupsBuilder,
            ArrayBuilder <ExternAliasRecord> externAliasesBuilder
            )
        {
            var importGroupBuilder = ArrayBuilder <ImportRecord> .GetInstance();

            while (!handle.IsNil)
            {
                var importScope = reader.GetImportScope(handle);

                try
                {
                    PopulateImports(
                        reader,
                        importScope,
                        symbolProvider,
                        importGroupBuilder,
                        externAliasesBuilder
                        );
                }
                catch (BadImageFormatException)
                {
                    // ignore invalid imports
                }

                // Portable PDBs represent project-level scope as the root of the chain of scopes.
                // This scope might contain aliases for assembly references, but is not considered
                // to be part of imports groups.
                if (isVisualBasicMethod || !importScope.Parent.IsNil)
                {
                    importGroupsBuilder.Add(importGroupBuilder.ToImmutable());
                    importGroupBuilder.Clear();
                }
                else
                {
                    // C# currently doesn't support global imports in PDBs
                    // https://github.com/dotnet/roslyn/issues/21862
                    Debug.Assert(importGroupBuilder.Count == 0);
                }

                handle = importScope.Parent;
            }

            importGroupBuilder.Free();
        }
        private static void PopulateImports(
            MetadataReader reader,
            ImportScopeHandle handle,
            EESymbolProvider <TTypeSymbol, TLocalSymbol> symbolProvider,
            bool isVisualBasicMethod,
            ArrayBuilder <ImmutableArray <ImportRecord> > importGroupsBuilder,
            ArrayBuilder <ExternAliasRecord> externAliasesBuilder)
        {
            var importGroupBuilder = ArrayBuilder <ImportRecord> .GetInstance();

            while (!handle.IsNil)
            {
                var importScope = reader.GetImportScope(handle);

                try
                {
                    PopulateImports(reader, importScope, symbolProvider, importGroupBuilder, externAliasesBuilder);
                }
                catch (BadImageFormatException)
                {
                    // ignore invalid imports
                }

                // VB always expects two import groups (even if they are empty).
                // TODO: consider doing this for C# as well and handle empty groups in the binder.
                if (isVisualBasicMethod || importGroupBuilder.Count > 0)
                {
                    importGroupsBuilder.Add(importGroupBuilder.ToImmutable());
                    importGroupBuilder.Clear();
                }

                handle = importScope.Parent;
            }

            importGroupBuilder.Free();
        }
        /// <exception cref="BadImageFormatException">Invalid data format.</exception>
        private static void PopulateImports(
            MetadataReader reader,
            ImportScope importScope,
            EESymbolProvider <TTypeSymbol, TLocalSymbol> symbolProvider,
            ArrayBuilder <ImportRecord> importGroupBuilder,
            ArrayBuilder <ExternAliasRecord> externAliasesBuilder
            )
        {
            foreach (ImportDefinition import in importScope.GetImports())
            {
                switch (import.Kind)
                {
                case ImportDefinitionKind.ImportNamespace:
                    importGroupBuilder.Add(
                        new ImportRecord(
                            ImportTargetKind.Namespace,
                            targetString: ReadUtf8String(reader, import.TargetNamespace)
                            )
                        );
                    break;

                case ImportDefinitionKind.ImportAssemblyNamespace:
                    importGroupBuilder.Add(
                        new ImportRecord(
                            ImportTargetKind.Namespace,
                            targetString: ReadUtf8String(reader, import.TargetNamespace),
                            targetAssembly: symbolProvider.GetReferencedAssembly(
                                import.TargetAssembly
                                )
                            )
                        );
                    break;

                case ImportDefinitionKind.ImportType:
                    importGroupBuilder.Add(
                        new ImportRecord(
                            ImportTargetKind.Type,
                            targetType: symbolProvider.GetType(import.TargetType)
                            )
                        );
                    break;

                case ImportDefinitionKind.ImportXmlNamespace:
                    importGroupBuilder.Add(
                        new ImportRecord(
                            ImportTargetKind.XmlNamespace,
                            alias: ReadUtf8String(reader, import.Alias),
                            targetString: ReadUtf8String(reader, import.TargetNamespace)
                            )
                        );
                    break;

                case ImportDefinitionKind.ImportAssemblyReferenceAlias:
                    importGroupBuilder.Add(
                        new ImportRecord(
                            ImportTargetKind.Assembly,
                            alias: ReadUtf8String(reader, import.Alias)
                            )
                        );
                    break;

                case ImportDefinitionKind.AliasAssemblyReference:
                    externAliasesBuilder.Add(
                        new ExternAliasRecord(
                            alias: ReadUtf8String(reader, import.Alias),
                            targetAssembly: symbolProvider.GetReferencedAssembly(
                                import.TargetAssembly
                                )
                            )
                        );
                    break;

                case ImportDefinitionKind.AliasNamespace:
                    importGroupBuilder.Add(
                        new ImportRecord(
                            ImportTargetKind.Namespace,
                            alias: ReadUtf8String(reader, import.Alias),
                            targetString: ReadUtf8String(reader, import.TargetNamespace)
                            )
                        );
                    break;

                case ImportDefinitionKind.AliasAssemblyNamespace:
                    importGroupBuilder.Add(
                        new ImportRecord(
                            ImportTargetKind.Namespace,
                            alias: ReadUtf8String(reader, import.Alias),
                            targetString: ReadUtf8String(reader, import.TargetNamespace),
                            targetAssembly: symbolProvider.GetReferencedAssembly(
                                import.TargetAssembly
                                )
                            )
                        );
                    break;

                case ImportDefinitionKind.AliasType:
                    importGroupBuilder.Add(
                        new ImportRecord(
                            ImportTargetKind.Type,
                            alias: ReadUtf8String(reader, import.Alias),
                            targetType: symbolProvider.GetType(import.TargetType)
                            )
                        );
                    break;
                }
            }
        }
        private static void ReadLocalScopeInformation(
            MetadataReader reader,
            MethodDefinitionHandle methodHandle,
            int ilOffset,
            EESymbolProvider <TTypeSymbol, TLocalSymbol> symbolProvider,
            bool isVisualBasicMethod,
            out ImmutableArray <ImmutableArray <ImportRecord> > importGroups,
            out ImmutableArray <ExternAliasRecord> externAliases,
            out ImmutableArray <string> localVariableNames,
            out ImmutableDictionary <int, ImmutableArray <bool> >?dynamicLocalMap,
            out ImmutableDictionary <int, ImmutableArray <string?> >?tupleLocalMap,
            out ImmutableArray <TLocalSymbol> localConstants,
            out ILSpan reuseSpan
            )
        {
            var localVariableNamesBuilder = ArrayBuilder <string> .GetInstance();

            var localConstantsBuilder = ArrayBuilder <TLocalSymbol> .GetInstance();

            ImmutableDictionary <int, ImmutableArray <bool> > .Builder?   lazyDynamicLocalsBuilder = null;
            ImmutableDictionary <int, ImmutableArray <string?> > .Builder?lazyTupleLocalsBuilder   =
                null;

            var  innerMostImportScope = default(ImportScopeHandle);
            uint reuseSpanStart       = 0;
            uint reuseSpanEnd         = uint.MaxValue;

            try
            {
                foreach (var scopeHandle in reader.GetLocalScopes(methodHandle))
                {
                    try
                    {
                        var scope = reader.GetLocalScope(scopeHandle);
                        if (ilOffset < scope.StartOffset)
                        {
                            // scopes are sorted by StartOffset, hence all scopes that follow can't contain ilOffset
                            reuseSpanEnd = Math.Min(reuseSpanEnd, (uint)scope.StartOffset);
                            break;
                        }

                        if (ilOffset >= scope.EndOffset)
                        {
                            // ilOffset is not in this scope, go to next one
                            reuseSpanStart = Math.Max(reuseSpanStart, (uint)scope.EndOffset);
                            continue;
                        }

                        // reuse span is a subspan of the inner-most scope containing the IL offset:
                        reuseSpanStart = Math.Max(reuseSpanStart, (uint)scope.StartOffset);
                        reuseSpanEnd   = Math.Min(reuseSpanEnd, (uint)scope.EndOffset);

                        // imports (use the inner-most):
                        innerMostImportScope = scope.ImportScope;

                        // locals (from all contained scopes):
                        foreach (var variableHandle in scope.GetLocalVariables())
                        {
                            var variable = reader.GetLocalVariable(variableHandle);
                            if ((variable.Attributes & LocalVariableAttributes.DebuggerHidden) != 0)
                            {
                                continue;
                            }

                            localVariableNamesBuilder.SetItem(
                                variable.Index,
                                reader.GetString(variable.Name)
                                );

                            var dynamicFlags = ReadDynamicCustomDebugInformation(
                                reader,
                                variableHandle
                                );
                            if (!dynamicFlags.IsDefault)
                            {
                                lazyDynamicLocalsBuilder ??= ImmutableDictionary.CreateBuilder <
                                    int,
                                    ImmutableArray <bool>
                                    >();
                                lazyDynamicLocalsBuilder[variable.Index] = dynamicFlags;
                            }

                            var tupleElementNames = ReadTupleCustomDebugInformation(
                                reader,
                                variableHandle
                                );
                            if (!tupleElementNames.IsDefault)
                            {
                                lazyTupleLocalsBuilder ??= ImmutableDictionary.CreateBuilder <
                                    int,
                                    ImmutableArray <string?>
                                    >();
                                lazyTupleLocalsBuilder[variable.Index] = tupleElementNames;
                            }
                        }

                        // constants (from all contained scopes):
                        foreach (var constantHandle in scope.GetLocalConstants())
                        {
                            var constant = reader.GetLocalConstant(constantHandle);

                            var sigReader = reader.GetBlobReader(constant.Signature);
                            symbolProvider.DecodeLocalConstant(
                                ref sigReader,
                                out var typeSymbol,
                                out var value
                                );

                            var name         = reader.GetString(constant.Name);
                            var dynamicFlags = ReadDynamicCustomDebugInformation(
                                reader,
                                constantHandle
                                );
                            var tupleElementNames = ReadTupleCustomDebugInformation(
                                reader,
                                constantHandle
                                );
                            localConstantsBuilder.Add(
                                symbolProvider.GetLocalConstant(
                                    name,
                                    typeSymbol,
                                    value,
                                    dynamicFlags,
                                    tupleElementNames
                                    )
                                );
                        }
                    }
                    catch (Exception e)
                        when(e is UnsupportedSignatureContent || e is BadImageFormatException)
                        {
                            // ignore scopes with invalid data
                        }
                }
            }
            finally
            {
                localVariableNames = localVariableNamesBuilder.ToImmutableAndFree();
                localConstants     = localConstantsBuilder.ToImmutableAndFree();
                dynamicLocalMap    = lazyDynamicLocalsBuilder?.ToImmutable();
                tupleLocalMap      = lazyTupleLocalsBuilder?.ToImmutable();
                reuseSpan          = new ILSpan(reuseSpanStart, reuseSpanEnd);
            }

            var importGroupsBuilder = ArrayBuilder <ImmutableArray <ImportRecord> > .GetInstance();

            var externAliasesBuilder = ArrayBuilder <ExternAliasRecord> .GetInstance();

            if (!innerMostImportScope.IsNil)
            {
                PopulateImports(
                    reader,
                    innerMostImportScope,
                    symbolProvider,
                    isVisualBasicMethod,
                    importGroupsBuilder,
                    externAliasesBuilder
                    );
            }

            importGroups  = importGroupsBuilder.ToImmutableAndFree();
            externAliases = externAliasesBuilder.ToImmutableAndFree();
        }
        public unsafe static MethodDebugInfo <TTypeSymbol, TLocalSymbol> ReadMethodDebugInfo(
            ISymUnmanagedReader3 symReader,
            EESymbolProvider <TTypeSymbol, TLocalSymbol> symbolProviderOpt, // TODO: only null in DTEE case where we looking for default namesapace
            int methodToken,
            int methodVersion,
            int ilOffset,
            bool isVisualBasicMethod)
        {
            // no symbols
            if (symReader == null)
            {
                return(None);
            }

            if (symReader is ISymUnmanagedReader5 symReader5)
            {
                int hr = symReader5.GetPortableDebugMetadataByVersion(methodVersion, out byte *metadata, out int size);
                ThrowExceptionForHR(hr);

                if (hr == S_OK)
                {
                    var mdReader = new MetadataReader(metadata, size);
                    try
                    {
                        return(ReadFromPortable(mdReader, methodToken, ilOffset, symbolProviderOpt, isVisualBasicMethod));
                    }
                    catch (BadImageFormatException)
                    {
                        // bad CDI, ignore
                        return(None);
                    }
                }
            }

            var allScopes = ArrayBuilder <ISymUnmanagedScope> .GetInstance();

            var containingScopes = ArrayBuilder <ISymUnmanagedScope> .GetInstance();

            try
            {
                var symMethod = symReader.GetMethodByVersion(methodToken, methodVersion);
                if (symMethod != null)
                {
                    symMethod.GetAllScopes(allScopes, containingScopes, ilOffset, isScopeEndInclusive: isVisualBasicMethod);
                }

                ImmutableArray <ImmutableArray <ImportRecord> > importRecordGroups;
                ImmutableArray <ExternAliasRecord> externAliasRecords;
                string defaultNamespaceName;

                if (isVisualBasicMethod)
                {
                    ReadVisualBasicImportsDebugInfo(
                        symReader,
                        methodToken,
                        methodVersion,
                        out importRecordGroups,
                        out defaultNamespaceName);

                    externAliasRecords = ImmutableArray <ExternAliasRecord> .Empty;
                }
                else
                {
                    Debug.Assert(symbolProviderOpt != null);

                    ReadCSharpNativeImportsInfo(
                        symReader,
                        symbolProviderOpt,
                        methodToken,
                        methodVersion,
                        out importRecordGroups,
                        out externAliasRecords);

                    defaultNamespaceName = "";
                }

                // VB should read hoisted scope information from local variables:
                var hoistedLocalScopeRecords = isVisualBasicMethod ?
                                               default(ImmutableArray <HoistedLocalScopeRecord>) :
                                               ImmutableArray <HoistedLocalScopeRecord> .Empty;

                ImmutableDictionary <int, ImmutableArray <bool> >    dynamicLocalMap                    = null;
                ImmutableDictionary <string, ImmutableArray <bool> > dynamicLocalConstantMap            = null;
                ImmutableDictionary <int, ImmutableArray <string> >  tupleLocalMap                      = null;
                ImmutableDictionary <LocalNameAndScope, ImmutableArray <string> > tupleLocalConstantMap = null;

                byte[] customDebugInfo = GetCustomDebugInfoBytes(symReader, methodToken, methodVersion);
                if (customDebugInfo != null)
                {
                    if (!isVisualBasicMethod)
                    {
                        var customDebugInfoRecord = CustomDebugInfoReader.TryGetCustomDebugInfoRecord(customDebugInfo, CustomDebugInfoKind.StateMachineHoistedLocalScopes);
                        if (!customDebugInfoRecord.IsDefault)
                        {
                            hoistedLocalScopeRecords = CustomDebugInfoReader.DecodeStateMachineHoistedLocalScopesRecord(customDebugInfoRecord)
                                                       .SelectAsArray(s => new HoistedLocalScopeRecord(s.StartOffset, s.Length));
                        }

                        GetCSharpDynamicLocalInfo(
                            customDebugInfo,
                            allScopes,
                            out dynamicLocalMap,
                            out dynamicLocalConstantMap);
                    }

                    GetTupleElementNamesLocalInfo(
                        customDebugInfo,
                        out tupleLocalMap,
                        out tupleLocalConstantMap);
                }

                var constantsBuilder = ArrayBuilder <TLocalSymbol> .GetInstance();

                if (symbolProviderOpt != null) // TODO
                {
                    GetConstants(constantsBuilder, symbolProviderOpt, containingScopes, dynamicLocalConstantMap, tupleLocalConstantMap);
                }

                var reuseSpan = GetReuseSpan(allScopes, ilOffset, isVisualBasicMethod);

                return(new MethodDebugInfo <TTypeSymbol, TLocalSymbol>(
                           hoistedLocalScopeRecords,
                           importRecordGroups,
                           externAliasRecords,
                           dynamicLocalMap,
                           tupleLocalMap,
                           defaultNamespaceName,
                           containingScopes.GetLocalNames(),
                           constantsBuilder.ToImmutableAndFree(),
                           reuseSpan));
            }
            catch (InvalidOperationException)
            {
                // bad CDI, ignore
                return(None);
            }
            finally
            {
                allScopes.Free();
                containingScopes.Free();
            }
        }
        private static void ReadCSharpNativeImportsInfo(
            ISymUnmanagedReader3 reader,
            EESymbolProvider <TTypeSymbol, TLocalSymbol> symbolProvider,
            int methodToken,
            int methodVersion,
            out ImmutableArray <ImmutableArray <ImportRecord> > importRecordGroups,
            out ImmutableArray <ExternAliasRecord> externAliasRecords)
        {
            ImmutableArray <string> externAliasStrings;

            var importStringGroups = CustomDebugInfoReader.GetCSharpGroupedImportStrings(
                methodToken,
                KeyValuePair.Create(reader, methodVersion),
                getMethodCustomDebugInfo: (token, arg) => GetCustomDebugInfoBytes(arg.Key, token, arg.Value),
                getMethodImportStrings: (token, arg) => GetImportStrings(arg.Key, token, arg.Value),
                externAliasStrings: out externAliasStrings);

            Debug.Assert(importStringGroups.IsDefault == externAliasStrings.IsDefault);

            ArrayBuilder <ImmutableArray <ImportRecord> > importRecordGroupBuilder = null;
            ArrayBuilder <ExternAliasRecord> externAliasRecordBuilder = null;

            if (!importStringGroups.IsDefault)
            {
                importRecordGroupBuilder = ArrayBuilder <ImmutableArray <ImportRecord> > .GetInstance(importStringGroups.Length);

                foreach (var importStringGroup in importStringGroups)
                {
                    var groupBuilder = ArrayBuilder <ImportRecord> .GetInstance(importStringGroup.Length);

                    foreach (var importString in importStringGroup)
                    {
                        ImportRecord record;
                        if (TryCreateImportRecordFromCSharpImportString(symbolProvider, importString, out record))
                        {
                            groupBuilder.Add(record);
                        }
                        else
                        {
                            Debug.WriteLine($"Failed to parse import string {importString}");
                        }
                    }
                    importRecordGroupBuilder.Add(groupBuilder.ToImmutableAndFree());
                }

                if (!externAliasStrings.IsDefault)
                {
                    externAliasRecordBuilder = ArrayBuilder <ExternAliasRecord> .GetInstance(externAliasStrings.Length);

                    foreach (string externAliasString in externAliasStrings)
                    {
                        string           alias;
                        string           externAlias;
                        string           target;
                        ImportTargetKind kind;
                        if (!CustomDebugInfoReader.TryParseCSharpImportString(externAliasString, out alias, out externAlias, out target, out kind))
                        {
                            Debug.WriteLine($"Unable to parse extern alias '{externAliasString}'");
                            continue;
                        }

                        Debug.Assert(kind == ImportTargetKind.Assembly, "Programmer error: How did a non-assembly get in the extern alias list?");
                        Debug.Assert(alias != null);       // Name of the extern alias.
                        Debug.Assert(externAlias == null); // Not used.
                        Debug.Assert(target != null);      // Name of the target assembly.

                        AssemblyIdentity targetIdentity;
                        if (!AssemblyIdentity.TryParseDisplayName(target, out targetIdentity))
                        {
                            Debug.WriteLine($"Unable to parse target of extern alias '{externAliasString}'");
                            continue;
                        }

                        externAliasRecordBuilder.Add(new ExternAliasRecord(alias, targetIdentity));
                    }
                }
            }

            importRecordGroups = importRecordGroupBuilder?.ToImmutableAndFree() ?? ImmutableArray <ImmutableArray <ImportRecord> > .Empty;
            externAliasRecords = externAliasRecordBuilder?.ToImmutableAndFree() ?? ImmutableArray <ExternAliasRecord> .Empty;
        }
Beispiel #11
0
        public unsafe static MethodDebugInfo <TTypeSymbol, TLocalSymbol> ReadMethodDebugInfo(
            ISymUnmanagedReader symReader,
            EESymbolProvider <TTypeSymbol, TLocalSymbol> symbolProviderOpt, // TODO: only null in DTEE case where we looking for default namesapace
            int methodToken,
            int methodVersion,
            int ilOffset,
            bool isVisualBasicMethod)
        {
            // no symbols
            if (symReader == null)
            {
                return(None);
            }

            var symReader4 = symReader as ISymUnmanagedReader4;

            if (symReader4 != null) // TODO: VB Portable PDBs
            {
                byte *metadata;
                int   size;

                // TODO: version
                int hr = symReader4.GetPortableDebugMetadata(out metadata, out size);
                SymUnmanagedReaderExtensions.ThrowExceptionForHR(hr);

                if (metadata != null)
                {
                    var mdReader = new MetadataReader(metadata, size);
                    try
                    {
                        return(ReadFromPortable(mdReader, methodToken, ilOffset, symbolProviderOpt, isVisualBasicMethod));
                    }
                    catch (BadImageFormatException)
                    {
                        // bad CDI, ignore
                        return(None);
                    }
                }
            }

            var allScopes = ArrayBuilder <ISymUnmanagedScope> .GetInstance();

            var containingScopes = ArrayBuilder <ISymUnmanagedScope> .GetInstance();

            try
            {
                ImmutableArray <HoistedLocalScopeRecord>        hoistedLocalScopeRecords;
                ImmutableArray <ImmutableArray <ImportRecord> > importRecordGroups;
                ImmutableArray <ExternAliasRecord> externAliasRecords;
                ImmutableDictionary <int, ImmutableArray <bool> >    dynamicLocalMap;
                ImmutableDictionary <string, ImmutableArray <bool> > dynamicLocalConstantMap;
                string defaultNamespaceName;

                var symMethod = symReader.GetMethodByVersion(methodToken, methodVersion);
                if (symMethod != null)
                {
                    symMethod.GetAllScopes(allScopes, containingScopes, ilOffset, isScopeEndInclusive: isVisualBasicMethod);
                }

                if (isVisualBasicMethod)
                {
                    ReadVisualBasicImportsDebugInfo(
                        symReader,
                        methodToken,
                        methodVersion,
                        out importRecordGroups,
                        out defaultNamespaceName);

                    hoistedLocalScopeRecords = ImmutableArray <HoistedLocalScopeRecord> .Empty;
                    externAliasRecords       = ImmutableArray <ExternAliasRecord> .Empty;
                    dynamicLocalMap          = null;
                    dynamicLocalConstantMap  = null;
                }
                else
                {
                    Debug.Assert(symbolProviderOpt != null);

                    ReadCSharpNativeImportsInfo(
                        symReader,
                        symbolProviderOpt,
                        methodToken,
                        methodVersion,
                        out importRecordGroups,
                        out externAliasRecords);

                    ReadCSharpNativeCustomDebugInfo(
                        symReader,
                        methodToken,
                        methodVersion,
                        allScopes,
                        out hoistedLocalScopeRecords,
                        out dynamicLocalMap,
                        out dynamicLocalConstantMap);

                    defaultNamespaceName = "";
                }

                var constantsBuilder = ArrayBuilder <TLocalSymbol> .GetInstance();

                if (symbolProviderOpt != null) // TODO
                {
                    GetConstants(constantsBuilder, symbolProviderOpt, containingScopes, dynamicLocalConstantMap);
                }

                var reuseSpan = GetReuseSpan(allScopes, ilOffset, isVisualBasicMethod);

                return(new MethodDebugInfo <TTypeSymbol, TLocalSymbol>(
                           hoistedLocalScopeRecords,
                           importRecordGroups,
                           externAliasRecords,
                           dynamicLocalMap,
                           defaultNamespaceName,
                           containingScopes.GetLocalNames(),
                           constantsBuilder.ToImmutableAndFree(),
                           reuseSpan));
            }
            catch (InvalidOperationException)
            {
                // bad CDI, ignore
                return(None);
            }
            finally
            {
                allScopes.Free();
                containingScopes.Free();
            }
        }
        /// <exception cref="BadImageFormatException">Invalid data format.</exception>
        public static MethodDebugInfo <TTypeSymbol, TLocalSymbol> ReadFromPortable(MetadataReader reader, int methodToken, int ilOffset, EESymbolProvider <TTypeSymbol, TLocalSymbol> symbolProvider, bool isVisualBasicMethod)
        {
            string defaultNamespace;
            ImmutableArray <HoistedLocalScopeRecord>            hoistedLocalScopes;
            ImmutableDictionary <int, ImmutableArray <bool> >   dynamicLocalMap;
            ImmutableDictionary <int, ImmutableArray <string> > tupleLocalMap;
            ImmutableArray <ImmutableArray <ImportRecord> >     importGroups;
            ImmutableArray <ExternAliasRecord> externAliases;
            ImmutableArray <string>            localVariableNames;
            ImmutableArray <TLocalSymbol>      localConstants;
            ILSpan reuseSpan;

            var methodHandle = (MethodDefinitionHandle)MetadataTokens.EntityHandle(methodToken);

            ReadLocalScopeInformation(
                reader,
                methodHandle,
                ilOffset,
                symbolProvider,
                isVisualBasicMethod,
                out importGroups,
                out externAliases,
                out localVariableNames,
                out dynamicLocalMap,
                out tupleLocalMap,
                out localConstants,
                out reuseSpan);
            ReadMethodCustomDebugInformation(reader, methodHandle, out hoistedLocalScopes, out defaultNamespace);

            return(new MethodDebugInfo <TTypeSymbol, TLocalSymbol>(
                       hoistedLocalScopes,
                       importGroups,
                       externAliases,
                       dynamicLocalMap,
                       tupleLocalMap,
                       defaultNamespace,
                       localVariableNames,
                       localConstants,
                       reuseSpan));
        }
        /// <exception cref="BadImageFormatException">Invalid data format.</exception>
        private static void PopulateImports(
            MetadataReader reader,
            ImportScopeHandle handle,
            EESymbolProvider <TTypeSymbol, TLocalSymbol> symbolProvider,
            bool isVisualBasicMethod,
            ArrayBuilder <ImmutableArray <ImportRecord> > importGroupsBuilder,
            ArrayBuilder <ExternAliasRecord> externAliasesBuilder)
        {
            var importGroupBuilder = ArrayBuilder <ImportRecord> .GetInstance();

            while (!handle.IsNil)
            {
                var importScope = reader.GetImportScope(handle);

                foreach (ImportDefinition import in importScope.GetImports())
                {
                    switch (import.Kind)
                    {
                    case ImportDefinitionKind.ImportNamespace:
                        importGroupBuilder.Add(new ImportRecord(
                                                   ImportTargetKind.Namespace,
                                                   targetString: ReadUtf8String(reader, import.TargetNamespace)));
                        break;

                    case ImportDefinitionKind.ImportAssemblyNamespace:
                        importGroupBuilder.Add(new ImportRecord(
                                                   ImportTargetKind.Namespace,
                                                   targetString: ReadUtf8String(reader, import.TargetNamespace),
                                                   targetAssembly: symbolProvider.GetReferencedAssembly(import.TargetAssembly)));
                        break;

                    case ImportDefinitionKind.ImportType:
                        importGroupBuilder.Add(new ImportRecord(
                                                   ImportTargetKind.Type,
                                                   targetType: symbolProvider.GetType(import.TargetType)));
                        break;

                    case ImportDefinitionKind.ImportXmlNamespace:
                        importGroupBuilder.Add(new ImportRecord(
                                                   ImportTargetKind.XmlNamespace,
                                                   alias: ReadUtf8String(reader, import.Alias),
                                                   targetString: ReadUtf8String(reader, import.TargetNamespace)));
                        break;

                    case ImportDefinitionKind.ImportAssemblyReferenceAlias:
                        importGroupBuilder.Add(new ImportRecord(
                                                   ImportTargetKind.Assembly,
                                                   alias: ReadUtf8String(reader, import.Alias)));
                        break;

                    case ImportDefinitionKind.AliasAssemblyReference:
                        externAliasesBuilder.Add(new ExternAliasRecord(
                                                     alias: ReadUtf8String(reader, import.Alias),
                                                     targetAssembly: symbolProvider.GetReferencedAssembly(import.TargetAssembly)));
                        break;

                    case ImportDefinitionKind.AliasNamespace:
                        importGroupBuilder.Add(new ImportRecord(
                                                   ImportTargetKind.Namespace,
                                                   alias: ReadUtf8String(reader, import.Alias),
                                                   targetString: ReadUtf8String(reader, import.TargetNamespace)));
                        break;

                    case ImportDefinitionKind.AliasAssemblyNamespace:
                        importGroupBuilder.Add(new ImportRecord(
                                                   ImportTargetKind.Namespace,
                                                   alias: ReadUtf8String(reader, import.Alias),
                                                   targetString: ReadUtf8String(reader, import.TargetNamespace),
                                                   targetAssembly: symbolProvider.GetReferencedAssembly(import.TargetAssembly)));
                        break;

                    case ImportDefinitionKind.AliasType:
                        importGroupBuilder.Add(new ImportRecord(
                                                   ImportTargetKind.Type,
                                                   alias: ReadUtf8String(reader, import.Alias),
                                                   targetType: symbolProvider.GetType(import.TargetType)));
                        break;
                    }
                }

                // VB always expects two import groups (even if they are empty).
                // TODO: consider doing this for C# as well and handle empty groups in the binder.
                if (isVisualBasicMethod || importGroupBuilder.Count > 0)
                {
                    importGroupsBuilder.Add(importGroupBuilder.ToImmutable());
                    importGroupBuilder.Clear();
                }

                handle = importScope.Parent;
            }

            importGroupBuilder.Free();
        }