Пример #1
0
        /// <summary>
        /// Locals are given slots when their declarations are encountered.  We only need give slots
        /// to local variables, out parameters, and the "this" variable of a struct constructs.
        /// Other variables are not given slots, and are therefore not tracked by the analysis.  This
        /// returns -1 for a variable that is not tracked, for fields of structs that have the same
        /// assigned status as the container, and for structs that (recursively) contain no data members.
        /// We do not need to track references to
        /// variables that occur before the variable is declared, as those are reported in an
        /// earlier phase as "use before declaration". That allows us to avoid giving slots to local
        /// variables before processing their declarations.
        /// </summary>
        protected int VariableSlot(Symbol symbol, int containingSlot = 0)
        {
            containingSlot = DescendThroughTupleRestFields(ref symbol, containingSlot, forceContainingSlotsToExist: false);

            int slot;

            return((_variableSlot.TryGetValue(new VariableIdentifier(symbol, containingSlot), out slot)) ? slot : -1);
        }
            protected virtual LabelSymbol GetDagNodeLabel(BoundDecisionDagNode dag)
            {
                if (!_dagNodeLabels.TryGetValue(dag, out LabelSymbol? label))
                {
                    _dagNodeLabels.Add(dag, label = dag is BoundLeafDecisionDagNode d ? d.Label : _factory.GenerateLabel("dagNode"));
                }

                return(label);
            }
Пример #3
0
            public void AddThrow(TypeSymbol type, SyntaxNode syntax)
            {
                PooledHashSet <SyntaxNode> nodes;

                if (!ThrowLocations.TryGetValue(type, out nodes))
                {
                    nodes = PooledHashSet <SyntaxNode> .GetInstance();

                    ThrowLocations.Add(type, nodes);
                }
                nodes.Add(syntax);
            }
Пример #4
0
        public int AssignLocalOrdinal(SynthesizedLocalKind localKind, int syntaxOffset)
        {
#if !DEBUG
            // Optimization (avoid growing the dictionary below):
            // User-defined locals have to have a distinct syntax offset, thus ordinal is always 0.
            if (localKind == SynthesizedLocalKind.UserDefined)
            {
                return(0);
            }
#endif
            int  ordinal;
            long key = MakeKey(localKind, syntaxOffset);

            // Group by syntax offset and kind.
            // Variables associated with the same syntax and kind will be assigned different ordinals.
            if (_lazyMap == null)
            {
                _lazyMap = PooledDictionary <long, int> .GetInstance();

                ordinal = 0;
            }
            else if (!_lazyMap.TryGetValue(key, out ordinal))
            {
                ordinal = 0;
            }

            _lazyMap[key] = ordinal + 1;
            Debug.Assert(ordinal == 0 || localKind != SynthesizedLocalKind.UserDefined);
            return(ordinal);
        }
            private void LowerWhenClause(BoundWhenDecisionDagNode whenClause)
            {
                // This node is used even when there is no when clause, to record bindings. In the case that there
                // is no when clause, whenClause.WhenExpression and whenClause.WhenFalse are null, and the syntax for this
                // node is the case clause.

                // We need to assign the pattern variables in the code where they are in scope, so we produce a branch
                // to the section where they are in scope and evaluate the when clause there.
                var         whenTrue            = (BoundLeafDecisionDagNode)whenClause.WhenTrue;
                LabelSymbol labelToSectionScope = GetDagNodeLabel(whenClause);

                // We need the section syntax to get the section builder from the map. Unfortunately this is a bit awkward
                SyntaxNode sectionSyntax       = whenClause.Syntax is SwitchLabelSyntax l ? l.Parent : whenClause.Syntax;
                bool       foundSectionBuilder = _switchArms.TryGetValue(sectionSyntax, out ArrayBuilder <BoundStatement> sectionBuilder);

                Debug.Assert(foundSectionBuilder);
                sectionBuilder.Add(_factory.Label(labelToSectionScope));
                foreach (BoundPatternBinding binding in whenClause.Bindings)
                {
                    BoundExpression left = _localRewriter.VisitExpression(binding.VariableAccess);
                    // Since a switch does not add variables to the enclosing scope, the pattern variables
                    // are locals even in a script and rewriting them should have no effect.
                    Debug.Assert(left.Kind == BoundKind.Local && left == binding.VariableAccess);
                    BoundExpression right = _tempAllocator.GetTemp(binding.TempContainingValue);
                    if (left != right)
                    {
                        sectionBuilder.Add(_factory.Assignment(left, right));
                    }
                }

                var whenFalse = whenClause.WhenFalse;
                var trueLabel = GetDagNodeLabel(whenTrue);

                if (whenClause.WhenExpression != null && whenClause.WhenExpression.ConstantValue != ConstantValue.True)
                {
                    _factory.Syntax = whenClause.Syntax;
                    BoundStatement conditionalGoto = _factory.ConditionalGoto(_localRewriter.VisitExpression(whenClause.WhenExpression), trueLabel, jumpIfTrue: true);

                    // Only add instrumentation (such as a sequence point) if the node is not compiler-generated.
                    if (IsSwitchStatement && !whenClause.WhenExpression.WasCompilerGenerated && _localRewriter.Instrument)
                    {
                        conditionalGoto = _localRewriter._instrumenter.InstrumentSwitchWhenClauseConditionalGotoBody(whenClause.WhenExpression, conditionalGoto);
                    }

                    sectionBuilder.Add(conditionalGoto);

                    Debug.Assert(whenFalse != null);

                    // We hide the jump back into the decision dag, as it is not logically part of the when clause
                    BoundStatement jump = _factory.Goto(GetDagNodeLabel(whenFalse));
                    sectionBuilder.Add(IsSwitchStatement ? _factory.HiddenSequencePoint(jump) : jump);
                }
                else
                {
                    Debug.Assert(whenFalse == null);
                    sectionBuilder.Add(_factory.Goto(trueLabel));
                }
            }
Пример #6
0
                public BoundExpression GetTemp(BoundDagTemp dagTemp)
                {
                    if (!_map.TryGetValue(dagTemp, out BoundExpression result))
                    {
                        LocalSymbol temp = _factory.SynthesizedLocal(dagTemp.Type, syntax: _node, kind: SynthesizedLocalKind.SwitchCasePatternMatching);
                        result = _factory.Local(temp);
                        _map.Add(dagTemp, result);
                        _temps.Add(temp);
                    }

                    return(result);
                }
                public BoundExpression GetTemp(BoundDagTemp dagTemp)
                {
                    if (!_map.TryGetValue(dagTemp, out BoundExpression result))
                    {
                        var         kind = _generateSequencePoints ? SynthesizedLocalKind.SwitchCasePatternMatching : SynthesizedLocalKind.LoweringTemp;
                        LocalSymbol temp = _factory.SynthesizedLocal(dagTemp.Type, syntax: _node, kind: kind);
                        result = _factory.Local(temp);
                        _map.Add(dagTemp, result);
                        _temps.Add(temp);
                    }

                    return(result);
                }
Пример #8
0
            public override BoundNode VisitLocal(BoundLocal node)
            {
                if (!node.LocalSymbol.SynthesizedKind.IsLongLived())
                {
                    LocalSymbol longLived;
                    if (_tempSubstitution.TryGetValue(node.LocalSymbol, out longLived))
                    {
                        return(node.Update(longLived, node.ConstantValueOpt, node.Type));
                    }
                }

                return(base.VisitLocal(node));
            }
Пример #9
0
        public ES_TypeTag?CheckTypeExists(ArrayPointer <byte> name, ES_TypeTag?ignoredType)
        {
            if (ignoredType != ES_TypeTag.Class && ClassBuilders.TryGetValue(name, out var _))
            {
                return(ES_TypeTag.Class);
            }

            if (ignoredType != ES_TypeTag.Struct && StructBuilders.TryGetValue(name, out var _))
            {
                return(ES_TypeTag.Struct);
            }

            if (ignoredType != ES_TypeTag.Enum && EnumBuilders.TryGetValue(name, out var _))
            {
                return(ES_TypeTag.Enum);
            }

            if (ignoredType != ES_TypeTag.Function && NamespaceData.functions.TryGetValue(name, out var _))
            {
                return(ES_TypeTag.Function);
            }

            return(null);
        }
            private void LowerWhenClause(BoundWhenDecisionDagNode whenClause)
            {
                // This node is used even when there is no when clause, to record bindings. In the case that there
                // is no when clause, whenClause.WhenExpression and whenClause.WhenFalse are null, and the syntax for this
                // node is the case clause.

                // We need to assign the pattern variables in the code where they are in scope, so we produce a branch
                // to the section where they are in scope and evaluate the when clause there.
                var         whenTrue            = (BoundLeafDecisionDagNode)whenClause.WhenTrue;
                LabelSymbol labelToSectionScope = GetDagNodeLabel(whenClause);

                // We need the section syntax to get the section builder from the map. Unfortunately this is a bit awkward
                SyntaxNode sectionSyntax       = whenClause.Syntax is SwitchLabelSyntax l ? l.Parent : whenClause.Syntax;
                bool       foundSectionBuilder = _switchArms.TryGetValue(sectionSyntax, out ArrayBuilder <BoundStatement>?sectionBuilder);

                Debug.Assert(foundSectionBuilder && sectionBuilder is { });
Пример #11
0
                /// <summary>
                /// This is where we calculate <see cref="Scope.CanMergeWithParent"/>.
                /// <see cref="Scope.CanMergeWithParent"/> is always true unless we jump from after
                /// the beginning of a scope, to a point in between the beginning of the parent scope, and the beginning of the scope
                /// </summary>
                /// <param name="jumpTarget"></param>
                private void CheckCanMergeWithParent(LabelSymbol jumpTarget)
                {
                    // since forward jumps can never effect Scope.SemanticallySafeToMergeIntoParent
                    // if we have not yet seen the jumpTarget, this is a forward jump, and can be ignored
                    if (_scopesAfterLabel.TryGetValue(jumpTarget, out var scopesAfterLabel))
                    {
                        foreach (var scope in scopesAfterLabel)
                        {
                            // this jump goes from a point after the beginning of the scope (as we have already visited or started visiting the scope),
                            // to a point in between the beginning of the parent scope, and the beginning of the scope, so it is not safe to move
                            // variables in the scope to the parent scope.
                            scope.CanMergeWithParent = false;
                        }

                        // Prevent us repeating this process for all scopes if another jumps goes to the same label
                        scopesAfterLabel.Clear();
                    }
                }
            static void AddPlatformsAndVersionCountFromGuardMethods(INamedTypeSymbol operatingSystemType, PooledDictionary <string, int> knownPlatforms)
            {
                var methods = operatingSystemType.GetMembers().OfType <IMethodSymbol>();

                foreach (var m in methods)
                {
                    if (m.IsStatic &&
                        m.ReturnType.SpecialType == SpecialType.System_Boolean &&
                        NameAndParametersValid(m))
                    {
                        var(platformName, versionPartsCount) = ExtractPlatformAndVersionCount(m);
                        if (!knownPlatforms.TryGetValue(platformName, out var count) ||
                            versionPartsCount > count)
                        {
                            knownPlatforms[platformName] = versionPartsCount; // only keep highest count
                        }
                    }
                }
            }
Пример #13
0
        private void ParseBlock(UDMFParsedMapData dataClass, string ident, ParserInfo.BlockInfo?info)
        {
            UDMFToken tok = scanner.Scan();

            if (tok.Type != UDMFTokenType.BROPEN)
            {
                Errors.Add(new UDMFParseError("Unexpected token '" + tok.Text.ToString().Replace("\n", "") + "' found. Expected " + UDMFToken.TokenTypeToString(UDMFTokenType.BROPEN), 0x1001, tok));
                return;
            }

            IUDMFBlock block;

            if (info != null)
            {
                block = (IUDMFBlock)Activator.CreateInstance(info.Value.BlockType);
                info.Value.Delegates.Getter(dataClass).AddBlock(block);
            }
            else
            {
                var newBlock = new UDMFUnknownBlock();
                block = newBlock;

                if (!unknownBlocksPooled.TryGetValue(ident, out var unknownBlocksList))
                {
                    unknownBlocksList = new CL_PooledList <UDMFUnknownBlock> ();
                    unknownBlocksPooled.Add(ident, unknownBlocksList);
                }

                unknownBlocksList.Add(newBlock);
            }

            ParseExpr_List(block, info);

            tok = scanner.Scan();
            if (tok.Type != UDMFTokenType.BRCLOSE)
            {
                Errors.Add(new UDMFParseError("Unexpected token '" + tok.Text.ToString().Replace("\n", "") + "' found. Expected " + UDMFToken.TokenTypeToString(UDMFTokenType.BRCLOSE), 0x1001, tok));
                return;
            }
        }
Пример #14
0
 public bool TryGetKey(TValue value, out TKey key) => ToKey.TryGetValue(value, out key);
Пример #15
0
 public bool TryGetValue(TKey key, out TValue value) => ToValue.TryGetValue(key, out value);
Пример #16
0
        private async Task AnalyzeDeclaringReference(CompilationEvent.SymbolDeclared symbolEvent, SyntaxReference decl, Action <Diagnostic> addDiagnostic, CancellationToken cancellationToken)
        {
            var symbol = symbolEvent.Symbol;
            var syntax = await decl.GetSyntaxAsync();

            var endedAnalyzers = ArrayBuilder <ICodeBlockEndedAnalyzer> .GetInstance();

            endedAnalyzers.AddRange(CodeBlockEndedAnalyzers);
            var nodeAnalyzers = ArrayBuilder <ISyntaxNodeAnalyzer <TSyntaxKind> > .GetInstance();

            nodeAnalyzers.AddRange(Analyzers.OfType <ISyntaxNodeAnalyzer <TSyntaxKind> >());
            foreach (var da in CodeBlockStartedAnalyzers)
            {
                // Catch Exception from da.OnCodeBlockStarted
                ExecuteAndCatchIfThrows(da, addDiagnostic, continueOnError, cancellationToken, () =>
                {
                    var blockStatefulAnalyzer = da.OnCodeBlockStarted(syntax, symbol, symbolEvent.SemanticModel(decl), addDiagnostic, cancellationToken);
                    var endedAnalyzer         = blockStatefulAnalyzer as ICodeBlockEndedAnalyzer;
                    if (endedAnalyzer != null)
                    {
                        endedAnalyzers.Add(endedAnalyzer);
                    }
                    var nodeAnalyzer = blockStatefulAnalyzer as ISyntaxNodeAnalyzer <TSyntaxKind>;
                    if (nodeAnalyzer != null)
                    {
                        nodeAnalyzers.Add(nodeAnalyzer);
                    }
                });
            }

            PooledDictionary <TSyntaxKind, ArrayBuilder <ISyntaxNodeAnalyzer <TSyntaxKind> > > nodeAnalyzersByKind = null;

            foreach (var nodeAnalyzer in nodeAnalyzers)
            {
                // Catch Exception from  nodeAnalyzer.SyntaxKindsOfInterest
                try
                {
                    foreach (var kind in nodeAnalyzer.SyntaxKindsOfInterest)
                    {
                        if (nodeAnalyzersByKind == null)
                        {
                            nodeAnalyzersByKind = PooledDictionary <TSyntaxKind, ArrayBuilder <ISyntaxNodeAnalyzer <TSyntaxKind> > > .GetInstance();
                        }
                        ArrayBuilder <ISyntaxNodeAnalyzer <TSyntaxKind> > analyzersForKind;
                        if (!nodeAnalyzersByKind.TryGetValue(kind, out analyzersForKind))
                        {
                            nodeAnalyzersByKind.Add(kind, analyzersForKind = ArrayBuilder <ISyntaxNodeAnalyzer <TSyntaxKind> > .GetInstance());
                        }
                        analyzersForKind.Add(nodeAnalyzer);
                    }
                }
                catch (Exception e)
                {
                    // Create a info diagnostic saying that the analyzer failed
                    addDiagnostic(GetAnalyzerDiagnostic(nodeAnalyzer, e));
                }
            }
            nodeAnalyzers.Free();

            SemanticModel semanticModel = (nodeAnalyzersByKind != null || endedAnalyzers.Any()) ? symbolEvent.SemanticModel(decl) : null;

            if (nodeAnalyzersByKind != null)
            {
                semanticModel = symbolEvent.SemanticModel(decl);
                foreach (var child in syntax.DescendantNodesAndSelf())
                {
                    ArrayBuilder <ISyntaxNodeAnalyzer <TSyntaxKind> > analyzersForKind;
                    if (nodeAnalyzersByKind.TryGetValue(GetKind(child), out analyzersForKind))
                    {
                        foreach (var analyzer in analyzersForKind)
                        {
                            // Catch Exception from analyzer.AnalyzeNode
                            ExecuteAndCatchIfThrows(analyzer, addDiagnostic, continueOnError, cancellationToken, () => { analyzer.AnalyzeNode(child, semanticModel, addDiagnostic, cancellationToken); });
                        }
                    }
                }

                foreach (var b in nodeAnalyzersByKind.Values)
                {
                    b.Free();
                }
                nodeAnalyzersByKind.Free();
            }

            foreach (var a in endedAnalyzers)
            {
                // Catch Exception from a.OnCodeBlockEnded
                ExecuteAndCatchIfThrows(a, addDiagnostic, continueOnError, cancellationToken, () => { a.OnCodeBlockEnded(syntax, symbol, semanticModel, addDiagnostic, cancellationToken); });
            }
            endedAnalyzers.Free();
        }
        private void ResolveAndBindMissingAssemblies(
            TCompilation compilation,
            ImmutableArray <AssemblyData> explicitAssemblies,
            ImmutableArray <PEModule> explicitModules,
            ImmutableArray <MetadataReference> explicitReferences,
            ImmutableArray <ResolvedReference> explicitReferenceMap,
            MetadataReferenceResolver resolver,
            MetadataImportOptions importOptions,
            bool supersedeLowerVersions,
            [In, Out] ArrayBuilder <AssemblyReferenceBinding[]> referenceBindings,
            [In, Out] Dictionary <string, List <ReferencedAssemblyIdentity> > assemblyReferencesBySimpleName,
            out ImmutableArray <AssemblyData> allAssemblies,
            out ImmutableArray <MetadataReference> metadataReferences,
            out ImmutableArray <ResolvedReference> resolvedReferences,
            DiagnosticBag resolutionDiagnostics)
        {
            Debug.Assert(explicitAssemblies[0] is AssemblyDataForAssemblyBeingBuilt);
            Debug.Assert(referenceBindings.Count == explicitAssemblies.Length);
            Debug.Assert(explicitReferences.Length == explicitReferenceMap.Length);

            // -1 for assembly being built:
            int totalReferencedAssemblyCount = explicitAssemblies.Length - 1;

            var implicitAssemblies = ArrayBuilder <AssemblyData> .GetInstance();

            // tracks identities we already asked the resolver to resolve:
            var requestedIdentities = PooledHashSet <AssemblyIdentity> .GetInstance();

            PooledDictionary <AssemblyIdentity, PortableExecutableReference> previouslyResolvedAssembliesOpt = null;

            // Avoid resolving previously resolved missing references. If we call to the resolver again we would create new assembly symbols for them,
            // which would not match the previously created ones. As a result we would get duplicate PE types and conversion errors.
            var previousScriptCompilation = compilation.ScriptCompilationInfo?.PreviousScriptCompilation;

            if (previousScriptCompilation != null)
            {
                previouslyResolvedAssembliesOpt = PooledDictionary <AssemblyIdentity, PortableExecutableReference> .GetInstance();

                foreach (var entry in previousScriptCompilation.GetBoundReferenceManager().GetImplicitlyResolvedAssemblyReferences())
                {
                    previouslyResolvedAssembliesOpt.Add(entry.Key, entry.Value);
                }
            }

            var metadataReferencesBuilder = ArrayBuilder <MetadataReference> .GetInstance();

            Dictionary <MetadataReference, MergedAliases> lazyAliasMap = null;

            // metadata references and corresponding bindings of their references, used to calculate a fixed point:
            var referenceBindingsToProcess = ArrayBuilder <(MetadataReference, ArraySegment <AssemblyReferenceBinding>)> .GetInstance();

            // collect all missing identities, resolve the assemblies and bind their references against explicit definitions:
            GetInitialReferenceBindingsToProcess(explicitModules, explicitReferences, explicitReferenceMap, referenceBindings, totalReferencedAssemblyCount, referenceBindingsToProcess);

            // NB: includes the assembly being built:
            int explicitAssemblyCount = explicitAssemblies.Length;

            try
            {
                while (referenceBindingsToProcess.Count > 0)
                {
                    var referenceAndBindings = referenceBindingsToProcess.Pop();
                    var requestingReference  = referenceAndBindings.Item1;
                    var bindings             = referenceAndBindings.Item2;

                    foreach (var binding in bindings)
                    {
                        // only attempt to resolve unbound references (regardless of version difference of the bound ones)
                        if (binding.IsBound)
                        {
                            continue;
                        }

                        if (!requestedIdentities.Add(binding.ReferenceIdentity))
                        {
                            continue;
                        }

                        PortableExecutableReference resolvedReference;
                        if (previouslyResolvedAssembliesOpt == null || !previouslyResolvedAssembliesOpt.TryGetValue(binding.ReferenceIdentity, out resolvedReference))
                        {
                            resolvedReference = resolver.ResolveMissingAssembly(requestingReference, binding.ReferenceIdentity);
                            if (resolvedReference == null)
                            {
                                continue;
                            }
                        }

                        var data = ResolveMissingAssembly(binding.ReferenceIdentity, resolvedReference, importOptions, resolutionDiagnostics);
                        if (data == null)
                        {
                            continue;
                        }

                        // The resolver may return different version than we asked for, so it may happen that
                        // it returns the same identity for two different input identities (e.g. if a higher version
                        // of an assembly is available than what the assemblies reference: "A, v1" -> "A, v3" and "A, v2" -> "A, v3").
                        // If such case occurs merge the properties (aliases) of the resulting references in the same way we do
                        // during initial explicit references resolution.

                        // -1 for assembly being built:
                        int index = explicitAssemblyCount - 1 + metadataReferencesBuilder.Count;

                        var existingReference = TryAddAssembly(data.Identity, resolvedReference, index, resolutionDiagnostics, Location.None, assemblyReferencesBySimpleName, supersedeLowerVersions);
                        if (existingReference != null)
                        {
                            MergeReferenceProperties(existingReference, resolvedReference, resolutionDiagnostics, ref lazyAliasMap);
                            continue;
                        }

                        metadataReferencesBuilder.Add(resolvedReference);
                        implicitAssemblies.Add(data);

                        var referenceBinding = data.BindAssemblyReferences(explicitAssemblies, IdentityComparer);
                        referenceBindings.Add(referenceBinding);
                        referenceBindingsToProcess.Push((resolvedReference, new ArraySegment <AssemblyReferenceBinding>(referenceBinding)));
                    }
                }

                if (implicitAssemblies.Count == 0)
                {
                    Debug.Assert(lazyAliasMap == null);

                    resolvedReferences = ImmutableArray <ResolvedReference> .Empty;
                    metadataReferences = ImmutableArray <MetadataReference> .Empty;
                    allAssemblies      = explicitAssemblies;
                    return;
                }

                // Rebind assembly references that were initially missing. All bindings established above
                // are against explicitly specified references.

                allAssemblies = explicitAssemblies.AddRange(implicitAssemblies);

                for (int bindingsIndex = 0; bindingsIndex < referenceBindings.Count; bindingsIndex++)
                {
                    var referenceBinding = referenceBindings[bindingsIndex];

                    for (int i = 0; i < referenceBinding.Length; i++)
                    {
                        var binding = referenceBinding[i];

                        // We don't rebind references bound to a non-matching version of a reference that was explicitly
                        // specified, even if we have a better version now.
                        if (binding.IsBound)
                        {
                            continue;
                        }

                        // We only need to resolve against implicitly resolved assemblies,
                        // since we already resolved against explicitly specified ones.
                        referenceBinding[i] = ResolveReferencedAssembly(
                            binding.ReferenceIdentity,
                            allAssemblies,
                            explicitAssemblyCount,
                            IdentityComparer);
                    }
                }

                UpdateBindingsOfAssemblyBeingBuilt(referenceBindings, explicitAssemblyCount, implicitAssemblies);

                metadataReferences = metadataReferencesBuilder.ToImmutable();
                resolvedReferences = ToResolvedAssemblyReferences(metadataReferences, lazyAliasMap, explicitAssemblyCount);
            }
            finally
            {
                implicitAssemblies.Free();
                requestedIdentities.Free();
                referenceBindingsToProcess.Free();
                metadataReferencesBuilder.Free();
                previouslyResolvedAssembliesOpt?.Free();
            }
        }
        private static TaintedDataConfig Create(Compilation compilation)
        {
            WellKnownTypeProvider wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(compilation);

            using PooledDictionary <SinkKind, Lazy <TaintedDataSymbolMap <SourceInfo> > > sourceSymbolMapBuilder =
                      PooledDictionary <SinkKind, Lazy <TaintedDataSymbolMap <SourceInfo> > > .GetInstance();

            using PooledDictionary <SinkKind, Lazy <TaintedDataSymbolMap <SanitizerInfo> > > sanitizerSymbolMapBuilder =
                      PooledDictionary <SinkKind, Lazy <TaintedDataSymbolMap <SanitizerInfo> > > .GetInstance();

            using PooledDictionary <SinkKind, Lazy <TaintedDataSymbolMap <SinkInfo> > > sinkSymbolMapBuilder =
                      PooledDictionary <SinkKind, Lazy <TaintedDataSymbolMap <SinkInfo> > > .GetInstance();

            // For tainted data rules with the same set of sources, we'll reuse the same TaintedDataSymbolMap<SourceInfo> instance.
            // Same for sanitizers.
            using PooledDictionary <ImmutableHashSet <SourceInfo>, Lazy <TaintedDataSymbolMap <SourceInfo> > > sourcesToSymbolMap =
                      PooledDictionary <ImmutableHashSet <SourceInfo>, Lazy <TaintedDataSymbolMap <SourceInfo> > > .GetInstance();

            using PooledDictionary <ImmutableHashSet <SanitizerInfo>, Lazy <TaintedDataSymbolMap <SanitizerInfo> > > sanitizersToSymbolMap =
                      PooledDictionary <ImmutableHashSet <SanitizerInfo>, Lazy <TaintedDataSymbolMap <SanitizerInfo> > > .GetInstance();

            // Build a mapping of (sourceSet, sanitizerSet) -> (sinkKinds, sinkSet), so we'll reuse the same TaintedDataSymbolMap<SinkInfo> instance.
            using PooledDictionary <(ImmutableHashSet <SourceInfo> SourceInfos, ImmutableHashSet <SanitizerInfo> SanitizerInfos), (ImmutableHashSet <SinkKind> .Builder SinkKinds, ImmutableHashSet <SinkInfo> .Builder SinkInfos)> sourceSanitizersToSinks =
                      PooledDictionary <(ImmutableHashSet <SourceInfo> SourceInfos, ImmutableHashSet <SanitizerInfo> SanitizerInfos), (ImmutableHashSet <SinkKind> .Builder SinkKinds, ImmutableHashSet <SinkInfo> .Builder SinkInfos)> .GetInstance();

            // Using LazyThreadSafetyMode.ExecutionAndPublication to avoid instantiating multiple times.
            foreach (SinkKind sinkKind in Enum.GetValues(typeof(SinkKind)))
            {
                ImmutableHashSet <SourceInfo> sources = GetSourceInfos(sinkKind);
                if (!sourcesToSymbolMap.TryGetValue(sources, out Lazy <TaintedDataSymbolMap <SourceInfo> > lazySourceSymbolMap))
                {
                    lazySourceSymbolMap = new Lazy <TaintedDataSymbolMap <SourceInfo> >(
                        () => { return(new TaintedDataSymbolMap <SourceInfo>(wellKnownTypeProvider, sources)); },
                        LazyThreadSafetyMode.ExecutionAndPublication);
                    sourcesToSymbolMap.Add(sources, lazySourceSymbolMap);
                }

                sourceSymbolMapBuilder.Add(sinkKind, lazySourceSymbolMap);

                ImmutableHashSet <SanitizerInfo> sanitizers = GetSanitizerInfos(sinkKind);
                if (!sanitizersToSymbolMap.TryGetValue(sanitizers, out Lazy <TaintedDataSymbolMap <SanitizerInfo> > lazySanitizerSymbolMap))
                {
                    lazySanitizerSymbolMap = new Lazy <TaintedDataSymbolMap <SanitizerInfo> >(
                        () => { return(new TaintedDataSymbolMap <SanitizerInfo>(wellKnownTypeProvider, sanitizers)); },
                        LazyThreadSafetyMode.ExecutionAndPublication);
                    sanitizersToSymbolMap.Add(sanitizers, lazySanitizerSymbolMap);
                }

                sanitizerSymbolMapBuilder.Add(sinkKind, lazySanitizerSymbolMap);

                ImmutableHashSet <SinkInfo> sinks = GetSinkInfos(sinkKind);
                if (!sourceSanitizersToSinks.TryGetValue((sources, sanitizers), out (ImmutableHashSet <SinkKind> .Builder SinkKinds, ImmutableHashSet <SinkInfo> .Builder SinkInfos)sinksPair))
                {
                    sinksPair = (ImmutableHashSet.CreateBuilder <SinkKind>(), ImmutableHashSet.CreateBuilder <SinkInfo>());
                    sourceSanitizersToSinks.Add((sources, sanitizers), sinksPair);
                }

                sinksPair.SinkKinds.Add(sinkKind);
                sinksPair.SinkInfos.UnionWith(sinks);
            }

            foreach (KeyValuePair <(ImmutableHashSet <SourceInfo> SourceInfos, ImmutableHashSet <SanitizerInfo> SanitizerInfos), (ImmutableHashSet <SinkKind> .Builder SinkKinds, ImmutableHashSet <SinkInfo> .Builder SinkInfos)> kvp in sourceSanitizersToSinks)
            {
                ImmutableHashSet <SinkInfo>             sinks             = kvp.Value.SinkInfos.ToImmutable();
                Lazy <TaintedDataSymbolMap <SinkInfo> > lazySinkSymbolMap = new Lazy <TaintedDataSymbolMap <SinkInfo> >(
                    () => { return(new TaintedDataSymbolMap <SinkInfo>(wellKnownTypeProvider, sinks)); },
                    LazyThreadSafetyMode.ExecutionAndPublication);
                foreach (SinkKind sinkKind in kvp.Value.SinkKinds)
                {
                    sinkSymbolMapBuilder.Add(sinkKind, lazySinkSymbolMap);
                }
            }

            return(new TaintedDataConfig(
                       wellKnownTypeProvider,
                       sourceSymbolMapBuilder.ToImmutableDictionary(),
                       sanitizerSymbolMapBuilder.ToImmutableDictionary(),
                       sinkSymbolMapBuilder.ToImmutableDictionary()));
        }
Пример #19
0
        private static void ProcessBody <TSyntaxKind>(
            SemanticModel semanticModel,
            ImmutableArray <IDiagnosticAnalyzer> analyzers,
            ICodeBlockStartedAnalyzer[] bodyAnalyzers,
            ISymbol symbol,
            SyntaxNode syntax,
            CancellationToken cancellationToken,
            Action <Diagnostic> addDiagnostic,
            AnalyzerOptions analyzerOptions,
            bool continueOnError,
            Func <SyntaxNode, TSyntaxKind> getKind)
        {
            var endedAnalyzers = ArrayBuilder <ICodeBlockEndedAnalyzer> .GetInstance();

            PooledDictionary <TSyntaxKind, ArrayBuilder <ISyntaxNodeAnalyzer <TSyntaxKind> > > nodeAnalyzersByKind = null;

            foreach (var a in bodyAnalyzers)
            {
                // Catch Exception from a.OnCodeBlockStarted
                ExecuteAndCatchIfThrows(a, addDiagnostic, continueOnError, cancellationToken, () =>
                {
                    var analyzer = a.OnCodeBlockStarted(syntax, symbol, semanticModel, addDiagnostic, analyzerOptions, cancellationToken);
                    if (analyzer != null && analyzer != a)
                    {
                        endedAnalyzers.Add(analyzer);
                    }
                });
            }

            foreach (var nodeAnalyzer in endedAnalyzers.Concat(analyzers).OfType <ISyntaxNodeAnalyzer <TSyntaxKind> >())
            {
                // Catch Exception from  nodeAnalyzer.SyntaxKindsOfInterest
                try
                {
                    foreach (var kind in nodeAnalyzer.SyntaxKindsOfInterest)
                    {
                        if (nodeAnalyzersByKind == null)
                        {
                            nodeAnalyzersByKind = PooledDictionary <TSyntaxKind, ArrayBuilder <ISyntaxNodeAnalyzer <TSyntaxKind> > > .GetInstance();
                        }
                        ArrayBuilder <ISyntaxNodeAnalyzer <TSyntaxKind> > analyzersForKind;
                        if (!nodeAnalyzersByKind.TryGetValue(kind, out analyzersForKind))
                        {
                            nodeAnalyzersByKind.Add(kind, analyzersForKind = ArrayBuilder <ISyntaxNodeAnalyzer <TSyntaxKind> > .GetInstance());
                        }
                        analyzersForKind.Add(nodeAnalyzer);
                    }
                }
                catch (Exception e)
                {
                    // Create a info diagnostic saying that the analyzer failed
                    addDiagnostic(GetAnalyzerDiagnostic(nodeAnalyzer, e));
                }
            }

            if (nodeAnalyzersByKind != null)
            {
                foreach (var child in syntax.DescendantNodesAndSelf())
                {
                    ArrayBuilder <ISyntaxNodeAnalyzer <TSyntaxKind> > analyzersForKind;
                    if (nodeAnalyzersByKind.TryGetValue(getKind(child), out analyzersForKind))
                    {
                        foreach (var analyzer in analyzersForKind)
                        {
                            // Catch Exception from analyzer.AnalyzeNode
                            ExecuteAndCatchIfThrows(analyzer, addDiagnostic, continueOnError, cancellationToken, () => { analyzer.AnalyzeNode(child, semanticModel, addDiagnostic, analyzerOptions, cancellationToken); });
                        }
                    }
                }

                foreach (var b in nodeAnalyzersByKind.Values)
                {
                    b.Free();
                }
                nodeAnalyzersByKind.Free();
            }

            foreach (var a in endedAnalyzers.Concat(analyzers.OfType <ICodeBlockEndedAnalyzer>()))
            {
                // Catch Exception from a.OnCodeBlockEnded
                ExecuteAndCatchIfThrows(a, addDiagnostic, continueOnError, cancellationToken, () => { a.OnCodeBlockEnded(syntax, symbol, semanticModel, addDiagnostic, analyzerOptions, cancellationToken); });
            }

            endedAnalyzers.Free();
        }
Пример #20
0
        private static TBlockAnalysisData RunCore(
            ImmutableArray <BasicBlock> blocks,
            DataFlowAnalyzer <TBlockAnalysisData> analyzer,
            int firstBlockOrdinal,
            int lastBlockOrdinal,
            TBlockAnalysisData initialAnalysisData,
            ArrayBuilder <BasicBlock> unreachableBlocksToVisit,
            SortedSet <int> outOfRangeBlocksToVisit,
            PooledDictionary <ControlFlowRegion, bool> continueDispatchAfterFinally,
            PooledHashSet <ControlFlowRegion> dispatchedExceptionsFromRegions,
            CancellationToken cancellationToken)
        {
            var toVisit = new SortedSet <int>();

            var firstBlock = blocks[firstBlockOrdinal];

            analyzer.SetCurrentAnalysisData(firstBlock, initialAnalysisData, cancellationToken);
            toVisit.Add(firstBlock.Ordinal);

            var processedBlocks = PooledHashSet <BasicBlock> .GetInstance();

            TBlockAnalysisData resultAnalysisData = default;

            do
            {
                cancellationToken.ThrowIfCancellationRequested();

                BasicBlock current;
                if (toVisit.Count > 0)
                {
                    var min = toVisit.Min;
                    toVisit.Remove(min);
                    current = blocks[min];
                }
                else
                {
                    int index;
                    current = null;
                    for (index = 0; index < unreachableBlocksToVisit.Count; index++)
                    {
                        var unreachableBlock = unreachableBlocksToVisit[index];
                        if (unreachableBlock.Ordinal >= firstBlockOrdinal && unreachableBlock.Ordinal <= lastBlockOrdinal)
                        {
                            current = unreachableBlock;
                            break;
                        }
                    }

                    if (current == null)
                    {
                        continue;
                    }

                    unreachableBlocksToVisit.RemoveAt(index);
                    if (processedBlocks.Contains(current))
                    {
                        // Already processed from a branch from another unreachable block.
                        continue;
                    }

                    analyzer.SetCurrentAnalysisData(current, analyzer.GetEmptyAnalysisData(), cancellationToken);
                }

                if (current.Ordinal < firstBlockOrdinal || current.Ordinal > lastBlockOrdinal)
                {
                    outOfRangeBlocksToVisit.Add(current.Ordinal);
                    continue;
                }

                if (current.Ordinal == current.EnclosingRegion.FirstBlockOrdinal)
                {
                    // We are revisiting first block of a region, so we need to again dispatch exceptions from region.
                    dispatchedExceptionsFromRegions.Remove(current.EnclosingRegion);
                }

                TBlockAnalysisData fallThroughAnalysisData = analyzer.AnalyzeBlock(current, cancellationToken);
                bool fallThroughSuccessorIsReachable       = true;

                if (current.ConditionKind != ControlFlowConditionKind.None)
                {
                    TBlockAnalysisData conditionalSuccessorAnalysisData;
                    (fallThroughAnalysisData, conditionalSuccessorAnalysisData) = analyzer.AnalyzeConditionalBranch(current, fallThroughAnalysisData, cancellationToken);

                    bool conditionalSuccesorIsReachable = true;
                    if (current.BranchValue.ConstantValue.HasValue && current.BranchValue.ConstantValue.Value is bool constant)
                    {
                        if (constant == (current.ConditionKind == ControlFlowConditionKind.WhenTrue))
                        {
                            fallThroughSuccessorIsReachable = false;
                        }
                        else
                        {
                            conditionalSuccesorIsReachable = false;
                        }
                    }

                    if (conditionalSuccesorIsReachable || analyzer.AnalyzeUnreachableBlocks)
                    {
                        FollowBranch(current, current.ConditionalSuccessor, conditionalSuccessorAnalysisData);
                    }
                }
                else
                {
                    fallThroughAnalysisData = analyzer.AnalyzeNonConditionalBranch(current, fallThroughAnalysisData, cancellationToken);
                }

                if (fallThroughSuccessorIsReachable || analyzer.AnalyzeUnreachableBlocks)
                {
                    ControlFlowBranch branch = current.FallThroughSuccessor;
                    FollowBranch(current, branch, fallThroughAnalysisData);

                    if (current.EnclosingRegion.Kind == ControlFlowRegionKind.Finally &&
                        current.Ordinal == lastBlockOrdinal)
                    {
                        continueDispatchAfterFinally[current.EnclosingRegion] = branch.Semantics != ControlFlowBranchSemantics.Throw &&
                                                                                branch.Semantics != ControlFlowBranchSemantics.Rethrow &&
                                                                                current.FallThroughSuccessor.Semantics == ControlFlowBranchSemantics.StructuredExceptionHandling;
                    }
                }

                if (current.Ordinal == lastBlockOrdinal)
                {
                    resultAnalysisData = fallThroughAnalysisData;
                }

                // We are using very simple approach:
                // If try block is reachable, we should dispatch an exception from it, even if it is empty.
                // To simplify implementation, we dispatch exception from every reachable basic block and rely
                // on dispatchedExceptionsFromRegions cache to avoid doing duplicate work.
                DispatchException(current.EnclosingRegion);

                processedBlocks.Add(current);
            }while (toVisit.Count != 0 || unreachableBlocksToVisit.Count != 0);

            return(resultAnalysisData);

            // Local functions.
            void FollowBranch(BasicBlock current, ControlFlowBranch branch, TBlockAnalysisData currentAnalsisData)
            {
                if (branch == null)
                {
                    return;
                }

                switch (branch.Semantics)
                {
                case ControlFlowBranchSemantics.None:
                case ControlFlowBranchSemantics.ProgramTermination:
                case ControlFlowBranchSemantics.StructuredExceptionHandling:
                case ControlFlowBranchSemantics.Error:
                    Debug.Assert(branch.Destination == null);
                    return;

                case ControlFlowBranchSemantics.Throw:
                case ControlFlowBranchSemantics.Rethrow:
                    Debug.Assert(branch.Destination == null);
                    StepThroughFinally(current.EnclosingRegion, destinationOrdinal: lastBlockOrdinal, ref currentAnalsisData);
                    return;

                case ControlFlowBranchSemantics.Regular:
                case ControlFlowBranchSemantics.Return:
                    Debug.Assert(branch.Destination != null);

                    if (StepThroughFinally(current.EnclosingRegion, branch.Destination.Ordinal, ref currentAnalsisData))
                    {
                        var destination            = branch.Destination;
                        var currentDestinationData = analyzer.GetCurrentAnalysisData(destination);
                        var mergedAnalysisData     = analyzer.Merge(currentDestinationData, currentAnalsisData, cancellationToken);
                        // We need to analyze the destination block if both the following conditions are met:
                        //  1. Either the current block is reachable both destination and current are non-reachable
                        //  2. Either the new analysis data for destination has changed or destination block hasn't
                        //     been processed.
                        if ((current.IsReachable || !destination.IsReachable) &&
                            (!analyzer.IsEqual(currentDestinationData, mergedAnalysisData) || !processedBlocks.Contains(destination)))
                        {
                            analyzer.SetCurrentAnalysisData(destination, mergedAnalysisData, cancellationToken);
                            toVisit.Add(branch.Destination.Ordinal);
                        }
                    }

                    return;

                default:
                    throw ExceptionUtilities.UnexpectedValue(branch.Semantics);
                }
            }

            // Returns whether we should proceed to the destination after finallies were taken care of.
            bool StepThroughFinally(ControlFlowRegion region, int destinationOrdinal, ref TBlockAnalysisData currentAnalysisData)
            {
                while (!region.ContainsBlock(destinationOrdinal))
                {
                    Debug.Assert(region.Kind != ControlFlowRegionKind.Root);
                    ControlFlowRegion enclosing = region.EnclosingRegion;
                    if (region.Kind == ControlFlowRegionKind.Try && enclosing.Kind == ControlFlowRegionKind.TryAndFinally)
                    {
                        Debug.Assert(enclosing.NestedRegions[0] == region);
                        Debug.Assert(enclosing.NestedRegions[1].Kind == ControlFlowRegionKind.Finally);
                        if (!StepThroughSingleFinally(enclosing.NestedRegions[1], ref currentAnalysisData))
                        {
                            // The point that continues dispatch is not reachable. Cancel the dispatch.
                            return(false);
                        }
                    }

                    region = enclosing;
                }

                return(true);
            }

            // Returns whether we should proceed with dispatch after finally was taken care of.
            bool StepThroughSingleFinally(ControlFlowRegion @finally, ref TBlockAnalysisData currentAnalysisData)
            {
                Debug.Assert(@finally.Kind == ControlFlowRegionKind.Finally);
                var previousAnalysisData = analyzer.GetCurrentAnalysisData(blocks[@finally.FirstBlockOrdinal]);
                var mergedAnalysisData   = analyzer.Merge(previousAnalysisData, currentAnalysisData, cancellationToken);

                if (!analyzer.IsEqual(previousAnalysisData, mergedAnalysisData))
                {
                    // For simplicity, we do a complete walk of the finally/filter region in isolation
                    // to make sure that the resume dispatch point is reachable from its beginning.
                    // It could also be reachable through invalid branches into the finally and we don't want to consider
                    // these cases for regular finally handling.
                    currentAnalysisData = RunCore(blocks,
                                                  analyzer,
                                                  @finally.FirstBlockOrdinal,
                                                  @finally.LastBlockOrdinal,
                                                  mergedAnalysisData,
                                                  unreachableBlocksToVisit,
                                                  outOfRangeBlocksToVisit: toVisit,
                                                  continueDispatchAfterFinally,
                                                  dispatchedExceptionsFromRegions,
                                                  cancellationToken);
                }

                if (!continueDispatchAfterFinally.TryGetValue(@finally, out bool dispatch))
                {
                    dispatch = false;
                    continueDispatchAfterFinally.Add(@finally, false);
                }

                return(dispatch);
            }

            void DispatchException(ControlFlowRegion fromRegion)
            {
                do
                {
                    if (!dispatchedExceptionsFromRegions.Add(fromRegion))
                    {
                        return;
                    }

                    ControlFlowRegion enclosing = fromRegion.Kind == ControlFlowRegionKind.Root ? null : fromRegion.EnclosingRegion;
                    if (fromRegion.Kind == ControlFlowRegionKind.Try)
                    {
                        switch (enclosing.Kind)
                        {
                        case ControlFlowRegionKind.TryAndFinally:
                            Debug.Assert(enclosing.NestedRegions[0] == fromRegion);
                            Debug.Assert(enclosing.NestedRegions[1].Kind == ControlFlowRegionKind.Finally);
                            var currentAnalysisData = analyzer.GetCurrentAnalysisData(blocks[fromRegion.FirstBlockOrdinal]);
                            if (!StepThroughSingleFinally(enclosing.NestedRegions[1], ref currentAnalysisData))
                            {
                                // The point that continues dispatch is not reachable. Cancel the dispatch.
                                return;
                            }
                            break;

                        case ControlFlowRegionKind.TryAndCatch:
                            Debug.Assert(enclosing.NestedRegions[0] == fromRegion);
                            DispatchExceptionThroughCatches(enclosing, startAt: 1);
                            break;

                        default:
                            throw ExceptionUtilities.UnexpectedValue(enclosing.Kind);
                        }
                    }
                    else if (fromRegion.Kind == ControlFlowRegionKind.Filter)
                    {
                        // If filter throws, dispatch is resumed at the next catch with an original exception
                        Debug.Assert(enclosing.Kind == ControlFlowRegionKind.FilterAndHandler);
                        ControlFlowRegion tryAndCatch = enclosing.EnclosingRegion;
                        Debug.Assert(tryAndCatch.Kind == ControlFlowRegionKind.TryAndCatch);

                        int index = tryAndCatch.NestedRegions.IndexOf(enclosing, startIndex: 1);

                        if (index > 0)
                        {
                            DispatchExceptionThroughCatches(tryAndCatch, startAt: index + 1);
                            fromRegion = tryAndCatch;
                            continue;
                        }

                        throw ExceptionUtilities.Unreachable;
                    }

                    fromRegion = enclosing;
                }while (fromRegion != null);
            }

            void DispatchExceptionThroughCatches(ControlFlowRegion tryAndCatch, int startAt)
            {
                // For simplicity, we do not try to figure out whether a catch clause definitely
                // handles all exceptions.

                Debug.Assert(tryAndCatch.Kind == ControlFlowRegionKind.TryAndCatch);
                Debug.Assert(startAt > 0);
                Debug.Assert(startAt <= tryAndCatch.NestedRegions.Length);

                for (int i = startAt; i < tryAndCatch.NestedRegions.Length; i++)
                {
                    ControlFlowRegion @catch = tryAndCatch.NestedRegions[i];

                    switch (@catch.Kind)
                    {
                    case ControlFlowRegionKind.Catch:
                        toVisit.Add(@catch.FirstBlockOrdinal);
                        break;

                    case ControlFlowRegionKind.FilterAndHandler:
                        BasicBlock entryBlock = blocks[@catch.FirstBlockOrdinal];
                        Debug.Assert(@catch.NestedRegions[0].Kind == ControlFlowRegionKind.Filter);
                        Debug.Assert(entryBlock.Ordinal == @catch.NestedRegions[0].FirstBlockOrdinal);

                        toVisit.Add(entryBlock.Ordinal);
                        break;

                    default:
                        throw ExceptionUtilities.UnexpectedValue(@catch.Kind);
                    }
                }
            }
        }
Пример #21
0
 public bool TryGetValue(TKey key, out TValue value)
 {
     Debug.Assert(!IsDisposed);
     return(_coreAnalysisData.TryGetValue(key, out value));
 }
Пример #22
0
        protected void AddStoredItemsFoldout()
        {
            if (!Application.isPlaying)
            {
                return;
            }

            this.showStored = EditorGUILayout.Foldout(this.showStored, "Stored Items");
            if (!this.showStored)
            {
                return;
            }

            EditorGUI.indentLevel++;

            var entityStore = this.target as EntityStore;

            using (var storedIds = ListPool <string> .Get())
            {
                entityStore.GetAllStoredKeys(storedIds);

                var defaultColor        = GUI.color;
                var defaultContentColor = GUI.contentColor;

                var now = DateTimeOffset.Now;

                foreach (var id in storedIds)
                {
                    var           foldoutStyle = EditorStyles.foldout;
                    ResolveStatus loadStatus;
                    if (!entityStore.GetResolveStatus(id, out loadStatus))
                    {
                        continue;
                    }

                    if (!string.IsNullOrEmpty(loadStatus.resolveError))
                    {
                        GUI.color        = ERROR;
                        GUI.contentColor = ERROR;
                    }
                    else if (loadStatus.isResolveInProgress)
                    {
                        GUI.color        = IN_PROGRESS;
                        GUI.contentColor = IN_PROGRESS;
                    }
                    else if (loadStatus.IsExpiredAt(now))
                    {
                        GUI.color        = EXPIRED;
                        GUI.contentColor = EXPIRED;

                        foldoutStyle = new GUIStyle(foldoutStyle);
                        foldoutStyle.normal.textColor = EXPIRED;
                    }
                    else
                    {
                        GUI.color        = loadStatus.hasResolved ? defaultColor : NONE;
                        GUI.contentColor = GUI.color;
                    }

                    bool showingDetails = false;
                    m_isExpandedById.TryGetValue(id, out showingDetails);

                    var foldoutString = this.foldoutStringDelegate != null?
                                        this.foldoutStringDelegate(id) : id;

                    var showDetails = EditorGUILayout.Foldout(showingDetails, new GUIContent(foldoutString), foldoutStyle);

                    if (showDetails != showingDetails)
                    {
                        m_isExpandedById[id] = showDetails;
                    }

                    if (!showDetails)
                    {
                        continue;
                    }

                    if (!string.IsNullOrEmpty(loadStatus.resolveError))
                    {
                        EditorGUILayout.LabelField("error", loadStatus.resolveError);
                    }
                    else if (loadStatus.isResolveInProgress)
                    {
                        EditorGUILayout.LabelField("loading for ", (DateTimeOffset.Now - loadStatus.updatedAt).TotalSeconds + "secs");
                        GUI.color = IN_PROGRESS;
                    }
                    else if (loadStatus.IsExpiredAt(now))
                    {
                        EditorGUILayout.LabelField("expired at",
                                                   loadStatus.timestamp.AddSeconds(loadStatus.maxAgeSecs).ToLocalTime().ToString("yyyy-MM-ddTHH:mm:ss.fff"));
                    }
                    else
                    {
                        GUI.color = loadStatus.hasResolved ? defaultColor : NONE;
                    }

                    if (loadStatus.hasResolved)
                    {
                        EditorGUILayout.LabelField("timestamp", loadStatus.timestamp.ToLocalTime().ToString("yyyy-MM-ddTHH:mm:ss.fff"));
                        EditorGUILayout.LabelField("max age", loadStatus.maxAgeSecs.ToString());
                    }

                    if (this.foldoutPropsDelegate != null)
                    {
                        using (var props = ListPool <KeyValuePair <string, string> > .Get()) {
                            this.foldoutPropsDelegate(id, props);
                            foreach (var p in props)
                            {
                                EditorGUILayout.LabelField(new GUIContent(p.Key),
                                                           new GUIContent(p.Value),
                                                           EditorStyles.wordWrappedLabel
                                                           );
                            }
                        }
                    }
                    else
                    {
                        try {
                            object data;
                            if (entityStore.GetDataAsObject(id, out data))
                            {
                                EditorGUILayout.TextArea(JsonUtility.ToJson(data, true));
                            }
                        }
                        catch (Exception e) {
                            Debug.LogError(e);
                        }
                    }

                    GUI.contentColor = defaultContentColor;

                    EditorGUILayout.Separator();
                }

                EditorGUI.indentLevel--;
            }
        }
#pragma warning disable CS8767 // Nullability of reference types in type of parameter doesn't match implicitly implemented member because of nullability attributes. https://github.com/dotnet/roslyn/issues/42552
        public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
#pragma warning restore CS8767 // Nullability of reference types in type of parameter doesn't match implicitly implemented member because of nullability attributes.
        {
            Debug.Assert(!IsDisposed);
            return(_coreAnalysisData.TryGetValue(key, out value));
        }