/rpython/translator/c/node.py
Python | 1068 lines | 1045 code | 8 blank | 15 comment | 9 complexity | 45a5caafc9248ad7e55918f9d47a8018 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
- from rpython.rtyper.lltypesystem.lltype import (Struct, Array, FixedSizeArray,
- FuncType, typeOf, GcStruct, GcArray, RttiStruct, ContainerType, parentlink,
- Void, OpaqueType, Float, RuntimeTypeInfo, getRuntimeTypeInfo, Char,
- _subarray)
- from rpython.rtyper.lltypesystem import llmemory, llgroup
- from rpython.translator.c.funcgen import make_funcgen
- from rpython.translator.c.support import USESLOTS # set to False if necessary while refactoring
- from rpython.translator.c.support import cdecl, forward_cdecl, somelettersfrom
- from rpython.translator.c.support import c_char_array_constant, barebonearray
- from rpython.translator.c.primitive import PrimitiveType, name_signed
- from rpython.rlib import exports
- from rpython.rlib.rfloat import isfinite, isinf
- def needs_gcheader(T):
- if not isinstance(T, ContainerType):
- return False
- if T._gckind != 'gc':
- return False
- if isinstance(T, GcStruct):
- if T._first_struct() != (None, None):
- return False # gcheader already in the first field
- return True
- class Node(object):
- __slots__ = ("db", )
- def __init__(self, db):
- self.db = db
- class NodeWithDependencies(Node):
- __slots__ = ("dependencies", )
- def __init__(self, db):
- Node.__init__(self, db)
- self.dependencies = set()
- class StructDefNode(NodeWithDependencies):
- typetag = 'struct'
- extra_union_for_varlength = True
- def __init__(self, db, STRUCT, varlength=None):
- NodeWithDependencies.__init__(self, db)
- self.STRUCT = STRUCT
- self.LLTYPE = STRUCT
- self.varlength = varlength
- if varlength is None:
- basename = STRUCT._name
- with_number = True
- else:
- basename = db.gettypedefnode(STRUCT).barename
- basename = '%s_len%d' % (basename, varlength)
- with_number = False
- if STRUCT._hints.get('union'):
- self.typetag = 'union'
- assert STRUCT._gckind == 'raw' # not supported: "GcUnion"
- if STRUCT._hints.get('typedef'):
- self.typetag = ''
- assert STRUCT._hints.get('external')
- if self.STRUCT._hints.get('external'): # XXX hack
- self.forward_decl = None
- if STRUCT._hints.get('c_name'):
- self.barename = self.name = STRUCT._hints['c_name']
- self.c_struct_field_name = self.verbatim_field_name
- else:
- (self.barename,
- self.name) = db.namespace.uniquename(basename,
- with_number=with_number,
- bare=True)
- self.prefix = somelettersfrom(STRUCT._name) + '_'
- #
- self.fieldnames = STRUCT._names
- if STRUCT._hints.get('typeptr', False):
- if db.gcpolicy.need_no_typeptr():
- assert self.fieldnames == ('typeptr',)
- self.fieldnames = ()
- #
- self.fulltypename = '%s %s @' % (self.typetag, self.name)
- def setup(self):
- # this computes self.fields
- if self.STRUCT._hints.get('external'): # XXX hack
- self.fields = None # external definition only
- return
- self.fields = []
- db = self.db
- STRUCT = self.STRUCT
- if self.varlength is not None:
- self.normalizedtypename = db.gettype(STRUCT, who_asks=self)
- if needs_gcheader(self.STRUCT):
- HDR = db.gcpolicy.struct_gcheader_definition(self)
- if HDR is not None:
- gc_field = ("_gcheader", db.gettype(HDR, who_asks=self))
- self.fields.append(gc_field)
- for name in self.fieldnames:
- T = self.c_struct_field_type(name)
- if name == STRUCT._arrayfld:
- typename = db.gettype(T, varlength=self.varlength,
- who_asks=self)
- else:
- typename = db.gettype(T, who_asks=self)
- self.fields.append((self.c_struct_field_name(name), typename))
- self.computegcinfo(self.db.gcpolicy)
- def computegcinfo(self, gcpolicy):
- # let the gcpolicy do its own setup
- self.gcinfo = None # unless overwritten below
- rtti = None
- STRUCT = self.STRUCT
- if isinstance(STRUCT, RttiStruct):
- try:
- rtti = getRuntimeTypeInfo(STRUCT)
- except ValueError:
- pass
- if self.varlength is None:
- gcpolicy.struct_setup(self, rtti)
- return self.gcinfo
- def gettype(self):
- return self.fulltypename
- def c_struct_field_name(self, name):
- # occasionally overridden in __init__():
- # self.c_struct_field_name = self.verbatim_field_name
- return self.prefix + name
- def verbatim_field_name(self, name):
- assert name.startswith('c_') # produced in this way by rffi
- return name[2:]
- def c_struct_field_type(self, name):
- return self.STRUCT._flds[name]
- def access_expr(self, baseexpr, fldname):
- fldname = self.c_struct_field_name(fldname)
- return '%s.%s' % (baseexpr, fldname)
- def ptr_access_expr(self, baseexpr, fldname, baseexpr_is_const=False):
- fldname = self.c_struct_field_name(fldname)
- if baseexpr_is_const:
- return '%s->%s' % (baseexpr, fldname)
- return 'RPyField(%s, %s)' % (baseexpr, fldname)
- def definition(self):
- if self.fields is None: # external definition only
- return
- yield '%s %s {' % (self.typetag, self.name)
- is_empty = True
- for name, typename in self.fields:
- line = '%s;' % cdecl(typename, name)
- if typename == PrimitiveType[Void]:
- line = '/* %s */' % line
- else:
- if is_empty and typename.endswith('[RPY_VARLENGTH]'):
- yield '\tRPY_DUMMY_VARLENGTH'
- is_empty = False
- yield '\t' + line
- if is_empty:
- yield '\t' + 'char _dummy; /* this struct is empty */'
- yield '};'
- if self.varlength is not None:
- assert self.typetag == 'struct'
- yield 'union %su {' % self.name
- yield ' struct %s a;' % self.name
- yield ' %s;' % cdecl(self.normalizedtypename, 'b')
- yield '};'
- def visitor_lines(self, prefix, on_field):
- for name in self.fieldnames:
- FIELD_T = self.c_struct_field_type(name)
- cname = self.c_struct_field_name(name)
- for line in on_field('%s.%s' % (prefix, cname),
- FIELD_T):
- yield line
- def deflength(varlength):
- if varlength is None:
- return 'RPY_VARLENGTH'
- elif varlength == 0:
- return 'RPY_LENGTH0'
- else:
- return varlength
- class ArrayDefNode(NodeWithDependencies):
- typetag = 'struct'
- extra_union_for_varlength = True
- def __init__(self, db, ARRAY, varlength=None):
- NodeWithDependencies.__init__(self, db)
- self.ARRAY = ARRAY
- self.LLTYPE = ARRAY
- self.gcfields = []
- self.varlength = varlength
- if varlength is None:
- basename = 'array'
- with_number = True
- else:
- basename = db.gettypedefnode(ARRAY).barename
- basename = '%s_len%d' % (basename, varlength)
- with_number = False
- (self.barename,
- self.name) = db.namespace.uniquename(basename, with_number=with_number,
- bare=True)
- self.fulltypename = '%s %s @' % (self.typetag, self.name)
- self.fullptrtypename = '%s %s *@' % (self.typetag, self.name)
- def setup(self):
- if hasattr(self, 'itemtypename'):
- return # setup() was already called, likely by __init__
- db = self.db
- ARRAY = self.ARRAY
- self.computegcinfo(db.gcpolicy)
- if self.varlength is not None:
- self.normalizedtypename = db.gettype(ARRAY, who_asks=self)
- if needs_gcheader(ARRAY):
- HDR = db.gcpolicy.array_gcheader_definition(self)
- if HDR is not None:
- gc_field = ("_gcheader", db.gettype(HDR, who_asks=self))
- self.gcfields.append(gc_field)
- self.itemtypename = db.gettype(ARRAY.OF, who_asks=self)
- def computegcinfo(self, gcpolicy):
- # let the gcpolicy do its own setup
- self.gcinfo = None # unless overwritten below
- if self.varlength is None:
- gcpolicy.array_setup(self)
- return self.gcinfo
- def gettype(self):
- return self.fulltypename
- def getptrtype(self):
- return self.fullptrtypename
- def access_expr(self, baseexpr, index):
- return '%s.items[%s]' % (baseexpr, index)
- access_expr_varindex = access_expr
- def ptr_access_expr(self, baseexpr, index, dummy=False):
- assert 0 <= index <= sys.maxint, "invalid constant index %r" % (index,)
- return self.itemindex_access_expr(baseexpr, index)
- def itemindex_access_expr(self, baseexpr, indexexpr):
- if self.ARRAY._hints.get('nolength', False):
- return 'RPyNLenItem(%s, %s)' % (baseexpr, indexexpr)
- else:
- return 'RPyItem(%s, %s)' % (baseexpr, indexexpr)
- def definition(self):
- yield 'struct %s {' % self.name
- for fname, typename in self.gcfields:
- yield '\t' + cdecl(typename, fname) + ';'
- if not self.ARRAY._hints.get('nolength', False):
- yield '\tlong length;'
- varlength = self.varlength
- if varlength is not None:
- varlength += self.ARRAY._hints.get('extra_item_after_alloc', 0)
- line = '%s;' % cdecl(self.itemtypename,
- 'items[%s]' % deflength(varlength))
- if self.ARRAY.OF is Void: # strange
- line = '/* array of void */'
- if self.ARRAY._hints.get('nolength', False):
- line = 'char _dummy; ' + line
- yield '\t' + line
- yield '};'
- if self.varlength is not None:
- yield 'union %su {' % self.name
- yield ' struct %s a;' % self.name
- yield ' %s;' % cdecl(self.normalizedtypename, 'b')
- yield '};'
- def visitor_lines(self, prefix, on_item):
- assert self.varlength is None
- ARRAY = self.ARRAY
- # we need a unique name for this C variable, or at least one that does
- # not collide with the expression in 'prefix'
- i = 0
- varname = 'p0'
- while prefix.find(varname) >= 0:
- i += 1
- varname = 'p%d' % i
- body = list(on_item('(*%s)' % varname, ARRAY.OF))
- if body:
- yield '{'
- yield '\t%s = %s.items;' % (cdecl(self.itemtypename, '*' + varname),
- prefix)
- yield '\t%s = %s + %s.length;' % (cdecl(self.itemtypename,
- '*%s_end' % varname),
- varname,
- prefix)
- yield '\twhile (%s != %s_end) {' % (varname, varname)
- for line in body:
- yield '\t\t' + line
- yield '\t\t%s++;' % varname
- yield '\t}'
- yield '}'
- class BareBoneArrayDefNode(NodeWithDependencies):
- """For 'simple' array types which don't need a length nor GC headers.
- Implemented directly as a C array instead of a struct with an items field.
- rffi kind of expects such arrays to be 'bare' C arrays.
- """
- gcinfo = None
- name = None
- forward_decl = None
- extra_union_for_varlength = False
- def __init__(self, db, ARRAY, varlength=None):
- NodeWithDependencies.__init__(self, db)
- self.ARRAY = ARRAY
- self.LLTYPE = ARRAY
- self.varlength = varlength
- contained_type = ARRAY.OF
- # There is no such thing as an array of voids:
- # we use a an array of chars instead; only the pointer can be void*.
- self.itemtypename = db.gettype(contained_type, who_asks=self)
- self.fulltypename = self.itemtypename.replace('@', '(@)[%s]' %
- deflength(varlength))
- if ARRAY._hints.get("render_as_void"):
- self.fullptrtypename = 'void *@'
- else:
- self.fullptrtypename = self.itemtypename.replace('@', '*@')
- if ARRAY._hints.get("render_as_const"):
- self.fullptrtypename = 'const ' + self.fullptrtypename
- def setup(self):
- """Array loops are forbidden by ForwardReference.become() because
- there is no way to declare them in C."""
- def gettype(self):
- return self.fulltypename
- def getptrtype(self):
- return self.fullptrtypename
- def access_expr(self, baseexpr, index):
- return '%s[%d]' % (baseexpr, index)
- access_expr_varindex = access_expr
- def ptr_access_expr(self, baseexpr, index, dummy=False):
- assert 0 <= index <= sys.maxint, "invalid constant index %r" % (index,)
- return self.itemindex_access_expr(baseexpr, index)
- def itemindex_access_expr(self, baseexpr, indexexpr):
- if self.ARRAY._hints.get("render_as_void"):
- return 'RPyBareItem((char*)%s, %s)' % (baseexpr, indexexpr)
- else:
- return 'RPyBareItem(%s, %s)' % (baseexpr, indexexpr)
- def definition(self):
- return [] # no declaration is needed
- def visitor_lines(self, prefix, on_item):
- raise Exception("cannot visit C arrays - don't know the length")
- class FixedSizeArrayDefNode(NodeWithDependencies):
- gcinfo = None
- name = None
- typetag = 'struct'
- extra_union_for_varlength = False
- def __init__(self, db, FIXEDARRAY):
- NodeWithDependencies.__init__(self, db)
- self.FIXEDARRAY = FIXEDARRAY
- self.LLTYPE = FIXEDARRAY
- self.itemtypename = db.gettype(FIXEDARRAY.OF, who_asks=self)
- self.fulltypename = self.itemtypename.replace('@', '(@)[%d]' %
- FIXEDARRAY.length)
- self.fullptrtypename = self.itemtypename.replace('@', '*@')
- def setup(self):
- """Loops are forbidden by ForwardReference.become() because
- there is no way to declare them in C."""
- def gettype(self):
- return self.fulltypename
- def getptrtype(self):
- return self.fullptrtypename
- def access_expr(self, baseexpr, index, dummy=False):
- if not isinstance(index, int):
- assert index.startswith('item')
- index = int(index[4:])
- if not (0 <= index < self.FIXEDARRAY.length):
- raise IndexError("refusing to generate a statically out-of-bounds"
- " array indexing")
- return '%s[%d]' % (baseexpr, index)
- ptr_access_expr = access_expr
- def access_expr_varindex(self, baseexpr, index):
- return '%s[%s]' % (baseexpr, index)
- def itemindex_access_expr(self, baseexpr, indexexpr):
- return 'RPyFxItem(%s, %s, %d)' % (baseexpr, indexexpr,
- self.FIXEDARRAY.length)
- def definition(self):
- return [] # no declaration is needed
- def visitor_lines(self, prefix, on_item):
- FIXEDARRAY = self.FIXEDARRAY
- # we need a unique name for this C variable, or at least one that does
- # not collide with the expression in 'prefix'
- i = 0
- varname = 'p0'
- while prefix.find(varname) >= 0:
- i += 1
- varname = 'p%d' % i
- body = list(on_item('(*%s)' % varname, FIXEDARRAY.OF))
- if body:
- yield '{'
- yield '\t%s = %s;' % (cdecl(self.itemtypename, '*' + varname),
- prefix)
- yield '\t%s = %s + %d;' % (cdecl(self.itemtypename,
- '*%s_end' % varname),
- varname,
- FIXEDARRAY.length)
- yield '\twhile (%s != %s_end) {' % (varname, varname)
- for line in body:
- yield '\t\t' + line
- yield '\t\t%s++;' % varname
- yield '\t}'
- yield '}'
- class ExtTypeOpaqueDefNode(NodeWithDependencies):
- """For OpaqueTypes created with the hint render_structure."""
- typetag = 'struct'
- def __init__(self, db, T):
- NodeWithDependencies.__init__(self, db)
- self.T = T
- self.name = 'RPyOpaque_%s' % (T.tag,)
- def setup(self):
- pass
- def definition(self):
- return []
- # ____________________________________________________________
- class ContainerNode(Node):
- if USESLOTS: # keep the number of slots down!
- __slots__ = """db obj
- typename implementationtypename
- name
- _funccodegen_owner
- globalcontainer""".split()
- eci_name = '_compilation_info'
- def __init__(self, db, T, obj):
- Node.__init__(self, db)
- self.obj = obj
- self.typename = db.gettype(T) #, who_asks=self)
- self.implementationtypename = db.gettype(
- T, varlength=self.getvarlength())
- parent, parentindex = parentlink(obj)
- if obj in exports.EXPORTS_obj2name:
- self.name = exports.EXPORTS_obj2name[obj]
- self.globalcontainer = 2 # meh
- elif parent is None:
- self.name = db.namespace.uniquename('g_' + self.basename())
- self.globalcontainer = True
- else:
- self.globalcontainer = False
- parentnode = db.getcontainernode(parent)
- defnode = db.gettypedefnode(parentnode.getTYPE())
- self.name = defnode.access_expr(parentnode.name, parentindex)
- if self.typename != self.implementationtypename:
- if db.gettypedefnode(T).extra_union_for_varlength:
- self.name += '.b'
- self._funccodegen_owner = None
- def getptrname(self):
- return '(&%s)' % self.name
- def getTYPE(self):
- return typeOf(self.obj)
- def is_thread_local(self):
- T = self.getTYPE()
- return hasattr(T, "_hints") and T._hints.get('thread_local')
- def is_exported(self):
- return self.globalcontainer == 2 # meh
- def compilation_info(self):
- return getattr(self.obj, self.eci_name, None)
- def get_declaration(self):
- if self.name[-2:] == '.b':
- # xxx fish fish
- assert self.implementationtypename.startswith('struct ')
- assert self.implementationtypename.endswith(' @')
- uniontypename = 'union %su @' % self.implementationtypename[7:-2]
- return uniontypename, self.name[:-2]
- else:
- return self.implementationtypename, self.name
- def forward_declaration(self):
- if llgroup.member_of_group(self.obj):
- return
- type, name = self.get_declaration()
- yield '%s;' % (
- forward_cdecl(type, name, self.db.standalone,
- is_thread_local=self.is_thread_local(),
- is_exported=self.is_exported()))
- def implementation(self):
- if llgroup.member_of_group(self.obj):
- return []
- lines = list(self.initializationexpr())
- type, name = self.get_declaration()
- if name != self.name and len(lines) < 2:
- # a union with length 0
- lines[0] = cdecl(type, name, self.is_thread_local())
- else:
- if name != self.name:
- lines[0] = '{ ' + lines[0] # extra braces around the 'a' part
- lines[-1] += ' }' # of the union
- lines[0] = '%s = %s' % (
- cdecl(type, name, self.is_thread_local()),
- lines[0])
- lines[-1] += ';'
- return lines
- def startupcode(self):
- return []
- def getvarlength(self):
- return None
- assert not USESLOTS or '__dict__' not in dir(ContainerNode)
- class StructNode(ContainerNode):
- nodekind = 'struct'
- if USESLOTS:
- __slots__ = ('gc_init',)
- def __init__(self, db, T, obj):
- ContainerNode.__init__(self, db, T, obj)
- if needs_gcheader(T):
- gct = self.db.gctransformer
- if gct is not None:
- self.gc_init = gct.gcheader_initdata(self.obj)
- else:
- self.gc_init = None
- def basename(self):
- T = self.getTYPE()
- return T._name
- def enum_dependencies(self):
- T = self.getTYPE()
- for name in T._names:
- yield getattr(self.obj, name)
- def getvarlength(self):
- T = self.getTYPE()
- if T._arrayfld is None:
- return None
- else:
- array = getattr(self.obj, T._arrayfld)
- return len(array.items)
- def initializationexpr(self, decoration=''):
- T = self.getTYPE()
- is_empty = True
- defnode = self.db.gettypedefnode(T)
- data = []
- if needs_gcheader(T):
- data.append(('gcheader', self.gc_init))
- for name in defnode.fieldnames:
- data.append((name, getattr(self.obj, name)))
- # Reasonably, you should only initialise one of the fields of a union
- # in C. This is possible with the syntax '.fieldname value' or
- # '.fieldname = value'. But here we don't know which of the
- # fields need initialization, so XXX we pick the first one
- # arbitrarily.
- if hasattr(T, "_hints") and T._hints.get('union'):
- data = data[0:1]
- if 'get_padding_drop' in T._hints:
- d = {}
- for name, _ in data:
- T1 = defnode.c_struct_field_type(name)
- typename = self.db.gettype(T1)
- d[name] = cdecl(typename, '')
- padding_drop = T._hints['get_padding_drop'](d)
- else:
- padding_drop = []
- type, name = self.get_declaration()
- if name != self.name and self.getvarlength() < 1 and len(data) < 2:
- # an empty union
- yield ''
- return
- yield '{'
- for name, value in data:
- if name in padding_drop:
- continue
- c_expr = defnode.access_expr(self.name, name)
- lines = generic_initializationexpr(self.db, value, c_expr,
- decoration + name)
- for line in lines:
- yield '\t' + line
- if not lines[0].startswith('/*'):
- is_empty = False
- if is_empty:
- yield '\t%s' % '0,'
- yield '}'
- assert not USESLOTS or '__dict__' not in dir(StructNode)
- class GcStructNodeWithHash(StructNode):
- # for the outermost level of nested structures, if it has a _hash_cache_.
- nodekind = 'struct'
- if USESLOTS:
- __slots__ = ()
- def get_hash_typename(self):
- return 'struct _hashT_%s @' % self.name
- def forward_declaration(self):
- T = self.getTYPE()
- assert self.typename == self.implementationtypename # no array part
- hash_typename = self.get_hash_typename()
- hash_offset = self.db.gctransformer.get_hash_offset(T)
- yield '%s {' % cdecl(hash_typename, '')
- yield '\tunion {'
- yield '\t\t%s;' % cdecl(self.implementationtypename, 'head')
- yield '\t\tchar pad[%s];' % name_signed(hash_offset, self.db)
- yield '\t} u;'
- yield '\tlong hash;'
- yield '};'
- yield '%s;' % (
- forward_cdecl(hash_typename, '_hash_' + self.name,
- self.db.standalone, self.is_thread_local()),)
- yield '#define %s _hash_%s.u.head' % (self.name, self.name)
- def implementation(self):
- hash_typename = self.get_hash_typename()
- hash = self.db.gctransformer.get_prebuilt_hash(self.obj)
- assert hash is not None
- lines = list(self.initializationexpr())
- lines.insert(0, '%s = { {' % (
- cdecl(hash_typename, '_hash_' + self.name,
- self.is_thread_local()),))
- lines.append('}, %s /* hash */ };' % name_signed(hash, self.db))
- return lines
- def gcstructnode_factory(db, T, obj):
- if (db.gctransformer and
- db.gctransformer.get_prebuilt_hash(obj) is not None):
- cls = GcStructNodeWithHash
- else:
- cls = StructNode
- return cls(db, T, obj)
- class ArrayNode(ContainerNode):
- nodekind = 'array'
- if USESLOTS:
- __slots__ = ('gc_init',)
- def __init__(self, db, T, obj):
- ContainerNode.__init__(self, db, T, obj)
- if needs_gcheader(T):
- gct = self.db.gctransformer
- if gct is not None:
- self.gc_init = gct.gcheader_initdata(self.obj)
- else:
- self.gc_init = None
- def getptrname(self):
- if barebonearray(self.getTYPE()):
- return self.name
- return ContainerNode.getptrname(self)
- def basename(self):
- return 'array'
- def enum_dependencies(self):
- return self.obj.items
- def getvarlength(self):
- return len(self.obj.items)
- def initializationexpr(self, decoration=''):
- T = self.getTYPE()
- yield '{'
- if needs_gcheader(T):
- lines = generic_initializationexpr(self.db, self.gc_init, 'gcheader',
- '%sgcheader' % (decoration,))
- for line in lines:
- yield line
- if T._hints.get('nolength', False):
- length = ''
- else:
- length = '%d, ' % len(self.obj.items)
- if T.OF is Void or len(self.obj.items) == 0:
- yield '\t%s' % length.rstrip(', ')
- yield '}'
- elif T.OF == Char:
- if len(self.obj.items) and self.obj.items[0] is None:
- s = ''.join([self.obj.getitem(i) for i in range(len(self.obj.items))])
- else:
- s = ''.join(self.obj.items)
- array_constant = c_char_array_constant(s)
- if array_constant.startswith('{') and barebonearray(T):
- assert array_constant.endswith('}')
- array_constant = array_constant[1:-1].strip()
- yield '\t%s%s' % (length, array_constant)
- yield '}'
- else:
- barebone = barebonearray(T)
- if not barebone:
- yield '\t%s{' % length
- for j in range(len(self.obj.items)):
- value = self.obj.items[j]
- basename = self.name
- if basename.endswith('.b'):
- basename = basename[:-2] + '.a'
- lines = generic_initializationexpr(self.db, value,
- '%s.items[%d]' % (basename, j),
- '%s%d' % (decoration, j))
- for line in lines:
- yield '\t' + line
- if not barebone:
- yield '} }'
- else:
- yield '}'
- assert not USESLOTS or '__dict__' not in dir(ArrayNode)
- class FixedSizeArrayNode(ContainerNode):
- nodekind = 'array'
- if USESLOTS:
- __slots__ = ()
- def getptrname(self):
- if not isinstance(self.obj, _subarray): # XXX hackish
- return self.name
- return ContainerNode.getptrname(self)
- def basename(self):
- T = self.getTYPE()
- return T._name
- def enum_dependencies(self):
- for i in range(self.obj.getlength()):
- yield self.obj.getitem(i)
- def getvarlength(self):
- return None # not variable-sized!
- def initializationexpr(self, decoration=''):
- T = self.getTYPE()
- assert self.typename == self.implementationtypename # not var-sized
- yield '{'
- # _names == ['item0', 'item1', ...]
- for j, name in enumerate(T._names):
- value = getattr(self.obj, name)
- lines = generic_initializationexpr(self.db, value,
- '%s[%d]' % (self.name, j),
- '%s%d' % (decoration, j))
- for line in lines:
- yield '\t' + line
- yield '}'
- def generic_initializationexpr(db, value, access_expr, decoration):
- if isinstance(typeOf(value), ContainerType):
- node = db.getcontainernode(value)
- lines = list(node.initializationexpr(decoration+'.'))
- lines[-1] += ','
- return lines
- else:
- comma = ','
- if typeOf(value) == Float and not isfinite(value):
- db.late_initializations.append(('%s' % access_expr, db.get(value)))
- if isinf(value):
- name = '-+'[value > 0] + 'inf'
- else:
- name = 'NaN'
- expr = '0.0 /* patched later with %s */' % (name,)
- else:
- expr = db.get(value)
- if typeOf(value) is Void:
- comma = ''
- expr += comma
- i = expr.find('\n')
- if i < 0:
- i = len(expr)
- expr = '%s\t/* %s */%s' % (expr[:i], decoration, expr[i:])
- return expr.split('\n')
- # ____________________________________________________________
- class FuncNodeBase(ContainerNode):
- nodekind = 'func'
- eci_name = 'compilation_info'
- # there not so many node of this kind, slots should not
- # be necessary
- def __init__(self, db, T, obj, ptrname):
- Node.__init__(self, db)
- self.globalcontainer = True
- self.T = T
- self.obj = obj
- self.name = ptrname
- self.typename = db.gettype(T) #, who_asks=self)
- def getptrname(self):
- return self.name
- def basename(self):
- return self.obj._name
- class FuncNode(FuncNodeBase):
- def __init__(self, db, T, obj, ptrname):
- FuncNodeBase.__init__(self, db, T, obj, ptrname)
- exception_policy = getattr(obj, 'exception_policy', None)
- self.funcgen = make_funcgen(obj.graph, db, exception_policy, ptrname)
- argnames = self.funcgen.argnames()
- self.implementationtypename = db.gettype(T, argnames=argnames)
- self._funccodegen_owner = self.funcgen
- def enum_dependencies(self):
- return self.funcgen.allconstantvalues()
- def forward_declaration(self):
- callable = getattr(self.obj, '_callable', None)
- is_exported = getattr(callable, 'exported_symbol', False)
- yield '%s;' % (
- forward_cdecl(self.implementationtypename,
- self.name, self.db.standalone, is_exported=is_exported))
- def graphs_to_patch(self):
- for i in self.funcgen.graphs_to_patch():
- yield i
- def implementation(self):
- funcgen = self.funcgen
- funcgen.implementation_begin()
- # recompute implementationtypename as the argnames may have changed
- argnames = funcgen.argnames()
- implementationtypename = self.db.gettype(self.T, argnames=argnames)
- yield '%s {' % cdecl(implementationtypename, self.name)
- #
- # declare the local variables
- #
- localnames = list(funcgen.cfunction_declarations())
- lengths = [len(a) for a in localnames]
- lengths.append(9999)
- start = 0
- while start < len(localnames):
- # pack the local declarations over as few lines as possible
- total = lengths[start] + 8
- end = start + 1
- while total + lengths[end] < 77:
- total += lengths[end] + 1
- end += 1
- yield '\t' + ' '.join(localnames[start:end])
- start = end
- #
- # generate the body itself
- #
- bodyiter = funcgen.cfunction_body()
- for line in bodyiter:
- # performs some formatting on the generated body:
- # indent normal lines with tabs; indent labels less than the rest
- if line.endswith(':'):
- if line.startswith('err'):
- try:
- nextline = bodyiter.next()
- except StopIteration:
- nextline = ''
- # merge this 'err:' label with the following line
- line = '\t%s\t%s' % (line, nextline)
- else:
- line = ' ' + line
- elif line:
- line = '\t' + line
- yield line
- yield '}'
- del bodyiter
- funcgen.implementation_end()
- class ExternalFuncNode(FuncNodeBase):
- def __init__(self, db, T, obj, ptrname):
- FuncNodeBase.__init__(self, db, T, obj, ptrname)
- self._funccodegen_owner = None
- def enum_dependencies(self):
- return []
- def forward_declaration(self):
- return []
- def implementation(self):
- return []
- def new_funcnode(db, T, obj, forcename=None):
- from rpython.rtyper.rtyper import llinterp_backend
- if db.sandbox:
- if (getattr(obj, 'external', None) is not None and
- not obj._safe_not_sandboxed):
- from rpython.translator.sandbox import rsandbox
- obj.__dict__['graph'] = rsandbox.get_sandbox_stub(
- obj, db.translator.rtyper)
- obj.__dict__.pop('_safe_not_sandboxed', None)
- obj.__dict__.pop('external', None)
- if forcename:
- name = forcename
- else:
- name = _select_name(db, obj)
- if hasattr(obj, 'graph'):
- return FuncNode(db, T, obj, name)
- elif getattr(obj, 'external', None) is not None:
- assert obj.external == 'C'
- if db.sandbox:
- assert obj._safe_not_sandboxed
- return ExternalFuncNode(db, T, obj, name)
- elif hasattr(obj._callable, "c_name"):
- return ExternalFuncNode(db, T, obj, name) # this case should only be used for entrypoints
- elif db.translator.rtyper.backend is llinterp_backend:
- # on llinterp, anything goes
- return ExternalFuncNode(db, T, obj, name)
- else:
- raise ValueError("don't know how to generate code for %r" % (obj,))
- def _select_name(db, obj):
- try:
- return obj._callable.c_name
- except AttributeError:
- pass
- if getattr(obj, 'external', None) == 'C':
- return obj._name
- return db.namespace.uniquename('g_' + obj._name)
- class ExtType_OpaqueNode(ContainerNode):
- nodekind = 'rpyopaque'
- def enum_dependencies(self):
- return []
- def initializationexpr(self, decoration=''):
- T = self.getTYPE()
- raise NotImplementedError(
- 'seeing an unexpected prebuilt object: %s' % (T.tag,))
- def startupcode(self):
- T = self.getTYPE()
- args = [self.getptrname()]
- # XXX how to make this code more generic?
- if T.tag == 'ThreadLock':
- lock = self.obj.externalobj
- if lock.locked():
- args.append('1')
- else:
- args.append('0')
- yield 'RPyOpaque_SETUP_%s(%s);' % (T.tag, ', '.join(args))
- def opaquenode_factory(db, T, obj):
- if T == RuntimeTypeInfo:
- return db.gcpolicy.rtti_node_factory()(db, T, obj)
- if T.hints.get("render_structure", False):
- return ExtType_OpaqueNode(db, T, obj)
- raise Exception("don't know about %r" % (T,))
- def weakrefnode_factory(db, T, obj):
- assert isinstance(obj, llmemory._wref)
- ptarget = obj._dereference()
- wrapper = db.gcpolicy.convert_weakref_to(ptarget)
- container = wrapper._obj
- #obj._converted_weakref = container # hack for genllvm :-/
- return db.getcontainernode(container, _dont_write_c_code=False)
- class GroupNode(ContainerNode):
- nodekind = 'group'
- count_members = None
- def __init__(self, *args):
- ContainerNode.__init__(self, *args)
- self.implementationtypename = 'struct group_%s_s @' % self.name
- def basename(self):
- return self.obj.name
- def enum_dependencies(self):
- # note: for the group used by the GC, it can grow during this phase,
- # which means that we might not return all members yet. This is fixed
- # by get_finish_tables() in rpython.memory.gctransform.framework.
- for member in self.obj.members:
- yield member._as_ptr()
- def _fix_members(self):
- if self.obj.outdated:
- raise Exception(self.obj.outdated)
- if self.count_members is None:
- self.count_members = len(self.obj.members)
- else:
- # make sure no new member showed up, because it's too late
- assert len(self.obj.members) == self.count_members
- def forward_declaration(self):
- self._fix_members()
- yield ''
- ctype = ['%s {' % cdecl(self.implementationtypename, '')]
- for i, member in enumerate(self.obj.members):
- structtypename = self.db.gettype(typeOf(member))
- ctype.append('\t%s;' % cdecl(structtypename, 'member%d' % i))
- ctype.append('} @')
- ctype = '\n'.join(ctype)
- yield '%s;' % (
- forward_cdecl(ctype, self.name, self.db.standalone,
- self.is_thread_local()))
- yield '#include "src/llgroup.h"'
- yield 'PYPY_GROUP_CHECK_SIZE(%s)' % (self.name,)
- for i, member in enumerate(self.obj.members):
- structnode = self.db.getcontainernode(member)
- yield '#define %s %s.member%d' % (structnode.name,
- self.name, i)
- yield ''
- def initializationexpr(self):
- self._fix_members()
- lines = ['{']
- lasti = len(self.obj.members) - 1
- for i, member in enumerate(self.obj.members):
- structnode = self.db.getcontainernode(member)
- lines1 = list(structnode.initializationexpr())
- lines1[0] += '\t/* member%d: %s */' % (i, structnode.name)
- if i != lasti:
- lines1[-1] += ','
- lines.extend(lines1)
- lines.append('}')
- return lines
- ContainerNodeFactory = {
- Struct: StructNode,
- GcStruct: gcstructnode_factory,
- Array: ArrayNode,
- GcArray: ArrayNode,
- FixedSizeArray: FixedSizeArrayNode,
- FuncType: new_funcnode,
- OpaqueType: opaquenode_factory,
- llmemory._WeakRefType: weakrefnode_factory,
- llgroup.GroupType: GroupNode,
- }