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(); }
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(); }
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(); }
private void PushLocal(Frame value) { _localStack[_localStackSize++] = value; }
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; }
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() }; } }
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); } }