Commits

Boris Feigin  committed cca62c8

* Adding rpython/ from hl-backend branch into trunk.

  • Participants
  • Parent commits 099b77d

Comments (0)

Files changed (110)

File pypy/rpython/__init__.py

+#

File pypy/rpython/annlowlevel.py

+"""
+The code needed to flow and annotate low-level helpers -- the ll_*() functions
+"""
+
+import types
+from pypy.annotation import model as annmodel
+from pypy.annotation.specialize import decide_callable
+from pypy.annotation.policy import AnnotatorPolicy
+from pypy.rpython import lltype
+from pypy.rpython import extfunctable
+
+def not_const(s_obj): # xxx move it somewhere else
+    if s_obj.is_constant():
+        new_s_obj = annmodel.SomeObject()
+        new_s_obj.__class__ = s_obj.__class__
+        new_s_obj.__dict__ = s_obj.__dict__
+        del new_s_obj.const
+        s_obj = new_s_obj
+    return s_obj
+
+
+class KeyComp(object):
+    def __init__(self, val):
+        self.val = val
+    def __eq__(self, other):
+        return self.__class__ == other.__class__ and self.val == other.val
+    def __ne__(self, other):
+        return not (self == other)
+    def __hash__(self):
+        return hash(self.val)
+    def __str__(self):
+        val = self.val
+        if isinstance(val, lltype.LowLevelType):
+            return val._short_name() + 'LlT'
+        s = getattr(val, '__name__', None)
+        if s is None:
+            compact = getattr(val, 'compact_repr', None)
+            if compact is None:
+                s = repr(s)
+            else:
+                s = compact()        
+        return s + 'Const'
+
+class LowLevelAnnotatorPolicy(AnnotatorPolicy):
+    allow_someobjects = False
+
+    def default_specialize(pol, bookkeeper, ignored, spaceop, func, args, mono):
+        args_s, kwds_s = args.unpack()
+        assert not kwds_s
+        if not args_s or not isinstance(func, types.FunctionType):
+            return None, None
+        key = [func]
+        new_args_s = []
+        for s_obj in args_s:
+            if isinstance(s_obj, annmodel.SomePBC):
+                assert s_obj.is_constant(), "ambiguous low-level helper specialization"
+                key.append(KeyComp(s_obj.const))
+                new_args_s.append(s_obj)
+            else:
+                new_args_s.append(not_const(s_obj))
+                try:
+                    key.append(annmodel.annotation_to_lltype(s_obj))
+                except ValueError:
+                    # passing non-low-level types to a ll_* function is allowed
+                    # for module/ll_*
+                    key.append(s_obj.__class__)
+        return tuple(key), bookkeeper.build_args('simple_call', new_args_s)
+
+    def override__init_opaque_object(pol, s_opaqueptr, s_value):
+        assert isinstance(s_opaqueptr, annmodel.SomePtr)
+        assert isinstance(s_opaqueptr.ll_ptrtype.TO, lltype.OpaqueType)
+        assert isinstance(s_value, annmodel.SomeExternalObject)
+        exttypeinfo = extfunctable.typetable[s_value.knowntype]
+        assert s_opaqueptr.ll_ptrtype.TO._exttypeinfo == exttypeinfo
+        return annmodel.SomeExternalObject(exttypeinfo.typ)
+
+    def override__from_opaque_object(pol, s_opaqueptr):
+        assert isinstance(s_opaqueptr, annmodel.SomePtr)
+        assert isinstance(s_opaqueptr.ll_ptrtype.TO, lltype.OpaqueType)
+        exttypeinfo = s_opaqueptr.ll_ptrtype.TO._exttypeinfo
+        return annmodel.SomeExternalObject(exttypeinfo.typ)
+
+
+def annotate_lowlevel_helper(annotator, ll_function, args_s):
+    saved = annotator.policy, annotator.added_blocks
+    annotator.policy = LowLevelAnnotatorPolicy()
+    try:
+        args = annotator.bookkeeper.build_args('simple_call', args_s)
+        (ll_function, args), key = decide_callable(annotator.bookkeeper, None, ll_function, args, mono=True, unpacked=True)
+        args_s, kwds_s = args.unpack()
+        assert not kwds_s
+        annotator.added_blocks = {}
+        s = annotator.build_types(ll_function, args_s)
+        # invoke annotation simplifications for the new blocks
+        annotator.simplify(block_subset=annotator.added_blocks)
+    finally:
+        annotator.policy, annotator.added_blocks = saved
+    return s, ll_function

File pypy/rpython/callparse.py

+from pypy.interpreter.pycode import cpython_code_signature
+from pypy.interpreter.argument import Arguments, ArgErr
+from pypy.annotation import model as annmodel
+from pypy.rpython import rtuple
+from pypy.rpython.rmodel import TyperError
+
+class CallPatternTooComplex(TyperError):
+    pass
+
+
+# for parsing call arguments
+class RPythonCallsSpace:
+    """Pseudo Object Space providing almost no real operation.
+    For the Arguments class: if it really needs other operations, it means
+    that the call pattern is too complex for R-Python.
+    """
+    def newtuple(self, items):
+        return NewTupleHolder(items)
+
+    def newdict(self, stuff):
+        raise CallPatternTooComplex, "'**' argument"
+
+    def unpackiterable(self, it, expected_length=None):
+        if it.is_tuple():
+            items = it.items()
+            if (expected_length is not None and
+                expected_length != len(items)):
+                raise ValueError
+            return items
+        raise CallPatternTooComplex, "'*' argument must be a tuple"
+
+
+def callparse(op, func, rinputs, hop):
+    space = RPythonCallsSpace()
+    def args_h(start):
+        return [VarHolder(i, hop.args_s[i]) for i in range(start, hop.nb_args)]
+    if op == "simple_call":
+        arguments =  Arguments(space, args_h(1))
+    elif op == "call_args":
+        arguments = Arguments.fromshape(space, hop.args_s[1].const, # shape
+                                        args_h(2))
+    # parse the arguments according to the function we are calling
+    signature = cpython_code_signature(func.func_code)
+    defs_h = []
+    if func.func_defaults:
+        for x in func.func_defaults:
+            defs_h.append(ConstHolder(x))
+    try:
+        holders = arguments.match_signature(signature, defs_h)
+    except ArgErr, e:
+        raise TyperError, "signature mismatch: %s" % e.getmsg(arguments, func.__name__)
+
+    assert len(holders) == len(rinputs), "argument parsing mismatch"
+    vlist = []
+    for h,r in zip(holders, rinputs):
+        v = h.emit(r, hop)
+        vlist.append(v)
+    return vlist
+
+
+class Holder(object):
+
+    def is_tuple(self):
+        return False
+
+    def emit(self, repr, hop):
+        try:
+            cache = self._cache
+        except AttributeError:
+            cache = self._cache = {}
+        try:
+            return cache[repr]
+        except KeyError:
+            v = self._emit(repr, hop)
+            cache[repr] = v
+            return v
+    
+
+class VarHolder(Holder):
+
+    def __init__(self, num, s_obj):
+        self.num = num
+        self.s_obj = s_obj
+
+    def is_tuple(self):
+        return isinstance(self.s_obj, annmodel.SomeTuple)
+
+    def items(self):
+        assert self.is_tuple()
+        n = len(self.s_obj.items)
+        return tuple([ItemHolder(self, i) for i in range(n)])
+        
+    def _emit(self, repr, hop):
+        return hop.inputarg(repr, arg=self.num)
+
+    def access(self, hop):
+        repr = hop.args_r[self.num]
+        return repr, self.emit(repr, hop)
+
+class ConstHolder(Holder):
+    def __init__(self, value):
+        self.value = value
+
+    def is_tuple(self):
+        return type(self.value) is tuple
+
+    def items(self):
+        assert self.is_tuple()
+        return self.value
+
+    def _emit(self, repr, hop):
+        return hop.inputconst(repr, self.value)
+
+
+class NewTupleHolder(Holder):
+    def __new__(cls, holders):
+        for h in holders:
+            if not isinstance(h, ItemHolder) or not h.holder == holders[0].holder:
+                break
+        else:
+            if 0 < len(holders) == len(holders[0].holder.items()):
+                return h[0].holder
+        inst = Holder.__new__(cls)
+        inst.holders = tuple(holders)
+        return inst
+
+    def is_tuple(self):
+        return True
+
+    def items(self):
+        return self.holders
+
+    def _emit(self, repr, hop):
+        assert isinstance(repr, rtuple.TupleRepr)
+        tupleitems_v = []
+        for h in self.holders:
+            v = h.emit(repr.items_r[len(tupleitems_v)], hop)
+            tupleitems_v.append(v)
+        vtuple = rtuple.newtuple(hop.llops, repr, tupleitems_v)
+        return vtuple
+
+
+class ItemHolder(Holder):
+    def __init__(self, holder, index):
+        self.holder = holder
+        self.index = index
+
+    def _emit(self, repr, hop):
+        index = self.index
+        r_tup, v_tuple = self.holder.access(hop)
+        v = r_tup.getitem(hop, v_tuple, index)
+        return hop.llops.convertvar(v, r_tup.items_r[index], repr)

File pypy/rpython/error.py

+
+class TyperError(Exception):
+    def __str__(self):
+        result = Exception.__str__(self)
+        if hasattr(self, 'where'):
+            result += '\n.. %r\n.. %r' % self.where
+        return result
+
+class MissingRTypeOperation(TyperError):
+    pass

File pypy/rpython/extfunctable.py

+"""
+information table about external functions for annotation/ rtyping and backends
+"""
+import os
+import time
+import math
+import types
+
+
+class ExtFuncInfo:
+    def __init__(self, func, annotation, ll_function_path, ll_annotable, backend_functiontemplate):
+        self.func = func
+        self.annotation = annotation
+        modulename, tail = ll_function_path.split('/')
+        if '.' not in modulename:
+            modulename = 'pypy.rpython.module.%s' % modulename
+        self.ll_module = ImportMe(modulename)
+        lastmodulename = modulename[modulename.rfind('.')+1:]
+        self.ll_function_name = '%s_%s' % (lastmodulename, tail)
+        self.ll_annotable = ll_annotable
+        self.backend_functiontemplate = backend_functiontemplate
+
+    def get_ll_function(self):
+        """Get the ll_*() function implementing the given high-level 'func'."""
+        mod = self.ll_module.load()
+        return getattr(mod, self.ll_function_name)
+    ll_function = property(get_ll_function)
+
+
+class ExtTypeInfo:
+    def __init__(self, typ, tag, methods):
+        self.typ = typ
+        self.tag = tag
+        self._TYPE = None
+        self.methods = methods     # {'name': ExtFuncInfo()}
+
+    def get_annotation(self, methodname):
+        return self.methods[methodname].annotation
+
+    def get_annotations(self):
+        return dict([(name, self.get_annotation(name))
+                     for name in self.methods])
+
+    def get_func_infos(self):
+        for extfuncinfo in self.methods.itervalues():
+            if extfuncinfo.func is not None:
+                yield (extfuncinfo.func, extfuncinfo)
+
+    def get_lltype(self):
+        if self._TYPE is None:
+            from pypy.rpython import lltype
+            OPAQUE = lltype.OpaqueType(self.tag)
+            OPAQUE._exttypeinfo = self
+            STRUCT = lltype.GcStruct(self.tag, ('obj', OPAQUE))
+            self._TYPE = STRUCT
+        return self._TYPE
+
+
+class ImportMe:
+    "Lazily imported module, for circular imports :-/"
+    def __init__(self, modulename):
+        self.modulename = modulename
+        self._mod = None
+    def load(self):
+        if self._mod is None:
+            self._mod = __import__(self.modulename, None, None, ['__doc__'])
+        return self._mod
+
+
+table_callbacks = []   # to track declare() that occur after 'table' is read
+
+table = {}
+def declare(func, annotation, ll_function, ll_annotable=True, backend_functiontemplate=None):
+    # annotation can be a function computing the annotation
+    # or a simple python type from which an annotation will be constructed
+    global table
+    if not isinstance(annotation, types.FunctionType):
+        typ = annotation
+        def annotation(*args_s):
+            from pypy.annotation import bookkeeper
+            return bookkeeper.getbookkeeper().valueoftype(typ)
+    info = ExtFuncInfo(func, annotation, ll_function, ll_annotable, backend_functiontemplate)
+    if func is not None:
+        table[func] = info
+        for callback in table_callbacks:
+            callback()
+    return info
+
+typetable = {}
+def declaretype(typ, tag, **methodsdecl):
+    assert isinstance(typ, type)
+    methods = {}
+    for name, args in methodsdecl.items():
+        # try to get the method object from the typ
+        for cls in typ.__mro__:
+            if name in typ.__dict__:
+                func = typ.__dict__[name]
+                break
+        else:
+            func = None   # failed (typical for old-style C types), ignore it
+        methods[name] = declare(func, *args)
+    info = ExtTypeInfo(typ, tag, methods)
+    typetable[typ] = info
+    for callback in table_callbacks:
+        callback()
+    return info
+
+# _____________________________________________________________
+
+
+
+def noneannotation(*args):
+    return None
+
+def posannotation(*args):
+    from pypy.annotation.model import SomeInteger
+    return SomeInteger(nonneg=True)
+
+def statannotation(*args):
+    from pypy.annotation.model import SomeInteger, SomeTuple
+    return SomeTuple((SomeInteger(),)*10)
+
+def frexpannotation(*args):
+    from pypy.annotation.model import SomeInteger, SomeTuple, SomeFloat
+    return SomeTuple((SomeFloat(), SomeInteger()))
+
+def modfannotation(*args):
+    from pypy.annotation.model import SomeTuple, SomeFloat
+    return SomeTuple((SomeFloat(), SomeFloat()))
+
+def strnullannotation(*args):
+    from pypy.annotation.model import SomeString
+    return SomeString(can_be_None=True)
+
+# external function declarations
+posix = __import__(os.name)
+declare(os.open     , int           , 'll_os/open')
+declare(os.read     , str           , 'll_os/read')
+declare(os.write    , posannotation , 'll_os/write')
+declare(os.close    , noneannotation, 'll_os/close')
+declare(os.dup      , int           , 'll_os/dup')
+declare(os.lseek    , int           , 'll_os/lseek')
+declare(os.isatty   , bool          , 'll_os/isatty')
+if hasattr(posix, 'ftruncate'):
+    declare(os.ftruncate, noneannotation, 'll_os/ftruncate')
+declare(os.fstat    , statannotation, 'll_os/fstat')
+declare(os.stat     , statannotation, 'll_os/stat')
+declare(os.system   , int           , 'll_os/system')
+declare(os.strerror , str           , 'll_os/strerror')
+declare(os.unlink   , noneannotation, 'll_os/unlink')
+declare(os.getcwd   , str           , 'll_os/getcwd')
+declare(os.chdir    , noneannotation, 'll_os/chdir')
+declare(os.mkdir    , noneannotation, 'll_os/mkdir')
+declare(os.rmdir    , noneannotation, 'll_os/rmdir')
+if hasattr(posix, 'unsetenv'):   # note: faked in os
+    declare(os.unsetenv , noneannotation, 'll_os/unsetenv')
+declare(os.path.exists, bool        , 'll_os_path/exists')
+declare(os.path.isdir, bool         , 'll_os_path/isdir')
+declare(time.time   , float         , 'll_time/time')
+declare(time.clock  , float         , 'll_time/clock')
+declare(time.sleep  , noneannotation, 'll_time/sleep')
+
+# ___________________________
+# math functions
+
+declare(math.frexp  , frexpannotation, 'll_math/frexp')
+declare(math.atan2  , float         , 'll_math/atan2')
+declare(math.fmod   , float         ,  'll_math/fmod')
+declare(math.floor  , float         ,  'll_math/floor')
+declare(math.ldexp  , float         ,  'll_math/ldexp')
+declare(math.modf   , modfannotation, 'll_math/modf')
+declare(math.hypot  , float         , 'll_math/hypot')
+declare(math.pow    , float         , 'll_math/pow')
+
+# the following functions all take one float, return one float
+# and are part of math.h
+simple_math_functions = [
+    'acos', 'asin', 'atan', 'ceil', 'cos', 'cosh', 'exp', 'fabs',
+    'floor', 'log', 'log10', 'sin', 'sinh', 'sqrt', 'tan', 'tanh'
+    ]
+
+for name in simple_math_functions:
+    declare(getattr(math, name), float, 'll_math/%s' % name)
+
+# ___________________________________________________________
+# win/NT hack: patch ntpath.isabs() to be RPythonic
+
+import ntpath
+def isabs(s):
+    """Test whether a path is absolute"""
+    s = ntpath.splitdrive(s)[1]
+    return s != '' and s[0] in '/\\'
+ntpath.isabs = isabs
+
+# ___________________________________________________________
+# string->float helper
+from pypy.rpython import rarithmetic
+declare(rarithmetic.parts_to_float, float, 'll_strtod/parts_to_float')
+# float->string helper
+declare(rarithmetic.formatd, str, 'll_strtod/formatd')
+
+# ___________________________________________________________
+# special helpers for os with no equivalent
+from pypy.rpython import ros
+declare(ros.putenv, noneannotation, 'll_os/putenv')
+declare(ros.environ, strnullannotation, 'll_os/environ')
+
+# ___________________________________________________________
+# stackless
+from pypy.rpython import objectmodel
+declare(objectmodel.stack_frames_depth, int, 'll_stackless/stack_frames_depth')
+declare(objectmodel.stack_too_big, bool, 'll_stack/too_big')
+declare(objectmodel.stack_check, noneannotation, 'll_stack/check')
+declare(objectmodel.stack_unwind, noneannotation, 'll_stack/unwind')
+
+# ___________________________________________________________
+# the exceptions that can be implicitely raised by some operations
+standardexceptions = {
+    TypeError        : True,
+    OverflowError    : True,
+    ValueError       : True,
+    ZeroDivisionError: True,
+    MemoryError      : True,
+    IOError          : True,
+    OSError          : True,
+    StopIteration    : True,
+    KeyError         : True,
+    IndexError       : True,
+    AssertionError   : True,
+    RuntimeError     : True,
+    }

File pypy/rpython/l3interp/__init__.py

Empty file added.

File pypy/rpython/l3interp/l3interp.py

+from pypy.rpython.l3interp import model
+from pypy.rpython.memory import lladdress
+
+class LLException(Exception):
+    def __init__(self):
+        pass
+
+class LLInterpreter(object):
+    def eval_graph_int(self, graph, args):
+        frame = LLFrame(graph, self)
+        returnlink = frame.eval(args)
+        return frame.get_int(0)
+
+class LLFrame(object):
+    def __init__(self, graph, lli):
+        self.interp = lli
+        self.graph = graph
+        self.int_vars = [0] * graph.max_num_ints
+
+    def eval(self, args):
+        assert len(args) == 0, "not implemented, XXX"
+        link = self.graph.startlink
+#        self.fill_input_arg(...
+        while type(link) == model.Link:
+            link = self.eval_block(link.target)
+            self.copy_link_vars(link)
+        return link
+
+    def eval_block(self, block):
+        for op in block.operations:
+            op.opimpl(self, op.result, op.args)
+        exitswitch = block.exitswitch
+        if exitswitch >= 0:
+            link = block.exits[self.int_vars[exitswitch]]
+            return link
+        return block.exits[0]
+        
+
+    def copy_link_vars(self, link):
+        for i in range(0, len(link.move_int_registers), 2):
+            source = link.move_int_registers[i]
+            target = link.move_int_registers[i + 1]
+            self.set_int(target, self.get_int(source))
+
+    def get_int(self, index):
+        if index < 0:
+            return self.graph.constants_int[~index]
+        else:
+            return self.int_vars[index]
+
+    def set_int(self, index, val):
+        self.int_vars[index] = val
+
+    def op_int_add(self, result, args):
+        int1 = self.get_int(args[0])
+        int2 = self.get_int(args[1])
+        self.set_int(result, int1 + int2)
+        
+

File pypy/rpython/l3interp/model.py

+from pypy.rpython.memory import lladdress
+from pypy.objspace.flow import model as flowmodel
+from pypy.rpython import lltype
+
+very_low_level_ops = [
+    #operations with adresses:
+    'adr_add', 'adr_delta', 'adr_eq', 'adr_ge', 'adr_gt', 'adr_le',
+    'adr_lt', 'adr_ne', 'adr_sub',
+
+    #operations with bools:
+    'bool_not',
+
+    #casts:
+    'cast_bool_to_float', 'cast_bool_to_int', 'cast_char_to_int',
+    'cast_float_to_int', 'cast_int_to_char', 'cast_int_to_float',
+    'cast_int_to_uint', 'cast_int_to_unichar', 'cast_pointer',
+    'cast_ptr_to_int', 'cast_uint_to_int', 'cast_unichar_to_int',
+
+    #operations with chars:
+    'char_eq', 'char_ge', 'char_gt', 'char_le', 'char_lt', 'char_ne',
+
+    #calls:
+    'direct_call',
+
+    #flavored memory operations:
+    'flavored_free', 'flavored_malloc',
+
+    #float operations:
+    'float_abs', 'float_add', 'float_div', 'float_eq', 'float_floor',
+    'float_floordiv', 'float_fmod', 'float_ge', 'float_gt', 'float_invert',
+    'float_is_true', 'float_le', 'float_lt', 'float_mod', 'float_mul',
+    'float_ne', 'float_neg', 'float_sub', 'float_truediv',
+
+    #array operations:
+    'getarrayitem', 'getarraysize', 'getarraysubstruct', 'setarrayitem',
+
+    #struct operations:
+    'getfield', 'getsubstruct', 'setfield', 
+
+    #integer operations:
+    'int_abs', 'int_abs_ovf', 'int_add', 'int_add_ovf', 'int_and',
+    'int_and_ovf', 'int_div', 'int_div_ovf', 'int_eq', 'int_eq_ovf',
+    'int_floordiv', 'int_floordiv_ovf', 'int_floordiv_ovf_zer', 'int_ge',
+    'int_ge_ovf', 'int_gt', 'int_gt_ovf', 'int_invert', 'int_invert_ovf',
+    'int_is_true', 'int_is_true_ovf', 'int_le', 'int_le_ovf', 'int_lshift',
+    'int_lshift_ovf', 'int_lt', 'int_lt_ovf', 'int_mod', 'int_mod_ovf',
+    'int_mod_ovf_zer', 'int_mul', 'int_mul_ovf', 'int_ne', 'int_ne_ovf',
+    'int_neg', 'int_neg_ovf', 'int_or', 'int_or_ovf', 'int_rshift',
+    'int_rshift_ovf', 'int_sub', 'int_sub_ovf', 'int_truediv',
+    'int_truediv_ovf', 'int_xor', 'int_xor_ovf',
+
+    #regular object memory operations:
+    'keepalive', 'malloc', 'malloc_varsize',
+
+    #pointer operations:
+    'ptr_eq', 'ptr_iszero', 'ptr_ne', 'ptr_nonzero',
+
+    #raw memory operations
+    'raw_free', 'raw_load', 'raw_malloc', 'raw_memcopy', 'raw_store',
+
+    #same_as:
+    'same_as',
+
+    #operations with unsigned integers: 
+    'uint_abs', 'uint_add', 'uint_and', 'uint_div', 'uint_eq',
+    'uint_floordiv', 'uint_ge', 'uint_gt', 'uint_invert', 'uint_is_true',
+    'uint_le', 'uint_lshift', 'uint_lt', 'uint_mod', 'uint_mul', 'uint_ne',
+    'uint_neg', 'uint_or', 'uint_rshift', 'uint_sub', 'uint_truediv',
+    'uint_xor',
+
+    #operations with unicode characters
+    'unichar_eq', 'unichar_ne'
+    ]
+
+
+
+
+primitives = [lltype.Signed, lltype.Unsigned, lltype.Float, lltype.Char,
+              lltype.UniChar, lladdress.Address, lltype.Void]
+
+primitive_to_number = {}
+for i, p in enumerate(primitives):
+    primitive_to_number[p] = -i - 1
+del p
+
+
+# possible values for exitswitch:
+ONE_EXIT = -1
+LAST_EXCEPTION = -2
+
+class Operation(object):
+    def __init__(self, opimpl, result, args):
+        self.opimpl = opimpl # unbound method of LLFrame
+        self.args = args     # list of ints: how to represent constants?
+        self.result = result # resulting variable
+
+class Link(object):
+    def __init__(self, target, args, exitcase=None):
+        self.target = target # target is a Block
+        self.intargs = args  # args is a list of ints, same meaning as Operation.args
+        self.exitcase = exitcase  # NULL for non-exceptional case
+                                  # address of exception class else
+        self.move_int_registers = []
+
+class ReturnLink(Link):
+    pass
+
+class StartLink(Link):
+    pass
+
+class Block(object):
+    def __init__(self, int_a, exitswitch, exits):
+        self.operations = []         # list of Operations
+        self.exitswitch = exitswitch # positives are variables
+                                     # negatives see above
+        self.exits = exits           # list of Links
+        self.int_inputargs = int_a   # list of ints
+
+class Graph(object):
+    def __init__(self, name, startlink):
+        self.name = name             # string
+        self.startlink = startlink # Block
+        self.constants_int = []
+        self.max_num_ints = 17 #XXX calculate this
+
+    def set_constants_int(self, constants):
+        self.constants_int = constants
+
+    def blocklist(self):
+        result = []
+        pending = [self.startblock]
+        seen = {}
+        while len(pending):
+            block = pending.pop()
+            if block in seen:
+                continue
+            result.append(block)
+            for i in range(len(block.exits)):
+                pending.append(block.exits[i].target)
+        return result
+
+class Globals(object):
+    def __init__(self):
+        self.graphs = []    # list of Graphs
+

File pypy/rpython/l3interp/test/__init__.py

+#

File pypy/rpython/l3interp/test/test_l3interp.py

+from pypy.rpython.l3interp import l3interp
+from pypy.rpython.l3interp import model
+
+def test_very_simple():
+    op = model.Operation(l3interp.LLFrame.op_int_add, 0, [-1, -2])
+    returnlink = model.ReturnLink(None, [])
+    block = model.Block([], model.ONE_EXIT, [returnlink])
+    block.operations.append(op)
+    startlink = model.Link(block, [])
+    graph = model.Graph("testgraph", startlink)
+    graph.set_constants_int([3, 4])
+    g = model.Globals()
+    g.graphs = [graph]
+    interp = l3interp.LLInterpreter()
+    result = interp.eval_graph_int(graph, [])
+    assert result == 7
+    

File pypy/rpython/llinterp.py

+from pypy.translator.translator import Translator
+from pypy.tool.sourcetools import compile2
+from pypy.objspace.flow.model import Constant, Variable, last_exception
+from pypy.rpython.rarithmetic import intmask, r_uint, ovfcheck
+from pypy.rpython import lltype
+from pypy.rpython.rmodel import getfunctionptr
+from pypy.rpython.memory import lladdress
+from pypy.rpython.objectmodel import free_non_gc_object
+
+import math
+import py
+
+log = py.log.Producer('llinterp')
+
+class LLException(Exception):
+    def __str__(self):
+        etype, evalue = self.args
+        return '<LLException %r>' % (''.join(etype.name).rstrip('\x00'),)
+
+class LLInterpreter(object):
+    """ low level interpreter working with concrete values. """
+
+    def __init__(self, flowgraphs, typer, lltype=lltype):
+        self.flowgraphs = flowgraphs
+        self.bindings = {}
+        self.typer = typer
+        self.llt = lltype  #module that contains the used lltype classes
+        self.active_frame = None
+        # XXX hack hack hack: set gc to None because
+        # prepare_graphs_and_create_gc might already use the llinterpreter!
+        self.gc = None
+        if hasattr(lltype, "prepare_graphs_and_create_gc"):
+            self.gc = lltype.prepare_graphs_and_create_gc(self, flowgraphs)
+
+    def getgraph(self, func):
+        return self.flowgraphs[func]
+
+    def eval_function(self, func, args=(), graph=None):
+        if graph is None:
+            graph = self.getgraph(func)
+        llframe = LLFrame(graph, args, self)
+        try:
+            return llframe.eval()
+        except LLException, e:
+            print "LLEXCEPTION:", e
+            self.print_traceback()
+            raise
+        except Exception, e:
+            print "AN ERROR OCCURED:", e
+            self.print_traceback()
+            raise
+
+    def print_traceback(self):
+        frame = self.active_frame
+        frames = []
+        while frame is not None:
+            frames.append(frame)
+            frame = frame.f_back
+        frames.reverse()
+        for frame in frames:
+            print frame.graph.name,
+            if frame.curr_block is None:
+                print "<not running yet>"
+                continue
+            try:
+                print self.typer.annotator.annotated[frame.curr_block].__module__
+            except KeyError:
+                # if the graph is from the GC it was not produced by the same
+                # translator :-(
+                print "<unknown module>"
+            for i, operation in enumerate(frame.curr_block.operations):
+                if i == frame.curr_operation_index:
+                    print "E  ",
+                else:
+                    print "   ",
+                print operation
+
+    def find_roots(self):
+        log.findroots("starting")
+        frame = self.active_frame
+        roots = []
+        while frame is not None:
+            log.findroots("graph", frame.graph.name)
+            frame.find_roots(roots)
+            frame = frame.f_back
+        return roots
+
+
+# implementations of ops from flow.operation
+from pypy.objspace.flow.operation import FunctionByName
+opimpls = FunctionByName.copy()
+opimpls['is_true'] = bool
+
+ops_returning_a_bool = {'gt': True, 'ge': True,
+                        'lt': True, 'le': True,
+                        'eq': True, 'ne': True,
+                        'is_true': True}
+
+class LLFrame(object):
+    def __init__(self, graph, args, llinterpreter, f_back=None):
+        self.graph = graph
+        self.args = args
+        self.llinterpreter = llinterpreter
+        self.llt = llinterpreter.llt
+        self.bindings = {}
+        self.f_back = f_back
+        self.curr_block = None
+        self.curr_operation_index = 0
+
+    # _______________________________________________________
+    # variable setters/getters helpers
+
+    def clear(self):
+        self.bindings.clear()
+
+    def fillvars(self, block, values):
+        vars = block.inputargs
+        assert len(vars) == len(values), (
+                   "block %s received %d args, expected %d" % (
+                    block, len(values), len(vars)))
+        for var, val in zip(vars, values):
+            self.setvar(var, val)
+
+    def setvar(self, var, val):
+        if var.concretetype != self.llt.Void:
+            assert var.concretetype == self.llt.typeOf(val)
+        assert isinstance(var, Variable)
+        self.bindings[var] = val
+
+    def setifvar(self, var, val):
+        if isinstance(var, Variable):
+            self.setvar(var, val)
+
+    def getval(self, varorconst):
+        try:
+            return varorconst.value
+        except AttributeError:
+            return self.bindings[varorconst]
+
+    # _______________________________________________________
+    # other helpers
+    def getoperationhandler(self, opname):
+        ophandler = getattr(self, 'op_' + opname, None)
+        if ophandler is None:
+            raise AssertionError, "cannot handle operation %r yet" %(opname,)
+        return ophandler
+    # _______________________________________________________
+    # evaling functions
+
+    def eval(self):
+        self.llinterpreter.active_frame = self
+        graph = self.graph
+        log.frame("evaluating", graph.name)
+        nextblock = graph.startblock
+        args = self.args
+        while 1:
+            self.clear()
+            self.fillvars(nextblock, args)
+            nextblock, args = self.eval_block(nextblock)
+            if nextblock is None:
+                self.llinterpreter.active_frame = self.f_back
+                return args
+
+    def eval_block(self, block):
+        """ return (nextblock, values) tuple. If nextblock
+            is None, values is the concrete return value.
+        """
+        self.curr_block = block
+        catch_exception = block.exitswitch == Constant(last_exception)
+        e = None
+
+        try:
+            for i, op in enumerate(block.operations):
+                self.curr_operation_index = i
+                self.eval_operation(op)
+        except LLException, e:
+            if not (catch_exception and op is block.operations[-1]):
+                raise
+
+        # determine nextblock and/or return value
+        if len(block.exits) == 0:
+            # return block
+            if len(block.inputargs) == 2:
+                # exception
+                etypevar, evaluevar = block.getvariables()
+                etype = self.getval(etypevar)
+                evalue = self.getval(evaluevar)
+                # watch out, these are _ptr's
+                raise LLException(etype, evalue)
+            resultvar, = block.getvariables()
+            result = self.getval(resultvar)
+            log.operation("returning", result)
+            return None, result
+        elif block.exitswitch is None:
+            # single-exit block
+            assert len(block.exits) == 1
+            link = block.exits[0]
+        elif catch_exception:
+            link = block.exits[0]
+            if e:
+                exdata = self.llinterpreter.typer.getexceptiondata()
+                cls, inst = e.args
+                for link in block.exits[1:]:
+                    assert issubclass(link.exitcase, Exception)
+                    if self.llinterpreter.eval_function(
+                        exdata.ll_exception_match, [cls, link.llexitcase]):
+                        self.setifvar(link.last_exception, cls)
+                        self.setifvar(link.last_exc_value, inst)
+                        break
+                else:
+                    # no handler found, pass on
+                    raise e
+        else:
+            index = self.getval(block.exitswitch)
+            link = block.exits[index]
+        return link.target, [self.getval(x) for x in link.args]
+
+    def eval_operation(self, operation):
+        log.operation("considering", operation)
+        ophandler = self.getoperationhandler(operation.opname)
+        vals = [self.getval(x) for x in operation.args]
+        # if these special cases pile up, do something better here
+        if operation.opname == 'cast_pointer':
+            vals.insert(0, operation.result.concretetype)
+        retval = ophandler(*vals)
+        self.setvar(operation.result, retval)
+
+    def make_llexception(self, exc):
+        exdata = self.llinterpreter.typer.getexceptiondata()
+        if isinstance(exc, OSError):
+            fn = getfunctionptr(self.llinterpreter.typer.annotator.translator,
+                                exdata.ll_raise_OSError)
+            self.op_direct_call(fn, exc.errno)
+            assert False, "op_direct_call above should have raised"
+        else:
+            exc_class = exc.__class__
+            evalue = self.llinterpreter.eval_function(
+                exdata.ll_pyexcclass2exc, [self.llt.pyobjectptr(exc_class)])
+            etype = self.llinterpreter.eval_function(
+                exdata.ll_type_of_exc_inst, [evalue])
+        raise LLException(etype, evalue)
+
+    def invoke_callable_with_pyexceptions(self, fptr, *args):
+        try:
+            return fptr._obj._callable(*args)
+        except Exception, e:
+            #print "GOT A CPYTHON EXCEPTION:", e.__class__, e
+            self.make_llexception(e)
+
+    def find_roots(self, roots):
+        log.findroots(self.curr_block.inputargs)
+        for arg in self.curr_block.inputargs:
+            if (isinstance(arg, Variable) and
+                isinstance(self.getval(arg), self.llt._ptr)):
+                roots.append(self.getval(arg))
+        for op in self.curr_block.operations[:self.curr_operation_index]:
+            if isinstance(self.getval(op.result), self.llt._ptr):
+                roots.append(self.getval(op.result))
+
+    # __________________________________________________________
+    # misc LL operation implementations
+
+    def op_keepalive(self, value):
+        pass
+
+    def op_same_as(self, x):
+        return x
+
+    def op_setfield(self, obj, fieldname, fieldvalue):
+        # obj should be pointer
+        FIELDTYPE = getattr(self.llt.typeOf(obj).TO, fieldname)
+        if FIELDTYPE != self.llt.Void:
+            gc = self.llinterpreter.gc
+            if gc is None or not gc.needs_write_barrier(FIELDTYPE):
+                setattr(obj, fieldname, fieldvalue)
+            else:
+                args = gc.get_arg_write_barrier(obj, fieldname, fieldvalue)
+                write_barrier = gc.get_funcptr_write_barrier()
+                result = self.op_direct_call(write_barrier, *args)
+
+    def op_getarrayitem(self, array, index):
+        return array[index]
+
+    def op_setarrayitem(self, array, index, item):
+        # array should be a pointer
+        ITEMTYPE = self.llt.typeOf(array).TO.OF
+        if ITEMTYPE != self.llt.Void:
+            gc = self.llinterpreter.gc
+            if gc is None or not gc.needs_write_barrier(ITEMTYPE):
+                array[index] = item
+            else:
+                args = gc.get_arg_write_barrier(array, index, item)
+                write_barrier = gc.get_funcptr_write_barrier()
+                self.op_direct_call(write_barrier, *args)
+
+    def op_direct_call(self, f, *args):
+        has_callable = getattr(f._obj, '_callable', None) is not None
+        if has_callable and getattr(f._obj._callable, 'suggested_primitive', False):
+                return self.invoke_callable_with_pyexceptions(f, *args)
+        if hasattr(f._obj, 'graph'):
+            graph = f._obj.graph
+        else:
+            try:
+                graph = self.llinterpreter.getgraph(f._obj._callable)
+            except KeyError:
+                assert has_callable, "don't know how to execute %r" % f
+                return self.invoke_callable_with_pyexceptions(f, *args)
+        frame = self.__class__(graph, args, self.llinterpreter, self)
+        return frame.eval()
+
+    def op_malloc(self, obj):
+        if self.llinterpreter.gc is not None:
+            args = self.llinterpreter.gc.get_arg_malloc(obj)
+            malloc = self.llinterpreter.gc.get_funcptr_malloc()
+            result = self.op_direct_call(malloc, *args)
+            return self.llinterpreter.gc.adjust_result_malloc(result, obj)
+        else:
+            return self.llt.malloc(obj)
+
+    def op_malloc_varsize(self, obj, size):
+        if self.llinterpreter.gc is not None:
+            args = self.llinterpreter.gc.get_arg_malloc(obj, size)
+            malloc = self.llinterpreter.gc.get_funcptr_malloc()
+            result = self.op_direct_call(malloc, *args)
+            return self.llinterpreter.gc.adjust_result_malloc(result, obj, size)
+        else:
+            try:
+                return self.llt.malloc(obj, size)
+            except MemoryError, e:
+                self.make_llexception(e)
+
+    def op_flavored_malloc(self, flavor, obj):
+        assert isinstance(flavor, str)
+        return self.llt.malloc(obj, flavor=flavor)
+
+    def op_flavored_free(self, flavor, obj):
+        assert isinstance(flavor, str)
+        self.llt.free(obj, flavor=flavor)
+
+    def op_getfield(self, obj, field):
+        assert isinstance(obj, self.llt._ptr)
+        result = getattr(obj, field)
+        # check the difference between op_getfield and op_getsubstruct:
+        # the former returns the real field, the latter a pointer to it
+        assert self.llt.typeOf(result) == getattr(self.llt.typeOf(obj).TO,
+                                                  field)
+        return result
+
+    def op_getsubstruct(self, obj, field):
+        assert isinstance(obj, self.llt._ptr)
+        result = getattr(obj, field)
+        # check the difference between op_getfield and op_getsubstruct:
+        # the former returns the real field, the latter a pointer to it
+        assert (self.llt.typeOf(result) ==
+                self.llt.Ptr(getattr(self.llt.typeOf(obj).TO, field)))
+        return result
+
+    def op_getarraysubstruct(self, array, index):
+        assert isinstance(array, self.llt._ptr)
+        result = array[index]
+        return result
+        # the diff between op_getarrayitem and op_getarraysubstruct
+        # is the same as between op_getfield and op_getsubstruct
+
+    def op_getarraysize(self, array):
+        #print array,type(array),dir(array)
+        assert isinstance(self.llt.typeOf(array).TO, self.llt.Array)
+        return len(array)
+
+    def op_cast_pointer(self, tp, obj):
+        # well, actually this is what's now in the globals.
+        return self.llt.cast_pointer(tp, obj)
+
+    def op_ptr_eq(self, ptr1, ptr2):
+        assert isinstance(ptr1, self.llt._ptr)
+        assert isinstance(ptr2, self.llt._ptr)
+        return ptr1 == ptr2
+
+    def op_ptr_ne(self, ptr1, ptr2):
+        assert isinstance(ptr1, self.llt._ptr)
+        assert isinstance(ptr2, self.llt._ptr)
+        return ptr1 != ptr2
+
+    def op_ptr_nonzero(self, ptr1):
+        assert isinstance(ptr1, self.llt._ptr)
+        return bool(ptr1)
+
+    def op_ptr_iszero(self, ptr1):
+        assert isinstance(ptr1, self.llt._ptr)
+        return not bool(ptr1)
+
+    def op_cast_ptr_to_int(self, ptr1):
+        assert isinstance(ptr1, self.llt._ptr)
+        assert isinstance(self.llt.typeOf(ptr1).TO, (self.llt.Array,
+                                                     self.llt.Struct))
+        return self.llt.cast_ptr_to_int(ptr1)
+
+    def op_cast_int_to_float(self, i):
+        assert type(i) is int
+        return float(i)
+
+    def op_cast_int_to_char(self, b):
+        assert type(b) is int
+        return chr(b)
+
+    def op_cast_bool_to_int(self, b):
+        assert type(b) is bool
+        return int(b)
+
+    def op_cast_bool_to_float(self, b):
+        assert type(b) is bool
+        return float(b)
+
+    def op_bool_not(self, b):
+        assert type(b) is bool
+        return not b
+
+    def op_cast_float_to_int(self, f):
+        assert type(f) is float
+        return ovfcheck(int(f))
+    
+    def op_cast_char_to_int(self, b):
+        assert type(b) is str and len(b) == 1
+        return ord(b)
+
+    def op_cast_unichar_to_int(self, b):
+        assert type(b) is unicode and len(b) == 1
+        return ord(b)
+
+    def op_cast_int_to_unichar(self, b):
+        assert type(b) is int 
+        return unichr(b)
+
+    def op_cast_int_to_uint(self, b):
+        assert type(b) is int
+        return r_uint(b)
+
+    def op_cast_uint_to_int(self, b):
+        assert type(b) is r_uint
+        return intmask(b)
+
+    def op_int_floordiv_ovf_zer(self, a, b):
+        assert type(a) is int
+        assert type(b) is int
+        if b == 0:
+            self.make_llexception(ZeroDivisionError())
+        return self.op_int_floordiv_ovf(a, b)
+            
+    def op_int_mod_ovf_zer(self, a, b):
+        assert type(a) is int
+        assert type(b) is int
+        if b == 0:
+            self.make_llexception(ZeroDivisionError())
+        return self.op_int_mod_ovf(a, b)
+            
+    def op_float_floor(self, b):
+        assert type(b) is float
+        return math.floor(b)
+
+    def op_float_fmod(self, b,c):
+        assert type(b) is float
+        assert type(c) is float
+        return math.fmod(b,c)
+
+    # operations on pyobjects!
+    for opname in opimpls.keys():
+        exec py.code.Source("""
+        def op_%(opname)s(self, *pyobjs):
+            for pyo in pyobjs:
+                assert self.llt.typeOf(pyo) == self.llt.Ptr(self.llt.PyObject)
+            func = opimpls[%(opname)r]
+            try:
+                pyo = func(*[pyo._obj.value for pyo in pyobjs])
+            except Exception, e:
+                self.make_llexception(e)
+            return self.llt.pyobjectptr(pyo)
+        """ % locals()).compile()
+    del opname
+
+    def op_simple_call(self, f, *args):
+        assert self.llt.typeOf(f) == self.llt.Ptr(self.llt.PyObject)
+        for pyo in args:
+            assert self.llt.typeOf(pyo) == self.llt.Ptr(self.llt.PyObject)
+        res = f._obj.value(*[pyo._obj.value for pyo in args])
+        return self.llt.pyobjectptr(res)
+
+    # __________________________________________________________
+    # operations on addresses
+
+    def op_raw_malloc(self, size):
+        assert self.llt.typeOf(size) == self.llt.Signed
+        return lladdress.raw_malloc(size)
+
+    def op_raw_free(self, addr):
+        assert self.llt.typeOf(addr) == lladdress.Address
+        lladdress.raw_free(addr)
+
+    def op_raw_memcopy(self, fromaddr, toaddr, size):
+        assert self.llt.typeOf(fromaddr) == lladdress.Address
+        assert self.llt.typeOf(toaddr) == lladdress.Address
+        lladdress.raw_memcopy(fromaddr, toaddr, size)
+
+    def op_raw_load(self, addr, typ, offset):
+        assert isinstance(addr, lladdress.address)
+        value = getattr(addr, str(typ).lower())[offset]
+        assert self.llt.typeOf(value) == typ
+        return value
+
+    def op_raw_store(self, addr, typ, offset, value):
+        assert isinstance(addr, lladdress.address)
+        assert self.llt.typeOf(value) == typ
+        getattr(addr, str(typ).lower())[offset] = value
+
+    def op_adr_add(self, addr, offset):
+        assert isinstance(addr, lladdress.address)
+        assert self.llt.typeOf(offset) is self.llt.Signed
+        return addr + offset
+
+    def op_adr_sub(self, addr, offset):
+        assert isinstance(addr, lladdress.address)
+        assert self.llt.typeOf(offset) is self.llt.Signed
+        return addr - offset
+
+    def op_adr_delta(self, addr1, addr2):
+        assert isinstance(addr1, lladdress.address)
+        assert isinstance(addr2, lladdress.address)
+        return addr1 - addr2
+
+    for opname, op in (("eq", "=="), ("ne", "!="), ("le", "<="), ("lt", "<"),
+                       ("gt", ">"), ("ge", ">=")):
+        exec py.code.Source("""
+            def op_adr_%s(self, addr1, addr2):
+                assert isinstance(addr1, lladdress.address)
+                assert isinstance(addr2, lladdress.address)
+                return addr1 %s addr2""" % (opname, op)).compile()
+
+    # __________________________________________________________
+    # primitive operations
+
+    for typ in (float, int, r_uint):
+        typname = typ.__name__
+        optup = ('add', 'sub', 'mul', 'div', 'truediv', 'floordiv',
+                 'mod', 'gt', 'lt', 'ge', 'ne', 'le', 'eq',)
+        if typ is r_uint:
+            opnameprefix = 'uint'
+        else:
+            opnameprefix = typname
+        if typ in (int, r_uint):
+            optup += 'and_', 'or_', 'lshift', 'rshift', 'xor'
+        for opname in optup:
+            assert opname in opimpls
+            if typ is int and opname not in ops_returning_a_bool:
+                adjust_result = 'intmask'
+            else:
+                adjust_result = ''
+            pureopname = opname.rstrip('_')
+            exec py.code.Source("""
+                def op_%(opnameprefix)s_%(pureopname)s(self, x, y):
+                    assert isinstance(x, %(typname)s)
+                    assert isinstance(y, %(typname)s)
+                    func = opimpls[%(opname)r]
+                    return %(adjust_result)s(func(x, y))
+            """ % locals()).compile()
+            if typ is int:
+                opname += '_ovf'
+                exec py.code.Source("""
+                    def op_%(opnameprefix)s_%(pureopname)s_ovf(self, x, y):
+                        assert isinstance(x, %(typname)s)
+                        assert isinstance(y, %(typname)s)
+                        func = opimpls[%(opname)r]
+                        try:
+                            return %(adjust_result)s(func(x, y))
+                        except OverflowError, e:
+                            self.make_llexception(e)
+                """ % locals()).compile()
+        for opname in 'is_true', 'neg', 'abs', 'invert':
+            assert opname in opimpls
+            if typ is int and opname not in ops_returning_a_bool:
+                adjust_result = 'intmask'
+            else:
+                adjust_result = ''
+            exec py.code.Source("""
+                def op_%(opnameprefix)s_%(opname)s(self, x):
+                    assert isinstance(x, %(typname)s)
+                    func = opimpls[%(opname)r]
+                    return %(adjust_result)s(func(x))
+            """ % locals()).compile()
+            if typ is int:
+                opname += '_ovf'
+                exec py.code.Source("""
+                    def op_%(opnameprefix)s_%(opname)s(self, x):
+                        assert isinstance(x, %(typname)s)
+                        func = opimpls[%(opname)r]
+                        try:
+                            return %(adjust_result)s(func(x))
+                        except OverflowError, e:
+                            self.make_llexception(e)
+                """ % locals()).compile()
+            
+    for opname in ('gt', 'lt', 'ge', 'ne', 'le', 'eq'):
+        assert opname in opimpls
+        exec py.code.Source("""
+            def op_char_%(opname)s(self, x, y):
+                assert isinstance(x, str) and len(x) == 1
+                assert isinstance(y, str) and len(y) == 1
+                func = opimpls[%(opname)r]
+                return func(x, y)
+        """ % locals()).compile()
+    
+    def op_unichar_eq(self, x, y):
+        assert isinstance(x, unicode) and len(x) == 1
+        assert isinstance(y, unicode) and len(y) == 1
+        func = opimpls['eq']
+        return func(x, y)
+
+    def op_unichar_ne(self, x, y):
+        assert isinstance(x, unicode) and len(x) == 1
+        assert isinstance(y, unicode) and len(y) == 1
+        func = opimpls['ne']
+        return func(x, y)
+
+# by default we route all logging messages to nothingness
+# e.g. tests can then switch on logging to get more help
+# for failing tests
+from pypy.tool.ansi_print import ansi_log
+py.log.setconsumer('llinterp', ansi_log)

File pypy/rpython/lltype.py

+from pypy.rpython.lltypesystem.lltype import * # FIXME

File pypy/rpython/lltypesystem/__init__.py

Empty file added.

File pypy/rpython/lltypesystem/exceptiondata.py

+from pypy.annotation import model as annmodel
+from pypy.rpython.lltypesystem import rclass
+from pypy.rpython.annlowlevel import annotate_lowlevel_helper
+from pypy.rpython.lltype import Array, malloc, Ptr, PyObject, pyobjectptr
+from pypy.rpython.lltype import FuncType, functionptr, Signed
+from pypy.rpython.extfunctable import standardexceptions
+from pypy.annotation.classdef import FORCE_ATTRIBUTES_INTO_CLASSES
+
+class ExceptionData:
+    """Public information for the code generators to help with exceptions."""
+    standardexceptions = standardexceptions
+
+    def __init__(self, rtyper):
+        self.make_standard_exceptions(rtyper)
+        # (NB. rclass identifies 'Exception' and 'object')
+        r_type = rclass.getclassrepr(rtyper, None)
+        r_instance = rclass.getinstancerepr(rtyper, None)
+        r_type.setup()
+        r_instance.setup()
+        self.r_exception_type  = r_type
+        self.r_exception_value = r_instance
+        self.lltype_of_exception_type  = r_type.lowleveltype
+        self.lltype_of_exception_value = r_instance.lowleveltype
+
+
+    def make_helpers(self, rtyper):
+        # create helper functions
+        self.ll_exception_match  = self.make_exception_matcher(rtyper)
+        self.ll_type_of_exc_inst = self.make_type_of_exc_inst(rtyper)
+        self.ll_pyexcclass2exc   = self.make_pyexcclass2exc(rtyper)
+        self.ll_raise_OSError    = self.make_raise_OSError(rtyper)
+
+
+    def make_standard_exceptions(self, rtyper):
+        bk = rtyper.annotator.bookkeeper
+        for cls in self.standardexceptions:
+            classdef = bk.getclassdef(cls)
+            rclass.getclassrepr(rtyper, classdef).setup()
+
+
+    def make_exception_matcher(self, rtyper):
+        # ll_exception_matcher(real_exception_vtable, match_exception_vtable)
+        s_typeptr = annmodel.SomePtr(self.lltype_of_exception_type)
+        dontcare, spec_function = annotate_lowlevel_helper(
+            rtyper.annotator, rclass.ll_issubclass, [s_typeptr, s_typeptr])
+        return spec_function
+
+
+    def make_raise_OSError(self, rtyper):
+        # ll_raise_OSError(errno)
+        def ll_raise_OSError(errno):
+            raise OSError(errno, None)
+        dontcare, spec_function = annotate_lowlevel_helper(
+            rtyper.annotator, ll_raise_OSError, [annmodel.SomeInteger()])
+        return spec_function
+
+
+    def make_type_of_exc_inst(self, rtyper):
+        # ll_type_of_exc_inst(exception_instance) -> exception_vtable
+        s_excinst = annmodel.SomePtr(self.lltype_of_exception_value)
+        dontcare, spec_function = annotate_lowlevel_helper(
+            rtyper.annotator, rclass.ll_type, [s_excinst])
+        return spec_function
+
+
+    def make_pyexcclass2exc(self, rtyper):
+        # ll_pyexcclass2exc(python_exception_class) -> exception_instance
+        table = {}
+        for clsdef in rtyper.class_reprs:
+            if (clsdef and clsdef.cls is not Exception
+                and issubclass(clsdef.cls, Exception)):
+                cls = clsdef.cls
+                if cls in self.standardexceptions and cls not in FORCE_ATTRIBUTES_INTO_CLASSES:
+                    is_standard = True
+                    assert not clsdef.attrs, (
+                        "%r should not have grown atributes" % (cls,))
+                else:
+                    is_standard = (cls.__module__ == 'exceptions'
+                                   and not clsdef.attrs)
+                if is_standard:
+                    r_inst = rclass.getinstancerepr(rtyper, clsdef)
+                    r_inst.setup()
+                    example = malloc(r_inst.lowleveltype.TO, immortal=True)
+                    example = rclass.ll_cast_to_object(example)
+                    example.typeptr = r_inst.rclass.getvtable()
+                    table[cls] = example
+                #else:
+                #    assert cls.__module__ != 'exceptions', (
+                #        "built-in exceptions should not grow attributes")
+        r_inst = rclass.getinstancerepr(rtyper, None)
+        r_inst.setup()
+        default_excinst = malloc(self.lltype_of_exception_value.TO,
+                                 immortal=True)
+        default_excinst.typeptr = r_inst.rclass.getvtable()
+
+        # build the table in order base classes first, subclasses last
+        sortedtable = []
+        def add_class(cls):
+            if cls in table:
+                for base in cls.__bases__:
+                    add_class(base)
+                sortedtable.append((cls, table[cls]))
+                del table[cls]
+        for cls in table.keys():
+            add_class(cls)
+        assert table == {}
+        #print sortedtable
+
+        A = Array(('pycls', Ptr(PyObject)),
+                  ('excinst', self.lltype_of_exception_value))
+        pycls2excinst = malloc(A, len(sortedtable), immortal=True)
+        for i in range(len(sortedtable)):
+            cls, example = sortedtable[i]
+            pycls2excinst[i].pycls   = pyobjectptr(cls)
+            pycls2excinst[i].excinst = example
+
+        FUNCTYPE = FuncType([Ptr(PyObject), Ptr(PyObject)], Signed)
+        PyErr_GivenExceptionMatches = functionptr(
+            FUNCTYPE, "PyErr_GivenExceptionMatches", external="C",
+            _callable=lambda pyobj1, pyobj2:
+                          int(issubclass(pyobj1._obj.value, pyobj2._obj.value)))
+
+        initial_value_of_i = len(pycls2excinst)-1
+
+        def ll_pyexcclass2exc(python_exception_class):
+            """Return an RPython instance of the best approximation of the
+            Python exception identified by its Python class.
+            """
+            i = initial_value_of_i
+            while i >= 0:
+                if PyErr_GivenExceptionMatches(python_exception_class,
+                                               pycls2excinst[i].pycls):
+                    return pycls2excinst[i].excinst
+                i -= 1
+            return default_excinst
+
+        s_pyobj = annmodel.SomePtr(Ptr(PyObject))
+        dontcare, spec_function = annotate_lowlevel_helper(
+            rtyper.annotator, ll_pyexcclass2exc, [s_pyobj])
+        return spec_function

File pypy/rpython/lltypesystem/lltype.py

+import weakref, operator
+import py
+from pypy.rpython.rarithmetic import r_uint
+from pypy.tool.uid import Hashable
+from pypy.tool.tls import tlsobject
+from types import NoneType
+
+log = py.log.Producer('lltype')
+
+TLS = tlsobject()
+
+def saferecursive(func, defl):
+    def safe(*args):
+        try:
+            seeing = TLS.seeing
+        except AttributeError:
+            seeing = TLS.seeing = {}
+        seeingkey = tuple([func] + [id(arg) for arg in args])
+        if seeingkey in seeing:
+            return defl
+        seeing[seeingkey] = True
+        try:
+            return func(*args)
+        finally:
+            del seeing[seeingkey]
+    return safe
+
+#safe_equal = saferecursive(operator.eq, True)
+def safe_equal(x, y):
+    # a specialized version for performance
+    try:
+        seeing = TLS.seeing_eq
+    except AttributeError:
+        seeing = TLS.seeing_eq = {}
+    seeingkey = (id(x), id(y))
+    if seeingkey in seeing:
+        return True
+    seeing[seeingkey] = True
+    try:
+        return x == y
+    finally:
+        del seeing[seeingkey]
+
+
+class frozendict(dict):
+
+    def __hash__(self):
+        items = self.items()
+        items.sort()
+        return hash(tuple(items))
+
+
+class LowLevelType(object):
+    # the following line prevents '__cached_hash' to be in the __dict__ of
+    # the instance, which is needed for __eq__() and __hash__() to work.
+    __slots__ = ['__dict__', '__cached_hash']
+
+    def __eq__(self, other):
+        return self.__class__ is other.__class__ and (
+            self is other or safe_equal(self.__dict__, other.__dict__))
+
+    def __ne__(self, other):
+        return not (self == other)
+
+    def __hash__(self):
+        # cannot use saferecursive() -- see test_lltype.test_hash().
+        # NB. the __cached_hash should neither be used nor updated
+        # if we enter with hash_level > 0, because the computed
+        # __hash__ can be different in this situation.
+        hash_level = 0
+        try:
+            hash_level = TLS.nested_hash_level
+            if hash_level == 0:
+                return self.__cached_hash
+        except AttributeError:
+            pass
+        if hash_level >= 3:
+            return 0
+        items = self.__dict__.items()
+        items.sort()
+        TLS.nested_hash_level = hash_level + 1
+        try:
+            result = hash((self.__class__,) + tuple(items))
+        finally:
+            TLS.nested_hash_level = hash_level
+        if hash_level == 0:
+            self.__cached_hash = result
+        return result
+
+    # due to this dynamic hash value, we should forbid
+    # pickling, until we have an algorithm for that.
+    # but we just provide a tag for external help.
+    __hash_is_not_constant__ = True
+
+    def __repr__(self):
+        return '<%s>' % (self,)
+
+    def __str__(self):
+        return self.__class__.__name__
+
+    def _short_name(self):
+        return str(self)
+
+    def _defl(self, parent=None, parentindex=None):
+        raise NotImplementedError
+
+    def _freeze_(self):
+        return True
+
+    def _inline_is_varsize(self, last):
+        return False
+
+    def _is_atomic(self):
+        return False
+
+    def _is_varsize(self):
+        return False
+
+
+class ContainerType(LowLevelType):
+    def _gcstatus(self):
+        return isinstance(self, GC_CONTAINER)
+
+    def _inline_is_varsize(self, last):
+        raise TypeError, "%r cannot be inlined in structure" % self
+
+
+class Struct(ContainerType):
+    def __init__(self, name, *fields):
+        self._name = self.__name__ = name
+        flds = {}
+        names = []
+        self._arrayfld = None
+        for name, typ in fields:
+            if name.startswith('_'):
+                raise NameError, ("%s: field name %r should not start with "
+                                  "an underscore" % (self._name, name,))
+            names.append(name)
+            if name in flds:
+                raise TypeError("%s: repeated field name" % self._name)
+            flds[name] = typ
+            if isinstance(typ, GC_CONTAINER):
+                if name == fields[0][0] and isinstance(self, GC_CONTAINER):
+                    pass  # can inline a GC_CONTAINER as 1st field of GcStruct
+                else:
+                    raise TypeError("%s: cannot inline GC container %r" % (
+                        self._name, typ))
+
+        # look if we have an inlined variable-sized array as the last field
+        if fields:
+            for name, typ in fields[:-1]:
+                typ._inline_is_varsize(False)
+                first = False
+            name, typ = fields[-1]
+            if typ._inline_is_varsize(True):
+                self._arrayfld = name
+        self._flds = frozendict(flds)
+        self._names = tuple(names)
+
+    def _first_struct(self):
+        if self._names:
+            first = self._names[0]
+            FIRSTTYPE = self._flds[first]
+            if isinstance(FIRSTTYPE, Struct) and self._gcstatus() == FIRSTTYPE._gcstatus():
+                return first, FIRSTTYPE
+        return None, None
+
+    def _inline_is_varsize(self, last):
+        if self._arrayfld:
+            raise TypeError("cannot inline a var-sized struct "
+                            "inside another container")
+        return False
+
+    def _is_atomic(self):
+        for typ in self._flds.values():
+            if not typ._is_atomic():
+                return False
+        return True
+
+    def _is_varsize(self):
+        return self._arrayfld is not None
+
+    def __getattr__(self, name):
+        try:
+            return self._flds[name]
+        except KeyError:
+            raise AttributeError, 'struct %s has no field %r' % (self._name,
+                                                                 name)
+
+    def _names_without_voids(self):
+        names_without_voids = [name for name in self._names if self._flds[name] is not Void]
+        return names_without_voids
+    
+    def _str_fields_without_voids(self):
+        return ', '.join(['%s: %s' % (name, self._flds[name])
+                          for name in self._names_without_voids(False)])
+    _str_fields_without_voids = saferecursive(_str_fields_without_voids, '...')
+
+    def _str_without_voids(self):
+        return "%s %s { %s }" % (self.__class__.__name__,
+                                 self._name, self._str_fields_without_voids())
+
+    def _str_fields(self):
+        return ', '.join(['%s: %s' % (name, self._flds[name])
+                          for name in self._names])
+    _str_fields = saferecursive(_str_fields, '...')
+
+    def __str__(self):
+        return "%s %s { %s }" % (self.__class__.__name__,
+                                 self._name, self._str_fields())
+
+    def _short_name(self):
+        return "%s %s" % (self.__class__.__name__, self._name)
+
+    def _defl(self, parent=None, parentindex=None):
+        return _struct(self, parent=parent, parentindex=parentindex)
+
+    def _container_example(self):
+        if self._arrayfld is None:
+            n = None
+        else:
+            n = 1
+        return _struct(self, n)
+
+class GcStruct(Struct):
+    _runtime_type_info = None
+
+    def _attach_runtime_type_info_funcptr(self, funcptr):
+        if self._runtime_type_info is None:
+            self._runtime_type_info = opaqueptr(RuntimeTypeInfo, name=self._name, about=self)._obj
+        if funcptr is not None:
+            T = typeOf(funcptr)
+            if (not isinstance(T, Ptr) or
+                not isinstance(T.TO, FuncType) or
+                len(T.TO.ARGS) != 1 or
+                T.TO.RESULT != Ptr(RuntimeTypeInfo) or
+                castable(T.TO.ARGS[0], Ptr(self)) < 0):
+                raise TypeError("expected a runtime type info function "
+                                "implementation, got: %s" % funcptr)
+            self._runtime_type_info.query_funcptr = funcptr
+
+class Array(ContainerType):
+    __name__ = 'array'
+    _anonym_struct = False
+    
+    def __init__(self, *fields):
+        if len(fields) == 1 and isinstance(fields[0], LowLevelType):
+            self.OF = fields[0]
+        else:
+            self.OF = Struct("<arrayitem>", *fields)
+            self._anonym_struct = True
+        if isinstance(self.OF, GcStruct):
+            raise TypeError("cannot have a GC structure as array item type")
+        self.OF._inline_is_varsize(False)
+
+    def _inline_is_varsize(self, last):
+        if not last:
+            raise TypeError("cannot inline an array in another container"
+                            " unless as the last field of a structure")
+        return True
+
+    def _is_atomic(self):
+        return self.OF._is_atomic()
+
+    def _is_varsize(self):
+        return True
+
+    def _str_fields(self):
+        if isinstance(self.OF, Struct):
+            of = self.OF
+            if self._anonym_struct:
+                return "{ %s }" % of._str_fields()
+            else:
+                return "%s { %s }" % (of._name, of._str_fields())
+        else:
+            return self.OF
+
+    def __str__(self):
+        return "%s of %s " % (self.__class__.__name__,
+                               self._str_fields(),)
+
+    def _short_name(self):
+        return "%s %s" % (self.__class__.__name__,
+                          self.OF._short_name(),)
+
+    def _container_example(self):
+        return _array(self, 1)
+
+class GcArray(Array):
+    def _inline_is_varsize(self, last):
+        raise TypeError("cannot inline a GC array inside a structure")
+
+class FuncType(ContainerType):
+    __name__ = 'func'
+    def __init__(self, args, result):
+        for arg in args:
+            assert isinstance(arg, LowLevelType)
+            if isinstance(arg, ContainerType):
+                raise TypeError, "function arguments can only be primitives or pointers"
+        self.ARGS = tuple(args)
+        assert isinstance(result, LowLevelType)
+        if isinstance(result, ContainerType):
+            raise TypeError, "function result can only be primitive or pointer"
+        self.RESULT = result
+
+    def __str__(self):
+        args = ', '.join(map(str, self.ARGS))
+        return "Func ( %s ) -> %s" % (args, self.RESULT)
+
+    def _short_name(self):        
+        args = ', '.join([ARG._short_name() for ARG in self.ARGS])
+        return "Func(%s)->%s" % (args, self.RESULT._short_name)        
+        
+    def _container_example(self):
+        def ex(*args):
+            return self.RESULT._defl()
+        return _func(self, _callable=ex)
+
+    def _trueargs(self):
+        return [arg for arg in self.ARGS if arg is not Void]
+
+
+class OpaqueType(ContainerType):
+    
+    def __init__(self, tag):
+        self.tag = tag
+        self.__name__ = tag
+
+    def __str__(self):
+        return "%s (opaque)" % self.tag
+
+    def _inline_is_varsize(self, last):
+        return False    # OpaqueType can be inlined
+
+    def _container_example(self):
+        return _opaque(self)
+
+    def _defl(self, parent=None, parentindex=None):
+        return _opaque(self, parent=parent, parentindex=parentindex)
+
+RuntimeTypeInfo = OpaqueType("RuntimeTypeInfo")
+
+class PyObjectType(ContainerType):
+    __name__ = 'PyObject'
+    def __str__(self):
+        return "PyObject"
+PyObject = PyObjectType()
+
+class ForwardReference(ContainerType):
+    def become(self, realcontainertype):
+        if not isinstance(realcontainertype, ContainerType):
+            raise TypeError("ForwardReference can only be to a container, "
+                            "not %r" % (realcontainertype,))
+        self.__class__ = realcontainertype.__class__
+        self.__dict__ = realcontainertype.__dict__
+
+    def __hash__(self):
+        raise TypeError("%r object is not hashable" % self.__class__.__name__)
+
+class GcForwardReference(ForwardReference):
+    def become(self, realcontainertype):
+        if not isinstance(realcontainertype, GC_CONTAINER):
+            raise TypeError("GcForwardReference can only be to GcStruct or "
+                            "GcArray, not %r" % (realcontainertype,))
+        self.__class__ = realcontainertype.__class__
+        self.__dict__ = realcontainertype.__dict__
+
+GC_CONTAINER = (GcStruct, GcArray, PyObjectType, GcForwardReference)
+
+
+class Primitive(LowLevelType):
+    def __init__(self, name, default):
+        self._name = self.__name__ = name
+        self._default = default
+
+    def __str__(self):
+        return self._name
+
+    def _defl(self, parent=None, parentindex=None):
+        return self._default
+
+    def _is_atomic(self):
+        return True
+
+    _example = _defl
+
+
+Signed   = Primitive("Signed", 0)
+Unsigned = Primitive("Unsigned", r_uint(0))
+Float    = Primitive("Float", 0.0)
+Char     = Primitive("Char", '\x00')
+Bool     = Primitive("Bool", False)
+Void     = Primitive("Void", None)
+UniChar  = Primitive("UniChar", u'\x00')
+
+