public ConditionalItem(ByteCodeGenerator generator, OpCodeValue opCode, Chain trueChain, Chain falseChain)
            : base(generator, PrimativeTypes.Boolean)
        {
            OpCode = opCode;

            TrueJumps = trueChain;
            FalseJumps = falseChain;
        }
        public void ResolvePendingJumps()
        {
            var x = pendingJumps;
            pendingJumps = null;

            ResolveChain(x, length);
        }
        public void ResolveChain(Chain chain)
        {
            Debug.Assert(!alive || chain == null ||
                state.StackSize == chain.State.StackSize);

            pendingJumps = MergeChains(chain, pendingJumps);
        }
        public void ResolveChain(Chain chain, int target)
        {
            var changed = false;
            var newState = state;

            for (; chain != null; chain = chain.Next)
            {
                Debug.Assert(state != chain.State && (target > chain.PC || state.StackSize == 0));
                if (target >= length)
                {
                    target = length;
                }
                else if (byteCodeStream[target] == (byte)OpCodeValue.@goto)
                {
                    if (fatcode)
                    {
                        target = target + (byteCodeStream[target + 1] << 24)
                                        + (byteCodeStream[target + 2] << 16)
                                        + (byteCodeStream[target + 3] << 8)
                                        + (byteCodeStream[target + 4]);
                    }
                    else
                    {
                        target = target + (byteCodeStream[target + 1] << 8) + (byteCodeStream[target + 2]);
                    }
                }
                if (byteCodeStream[chain.PC] == (byte)OpCodeValue.@goto && chain.PC + 3 == target && target == length && !fixedPc)
                {
                    length = length - 3;
                    target = target - 3;
                    if (chain.Next == null)
                    {
                        alive = true;
                        break;
                    }
                }
                else
                {
                    var value = target - chain.PC;

                    if (fatcode)
                    {
                        byteCodeStream[chain.PC + 1] = (byte)((value >> 24) & 0xFF);
                        byteCodeStream[chain.PC + 2] = (byte)((value >> 16) & 0xFF);
                        byteCodeStream[chain.PC + 3] = (byte)((value >> 8) & 0xFF);
                        byteCodeStream[chain.PC + 4] = (byte)(value & 0xFF);
                    }
                    else if (value < short.MinValue || value > short.MaxValue)
                    {
                        fatcode = true;
                    }
                    else
                    {
                        byteCodeStream[chain.PC + 1] = (byte)((value >> 8) & 0xFF);
                        byteCodeStream[chain.PC + 2] = (byte)(value & 0xFF);
                    }
                    Debug.Assert(!alive || chain.State.StackSize == newState.StackSize);
                }

                fixedPc = true;
                if (length != target) continue;

                changed = true;

                if (alive)
                {
                    newState = chain.State.Join(newState);
                }
                else
                {
                    newState = chain.State;
                    alive = true;
                }
            }

            Debug.Assert(!changed || state != newState);
            if (state == newState) return;

            newState.MaxStackSize = Math.Max(newState.MaxStackSize, state.MaxStackSize);

            state = newState;
            pendingStackMap = true;
        }
        public Chain Branch(OpCodeValue opcode)
        {
            Chain result = null;

            if (opcode == OpCodeValue.@goto)
            {
                result = pendingJumps;
                pendingJumps = null;
            }
            if (opcode != OpCodeValue.jsr && alive)
            {
                result = new Chain(EmitJump(opcode), result, state.Clone());

                fixedPc = fatcode;

                if (opcode == OpCodeValue.@goto) alive = false;
            }

            return result;
        }
        public static Chain MergeChains(Chain chain1, Chain chain2)
        {
            // recursive merge sort
            if (chain2 == null) return chain1;
            if (chain1 == null) return chain2;

            Debug.Assert(chain1.State.StackSize == chain2.State.StackSize);

            var chain = chain1.PC < chain2.PC
                ? new Chain(chain2.PC, MergeChains(chain1, chain2.Next), chain2.State)
                : new Chain(chain1.PC, MergeChains(chain1.Next, chain2), chain1.State);

            chain.State.MaxStackSize = Math.Max(chain1.State.MaxStackSize, chain2.State.MaxStackSize);

            return chain;
        }