private int?GetLineWithPossibleBreakpoint(SourceFileInfo sourceFile, int startLine)
        {
            if (sourceFile.PossibleBreakpointLocationsByLine.ContainsKey(startLine))
            {
                return(startLine);
            }

            return(sourceFile.PossibleBreakpointLocationsByLine.Keys.FirstOrDefault(l => l >= startLine));
        }
        private void AddPdb(string moduleName, MetadataReader peMetadataReader, MetadataReader pdbMetadataReader)
        {
            // Add all the documents (source files)
            var tempDocNameToSourceFileMap = new Dictionary <string, SourceFileInfo>();

            foreach (var docHandle in pdbMetadataReader.Documents)
            {
                var doc        = pdbMetadataReader.GetDocument(docHandle);
                var docRawName = pdbMetadataReader.GetString(doc.Name);
                var filenameWithForwardSlashes = docRawName.Replace('\\', '/');
                var url        = $"{DotnetSourceFilePrefix}//{moduleName}/{filenameWithForwardSlashes}";
                var sourceFile = new SourceFileInfo
                {
                    Id           = $"{DotnetSourceFilePrefix}{nextSourceFileId++}",
                    AssemblyName = moduleName,
                    FileName     = filenameWithForwardSlashes,
                    Url          = url,
                    PossibleBreakpointLocationsByLine = new Dictionary <int, List <PossibleBreakpointLocation> >()
                };
                _sourceFiles.Add(sourceFile.Id, sourceFile);
                tempDocNameToSourceFileMap.Add(docRawName, sourceFile);
            }

            // Add all the debug sequence points
            foreach (var methodDebugInfoHandle in pdbMetadataReader.MethodDebugInformation)
            {
                var    methodDebugInfo = pdbMetadataReader.GetMethodDebugInformation(methodDebugInfoHandle);
                var    methodDefHandle = methodDebugInfoHandle.ToDefinitionHandle();
                var    methodDef       = peMetadataReader.GetMethodDefinition(methodDefHandle);
                var    document        = pdbMetadataReader.GetDocument(methodDebugInfo.Document);
                string documentName    = null;
                try
                {
                    documentName = pdbMetadataReader.GetString(document.Name);
                }
                catch (Exception)
                {
                    // If the document name isn't readable, don't process it
                }
                if (documentName != null && tempDocNameToSourceFileMap.TryGetValue(documentName, out var sourceFile))
                {
                    // Construct the DNA method identifier string
                    var methodDefToken       = MetadataTokens.GetToken(methodDefHandle);
                    var methodDebugInfoToken = MetadataTokens.GetToken(methodDefHandle);
                    var declaringType        = peMetadataReader.GetTypeDefinition(methodDef.GetDeclaringType());
                    var namespaceName        = peMetadataReader.GetString(declaringType.Namespace);
                    var className            = peMetadataReader.GetString(declaringType.Name);
                    var methodName           = peMetadataReader.GetString(methodDef.Name);
                    var dnaMethodIdentifier  = moduleName + namespaceName + className + methodName;

                    // Add PossibleBreakpointLocation objects to the sourcefile
                    int sequencePointIndex = 0;
                    foreach (var sequencePoint in methodDebugInfo.GetSequencePoints().Where(sp => !sp.IsHidden))
                    {
                        var possibleBreakpointLocations = sourceFile.PossibleBreakpointLocationsByLine;
                        var lineNumber = sequencePoint.StartLine;
                        if (!possibleBreakpointLocations.TryGetValue(lineNumber, out _))
                        {
                            possibleBreakpointLocations.Add(lineNumber, new List <PossibleBreakpointLocation>());
                        }
                        possibleBreakpointLocations[lineNumber].Add(new PossibleBreakpointLocation
                        {
                            DnaMethodIdentifier = dnaMethodIdentifier,
                            SequencePointIndex  = sequencePointIndex,
                            SequencePointInfo   = sequencePoint,
                            MethodName          = methodName,
                            SourceFile          = sourceFile,
                        });

                        sequencePointIndex++;
                    }
                }
            }
        }
        public PossibleBreakpointLocation GetClosestBreakpointLocation(SourceFileInfo script, int lineNumber, int columnNumber)
        {
            var candidates = GetBreakpointLocations(script.Id, lineNumber);

            return(candidates.FirstOrDefault(loc => loc.SequencePointInfo.StartLine >= lineNumber && loc.SequencePointInfo.StartColumn >= columnNumber));
        }
        internal PossibleBreakpointLocation FindBreakpointUsingDnaData(string dnaLocationId, int ilOffset, out SourceFileInfo inSourceFile)
        {
            // TODO: Find a more efficient way of looking this up. Might just need to maintain
            // a lookup from dnaLocationId to sourcefiles
            foreach (var sourceFile in _sourceFiles.Values)
            {
                foreach (var breakpointCollections in sourceFile.PossibleBreakpointLocationsByLine.Values)
                {
                    foreach (var breakpoint in breakpointCollections)
                    {
                        if (breakpoint.DnaMethodIdentifier == dnaLocationId && breakpoint.SequencePointInfo.Offset == ilOffset)
                        {
                            inSourceFile = sourceFile;
                            return(breakpoint);
                        }
                    }
                }
            }

            inSourceFile = null;
            return(null);
        }