Ejemplo n.º 1
0
        public MondValue Call(MondValue closure, params MondValue[] arguments)
        {
            if (closure.Type != MondValueType.Closure)
                throw new MondRuntimeException("Attempt to call non-closure");

            var closureValue = closure.ClosureValue;

            if (closureValue.Type == ClosureType.Mond)
            {
                var argFrame = closureValue.Arguments;
                if (argFrame == null)
                    argFrame = new Frame(0, null, arguments.Length);
                else
                    argFrame = new Frame(argFrame.Depth + 1, argFrame, arguments.Length);

                for (var i = 0; i < arguments.Length; i++)
                {
                    argFrame.Values[i] = arguments[i];
                }

                _callStack.Push(new ReturnAddress(closureValue.ProgramId, closureValue.Address, argFrame));

                if (closureValue.Locals != null)
                    _localStack.Push(closureValue.Locals);
            }
            else
            {
                throw new NotSupportedException();
            }

            return Run();
        }
Ejemplo n.º 2
0
        internal MondDebugContext(
            MondState state, MondProgram program, int address,
            Frame locals, Frame args,
            ReturnAddress[] callStack,  int callStackTop, int callStackBottom)
        {
            _state = state;
            _address = address;
            _locals = locals;
            _args = args;

            Program = program;
            DebugInfo = program.DebugInfo;

            CallStack = GenerateCallStack(address, callStack, callStackTop, callStackBottom).AsReadOnly();

            _localObject = CreateLocalObject();
        }
Ejemplo n.º 3
0
        public MondValue Call(MondValue function, params MondValue[] arguments)
        {
            if (function.Type == MondValueType.Object)
            {
                // insert "this" value into argument array
                Array.Resize(ref arguments, arguments.Length + 1);
                Array.Copy(arguments, 0, arguments, 1, arguments.Length - 1);
                arguments[0] = function;

                MondValue result;
                if (function.TryDispatch("__call", out result, arguments))
                    return result;
            }

            if (function.Type != MondValueType.Function)
                throw new MondRuntimeException(RuntimeError.ValueNotCallable, function.Type.GetName());

            var closure = function.FunctionValue;

            switch (closure.Type)
            {
                case ClosureType.Mond:
                    var argFrame = closure.Arguments;
                    if (argFrame == null)
                        argFrame = new Frame(0, null, arguments.Length);
                    else
                        argFrame = new Frame(argFrame.Depth + 1, argFrame, arguments.Length);

                    for (var i = 0; i < arguments.Length; i++)
                    {
                        argFrame.Values[i] = arguments[i];
                    }

                    PushCall(new ReturnAddress(closure.Program, closure.Address, argFrame, _evalStackSize));
                    PushLocal(closure.Locals);
                    break;

                case ClosureType.Native:
                    return closure.NativeFunction(_state, arguments);

                default:
                    throw new NotSupportedException();
            }

            return Run();
        }
Ejemplo n.º 4
0
 private void PushLocal(Frame value)
 {
     _localStack[_localStackSize++] = value;
 }
Ejemplo n.º 5
0
        private void DebuggerBreak(MondProgram program, Frame locals, Frame args, int address, int initialCallDepth)
        {
            var context = new MondDebugContext(
                _state, program, address, locals, args, _callStack, _callStackSize, initialCallDepth);

            _debugAction = Debugger.Break(context, address);
            _debugAlign = false;
            _debugDepth = 0;
        }
Ejemplo n.º 6
0
        private MondValue Run()
        {
            var functionAddress = PeekCall();
            var program = functionAddress.Program;
            var code = program.Bytecode;

            var initialCallDepth = _callStackSize - 1; // "- 1" to not include values pushed by Call()
            var initialLocalDepth = _localStackSize - 1;
            var initialEvalDepth = _evalStackSize;

            var ip = functionAddress.Address;
            var errorIp = 0;

            var args = functionAddress.Arguments;
            Frame locals = null;

            try
            {
                while (true)
                {
                    if (Debugger != null)
                    {
                        var skip = _debugSkip;
                        _debugSkip = false;

                        var shouldStopAtStmt =
                            (_debugAction == MondDebugAction.StepInto) ||
                            (_debugAction == MondDebugAction.StepOver && _debugDepth == 0);

                        var shouldBreak =
                            (_debugAlign && program.DebugInfo == null) ||
                            (_debugAlign && program.DebugInfo.IsStatementStart(ip)) ||
                            (Debugger.ShouldBreak(program, ip)) ||
                            (shouldStopAtStmt && program.DebugInfo != null && program.DebugInfo.IsStatementStart(ip));

                        if (!skip && shouldBreak)
                            DebuggerBreak(program, locals, args, ip, initialCallDepth);
                    }

                    errorIp = ip;

                    switch (code[ip++])
                    {
                        #region Stack Manipulation
                        case (int)InstructionType.Dup:
                            {
                                Push(Peek());
                                break;
                            }

                        case (int)InstructionType.Dup2:
                            {
                                var value2 = Pop();
                                var value1 = Pop();
                                Push(value1);
                                Push(value2);
                                Push(value1);
                                Push(value2);
                                break;
                            }

                        case (int)InstructionType.Drop:
                            {
                                Pop();
                                break;
                            }

                        case (int)InstructionType.Swap:
                            {
                                var value1 = Pop();
                                var value2 = Pop();
                                Push(value1);
                                Push(value2);
                                break;
                            }

                        case (int)InstructionType.Swap1For2:
                            {
                                var one = Pop();
                                var two2 = Pop();
                                var two1 = Pop();
                                Push(one);
                                Push(two1);
                                Push(two2);
                                break;
                            }
                        #endregion

                        #region Constants
                        case (int)InstructionType.LdUndef:
                            {
                                Push(MondValue.Undefined);
                                break;
                            }

                        case (int)InstructionType.LdNull:
                            {
                                Push(MondValue.Null);
                                break;
                            }

                        case (int)InstructionType.LdTrue:
                            {
                                Push(MondValue.True);
                                break;
                            }

                        case (int)InstructionType.LdFalse:
                            {
                                Push(MondValue.False);
                                break;
                            }

                        case (int)InstructionType.LdNum:
                            {
                                var numId = ReadInt32(code, ref ip);
                                Push(program.Numbers[numId]);
                                break;
                            }

                        case (int)InstructionType.LdStr:
                            {
                                var strId = ReadInt32(code, ref ip);
                                Push(program.Strings[strId]);
                                break;
                            }

                        case (int)InstructionType.LdGlobal:
                            {
                                Push(Global);
                                break;
                            }
                        #endregion

                        #region Storables
                        case (int)InstructionType.LdLocF:
                            {
                                var index = ReadInt32(code, ref ip);
                                Push(locals.Values[index]);
                                break;
                            }

                        case (int)InstructionType.StLocF:
                            {
                                var index = ReadInt32(code, ref ip);
                                locals.Values[index] = Pop();
                                break;
                            }

                        case (int)InstructionType.LdLoc:
                            {
                                var depth = ReadInt32(code, ref ip);
                                var index = ReadInt32(code, ref ip);

                                if (depth < 0)
                                    Push(args.Get(-depth, index));
                                else
                                    Push(locals.Get(depth, index));

                                break;
                            }

                        case (int)InstructionType.StLoc:
                            {
                                var depth = ReadInt32(code, ref ip);
                                var index = ReadInt32(code, ref ip);

                                if (depth < 0)
                                    args.Set(-depth, index, Pop());
                                else
                                    locals.Set(depth, index, Pop());

                                break;
                            }

                        case (int)InstructionType.LdFld:
                            {
                                var obj = Pop();
                                Push(obj[program.Strings[ReadInt32(code, ref ip)]]);
                                break;
                            }

                        case (int)InstructionType.StFld:
                            {
                                var obj = Pop();
                                var value = Pop();

                                obj[program.Strings[ReadInt32(code, ref ip)]] = value;
                                break;
                            }

                        case (int)InstructionType.LdArr:
                            {
                                var index = Pop();
                                var array = Pop();
                                Push(array[index]);
                                break;
                            }

                        case (int)InstructionType.StArr:
                            {
                                var index = Pop();
                                var array = Pop();
                                var value = Pop();
                                array[index] = value;
                                break;
                            }

                        case (int)InstructionType.LdState:
                            {
                                var depth = ReadInt32(code, ref ip);
                                var frame = locals.GetFrame(depth);
                                locals = frame.StoredFrame;

                                PopLocal();
                                PushLocal(locals);

                                var evals = frame.StoredEvals;
                                if (evals != null)
                                {
                                    for (var i = evals.Count - 1; i >= 0; i--)
                                    {
                                        Push(evals[i]);
                                    }

                                    evals.Clear();
                                }

                                break;
                            }

                        case (int)InstructionType.StState:
                            {
                                var depth = ReadInt32(code, ref ip);
                                var frame = locals.GetFrame(depth);
                                frame.StoredFrame = locals;

                                var initialEvals = _callStackSize > 0 ? PeekCall().EvalDepth : 0;
                                var currentEvals = _evalStackSize;

                                if (currentEvals != initialEvals)
                                {
                                    var evals = frame.StoredEvals ?? (frame.StoredEvals = new List<MondValue>());

                                    while (currentEvals != initialEvals)
                                    {
                                        evals.Add(Pop());
                                        currentEvals--;
                                    }
                                }

                                break;
                            }
                        #endregion

                        #region Object Creation
                        case (int)InstructionType.NewObject:
                            {
                                var obj = new MondValue(_state);
                                Push(obj);
                                break;
                            }

                        case (int)InstructionType.NewArray:
                            {
                                var count = ReadInt32(code, ref ip);
                                var array = new MondValue(MondValueType.Array);

                                for (var i = 0; i < count; i++)
                                {
                                    array.ArrayValue.Add(default(MondValue));
                                }

                                for (var i = count - 1; i >= 0; i--)
                                {
                                    array.ArrayValue[i] = Pop();
                                }

                                Push(array);
                                break;
                            }

                        case (int)InstructionType.Slice:
                            {
                                var step = Pop();
                                var end = Pop();
                                var start = Pop();
                                var array = Pop();

                                Push(array.Slice(start, end, step));
                                break;
                            }
                        #endregion

                        #region Math
                        case (int)InstructionType.Add:
                            {
                                var right = Pop();
                                var left = Pop();
                                Push(left + right);
                                break;
                            }

                        case (int)InstructionType.Sub:
                            {
                                var right = Pop();
                                var left = Pop();
                                Push(left - right);
                                break;
                            }

                        case (int)InstructionType.Mul:
                            {
                                var right = Pop();
                                var left = Pop();
                                Push(left * right);
                                break;
                            }

                        case (int)InstructionType.Div:
                            {
                                var right = Pop();
                                var left = Pop();
                                Push(left / right);
                                break;
                            }

                        case (int)InstructionType.Mod:
                            {
                                var right = Pop();
                                var left = Pop();
                                Push(left % right);
                                break;
                            }

                        case (int)InstructionType.Exp:
                            {
                                var right = Pop();
                                var left = Pop();
                                Push(left.Pow(right));
                                break;
                            }

                        case (int)InstructionType.BitLShift:
                            {
                                var right = Pop();
                                var left = Pop();
                                Push(left.LShift(right));
                                break;
                            }

                        case (int)InstructionType.BitRShift:
                            {
                                var right = Pop();
                                var left = Pop();
                                Push(left.RShift(right));
                                break;
                            }

                        case (int)InstructionType.BitAnd:
                            {
                                var right = Pop();
                                var left = Pop();
                                Push(left & right);
                                break;
                            }

                        case (int)InstructionType.BitOr:
                            {
                                var right = Pop();
                                var left = Pop();
                                Push(left | right);
                                break;
                            }

                        case (int)InstructionType.BitXor:
                            {
                                var right = Pop();
                                var left = Pop();
                                Push(left ^ right);
                                break;
                            }

                        case (int)InstructionType.Neg:
                            {
                                Push(-Pop());
                                break;
                            }

                        case (int)InstructionType.BitNot:
                            {
                                Push(~Pop());
                                break;
                            }
                        #endregion

                        #region Logic
                        case (int)InstructionType.Eq:
                            {
                                var right = Pop();
                                var left = Pop();
                                Push(left == right);
                                break;
                            }

                        case (int)InstructionType.Neq:
                            {
                                var right = Pop();
                                var left = Pop();
                                Push(left != right);
                                break;
                            }

                        case (int)InstructionType.Gt:
                            {
                                var right = Pop();
                                var left = Pop();
                                Push(left > right);
                                break;
                            }

                        case (int)InstructionType.Gte:
                            {
                                var right = Pop();
                                var left = Pop();
                                Push(left >= right);
                                break;
                            }

                        case (int)InstructionType.Lt:
                            {
                                var right = Pop();
                                var left = Pop();
                                Push(left < right);
                                break;
                            }

                        case (int)InstructionType.Lte:
                            {
                                var right = Pop();
                                var left = Pop();
                                Push(left <= right);
                                break;
                            }

                        case (int)InstructionType.Not:
                            {
                                Push(!Pop());
                                break;
                            }

                        case (int)InstructionType.In:
                            {
                                var right = Pop();
                                var left = Pop();
                                Push(right.Contains(left));
                                break;
                            }

                        case (int)InstructionType.NotIn:
                            {
                                var right = Pop();
                                var left = Pop();
                                Push(!right.Contains(left));
                                break;
                            }
                        #endregion

                        #region Functions
                        case (int)InstructionType.Closure:
                            {
                                var address = ReadInt32(code, ref ip);
                                Push(new MondValue(new Closure(program, address, args, locals)));
                                break;
                            }

                        case (int)InstructionType.Call:
                            {
                                var argCount = ReadInt32(code, ref ip);
                                var unpackCount = code[ip++];

                                var function = Pop();

                                List<MondValue> unpackedArgs = null;

                                if (unpackCount > 0)
                                    unpackedArgs = UnpackArgs(code, ref ip, argCount, unpackCount);

                                var returnAddress = ip;

                                if (function.Type == MondValueType.Object)
                                {
                                    MondValue[] argArr;

                                    if (unpackedArgs == null)
                                    {
                                        argArr = new MondValue[argCount + 1];

                                        for (var i = argCount; i >= 1; i--)
                                        {
                                            argArr[i] = Pop();
                                        }

                                        argArr[0] = function;
                                    }
                                    else
                                    {
                                        unpackedArgs.Insert(0, function);
                                        argArr = unpackedArgs.ToArray();
                                    }

                                    MondValue result;
                                    if (function.TryDispatch("__call", out result, argArr))
                                    {
                                        Push(result);
                                        break;
                                    }
                                }

                                if (function.Type != MondValueType.Function)
                                    throw new MondRuntimeException(RuntimeError.ValueNotCallable, function.Type.GetName());

                                var closure = function.FunctionValue;

                                var argFrame = function.FunctionValue.Arguments;
                                var argFrameCount = unpackedArgs == null ? argCount : unpackedArgs.Count;

                                if (argFrame == null)
                                    argFrame = new Frame(1, null, argFrameCount);
                                else
                                    argFrame = new Frame(argFrame.Depth + 1, argFrame, argFrameCount);

                                // copy arguments into frame
                                if (unpackedArgs == null)
                                {
                                    for (var i = argFrameCount - 1; i >= 0; i--)
                                    {
                                        argFrame.Values[i] = Pop();
                                    }
                                }
                                else
                                {
                                    for (var i = 0; i < argFrameCount; i++)
                                    {
                                        argFrame.Values[i] = unpackedArgs[i];
                                    }
                                }

                                switch (closure.Type)
                                {
                                    case ClosureType.Mond:
                                        PushCall(new ReturnAddress(program, returnAddress, argFrame, _evalStackSize));
                                        PushLocal(closure.Locals);

                                        program = closure.Program;
                                        code = program.Bytecode;
                                        ip = closure.Address;
                                        args = argFrame;
                                        locals = closure.Locals;

                                        if (Debugger != null)
                                            DebuggerCheckCall();

                                        break;

                                    case ClosureType.Native:
                                        var result = closure.NativeFunction(_state, argFrame.Values);
                                        Push(result);
                                        break;

                                    default:
                                        throw new MondRuntimeException(RuntimeError.UnhandledClosureType);
                                }

                                break;
                            }

                        case (int)InstructionType.TailCall:
                            {
                                var argCount = ReadInt32(code, ref ip);
                                var address = ReadInt32(code, ref ip);
                                var unpackCount = code[ip++];

                                List<MondValue> unpackedArgs = null;

                                if (unpackCount > 0)
                                    unpackedArgs = UnpackArgs(code, ref ip, argCount, unpackCount);

                                var returnAddress = PopCall();
                                var argFrame = returnAddress.Arguments;
                                var argFrameCount = unpackedArgs == null ? argCount : unpackedArgs.Count;

                                // make sure we have the correct number of values
                                if (argFrameCount != argFrame.Values.Length)
                                    argFrame.Values = new MondValue[argFrameCount];

                                // copy arguments into frame
                                if (unpackedArgs == null)
                                {
                                    for (var i = argFrameCount - 1; i >= 0; i--)
                                    {
                                        argFrame.Values[i] = Pop();
                                    }
                                }
                                else
                                {
                                    for (var i = 0; i < argFrameCount; i++)
                                    {
                                        argFrame.Values[i] = unpackedArgs[i];
                                    }
                                }

                                // get rid of old locals
                                PushLocal(PopLocal().Previous);

                                PushCall(new ReturnAddress(returnAddress.Program, returnAddress.Address, argFrame, _evalStackSize));

                                ip = address;
                                break;
                            }

                        case (int)InstructionType.Enter:
                            {
                                var localCount = ReadInt32(code, ref ip);

                                var frame = PopLocal();
                                frame = new Frame(frame != null ? frame.Depth + 1 : 0, frame, localCount);

                                PushLocal(frame);
                                locals = frame;
                                break;
                            }

                        case (int)InstructionType.Leave:
                            {
                                var frame = PopLocal();
                                frame = frame.Previous;

                                PushLocal(frame);
                                locals = frame;
                                break;
                            }

                        case (int)InstructionType.Ret:
                            {
                                var returnAddress = PopCall();
                                PopLocal();

                                program = returnAddress.Program;
                                code = program.Bytecode;
                                ip = returnAddress.Address;

                                args = _callStackSize > 0 ? PeekCall().Arguments : null;
                                locals = _localStackSize > 0 ? PeekLocal() : null;

                                if (_callStackSize == initialCallDepth)
                                    return Pop();

                                if (Debugger != null && DebuggerCheckReturn())
                                    DebuggerBreak(program, locals, args, ip, initialCallDepth);

                                break;
                            }

                        case (int)InstructionType.VarArgs:
                            {
                                var fixedCount = ReadInt32(code, ref ip);
                                var varArgs = new MondValue(MondValueType.Array);

                                for (var i = fixedCount; i < args.Values.Length; i++)
                                {
                                    varArgs.ArrayValue.Add(args.Values[i]);
                                }

                                args.Set(args.Depth, fixedCount, varArgs);
                                break;
                            }
                        #endregion

                        #region Branching
                        case (int)InstructionType.Jmp:
                            {
                                var address = ReadInt32(code, ref ip);
                                ip = address;
                                break;
                            }

                        case (int)InstructionType.JmpTrueP:
                            {
                                var address = ReadInt32(code, ref ip);

                                if (Peek())
                                    ip = address;

                                break;
                            }

                        case (int)InstructionType.JmpFalseP:
                            {
                                var address = ReadInt32(code, ref ip);

                                if (!Peek())
                                    ip = address;

                                break;
                            }

                        case (int)InstructionType.JmpTrue:
                            {
                                var address = ReadInt32(code, ref ip);

                                if (Pop())
                                    ip = address;

                                break;
                            }

                        case (int)InstructionType.JmpFalse:
                            {
                                var address = ReadInt32(code, ref ip);

                                if (!Pop())
                                    ip = address;

                                break;
                            }

                        case (int)InstructionType.JmpTable:
                            {
                                var start = ReadInt32(code, ref ip);
                                var count = ReadInt32(code, ref ip);

                                var endIp = ip + count * 4;

                                var value = Pop();
                                if (value.Type == MondValueType.Number)
                                {
                                    var number = (double)value;
                                    var numberInt = (int)number;

                                    if (number >= start && number < start + count &&
                                        Math.Abs(number - numberInt) <= double.Epsilon)
                                    {
                                        ip += (numberInt - start) * 4;
                                        ip = ReadInt32(code, ref ip);
                                        break;
                                    }
                                }

                                ip = endIp;
                                break;
                            }
                        #endregion

                        case (int)InstructionType.Breakpoint:
                            {
                                if (Debugger == null)
                                    break;

                                DebuggerBreak(program, locals, args, ip, initialCallDepth);

                                // we stop for the statement *after* the debugger statement so we
                                // skip the next break opportunity, otherwise we break twice
                                _debugSkip = true;
                                break;
                            }

                        default:
                            throw new MondRuntimeException(RuntimeError.UnhandledOpcode);
                    }
                }
            }
            catch (Exception e)
            {
                var message = e.Message.Trim();

                // we skip the OOB checks in the stack methods because the CLR has issues eliminating 
                // its own checks, so we let it throw and check here for a bit of a speed boost
                if (e is IndexOutOfRangeException)
                {
                    if (_callStackSize >= CallStackCapacity || _localStackSize >= CallStackCapacity || _evalStackSize >= EvalStackCapacity)
                    {
                        message = RuntimeError.StackOverflow;
                    }
                    else if (_callStackSize < 0 || _localStackSize < 0 || _evalStackSize < 0)
                    {
                        message = RuntimeError.StackEmpty;
                    }
                }

                StringBuilder stackTraceBuilder;

                var runtimeException = e as MondRuntimeException;
                if (runtimeException != null && runtimeException.InternalStackTrace != null)
                {
                    stackTraceBuilder = new StringBuilder(runtimeException.InternalStackTrace);

                    // check if we are running in a wrapped function
                    var stackTrace = new System.Diagnostics.StackTrace(e);
                    var foundWrapper = false;

                    // skip the first frame because it's this method? need to verify
                    for (var i = 1; i < stackTrace.FrameCount; i++)
                    {
                        var method = stackTrace.GetFrame(i).GetMethod();
                        if (method == null)
                            continue; // ???

                        var type = method.DeclaringType;

                        // stop at the next call to Machine.Run because it can be recursive
                        if (type == typeof(Machine) && method.Name == "Run")
                            break;

                        // the wrapper is a lambda so it's in a compiler generated type, which will be nested
                        var parentType = type.DeclaringType;
                        if (parentType == null)
                            continue;

                        // the type and method are compiler generated so they have a weird (and compiler specific) name
                        const string wrapperMagic = "<CheckWrapFunction>";

                        // make sure the type is nested in MondValue and check both the type and method name
                        if (parentType == typeof(MondValue) && (method.Name.Contains(wrapperMagic) || type.Name.Contains(wrapperMagic)))
                        {
                            foundWrapper = true;
                            break;
                        }
                    }
                    
                    // don't show a native transition for wrappers
                    if (!foundWrapper)
                        stackTraceBuilder.AppendLine("[... native ...]");
                }
                else
                {
                    stackTraceBuilder = new StringBuilder();
                }

                // first line of the stack trace is where we are running
                stackTraceBuilder.AppendLine(GetAddressDebugInfo(program, errorIp));

                // generate stack trace and reset stacks
                for (var i = Math.Min(_callStackSize - 1, CallStackCapacity - 1); i > initialCallDepth; i--)
                {
                    var returnAddress = _callStack[i];
                    stackTraceBuilder.AppendLine(GetAddressDebugInfo(returnAddress.Program, returnAddress.Address));
                }

                _callStackSize = initialCallDepth;
                for (var i = _callStackSize; i < CallStackCapacity; i++)
                {
                    _callStack[i] = default(ReturnAddress);
                }

                _localStackSize = initialLocalDepth;
                for (var i = _localStackSize; i < CallStackCapacity; i++)
                {
                    _localStack[i] = default(Frame);
                }

                _evalStackSize = initialEvalDepth;
                for (var i = _evalStackSize; i < EvalStackCapacity; i++)
                {
                    _evalStack[i] = default(MondValue);
                }

                throw new MondRuntimeException(message, e)
                {
                    InternalStackTrace = stackTraceBuilder.ToString()
                };
            }
        }
Ejemplo n.º 7
0
        public MondValue Run()
        {
            var functionAddress = _callStack.Peek();
            var programId = functionAddress.ProgramId;
            var program = _programs[programId];
            var code = program.Bytecode;

            var initialCallDepth = _callStack.Count - 1;

            var ip = functionAddress.Address;
            var errorIp = 0;

            var args = functionAddress.Arguments;
            Frame locals = null;

            try
            {
                while (true)
                {
                    /*if (program.DebugInfo != null)
                    {
                        var line = program.DebugInfo.FindLine(errorIp);
                        if (line.HasValue)
                            Console.WriteLine("line {0}", line.Value.LineNumber);
                    }*/

                    //Console.WriteLine((InstructionType)code[ip]);

                    errorIp = ip;

                    switch (code[ip++])
                    {
                        #region Stack Manipulation
                        case (int)InstructionType.Dup:
                            {
                                _evalStack.Push(_evalStack.Peek());
                                break;
                            }

                        case (int)InstructionType.Drop:
                            {
                                _evalStack.Pop();
                                break;
                            }

                        case (int)InstructionType.Swap:
                            {
                                var value1 = _evalStack.Pop();
                                var value2 = _evalStack.Pop();
                                _evalStack.Push(value1);
                                _evalStack.Push(value2);
                                break;
                            }
                        #endregion

                        #region Constants
                        case (int)InstructionType.LdUndef:
                            {
                                _evalStack.Push(MondValue.Undefined);
                                break;
                            }

                        case (int)InstructionType.LdNull:
                            {
                                _evalStack.Push(MondValue.Null);
                                break;
                            }

                        case (int)InstructionType.LdTrue:
                            {
                                _evalStack.Push(MondValue.True);
                                break;
                            }

                        case (int)InstructionType.LdFalse:
                            {
                                _evalStack.Push(MondValue.False);
                                break;
                            }

                        case (int)InstructionType.LdNum:
                            {
                                var numId = BitConverter.ToInt32(code, ip);
                                ip += 4;

                                _evalStack.Push(new MondValue(program.Numbers[numId]));
                                break;
                            }

                        case (int)InstructionType.LdStr:
                            {
                                var strId = BitConverter.ToInt32(code, ip);
                                ip += 4;

                                _evalStack.Push(new MondValue(program.Strings[strId]));
                                break;
                            }

                        case (int)InstructionType.LdGlobal:
                            {
                                _evalStack.Push(Global);
                                break;
                            }
                        #endregion

                        #region Storables
                        case (int)InstructionType.LdLoc:
                            {
                                var depth = BitConverter.ToInt32(code, ip);
                                ip += 4;
                                var index = BitConverter.ToInt32(code, ip);
                                ip += 4;

                                if (depth < 0)
                                    _evalStack.Push(args.Get(Math.Abs(depth), index));
                                else
                                    _evalStack.Push(locals.Get(depth, index));

                                break;
                            }

                        case (int)InstructionType.StLoc:
                            {
                                var depth = BitConverter.ToInt32(code, ip);
                                ip += 4;
                                var index = BitConverter.ToInt32(code, ip);
                                ip += 4;

                                if (depth < 0)
                                    args.Set(Math.Abs(depth), index, _evalStack.Pop());
                                else
                                    locals.Set(depth, index, _evalStack.Pop());

                                break;
                            }

                        case (int)InstructionType.LdFld:
                            {
                                var obj = _evalStack.Pop();

                                _evalStack.Push(obj[program.Strings[BitConverter.ToInt32(code, ip)]]);
                                ip += 4;
                                break;
                            }

                        case (int)InstructionType.StFld:
                            {
                                var obj = _evalStack.Pop();
                                var value = _evalStack.Pop();

                                obj[program.Strings[BitConverter.ToInt32(code, ip)]] = value;
                                ip += 4;
                                break;
                            }

                        case (int)InstructionType.LdArr:
                            {
                                var index = _evalStack.Pop();
                                var array = _evalStack.Pop();

                                _evalStack.Push(array[index]);
                                break;
                            }

                        case (int)InstructionType.StArr:
                            {
                                var index = _evalStack.Pop();
                                var array = _evalStack.Pop();
                                var value = _evalStack.Pop();

                                array[index] = value;
                                break;
                            }
                        #endregion

                        #region Object Creation
                        case (int)InstructionType.NewObject:
                            {
                                _evalStack.Push(new MondValue(MondValueType.Object));
                                break;
                            }

                        case (int)InstructionType.NewArray:
                            {
                                var count = BitConverter.ToInt32(code, ip);
                                ip += 4;

                                var array = new MondValue(MondValueType.Array);

                                for (var i = 0; i < count; i++)
                                {
                                    array.ArrayValue.Add(default(MondValue));
                                }

                                for (var i = count - 1; i >= 0; i--)
                                {
                                    array.ArrayValue[i] = _evalStack.Pop();
                                }

                                _evalStack.Push(array);
                                break;
                            }
                        #endregion

                        #region Math
                        case (int)InstructionType.Add:
                            {
                                var left = _evalStack.Pop();
                                var right = _evalStack.Pop();
                                _evalStack.Push(left + right);
                                break;
                            }

                        case (int)InstructionType.Sub:
                            {
                                var left = _evalStack.Pop();
                                var right = _evalStack.Pop();
                                _evalStack.Push(left - right);
                                break;
                            }

                        case (int)InstructionType.Mul:
                            {
                                var left = _evalStack.Pop();
                                var right = _evalStack.Pop();
                                _evalStack.Push(left * right);
                                break;
                            }

                        case (int)InstructionType.Div:
                            {
                                var left = _evalStack.Pop();
                                var right = _evalStack.Pop();
                                _evalStack.Push(left / right);
                                break;
                            }

                        case (int)InstructionType.Mod:
                            {
                                var left = _evalStack.Pop();
                                var right = _evalStack.Pop();
                                _evalStack.Push(left % right);
                                break;
                            }

                        case (int)InstructionType.Neg:
                            {
                                _evalStack.Push(-_evalStack.Pop());
                                break;
                            }
                        #endregion

                        #region Logic
                        case (int)InstructionType.Eq:
                            {
                                var left = _evalStack.Pop();
                                var right = _evalStack.Pop();
                                _evalStack.Push(left == right);
                                break;
                            }

                        case (int)InstructionType.Neq:
                            {
                                var left = _evalStack.Pop();
                                var right = _evalStack.Pop();
                                _evalStack.Push(left != right);
                                break;
                            }

                        case (int)InstructionType.Gt:
                            {
                                var left = _evalStack.Pop();
                                var right = _evalStack.Pop();
                                _evalStack.Push(left > right);
                                break;
                            }

                        case (int)InstructionType.Gte:
                            {
                                var left = _evalStack.Pop();
                                var right = _evalStack.Pop();
                                _evalStack.Push(left >= right);
                                break;
                            }

                        case (int)InstructionType.Lt:
                            {
                                var left = _evalStack.Pop();
                                var right = _evalStack.Pop();
                                _evalStack.Push(left < right);
                                break;
                            }

                        case (int)InstructionType.Lte:
                            {
                                var left = _evalStack.Pop();
                                var right = _evalStack.Pop();
                                _evalStack.Push(left <= right);
                                break;
                            }

                        case (int)InstructionType.Not:
                            {
                                _evalStack.Push(!_evalStack.Pop());
                                break;
                            }
                        #endregion

                        #region Functions
                        case (int)InstructionType.Closure:
                            {
                                var address = BitConverter.ToInt32(code, ip);
                                ip += 4;

                                _evalStack.Push(new MondValue(new Closure(programId, address, args, locals)));
                                break;
                            }

                        case (int)InstructionType.Call:
                            {
                                var argCount = BitConverter.ToInt32(code, ip);
                                ip += 4;

                                var returnAddress = ip;
                                var closure = _evalStack.Pop();

                                if (closure.Type != MondValueType.Closure)
                                    throw new MondRuntimeException("Value of type {0} is not callable", closure.Type);

                                var closureValue = closure.ClosureValue;

                                var argFrame = closure.ClosureValue.Arguments;
                                if (argFrame == null)
                                    argFrame = new Frame(1, null, argCount);
                                else
                                    argFrame = new Frame(argFrame.Depth + 1, argFrame, argCount);

                                for (var i = argCount - 1; i >= 0; i--)
                                {
                                    argFrame.Values[i] = _evalStack.Pop();
                                }

                                if (closureValue.Type == ClosureType.Mond)
                                {
                                    _callStack.Push(new ReturnAddress(programId, returnAddress, argFrame));
                                    _localStack.Push(closureValue.Locals);

                                    programId = closureValue.ProgramId;
                                    program = _programs[programId];
                                    code = program.Bytecode;
                                    ip = closureValue.Address;

                                    args = argFrame;
                                    locals = closureValue.Locals;
                                }
                                else if (closureValue.Type == ClosureType.Native)
                                {
                                    var result = closureValue.NativeFunction(_state, argFrame.Values);
                                    _evalStack.Push(result);
                                }
                                else
                                {
                                    throw new MondRuntimeException("Unhandled closure type");
                                }

                                break;
                            }

                        case (int)InstructionType.TailCall:
                            {
                                var argCount = BitConverter.ToInt32(code, ip);
                                ip += 4;
                                var address = BitConverter.ToInt32(code, ip);

                                var returnAddress = _callStack.Pop();
                                var argFrame = returnAddress.Arguments;

                                if (argFrame.Values.Length < argCount)
                                    argFrame = new Frame(argFrame.Depth + 1, argFrame.Previous, argCount);

                                for (var i = argCount - 1; i >= 0; i--)
                                {
                                    argFrame.Values[i] = _evalStack.Pop();
                                }

                                _callStack.Push(new ReturnAddress(returnAddress.ProgramId, returnAddress.Address, argFrame));

                                ip = address;
                                break;
                            }

                        case (int)InstructionType.Enter:
                            {
                                var localCount = BitConverter.ToInt32(code, ip);
                                ip += 4;

                                Frame frame;

                                if (_localStack.Count > 0)
                                {
                                    frame = _localStack.Pop();
                                    frame = new Frame(frame.Depth + 1, frame, localCount);
                                }
                                else
                                {
                                    frame = new Frame(0, null, localCount);
                                }

                                _localStack.Push(frame);
                                locals = frame;
                                break;
                            }

                        case (int)InstructionType.Ret:
                            {
                                var returnAddress = _callStack.Pop();
                                _localStack.Pop();

                                programId = returnAddress.ProgramId;
                                program = _programs[programId];
                                code = program.Bytecode;
                                ip = returnAddress.Address;

                                args = _callStack.Count > 0 ? _callStack.Peek().Arguments : null;
                                locals = _localStack.Count > 0 ? _localStack.Peek() : null;

                                if (_callStack.Count == initialCallDepth)
                                    return _evalStack.Pop();

                                break;
                            }

                        case (int)InstructionType.JmpTable:
                            {
                                var start = BitConverter.ToInt32(code, ip);
                                ip += 4;
                                var count = BitConverter.ToInt32(code, ip);
                                ip += 4;

                                var endIp = ip + count * 4;

                                var value = _evalStack.Pop();
                                if (value.Type == MondValueType.Number)
                                {
                                    var number = value.NumberValue;
                                    var numberInt = (int)Math.Truncate(number);

                                    if (number >= start && number < start + count &&
                                        Math.Abs(number - numberInt) <= double.Epsilon)
                                    {
                                        ip = BitConverter.ToInt32(code, ip + (numberInt - start) * 4);
                                        break;
                                    }
                                }

                                ip = endIp;
                                break;
                            }
                        #endregion

                        #region Branching
                        case (int)InstructionType.Jmp:
                            {
                                var address = BitConverter.ToInt32(code, ip);
                                ip = address;
                                break;
                            }

                        case (int)InstructionType.JmpTrueP:
                            {
                                var address = BitConverter.ToInt32(code, ip);
                                ip += 4;

                                if (_evalStack.Peek())
                                    ip = address;

                                break;
                            }

                        case (int)InstructionType.JmpFalseP:
                            {
                                var address = BitConverter.ToInt32(code, ip);
                                ip += 4;

                                if (!_evalStack.Peek())
                                    ip = address;

                                break;
                            }

                        case (int)InstructionType.JmpTrue:
                            {
                                var address = BitConverter.ToInt32(code, ip);
                                ip += 4;

                                if (_evalStack.Pop())
                                    ip = address;

                                break;
                            }

                        case (int)InstructionType.JmpFalse:
                            {
                                var address = BitConverter.ToInt32(code, ip);
                                ip += 4;

                                if (!_evalStack.Pop())
                                    ip = address;

                                break;
                            }
                        #endregion

                        default:
                            throw new MondRuntimeException("Unhandled opcode");
                    }
                }
            }
            catch (Exception e)
            {
                string locationPrefix = null;

                if (program.DebugInfo != null)
                {
                    var line = program.DebugInfo.FindLine(errorIp);
                    if (line.HasValue)
                        locationPrefix = string.Format("{0}(line {1}): ", program.Strings[line.Value.FileName], line.Value.LineNumber);
                }

                if (locationPrefix == null)
                    locationPrefix = string.Format("{0:X8}: ", errorIp);

                throw new MondRuntimeException(locationPrefix + e.Message, e);
            }
        }