Wasm/Interpret/InterpreterContext.cs
using System.Collections.Generic;
using System.Linq;

namespace WasmBox.Wasm.Interpret {
    /// <summary>
    /// Describes the context in which instructions are interpreted.
    /// </summary>
    public sealed class InterpreterContext {
        /// <summary>
        /// Creates a new interpreter context from the given module and
        /// expected return types.
        /// </summary>
        /// <param name="module">The owning module.</param>
        /// <param name="returnTypes">The list of expected return types.</param>
        public InterpreterContext(ModuleInstance module, IReadOnlyList<WasmValueType> returnTypes)
            : this(module, returnTypes, new Variable[0]) { }

        /// <summary>
        /// Creates a new interpreter context from the given module, return types
        /// and list of local variables.
        /// </summary>
        /// <param name="module">The owning module.</param>
        /// <param name="returnTypes">The list of expected return types.</param>
        /// <param name="locals">The list of local variables in this context.</param>
        public InterpreterContext(
            ModuleInstance module,
            IReadOnlyList<WasmValueType> returnTypes,
            IReadOnlyList<Variable> locals)
            : this(module, returnTypes, locals, ExecutionPolicy.Create()) { }

        /// <summary>
        /// Creates a new interpreter context from the given module, return types
        /// and list of local variables.
        /// </summary>
        /// <param name="module">The owning module.</param>
        /// <param name="returnTypes">The list of expected return types.</param>
        /// <param name="locals">The list of local variables in this context.</param>
        /// <param name="policy">The execution policy to use.</param>
        /// <param name="callStackDepth">
        /// The current depth of the call stack.
        /// </param>
        public InterpreterContext(
            ModuleInstance module,
            IReadOnlyList<WasmValueType> returnTypes,
            IReadOnlyList<Variable> locals,
            ExecutionPolicy policy,
            uint callStackDepth = 0) {
            this.Module = module;
            this.ReturnTypes = returnTypes;
            this.Locals = locals;
            this.Policy = policy;
            this.valStack = new Stack<object>();
            this.ReturnValues = null;
            this.BreakDepth = -1;
            this.CallStackDepth = callStackDepth;
        }

        /// <summary>
        /// Gets the module instance that owns the instructions being interpreted.
        /// </summary>
        /// <returns>The module instance.</returns>
        public ModuleInstance Module { get; private set; }

        /// <summary>
        /// Gets the list of expected return types.
        /// </summary>
        /// <value>The expected return types.</value>
        public IReadOnlyList<WasmValueType> ReturnTypes { get; private set; }

        /// <summary>
        /// Gets a list of local variables for this interpreter context.
        /// </summary>
        /// <returns>A list of local variables.</returns>
        public IReadOnlyList<Variable> Locals { get; private set; }

        /// <summary>
        /// Gets the execution policy associated with this interpreter context.
        /// </summary>
        /// <value>An execution policy.</value>
        public ExecutionPolicy Policy { get; private set; }

        /// <summary>
        /// Gets the depth of the call stack at which the "frame" is placed for
        /// the instructions currently being executed.
        /// </summary>
        /// <value>A call stack depth.</value>
        public uint CallStackDepth { get; private set; }

        /// <summary>
        /// The evaluation stack.
        /// </summary>
        private Stack<object> valStack;

        /// <summary>
        /// Gets or sets the evaluation stack.
        /// </summary>
        /// <value>The evaluation stack.</value>
        public EvaluationStack Stack {
            get {
                return new EvaluationStack { stack = valStack };
            }
            set {
                this.valStack = value.stack;
            }
        }

        /// <summary>
        /// Gets the list of values that have been returned, or <c>null</c> if nothing
        /// has been returned yet.
        /// </summary>
        /// <returns>The list of values that have been returned, or <c>null</c> if nothing
        /// has been returned yet.</returns>
        public IReadOnlyList<object> ReturnValues { get; private set; }

        /// <summary>
        /// Gets the number of items that are currently on the evaluation stack.
        /// </summary>
        public int StackDepth => valStack.Count;

        /// <summary>
        /// Tests if this interpreter context has returned.
        /// </summary>
        public bool HasReturned => ReturnValues != null;

        /// <summary>
        /// Gets or sets the depth of the break that is currently being handled.
        /// </summary>
        /// <returns>The depth of the break that is currently being handled.
        /// A negative value means that no break is currently being handled.</returns>
        public int BreakDepth { get; set; }

        /// <summary>
        /// Gets a flag that tells if a break has been requested.
        /// </summary>
        /// <returns>A flag that tells if a break has been requested.</returns>
        public bool BreakRequested => BreakDepth >= 0;

        /// <summary>
        /// Pops a value of the given type from the value stack.
        /// </summary>
        /// <returns>The popped value.</returns>
        public T Pop<T>() {
            if (StackDepth == 0) {
                throw new WasmException("Cannot pop an element from an empty stack.");
            }

            return (T)valStack.Pop();
        }

        /// <summary>
        /// Pops an array of values of the given type from the value stack.
        /// </summary>
        /// <returns>The popped values.</returns>
        public T[] Pop<T>(int count) {
            var results = new T[count];
            for (int i = count - 1; i >= 0; i--) {
                results[i] = Pop<T>();
            }
            return results;
        }

        /// <summary>
        /// Sets the list of return values to the contents of the value stack,
        /// if nothing has been returned already.
        /// </summary>
        /// <returns>
        /// <c>true</c> if the contents of the value stack have been promoted
        /// to return values; otherwise, <c>false</c>.
        /// </returns>
        public bool Return() {
            if (HasReturned) {
                return false;
            }
            else {
                ReturnValues = valStack.ToArray();
                return true;
            }
        }

        /// <summary>
        /// Peeks a value of the given type from the value stack.
        /// </summary>
        /// <returns>The peeked value.</returns>
        public T Peek<T>() {
            if (StackDepth == 0) {
                throw new WasmException("Cannot peek an element from an empty stack.");
            }

            return (T)valStack.Peek();
        }

        /// <summary>
        /// Pushes the given value onto the value stack.
        /// </summary>
        /// <param name="value">The value to push onto the stack.</param>
        public void Push<T>(T value) {
            valStack.Push(value);
        }

        /// <summary>
        /// Pushes the given sequence of values onto the value stack.
        /// </summary>
        /// <param name="values">The list of values to push onto the stack.</param>
        public void Push<T>(IEnumerable<T> values) {
            foreach (var item in values) {
				Push( item);
            }
        }

        /// <summary>
        /// Pushes the contents of an evaluation stack onto this context's stack.
        /// </summary>
        /// <param name="stack">The stack to push onto this context's evaluation stack.</param>
        public void Push(EvaluationStack stack) {
			Push( stack.stack.Reverse());
        }

        /// <summary>
        /// Pushes the topmost <paramref name="count"/> elements of <paramref name="stack"/> onto this context's
        /// evaluation stack.
        /// </summary>
        /// <param name="stack">The stack to push onto this context's evaluation stack.</param>
        /// <param name="count">
        /// The number of elements to take from <paramref name="stack"/> and push onto this
        /// context's evaluation stack.
        /// </param>
        public void Push(EvaluationStack stack, int count) {
			Push( stack.stack.Take(count).Reverse());
        }

        /// <summary>
        /// Creates an empty evaluation stack.
        /// </summary>
        /// <returns>An empty evaluation stack.</returns>
        public EvaluationStack CreateStack() {
            return new EvaluationStack { stack = new Stack<object>() };
        }

        /// <summary>
        /// A data structure that represents the interpreter's value stack.
        /// </summary>
        public struct EvaluationStack {
            // Internal on purpose so we can keep the 'Stack<object>' an
            // implementation detail.
            internal Stack<object> stack;
        }
    }

    /// <summary>
    /// A description of an execution policy for WebAssembly modules.
    /// </summary>
    public sealed class ExecutionPolicy {
        private ExecutionPolicy() { }

        /// <summary>
        /// Creates a new execution policy.
        /// </summary>
        /// <param name="maxCallStackDepth">The maximal depth of the call stack.</param>
        /// <param name="maxMemorySize">
        /// The maximum size of any memory, in page units. A value of zero
        /// indicates that there is not maximum memory size.
        /// </param>
        /// <param name="enforceAlignment">
        /// Tells if memory access alignments should be taken to be normative instead
        /// of as hints.
        /// </param>
        /// <param name="translateExceptions">
        /// Tells if CLR exceptions should be translated to <see cref="TrapException"/> values.
        /// </param>
        public static ExecutionPolicy Create(
            uint maxCallStackDepth = 256,
            uint maxMemorySize = 0,
            bool enforceAlignment = false,
            bool translateExceptions = true) {
            return new ExecutionPolicy() {
                MaxCallStackDepth = maxCallStackDepth,
                EnforceAlignment = enforceAlignment,
                MaxMemorySize = maxMemorySize,
                TranslateExceptions = translateExceptions
            };
        }

        /// <summary>
        /// Tells if the alignment specified by memory instructions is to be taken as
        /// a mandatory alignment to which memory accesses must adhere instead of a mere
        /// hint.
        /// </summary>
        /// <value><c>true</c> if unaligned accesses must throw exceptions; otherwise, <c>false</c>.</value>
        /// <remarks>
        /// The WebAssembly specification states that memory instruction alignments do not
        /// affect execution semantics. In order to comply with the standard, this property
        /// should be set to <c>false</c> (the default).
        /// </remarks>
        public bool EnforceAlignment { get; private set; }

        /// <summary>
        /// Gets the maximal depth of the call stack.
        /// </summary>
        /// <value>A maximal call stack depth.</value>
        public uint MaxCallStackDepth { get; private set; }

        /// <summary>
        /// Gets the maximum size of any memory, in page units. A value of zero
        /// indicates that there is not maximum memory size.
        /// </summary>
        /// <value>The maximum memory size.</value>
        public uint MaxMemorySize { get; private set; }

        /// <summary>
        /// Tells if CLR exceptions should be translated to <see cref="TrapException"/> values.
        /// </summary>
        /// <value>
        /// <c>true</c> if WebAssembly execution should throw only <see cref="TrapException"/> values;
        /// <c>false</c> if it may also throw other types of exceptions.
        /// </value>
        public bool TranslateExceptions { get; private set; }
    }
}