示例#1
0
    def __init__(self, code, frame, globals):
        self.code = code
        self.env = Environment(frame, globals, self.code.localSize(),
                               promote(self.code.maxDepth),
                               promote(self.code.maxHandlerDepth))

        # For vat checkpointing.
        from typhon.vats import currentVat
        self.vat = currentVat.get()
示例#2
0
文件: machine.py 项目: washort/typhon
    def __init__(self, code, frame, globals):
        self.code = code
        self.env = Environment(frame, globals, self.code.localSize(),
                               promote(self.code.maxDepth),
                               promote(self.code.maxHandlerDepth))

        # For vat checkpointing.
        from typhon.vats import currentVat
        self.vat = currentVat.get()
示例#3
0
class SmallCaps(object):
    """
    A SmallCaps abstract bytecode interpreter.
    """

    _immutable_ = True
    _immutable_fields_ = "code", "env"

    def __init__(self, code, frame, globals):
        self.code = code
        self.env = Environment(frame, globals, self.code.localSize(),
                               promote(self.code.maxDepth),
                               promote(self.code.maxHandlerDepth))

        # For vat checkpointing.
        from typhon.vats import currentVat
        self.vat = currentVat.get()

    @staticmethod
    def withDictScope(code, scope):
        try:
            frame = [scope[key] for key in code.frame]
            globals = [scope[key] for key in code.globals]
        except KeyError:
            missing = []
            for key in code.frame:
                if key not in scope:
                    missing.append(u"%s (local)" % key)
            for key in code.globals:
                if key not in scope:
                    missing.append(u"%s (global)" % key)
            message = u"Keys not in scope: %s" % u", ".join(missing)
            raise userError(message)
        return SmallCaps(code, frame, globals)

    def pop(self):
        return self.env.pop()

    def popSlice(self, size):
        return self.env.popSlice(promote(size))

    def push(self, value):
        self.env.push(value)

    def peek(self):
        return self.env.peek()

    @unroll_safe
    def bindObject(self, scriptIndex):
        from typhon.objects.ejectors import theThrower
        script = self.code.script(scriptIndex)
        auditors = self.popSlice(script.numAuditors)
        globals = self.popSlice(script.globalSize)
        closure = self.popSlice(script.closureSize)
        obj = script.makeObject(closure, globals, auditors)
        # Not a typo. The first copy is the actual return value from creating
        # the object expression; the second copy is given to the slot
        # constructor.
        self.push(obj)
        self.push(obj)
        self.push(theThrower)
        if auditors[0] is NullObject:
            self.push(anyGuard)
        else:
            self.push(auditors[0])

    @unroll_safe
    def listPattern(self, size):
        ej = self.pop()
        xs = unwrapList(self.pop(), ej)
        if len(xs) != size:
            throw(
                ej,
                StrObject(u"Failed list pattern (needed %d, got %d)" %
                          (size, len(xs))))
        while size:
            size -= 1
            self.push(xs[size])
            self.push(ej)

    @unroll_safe
    @specialize.arg(2)
    def call(self, index, withMap):
        # Checkpoint the vat. This will, rarely, cause an exception to escape
        # from within us.
        self.vat.checkpoint()

        atom = self.code.atom(index)

        if withMap:
            # Grab the named args.
            namedArgs = self.pop()
            assert isinstance(namedArgs,
                              ConstMap), "No polymorphism in namedArgs"
            # Avoid _or() if possible; it is slow and JIT-opaque. ~ C.
            namedArgs = namedArgs._or(MIRANDA_ARGS)
        else:
            namedArgs = MIRANDA_ARGS

        args = self.popSlice(atom.arity)
        target = self.pop()

        # We used to add the call trail for tracebacks here, but it's been
        # moved to t.o.root. Happy bug hunting! ~ C.
        rv = target.callAtom(atom, args, namedArgs)
        if rv is None:
            print "A call to", target.__class__.__name__, "with atom", \
                  atom.repr, "returned None"
            raise RuntimeError("Implementation error")
        self.push(rv)

    @unroll_safe
    def buildMap(self, index):
        # XXX monteMap()
        d = monteMap()
        for i in range(index):
            # Yikes, the order of operations here is dangerous.
            d[self.pop()] = self.pop()
        self.push(ConstMap(d))

    def runInstruction(self, instruction, pc):
        index = self.code.index(pc)
        # jit_debug(self.code.disAt(pc))

        if instruction.asInt == ops.DUP.asInt:
            self.push(self.peek())
            return pc + 1
        elif instruction.asInt == ops.ROT.asInt:
            z = self.pop()
            y = self.pop()
            x = self.pop()
            self.push(y)
            self.push(z)
            self.push(x)
            return pc + 1
        elif instruction.asInt == ops.POP.asInt:
            self.pop()
            return pc + 1
        elif instruction.asInt == ops.SWAP.asInt:
            y = self.pop()
            x = self.pop()
            self.push(y)
            self.push(x)
            return pc + 1
        elif instruction.asInt == ops.ASSIGN_GLOBAL.asInt:
            value = self.pop()
            self.env.putValueGlobal(index, value)
            return pc + 1
        elif instruction.asInt == ops.ASSIGN_FRAME.asInt:
            value = self.pop()
            self.env.putValueFrame(index, value)
            return pc + 1
        elif instruction.asInt == ops.ASSIGN_LOCAL.asInt:
            value = self.pop()
            self.env.putValueLocal(index, value)
            return pc + 1
        elif instruction.asInt == ops.BIND.asInt:
            binding = self.pop()
            self.env.createBindingLocal(index, binding)
            return pc + 1
        elif instruction.asInt == ops.BINDFINALSLOT.asInt:
            guard = self.pop()
            ej = self.pop()
            specimen = self.pop()
            val = guard.call(u"coerce", [specimen, ej])
            self.env.createBindingLocal(index, finalBinding(val, guard))
            return pc + 1
        elif instruction.asInt == ops.BINDVARSLOT.asInt:
            guard = self.pop()
            ej = self.pop()
            specimen = self.pop()
            val = guard.call(u"coerce", [specimen, ej])
            self.env.createBindingLocal(index, varBinding(val, guard))
            return pc + 1
        elif instruction.asInt == ops.SLOT_GLOBAL.asInt:
            self.push(self.env.getSlotGlobal(index))
            return pc + 1
        elif instruction.asInt == ops.SLOT_FRAME.asInt:
            self.push(self.env.getSlotFrame(index))
            return pc + 1
        elif instruction.asInt == ops.SLOT_LOCAL.asInt:
            self.push(self.env.getSlotLocal(index))
            return pc + 1
        elif instruction.asInt == ops.NOUN_GLOBAL.asInt:
            self.push(self.env.getValueGlobal(index))
            return pc + 1
        elif instruction.asInt == ops.NOUN_FRAME.asInt:
            self.push(self.env.getValueFrame(index))
            return pc + 1
        elif instruction.asInt == ops.NOUN_LOCAL.asInt:
            self.push(self.env.getValueLocal(index))
            return pc + 1
        elif instruction.asInt == ops.BINDING_GLOBAL.asInt:
            self.push(self.env.getBindingGlobal(index))
            return pc + 1
        elif instruction.asInt == ops.BINDING_FRAME.asInt:
            self.push(self.env.getBindingFrame(index))
            return pc + 1
        elif instruction.asInt == ops.BINDING_LOCAL.asInt:
            self.push(self.env.getBindingLocal(index))
            return pc + 1
        elif instruction.asInt == ops.LIST_PATT.asInt:
            self.listPattern(index)
            return pc + 1
        elif instruction.asInt == ops.LITERAL.asInt:
            self.push(self.code.literal(index))
            return pc + 1
        elif instruction.asInt == ops.BINDOBJECT.asInt:
            self.bindObject(index)
            return pc + 1
        elif instruction.asInt == ops.EJECTOR.asInt:
            # Look carefully at the order of operations. The handler captures
            # the depth of the stack, so it's important to create it *before*
            # pushing the ejector onto the stack. Otherwise, the handler
            # thinks that the stack started off with an extra level of depth.
            ej = Ejector()
            handler = Eject(self, ej, index)
            self.push(ej)
            self.env.pushHandler(handler)
            return pc + 1
        elif instruction.asInt == ops.TRY.asInt:
            self.env.pushHandler(Catch(self, index))
            return pc + 1
        elif instruction.asInt == ops.UNWIND.asInt:
            self.env.pushHandler(Unwind(self, index))
            return pc + 1
        elif instruction.asInt == ops.END_HANDLER.asInt:
            handler = self.env.popHandler()
            return handler.drop(self, pc, index)
        elif instruction.asInt == ops.BRANCH.asInt:
            cond = unwrapBool(self.pop())
            if cond:
                return pc + 1
            else:
                return index
        elif instruction.asInt == ops.CALL.asInt:
            self.call(index, withMap=False)
            return pc + 1
        elif instruction.asInt == ops.CALL_MAP.asInt:
            self.call(index, withMap=True)
            return pc + 1
        elif instruction.asInt == ops.BUILD_MAP.asInt:
            self.buildMap(index)
            return pc + 1
        elif instruction.asInt == ops.NAMEDARG_EXTRACT.asInt:
            k = self.pop()
            d = unwrapMap(self.pop())
            if k not in d:
                raise userError(u"Named arg %s missing in call" %
                                (k.toString(), ))
            self.push(d[k])
            return pc + 1
        elif instruction.asInt == ops.NAMEDARG_EXTRACT_OPTIONAL.asInt:
            k = self.pop()
            d = unwrapMap(self.pop())
            if k not in d:
                self.push(NullObject)
                return pc + 1
            self.push(d[k])
            return index
        elif instruction.asInt == ops.JUMP.asInt:
            return index
        else:
            raise RuntimeError("Unknown instruction %s" %
                               instruction.repr.encode("utf-8"))

    # Second argument is how to get a code object from a machine object.
    @rvmprof.vmprof_execute_code("smallcaps", lambda self: self.code)
    @unroll_safe
    def run(self):
        jit_debug(self.code.profileName)
        # print ">" * 10
        pc = 0
        while pc < self.code.instSize():
            instruction = self.code.inst(promote(pc))
            try:
                pc = self.runInstruction(instruction, pc)
            except Ejecting as e:
                pc = self.unwindEjector(e)
            except UserException as ue:
                pc = self.unwindEx(ue)
        # If there is a final handler, drop it; it may cause exceptions to
        # propagate or perform some additional stack unwinding.
        if self.env.handlerDepth:
            finalHandler = self.env.popHandler()
            # Return value ignored here.
            finalHandler.drop(self, pc, pc)
        # print "<" * 10
        return 0

    @unroll_safe
    def unwindEjector(self, ex):
        while self.env.handlerDepth:
            handler = self.env.popHandler()
            rv = handler.eject(self, ex)
            if rv != -1:
                return rv
        raise ex

    @unroll_safe
    def unwindEx(self, ex):
        while self.env.handlerDepth:
            handler = self.env.popHandler()
            rv = handler.unwind(self, ex)
            if rv != -1:
                return rv
        raise ex
示例#4
0
 def testFinalImmutabilityLocal(self):
     env = Environment([], [], 1, 0, 0)
     env.createSlotLocal(0, FinalSlot(NullObject, NullObject),
                         theFinalSlotGuardMaker)
     self.assertRaises(UserException, env.putValueLocal, 0, NullObject)
示例#5
0
 def testFinalImmutabilityFrame(self):
     env = Environment([
         Binding(FinalSlot(NullObject, NullObject), theFinalSlotGuardMaker)
     ], [], 0, 0, 0)
     self.assertRaises(UserException, env.putValueFrame, 0, NullObject)
示例#6
0
class SmallCaps(object):
    """
    A SmallCaps abstract bytecode interpreter.
    """

    _immutable_ = True
    _immutable_fields_ = "code", "env"

    def __init__(self, code, frame, globals):
        self.code = code
        self.env = Environment(
            frame, globals, self.code.localSize(), promote(self.code.maxDepth), promote(self.code.maxHandlerDepth)
        )

        # For vat checkpointing.
        from typhon.vats import currentVat

        self.vat = currentVat.get()

    @staticmethod
    def withDictScope(code, scope):
        try:
            frame = [scope[key] for key in code.frame]
            globals = [scope[key] for key in code.globals]
        except KeyError:
            missing = []
            for key in code.frame:
                if key not in scope:
                    missing.append(u"%s (local)" % key)
            for key in code.globals:
                if key not in scope:
                    missing.append(u"%s (global)" % key)
            message = u"Keys not in scope: %s" % u", ".join(missing)
            raise userError(message)
        return SmallCaps(code, frame, globals)

    def pop(self):
        return self.env.pop()

    def popSlice(self, size):
        return self.env.popSlice(promote(size))

    def push(self, value):
        self.env.push(value)

    def peek(self):
        return self.env.peek()

    @unroll_safe
    def bindObject(self, scriptIndex):
        from typhon.objects.ejectors import theThrower

        script = self.code.script(scriptIndex)
        auditors = self.popSlice(script.numAuditors)
        globals = self.popSlice(script.globalSize)
        closure = self.popSlice(script.closureSize)
        obj = script.makeObject(closure, globals, auditors)
        # Not a typo. The first copy is the actual return value from creating
        # the object expression; the second copy is given to the slot
        # constructor.
        self.push(obj)
        self.push(obj)
        self.push(theThrower)
        if auditors[0] is NullObject:
            self.push(anyGuard)
        else:
            self.push(auditors[0])

    @unroll_safe
    def listPattern(self, size):
        ej = self.pop()
        xs = unwrapList(self.pop(), ej)
        if len(xs) != size:
            throw(ej, StrObject(u"Failed list pattern (needed %d, got %d)" % (size, len(xs))))
        while size:
            size -= 1
            self.push(xs[size])
            self.push(ej)

    @unroll_safe
    @specialize.arg(2)
    def call(self, index, withMap):
        # Checkpoint the vat. This will, rarely, cause an exception to escape
        # from within us.
        self.vat.checkpoint()

        atom = self.code.atom(index)

        if withMap:
            # Grab the named args.
            namedArgs = self.pop()
            assert isinstance(namedArgs, ConstMap), "No polymorphism in namedArgs"
            # Avoid _or() if possible; it is slow and JIT-opaque. ~ C.
            namedArgs = namedArgs._or(MIRANDA_ARGS)
        else:
            namedArgs = MIRANDA_ARGS

        args = self.popSlice(atom.arity)
        target = self.pop()

        # We used to add the call trail for tracebacks here, but it's been
        # moved to t.o.root. Happy bug hunting! ~ C.
        rv = target.callAtom(atom, args, namedArgs)
        if rv is None:
            print "A call to", target.__class__.__name__, "with atom", atom.repr, "returned None"
            raise RuntimeError("Implementation error")
        self.push(rv)

    @unroll_safe
    def buildMap(self, index):
        # XXX monteMap()
        d = monteMap()
        for i in range(index):
            # Yikes, the order of operations here is dangerous.
            d[self.pop()] = self.pop()
        self.push(ConstMap(d))

    def runInstruction(self, instruction, pc):
        index = self.code.index(pc)
        # jit_debug(self.code.disAt(pc))

        if instruction.asInt == ops.DUP.asInt:
            self.push(self.peek())
            return pc + 1
        elif instruction.asInt == ops.ROT.asInt:
            z = self.pop()
            y = self.pop()
            x = self.pop()
            self.push(y)
            self.push(z)
            self.push(x)
            return pc + 1
        elif instruction.asInt == ops.POP.asInt:
            self.pop()
            return pc + 1
        elif instruction.asInt == ops.SWAP.asInt:
            y = self.pop()
            x = self.pop()
            self.push(y)
            self.push(x)
            return pc + 1
        elif instruction.asInt == ops.ASSIGN_GLOBAL.asInt:
            value = self.pop()
            self.env.putValueGlobal(index, value)
            return pc + 1
        elif instruction.asInt == ops.ASSIGN_FRAME.asInt:
            value = self.pop()
            self.env.putValueFrame(index, value)
            return pc + 1
        elif instruction.asInt == ops.ASSIGN_LOCAL.asInt:
            value = self.pop()
            self.env.putValueLocal(index, value)
            return pc + 1
        elif instruction.asInt == ops.BIND.asInt:
            binding = self.pop()
            self.env.createBindingLocal(index, binding)
            return pc + 1
        elif instruction.asInt == ops.BINDFINALSLOT.asInt:
            guard = self.pop()
            ej = self.pop()
            specimen = self.pop()
            val = guard.call(u"coerce", [specimen, ej])
            self.env.createBindingLocal(index, finalBinding(val, guard))
            return pc + 1
        elif instruction.asInt == ops.BINDVARSLOT.asInt:
            guard = self.pop()
            ej = self.pop()
            specimen = self.pop()
            val = guard.call(u"coerce", [specimen, ej])
            self.env.createBindingLocal(index, varBinding(val, guard))
            return pc + 1
        elif instruction.asInt == ops.SLOT_GLOBAL.asInt:
            self.push(self.env.getSlotGlobal(index))
            return pc + 1
        elif instruction.asInt == ops.SLOT_FRAME.asInt:
            self.push(self.env.getSlotFrame(index))
            return pc + 1
        elif instruction.asInt == ops.SLOT_LOCAL.asInt:
            self.push(self.env.getSlotLocal(index))
            return pc + 1
        elif instruction.asInt == ops.NOUN_GLOBAL.asInt:
            self.push(self.env.getValueGlobal(index))
            return pc + 1
        elif instruction.asInt == ops.NOUN_FRAME.asInt:
            self.push(self.env.getValueFrame(index))
            return pc + 1
        elif instruction.asInt == ops.NOUN_LOCAL.asInt:
            self.push(self.env.getValueLocal(index))
            return pc + 1
        elif instruction.asInt == ops.BINDING_GLOBAL.asInt:
            self.push(self.env.getBindingGlobal(index))
            return pc + 1
        elif instruction.asInt == ops.BINDING_FRAME.asInt:
            self.push(self.env.getBindingFrame(index))
            return pc + 1
        elif instruction.asInt == ops.BINDING_LOCAL.asInt:
            self.push(self.env.getBindingLocal(index))
            return pc + 1
        elif instruction.asInt == ops.LIST_PATT.asInt:
            self.listPattern(index)
            return pc + 1
        elif instruction.asInt == ops.LITERAL.asInt:
            self.push(self.code.literal(index))
            return pc + 1
        elif instruction.asInt == ops.BINDOBJECT.asInt:
            self.bindObject(index)
            return pc + 1
        elif instruction.asInt == ops.EJECTOR.asInt:
            # Look carefully at the order of operations. The handler captures
            # the depth of the stack, so it's important to create it *before*
            # pushing the ejector onto the stack. Otherwise, the handler
            # thinks that the stack started off with an extra level of depth.
            ej = Ejector()
            handler = Eject(self, ej, index)
            self.push(ej)
            self.env.pushHandler(handler)
            return pc + 1
        elif instruction.asInt == ops.TRY.asInt:
            self.env.pushHandler(Catch(self, index))
            return pc + 1
        elif instruction.asInt == ops.UNWIND.asInt:
            self.env.pushHandler(Unwind(self, index))
            return pc + 1
        elif instruction.asInt == ops.END_HANDLER.asInt:
            handler = self.env.popHandler()
            return handler.drop(self, pc, index)
        elif instruction.asInt == ops.BRANCH.asInt:
            cond = unwrapBool(self.pop())
            if cond:
                return pc + 1
            else:
                return index
        elif instruction.asInt == ops.CALL.asInt:
            self.call(index, withMap=False)
            return pc + 1
        elif instruction.asInt == ops.CALL_MAP.asInt:
            self.call(index, withMap=True)
            return pc + 1
        elif instruction.asInt == ops.BUILD_MAP.asInt:
            self.buildMap(index)
            return pc + 1
        elif instruction.asInt == ops.NAMEDARG_EXTRACT.asInt:
            k = self.pop()
            d = unwrapMap(self.pop())
            if k not in d:
                raise userError(u"Named arg %s missing in call" % (k.toString(),))
            self.push(d[k])
            return pc + 1
        elif instruction.asInt == ops.NAMEDARG_EXTRACT_OPTIONAL.asInt:
            k = self.pop()
            d = unwrapMap(self.pop())
            if k not in d:
                self.push(NullObject)
                return pc + 1
            self.push(d[k])
            return index
        elif instruction.asInt == ops.JUMP.asInt:
            return index
        else:
            raise RuntimeError("Unknown instruction %s" % instruction.repr.encode("utf-8"))

    # Second argument is how to get a code object from a machine object.
    @rvmprof.vmprof_execute_code("smallcaps", lambda self: self.code)
    @unroll_safe
    def run(self):
        jit_debug(self.code.profileName)
        # print ">" * 10
        pc = 0
        while pc < self.code.instSize():
            instruction = self.code.inst(promote(pc))
            try:
                pc = self.runInstruction(instruction, pc)
            except Ejecting as e:
                pc = self.unwindEjector(e)
            except UserException as ue:
                pc = self.unwindEx(ue)
        # If there is a final handler, drop it; it may cause exceptions to
        # propagate or perform some additional stack unwinding.
        if self.env.handlerDepth:
            finalHandler = self.env.popHandler()
            # Return value ignored here.
            finalHandler.drop(self, pc, pc)
        # print "<" * 10
        return 0

    @unroll_safe
    def unwindEjector(self, ex):
        while self.env.handlerDepth:
            handler = self.env.popHandler()
            rv = handler.eject(self, ex)
            if rv != -1:
                return rv
        raise ex

    @unroll_safe
    def unwindEx(self, ex):
        while self.env.handlerDepth:
            handler = self.env.popHandler()
            rv = handler.unwind(self, ex)
            if rv != -1:
                return rv
        raise ex