SymbolKey.SymbolKeyReader.cs 23.6 KB
Newer Older
J
Jonathon Marolf 已提交
1 2 3
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
C
CyrusNajmabadi 已提交
4 5

using System;
6 7 8 9 10
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Text;
using System.Threading;
T
Tomas Matousek 已提交
11
using Microsoft.CodeAnalysis.PooledObjects;
12
using Microsoft.CodeAnalysis.Shared.Extensions;
13
using Microsoft.CodeAnalysis.Shared.Utilities;
14
using Microsoft.CodeAnalysis.Text;
15 16 17 18
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis
{
19
    internal partial struct SymbolKey
20
    {
21
        private abstract class Reader<TStringResult> : IDisposable
22 23 24 25 26 27
        {
            protected const char OpenParenChar = '(';
            protected const char CloseParenChar = ')';
            protected const char SpaceChar = ' ';
            protected const char DoubleQuoteChar = '"';

28 29 30
            private readonly ReadFunction<TStringResult> _readString;
            private readonly ReadFunction<bool> _readBoolean;
            private readonly ReadFunction<RefKind> _readRefKind;
31 32 33 34 35 36 37 38 39

            protected string Data { get; private set; }
            public CancellationToken CancellationToken { get; private set; }

            public int Position;

            public Reader()
            {
                _readString = ReadString;
40
                _readBoolean = ReadBoolean;
41
                _readRefKind = ReadRefKind;
42 43 44 45 46 47 48 49 50 51 52 53
            }

            protected virtual void Initialize(string data, CancellationToken cancellationToken)
            {
                Data = data;
                CancellationToken = cancellationToken;
                Position = 0;
            }

            public virtual void Dispose()
            {
                Data = null;
C
CyrusNajmabadi 已提交
54
                CancellationToken = default;
55 56 57
            }

            protected char Eat(SymbolKeyType type)
58
                => Eat((char)type);
59 60 61 62 63 64 65 66 67

            protected char Eat(char c)
            {
                Debug.Assert(Data[Position] == c);
                Position++;
                return c;
            }

            protected void EatCloseParen()
68
                => Eat(CloseParenChar);
69 70

            protected void EatOpenParen()
71
                => Eat(OpenParenChar);
72 73 74 75

            public int ReadInteger()
            {
                EatSpace();
76 77 78 79
                return ReadIntegerRaw_DoNotCallDirectly();
            }

            public int ReadFormatVersion()
C
Fixes  
Cyrus Najmabadi 已提交
80
                => ReadIntegerRaw_DoNotCallDirectly();
81 82 83

            private int ReadIntegerRaw_DoNotCallDirectly()
            {
84 85
                Debug.Assert(char.IsNumber(Data[Position]));

C
Use var  
Cyrus Najmabadi 已提交
86
                var value = 0;
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103

                var start = Position;
                while (char.IsNumber(Data[Position]))
                {
                    var digit = Data[Position] - '0';

                    value *= 10;
                    value += digit;

                    Position++;
                }

                Debug.Assert(start != Position);
                return value;
            }

            protected char EatSpace()
104
                => Eat(SpaceChar);
105 106

            public bool ReadBoolean()
107 108 109
                => ReadBoolean(out _);

            public bool ReadBoolean(out string failureReason)
110
            {
111
                failureReason = null;
112 113 114 115 116 117
                var val = ReadInteger();
                Debug.Assert(val == 0 || val == 1);
                return val == 1;
            }

            public TStringResult ReadString()
118 119 120
                => ReadString(out _);

            public TStringResult ReadString(out string failureReason)
121
            {
122
                failureReason = null;
123 124 125 126 127 128
                EatSpace();
                return ReadStringNoSpace();
            }

            protected TStringResult ReadStringNoSpace()
            {
J
Julien 已提交
129 130 131 132 133 134
                if ((SymbolKeyType)Data[Position] == SymbolKeyType.Null)
                {
                    Eat(SymbolKeyType.Null);
                    return CreateNullForString();
                }

135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
                EatDoubleQuote();

                var start = Position;

                var hasEmbeddedQuote = false;
                while (true)
                {
                    if (Data[Position] != DoubleQuoteChar)
                    {
                        Position++;
                        continue;
                    }

                    // We have a quote.  See if it's the final quote, or if it's an escaped
                    // embedded quote.
                    if (Data[Position + 1] == DoubleQuoteChar)
                    {
                        hasEmbeddedQuote = true;
                        Position += 2;
                        continue;
                    }

                    break;
                }

                var end = Position;
                EatDoubleQuote();

                var result = CreateResultForString(start, end, hasEmbeddedQuote);

                return result;
            }

            protected abstract TStringResult CreateResultForString(int start, int end, bool hasEmbeddedQuote);
J
Julien 已提交
169
            protected abstract TStringResult CreateNullForString();
170 171

            private void EatDoubleQuote()
172
                => Eat(DoubleQuoteChar);
173

C
Cyrus Najmabadi 已提交
174
            public PooledArrayBuilder<TStringResult> ReadStringArray()
175
                => ReadArray(_readString, out _);
176

C
Cyrus Najmabadi 已提交
177
            public PooledArrayBuilder<bool> ReadBooleanArray()
178
                => ReadArray(_readBoolean, out _);
179

C
Cyrus Najmabadi 已提交
180
            public PooledArrayBuilder<RefKind> ReadRefKindArray()
181
                => ReadArray(_readRefKind, out _);
182

183
            public PooledArrayBuilder<T> ReadArray<T>(ReadFunction<T> readFunction, out string failureReason)
184
            {
C
Cyrus Najmabadi 已提交
185
                var builder = PooledArrayBuilder<T>.GetInstance();
186 187
                EatSpace();

188
                Debug.Assert((SymbolKeyType)Data[Position] != SymbolKeyType.Null);
189 190 191 192

                EatOpenParen();
                Eat(SymbolKeyType.Array);

193
                string totalFailureReason = null;
194 195 196 197
                var length = ReadInteger();
                for (var i = 0; i < length; i++)
                {
                    CancellationToken.ThrowIfCancellationRequested();
198 199 200 201 202 203 204 205 206
                    builder.Builder.Add(readFunction(out var elementFailureReason));

                    if (elementFailureReason != null)
                    {
                        var reason = $"element {i} failed {elementFailureReason}";
                        totalFailureReason = totalFailureReason == null
                            ? $"({reason})"
                            : $"(totalFailureReason -> {reason})";
                    }
207 208 209
                }

                EatCloseParen();
210
                failureReason = totalFailureReason;
C
Cyrus Najmabadi 已提交
211
                return builder;
212
            }
213 214

            public RefKind ReadRefKind()
215 216 217
                => ReadRefKind(out _);

            public RefKind ReadRefKind(out string failureReason)
218
            {
219
                failureReason = null;
220 221
                return (RefKind)ReadInteger();
            }
222 223
        }

224
        private class RemoveAssemblySymbolKeysReader : Reader<object>
225 226 227 228 229 230 231 232 233 234
        {
            private readonly StringBuilder _builder = new StringBuilder();

            private bool _skipString = false;

            public RemoveAssemblySymbolKeysReader()
            {
            }

            public void Initialize(string data)
235
                => base.Initialize(data, CancellationToken.None);
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263

            public string RemoveAssemblySymbolKeys()
            {
                while (Position < Data.Length)
                {
                    var ch = Data[Position];
                    if (ch == OpenParenChar)
                    {
                        _builder.Append(Eat(OpenParenChar));

                        var type = (SymbolKeyType)Data[Position];
                        _builder.Append(Eat(type));
                        if (type == SymbolKeyType.Assembly)
                        {
                            Debug.Assert(_skipString == false);
                            _skipString = true;
                            ReadString();

                            Debug.Assert(_skipString == true);
                            _skipString = false;
                        }
                    }
                    else if (Data[Position] == DoubleQuoteChar)
                    {
                        ReadStringNoSpace();
                    }
                    else
                    {
264
                        // All other characters we pass along directly to the string builder.
265 266 267 268 269 270 271 272 273 274
                        _builder.Append(Eat(ch));
                    }
                }

                return _builder.ToString();
            }

            protected override object CreateResultForString(int start, int end, bool hasEmbeddedQuote)
            {
                // 'start' is right after the open quote, and 'end' is right before the close quote.
275
                // However, we want to include both quotes in the result.
276 277 278 279 280 281 282 283 284 285 286
                _builder.Append(DoubleQuoteChar);
                if (!_skipString)
                {
                    for (var i = start; i < end; i++)
                    {
                        _builder.Append(Data[i]);
                    }
                }
                _builder.Append(DoubleQuoteChar);
                return null;
            }
J
Julien 已提交
287 288

            protected override object CreateNullForString()
289
                => null;
290 291
        }

292 293
        private delegate T ReadFunction<T>(out string failureReason);

C
CyrusNajmabadi 已提交
294
        private class SymbolKeyReader : Reader<string>
295
        {
C
revert  
Cyrus Najmabadi 已提交
296
            private static readonly ObjectPool<SymbolKeyReader> s_readerPool = SharedPools.Default<SymbolKeyReader>();
297

C
CyrusNajmabadi 已提交
298
            private readonly Dictionary<int, SymbolKeyResolution> _idToResult = new Dictionary<int, SymbolKeyResolution>();
299 300
            private readonly ReadFunction<SymbolKeyResolution> _readSymbolKey;
            private readonly ReadFunction<Location> _readLocation;
C
CyrusNajmabadi 已提交
301

302 303 304 305
            public Compilation Compilation { get; private set; }
            public bool IgnoreAssemblyKey { get; private set; }
            public SymbolEquivalenceComparer Comparer { get; private set; }

306
            private readonly List<IMethodSymbol> _methodSymbolStack = new List<IMethodSymbol>();
307

C
Cyrus Najmabadi 已提交
308
            public SymbolKeyReader()
309
            {
C
CyrusNajmabadi 已提交
310 311
                _readSymbolKey = ReadSymbolKey;
                _readLocation = ReadLocation;
312 313 314 315 316
            }

            public override void Dispose()
            {
                base.Dispose();
C
CyrusNajmabadi 已提交
317
                _idToResult.Clear();
318 319 320
                Compilation = null;
                IgnoreAssemblyKey = false;
                Comparer = null;
321
                _methodSymbolStack.Clear();
322 323 324 325 326

                // Place us back in the pool for future use.
                s_readerPool.Free(this);
            }

C
CyrusNajmabadi 已提交
327 328
            public static SymbolKeyReader GetReader(
                string data, Compilation compilation,
C
Cyrus Najmabadi 已提交
329
                bool ignoreAssemblyKey,
330
                CancellationToken cancellationToken)
C
CyrusNajmabadi 已提交
331 332
            {
                var reader = s_readerPool.Allocate();
C
Cyrus Najmabadi 已提交
333
                reader.Initialize(data, compilation, ignoreAssemblyKey, cancellationToken);
C
CyrusNajmabadi 已提交
334 335 336
                return reader;
            }

337 338 339 340 341 342 343 344 345
            private void Initialize(
                string data,
                Compilation compilation,
                bool ignoreAssemblyKey,
                CancellationToken cancellationToken)
            {
                base.Initialize(data, cancellationToken);
                Compilation = compilation;
                IgnoreAssemblyKey = ignoreAssemblyKey;
346

347 348 349 350 351
                Comparer = ignoreAssemblyKey
                    ? SymbolEquivalenceComparer.IgnoreAssembliesInstance
                    : SymbolEquivalenceComparer.Instance;
            }

C
CyrusNajmabadi 已提交
352 353
            internal bool ParameterTypesMatch(
                ImmutableArray<IParameterSymbol> parameters,
354
                PooledArrayBuilder<ITypeSymbol> originalParameterTypes)
355
            {
C
Cyrus Najmabadi 已提交
356
                if (originalParameterTypes.IsDefault || parameters.Length != originalParameterTypes.Count)
C
CyrusNajmabadi 已提交
357 358 359
                {
                    return false;
                }
360

C
CyrusNajmabadi 已提交
361 362 363 364 365 366
                // We are checking parameters for equality, if they refer to method type parameters,
                // then we don't want to recurse through the method (which would then recurse right
                // back into the parameters).  So we use a signature type comparer as it will properly
                // compare method type parameters by ordinal.
                var signatureComparer = Comparer.SignatureTypeEquivalenceComparer;

367
                for (var i = 0; i < originalParameterTypes.Count; i++)
C
CyrusNajmabadi 已提交
368 369 370 371 372 373 374 375
                {
                    if (!signatureComparer.Equals(originalParameterTypes[i], parameters[i].Type))
                    {
                        return false;
                    }
                }

                return true;
J
Julien 已提交
376 377
            }

378 379
            public void PushMethod(IMethodSymbol methodOpt)
                => _methodSymbolStack.Add(methodOpt);
380

381
            public void PopMethod(IMethodSymbol methodOpt)
382
            {
383
                Contract.ThrowIfTrue(_methodSymbolStack.Count == 0);
C
Cyrus Najmabadi 已提交
384
                Contract.ThrowIfFalse(Equals(methodOpt, _methodSymbolStack[_methodSymbolStack.Count - 1]));
385
                _methodSymbolStack.RemoveAt(_methodSymbolStack.Count - 1);
386 387
            }

388 389 390 391
            public IMethodSymbol ResolveMethod(int index)
                => _methodSymbolStack[index];

            internal SyntaxTree GetSyntaxTree(string filePath)
C
Cyrus Najmabadi 已提交
392 393 394 395 396 397 398 399 400 401 402
            {
                foreach (var tree in this.Compilation.SyntaxTrees)
                {
                    if (tree.FilePath == filePath)
                    {
                        return tree;
                    }
                }

                return null;
            }
403

C
CyrusNajmabadi 已提交
404 405
            #region Symbols

406
            public SymbolKeyResolution ReadSymbolKey(out string failureReason)
407
            {
C
CyrusNajmabadi 已提交
408
                CancellationToken.ThrowIfCancellationRequested();
409
                EatSpace();
C
CyrusNajmabadi 已提交
410 411 412 413 414

                var type = (SymbolKeyType)Data[Position];
                if (type == SymbolKeyType.Null)
                {
                    Eat(type);
415
                    failureReason = null;
C
CyrusNajmabadi 已提交
416
                    return default;
C
CyrusNajmabadi 已提交
417 418 419 420 421 422 423 424 425 426 427 428
                }

                EatOpenParen();
                SymbolKeyResolution result;

                type = (SymbolKeyType)Data[Position];
                Eat(type);

                if (type == SymbolKeyType.Reference)
                {
                    var id = ReadInteger();
                    result = _idToResult[id];
429
                    failureReason = null;
C
CyrusNajmabadi 已提交
430 431 432
                }
                else
                {
433
                    result = ReadWorker(type, out failureReason);
C
CyrusNajmabadi 已提交
434 435 436 437 438 439 440
                    var id = ReadInteger();
                    _idToResult[id] = result;
                }

                EatCloseParen();

                return result;
441 442
            }

443
            private SymbolKeyResolution ReadWorker(SymbolKeyType type, out string failureReason)
444
                => type switch
445
                {
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
                    SymbolKeyType.Alias => AliasSymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.BodyLevel => BodyLevelSymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.ConstructedMethod => ConstructedMethodSymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.NamedType => NamedTypeSymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.ErrorType => ErrorTypeSymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.Field => FieldSymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.FunctionPointer => FunctionPointerTypeSymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.DynamicType => DynamicTypeSymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.Method => MethodSymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.Namespace => NamespaceSymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.PointerType => PointerTypeSymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.Parameter => ParameterSymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.Property => PropertySymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.ArrayType => ArrayTypeSymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.Assembly => AssemblySymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.TupleType => TupleTypeSymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.Module => ModuleSymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.Event => EventSymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.ReducedExtensionMethod => ReducedExtensionMethodSymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.TypeParameter => TypeParameterSymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.AnonymousType => AnonymousTypeSymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.AnonymousFunctionOrDelegate => AnonymousFunctionOrDelegateSymbolKey.Resolve(this, out failureReason),
                    SymbolKeyType.TypeParameterOrdinal => TypeParameterOrdinalSymbolKey.Resolve(this, out failureReason),
469 470
                    _ => throw new NotImplementedException(),
                };
471

C
temp  
Cyrus Najmabadi 已提交
472 473
            /// <summary>
            /// Reads an array of symbols out from the key.  Note: the number of symbols returned 
C
Cyrus Najmabadi 已提交
474
            /// will either be the same as the original amount written, or <c>default</c> will be 
C
Cyrus Najmabadi 已提交
475 476
            /// returned. It will never be less or more.  <c>default</c> will be returned if any 
            /// elements could not be resolved to the requested <typeparamref name="TSymbol"/> type 
477
            /// in the provided <see cref="SymbolKeyReader.Compilation"/>.
C
Cyrus Najmabadi 已提交
478 479 480
            /// 
            /// Callers should <see cref="IDisposable.Dispose"/> the instance returned.  No check is
            /// necessary if <c>default</c> was returned before calling <see cref="IDisposable.Dispose"/>
C
temp  
Cyrus Najmabadi 已提交
481
            /// </summary>
482
            public PooledArrayBuilder<TSymbol> ReadSymbolKeyArray<TSymbol>(out string failureReason) where TSymbol : ISymbol
483
            {
484 485 486 487 488 489
                using var resolutions = ReadArray(_readSymbolKey, out var elementsFailureReason);
                if (elementsFailureReason != null)
                {
                    failureReason = elementsFailureReason;
                    return default;
                }
490

C
Cyrus Najmabadi 已提交
491
                var result = PooledArrayBuilder<TSymbol>.GetInstance();
492 493 494 495
                foreach (var resolution in resolutions)
                {
                    if (resolution.GetAnySymbol() is TSymbol castedSymbol)
                    {
C
Cyrus Najmabadi 已提交
496
                        result.AddIfNotNull(castedSymbol);
497
                    }
C
temp  
Cyrus Najmabadi 已提交
498 499
                    else
                    {
C
Cyrus Najmabadi 已提交
500
                        result.Dispose();
501
                        failureReason = $"({nameof(ReadSymbolKeyArray)} incorrect type for element)";
C
Cyrus Najmabadi 已提交
502
                        return default;
C
temp  
Cyrus Najmabadi 已提交
503
                    }
504
                }
C
Cyrus Najmabadi 已提交
505

506
                failureReason = null;
C
Cyrus Najmabadi 已提交
507
                return result;
508
            }
C
CyrusNajmabadi 已提交
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523

            #endregion

            #region Strings

            protected override string CreateResultForString(int start, int end, bool hasEmbeddedQuote)
            {
                var substring = Data.Substring(start, end - start);
                var result = hasEmbeddedQuote
                    ? substring.Replace("\"\"", "\"")
                    : substring;
                return result;
            }

            protected override string CreateNullForString()
524
                => null;
C
CyrusNajmabadi 已提交
525 526 527 528 529

            #endregion

            #region Locations

530
            public Location ReadLocation(out string failureReason)
C
CyrusNajmabadi 已提交
531 532 533
            {
                EatSpace();
                if ((SymbolKeyType)Data[Position] == SymbolKeyType.Null)
534
                {
C
CyrusNajmabadi 已提交
535
                    Eat(SymbolKeyType.Null);
536
                    failureReason = null;
C
CyrusNajmabadi 已提交
537
                    return null;
538 539
                }

C
CyrusNajmabadi 已提交
540
                var kind = (LocationKind)ReadInteger();
541
                if (kind == LocationKind.SourceFile)
C
CyrusNajmabadi 已提交
542 543 544 545
                {
                    var filePath = ReadString();
                    var start = ReadInteger();
                    var length = ReadInteger();
546

C
Cyrus Najmabadi 已提交
547 548 549 550
                    // The syntax tree can be null if we're resolving this location in a compilation
                    // that does not contain this file.  In this case, just map this location to None.
                    var syntaxTree = GetSyntaxTree(filePath);
                    if (syntaxTree != null)
551
                    {
C
Fix  
Cyrus Najmabadi 已提交
552
                        failureReason = null;
C
Cyrus Najmabadi 已提交
553
                        return Location.Create(syntaxTree, new TextSpan(start, length));
554
                    }
C
CyrusNajmabadi 已提交
555
                }
556
                else if (kind == LocationKind.MetadataFile)
C
CyrusNajmabadi 已提交
557
                {
558
                    var assemblyResolution = ReadSymbolKey(out var assemblyFailureReason);
C
Reorder  
Cyrus Najmabadi 已提交
559 560
                    var moduleName = ReadString();

561 562 563 564 565 566
                    if (assemblyFailureReason != null)
                    {
                        failureReason = $"{nameof(ReadLocation)} {nameof(assemblyResolution)} failed -> " + assemblyFailureReason;
                        return Location.None;
                    }

C
Cyrus Najmabadi 已提交
567 568 569
                    // We may be resolving in a compilation where we don't have a module
                    // with this name.  In that case, just map this location to none.
                    if (assemblyResolution.GetAnySymbol() is IAssemblySymbol assembly)
570
                    {
C
Cyrus Najmabadi 已提交
571 572
                        var module = GetModule(assembly.Modules, moduleName);
                        if (module != null)
573
                        {
C
Cyrus Najmabadi 已提交
574 575
                            var location = FirstOrDefault(module.Locations);
                            if (location != null)
576
                            {
577
                                failureReason = null;
C
Cyrus Najmabadi 已提交
578
                                return location;
579
                            }
580 581
                        }
                    }
C
CyrusNajmabadi 已提交
582
                }
583

584
                failureReason = null;
585
                return Location.None;
586
            }
C
CyrusNajmabadi 已提交
587

588 589 590 591 592 593
            public SymbolKeyResolution? ResolveLocation(Location location)
            {
                if (location.SourceTree != null)
                {
                    var node = location.FindNode(findInsideTrivia: true, getInnermostNodeForTie: true, CancellationToken);
                    var semanticModel = Compilation.GetSemanticModel(location.SourceTree);
C
Fixes  
Cyrus Najmabadi 已提交
594 595 596 597
                    var symbol = semanticModel.GetDeclaredSymbol(node, CancellationToken);
                    if (symbol != null)
                        return new SymbolKeyResolution(symbol);

598 599 600 601 602 603 604 605 606 607 608
                    var info = semanticModel.GetSymbolInfo(node, CancellationToken);
                    if (info.Symbol != null)
                        return new SymbolKeyResolution(info.Symbol);

                    if (info.CandidateSymbols.Length > 0)
                        return new SymbolKeyResolution(info.CandidateSymbols, info.CandidateReason);
                }

                return null;
            }

609
            private static IModuleSymbol GetModule(IEnumerable<IModuleSymbol> modules, string moduleName)
C
Cyrus Najmabadi 已提交
610 611 612 613 614 615 616 617 618 619 620 621
            {
                foreach (var module in modules)
                {
                    if (module.MetadataName == moduleName)
                    {
                        return module;
                    }
                }

                return null;
            }

622 623
            public PooledArrayBuilder<Location> ReadLocationArray(out string failureReason)
                => ReadArray(_readLocation, out failureReason);
C
CyrusNajmabadi 已提交
624 625

            #endregion
626 627
        }
    }
T
Tomas Matousek 已提交
628
}