Code/Wasm/Interpret/Variable.cs
using System;

namespace WasmBox.Wasm.Interpret {
    /// <summary>
    /// Describes a WebAssembly variable.
    /// </summary>
    public sealed class Variable {
        /// <summary>
        /// Creates a variable with the given value, type and mutability.
        /// </summary>
        /// <param name="value">The variable's value.</param>
        /// <param name="type">The variable's type.</param>
        /// <param name="isMutable">The variable's mutability.</param>
        private Variable(object value, WasmValueType type, bool isMutable) {
            this.val = value;
            this.Type = type;
            this.IsMutable = isMutable;
        }

        /// <summary>
        /// The variable's value.
        /// </summary>
        private object val;

        /// <summary>
        /// Gets this variable's type.
        /// </summary>
        /// <returns>The variable's type.</returns>
        public WasmValueType Type { get; private set; }

        /// <summary>
        /// Gets this variable's mutability.
        /// </summary>
        /// <returns>The variable's mutability.</returns>
        public bool IsMutable { get; private set; }

        /// <summary>
        /// Gets this variable's value.
        /// </summary>
        /// <returns>The variable's value.</returns>
        public T Get<T>() {
            return (T)val;
        }

        /// <summary>
        /// Sets this variable's value.
        /// </summary>
        /// <param name="Value">The variable's new value.</param>
        public void Set<T>(T Value) {
            if (!IsMutable) {
                throw new WasmException("Cannot assign a value to an immutable variable.");
            }

            if (!IsInstanceOf( Value, Type)) {
                throw new WasmException(
                    "Cannot assign a value of type '" + GetTypeName(Value) +
                    "' to a variable of type '" + ((object)Type).ToString() + "'.");
            }

            val = Value;
        }

        /// <summary>
        /// Creates a new variable from the given value.
        /// </summary>
        /// <param name="type">The variable's type.</param>
        /// <param name="isMutable">The variable's mutability.</param>
        /// <param name="value">The variable's initial value.</param>
        /// <returns>The newly-created variable.</returns>
        public static Variable Create<T>(WasmValueType type, bool isMutable, T value) {
            if (!IsInstanceOf( value, type)) {
                throw new WasmException(
                    "Cannot create a variable of type '" + ((object)type).ToString() +
                    "' with an initial value of type '" + GetTypeName(value) + "'.");
            }

            return new Variable(value, type, isMutable);
        }

        /// <summary>
        /// Creates a new variable of the given type and mutability, and initializes
        /// it with the default value for the given type.
        /// </summary>
        /// <param name="type">The variable's type.</param>
        /// <param name="isMutable">The variable's mutability.</param>
        /// <returns>The newly-created variable.</returns>
        public static Variable CreateDefault(WasmValueType type, bool isMutable) {
            return Create( type, isMutable, GetDefaultValue(type));
        }

        /// <summary>
        /// Gets the default value for the given WebAssembly value tyoe.
        /// </summary>
        /// <param name="type">A WebAssembly value type.</param>
        /// <returns>The default value.</returns>
        public static object GetDefaultValue(WasmValueType type) {
            switch (type) {
                case WasmValueType.Int32:
                    return default(int);
                case WasmValueType.Int64:
                    return default(long);
                case WasmValueType.Float32:
                    return default(float);
                case WasmValueType.Float64:
                    return default(double);
                default:
                    throw new WasmException("Unknown value type: " + type);
            }
        }

        /// <summary>
        /// Checks if the given value is an instance of the given WebAssembly value type.
        /// </summary>
        /// <param name="value">A value.</param>
        /// <param name="type">A WebAssembly value type.</param>
        /// <returns>
        /// <c>true</c> if the given value is an instance of the given WebAssembly value type;
        /// otherwise, <c>false</c>.
        /// </returns>
        public static bool IsInstanceOf<T>(T value, WasmValueType type) {
            switch (type) {
                case WasmValueType.Int32:
                    return value is int;
                case WasmValueType.Int64:
                    return value is long;
                case WasmValueType.Float32:
                    return value is float;
                case WasmValueType.Float64:
                    return value is double;
                default:
                    throw new WasmException("Unknown value type: " + type);
            }
        }

        private static string GetTypeName(object value) {
            return value.GetType().Name;
        }
    }
}