DbgEngineLocalsValueNodeInfo[] GetNodesCore(DbgEvaluationInfo evalInfo, DbgValueNodeEvaluationOptions options, DbgLocalsValueNodeEvaluationOptions localsOptions)
        {
            DbgEngineLocalsValueNodeInfo[] valueNodes = null;
            try {
                var module = evalInfo.Frame.Module;
                if (module == null)
                {
                    return(Array.Empty <DbgEngineLocalsValueNodeInfo>());
                }
                var languageDebugInfo = evalInfo.Context.TryGetLanguageDebugInfo();
                if (languageDebugInfo == null)
                {
                    return(Array.Empty <DbgEngineLocalsValueNodeInfo>());
                }
                var methodDebugInfo = languageDebugInfo.MethodDebugInfo;

                // All the variables windows use the same cached module references so make sure we pass
                // in the same arguments so it won't get recreated every time the method gets called.
                var info       = dbgAliasProvider.GetAliases(evalInfo);
                var refsResult = dbgModuleReferenceProvider.GetModuleReferences(evalInfo.Runtime, evalInfo.Frame, info.typeReferences);
                if (refsResult.ErrorMessage != null)
                {
                    return new[] { CreateInternalErrorNode(evalInfo, refsResult.ErrorMessage) }
                }
                ;

                // Since we attach this to the module, the module doesn't have to be part of Key
                var state = StateWithKey <GetNodesState> .GetOrCreate(module, this);

                var localsOptionsKey = localsOptions & ~(DbgLocalsValueNodeEvaluationOptions.ShowCompilerGeneratedVariables | DbgLocalsValueNodeEvaluationOptions.ShowDecompilerGeneratedVariables);
                var key = new GetNodesState.Key(methodDebugInfo.DebugInfoVersion,
                                                methodDebugInfo.Method.MDToken.ToInt32(), languageDebugInfo.MethodVersion,
                                                refsResult.ModuleReferences, MethodDebugScopeUtils.GetScope(methodDebugInfo.Scope, languageDebugInfo.ILOffset),
                                                options, localsOptionsKey);

                var evalOptions = DbgEvaluationOptions.None;
                if ((options & DbgValueNodeEvaluationOptions.NoFuncEval) != 0)
                {
                    evalOptions |= DbgEvaluationOptions.NoFuncEval;
                }
                if ((localsOptions & DbgLocalsValueNodeEvaluationOptions.ShowRawLocals) != 0)
                {
                    evalOptions |= DbgEvaluationOptions.RawLocals;
                }

                ValueInfo[] valueInfos;
                byte[]      assemblyBytes;
                int         compilerGeneratedCount;
                int         decompilerGeneratedCount;
                if (key.Equals(state.CachedKey))
                {
                    valueInfos               = state.CachedValueInfos;
                    assemblyBytes            = state.CachedAssemblyBytes;
                    decompilerGeneratedCount = state.CachedDecompilerGeneratedCount;
                    compilerGeneratedCount   = state.CachedCompilerGeneratedCount;
                }
                else
                {
                    var compilationResult = expressionCompiler.CompileGetLocals(evalInfo, refsResult.ModuleReferences, evalOptions);
                    evalInfo.CancellationToken.ThrowIfCancellationRequested();
                    if (compilationResult.IsError)
                    {
                        return new[] { CreateInternalErrorNode(evalInfo, compilationResult.ErrorMessage) }
                    }
                    ;

                    decompilerGeneratedCount = GetDecompilerGeneratedVariablesCount(methodDebugInfo.Scope, languageDebugInfo.ILOffset);

                    valueInfos = new ValueInfo[compilationResult.CompiledExpressions.Length + decompilerGeneratedCount];
                    int valueInfosIndex = 0;
                    compilerGeneratedCount = 0;
                    for (int i = 0; i < compilationResult.CompiledExpressions.Length; i++, valueInfosIndex++)
                    {
                        if ((compilationResult.CompiledExpressions[i].ResultFlags & DbgDotNetCompiledExpressionResultFlags.CompilerGenerated) != 0)
                        {
                            compilerGeneratedCount++;
                        }
                        valueInfos[valueInfosIndex] = new CompiledExpressionValueInfo(compilationResult.CompiledExpressions, i);
                    }

                    if (decompilerGeneratedCount > 0)
                    {
                        var scope = methodDebugInfo.Scope;
                        for (;;)
                        {
                            foreach (var local in scope.Locals)
                            {
                                if (local.IsDecompilerGenerated)
                                {
                                    valueInfos[valueInfosIndex] = new DecompilerGeneratedVariableValueInfo(local.Name);
                                    valueInfosIndex++;
                                }
                            }

                            bool found = false;
                            foreach (var childScope in scope.Scopes)
                            {
                                if (childScope.Span.Start <= languageDebugInfo.ILOffset && languageDebugInfo.ILOffset < childScope.Span.End)
                                {
                                    found = true;
                                    scope = childScope;
                                    break;
                                }
                            }
                            if (!found)
                            {
                                break;
                            }
                        }
                    }

                    if (valueInfos.Length != valueInfosIndex)
                    {
                        throw new InvalidOperationException();
                    }

                    assemblyBytes                        = compilationResult.Assembly;
                    state.CachedKey                      = key;
                    state.CachedValueInfos               = valueInfos;
                    state.CachedAssemblyBytes            = assemblyBytes;
                    state.CachedILInterpreterState       = null;
                    state.CachedDecompilerGeneratedCount = decompilerGeneratedCount;
                    state.CachedCompilerGeneratedCount   = compilerGeneratedCount;
                }

                int count = valueInfos.Length;
                if ((localsOptions & DbgLocalsValueNodeEvaluationOptions.ShowCompilerGeneratedVariables) == 0)
                {
                    count -= compilerGeneratedCount;
                }
                if ((localsOptions & DbgLocalsValueNodeEvaluationOptions.ShowDecompilerGeneratedVariables) == 0)
                {
                    count -= decompilerGeneratedCount;
                }
                valueNodes = count == 0 ? Array.Empty <DbgEngineLocalsValueNodeInfo>() : new DbgEngineLocalsValueNodeInfo[count];
                var valueCreator = new DbgDotNetValueCreator(valueNodeFactory, dnILInterpreter, evalInfo, options, evalOptions, assemblyBytes);
                int w            = 0;
                for (int i = 0; i < valueInfos.Length; i++)
                {
                    evalInfo.CancellationToken.ThrowIfCancellationRequested();
                    var valueInfo = valueInfos[i];

                    DbgEngineLocalsValueNodeInfo valueNodeInfo;
                    switch (valueInfo.Kind)
                    {
                    case ValueInfoKind.CompiledExpression:
                        var compExpr = (CompiledExpressionValueInfo)valueInfo;
                        if ((localsOptions & DbgLocalsValueNodeEvaluationOptions.ShowCompilerGeneratedVariables) == 0 && compExpr.IsCompilerGenerated)
                        {
                            continue;
                        }
                        valueNodeInfo = new DbgEngineLocalsValueNodeInfo(
                            compExpr.IsParameter ? DbgLocalsValueNodeKind.Parameter : DbgLocalsValueNodeKind.Local,
                            valueCreator.CreateValueNode(ref state.CachedILInterpreterState, ref compExpr.CompiledExpressionResult));
                        break;

                    case ValueInfoKind.DecompilerGeneratedVariable:
                        if ((localsOptions & DbgLocalsValueNodeEvaluationOptions.ShowDecompilerGeneratedVariables) == 0)
                        {
                            continue;
                        }
                        var decGen = (DecompilerGeneratedVariableValueInfo)valueInfo;
                        valueNodeInfo = new DbgEngineLocalsValueNodeInfo(DbgLocalsValueNodeKind.Local,
                                                                         valueNodeFactory.CreateError(evalInfo,
                                                                                                      new DbgDotNetText(new DbgDotNetTextPart(DbgTextColor.Local, decGen.Name)),
                                                                                                      dnSpy_Debugger_DotNet_Resources.DecompilerGeneratedVariablesCanNotBeEvaluated,
                                                                                                      decGen.Name, false));
                        break;

                    default:
                        throw new InvalidOperationException();
                    }

                    valueNodes[w++] = valueNodeInfo;
                }
                if (w != valueNodes.Length)
                {
                    throw new InvalidOperationException();
                }

                return(valueNodes);
            }
            catch (Exception ex) {
                if (valueNodes != null)
                {
                    evalInfo.Runtime.Process.DbgManager.Close(valueNodes.Select(a => a.ValueNode).Where(a => a != null));
                }
                if (!ExceptionUtils.IsInternalDebuggerError(ex))
                {
                    throw;
                }
                return(new[] { CreateInternalErrorNode(evalInfo, PredefinedEvaluationErrorMessages.InternalDebuggerError) });
            }
        }