// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Runtime.CompilerServices;

namespace System.Linq.Expressions.Interpreter
{
    /// <summary>
    /// A simple forth-style stack machine for executing Expression trees
    /// without the need to compile to IL and then invoke the JIT.  This trades
    /// off much faster compilation time for a slower execution performance.
    /// For code that is only run a small number of times this can be a
    /// sweet spot.
    ///
    /// The core loop in the interpreter is the <see cref="Run(InterpretedFrame)"/>  method.
    /// </summary>
    internal sealed class Interpreter
    {
        internal static readonly object NoValue = new object();
        internal const int RethrowOnReturn = int.MaxValue;

        private readonly InstructionArray _instructions;
        internal readonly object[]? _objects;
        internal readonly RuntimeLabel[] _labels;
        internal readonly DebugInfo[] _debugInfos;

        internal Interpreter(string? name, LocalVariables locals, InstructionArray instructions, DebugInfo[] debugInfos)
        {
            Name = name;
            LocalCount = locals.LocalCount;
            ClosureVariables = locals.ClosureVariables;

            _instructions = instructions;
            _objects = instructions.Objects;
            _labels = instructions.Labels;
            _debugInfos = debugInfos;
        }

        internal string? Name { get; }
        internal int LocalCount { get; }
        internal int ClosureSize => ClosureVariables?.Count ?? 0;
        internal InstructionArray Instructions => _instructions;
        internal Dictionary<ParameterExpression, LocalVariable>? ClosureVariables { get; }

        /// <summary>
        /// Runs instructions within the given frame.
        /// </summary>
        /// <remarks>
        /// Interpreted stack frames are linked via Parent reference so that each CLR frame of this method corresponds
        /// to an interpreted stack frame in the chain. It is therefore possible to combine CLR stack traces with
        /// interpreted stack traces by aligning interpreted frames to the frames of this method.
        /// Each group of subsequent frames of Run method corresponds to a single interpreted frame.
        /// </remarks>
        [MethodImpl(MethodImplOptions.NoInlining)]
        public void Run(InterpretedFrame frame)
        {
            Instruction[] instructions = _instructions.Instructions;
            int index = frame.InstructionIndex;
            while (index < instructions.Length)
            {
                index += instructions[index].Run(frame);
                frame.InstructionIndex = index;
            }
        }
    }
}
