text: import sys
  from pypy.interpreter.error import oefmt
  from pypy.interpreter.baseobjspace import W_Root
  from pypy.interpreter.typedef import TypeDef, GetSetProperty, ClassAttr
  from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault
  from rpython.rlib import jit, rgc
  from rpython.rtyper.lltypesystem import lltype, rffi
  
  from pypy.module._cffi_backend import get_dict_rtld_constants
  from pypy.module._cffi_backend import parse_c_type, realize_c_type
  from pypy.module._cffi_backend import newtype, cerrno, ccallback, ctypearray
  from pypy.module._cffi_backend import ctypestruct, ctypeptr, handle
  from pypy.module._cffi_backend import cbuffer, func, wrapper, call_python
  from pypy.module._cffi_backend import cffi_opcode, allocator
  from pypy.module._cffi_backend.ctypeobj import W_CType
  from pypy.module._cffi_backend.cdataobj import W_CData
  
  
  ACCEPT_STRING   = 1
  ACCEPT_CTYPE    = 2
  ACCEPT_CDATA    = 4
  ACCEPT_ALL      = ACCEPT_STRING | ACCEPT_CTYPE | ACCEPT_CDATA
  CONSIDER_FN_AS_FNPTR  = 8
  
  
  def get_ffi_error(space):
      w_ffitype = space.gettypefor(W_FFIObject)
      return w_ffitype.getdictvalue(space, 'error')
  
  
  class FreeCtxObj(object):
      def __init__(self, ctxobj):
          self.ctxobj = ctxobj
          self.free_mems = []       # filled from cdlopen.py
  
      def __del__(self):
          ctxobj = self.ctxobj
          free_mems = self.free_mems
          parse_c_type.free_ctxobj(ctxobj)
          i = len(free_mems) - 1
          while i >= 0:
              lltype.free(free_mems[i], flavor='raw')
              i -= 1
  
  
  class W_FFIObject(W_Root):
      ACCEPT_STRING = ACCEPT_STRING
      ACCEPT_CTYPE  = ACCEPT_CTYPE
      ACCEPT_CDATA  = ACCEPT_CDATA
  
      w_gc_wref_remove = None
      w_init_once_cache = None
      jit_init_once_cache = None
  
      @jit.dont_look_inside
      def __init__(self, space, src_ctx):
          self.space = space
          self.types_dict = {}
          self.ctxobj = parse_c_type.allocate_ctxobj(src_ctx)
          self.is_static = bool(src_ctx)
          self.is_nonempty = bool(src_ctx)
          self._finalizer = FreeCtxObj(self.ctxobj)
          if src_ctx:
              self.cached_types = [None] * parse_c_type.get_num_types(src_ctx)
          else:
              self.cached_types = None
          self.w_FFIError = get_ffi_error(space)
          #
          # list of (W_FFIObject, W_LibObject) included in this ffi,
          # where the lib object may be None
          self.included_ffis_libs = []
  
      def fetch_int_constant(self, name):
          index = parse_c_type.search_in_globals(self.ctxobj.ctx, name)
          if index >= 0:
              g = self.ctxobj.ctx.c_globals[index]
              op = realize_c_type.getop(g.c_type_op)
              if (op == cffi_opcode.OP_CONSTANT_INT or
                    op == cffi_opcode.OP_ENUM):
                  return realize_c_type.realize_global_int(self, g, index)
              raise oefmt(self.w_FFIError,
                          "function, global variable or non-integer constant "
                          "'%s' must be fetched from its original 'lib' "
                          "object", name)
  
          for ffi1, _ in self.included_ffis_libs:
              w_result = ffi1.fetch_int_constant(name)
              if w_result is not None:
                  return w_result
          return None
  
      @jit.elidable_promote()
      def get_string_to_type(self, string, consider_fn_as_fnptr):
          x = self.types_dict[string]     # KeyError if not found
          if isinstance(x, W_CType):
              return x
          else:
              assert isinstance(x, realize_c_type.W_RawFuncType)
              if consider_fn_as_fnptr:
                  return x.unwrap_as_fnptr_in_elidable()
              else:
                  raise KeyError    # don't handle this error case here
  
      def _ffi_bad_type(self, input_text):
          info = self.ctxobj.info
          errmsg = rffi.charp2str(info.c_error_message)
          if len(input_text) > 500:
              raise oefmt(self.w_FFIError, "%s", errmsg)
          printable_text = ['?'] * len(input_text)
          for i in range(len(input_text)):
              if ' ' <= input_text[i] < '\x7f':
                  printable_text[i] = input_text[i]
              elif input_text[i] == '\t' or input_text[i] == '\n':
                  printable_text[i] = ' '
          num_spaces = rffi.getintfield(info, 'c_error_location')
          raise oefmt(self.w_FFIError, "%s\n%s\n%s^",
                      rffi.charp2str(info.c_error_message),
                      ''.join(printable_text),
                      " " * num_spaces)
  
      @jit.dont_look_inside
      def parse_string_to_type(self, string, consider_fn_as_fnptr):
          # This cannot be made @elidable because it calls general space
          # functions (indirectly, e.g. via the new_xxx_type() functions).
          # The get_string_to_type() function above is elidable, and we
          # hope that in almost all cases, get_string_to_type() has already
          # found an answer.
          try:
              x = self.types_dict[string]
          except KeyError:
              info = self.ctxobj.info
              index = parse_c_type.parse_c_type(info, string)
              if index < 0:
                  raise self._ffi_bad_type(string)
              x = realize_c_type.realize_c_type_or_func(
                  self, self.ctxobj.info.c_output, index)
              assert x is not None
              if isinstance(x, realize_c_type.W_RawFuncType):
                  x.unwrap_as_fnptr(self)      # force it here
              self.types_dict[string] = x
          #
          if isinstance(x, W_CType):
              return x
          else:
              assert isinstance(x, realize_c_type.W_RawFuncType)
              if consider_fn_as_fnptr:
                  return x.unwrap_as_fnptr_in_elidable()
              else:
                  raise x.unexpected_fn_type(self)
  
      def ffi_type(self, w_x, accept):
          space = self.space
          if (accept & ACCEPT_STRING) and (
                  space.isinstance_w(w_x, space.w_basestring)):
              string = space.text_w(w_x)
              consider_fn_as_fnptr = (accept & CONSIDER_FN_AS_FNPTR) != 0
              if jit.isconstant(string):
                  try:
                      return self.get_string_to_type(string, consider_fn_as_fnptr)
                  except KeyError:
                      pass
              return self.parse_string_to_type(string, consider_fn_as_fnptr)
          if (accept & ACCEPT_CTYPE) and isinstance(w_x, W_CType):
              return w_x
          if (accept & ACCEPT_CDATA) and isinstance(w_x, W_CData):
              return w_x.ctype
          #
          m1 = "string" if accept & ACCEPT_STRING else ""
          m2 = "ctype object" if accept & ACCEPT_CTYPE else ""
          m3 = "cdata object" if accept & ACCEPT_CDATA else ""
          s12 = " or " if m1 and (m2 or m3) else ""
          s23 = " or " if m2 and m3 else ""
          raise oefmt(space.w_TypeError, "expected a %s%s%s%s%s, got '%T'",
                      m1, s12, m2, s23, m3, w_x)
  
  
      @unwrap_spec(module_name='text', _version=int, _types='bytes')
      def descr_init(self, module_name='?', _version=-1, _types='',
                     w__globals=None, w__struct_unions=None, w__enums=None,
                     w__typenames=None, w__includes=None):
          from pypy.module._cffi_backend import cdlopen
          #
          space = self.space
          if self.is_nonempty:
              raise oefmt(space.w_ValueError,
                          "cannot call FFI.__init__() more than once")
          self.is_nonempty = True
          #
          cdlopen.ffiobj_init(self, module_name, _version, _types,
                              w__globals, w__struct_unions, w__enums,
                              w__typenames, w__includes)
  
  
      doc_errno = "the value of 'errno' from/to the C calls"
  
      def get_errno(self, space):
          return cerrno.get_errno(space)
  
      def set_errno(self, space, errno):
          cerrno.set_errno(space, space.c_int_w(errno))
  
  
      def _more_addressof(self, args_w, w_ctype):
          # contains a loop, the JIT doesn't look inside this helper
          offset = 0
          for i in range(len(args_w)):
              w_ctype, ofs1 = w_ctype.direct_typeoffsetof(args_w[i], i > 0)
              offset += ofs1
          return w_ctype, offset
  
      def descr_addressof(self, w_arg, args_w):
          """\
  Limited equivalent to the '&' operator in C:
  
  1. ffi.addressof(<cdata 'struct-or-union'>) returns a cdata that is a
  pointer to this struct or union.
  
  2. ffi.addressof(<cdata>, field-or-index...) returns the address of a
  field or array item inside the given structure or array, recursively
  in case of nested structures or arrays.
  
  3. ffi.addressof(<library>, "name") returns the address of the named
  function or global variable."""
          #
          from pypy.module._cffi_backend.lib_obj import W_LibObject
          space = self.space
          if isinstance(w_arg, W_LibObject) and len(args_w) == 1:
              # case 3 in the docstring
              return w_arg.address_of_func_or_global_var(space.text_w(args_w[0]))
          #
          w_ctype = self.ffi_type(w_arg, ACCEPT_CDATA)
          if len(args_w) == 0:
              # case 1 in the docstring
              if (not isinstance(w_ctype, ctypestruct.W_CTypeStructOrUnion) and
                  not isinstance(w_ctype, ctypearray.W_CTypeArray)):
                  raise oefmt(space.w_TypeError,
                              "expected a cdata struct/union/array object")
              offset = 0
          else:
              # case 2 in the docstring
              if (not isinstance(w_ctype, ctypestruct.W_CTypeStructOrUnion) and
                  not isinstance(w_ctype, ctypearray.W_CTypeArray) and
                  not isinstance(w_ctype, ctypeptr.W_CTypePointer)):
                  raise oefmt(space.w_TypeError,
                          "expected a cdata struct/union/array/pointer object")
              if len(args_w) == 1:
                  w_ctype, offset = w_ctype.direct_typeoffsetof(args_w[0], False)
              else:
                  w_ctype, offset = self._more_addressof(args_w, w_ctype)
          #
          assert isinstance(w_arg, W_CData)
          cdata = w_arg.unsafe_escaping_ptr()
          cdata = rffi.ptradd(cdata, offset)
          w_ctypeptr = newtype.new_pointer_type(space, w_ctype)
          return W_CData(space, cdata, w_ctypeptr)
  
  
      def descr_alignof(self, w_arg):
          """\
  Return the natural alignment size in bytes of the argument.
  It can be a string naming a C type, or a 'cdata' instance."""
          #
          w_ctype = self.ffi_type(w_arg, ACCEPT_ALL)
          align = w_ctype.alignof()
          return self.space.newint(align)
  
  
      @unwrap_spec(w_name=WrappedDefault(None),
                   w_error=WrappedDefault(None),
                   w_onerror=WrappedDefault(None))
      def descr_def_extern(self, w_name, w_error, w_onerror):
          """\
  A decorator.  Attaches the decorated Python function to the C code
  generated for the 'extern "Python" function of the same name.
  Calling the C function will then invoke the Python function.
  
  Optional arguments: 'name' is the name of the C function, if
  different from the Python function; and 'error' and 'onerror'
  handle what occurs if the Python function raises an exception
  (see the docs for details)."""
          #
          # returns a single-argument function
          space = self.space
          w_ffi = self
          w_decorator = call_python.get_generic_decorator(space)
          return space.appexec([w_decorator, w_ffi, w_name, w_error, w_onerror],
          """(decorator, ffi, name, error, onerror):
              return lambda python_callable: decorator(ffi, python_callable,
                                                       name, error, onerror)""")
  
  
      @unwrap_spec(w_python_callable=WrappedDefault(None),
                   w_error=WrappedDefault(None),
                   w_onerror=WrappedDefault(None))
      def descr_callback(self, w_cdecl, w_python_callable, w_error, w_onerror):
          """\
  Return a callback object or a decorator making such a callback object.
  'cdecl' must name a C function pointer type.  The callback invokes the
  specified 'python_callable' (which may be provided either directly or
  via a decorator).  Important: the callback object must be manually
  kept alive for as long as the callback may be invoked from the C code."""
          #
          w_ctype = self.ffi_type(w_cdecl, ACCEPT_STRING | ACCEPT_CTYPE |
                                           CONSIDER_FN_AS_FNPTR)
          space = self.space
          if not space.is_none(w_python_callable):
              return ccallback.make_callback(space, w_ctype,
                                             w_python_callable, w_error,
                                             w_onerror)
          else:
              # decorator mode: returns a single-argument function
              return space.appexec([w_ctype, w_error, w_onerror],
              """(ctype, error, onerror):
                  import _cffi_backend
                  return lambda python_callable: (
                      _cffi_backend.callback(ctype, python_callable,
                                             error, onerror))""")
  
  
      def descr_cast(self, w_arg, w_ob):
          """\
  Similar to a C cast: returns an instance of the named C
  type initialized with the given 'source'.  The source is
  casted between integers or pointers of any type."""
          #
          w_ctype = self.ffi_type(w_arg, ACCEPT_STRING | ACCEPT_CTYPE)
          return w_ctype.cast(w_ob)
  
  
      def descr_from_buffer(self, w_python_buffer):
          """\
  Return a <cdata 'char[]'> that points to the data of the given Python
  object, which must support the buffer interface.  Note that this is
  not meant to be used on the built-in types str or unicode
  (you can build 'char[]' arrays explicitly) but only on objects
  containing large quantities of raw data in some other format, like
  'array.array' or numpy arrays."""
          #
          w_ctchara = newtype._new_chara_type(self.space)
          return func._from_buffer(self.space, w_ctchara, w_python_buffer)
  
  
      @unwrap_spec(w_arg=W_CData)
      def descr_from_handle(self, w_arg):
          """\
  Cast a 'void *' back to a Python object.  Must be used *only* on the
  pointers returned by new_handle(), and *only* as long as the exact
  cdata object returned by new_handle() is still alive (somewhere else
  in the program).  Failure to follow these rules will crash."""
          #
          return handle.from_handle(self.space, w_arg)
  
  
      @unwrap_spec(w_cdata=W_CData, size=int)
      def descr_gc(self, w_cdata, w_destructor, size=0):
          """\
  Return a new cdata object that points to the same data.
  Later, when this new cdata object is garbage-collected,
  'destructor(old_cdata_object)' will be called."""
          #
          return w_cdata.with_gc(w_destructor, size)
  
  
      @unwrap_spec(replace_with='text')
      def descr_getctype(self, w_cdecl, replace_with=''):
          """\
  Return a string giving the C type 'cdecl', which may be itself a
  string or a <ctype> object.  If 'replace_with' is given, it gives
  extra text to append (or insert for more complicated C types), like a
  variable name, or '*' to get actually the C type 'pointer-to-cdecl'."""
          #
          w_ctype = self.ffi_type(w_cdecl, ACCEPT_STRING | ACCEPT_CTYPE)
          replace_with = replace_with.strip(' ')
          if len(replace_with) == 0:
              result = w_ctype.name
          else:
              add_paren = (replace_with[0] == '*' and
                           isinstance(w_ctype, ctypearray.W_CTypeArray))
              add_space = (not add_paren and replace_with[0] != '['
                                         and replace_with[0] != '(')
              #
              result = w_ctype.name[:w_ctype.name_position]
              if add_paren:
                  result += '('
              if add_space:
                  result += ' '
              result += replace_with
              if add_paren:
                  result += ')'
              result += w_ctype.name[w_ctype.name_position:]
          # Python 3: bytes -> unicode string
          return self.space.newtext(result)
  
  
      @unwrap_spec(code=int)
      def descr_getwinerror(self, code=-1):
          """\
  Return either the GetLastError() or the error number given by the
  optional 'code' argument, as a tuple '(code, message)'."""
          #
          return cerrno.getwinerror(self.space, code)
  
  
      @unwrap_spec(n=int)
      def descr_memmove(self, w_dest, w_src, n):
          """\
  ffi.memmove(dest, src, n) copies n bytes of memory from src to dest.
  
  Like the C function memmove(), the memory areas may overlap;
  apart from that it behaves like the C function memcpy().
  
  'src' can be any cdata ptr or array, or any Python buffer object.
  'dest' can be any cdata ptr or array, or a writable Python buffer
  object.  The size to copy, 'n', is always measured in bytes.
  
  Unlike other methods, this one supports all Python buffer including
  byte strings and bytearrays---but it still does not support
  non-contiguous buffers."""
          #
          return func.memmove(self.space, w_dest, w_src, n)
  
  
      @unwrap_spec(w_init=WrappedDefault(None))
      def descr_new(self, w_arg, w_init):
          """\
  Allocate an instance according to the specified C type and return a
  pointer to it.  The specified C type must be either a pointer or an
  array: ``new('X *')`` allocates an X and returns a pointer to it,
  whereas ``new('X[n]')`` allocates an array of n X'es and returns an
  array referencing it (which works mostly like a pointer, like in C).
  You can also use ``new('X[]', n)`` to allocate an array of a
  non-constant length n.
  
  The memory is initialized following the rules of declaring a global
  variable in C: by default it is zero-initialized, but an explicit
  initializer can be given which can be used to fill all or part of the
  memory.
  
  When the returned <cdata> object goes out of scope, the memory is
  freed.  In other words the returned <cdata> object has ownership of
  the value of type 'cdecl' that it points to.  This means that the raw
  data can be used as long as this object is kept alive, but must not be
  used for a longer time.  Be careful about that when copying the
  pointer to the memory somewhere else, e.g. into another structure."""
          #
          w_ctype = self.ffi_type(w_arg, ACCEPT_STRING | ACCEPT_CTYPE)
          return w_ctype.newp(w_init, allocator.default_allocator)
  
  
      @unwrap_spec(w_alloc=WrappedDefault(None),
                   w_free=WrappedDefault(None),
                   should_clear_after_alloc=int)
      def descr_new_allocator(self, w_alloc, w_free,
                              should_clear_after_alloc=1):
          """\
  Return a new allocator, i.e. a function that behaves like ffi.new()
  but uses the provided low-level 'alloc' and 'free' functions.
  
  'alloc' is called with the size as argument.  If it returns NULL, a
  MemoryError is raised.  'free' is called with the result of 'alloc'
  as argument.  Both can be either Python functions or directly C
  functions.  If 'free' is None, then no free function is called.
  If both 'alloc' and 'free' are None, the default is used.
  
  If 'should_clear_after_alloc' is set to False, then the memory
  returned by 'alloc' is assumed to be already cleared (or you are
  fine with garbage); otherwise CFFI will clear it.
          """
          #
          return allocator.new_allocator(self, w_alloc, w_free,
                                         should_clear_after_alloc)
  
  
      def descr_new_handle(self, w_arg):
          """\
  Return a non-NULL cdata of type 'void *' that contains an opaque
  reference to the argument, which can be any Python object.  To cast it
  back to the original object, use from_handle().  You must keep alive
  the cdata object returned by new_handle()!"""
          #
          space = self.space
          return handle._newp_handle(space, newtype._new_voidp_type(space), w_arg)
  
  
      def _more_offsetof(self, w_ctype, w_arg0, args_w):
          # contains a loop, the JIT doesn't look inside this helper
          w_ctype, offset = w_ctype.direct_typeoffsetof(w_arg0, False)
          for i in range(len(args_w)):
              w_ctype, ofs1 = w_ctype.direct_typeoffsetof(args_w[i], True)
              offset += ofs1
          return offset
  
      def descr_offsetof(self, w_arg, w_field_or_array, args_w):
          """\
  Return the offset of the named field inside the given structure or
  array, which must be given as a C type name.  You can give several
  field names in case of nested structures.  You can also give numeric
  values which correspond to array items, in case of an array type."""
          #
          w_ctype = self.ffi_type(w_arg, ACCEPT_STRING | ACCEPT_CTYPE)
          if len(args_w) == 0:
              _, offset = w_ctype.direct_typeoffsetof(w_field_or_array, False)
          else:
              offset = self._more_offsetof(w_ctype, w_field_or_array, args_w)
          return self.space.newint(offset)
  
  
      @unwrap_spec(w_cdata=W_CData, maxlen=int)
      def descr_string(self, w_cdata, maxlen=-1):
          """\
  Return a Python string (or unicode string) from the 'cdata'.  If
  'cdata' is a pointer or array of characters or bytes, returns the
  null-terminated string.  The returned string extends until the first
  null character, or at most 'maxlen' characters.  If 'cdata' is an
  array then 'maxlen' defaults to its length.
  
  If 'cdata' is a pointer or array of wchar_t, returns a unicode string
  following the same rules.
  
  If 'cdata' is a single character or byte or a wchar_t, returns it as a
  string or unicode string.
  
  If 'cdata' is an enum, returns the value of the enumerator as a
  string, or 'NUMBER' if the value is out of range."""
          #
          return w_cdata.ctype.string(w_cdata, maxlen)
  
  
      @unwrap_spec(w_cdata=W_CData, length=int)
      def descr_unpack(self, w_cdata, length):
          """Unpack an array of C data of the given length,
  returning a Python string/unicode/list.
  
  If 'cdata' is a pointer to 'char', returns a byte string.
  It does not stop at the first null.  This is equivalent to:
  ffi.buffer(cdata, length)[:]
  
  If 'cdata' is a pointer to 'wchar_t', returns a unicode string.
  'length' is measured in wchar_t's; it is not the size in bytes.
  
  If 'cdata' is a pointer to anything else, returns a list of
  'length' items.  This is a faster equivalent to:
  [cdata[i] for i in range(length)]"""
          #
          return w_cdata.unpack(length)
  
  
      def descr_sizeof(self, w_arg):
          """\
  Return the size in bytes of the argument.
  It can be a string naming a C type, or a 'cdata' instance."""
          #
          if isinstance(w_arg, W_CData):
              size = w_arg._sizeof()
          else:
              w_ctype = self.ffi_type(w_arg, ACCEPT_ALL)
              size = w_ctype.size
              if size < 0:
                  raise oefmt(self.w_FFIError,
                              "don't know the size of ctype '%s'", w_ctype.name)
          return self.space.newint(size)
  
  
      def descr_typeof(self, w_arg):
          """\
  Parse the C type given as a string and return the
  corresponding <ctype> object.
  It can also be used on 'cdata' instance to get its C type."""
          #
          if isinstance(w_arg, wrapper.W_FunctionWrapper):
              return w_arg.typeof(self)
          return self.ffi_type(w_arg, ACCEPT_STRING | ACCEPT_CDATA)
  
  
      @unwrap_spec(filename="fsencode_or_none", flags=int)
      def descr_dlopen(self, filename, flags=0):
          """\
  Load and return a dynamic library identified by 'name'.  The standard
  C library can be loaded by passing None.
  
  Note that functions and types declared with 'ffi.cdef()' are not
  linked to a particular library, just like C headers.  In the library
  we only look for the actual (untyped) symbols at the time of their
  first access."""
          #
          from pypy.module._cffi_backend import cdlopen
          return cdlopen.W_DlOpenLibObject(self, filename, flags)
  
  
      def descr_dlclose(self, w_lib):
          """\
  Close a library obtained with ffi.dlopen().  After this call, access to
  "functions or variables from the library will fail (possibly with a
  segmentation fault)."""
          #
          from pypy.module._cffi_backend.lib_obj import W_LibObject
          lib = self.space.interp_w(W_LibObject, w_lib)
          lib.cdlopen_close()
  
  
      @unwrap_spec(name='text')
      def descr_integer_const(self, name):
          """\
  Get the value of an integer constant.
  
  'ffi.integer_const(\"xxx\")' is equivalent to 'lib.xxx' if xxx names an
  integer constant.  The point of this function is limited to use cases
  where you have an 'ffi' object but not any associated 'lib' object."""
          #
          w_result = self.fetch_int_constant(name)
          if w_result is None:
              raise oefmt(self.space.w_AttributeError,
                          "integer constant '%s' not found", name)
          return w_result
  
  
      def descr_list_types(self):
          """\
  Returns the user type names known to this FFI instance.
  This returns a tuple containing three lists of names:
  (typedef_names, names_of_structs, names_of_unions)"""
          #
          space = self.space
          ctx = self.ctxobj.ctx
  
          lst1_w = []
          for i in range(rffi.getintfield(ctx, 'c_num_typenames')):
              s = rffi.charp2str(ctx.c_typenames[i].c_name)
              lst1_w.append(space.newtext(s))
  
          lst2_w = []
          lst3_w = []
          for i in range(rffi.getintfield(ctx, 'c_num_struct_unions')):
              su = ctx.c_struct_unions[i]
              if su.c_name[0] == '$':
                  continue
              s = rffi.charp2str(su.c_name)
              if rffi.getintfield(su, 'c_flags') & cffi_opcode.F_UNION:
                  lst_w = lst3_w
              else:
                  lst_w = lst2_w
              lst_w.append(space.newtext(s))
  
          return space.newtuple([space.newlist(lst1_w),
                                 space.newlist(lst2_w),
                                 space.newlist(lst3_w)])
  
  
      def descr_init_once(self, w_func, w_tag):
          """\
  init_once(function, tag): run function() once.  More precisely,
  'function()' is called the first time we see a given 'tag'.
  
  The return value of function() is remembered and returned by the current
  and all future init_once() with the same tag.  If init_once() is called
  from multiple threads in parallel, all calls block until the execution
  of function() is done.  If function() raises an exception, it is
  propagated and nothing is cached."""
          #
          # first, a fast-path for the JIT which only works if the very
          # same w_tag object is passed; then it turns into no code at all
          try:
              return self._init_once_elidable(w_tag)
          except KeyError:
              return self._init_once_slowpath(w_func, w_tag)
  
      @jit.elidable
      def _init_once_elidable(self, w_tag):
          jit_cache = self.jit_init_once_cache
          if jit_cache is not None:
              return jit_cache[w_tag]
          else:
              raise KeyError
  
      @jit.dont_look_inside
      def _init_once_slowpath(self, w_func, w_tag):
          space = self.space
          w_cache = self.w_init_once_cache
          if w_cache is None:
              w_cache = self.space.newdict()
              jit_cache = {}
              self.w_init_once_cache = w_cache
              self.jit_init_once_cache = jit_cache
          #
          # get the lock or result from cache[tag]
          w_res = space.finditem(w_cache, w_tag)
          if w_res is None:
              w_res = W_InitOnceLock(space)
              w_res = space.call_method(w_cache, 'setdefault', w_tag, w_res)
          if not isinstance(w_res, W_InitOnceLock):
              return w_res
          with w_res.lock:
              w_res = space.finditem(w_cache, w_tag)
              if w_res is None or isinstance(w_res, W_InitOnceLock):
                  w_res = space.call_function(w_func)
                  self.jit_init_once_cache[w_tag] = w_res
                  space.setitem(w_cache, w_tag, w_res)
              else:
                  # the real result was put in the dict while we were
                  # waiting for lock.__enter__() above
                  pass
          return w_res
  
  
  class W_InitOnceLock(W_Root):
      def __init__(self, space):
          self.lock = space.allocate_lock()
  
  
  @jit.dont_look_inside
  def make_plain_ffi_object(space, w_ffitype=None):
      if w_ffitype is None:
          w_ffitype = space.gettypefor(W_FFIObject)
      r = space.allocate_instance(W_FFIObject, w_ffitype)
      # get in 'src_ctx' a NULL which translation doesn't consider to be constant
      src_ctx = rffi.cast(parse_c_type.PCTX, 0)
      r.__init__(space, src_ctx)
      return r
  
  def W_FFIObject___new__(space, w_subtype, __args__):
      return make_plain_ffi_object(space, w_subtype)
  
  def make_CData(space):
      return space.gettypefor(W_CData)
  
  def make_CType(space):
      return space.gettypefor(W_CType)
  
  def make_NULL(space):
      ctvoidp = newtype._new_voidp_type(space)
      w_NULL = ctvoidp.cast(space.newint(0))
      return w_NULL
  
  def make_error(space):
      return space.appexec([], """():
          return type('error', (Exception,), {'__module__': 'ffi'})""")
  
  def make_buffer(space):
      return space.gettypefor(cbuffer.MiniBuffer)
  
  _extras = get_dict_rtld_constants()
  if sys.platform == 'win32':
      _extras['getwinerror'] = interp2app(W_FFIObject.descr_getwinerror)
  
  
  W_FFIObject.typedef = TypeDef(
          'CompiledFFI',
          __new__     = interp2app(W_FFIObject___new__),
          __init__    = interp2app(W_FFIObject.descr_init),
          CData       = ClassAttr(make_CData),
          CType       = ClassAttr(make_CType),
          NULL        = ClassAttr(make_NULL),
          error       = ClassAttr(make_error),
          errno       = GetSetProperty(W_FFIObject.get_errno,
                                       W_FFIObject.set_errno,
                                       doc=W_FFIObject.doc_errno,
                                       cls=W_FFIObject),
          addressof   = interp2app(W_FFIObject.descr_addressof),
          alignof     = interp2app(W_FFIObject.descr_alignof),
          buffer      = ClassAttr(make_buffer),
          callback    = interp2app(W_FFIObject.descr_callback),
          cast        = interp2app(W_FFIObject.descr_cast),
          def_extern  = interp2app(W_FFIObject.descr_def_extern),
          dlclose     = interp2app(W_FFIObject.descr_dlclose),
          dlopen      = interp2app(W_FFIObject.descr_dlopen),
          from_buffer = interp2app(W_FFIObject.descr_from_buffer),
          from_handle = interp2app(W_FFIObject.descr_from_handle),
          gc          = interp2app(W_FFIObject.descr_gc),
          getctype    = interp2app(W_FFIObject.descr_getctype),
          init_once   = interp2app(W_FFIObject.descr_init_once),
          integer_const = interp2app(W_FFIObject.descr_integer_const),
          list_types  = interp2app(W_FFIObject.descr_list_types),
          memmove     = interp2app(W_FFIObject.descr_memmove),
          new         = interp2app(W_FFIObject.descr_new),
          new_allocator = interp2app(W_FFIObject.descr_new_allocator),
          new_handle  = interp2app(W_FFIObject.descr_new_handle),
          offsetof    = interp2app(W_FFIObject.descr_offsetof),
          sizeof      = interp2app(W_FFIObject.descr_sizeof),
          string      = interp2app(W_FFIObject.descr_string),
          typeof      = interp2app(W_FFIObject.descr_typeof),
          unpack      = interp2app(W_FFIObject.descr_unpack),
          **_extras)
  