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 = MondValue.Object(_state); Push(obj); break; } case (int)InstructionType.NewArray: { var count = ReadInt32(code, ref ip); var array = MondValue.Array(); array.ArrayValue.Capacity = count; for (var i = 0; i < count; i++) { array.ArrayValue.Add(default(MondValue)); } 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(); } if (function.TryDispatch("__call", out var result, argArr)) { Push(result); break; } } if (function.Type != MondValueType.Function) { var ldFldBase = ip - 1 - 4 - 1 - 4 - 1; if (ldFldBase >= 0 && code[ldFldBase] == (int)InstructionType.LdFld) { var ldFldIdx = ldFldBase + 1; var fieldNameIdx = ReadInt32(code, ref ldFldIdx); if (fieldNameIdx >= 0 && fieldNameIdx < program.Strings.Count) { var fieldName = program.Strings[fieldNameIdx]; throw new MondRuntimeException(RuntimeError.FieldNotCallable, (string)fieldName); } } throw new MondRuntimeException(RuntimeError.ValueNotCallable, function.Type.GetName()); } var closure = function.FunctionValue; var argFrame = function.FunctionValue.Arguments; var argFrameCount = unpackedArgs?.Count ?? argCount; 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?.Count ?? argCount; // 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?.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 = MondValue.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; if (e is MondRuntimeException runtimeException && runtimeException.MondStackTrace != null) { stackTraceBuilder = new StringBuilder(runtimeException.MondStackTrace); // check if we are running in a wrapped function var stackTrace = new System.Diagnostics.StackTrace(e, false); var frames = stackTrace.GetFrames(); var foundWrapper = false; // skip the first frame because it's this method? need to verify for (var i = 1; i < frames.Length; i++) { var method = frames[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 ...]"); } }
public MondValue Run() { var functionAddress = _callStack.Peek(); var program = functionAddress.Program; var code = program.Bytecode; var initialCallDepth = _callStack.Count - 1; var initialLocalDepth = _localStack.Count; var initialEvalDepth = _evalStack.Count; var ip = functionAddress.Address; var errorIp = 0; var args = functionAddress.Arguments; Frame locals = null; try { while (true) { errorIp = ip; /*if (program.DebugInfo != null) * { * var line = program.DebugInfo.FindLine(errorIp); * if (line.HasValue) * Console.WriteLine("{0:X4} {1} line {2}: {3}", errorIp, program.Strings[line.Value.FileName], line.Value.LineNumber, (InstructionType)code[ip]); * else * Console.WriteLine("{0:X4}: {1}", errorIp, (InstructionType)code[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 = ReadInt32(code, ref ip); _evalStack.Push(program.Numbers[numId]); break; } case (int)InstructionType.LdStr: { var strId = ReadInt32(code, ref ip); _evalStack.Push(program.Strings[strId]); break; } case (int)InstructionType.LdGlobal: { _evalStack.Push(Global); break; } #endregion #region Storables case (int)InstructionType.LdLoc: { var depth = ReadInt32(code, ref ip); var index = ReadInt32(code, ref ip); 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 = ReadInt32(code, ref ip); var index = ReadInt32(code, ref ip); 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[ReadInt32(code, ref ip)]]); break; } case (int)InstructionType.StFld: { var obj = _evalStack.Pop(); var value = _evalStack.Pop(); obj[program.Strings[ReadInt32(code, ref ip)]] = value; 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 = 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] = _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.Exp: { var left = _evalStack.Pop(); var right = _evalStack.Pop(); _evalStack.Push(MondValue.Pow(left, right)); break; } case (int)InstructionType.BitLShift: { var left = _evalStack.Pop(); var right = (int)_evalStack.Pop(); _evalStack.Push(left << right); break; } case (int)InstructionType.BitRShift: { var left = _evalStack.Pop(); var right = (int)_evalStack.Pop(); _evalStack.Push(left >> right); break; } case (int)InstructionType.BitAnd: { var left = _evalStack.Pop(); var right = _evalStack.Pop(); _evalStack.Push(left & right); break; } case (int)InstructionType.BitOr: { var left = _evalStack.Pop(); var right = _evalStack.Pop(); _evalStack.Push(left | right); break; } case (int)InstructionType.BitXor: { var left = _evalStack.Pop(); var right = _evalStack.Pop(); _evalStack.Push(left ^ right); break; } case (int)InstructionType.Neg: { _evalStack.Push(-_evalStack.Pop()); break; } case (int)InstructionType.BitNot: { _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; } case (int)InstructionType.In: { var left = _evalStack.Pop(); var right = _evalStack.Pop(); _evalStack.Push(right.Contains(left)); break; } #endregion #region Functions case (int)InstructionType.Closure: { var address = ReadInt32(code, ref ip); _evalStack.Push(new MondValue(new Closure(program, address, args, locals))); break; } case (int)InstructionType.Call: { var argCount = ReadInt32(code, ref ip); var returnAddress = ip; var function = _evalStack.Pop(); if (function.Type != MondValueType.Function) { throw new MondRuntimeException(RuntimeError.ValueNotCallable, function.Type); } var closure = function.FunctionValue; var argFrame = function.FunctionValue.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(); } switch (closure.Type) { case ClosureType.Mond: if (_callStack.Count >= MaxCallDepth) { throw new MondRuntimeException(RuntimeError.StackOverflow); } _callStack.Push(new ReturnAddress(program, returnAddress, argFrame)); _localStack.Push(closure.Locals); program = closure.Program; code = program.Bytecode; ip = closure.Address; args = argFrame; locals = closure.Locals; break; case ClosureType.Native: var result = closure.NativeFunction(_state, argFrame.Values); _evalStack.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 returnAddress = _callStack.Pop(); var argFrame = returnAddress.Arguments; // copy arguments into frame for (var i = argCount - 1; i >= 0; i--) { argFrame.Values[i] = _evalStack.Pop(); } // clear other arguments for (var i = argCount; i < argFrame.Values.Length; i++) { argFrame.Values[i] = MondValue.Undefined; } // get rid of old locals _localStack.Push(_localStack.Pop().Previous); _callStack.Push(new ReturnAddress(returnAddress.Program, returnAddress.Address, argFrame)); ip = address; break; } case (int)InstructionType.Enter: { var localCount = ReadInt32(code, ref ip); var frame = _localStack.Pop(); frame = new Frame(frame != null ? frame.Depth + 1 : 0, frame, localCount); _localStack.Push(frame); locals = frame; break; } case (int)InstructionType.Ret: { var returnAddress = _callStack.Pop(); _localStack.Pop(); program = returnAddress.Program; 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.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; } case (int)InstructionType.JmpTable: { var start = ReadInt32(code, ref ip); var count = ReadInt32(code, ref ip); var endIp = ip + count * 4; var value = _evalStack.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 #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 (_evalStack.Peek()) { ip = address; } break; } case (int)InstructionType.JmpFalseP: { var address = ReadInt32(code, ref ip); if (!_evalStack.Peek()) { ip = address; } break; } case (int)InstructionType.JmpTrue: { var address = ReadInt32(code, ref ip); if (_evalStack.Pop()) { ip = address; } break; } case (int)InstructionType.JmpFalse: { var address = ReadInt32(code, ref ip); if (!_evalStack.Pop()) { ip = address; } break; } #endregion default: throw new MondRuntimeException(RuntimeError.UnhandledOpcode); } } } catch (Exception e) { var errorBuilder = new StringBuilder(); errorBuilder.AppendLine(e.Message.Trim()); var runtimeException = e as MondRuntimeException; if (runtimeException != null && runtimeException.HasStackTrace) { errorBuilder.AppendLine("[... native ...]"); } else { errorBuilder.AppendLine(); } errorBuilder.AppendLine(GetAddressDebugInfo(program, errorIp)); while (_callStack.Count > initialCallDepth + 1) { var returnAddress = _callStack.Pop(); errorBuilder.AppendLine(GetAddressDebugInfo(returnAddress.Program, returnAddress.Address)); } _callStack.Pop(); while (_localStack.Count > initialLocalDepth) { _localStack.Pop(); } while (_evalStack.Count > initialEvalDepth) { _evalStack.Pop(); } throw new MondRuntimeException(errorBuilder.ToString(), e, true); } }