Ejemplo n.º 1
0
        // The main bytecode interpreter loop. This is where the magic happens. It is
        // also, as you can imagine, highly performance critical. Returns `true` if the
        // fiber completed without error.
        private bool RunInterpreter()
        {
            /* Load Frame */
            CallFrame frame = Fiber.Frames[Fiber.NumFrames - 1];
            int ip = frame.Ip;
            int stackStart = frame.StackStart;
            Obj[] stack = Fiber.Stack;

            ObjFn fn = frame.Fn as ObjFn ?? ((ObjClosure)frame.Fn).Function;
            byte[] bytecode = fn.Bytecode;

            while (true)
            {
                Instruction instruction = (Instruction)bytecode[ip++];
                int index;
                switch (instruction)
                {
                    case Instruction.LOAD_LOCAL_0:
                    case Instruction.LOAD_LOCAL_1:
                    case Instruction.LOAD_LOCAL_2:
                    case Instruction.LOAD_LOCAL_3:
                    case Instruction.LOAD_LOCAL_4:
                    case Instruction.LOAD_LOCAL_5:
                    case Instruction.LOAD_LOCAL_6:
                    case Instruction.LOAD_LOCAL_7:
                    case Instruction.LOAD_LOCAL_8:
                        {
                            index = stackStart + instruction - Instruction.LOAD_LOCAL_0;
                            if (Fiber.StackTop >= Fiber.Capacity)
                                stack = Fiber.IncreaseStack();
                            stack[Fiber.StackTop++] = stack[index];
                            break;
                        }

                    case Instruction.LOAD_LOCAL:
                        {
                            index = stackStart + bytecode[ip++];
                            if (Fiber.StackTop >= Fiber.Capacity)
                                stack = Fiber.IncreaseStack();
                            stack[Fiber.StackTop++] = stack[index];
                            break;
                        }

                    case Instruction.LOAD_FIELD_THIS:
                        {
                            byte field = bytecode[ip++];
                            ObjInstance instance = (ObjInstance)stack[stackStart];
                            if (Fiber.StackTop >= Fiber.Capacity)
                                stack = Fiber.IncreaseStack();
                            stack[Fiber.StackTop++] = instance.Fields[field];
                            break;
                        }

                    case Instruction.POP:
                        {
                            Fiber.StackTop--;
                            break;
                        }

                    case Instruction.DUP:
                        {
                            if (Fiber.StackTop >= Fiber.Capacity)
                                stack = Fiber.IncreaseStack();
                            stack[Fiber.StackTop] = stack[Fiber.StackTop - 1];
                            Fiber.StackTop++;
                            break;
                        }

                    case Instruction.NULL:
                        {
                            if (Fiber.StackTop >= Fiber.Capacity)
                                stack = Fiber.IncreaseStack();
                            stack[Fiber.StackTop++] = Obj.Null;
                            break;
                        }

                    case Instruction.FALSE:
                        {
                            if (Fiber.StackTop >= Fiber.Capacity)
                                stack = Fiber.IncreaseStack();
                            stack[Fiber.StackTop++] = Obj.False;
                            break;
                        }

                    case Instruction.TRUE:
                        {
                            if (Fiber.StackTop >= Fiber.Capacity)
                                stack = Fiber.IncreaseStack();
                            stack[Fiber.StackTop++] = Obj.True;
                            break;
                        }

                    case Instruction.CALL_0:
                    case Instruction.CALL_1:
                    case Instruction.CALL_2:
                    case Instruction.CALL_3:
                    case Instruction.CALL_4:
                    case Instruction.CALL_5:
                    case Instruction.CALL_6:
                    case Instruction.CALL_7:
                    case Instruction.CALL_8:
                    case Instruction.CALL_9:
                    case Instruction.CALL_10:
                    case Instruction.CALL_11:
                    case Instruction.CALL_12:
                    case Instruction.CALL_13:
                    case Instruction.CALL_14:
                    case Instruction.CALL_15:
                    case Instruction.CALL_16:
                    // Handle Super calls
                    case Instruction.SUPER_0:
                    case Instruction.SUPER_1:
                    case Instruction.SUPER_2:
                    case Instruction.SUPER_3:
                    case Instruction.SUPER_4:
                    case Instruction.SUPER_5:
                    case Instruction.SUPER_6:
                    case Instruction.SUPER_7:
                    case Instruction.SUPER_8:
                    case Instruction.SUPER_9:
                    case Instruction.SUPER_10:
                    case Instruction.SUPER_11:
                    case Instruction.SUPER_12:
                    case Instruction.SUPER_13:
                    case Instruction.SUPER_14:
                    case Instruction.SUPER_15:
                    case Instruction.SUPER_16:
                        {
                            int numArgs = instruction - (instruction >= Instruction.SUPER_0 ? Instruction.SUPER_0 : Instruction.CALL_0) + 1;
                            int symbol = (bytecode[ip] << 8) + bytecode[ip + 1];
                            ip += 2;

                            // The receiver is the first argument.
                            int argStart = Fiber.StackTop - numArgs;
                            Obj receiver = stack[argStart];
                            ObjClass classObj;

                            if (instruction < Instruction.SUPER_0)
                            {
                                if (receiver.Type == ObjType.Obj)
                                    classObj = receiver.ClassObj;
                                else if (receiver.Type == ObjType.Num)
                                    classObj = NumClass;
                                else if (receiver == Obj.True || receiver == Obj.False)
                                    classObj = BoolClass;
                                else
                                    classObj = NullClass;
                            }
                            else
                            {
                                // The superclass is stored in a constant.
                                classObj = fn.Constants[(bytecode[ip] << 8) + bytecode[ip + 1]] as ObjClass;
                                ip += 2;
                            }

                            // If the class's method table doesn't include the symbol, bail.
                            Method method = symbol < classObj.Methods.Length ? classObj.Methods[symbol] : null;

                            if (method == null)
                            {
                                /* Method not found */
                                frame.Ip = ip;
                                MethodNotFound(this, classObj, symbol);
                                if (!HandleRuntimeError())
                                    return false;
                                frame = Fiber.Frames[Fiber.NumFrames - 1];
                                ip = frame.Ip;
                                stackStart = frame.StackStart;
                                stack = Fiber.Stack;
                                fn = (frame.Fn as ObjFn) ?? (frame.Fn as ObjClosure).Function;
                                bytecode = fn.Bytecode;
                                break;
                            }

                            if (method.MType == MethodType.Primitive)
                            {
                                // After calling this, the result will be in the first arg slot.
                                if (method.Primitive(this, stack, argStart))
                                {
                                    Fiber.StackTop = argStart + 1;
                                }
                                else
                                {
                                    frame.Ip = ip;

                                    if (Fiber.Error != null && Fiber.Error != Obj.Null)
                                    {
                                        if (!HandleRuntimeError())
                                            return false;
                                    }
                                    else
                                    {
                                        // If we don't have a fiber to switch to, stop interpreting.
                                        if (stack[argStart] == Obj.Null)
                                            return true;
                                        Fiber = stack[argStart] as ObjFiber;
                                        if (Fiber == null)
                                            return false;
                                    }

                                    /* Load Frame */
                                    frame = Fiber.Frames[Fiber.NumFrames - 1];
                                    ip = frame.Ip;
                                    stackStart = frame.StackStart;
                                    stack = Fiber.Stack;
                                    fn = (frame.Fn as ObjFn) ?? (frame.Fn as ObjClosure).Function;
                                    bytecode = fn.Bytecode;
                                }
                                break;
                            }

                            frame.Ip = ip;

                            if (method.MType == MethodType.Block)
                            {
                                receiver = method.Obj;
                            }
                            else if (!CheckArity(stack, numArgs, argStart))
                            {
                                if (!HandleRuntimeError())
                                    return false;

                                frame = Fiber.Frames[Fiber.NumFrames - 1];
                                ip = frame.Ip;
                                stackStart = frame.StackStart;
                                stack = Fiber.Stack;
                                fn = (frame.Fn as ObjFn) ?? (frame.Fn as ObjClosure).Function;
                                bytecode = fn.Bytecode;
                                break;
                            }

                            Fiber.Frames.Add(frame = new CallFrame { Fn = receiver, StackStart = argStart, Ip = 0 });
                            Fiber.NumFrames++;
                            /* Load Frame */
                            ip = 0;
                            stackStart = argStart;
                            fn = (receiver as ObjFn) ?? (receiver as ObjClosure).Function;
                            bytecode = fn.Bytecode;
                            break;
                        }

                    case Instruction.STORE_LOCAL:
                        {
                            index = stackStart + bytecode[ip++];
                            stack[index] = stack[Fiber.StackTop - 1];
                            break;
                        }

                    case Instruction.CONSTANT:
                        {
                            if (Fiber.StackTop >= Fiber.Capacity)
                                stack = Fiber.IncreaseStack();
                            stack[Fiber.StackTop++] = fn.Constants[(bytecode[ip] << 8) + bytecode[ip + 1]];
                            ip += 2;
                            break;
                        }

                    case Instruction.LOAD_UPVALUE:
                        {
                            if (Fiber.StackTop >= Fiber.Capacity)
                                stack = Fiber.IncreaseStack();
                            stack[Fiber.StackTop++] = ((ObjClosure)frame.Fn).Upvalues[bytecode[ip++]].Container;
                            break;
                        }

                    case Instruction.STORE_UPVALUE:
                        {
                            ObjUpvalue[] upvalues = ((ObjClosure)frame.Fn).Upvalues;
                            upvalues[bytecode[ip++]].Container = stack[Fiber.StackTop - 1];
                            break;
                        }

                    case Instruction.LOAD_MODULE_VAR:
                        {
                            if (Fiber.StackTop >= Fiber.Capacity)
                                stack = Fiber.IncreaseStack();
                            stack[Fiber.StackTop++] = fn.Module.Variables[(bytecode[ip] << 8) + bytecode[ip + 1]].Container;
                            ip += 2;
                            break;
                        }

                    case Instruction.STORE_MODULE_VAR:
                        {
                            fn.Module.Variables[(bytecode[ip] << 8) + bytecode[ip + 1]].Container = stack[Fiber.StackTop - 1];
                            ip += 2;
                            break;
                        }

                    case Instruction.STORE_FIELD_THIS:
                        {
                            byte field = bytecode[ip++];
                            ObjInstance instance = (ObjInstance)stack[stackStart];
                            instance.Fields[field] = stack[Fiber.StackTop - 1];
                            break;
                        }

                    case Instruction.LOAD_FIELD:
                        {
                            byte field = bytecode[ip++];
                            ObjInstance instance = (ObjInstance)stack[--Fiber.StackTop];
                            if (Fiber.StackTop >= Fiber.Capacity)
                                stack = Fiber.IncreaseStack();
                            stack[Fiber.StackTop++] = instance.Fields[field];
                            break;
                        }

                    case Instruction.STORE_FIELD:
                        {
                            byte field = bytecode[ip++];
                            ObjInstance instance = (ObjInstance)stack[--Fiber.StackTop];
                            instance.Fields[field] = stack[Fiber.StackTop - 1];
                            break;
                        }

                    case Instruction.JUMP:
                        {
                            int offset = (bytecode[ip] << 8) + bytecode[ip + 1];
                            ip += offset + 2;
                            break;
                        }

                    case Instruction.LOOP:
                        {
                            // Jump back to the top of the loop.
                            int offset = (bytecode[ip] << 8) + bytecode[ip + 1];
                            ip += 2;
                            ip -= offset;
                            break;
                        }

                    case Instruction.JUMP_IF:
                        {
                            int offset = (bytecode[ip] << 8) + bytecode[ip + 1];
                            ip += 2;
                            Obj condition = stack[--Fiber.StackTop];

                            if (condition == Obj.False || condition == Obj.Null) ip += offset;
                            break;
                        }

                    case Instruction.AND:
                    case Instruction.OR:
                        {
                            int offset = (bytecode[ip] << 8) + bytecode[ip + 1];
                            ip += 2;
                            Obj condition = stack[Fiber.StackTop - 1];

                            if ((condition == Obj.Null || condition == Obj.False) ^ instruction == Instruction.OR)
                                ip += offset;
                            else
                                Fiber.StackTop--;
                            break;
                        }

                    case Instruction.CLOSE_UPVALUE:
                        {
                            Fiber.CloseUpvalue();
                            Fiber.StackTop--;
                            break;
                        }

                    case Instruction.RETURN:
                        {
                            Fiber.Frames.RemoveAt(--Fiber.NumFrames);
                            Obj result = stack[--Fiber.StackTop];
                            // Close any upvalues still in scope.
                            if (Fiber.StackTop > stackStart)
                            {
                                while (Fiber.OpenUpvalues != null && Fiber.OpenUpvalues.Index >= stackStart)
                                {
                                    Fiber.CloseUpvalue();
                                }
                            }

                            // If the fiber is complete, end it.
                            if (Fiber.NumFrames == 0)
                            {
                                // If this is the main fiber, we're done.
                                if (Fiber.Caller == null)
                                    return true;

                                // We have a calling fiber to resume.
                                Fiber = Fiber.Caller;
                                stack = Fiber.Stack;
                                // Store the result in the resuming fiber.
                                stack[Fiber.StackTop - 1] = result;
                            }
                            else
                            {
                                // Discard the stack slots for the call frame (leaving one slot for the result).
                                Fiber.StackTop = stackStart + 1;

                                // Store the result of the block in the first slot, which is where the
                                // caller expects it.
                                stack[stackStart] = result;
                            }

                            /* Load Frame */
                            frame = Fiber.Frames[Fiber.NumFrames - 1];
                            ip = frame.Ip;
                            stackStart = frame.StackStart;
                            fn = frame.Fn as ObjFn ?? (frame.Fn as ObjClosure).Function;
                            bytecode = fn.Bytecode;
                            break;
                        }

                    case Instruction.CLOSURE:
                        {
                            ObjFn prototype = fn.Constants[(bytecode[ip] << 8) + bytecode[ip + 1]] as ObjFn;
                            ip += 2;

                            // Create the closure and push it on the stack before creating upvalues
                            // so that it doesn't get collected.
                            ObjClosure closure = new ObjClosure(prototype);
                            if (Fiber.StackTop >= Fiber.Capacity)
                                stack = Fiber.IncreaseStack();
                            stack[Fiber.StackTop++] = closure;

                            // Capture upvalues.
                            for (int i = 0; i < prototype.NumUpvalues; i++)
                            {
                                byte isLocal = bytecode[ip++];
                                index = bytecode[ip++];
                                if (isLocal > 0)
                                {
                                    // Make an new upvalue to close over the parent's local variable.
                                    closure.Upvalues[i] = Fiber.CaptureUpvalue(stackStart + index);
                                }
                                else
                                {
                                    // Use the same upvalue as the current call frame.
                                    closure.Upvalues[i] = ((ObjClosure)frame.Fn).Upvalues[index];
                                }
                            }

                            break;
                        }

                    case Instruction.CLASS:
                        {
                            Obj name = stack[Fiber.StackTop - 2];
                            ObjClass superclass = stack[Fiber.StackTop - 1] as ObjClass;

                            Obj error = ValidateSuperclass(name, stack[Fiber.StackTop - 1]);
                            if (error != null)
                            {
                                Fiber.Error = error;
                                frame.Ip = ip;
                                if (!HandleRuntimeError())
                                    return false;
                                /* Load Frame */
                                frame = Fiber.Frames[Fiber.NumFrames - 1];
                                ip = frame.Ip;
                                stackStart = frame.StackStart;
                                stack = Fiber.Stack;
                                fn = (frame.Fn as ObjFn) ?? (frame.Fn as ObjClosure).Function;
                                bytecode = fn.Bytecode;
                                break;
                            }

                            int numFields = bytecode[ip++];

                            Obj classObj = new ObjClass(superclass, numFields, name as ObjString);

                            // Don't pop the superclass and name off the stack until the subclass is
                            // done being created, to make sure it doesn't get collected.
                            Fiber.StackTop -= 2;

                            // Now that we know the total number of fields, make sure we don't overflow.
                            if (superclass.NumFields + numFields <= Compiler.MaxFields)
                            {
                                stack[Fiber.StackTop++] = classObj;
                                break;
                            }

                            // Overflow handling
                            frame.Ip = ip;
                            Fiber.Error = Obj.MakeString(string.Format("Class '{0}' may not have more than 255 fields, including inherited ones.", name));
                            if (!HandleRuntimeError())
                                return false;
                            /* Load Frame */
                            frame = Fiber.Frames[Fiber.NumFrames - 1];
                            ip = frame.Ip;
                            stackStart = frame.StackStart;
                            stack = Fiber.Stack;
                            fn = (frame.Fn as ObjFn) ?? (frame.Fn as ObjClosure).Function;
                            bytecode = fn.Bytecode;
                            break;
                        }

                    case Instruction.METHOD_INSTANCE:
                    case Instruction.METHOD_STATIC:
                        {
                            int symbol = (bytecode[ip] << 8) + bytecode[ip + 1];
                            ip += 2;
                            ObjClass classObj = stack[Fiber.StackTop - 1] as ObjClass;
                            Obj method = stack[Fiber.StackTop - 2];
                            bool isStatic = instruction == Instruction.METHOD_STATIC;
                            if (!BindMethod(isStatic, symbol, classObj, method))
                            {
                                frame.Ip = ip;
                                Fiber.Error = Obj.MakeString("Error while binding method");
                                if (!HandleRuntimeError())
                                    return false;
                                /* Load Frame */
                                frame = Fiber.Frames[Fiber.NumFrames - 1];
                                ip = frame.Ip;
                                stackStart = frame.StackStart;
                                stack = Fiber.Stack;
                                fn = (frame.Fn as ObjFn) ?? (frame.Fn as ObjClosure).Function;
                                bytecode = fn.Bytecode;
                                break;
                            }
                            Fiber.StackTop -= 2;
                            break;
                        }

                    case Instruction.LOAD_MODULE:
                        {
                            Obj name = fn.Constants[(bytecode[ip] << 8) + bytecode[ip + 1]];
                            ip += 2;
                            Obj result = ImportModule(name);

                            // If it returned a string, it was an error message.
                            if ((result is ObjString))
                            {
                                frame.Ip = ip;
                                Fiber.Error = result;
                                if (!HandleRuntimeError())
                                    return false;
                                /* Load Frame */
                                frame = Fiber.Frames[Fiber.NumFrames - 1];
                                ip = frame.Ip;
                                stackStart = frame.StackStart;
                                stack = Fiber.Stack;
                                fn = (frame.Fn as ObjFn) ?? (frame.Fn as ObjClosure).Function;
                                bytecode = fn.Bytecode;
                                break;
                            }

                            // Make a slot that the module's fiber can use to store its result in.
                            // It ends up getting discarded, but CODE_RETURN expects to be able to
                            // place a value there.
                            if (Fiber.StackTop >= Fiber.Capacity)
                                stack = Fiber.IncreaseStack();
                            stack[Fiber.StackTop++] = Obj.Null;

                            // If it returned a fiber to execute the module body, switch to it.
                            if (result is ObjFiber)
                            {
                                // Return to this module when that one is done.
                                (result as ObjFiber).Caller = Fiber;

                                frame.Ip = ip;
                                Fiber = (result as ObjFiber);
                                /* Load Frame */
                                frame = Fiber.Frames[Fiber.NumFrames - 1];
                                ip = frame.Ip;
                                stackStart = frame.StackStart;
                                stack = Fiber.Stack;
                                fn = (frame.Fn as ObjFn) ?? (frame.Fn as ObjClosure).Function;
                                bytecode = fn.Bytecode;
                            }

                            break;
                        }

                    case Instruction.IMPORT_VARIABLE:
                        {
                            Obj module = fn.Constants[(bytecode[ip] << 8) + bytecode[ip + 1]];
                            ip += 2;
                            Obj variable = fn.Constants[(bytecode[ip] << 8) + bytecode[ip + 1]];
                            ip += 2;
                            Obj result;
                            if (ImportVariable(module, variable, out result))
                            {
                                if (Fiber.StackTop >= Fiber.Capacity)
                                    stack = Fiber.IncreaseStack();
                                stack[Fiber.StackTop++] = result;
                            }
                            else
                            {
                                frame.Ip = ip;
                                Fiber.Error = result;
                                if (!HandleRuntimeError())
                                    return false;
                                /* Load Frame */
                                frame = Fiber.Frames[Fiber.NumFrames - 1];
                                ip = frame.Ip;
                                stackStart = frame.StackStart;
                                stack = Fiber.Stack;
                                fn = (frame.Fn as ObjFn) ?? (frame.Fn as ObjClosure).Function;
                                bytecode = fn.Bytecode;
                            }
                            break;
                        }

                    case Instruction.CONSTRUCT:
                        {
                            int stackPosition = Fiber.StackTop - 1 + (Instruction.CALL_0 - (Instruction)bytecode[ip]);
                            ObjClass v = stack[stackPosition] as ObjClass;
                            if (v == null)
                            {
                                Fiber.Error = Obj.MakeString("'this' should be a class.");
                                if (!HandleRuntimeError())
                                    return false;
                                /* Load Frame */
                                frame = Fiber.Frames[Fiber.NumFrames - 1];
                                ip = frame.Ip;
                                stackStart = frame.StackStart;
                                stack = Fiber.Stack;
                                fn = (frame.Fn as ObjFn) ?? (frame.Fn as ObjClosure).Function;
                                bytecode = fn.Bytecode;
                                break;
                            }
                            stack[stackPosition] = new ObjInstance(v);
                        }
                        break;

                    case Instruction.FOREIGN_CLASS:
                        // Not yet implemented
                        break;

                    case Instruction.FOREIGN_CONSTRUCT:
                        // Not yet implemented
                        break;

                    case Instruction.END:
                        // A CODE_END should always be preceded by a CODE_RETURN. If we get here,
                        // the compiler generated wrong code.
                        return false;
                }
            }

            // We should only exit this function from an explicit return from CODE_RETURN
            // or a runtime error.
        }
Ejemplo n.º 2
0
        // The main bytecode interpreter loop. This is where the magic happens. It is
        // also, as you can imagine, highly performance critical. Returns `true` if the
        // fiber completed without error.
        private bool RunInterpreter()
        {
            Instruction instruction;
            int index;

            /* Load Frame */
            CallFrame frame = fiber.Frames[fiber.NumFrames - 1];
            int ip = frame.ip;
            int stackStart = frame.StackStart;
            Value[] stack = fiber.Stack;
            Value[] args = new Value[17];

            ObjFn fn = frame.fn as ObjFn ?? ((ObjClosure)frame.fn).Function;
            byte[] bytecode = fn.Bytecode;

            while (true)
            {
                switch (instruction = (Instruction)bytecode[ip++])
                {
                    case Instruction.LOAD_LOCAL_0:
                    case Instruction.LOAD_LOCAL_1:
                    case Instruction.LOAD_LOCAL_2:
                    case Instruction.LOAD_LOCAL_3:
                    case Instruction.LOAD_LOCAL_4:
                    case Instruction.LOAD_LOCAL_5:
                    case Instruction.LOAD_LOCAL_6:
                    case Instruction.LOAD_LOCAL_7:
                    case Instruction.LOAD_LOCAL_8:
                        index = stackStart + (int)instruction; // LOAD_LOCAL_0 has code 0
                        if (fiber.StackTop >= fiber.Capacity)
                            stack = fiber.IncreaseStack();
                        stack[fiber.StackTop++] = stack[index];
                        break;

                    case Instruction.LOAD_LOCAL:
                        index = stackStart + bytecode[ip++];
                        if (fiber.StackTop >= fiber.Capacity)
                            stack = fiber.IncreaseStack();
                        stack[fiber.StackTop++] = stack[index];
                        break;

                    case Instruction.LOAD_FIELD_THIS:
                        {
                            byte field = bytecode[ip++];
                            Value receiver = stack[stackStart];
                            ObjInstance instance = receiver.Obj as ObjInstance;
                            if (fiber.StackTop >= fiber.Capacity)
                                fiber.IncreaseStack();
                            stack[fiber.StackTop++] = instance.Fields[field];
                            break;
                        }

                    case Instruction.POP:
                        fiber.StackTop--;
                        break;
                    case Instruction.DUP:
                        if (fiber.StackTop >= fiber.Capacity)
                            stack = fiber.IncreaseStack();
                        stack[fiber.StackTop] = stack[fiber.StackTop - 1];
                        fiber.StackTop++;
                        break;
                    case Instruction.NULL:
                        if (fiber.StackTop >= fiber.Capacity)
                            stack = fiber.IncreaseStack();
                        stack[fiber.StackTop++] = new Value(ValueType.Null);
                        break;
                    case Instruction.FALSE:
                        if (fiber.StackTop >= fiber.Capacity)
                            stack = fiber.IncreaseStack();
                        stack[fiber.StackTop++] = new Value(ValueType.False);
                        break;
                    case Instruction.TRUE:
                        if (fiber.StackTop >= fiber.Capacity)
                            stack = fiber.IncreaseStack();
                        stack[fiber.StackTop++] = new Value(ValueType.True);
                        break;

                    case Instruction.CALL_0:
                    case Instruction.CALL_1:
                    case Instruction.CALL_2:
                    case Instruction.CALL_3:
                    case Instruction.CALL_4:
                    case Instruction.CALL_5:
                    case Instruction.CALL_6:
                    case Instruction.CALL_7:
                    case Instruction.CALL_8:
                    case Instruction.CALL_9:
                    case Instruction.CALL_10:
                    case Instruction.CALL_11:
                    case Instruction.CALL_12:
                    case Instruction.CALL_13:
                    case Instruction.CALL_14:
                    case Instruction.CALL_15:
                    case Instruction.CALL_16:
                    // Handle Super calls
                    case Instruction.SUPER_0:
                    case Instruction.SUPER_1:
                    case Instruction.SUPER_2:
                    case Instruction.SUPER_3:
                    case Instruction.SUPER_4:
                    case Instruction.SUPER_5:
                    case Instruction.SUPER_6:
                    case Instruction.SUPER_7:
                    case Instruction.SUPER_8:
                    case Instruction.SUPER_9:
                    case Instruction.SUPER_10:
                    case Instruction.SUPER_11:
                    case Instruction.SUPER_12:
                    case Instruction.SUPER_13:
                    case Instruction.SUPER_14:
                    case Instruction.SUPER_15:
                    case Instruction.SUPER_16:
                        {
                            int numArgs = (int)instruction & 63;
                            int symbol = (bytecode[ip] << 8) + bytecode[ip + 1];
                            ip += 2;

                            // The receiver is the first argument.
                            int argStart = fiber.StackTop - numArgs;
                            Value receiver = stack[argStart];
                            ObjClass classObj;

                            if (instruction < Instruction.SUPER_0)
                            {
                                if (receiver.Type == ValueType.Obj)
                                {
                                    classObj = receiver.Obj.ClassObj;
                                }
                                else if (receiver.Type == ValueType.Num)
                                {
                                    classObj = NumClass;
                                }
                                else if (receiver.Type == ValueType.True || receiver.Type == ValueType.False)
                                {
                                    classObj = BoolClass;
                                }
                                else
                                {
                                    classObj = NullClass;
                                }
                            }
                            else
                            {
                                // The superclass is stored in a constant.
                                classObj = fn.Constants[(bytecode[ip] << 8) + bytecode[ip + 1]].Obj as ObjClass;
                                ip += 2;
                            }

                            Method[] methods = classObj.Methods;

                            // If the class's method table doesn't include the symbol, bail.
                            if (symbol < methods.Length)
                            {
                                Method method = methods[symbol];

                                if (method != null)
                                {
                                    if (method.mType == MethodType.Primitive)
                                    {
                                        for (int i = 0; i < numArgs; i++)
                                            args[i] = stack[argStart + i];
                                        // After calling this, the result will be in the first arg slot.
                                        PrimitiveResult result = method.primitive(this, fiber, args);

                                        if (result == PrimitiveResult.Value)
                                        {
                                            fiber.StackTop = argStart + 1;
                                            stack[argStart] = args[0];
                                            Instruction next = (Instruction)bytecode[ip];
                                            if (next == Instruction.STORE_LOCAL)
                                            {
                                                index = stackStart + bytecode[ip + 1];
                                                stack[index] = args[0];
                                                ip += 2;
                                            }
                                            break;
                                        }

                                        frame.ip = ip;

                                        switch (result)
                                        {
                                            case PrimitiveResult.RunFiber:

                                                // If we don't have a fiber to switch to, stop interpreting.
                                                if (args[0].Type == ValueType.Null) return true;

                                                fiber = args[0].Obj as ObjFiber;
                                                /* Load Frame */
                                                frame = fiber.Frames[fiber.NumFrames - 1];
                                                ip = frame.ip;
                                                stackStart = frame.StackStart;
                                                stack = fiber.Stack;
                                                fn = (frame.fn as ObjFn) ?? (frame.fn as ObjClosure).Function;
                                                bytecode = fn.Bytecode;
                                                break;

                                            case PrimitiveResult.Call:
                                                fiber.Frames.Add(frame = new CallFrame { fn = receiver.Obj, StackStart = argStart, ip = 0 });
                                                fiber.NumFrames++;

                                                /* Load Frame */
                                                ip = 0;
                                                stackStart = argStart;
                                                fn = (frame.fn as ObjFn) ?? (frame.fn as ObjClosure).Function;
                                                bytecode = fn.Bytecode;
                                                break;

                                            case PrimitiveResult.Error:
                                                RUNTIME_ERROR(fiber, args[0]);
                                                if (fiber == null)
                                                    return false;
                                                frame = fiber.Frames[fiber.NumFrames - 1];
                                                ip = frame.ip;
                                                stackStart = frame.StackStart;
                                                stack = fiber.Stack;
                                                fn = (frame.fn as ObjFn) ?? (frame.fn as ObjClosure).Function;
                                                bytecode = fn.Bytecode;
                                                break;
                                        }
                                        break;
                                    }

                                    if (method.mType == MethodType.Block)
                                    {
                                        Obj mObj = method.obj;
                                        frame.ip = ip;
                                        fiber.Frames.Add(frame = new CallFrame { fn = mObj, StackStart = argStart, ip = 0 });
                                        fiber.NumFrames++;
                                        /* Load Frame */
                                        ip = 0;
                                        stackStart = argStart;
                                        fn = (mObj as ObjFn) ?? (mObj as ObjClosure).Function;
                                        bytecode = fn.Bytecode;
                                        break;
                                    }
                                }
                            }

                            /* Method not found */
                            frame.ip = ip;
                            RUNTIME_ERROR(fiber, MethodNotFound(this, classObj, symbol));
                            if (fiber == null)
                                return false;
                            frame = fiber.Frames[fiber.NumFrames - 1];
                            ip = frame.ip;
                            stackStart = frame.StackStart;
                            stack = fiber.Stack;
                            fn = (frame.fn as ObjFn) ?? (frame.fn as ObjClosure).Function;
                            bytecode = fn.Bytecode;

                        }
                        break;

                    case Instruction.STORE_LOCAL:
                        index = stackStart + bytecode[ip++];
                        stack[index] = stack[fiber.StackTop - 1];
                        break;

                    case Instruction.CONSTANT:
                        if (fiber.StackTop >= fiber.Capacity)
                            stack = fiber.IncreaseStack();
                        stack[fiber.StackTop++] = fn.Constants[(bytecode[ip] << 8) + bytecode[ip + 1]];
                        ip += 2;
                        break;

                    case Instruction.LOAD_UPVALUE:
                        {
                            if (fiber.StackTop >= fiber.Capacity)
                                stack = fiber.IncreaseStack();
                            stack[fiber.StackTop++] = ((ObjClosure)frame.fn).Upvalues[bytecode[ip++]].Container;
                            break;
                        }

                    case Instruction.STORE_UPVALUE:
                        {
                            ObjUpvalue[] upvalues = ((ObjClosure)frame.fn).Upvalues;
                            upvalues[bytecode[ip++]].Container = stack[fiber.StackTop - 1];
                            break;
                        }

                    case Instruction.LOAD_MODULE_VAR:
                        if (fiber.StackTop >= fiber.Capacity)
                            stack = fiber.IncreaseStack();
                        stack[fiber.StackTop++] = fn.Module.Variables[(bytecode[ip] << 8) + bytecode[ip + 1]].Container;
                        ip += 2;
                        break;

                    case Instruction.STORE_MODULE_VAR:
                        fn.Module.Variables[(bytecode[ip] << 8) + bytecode[ip + 1]].Container = stack[fiber.StackTop - 1];
                        ip += 2;
                        break;

                    case Instruction.STORE_FIELD_THIS:
                        {
                            byte field = bytecode[ip++];
                            Value receiver = stack[stackStart];
                            ObjInstance instance = receiver.Obj as ObjInstance;
                            instance.Fields[field] = stack[fiber.StackTop - 1];
                            break;
                        }

                    case Instruction.LOAD_FIELD:
                        {
                            byte field = bytecode[ip++];
                            Value receiver = stack[--fiber.StackTop];
                            ObjInstance instance = receiver.Obj as ObjInstance;
                            if (fiber.StackTop >= fiber.Capacity)
                                stack = fiber.IncreaseStack();
                            stack[fiber.StackTop++] = instance.Fields[field];
                            break;
                        }

                    case Instruction.STORE_FIELD:
                        {
                            byte field = bytecode[ip++];
                            Value receiver = stack[--fiber.StackTop];
                            ObjInstance instance = receiver.Obj as ObjInstance;
                            instance.Fields[field] = stack[fiber.StackTop - 1];
                            break;
                        }

                    case Instruction.JUMP:
                        {
                            int offset = (bytecode[ip] << 8) + bytecode[ip + 1];
                            ip += offset + 2;
                            break;
                        }

                    case Instruction.LOOP:
                        {
                            // Jump back to the top of the loop.
                            int offset = (bytecode[ip] << 8) + bytecode[ip + 1];
                            ip += 2;
                            ip -= offset;
                            break;
                        }

                    case Instruction.JUMP_IF:
                        {
                            int offset = (bytecode[ip] << 8) + bytecode[ip + 1];
                            ip += 2;
                            ValueType condition = stack[--fiber.StackTop].Type;

                            if (condition == ValueType.False || condition == ValueType.Null) ip += offset;
                            break;
                        }

                    case Instruction.AND:
                        {
                            int offset = (bytecode[ip] << 8) + bytecode[ip + 1];
                            ip += 2;
                            ValueType condition = stack[fiber.StackTop - 1].Type;

                            switch (condition)
                            {
                                case ValueType.Null:
                                case ValueType.False:
                                    ip += offset;
                                    break;
                                default:
                                    fiber.StackTop--;
                                    break;
                            }
                            break;
                        }

                    case Instruction.OR:
                        {
                            int offset = (bytecode[ip] << 8) + bytecode[ip + 1];
                            ip += 2;
                            Value condition = stack[fiber.StackTop - 1];

                            switch (condition.Type)
                            {
                                case ValueType.Null:
                                case ValueType.False:
                                    fiber.StackTop--;
                                    break;
                                default:
                                    ip += offset;
                                    break;
                            }
                            break;
                        }

                    case Instruction.CLOSE_UPVALUE:
                        fiber.CloseUpvalue();
                        fiber.StackTop--;
                        break;

                    case Instruction.RETURN:
                        {
                            fiber.Frames.RemoveAt(--fiber.NumFrames);
                            Value result = stack[--fiber.StackTop];
                            // Close any upvalues still in scope.
                            if (fiber.StackTop > stackStart)
                            {
                                Value first = stack[stackStart];
                                while (fiber.OpenUpvalues != null &&
                                       fiber.OpenUpvalues.Container != first)
                                {
                                    fiber.CloseUpvalue();
                                }
                                fiber.CloseUpvalue();
                            }

                            // If the fiber is complete, end it.
                            if (fiber.NumFrames == 0)
                            {
                                // If this is the main fiber, we're done.
                                if (fiber.Caller == null) return true;

                                // We have a calling fiber to resume.
                                fiber = fiber.Caller;
                                stack = fiber.Stack;
                                // Store the result in the resuming fiber.
                                stack[fiber.StackTop - 1] = result;
                            }
                            else
                            {
                                // Discard the stack slots for the call frame (leaving one slot for the result).
                                fiber.StackTop = stackStart + 1;

                                // Store the result of the block in the first slot, which is where the
                                // caller expects it.
                                stack[fiber.StackTop - 1] = result;
                            }

                            /* Load Frame */
                            frame = fiber.Frames[fiber.NumFrames - 1];
                            ip = frame.ip;
                            stackStart = frame.StackStart;
                            fn = frame.fn as ObjFn ?? (frame.fn as ObjClosure).Function;
                            bytecode = fn.Bytecode;
                            break;
                        }

                    case Instruction.CLOSURE:
                        {
                            ObjFn prototype = fn.Constants[(bytecode[ip] << 8) + bytecode[ip + 1]].Obj as ObjFn;
                            ip += 2;

                            // Create the closure and push it on the stack before creating upvalues
                            // so that it doesn't get collected.
                            ObjClosure closure = new ObjClosure(prototype);
                            if (fiber.StackTop >= fiber.Capacity)
                                stack = fiber.IncreaseStack();
                            stack[fiber.StackTop++] = new Value(closure);

                            // Capture upvalues.
                            for (int i = 0; i < prototype.NumUpvalues; i++)
                            {
                                byte isLocal = bytecode[ip++];
                                index = bytecode[ip++];
                                if (isLocal > 0)
                                {
                                    // Make an new upvalue to close over the parent's local variable.
                                    closure.Upvalues[i] = fiber.CaptureUpvalue(stack[stackStart + index]);
                                }
                                else
                                {
                                    // Use the same upvalue as the current call frame.
                                    closure.Upvalues[i] = ((ObjClosure)frame.fn).Upvalues[index];
                                }
                            }

                            break;
                        }

                    case Instruction.CLASS:
                        {
                            Value name = stack[fiber.StackTop - 2];
                            ObjClass superclass = ObjectClass;

                            // Use implicit Object superclass if none given.
                            if (stack[fiber.StackTop - 1].Type != ValueType.Null)
                            {
                                Value error = ValidateSuperclass(name, stack[fiber.StackTop - 1]);
                                if (error != null)
                                {
                                    frame.ip = ip;
                                    RUNTIME_ERROR(fiber, error);
                                    if (fiber == null)
                                        return false;
                                    /* Load Frame */
                                    frame = fiber.Frames[fiber.NumFrames - 1];
                                    ip = frame.ip;
                                    stackStart = frame.StackStart;
                                    stack = fiber.Stack;
                                    fn = (frame.fn as ObjFn) ?? (frame.fn as ObjClosure).Function;
                                    bytecode = fn.Bytecode;
                                    break;
                                }
                                superclass = stack[fiber.StackTop - 1].Obj as ObjClass;
                            }

                            int numFields = bytecode[ip++];

                            Value classObj = new Value(new ObjClass(superclass, numFields, name.Obj as ObjString));

                            // Don't pop the superclass and name off the stack until the subclass is
                            // done being created, to make sure it doesn't get collected.
                            fiber.StackTop -= 2;

                            // Now that we know the total number of fields, make sure we don't overflow.
                            if (superclass.NumFields + numFields > Compiler.MAX_FIELDS)
                            {
                                frame.ip = ip;
                                RUNTIME_ERROR(fiber, new Value(string.Format("Class '{0}' may not have more than 255 fields, including inherited ones.", name.Obj)));
                                if (fiber == null)
                                    return false;
                                /* Load Frame */
                                frame = fiber.Frames[fiber.NumFrames - 1];
                                ip = frame.ip;
                                stackStart = frame.StackStart;
                                stack = fiber.Stack;
                                fn = (frame.fn as ObjFn) ?? (frame.fn as ObjClosure).Function;
                                bytecode = fn.Bytecode;
                                break;
                            }

                            if (fiber.StackTop >= fiber.Capacity)
                                stack = fiber.IncreaseStack();
                            stack[fiber.StackTop++] = classObj;
                            break;
                        }

                    case Instruction.METHOD_INSTANCE:
                    case Instruction.METHOD_STATIC:
                        {
                            int symbol = (bytecode[ip] << 8) + bytecode[ip + 1];
                            ip += 2;
                            ObjClass classObj = stack[fiber.StackTop - 1].Obj as ObjClass;
                            Value method = stack[fiber.StackTop - 2];
                            MethodType methodType = instruction == Instruction.METHOD_INSTANCE ? MethodType.None : MethodType.Static;
                            Value error = BindMethod(methodType, symbol, classObj, method);
                            if ((error.Obj is ObjString))
                            {
                                frame.ip = ip;
                                RUNTIME_ERROR(fiber, error);
                                if (fiber == null)
                                    return false;
                                /* Load Frame */
                                frame = fiber.Frames[fiber.NumFrames - 1];
                                ip = frame.ip;
                                stackStart = frame.StackStart;
                                stack = fiber.Stack;
                                fn = (frame.fn as ObjFn) ?? (frame.fn as ObjClosure).Function;
                                bytecode = fn.Bytecode;
                                break;
                            }
                            fiber.StackTop -= 2;
                            break;
                        }

                    case Instruction.LOAD_MODULE:
                        {
                            Value name = fn.Constants[(bytecode[ip] << 8) + bytecode[ip + 1]];
                            ip += 2;
                            Value result = ImportModule(name);

                            // If it returned a string, it was an error message.
                            if ((result.Obj is ObjString))
                            {
                                frame.ip = ip;
                                RUNTIME_ERROR(fiber, result);
                                if (fiber == null)
                                    return false;
                                /* Load Frame */
                                frame = fiber.Frames[fiber.NumFrames - 1];
                                ip = frame.ip;
                                stackStart = frame.StackStart;
                                stack = fiber.Stack;
                                fn = (frame.fn as ObjFn) ?? (frame.fn as ObjClosure).Function;
                                bytecode = fn.Bytecode;
                                break;
                            }

                            // Make a slot that the module's fiber can use to store its result in.
                            // It ends up getting discarded, but CODE_RETURN expects to be able to
                            // place a value there.
                            if (fiber.StackTop >= fiber.Capacity)
                                stack = fiber.IncreaseStack();
                            stack[fiber.StackTop++] = Value.Null;

                            // If it returned a fiber to execute the module body, switch to it.
                            if (result.Obj is ObjFiber)
                            {
                                // Return to this module when that one is done.
                                (result.Obj as ObjFiber).Caller = fiber;

                                frame.ip = ip;
                                fiber = (result.Obj as ObjFiber);
                                /* Load Frame */
                                frame = fiber.Frames[fiber.NumFrames - 1];
                                ip = frame.ip;
                                stackStart = frame.StackStart;
                                stack = fiber.Stack;
                                fn = (frame.fn as ObjFn) ?? (frame.fn as ObjClosure).Function;
                                bytecode = fn.Bytecode;
                            }

                            break;
                        }

                    case Instruction.IMPORT_VARIABLE:
                        {
                            Value module = fn.Constants[(bytecode[ip] << 8) + bytecode[ip + 1]];
                            ip += 2;
                            Value variable = fn.Constants[(bytecode[ip] << 8) + bytecode[ip + 1]];
                            ip += 2;
                            Value result;
                            if (ImportVariable(module, variable, out result))
                            {
                                if (fiber.StackTop >= fiber.Capacity)
                                    stack = fiber.IncreaseStack();
                                stack[fiber.StackTop++] = result;
                            }
                            else
                            {
                                frame.ip = ip;
                                RUNTIME_ERROR(fiber, result);
                                if (fiber == null)
                                    return false;
                                /* Load Frame */
                                frame = fiber.Frames[fiber.NumFrames - 1];
                                ip = frame.ip;
                                stackStart = frame.StackStart;
                                stack = fiber.Stack;
                                fn = (frame.fn as ObjFn) ?? (frame.fn as ObjClosure).Function;
                                bytecode = fn.Bytecode;
                            }
                            break;
                        }

                    case Instruction.END:
                        // A CODE_END should always be preceded by a CODE_RETURN. If we get here,
                        // the compiler generated wrong code.
                        return false;
                }
            }

            // We should only exit this function from an explicit return from CODE_RETURN
            // or a runtime error.
        }