internal EvaluationContext(
            ITraceWriter trace,
            ISecretMasker secretMasker,
            Object state,
            EvaluationOptions options,
            ExpressionNode node)
        {
            ArgumentUtility.CheckForNull(trace, nameof(trace));
            ArgumentUtility.CheckForNull(secretMasker, nameof(secretMasker));
            Trace        = trace;
            SecretMasker = secretMasker;
            State        = state;

            // Copy the options
            options = new EvaluationOptions(copy: options);
            if (options.MaxMemory == 0)
            {
                // Set a reasonable default max memory
                options.MaxMemory = 1048576; // 1 mb
            }
            Options = options;
            Memory  = new EvaluationMemory(options.MaxMemory, node);

            m_traceResults = new Dictionary <ExpressionNode, String>();
            m_traceMemory  = new MemoryCounter(null, options.MaxMemory);
        }
        /// <summary>
        /// This function is intended only for ExpressionNode authors to call. The EvaluationContext
        /// caches result-state specific to the evaluation instance.
        /// </summary>
        public EvaluationResult Evaluate(EvaluationContext context)
        {
            // Evaluate
            Level = Container == null ? 0 : Container.Level + 1;
            TraceVerbose(context, Level, $"Evaluating {Name}:");
            var coreResult = EvaluateCore(context, out ResultMemory coreMemory);

            if (coreMemory == null)
            {
                coreMemory = new ResultMemory();
            }

            // Convert to canonical value
            var val = ExpressionUtility.ConvertToCanonicalValue(coreResult, out ValueKind kind, out Object raw);

            // The depth can be safely trimmed when the total size of the core result is known,
            // or when the total size of the core result can easily be determined.
            var trimDepth = coreMemory.IsTotal || (Object.ReferenceEquals(raw, null) && ExpressionUtility.IsPrimitive(kind));

            // Account for the memory overhead of the core result
            var coreBytes = coreMemory.Bytes ?? EvaluationMemory.CalculateBytes(raw ?? val);

            context.Memory.AddAmount(Level, coreBytes, trimDepth);

            // Account for the memory overhead of the conversion result
            if (!Object.ReferenceEquals(raw, null))
            {
                var conversionBytes = EvaluationMemory.CalculateBytes(val);
                context.Memory.AddAmount(Level, conversionBytes);
            }

            var result = new EvaluationResult(context, Level, val, kind, raw);

            // Store the trace result
            if (this.TraceFullyRealized)
            {
                context.SetTraceResult(this, result);
            }

            return(result);
        }