Code/Wasm/Instructions/IfElseInstruction.cs
using System.Collections.Generic;
using System.IO;
using WasmBox.Wasm.Binary;

namespace WasmBox.Wasm.Instructions {
    /// <summary>
    /// Describes a WebAssembly stack machine instruction that runs one of two
    /// blocks of instructions, based on the value of its predicate.
    /// </summary>
    public sealed class IfElseInstruction : Instruction {
        /// <summary>
        /// Creates an if-else instruction from the given type, if-branch and
        /// else-branch.
        /// </summary>
        /// <param name="type">The type of value returned by the if-else instruction.</param>
        /// <param name="ifBranch">The if-else instruction's 'if' branch.</param>
        /// <param name="elseBranch">The if-else instruction's 'else' branch.</param>
        public IfElseInstruction(
            WasmType type,
            IEnumerable<Instruction> ifBranch,
            IEnumerable<Instruction> elseBranch) {
            this.Type = type;
            this.IfBranch = new List<Instruction>(ifBranch);
            this.ElseBranch = elseBranch == null ? null : new List<Instruction>(elseBranch);
        }

        /// <summary>
        /// Gets the operator for this instruction.
        /// </summary>
        /// <returns>The instruction's operator.</returns>
        public override Operator Op { get { return Operators.If; } }

        /// <summary>
        /// Gets the type of value returned by this instruction.
        /// </summary>
        /// <returns>The type of value returned by this instruction.</returns>
        public WasmType Type { get; set; }

        /// <summary>
        /// Gets this if-else instruction's contents for the 'if' branch.
        /// </summary>
        /// <returns>The if-else instruction's contents for the 'if' branch.</returns>
        public List<Instruction> IfBranch { get; private set; }

        /// <summary>
        /// Gets this if-else instruction's contents for the 'else' branch.
        /// </summary>
        /// <returns>
        /// The if-else instruction's contents for the 'else' branch.
        /// <c>null</c> is returned if there is no 'else' branch.
        /// </returns>
        public List<Instruction> ElseBranch { get; private set; }

        /// <summary>
        /// Checks if this if-else instruction has an 'else' branch.
        /// </summary>
        public bool HasElseBranch => ElseBranch != null;

        /// <summary>
        /// Writes this instruction's immediates (but not its opcode)
        /// to the given WebAssembly file writer.
        /// </summary>
        /// <param name="writer">The writer to write this instruction's immediates to.</param>
        public override void WriteImmediatesTo(BinaryWasmWriter writer) {
            writer.WriteWasmType(Type);
            WriteContentsTo(writer);
        }

        /// <summary>
        /// Writes this instruction's child instructions to the given WebAssembly file writer,
        /// followed by an 'end' opcode.
        /// </summary>
        /// <param name="writer">The writer to write this instruction's child instructions to.</param>
        public void WriteContentsTo(BinaryWasmWriter writer) {
            foreach (var instr in IfBranch) {
                instr.WriteTo(writer);
            }
            if (HasElseBranch) {
                writer.Writer.Write(Operators.ElseOpCode);
                foreach (var instr in ElseBranch) {
                    instr.WriteTo(writer);
                }
            }
            writer.Writer.Write(Operators.EndOpCode);
        }

        /// <summary>
        /// Writes a string representation of this instruction to the given text writer.
        /// </summary>
        /// <param name="writer">
        /// The writer to which a representation of this instruction is written.
        /// </param>
        public override void Dump(TextWriter writer) {
            Op.Dump(writer);
            writer.Write(" (result: ");
            DumpHelpers.DumpWasmType(Type, writer);
            writer.Write(")");
            var indentedWriter = DumpHelpers.CreateIndentedTextWriter(writer);
            foreach (var instr in IfBranch) {
                indentedWriter.WriteLine();
                instr.Dump(indentedWriter);
            }
            writer.WriteLine();
            if (HasElseBranch) {
                writer.Write("else");
                foreach (var instr in ElseBranch) {
                    indentedWriter.WriteLine();
                    instr.Dump(indentedWriter);
                }
                writer.WriteLine();
            }
            writer.Write("end");
        }
    }
}