Author: Ronan Lamy <ronan.l...@gmail.com> Branch: py3.5 Changeset: r89063:8454bbf43352 Date: 2016-12-14 18:13 +0000 http://bitbucket.org/pypy/pypy/changeset/8454bbf43352/
Log: hg merge default diff too long, truncating to 2000 out of 11892 lines diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -78,3 +78,5 @@ ^.hypothesis/ ^release/ ^rpython/_cache$ + +pypy/module/cppyy/.+/*\.pcm diff --git a/pypy/doc/cppyy.rst b/pypy/doc/cppyy.rst --- a/pypy/doc/cppyy.rst +++ b/pypy/doc/cppyy.rst @@ -1,135 +1,36 @@ cppyy: C++ bindings for PyPy ============================ -The cppyy module creates, at run-time, Python-side classes and functions for -C++, by querying a C++ reflection system. -The default system used is `Reflex`_, which extracts the needed information -from C++ header files. -Another current backend is based on `CINT`_, and yet another, more important -one for the medium- to long-term will be based on `cling`_. -The latter sits on top of `llvm`_'s `clang`_, and will therefore allow the use -of C++11. -The work on the cling backend has so far been done only for CPython, but -bringing it to PyPy is a lot less work than developing it in the first place. +The cppyy module delivers dynamic Python-C++ bindings. +It is designed for automation, high performance, scale, interactivity, and +handling all of modern C++. +It is based on `Cling`_ which, through `LLVM`_/`clang`_, provides C++ +reflection and interactivity. +Reflection information is extracted from C++ header files. +Cppyy itself is built into PyPy (an alternative exists for CPython), but +it requires a backend, installable through pip, to interface with Cling. -.. _Reflex: https://root.cern.ch/how/how-use-reflex -.. _CINT: https://root.cern.ch/introduction-cint -.. _cling: https://root.cern.ch/cling -.. _llvm: http://llvm.org/ +.. _Cling: https://root.cern.ch/cling +.. _LLVM: http://llvm.org/ .. _clang: http://clang.llvm.org/ -This document describes the version of cppyy that lives in the main branch of -PyPy. -The development of cppyy happens in the "reflex-support" branch. - - -Motivation ----------- - -To provide bindings to another language in CPython, you program to a -generic C-API that exposes many of the interpreter features. -With PyPy, however, there is no such generic C-API, because several of the -interpreter features (e.g. the memory model) are pluggable and therefore -subject to change. -Furthermore, a generic API does not allow any assumptions about the calls -into another language, forcing the JIT to behave conservatively around these -calls and with the objects that cross language boundaries. -In contrast, cppyy does not expose an API, but expects one to be implemented -by a backend. -It makes strong assumptions about the semantics of the API that it uses and -that in turn allows the JIT to make equally strong assumptions. -This is possible, because the expected API is only for providing C++ language -bindings, and does not provide generic programmability. - -The cppyy module further offers two features, which result in improved -performance as well as better functionality and cross-language integration. -First, cppyy itself is written in RPython and therefore open to optimizations -by the JIT up until the actual point of call into C++. -This means for example, that if variables are already unboxed by the JIT, they -can be passed through directly to C++. -Second, a backend such as Reflex (and cling far more so) adds dynamic features -to C++, thus greatly reducing impedance mismatches between the two languages. -For example, Reflex is dynamic enough to allow writing runtime bindings -generation in python (as opposed to RPython) and this is used to create very -natural "pythonizations" of the bound code. -As another example, cling allows automatic instantiations of templates. - -See this description of the `cppyy architecture`_ for further details. - -.. _cppyy architecture: http://morepypy.blogspot.com/2012/06/architecture-of-cppyy.html - Installation ------------ -There are two ways of using cppyy, and the choice depends on how pypy-c was -built: the backend can be builtin, or dynamically loadable. -The former has the disadvantage of requiring pypy-c to be linked with external -C++ libraries (e.g. libReflex.so), but has the advantage of being faster in -some cases. -That advantage will disappear over time, however, with improvements in the -JIT. -Therefore, this document assumes that the dynamically loadable backend is -chosen (it is, by default). -See the :doc:`backend documentation <cppyy_backend>`. +This assumes PyPy2.7 v5.7 or later; earlier versions use a Reflex-based cppyy +module, which is no longer supported. +Both the tooling and user-facing Python codes are very backwards compatible, +however. +Further dependencies are cmake (for general build) and Python2.7 (for LLVM). -A standalone version of Reflex that also provides the dynamically loadable -backend is available for `download`_. Note this is currently the only way to -get the dynamically loadable backend, so use this first. +Assuming you have a recent enough version of PyPy installed, use pip to +complete the installation of cppyy:: -That version, as well as any other distribution of Reflex (e.g. the one that -comes with `ROOT`_, which may be part of your Linux distribution as part of -the selection of scientific software) will also work for a build with the -builtin backend. + $ pypy-c -m pip install PyPy-cppyy-backend -.. _download: http://cern.ch/wlav/reflex-2014-10-20.tar.bz2 -.. _ROOT: http://root.cern.ch/ - -Besides Reflex, you probably need a version of `gccxml`_ installed, which is -most easily provided by the packager of your system. -If you read up on gccxml, you will probably notice that it is no longer being -developed and hence will not provide C++11 support. -That's why the medium term plan is to move to cling. -Note that gccxml is only needed to generate reflection libraries. -It is not needed to use them. - -.. _gccxml: http://www.gccxml.org - -To install the standalone version of Reflex, after download:: - - $ tar jxf reflex-2014-10-20.tar.bz2 - $ cd reflex-2014-10-20 - $ ./build/autogen - $ ./configure <usual set of options such as --prefix> - $ make && make install - -The usual rules apply: <prefix>/bin needs to be added to the ``PATH`` and -<prefix>/lib to the ``LD_LIBRARY_PATH`` environment variable. -For convenience, this document will assume that there is a ``REFLEXHOME`` -variable that points to <prefix>. -If you downloaded or built the whole of ROOT, ``REFLEXHOME`` should be equal -to ``ROOTSYS``. - -The following is optional, and is only to show how pypy-c can be build -:doc:`from source <build>`, for example to get at the main development branch of cppyy. -The :doc:`backend documentation <cppyy_backend>` has more details on the backend-specific -prerequisites. - -Then run the translation to build ``pypy-c``:: - - $ hg clone https://bitbucket.org/pypy/pypy - $ cd pypy - $ hg up reflex-support # optional - - # This example shows python, but using pypy-c is faster and uses less memory - $ python rpython/bin/rpython --opt=jit pypy/goal/targetpypystandalone --withmod-cppyy - -This will build a ``pypy-c`` that includes the cppyy module, and through that, -Reflex support. -Of course, if you already have a pre-built version of the ``pypy`` interpreter, -you can use that for the translation rather than ``python``. -If not, you may want :ref:`to obtain a binary distribution <prebuilt-pypy>` to speed up the -translation step. +The building process may take quite some time as it includes a customized +version of LLVM as part of Cling. Basic bindings example diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -426,6 +426,8 @@ make_finalizer_queue(W_Root, self) self._code_of_sys_exc_info = None + self._builtin_functions_by_identifier = {'': None} + # can be overridden to a subclass self.initialize() diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py --- a/pypy/interpreter/function.py +++ b/pypy/interpreter/function.py @@ -257,16 +257,15 @@ def descr_function_repr(self): return self.getrepr(self.space, u'function %s' % self.qualname) - # delicate - _all = {'': None} def _cleanup_(self): + # delicate from pypy.interpreter.gateway import BuiltinCode if isinstance(self.code, BuiltinCode): # we have been seen by other means so rtyping should not choke # on us identifier = self.code.identifier - previous = Function._all.get(identifier, self) + previous = self.space._builtin_functions_by_identifier.get(identifier, self) assert previous is self, ( "duplicate function ids with identifier=%r: %r and %r" % ( identifier, previous, self)) @@ -274,10 +273,10 @@ return False def add_to_table(self): - Function._all[self.code.identifier] = self + self.space._builtin_functions_by_identifier[self.code.identifier] = self - def find(identifier): - return Function._all[identifier] + def find(space, identifier): + return space._builtin_functions_by_identifier[identifier] find = staticmethod(find) def descr_function__reduce__(self, space): diff --git a/pypy/interpreter/gateway.py b/pypy/interpreter/gateway.py --- a/pypy/interpreter/gateway.py +++ b/pypy/interpreter/gateway.py @@ -724,10 +724,10 @@ return space.newtuple([builtin_code, space.newtuple([space.wrap(self.identifier)])]) - def find(indentifier): + @staticmethod + def find(space, identifier): from pypy.interpreter.function import Function - return Function._all[indentifier].code - find = staticmethod(find) + return Function.find(space, identifier).code def signature(self): return self.sig diff --git a/pypy/module/_pickle_support/maker.py b/pypy/module/_pickle_support/maker.py --- a/pypy/module/_pickle_support/maker.py +++ b/pypy/module/_pickle_support/maker.py @@ -73,7 +73,7 @@ def builtin_code(space, identifier): from pypy.interpreter import gateway try: - return gateway.BuiltinCode.find(identifier) + return gateway.BuiltinCode.find(space, identifier) except KeyError: raise oefmt(space.w_RuntimeError, "cannot unpickle builtin code: %s", identifier) @@ -82,7 +82,7 @@ def builtin_function(space, identifier): from pypy.interpreter import function try: - return function.Function.find(identifier) + return function.Function.find(space, identifier) except KeyError: raise oefmt(space.w_RuntimeError, "cannot unpickle builtin function: %s", identifier) diff --git a/pypy/module/cppyy/__init__.py b/pypy/module/cppyy/__init__.py --- a/pypy/module/cppyy/__init__.py +++ b/pypy/module/cppyy/__init__.py @@ -14,7 +14,6 @@ '_set_class_generator' : 'interp_cppyy.set_class_generator', '_set_function_generator': 'interp_cppyy.set_function_generator', '_register_class' : 'interp_cppyy.register_class', - '_is_static' : 'interp_cppyy.is_static', '_get_nullptr' : 'interp_cppyy.get_nullptr', 'CPPInstanceBase' : 'interp_cppyy.W_CPPInstance', 'addressof' : 'interp_cppyy.addressof', diff --git a/pypy/module/cppyy/bench/Makefile b/pypy/module/cppyy/bench/Makefile --- a/pypy/module/cppyy/bench/Makefile +++ b/pypy/module/cppyy/bench/Makefile @@ -26,4 +26,4 @@ bench02Dict_reflex.so: bench02.h bench02.cxx bench02.xml $(genreflex) bench02.h $(genreflexflags) --selection=bench02.xml -I$(ROOTSYS)/include - g++ -o $@ bench02.cxx bench02_rflx.cpp -I$(ROOTSYS)/include -shared -lReflex -lHistPainter `root-config --libs` $(cppflags) $(cppflags2) + g++ -o $@ bench02.cxx bench02_rflx.cpp -I$(ROOTSYS)/include -shared -std=c++11 -lHistPainter `root-config --libs` $(cppflags) $(cppflags2) diff --git a/pypy/module/cppyy/capi/builtin_capi.py b/pypy/module/cppyy/capi/builtin_capi.py --- a/pypy/module/cppyy/capi/builtin_capi.py +++ b/pypy/module/cppyy/capi/builtin_capi.py @@ -1,12 +1,11 @@ from rpython.rtyper.lltypesystem import rffi, lltype +from rpython.rlib.rarithmetic import intmask from rpython.rlib import jit -import reflex_capi as backend -#import cint_capi as backend +import cling_capi as backend from pypy.module.cppyy.capi.capi_types import C_SCOPE, C_TYPE, C_OBJECT,\ - C_METHOD, C_INDEX, C_INDEX_ARRAY, WLAVC_INDEX,\ - C_METHPTRGETTER, C_METHPTRGETTER_PTR + C_METHOD, C_INDEX, C_INDEX_ARRAY, WLAVC_INDEX, C_FUNC_PTR identify = backend.identify pythonize = backend.pythonize @@ -52,13 +51,6 @@ compilation_info=backend.eci) def c_get_scope_opaque(space, name): return _c_get_scope_opaque(name) -_c_get_template = rffi.llexternal( - "cppyy_get_template", - [rffi.CCHARP], C_TYPE, - releasegil=ts_reflect, - compilation_info=backend.eci) -def c_get_template(space, name): - return _c_get_template(name) _c_actual_class = rffi.llexternal( "cppyy_actual_class", [C_TYPE, C_OBJECT], C_TYPE, @@ -154,6 +146,13 @@ compilation_info=backend.eci) def c_call_d(space, cppmethod, cppobject, nargs, args): return _c_call_d(cppmethod, cppobject, nargs, args) +_c_call_ld = rffi.llexternal( + "cppyy_call_ld", + [C_METHOD, C_OBJECT, rffi.INT, rffi.VOIDP], rffi.LONGDOUBLE, + releasegil=ts_call, + compilation_info=backend.eci) +def c_call_ld(space, cppmethod, cppobject, nargs, args): + return _c_call_ld(cppmethod, cppobject, nargs, args) _c_call_r = rffi.llexternal( "cppyy_call_r", @@ -164,11 +163,17 @@ return _c_call_r(cppmethod, cppobject, nargs, args) _c_call_s = rffi.llexternal( "cppyy_call_s", - [C_METHOD, C_OBJECT, rffi.INT, rffi.VOIDP], rffi.CCHARP, + [C_METHOD, C_OBJECT, rffi.INT, rffi.VOIDP, rffi.SIZE_TP], rffi.CCHARP, releasegil=ts_call, compilation_info=backend.eci) def c_call_s(space, cppmethod, cppobject, nargs, args): - return _c_call_s(cppmethod, cppobject, nargs, args) + length = lltype.malloc(rffi.SIZE_TP.TO, 1, flavor='raw') + try: + cstr = _c_call_s(cppmethod, cppobject, nargs, args, length) + cstr_len = intmask(length[0]) + finally: + lltype.free(length, flavor='raw') + return cstr, cstr_len _c_constructor = rffi.llexternal( "cppyy_constructor", @@ -185,15 +190,14 @@ def c_call_o(space, method, cppobj, nargs, args, cppclass): return _c_call_o(method, cppobj, nargs, args, cppclass.handle) -_c_get_methptr_getter = rffi.llexternal( - "cppyy_get_methptr_getter", - [C_SCOPE, C_INDEX], C_METHPTRGETTER_PTR, +_c_get_function_address = rffi.llexternal( + "cppyy_get_function_address", + [C_SCOPE, C_INDEX], C_FUNC_PTR, releasegil=ts_reflect, compilation_info=backend.eci, - elidable_function=True, random_effects_on_gcobjs=False) -def c_get_methptr_getter(space, cppscope, index): - return _c_get_methptr_getter(cppscope.handle, index) +def c_get_function_address(space, cppscope, index): + return _c_get_function_address(cppscope.handle, index) # handling of function argument buffer --------------------------------------- _c_allocate_function_args = rffi.llexternal( @@ -215,8 +219,8 @@ [], rffi.SIZE_T, releasegil=ts_memory, compilation_info=backend.eci, - elidable_function=True, random_effects_on_gcobjs=False) +@jit.elidable def c_function_arg_sizeof(space): return _c_function_arg_sizeof() _c_function_arg_typeoffset = rffi.llexternal( @@ -224,8 +228,8 @@ [], rffi.SIZE_T, releasegil=ts_memory, compilation_info=backend.eci, - elidable_function=True, random_effects_on_gcobjs=False) +@jit.elidable def c_function_arg_typeoffset(space): return _c_function_arg_typeoffset() @@ -237,6 +241,20 @@ compilation_info=backend.eci) def c_is_namespace(space, scope): return _c_is_namespace(scope) +_c_is_template = rffi.llexternal( + "cppyy_is_template", + [rffi.CCHARP], rffi.INT, + releasegil=ts_reflect, + compilation_info=backend.eci) +def c_is_template(space, name): + return _c_is_template(name) +_c_is_abstract = rffi.llexternal( + "cppyy_is_abstract", + [C_SCOPE], rffi.INT, + releasegil=ts_reflect, + compilation_info=backend.eci) +def c_is_abstract(space, cpptype): + return _c_is_abstract(cpptype) _c_is_enum = rffi.llexternal( "cppyy_is_enum", [rffi.CCHARP], rffi.INT, @@ -286,9 +304,8 @@ [C_TYPE, C_TYPE], rffi.INT, releasegil=ts_reflect, compilation_info=backend.eci, - elidable_function=True, random_effects_on_gcobjs=False) -@jit.elidable_promote('2') +@jit.elidable def c_is_subtype(space, derived, base): if derived == base: return 1 @@ -296,12 +313,11 @@ _c_base_offset = rffi.llexternal( "cppyy_base_offset", - [C_TYPE, C_TYPE, C_OBJECT, rffi.INT], rffi.SIZE_T, + [C_TYPE, C_TYPE, C_OBJECT, rffi.INT], rffi.LONG, # actually ptrdiff_t releasegil=ts_reflect, compilation_info=backend.eci, - elidable_function=True, random_effects_on_gcobjs=False) -@jit.elidable_promote('1,2,4') +@jit.elidable def c_base_offset(space, derived, base, address, direction): if derived == base: return 0 @@ -340,7 +356,7 @@ i += 1 py_indices.append(index) index = indices[i] - c_free(rffi.cast(rffi.VOIDP, indices)) # c_free defined below + c_free(space, rffi.cast(rffi.VOIDP, indices)) # c_free defined below return py_indices _c_method_name = rffi.llexternal( @@ -474,7 +490,7 @@ return charp2str_free(space, _c_datamember_type(cppscope.handle, datamember_index)) _c_datamember_offset = rffi.llexternal( "cppyy_datamember_offset", - [C_SCOPE, rffi.INT], rffi.SIZE_T, + [C_SCOPE, rffi.INT], rffi.LONG, # actually ptrdiff_t releasegil=ts_reflect, compilation_info=backend.eci) def c_datamember_offset(space, cppscope, datamember_index): @@ -519,27 +535,29 @@ compilation_info=backend.eci) def c_strtoull(space, svalue): return _c_strtoull(svalue) -c_free = rffi.llexternal( +_c_free = rffi.llexternal( "cppyy_free", [rffi.VOIDP], lltype.Void, releasegil=ts_memory, compilation_info=backend.eci) +def c_free(space, voidp): + return _c_free(voidp) def charp2str_free(space, charp): string = rffi.charp2str(charp) voidp = rffi.cast(rffi.VOIDP, charp) - c_free(voidp) + _c_free(voidp) return string _c_charp2stdstring = rffi.llexternal( "cppyy_charp2stdstring", - [rffi.CCHARP], C_OBJECT, + [rffi.CCHARP, rffi.SIZE_T], C_OBJECT, releasegil=ts_helper, compilation_info=backend.eci) -def c_charp2stdstring(space, svalue): - with rffi.scoped_view_charp(svalue) as charp: - result = _c_charp2stdstring(charp) - return result +def c_charp2stdstring(space, pystr, sz): + with rffi.scoped_view_charp(pystr) as cstr: + cppstr = _c_charp2stdstring(cstr, sz) + return cppstr _c_stdstring2stdstring = rffi.llexternal( "cppyy_stdstring2stdstring", [C_OBJECT], C_OBJECT, @@ -547,3 +565,26 @@ compilation_info=backend.eci) def c_stdstring2stdstring(space, cppobject): return _c_stdstring2stdstring(cppobject) + +_c_stdvector_valuetype = rffi.llexternal( + "cppyy_stdvector_valuetype", + [rffi.CCHARP], rffi.CCHARP, + releasegil=ts_helper, + compilation_info=backend.eci) +def c_stdvector_valuetype(space, pystr): + cstr = rffi.str2charp(pystr) + result = _c_stdvector_valuetype(cstr) + rffi.free_charp(cstr) + if result: + return charp2str_free(space, result) + return "" +_c_stdvector_valuesize = rffi.llexternal( + "cppyy_stdvector_valuesize", + [rffi.CCHARP], rffi.SIZE_T, + releasegil=ts_helper, + compilation_info=backend.eci) +def c_stdvector_valuesize(space, pystr): + cstr = rffi.str2charp(pystr) + result = _c_stdvector_valuesize(cstr) + rffi.free_charp(cstr) + return result diff --git a/pypy/module/cppyy/capi/capi_types.py b/pypy/module/cppyy/capi/capi_types.py --- a/pypy/module/cppyy/capi/capi_types.py +++ b/pypy/module/cppyy/capi/capi_types.py @@ -18,5 +18,4 @@ C_INDEX_ARRAY = rffi.LONGP WLAVC_INDEX = rffi.LONG -C_METHPTRGETTER = lltype.FuncType([C_OBJECT], rffi.VOIDP) -C_METHPTRGETTER_PTR = lltype.Ptr(C_METHPTRGETTER) +C_FUNC_PTR = rffi.VOIDP diff --git a/pypy/module/cppyy/capi/cint_capi.py b/pypy/module/cppyy/capi/cint_capi.py deleted file mode 100644 --- a/pypy/module/cppyy/capi/cint_capi.py +++ /dev/null @@ -1,437 +0,0 @@ -import py, os, sys - -from pypy.interpreter.error import OperationError -from pypy.interpreter.gateway import interp2app, unwrap_spec -from pypy.interpreter.typedef import TypeDef -from pypy.interpreter.baseobjspace import W_Root - -from rpython.translator.tool.cbuild import ExternalCompilationInfo -from rpython.rtyper.lltypesystem import rffi, lltype -from rpython.rlib import libffi, rdynload -from rpython.tool.udir import udir - -from pypy.module.cppyy.capi.capi_types import C_OBJECT - - -__all__ = ['identify', 'std_string_name', 'eci', 'c_load_dictionary'] - -pkgpath = py.path.local(__file__).dirpath().join(os.pardir) -srcpath = pkgpath.join("src") -incpath = pkgpath.join("include") - -if os.environ.get("ROOTSYS"): - import commands - (stat, incdir) = commands.getstatusoutput("root-config --incdir") - if stat != 0: - rootincpath = [os.path.join(os.environ["ROOTSYS"], "include"), py.path.local(udir)] - rootlibpath = [os.path.join(os.environ["ROOTSYS"], "lib64"), os.path.join(os.environ["ROOTSYS"], "lib")] - else: - rootincpath = [incdir, py.path.local(udir)] - rootlibpath = commands.getoutput("root-config --libdir").split() -else: - rootincpath = [py.path.local(udir)] - rootlibpath = [] - -def identify(): - return 'CINT' - -ts_reflect = True -ts_call = True -ts_memory = False -ts_helper = False - -std_string_name = 'string' - -# force loading in global mode of core libraries, rather than linking with -# them as PyPy uses various version of dlopen in various places; note that -# this isn't going to fly on Windows (note that locking them in objects and -# calling dlclose in __del__ seems to come too late, so this'll do for now) -with rffi.scoped_str2charp('libCint.so') as ll_libname: - _cintdll = rdynload.dlopen(ll_libname, rdynload.RTLD_GLOBAL | rdynload.RTLD_NOW) -with rffi.scoped_str2charp('libCore.so') as ll_libname: - _coredll = rdynload.dlopen(ll_libname, rdynload.RTLD_GLOBAL | rdynload.RTLD_NOW) -with rffi.scoped_str2charp('libHist.so') as ll_libname: - _coredll = rdynload.dlopen(ll_libname, rdynload.RTLD_GLOBAL | rdynload.RTLD_NOW) - -eci = ExternalCompilationInfo( - separate_module_files=[srcpath.join("cintcwrapper.cxx")], - include_dirs=[incpath] + rootincpath, - includes=["cintcwrapper.h"], - library_dirs=rootlibpath, - libraries=["Hist", "Core", "Cint"], - use_cpp_linker=True, -) - -_c_load_dictionary = rffi.llexternal( - "cppyy_load_dictionary", - [rffi.CCHARP], rdynload.DLLHANDLE, - releasegil=False, - compilation_info=eci) - -def c_load_dictionary(name): - result = _c_load_dictionary(name) - # ignore result: libffi.CDLL(name) either returns a handle to the already - # open file, or will fail as well and produce a correctly formatted error - return libffi.CDLL(name) - - -# CINT-specific pythonizations =============================================== -_c_charp2TString = rffi.llexternal( - "cppyy_charp2TString", - [rffi.CCHARP], C_OBJECT, - releasegil=ts_helper, - compilation_info=eci) -def c_charp2TString(space, svalue): - with rffi.scoped_view_charp(svalue) as charp: - result = _c_charp2TString(charp) - return result -_c_TString2TString = rffi.llexternal( - "cppyy_TString2TString", - [C_OBJECT], C_OBJECT, - releasegil=ts_helper, - compilation_info=eci) -def c_TString2TString(space, cppobject): - return _c_TString2TString(cppobject) - -def _get_string_data(space, w_obj, m1, m2 = None): - from pypy.module.cppyy import interp_cppyy - obj = space.interp_w(interp_cppyy.W_CPPInstance, w_obj) - w_1 = obj.space.call_method(w_obj, m1) - if m2 is None: - return w_1 - return obj.space.call_method(w_1, m2) - -### TF1 ---------------------------------------------------------------------- -class State(object): - def __init__(self, space): - self.tfn_pyfuncs = [] - self.tfn_callbacks = [] - -_create_tf1 = rffi.llexternal( - "cppyy_create_tf1", - [rffi.CCHARP, rffi.ULONG, rffi.DOUBLE, rffi.DOUBLE, rffi.INT], C_OBJECT, - releasegil=False, - compilation_info=eci) - -@unwrap_spec(args_w='args_w') -def tf1_tf1(space, w_self, args_w): - """Pythonized version of TF1 constructor: - takes functions and callable objects, and allows a callback into them.""" - - from pypy.module.cppyy import interp_cppyy - tf1_class = interp_cppyy.scope_byname(space, "TF1") - - # expected signature: - # 1. (char* name, pyfunc, double xmin, double xmax, int npar = 0) - argc = len(args_w) - - try: - if argc < 4 or 5 < argc: - raise TypeError("wrong number of arguments") - - # first argument must be a name - funcname = space.str_w(args_w[0]) - - # last (optional) argument is number of parameters - npar = 0 - if argc == 5: npar = space.int_w(args_w[4]) - - # second argument must be a callable python object - w_callable = args_w[1] - if not space.is_true(space.callable(w_callable)): - raise TypeError("2nd argument is not a valid python callable") - - # generate a pointer to function - from pypy.module._cffi_backend import newtype, ctypefunc, func - - c_double = newtype.new_primitive_type(space, 'double') - c_doublep = newtype.new_pointer_type(space, c_double) - - # wrap the callable as the signature needs modifying - w_ifunc = interp_cppyy.get_interface_func(space, w_callable, npar) - - w_cfunc = ctypefunc.W_CTypeFunc(space, [c_doublep, c_doublep], c_double, False) - w_callback = func.callback(space, w_cfunc, w_ifunc, None) - funcaddr = rffi.cast(rffi.ULONG, w_callback.get_closure()) - - # so far, so good; leaves on issue: CINT is expecting a wrapper, but - # we need the overload that takes a function pointer, which is not in - # the dictionary, hence this helper: - newinst = _create_tf1(space.str_w(args_w[0]), funcaddr, - space.float_w(args_w[2]), space.float_w(args_w[3]), npar) - - # w_self is a null-ptr bound as TF1 - from pypy.module.cppyy.interp_cppyy import W_CPPInstance, memory_regulator - cppself = space.interp_w(W_CPPInstance, w_self, can_be_None=False) - cppself._rawobject = newinst - memory_regulator.register(cppself) - - # tie all the life times to the TF1 instance - space.setattr(w_self, space.wrap('_callback'), w_callback) - - # by definition for __init__ - return None - - except (OperationError, TypeError, IndexError) as e: - newargs_w = args_w[1:] # drop class - - # return control back to the original, unpythonized overload - ol = tf1_class.get_overload("TF1") - return ol.call(None, newargs_w) - -### TTree -------------------------------------------------------------------- -_ttree_Branch = rffi.llexternal( - "cppyy_ttree_Branch", - [rffi.VOIDP, rffi.CCHARP, rffi.CCHARP, rffi.VOIDP, rffi.INT, rffi.INT], C_OBJECT, - releasegil=False, - compilation_info=eci) - -@unwrap_spec(args_w='args_w') -def ttree_Branch(space, w_self, args_w): - """Pythonized version of TTree::Branch(): takes proxy objects and by-passes - the CINT-manual layer.""" - - from pypy.module.cppyy import interp_cppyy - tree_class = interp_cppyy.scope_byname(space, "TTree") - - # sigs to modify (and by-pass CINT): - # 1. (const char*, const char*, T**, Int_t=32000, Int_t=99) - # 2. (const char*, T**, Int_t=32000, Int_t=99) - argc = len(args_w) - - # basic error handling of wrong arguments is best left to the original call, - # so that error messages etc. remain consistent in appearance: the following - # block may raise TypeError or IndexError to break out anytime - - try: - if argc < 2 or 5 < argc: - raise TypeError("wrong number of arguments") - - tree = space.interp_w(interp_cppyy.W_CPPInstance, w_self, can_be_None=True) - if (tree is None) or (tree.cppclass != tree_class): - raise TypeError("not a TTree") - - # first argument must always always be cont char* - branchname = space.str_w(args_w[0]) - - # if args_w[1] is a classname, then case 1, else case 2 - try: - classname = space.str_w(args_w[1]) - addr_idx = 2 - w_address = args_w[addr_idx] - except (OperationError, TypeError): - addr_idx = 1 - w_address = args_w[addr_idx] - - bufsize, splitlevel = 32000, 99 - if addr_idx+1 < argc: bufsize = space.c_int_w(args_w[addr_idx+1]) - if addr_idx+2 < argc: splitlevel = space.c_int_w(args_w[addr_idx+2]) - - # now retrieve the W_CPPInstance and build other stub arguments - space = tree.space # holds the class cache in State - cppinstance = space.interp_w(interp_cppyy.W_CPPInstance, w_address) - address = rffi.cast(rffi.VOIDP, cppinstance.get_rawobject()) - klassname = cppinstance.cppclass.full_name() - vtree = rffi.cast(rffi.VOIDP, tree.get_rawobject()) - - # call the helper stub to by-pass CINT - vbranch = _ttree_Branch(vtree, branchname, klassname, address, bufsize, splitlevel) - branch_class = interp_cppyy.scope_byname(space, "TBranch") - w_branch = interp_cppyy.wrap_cppobject(space, vbranch, branch_class) - return w_branch - except (OperationError, TypeError, IndexError): - pass - - # return control back to the original, unpythonized overload - ol = tree_class.get_overload("Branch") - return ol.call(w_self, args_w) - -def activate_branch(space, w_branch): - w_branches = space.call_method(w_branch, "GetListOfBranches") - for i in range(space.r_longlong_w(space.call_method(w_branches, "GetEntriesFast"))): - w_b = space.call_method(w_branches, "At", space.wrap(i)) - activate_branch(space, w_b) - space.call_method(w_branch, "SetStatus", space.wrap(1)) - space.call_method(w_branch, "ResetReadEntry") - -c_ttree_GetEntry = rffi.llexternal( - "cppyy_ttree_GetEntry", - [rffi.VOIDP, rffi.LONGLONG], rffi.LONGLONG, - releasegil=False, - compilation_info=eci) - -@unwrap_spec(args_w='args_w') -def ttree_getattr(space, w_self, args_w): - """Specialized __getattr__ for TTree's that allows switching on/off the - reading of individual branchs.""" - - from pypy.module.cppyy import interp_cppyy - tree = space.interp_w(interp_cppyy.W_CPPInstance, w_self) - - space = tree.space # holds the class cache in State - - # prevent recursion - attr = space.str_w(args_w[0]) - if attr and attr[0] == '_': - raise OperationError(space.w_AttributeError, args_w[0]) - - # try the saved cdata (for builtin types) - try: - w_cdata = space.getattr(w_self, space.wrap('_'+attr)) - from pypy.module._cffi_backend import cdataobj - cdata = space.interp_w(cdataobj.W_CData, w_cdata, can_be_None=False) - return cdata.convert_to_object() - except OperationError: - pass - - # setup branch as a data member and enable it for reading - w_branch = space.call_method(w_self, "GetBranch", args_w[0]) - if not space.is_true(w_branch): - raise OperationError(space.w_AttributeError, args_w[0]) - activate_branch(space, w_branch) - - # figure out from where we're reading - entry = space.r_longlong_w(space.call_method(w_self, "GetReadEntry")) - if entry == -1: - entry = 0 - - # setup cache structure - w_klassname = space.call_method(w_branch, "GetClassName") - if space.is_true(w_klassname): - # some instance - klass = interp_cppyy.scope_byname(space, space.str_w(w_klassname)) - w_obj = klass.construct() - # 0x10000 = kDeleteObject; reset because we own the object - space.call_method(w_branch, "ResetBit", space.wrap(0x10000)) - space.call_method(w_branch, "SetObject", w_obj) - space.call_method(w_branch, "GetEntry", space.wrap(entry)) - space.setattr(w_self, args_w[0], w_obj) - return w_obj - else: - # builtin data - w_leaf = space.call_method(w_self, "GetLeaf", args_w[0]) - space.call_method(w_branch, "GetEntry", space.wrap(entry)) - - # location - w_address = space.call_method(w_leaf, "GetValuePointer") - buf = space.getarg_w('s*', w_address) - from pypy.module._rawffi import buffer - assert isinstance(buf, buffer.RawFFIBuffer) - address = rffi.cast(rffi.CCHARP, buf.datainstance.ll_buffer) - - # placeholder - w_typename = space.call_method(w_leaf, "GetTypeName" ) - from pypy.module.cppyy import capi - typename = capi.c_resolve_name(space, space.str_w(w_typename)) - if typename == 'bool': typename = '_Bool' - w_address = space.call_method(w_leaf, "GetValuePointer") - from pypy.module._cffi_backend import cdataobj, newtype - cdata = cdataobj.W_CData(space, address, newtype.new_primitive_type(space, typename)) - - # cache result - space.setattr(w_self, space.wrap('_'+attr), space.wrap(cdata)) - return space.getattr(w_self, args_w[0]) - -class W_TTreeIter(W_Root): - def __init__(self, space, w_tree): - from pypy.module.cppyy import interp_cppyy - tree = space.interp_w(interp_cppyy.W_CPPInstance, w_tree) - self.vtree = rffi.cast(rffi.VOIDP, tree.get_cppthis(tree.cppclass)) - self.w_tree = w_tree - - self.current = 0 - self.maxentry = space.r_longlong_w(space.call_method(w_tree, "GetEntriesFast")) - - space = self.space = tree.space # holds the class cache in State - space.call_method(w_tree, "SetBranchStatus", space.wrap("*"), space.wrap(0)) - - def iter_w(self): - return self.space.wrap(self) - - def next_w(self): - if self.current == self.maxentry: - raise OperationError(self.space.w_StopIteration, self.space.w_None) - # TODO: check bytes read? - c_ttree_GetEntry(self.vtree, self.current) - self.current += 1 - return self.w_tree - -W_TTreeIter.typedef = TypeDef( - 'TTreeIter', - __iter__ = interp2app(W_TTreeIter.iter_w), - next = interp2app(W_TTreeIter.next_w), -) - -def ttree_iter(space, w_self): - """Allow iteration over TTree's. Also initializes branch data members and - sets addresses, if needed.""" - w_treeiter = W_TTreeIter(space, w_self) - return w_treeiter - -# setup pythonizations for later use at run-time -_pythonizations = {} -def register_pythonizations(space): - "NOT_RPYTHON" - - allfuncs = [ - - ### TF1 - tf1_tf1, - - ### TTree - ttree_Branch, ttree_iter, ttree_getattr, - ] - - for f in allfuncs: - _pythonizations[f.__name__] = space.wrap(interp2app(f)) - -def _method_alias(space, w_pycppclass, m1, m2): - space.setattr(w_pycppclass, space.wrap(m1), - space.getattr(w_pycppclass, space.wrap(m2))) - -# callback coming in when app-level bound classes have been created -def pythonize(space, name, w_pycppclass): - - if name == "TCollection": - _method_alias(space, w_pycppclass, "append", "Add") - _method_alias(space, w_pycppclass, "__len__", "GetSize") - - elif name == "TF1": - space.setattr(w_pycppclass, space.wrap("__init__"), _pythonizations["tf1_tf1"]) - - elif name == "TFile": - _method_alias(space, w_pycppclass, "__getattr__", "Get") - - elif name == "TObjString": - _method_alias(space, w_pycppclass, "__str__", "GetName") - _method_alias(space, w_pycppclass, "_cppyy_as_builtin", "GetString") - - elif name == "TString": - _method_alias(space, w_pycppclass, "__str__", "Data") - _method_alias(space, w_pycppclass, "__len__", "Length") - _method_alias(space, w_pycppclass, "__cmp__", "CompareTo") - _method_alias(space, w_pycppclass, "_cppyy_as_builtin", "Data") - - elif name == "TTree": - _method_alias(space, w_pycppclass, "_unpythonized_Branch", "Branch") - - space.setattr(w_pycppclass, space.wrap("Branch"), _pythonizations["ttree_Branch"]) - space.setattr(w_pycppclass, space.wrap("__iter__"), _pythonizations["ttree_iter"]) - space.setattr(w_pycppclass, space.wrap("__getattr__"), _pythonizations["ttree_getattr"]) - - elif name[0:8] == "TVectorT": # TVectorT<> template - _method_alias(space, w_pycppclass, "__len__", "GetNoElements") - -# destruction callback (needs better solution, but this is for CINT -# only and should not appear outside of ROOT-specific uses) -from pypy.module.cpyext.api import cpython_api, CANNOT_FAIL - -@cpython_api([rffi.VOIDP], lltype.Void, error=CANNOT_FAIL) -def _Py_cppyy_recursive_remove(space, cppobject): - from pypy.module.cppyy.interp_cppyy import memory_regulator - from pypy.module.cppyy.capi import C_OBJECT, C_NULL_OBJECT - - obj = memory_regulator.retrieve(rffi.cast(C_OBJECT, cppobject)) - if obj is not None: - memory_regulator.unregister(obj) - obj._rawobject = C_NULL_OBJECT diff --git a/pypy/module/cppyy/capi/cling_capi.py b/pypy/module/cppyy/capi/cling_capi.py --- a/pypy/module/cppyy/capi/cling_capi.py +++ b/pypy/module/cppyy/capi/cling_capi.py @@ -1,8 +1,17 @@ import py, os +from pypy.objspace.std.iterobject import W_AbstractSeqIterObject + +from pypy.interpreter.error import OperationError +from pypy.interpreter.gateway import interp2app + from rpython.translator.tool.cbuild import ExternalCompilationInfo -from rpython.rtyper.lltypesystem import rffi -from rpython.rlib import libffi, rdynload +from rpython.rtyper.lltypesystem import rffi, lltype +from rpython.rlib.rarithmetic import intmask +from rpython.rlib import jit, libffi, rdynload + +from pypy.module._rawffi.array import W_ArrayInstance +from pypy.module.cppyy.capi.capi_types import C_OBJECT __all__ = ['identify', 'std_string_name', 'eci', 'c_load_dictionary'] @@ -16,7 +25,8 @@ if os.environ.get("ROOTSYS"): if config_stat != 0: # presumably Reflex-only rootincpath = [os.path.join(os.environ["ROOTSYS"], "interpreter/cling/include"), - os.path.join(os.environ["ROOTSYS"], "interpreter/llvm/inst/include")] + os.path.join(os.environ["ROOTSYS"], "interpreter/llvm/inst/include"), + os.path.join(os.environ["ROOTSYS"], "include"),] rootlibpath = [os.path.join(os.environ["ROOTSYS"], "lib64"), os.path.join(os.environ["ROOTSYS"], "lib")] else: rootincpath = [incdir] @@ -39,13 +49,21 @@ std_string_name = 'std::basic_string<char>' +# force loading (and exposure) of libCore symbols +with rffi.scoped_str2charp('libCore.so') as ll_libname: + _coredll = rdynload.dlopen(ll_libname, rdynload.RTLD_GLOBAL | rdynload.RTLD_NOW) + +# require local translator path to pickup common defs +from rpython.translator import cdir +translator_c_dir = py.path.local(cdir) + eci = ExternalCompilationInfo( separate_module_files=[srcpath.join("clingcwrapper.cxx")], - include_dirs=[incpath] + rootincpath, + include_dirs=[incpath, translator_c_dir] + rootincpath, includes=["clingcwrapper.h"], library_dirs=rootlibpath, libraries=["Cling"], - compile_extra=["-fno-strict-aliasing"], + compile_extra=["-fno-strict-aliasing", "-std=c++11"], use_cpp_linker=True, ) @@ -59,11 +77,120 @@ pch = _c_load_dictionary(name) return pch +_c_stdstring2charp = rffi.llexternal( + "cppyy_stdstring2charp", + [C_OBJECT, rffi.SIZE_TP], rffi.CCHARP, + releasegil=ts_helper, + compilation_info=eci) +def c_stdstring2charp(space, cppstr): + sz = lltype.malloc(rffi.SIZE_TP.TO, 1, flavor='raw') + try: + cstr = _c_stdstring2charp(cppstr, sz) + cstr_len = intmask(sz[0]) + finally: + lltype.free(sz, flavor='raw') + return rffi.charpsize2str(cstr, cstr_len) -# Cling-specific pythonizations +# TODO: factor these out ... +# pythonizations + +# +# std::string behavior +def stdstring_c_str(space, w_self): + """Return a python string taking into account \0""" + + from pypy.module.cppyy import interp_cppyy + cppstr = space.interp_w(interp_cppyy.W_CPPInstance, w_self, can_be_None=False) + return space.wrap(c_stdstring2charp(space, cppstr._rawobject)) + +# +# std::vector behavior +class W_STLVectorIter(W_AbstractSeqIterObject): + _immutable_fields_ = ['overload', 'len']#'data', 'converter', 'len', 'stride', 'vector'] + + def __init__(self, space, w_vector): + W_AbstractSeqIterObject.__init__(self, w_vector) + # TODO: this should live in rpythonize.py or something so that the + # imports can move to the top w/o getting circles + from pypy.module.cppyy import interp_cppyy + assert isinstance(w_vector, interp_cppyy.W_CPPInstance) + vector = space.interp_w(interp_cppyy.W_CPPInstance, w_vector) + self.overload = vector.cppclass.get_overload("__getitem__") + + from pypy.module.cppyy import capi + v_type = capi.c_stdvector_valuetype(space, vector.cppclass.name) + v_size = capi.c_stdvector_valuesize(space, vector.cppclass.name) + + if not v_type or not v_size: + raise NotImplementedError # fallback on getitem + + w_arr = vector.cppclass.get_overload("data").call(w_vector, []) + arr = space.interp_w(W_ArrayInstance, w_arr, can_be_None=True) + if not arr: + raise OperationError(space.w_StopIteration, space.w_None) + + self.data = rffi.cast(rffi.VOIDP, space.uint_w(arr.getbuffer(space))) + + from pypy.module.cppyy import converter + self.converter = converter.get_converter(space, v_type, '') + self.len = space.uint_w(vector.cppclass.get_overload("size").call(w_vector, [])) + self.stride = v_size + + def descr_next(self, space): + if self.w_seq is None: + raise OperationError(space.w_StopIteration, space.w_None) + if self.len <= self.index: + self.w_seq = None + raise OperationError(space.w_StopIteration, space.w_None) + try: + from pypy.module.cppyy import capi # TODO: refector + offset = capi.direct_ptradd(rffi.cast(C_OBJECT, self.data), self.index*self.stride) + w_item = self.converter.from_memory(space, space.w_None, space.w_None, offset) + except OperationError as e: + self.w_seq = None + if not e.match(space, space.w_IndexError): + raise + raise OperationError(space.w_StopIteration, space.w_None) + self.index += 1 + return w_item + +def stdvector_iter(space, w_self): + return W_STLVectorIter(space, w_self) + +# setup pythonizations for later use at run-time +_pythonizations = {} def register_pythonizations(space): "NOT_RPYTHON" - pass + + allfuncs = [ + + ### std::string + stdstring_c_str, + + ### std::vector + stdvector_iter, + + ] + + for f in allfuncs: + _pythonizations[f.__name__] = space.wrap(interp2app(f)) + +def _method_alias(space, w_pycppclass, m1, m2): + space.setattr(w_pycppclass, space.wrap(m1), + space.getattr(w_pycppclass, space.wrap(m2))) def pythonize(space, name, w_pycppclass): - pass + if name == "string": + space.setattr(w_pycppclass, space.wrap("c_str"), _pythonizations["stdstring_c_str"]) + _method_alias(space, w_pycppclass, "_cppyy_as_builtin", "c_str") + _method_alias(space, w_pycppclass, "__str__", "c_str") + + if "vector" in name[:11]: # len('std::vector') == 11 + from pypy.module.cppyy import capi + v_type = capi.c_stdvector_valuetype(space, name) + if v_type: + space.setattr(w_pycppclass, space.wrap("value_type"), space.wrap(v_type)) + v_size = capi.c_stdvector_valuesize(space, name) + if v_size: + space.setattr(w_pycppclass, space.wrap("value_size"), space.wrap(v_size)) + space.setattr(w_pycppclass, space.wrap("__iter__"), _pythonizations["stdvector_iter"]) diff --git a/pypy/module/cppyy/capi/loadable_capi.py b/pypy/module/cppyy/capi/loadable_capi.py --- a/pypy/module/cppyy/capi/loadable_capi.py +++ b/pypy/module/cppyy/capi/loadable_capi.py @@ -1,14 +1,18 @@ from rpython.rtyper.lltypesystem import rffi, lltype +from rpython.rlib.rarithmetic import intmask from rpython.rlib import jit, jit_libffi, libffi, rdynload, objectmodel from rpython.rlib.rarithmetic import r_singlefloat from rpython.tool import leakfinder +from pypy.interpreter.gateway import interp2app from pypy.interpreter.error import oefmt from pypy.module._cffi_backend import ctypefunc, ctypeprim, cdataobj, misc +from pypy.module._cffi_backend import newtype +from pypy.module.cppyy import ffitypes from pypy.module.cppyy.capi.capi_types import C_SCOPE, C_TYPE, C_OBJECT,\ - C_METHOD, C_INDEX, C_INDEX_ARRAY, WLAVC_INDEX, C_METHPTRGETTER_PTR + C_METHOD, C_INDEX, C_INDEX_ARRAY, WLAVC_INDEX, C_FUNC_PTR reflection_library = 'libcppyy_backend.so' @@ -21,11 +25,32 @@ class _Arg: # poor man's union _immutable_ = True - def __init__(self, h = 0, l = -1, s = '', vp = rffi.cast(rffi.VOIDP, 0)): + def __init__(self, tc, h = 0, l = -1, s = '', p = rffi.cast(rffi.VOIDP, 0)): + self.tc = tc self._handle = h self._long = l self._string = s - self._voidp = vp + self._voidp = p + +class _ArgH(_Arg): + _immutable_ = True + def __init__(self, val): + _Arg.__init__(self, 'h', h = val) + +class _ArgL(_Arg): + _immutable_ = True + def __init__(self, val): + _Arg.__init__(self, 'l', l = val) + +class _ArgS(_Arg): + _immutable_ = True + def __init__(self, val): + _Arg.__init__(self, 's', s = val) + +class _ArgP(_Arg): + _immutable_ = True + def __init__(self, val): + _Arg.__init__(self, 'p', p = val) # For the loadable CAPI, the calls start and end in RPython. Therefore, the standard # _call of W_CTypeFunc, which expects wrapped objects, does not quite work: some @@ -55,14 +80,18 @@ argtype = self.fargs[i] # the following is clumsy, but the data types used as arguments are # very limited, so it'll do for now - if isinstance(argtype, ctypeprim.W_CTypePrimitiveSigned): + if obj.tc == 'l': + assert isinstance(argtype, ctypeprim.W_CTypePrimitiveSigned) misc.write_raw_signed_data(data, rffi.cast(rffi.LONG, obj._long), argtype.size) - elif isinstance(argtype, ctypeprim.W_CTypePrimitiveUnsigned): + elif obj.tc == 'h': + assert isinstance(argtype, ctypeprim.W_CTypePrimitiveUnsigned) misc.write_raw_unsigned_data(data, rffi.cast(rffi.ULONG, obj._handle), argtype.size) - elif obj._voidp != rffi.cast(rffi.VOIDP, 0): + elif obj.tc == 'p': + assert obj._voidp != rffi.cast(rffi.VOIDP, 0) data = rffi.cast(rffi.VOIDPP, data) data[0] = obj._voidp else: # only other use is sring + assert obj.tc == 's' n = len(obj._string) assert raw_string == rffi.cast(rffi.CCHARP, 0) # XXX could use rffi.get_nonmovingbuffer_final_null() @@ -89,35 +118,36 @@ self.library = None self.capi_calls = {} - import pypy.module._cffi_backend.newtype as nt + nt = newtype # module from _cffi_backend + state = space.fromcache(ffitypes.State) # factored out common types # TODO: the following need to match up with the globally defined C_XYZ low-level # types (see capi/__init__.py), but by using strings here, that isn't guaranteed - c_opaque_ptr = nt.new_primitive_type(space, 'unsigned long') + c_opaque_ptr = state.c_ulong - c_scope = c_opaque_ptr - c_type = c_scope - c_object = c_opaque_ptr - c_method = c_opaque_ptr - c_index = nt.new_primitive_type(space, 'long') + c_scope = c_opaque_ptr + c_type = c_scope + c_object = c_opaque_ptr + c_method = c_opaque_ptr + c_index = state.c_long + c_index_array = state.c_voidp - c_void = nt.new_void_type(space) - c_char = nt.new_primitive_type(space, 'char') - c_uchar = nt.new_primitive_type(space, 'unsigned char') - c_short = nt.new_primitive_type(space, 'short') - c_int = nt.new_primitive_type(space, 'int') - c_long = nt.new_primitive_type(space, 'long') - c_llong = nt.new_primitive_type(space, 'long long') - c_ullong = nt.new_primitive_type(space, 'unsigned long long') - c_float = nt.new_primitive_type(space, 'float') - c_double = nt.new_primitive_type(space, 'double') + c_void = state.c_void + c_char = state.c_char + c_uchar = state.c_uchar + c_short = state.c_short + c_int = state.c_int + c_long = state.c_long + c_llong = state.c_llong + c_ullong = state.c_ullong + c_float = state.c_float + c_double = state.c_double + c_ldouble = state.c_ldouble - c_ccharp = nt.new_pointer_type(space, c_char) - c_index_array = nt.new_pointer_type(space, c_void) + c_ccharp = state.c_ccharp + c_voidp = state.c_voidp - c_voidp = nt.new_pointer_type(space, c_void) c_size_t = nt.new_primitive_type(space, 'size_t') - c_ptrdiff_t = nt.new_primitive_type(space, 'ptrdiff_t') self.capi_call_ifaces = { @@ -127,7 +157,6 @@ 'resolve_name' : ([c_ccharp], c_ccharp), 'get_scope' : ([c_ccharp], c_scope), - 'get_template' : ([c_ccharp], c_type), 'actual_class' : ([c_type, c_object], c_type), # memory management @@ -146,14 +175,16 @@ 'call_ll' : ([c_method, c_object, c_int, c_voidp], c_llong), 'call_f' : ([c_method, c_object, c_int, c_voidp], c_float), 'call_d' : ([c_method, c_object, c_int, c_voidp], c_double), + 'call_ld' : ([c_method, c_object, c_int, c_voidp], c_ldouble), 'call_r' : ([c_method, c_object, c_int, c_voidp], c_voidp), - 'call_s' : ([c_method, c_object, c_int, c_voidp], c_ccharp), + # call_s actually takes an size_t* as last parameter, but this will do + 'call_s' : ([c_method, c_object, c_int, c_voidp, c_voidp], c_ccharp), 'constructor' : ([c_method, c_object, c_int, c_voidp], c_object), 'call_o' : ([c_method, c_object, c_int, c_voidp, c_type], c_object), - 'get_methptr_getter' : ([c_scope, c_index], c_voidp), # TODO: verify + 'get_function_address' : ([c_scope, c_index], c_voidp), # TODO: verify # handling of function argument buffer 'allocate_function_args' : ([c_int], c_voidp), @@ -163,6 +194,8 @@ # scope reflection information 'is_namespace' : ([c_scope], c_int), + 'is_template' : ([c_ccharp], c_int), + 'is_abstract' : ([c_type], c_int), 'is_enum' : ([c_ccharp], c_int), # type/class reflection information @@ -216,8 +249,14 @@ 'strtoull' : ([c_ccharp], c_ullong), 'free' : ([c_voidp], c_void), - 'charp2stdstring' : ([c_ccharp], c_object), + 'charp2stdstring' : ([c_ccharp, c_size_t], c_object), + #stdstring2charp actually takes an size_t* as last parameter, but this will do + 'stdstring2charp' : ([c_object, c_voidp], c_ccharp), 'stdstring2stdstring' : ([c_object], c_object), + + 'stdvector_valuetype' : ([c_ccharp], c_ccharp), + 'stdvector_valuesize' : ([c_ccharp], c_size_t), + } # size/offset are backend-specific but fixed after load @@ -277,87 +316,99 @@ ptr = w_cdata.unsafe_escaping_ptr() return rffi.cast(rffi.VOIDP, ptr) +def _cdata_to_ccharp(space, w_cdata): + ptr = _cdata_to_ptr(space, w_cdata) # see above ... something better? + return rffi.cast(rffi.CCHARP, ptr) + def c_load_dictionary(name): return libffi.CDLL(name) # name to opaque C++ scope representation ------------------------------------ def c_num_scopes(space, cppscope): - return space.int_w(call_capi(space, 'num_scopes', [_Arg(h=cppscope.handle)])) + return space.int_w(call_capi(space, 'num_scopes', [_ArgH(cppscope.handle)])) def c_scope_name(space, cppscope, iscope): - args = [_Arg(h=cppscope.handle), _Arg(l=iscope)] + args = [_ArgH(cppscope.handle), _ArgL(iscope)] return charp2str_free(space, call_capi(space, 'scope_name', args)) def c_resolve_name(space, name): - return charp2str_free(space, call_capi(space, 'resolve_name', [_Arg(s=name)])) + return charp2str_free(space, call_capi(space, 'resolve_name', [_ArgS(name)])) def c_get_scope_opaque(space, name): - return rffi.cast(C_SCOPE, space.uint_w(call_capi(space, 'get_scope', [_Arg(s=name)]))) -def c_get_template(space, name): - return rffi.cast(C_TYPE, space.uint_w(call_capi(space, 'get_template', [_Arg(s=name)]))) + return rffi.cast(C_SCOPE, space.uint_w(call_capi(space, 'get_scope', [_ArgS(name)]))) def c_actual_class(space, cppclass, cppobj): - args = [_Arg(h=cppclass.handle), _Arg(h=cppobj)] + args = [_ArgH(cppclass.handle), _ArgH(cppobj)] return rffi.cast(C_TYPE, space.uint_w(call_capi(space, 'actual_class', args))) # memory management ---------------------------------------------------------- def c_allocate(space, cppclass): - return _cdata_to_cobject(space, call_capi(space, 'allocate', [_Arg(h=cppclass.handle)])) + return _cdata_to_cobject(space, call_capi(space, 'allocate', [_ArgH(cppclass.handle)])) def c_deallocate(space, cppclass, cppobject): - call_capi(space, 'deallocate', [_Arg(h=cppclass.handle), _Arg(h=cppobject)]) + call_capi(space, 'deallocate', [_ArgH(cppclass.handle), _ArgH(cppobject)]) def c_destruct(space, cppclass, cppobject): - call_capi(space, 'destruct', [_Arg(h=cppclass.handle), _Arg(h=cppobject)]) + call_capi(space, 'destruct', [_ArgH(cppclass.handle), _ArgH(cppobject)]) # method/function dispatching ------------------------------------------------ def c_call_v(space, cppmethod, cppobject, nargs, cargs): - args = [_Arg(h=cppmethod), _Arg(h=cppobject), _Arg(l=nargs), _Arg(vp=cargs)] + args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs)] call_capi(space, 'call_v', args) def c_call_b(space, cppmethod, cppobject, nargs, cargs): - args = [_Arg(h=cppmethod), _Arg(h=cppobject), _Arg(l=nargs), _Arg(vp=cargs)] + args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs)] return rffi.cast(rffi.UCHAR, space.c_uint_w(call_capi(space, 'call_b', args))) def c_call_c(space, cppmethod, cppobject, nargs, cargs): - args = [_Arg(h=cppmethod), _Arg(h=cppobject), _Arg(l=nargs), _Arg(vp=cargs)] + args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs)] return rffi.cast(rffi.CHAR, space.str_w(call_capi(space, 'call_c', args))[0]) def c_call_h(space, cppmethod, cppobject, nargs, cargs): - args = [_Arg(h=cppmethod), _Arg(h=cppobject), _Arg(l=nargs), _Arg(vp=cargs)] + args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs)] return rffi.cast(rffi.SHORT, space.int_w(call_capi(space, 'call_h', args))) def c_call_i(space, cppmethod, cppobject, nargs, cargs): - args = [_Arg(h=cppmethod), _Arg(h=cppobject), _Arg(l=nargs), _Arg(vp=cargs)] + args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs)] return rffi.cast(rffi.INT, space.c_int_w(call_capi(space, 'call_i', args))) def c_call_l(space, cppmethod, cppobject, nargs, cargs): - args = [_Arg(h=cppmethod), _Arg(h=cppobject), _Arg(l=nargs), _Arg(vp=cargs)] + args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs)] return rffi.cast(rffi.LONG, space.int_w(call_capi(space, 'call_l', args))) def c_call_ll(space, cppmethod, cppobject, nargs, cargs): - args = [_Arg(h=cppmethod), _Arg(h=cppobject), _Arg(l=nargs), _Arg(vp=cargs)] + args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs)] return rffi.cast(rffi.LONGLONG, space.r_longlong_w(call_capi(space, 'call_ll', args))) def c_call_f(space, cppmethod, cppobject, nargs, cargs): - args = [_Arg(h=cppmethod), _Arg(h=cppobject), _Arg(l=nargs), _Arg(vp=cargs)] + args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs)] return rffi.cast(rffi.FLOAT, r_singlefloat(space.float_w(call_capi(space, 'call_f', args)))) def c_call_d(space, cppmethod, cppobject, nargs, cargs): - args = [_Arg(h=cppmethod), _Arg(h=cppobject), _Arg(l=nargs), _Arg(vp=cargs)] + args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs)] return rffi.cast(rffi.DOUBLE, space.float_w(call_capi(space, 'call_d', args))) +def c_call_ld(space, cppmethod, cppobject, nargs, cargs): + args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs)] + return rffi.cast(rffi.LONGDOUBLE, space.float_w(call_capi(space, 'call_ld', args))) def c_call_r(space, cppmethod, cppobject, nargs, cargs): - args = [_Arg(h=cppmethod), _Arg(h=cppobject), _Arg(l=nargs), _Arg(vp=cargs)] + args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs)] return _cdata_to_ptr(space, call_capi(space, 'call_r', args)) def c_call_s(space, cppmethod, cppobject, nargs, cargs): - args = [_Arg(h=cppmethod), _Arg(h=cppobject), _Arg(l=nargs), _Arg(vp=cargs)] - return call_capi(space, 'call_s', args) + length = lltype.malloc(rffi.SIZE_TP.TO, 1, flavor='raw') + try: + w_cstr = call_capi(space, 'call_s', + [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs), + _ArgP(rffi.cast(rffi.VOIDP, length))]) + cstr_len = intmask(length[0]) + finally: + lltype.free(length, flavor='raw') + return _cdata_to_ccharp(space, w_cstr), cstr_len def c_constructor(space, cppmethod, cppobject, nargs, cargs): - args = [_Arg(h=cppmethod), _Arg(h=cppobject), _Arg(l=nargs), _Arg(vp=cargs)] + args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs)] return _cdata_to_cobject(space, call_capi(space, 'constructor', args)) def c_call_o(space, cppmethod, cppobject, nargs, cargs, cppclass): - args = [_Arg(h=cppmethod), _Arg(h=cppobject), _Arg(l=nargs), _Arg(vp=cargs), _Arg(h=cppclass.handle)] + args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs), _ArgH(cppclass.handle)] return _cdata_to_cobject(space, call_capi(space, 'call_o', args)) -def c_get_methptr_getter(space, cppscope, index): - args = [_Arg(h=cppscope.handle), _Arg(l=index)] - return rffi.cast(C_METHPTRGETTER_PTR, - _cdata_to_ptr(space, call_capi(space, 'get_methptr_getter', args))) +def c_get_function_address(space, cppscope, index): + args = [_ArgH(cppscope.handle), _ArgL(index)] + return rffi.cast(C_FUNC_PTR, + _cdata_to_ptr(space, call_capi(space, 'get_function_address', args))) # handling of function argument buffer --------------------------------------- def c_allocate_function_args(space, size): - return _cdata_to_ptr(space, call_capi(space, 'allocate_function_args', [_Arg(l=size)])) + return _cdata_to_ptr(space, call_capi(space, 'allocate_function_args', [_ArgL(size)])) def c_deallocate_function_args(space, cargs): - call_capi(space, 'deallocate_function_args', [_Arg(vp=cargs)]) + call_capi(space, 'deallocate_function_args', [_ArgP(cargs)]) def c_function_arg_sizeof(space): state = space.fromcache(State) return state.c_sizeof_farg @@ -367,30 +418,34 @@ # scope reflection information ----------------------------------------------- def c_is_namespace(space, scope): - return space.bool_w(call_capi(space, 'is_namespace', [_Arg(h=scope)])) + return space.bool_w(call_capi(space, 'is_namespace', [_ArgH(scope)])) +def c_is_template(space, name): + return space.bool_w(call_capi(space, 'is_template', [_ArgS(name)])) +def c_is_abstract(space, cpptype): + return space.bool_w(call_capi(space, 'is_abstract', [_ArgH(cpptype)])) def c_is_enum(space, name): - return space.bool_w(call_capi(space, 'is_enum', [_Arg(s=name)])) + return space.bool_w(call_capi(space, 'is_enum', [_ArgS(name)])) # type/class reflection information ------------------------------------------ def c_final_name(space, cpptype): - return charp2str_free(space, call_capi(space, 'final_name', [_Arg(h=cpptype)])) + return charp2str_free(space, call_capi(space, 'final_name', [_ArgH(cpptype)])) def c_scoped_final_name(space, cpptype): - return charp2str_free(space, call_capi(space, 'scoped_final_name', [_Arg(h=cpptype)])) + return charp2str_free(space, call_capi(space, 'scoped_final_name', [_ArgH(cpptype)])) def c_has_complex_hierarchy(space, handle): - return space.bool_w(call_capi(space, 'has_complex_hierarchy', [_Arg(h=handle)])) + return space.bool_w(call_capi(space, 'has_complex_hierarchy', [_ArgH(handle)])) def c_num_bases(space, cppclass): - return space.int_w(call_capi(space, 'num_bases', [_Arg(h=cppclass.handle)])) + return space.int_w(call_capi(space, 'num_bases', [_ArgH(cppclass.handle)])) def c_base_name(space, cppclass, base_index): - args = [_Arg(h=cppclass.handle), _Arg(l=base_index)] + args = [_ArgH(cppclass.handle), _ArgL(base_index)] return charp2str_free(space, call_capi(space, 'base_name', args)) def c_is_subtype(space, derived, base): jit.promote(base) if derived == base: return bool(1) - return space.bool_w(call_capi(space, 'is_subtype', [_Arg(h=derived.handle), _Arg(h=base.handle)])) + return space.bool_w(call_capi(space, 'is_subtype', [_ArgH(derived.handle), _ArgH(base.handle)])) def _c_base_offset(space, derived_h, base_h, address, direction): - args = [_Arg(h=derived_h), _Arg(h=base_h), _Arg(h=address), _Arg(l=direction)] + args = [_ArgH(derived_h), _ArgH(base_h), _ArgH(address), _ArgL(direction)] return _cdata_to_ptrdiff_t(space, call_capi(space, 'base_offset', args)) def c_base_offset(space, derived, base, address, direction): if derived == base: @@ -401,13 +456,13 @@ # method/function reflection information ------------------------------------- def c_num_methods(space, cppscope): - args = [_Arg(h=cppscope.handle)] + args = [_ArgH(cppscope.handle)] return space.int_w(call_capi(space, 'num_methods', args)) def c_method_index_at(space, cppscope, imethod): - args = [_Arg(h=cppscope.handle), _Arg(l=imethod)] + args = [_ArgH(cppscope.handle), _ArgL(imethod)] return space.int_w(call_capi(space, 'method_index_at', args)) def c_method_indices_from_name(space, cppscope, name): - args = [_Arg(h=cppscope.handle), _Arg(s=name)] + args = [_ArgH(cppscope.handle), _ArgS(name)] indices = rffi.cast(C_INDEX_ARRAY, _cdata_to_ptr(space, call_capi(space, 'method_indices_from_name', args))) if not indices: @@ -423,91 +478,91 @@ return py_indices def c_method_name(space, cppscope, index): - args = [_Arg(h=cppscope.handle), _Arg(l=index)] + args = [_ArgH(cppscope.handle), _ArgL(index)] return charp2str_free(space, call_capi(space, 'method_name', args)) def c_method_result_type(space, cppscope, index): - args = [_Arg(h=cppscope.handle), _Arg(l=index)] + args = [_ArgH(cppscope.handle), _ArgL(index)] return charp2str_free(space, call_capi(space, 'method_result_type', args)) def c_method_num_args(space, cppscope, index): - args = [_Arg(h=cppscope.handle), _Arg(l=index)] + args = [_ArgH(cppscope.handle), _ArgL(index)] return space.int_w(call_capi(space, 'method_num_args', args)) def c_method_req_args(space, cppscope, index): - args = [_Arg(h=cppscope.handle), _Arg(l=index)] + args = [_ArgH(cppscope.handle), _ArgL(index)] return space.int_w(call_capi(space, 'method_req_args', args)) def c_method_arg_type(space, cppscope, index, arg_index): - args = [_Arg(h=cppscope.handle), _Arg(l=index), _Arg(l=arg_index)] + args = [_ArgH(cppscope.handle), _ArgL(index), _ArgL(arg_index)] return charp2str_free(space, call_capi(space, 'method_arg_type', args)) def c_method_arg_default(space, cppscope, index, arg_index): - args = [_Arg(h=cppscope.handle), _Arg(l=index), _Arg(l=arg_index)] + args = [_ArgH(cppscope.handle), _ArgL(index), _ArgL(arg_index)] return charp2str_free(space, call_capi(space, 'method_arg_default', args)) def c_method_signature(space, cppscope, index): - args = [_Arg(h=cppscope.handle), _Arg(l=index)] + args = [_ArgH(cppscope.handle), _ArgL(index)] return charp2str_free(space, call_capi(space, 'method_signature', args)) def c_method_is_template(space, cppscope, index): - args = [_Arg(h=cppscope.handle), _Arg(l=index)] + args = [_ArgH(cppscope.handle), _ArgL(index)] return space.bool_w(call_capi(space, 'method_is_template', args)) def _c_method_num_template_args(space, cppscope, index): - args = [_Arg(h=cppscope.handle), _Arg(l=index)] + args = [_ArgH(cppscope.handle), _ArgL(index)] return space.int_w(call_capi(space, 'method_num_template_args', args)) def c_template_args(space, cppscope, index): nargs = _c_method_num_template_args(space, cppscope, index) - arg1 = _Arg(h=cppscope.handle) - arg2 = _Arg(l=index) + arg1 = _ArgH(cppscope.handle) + arg2 = _ArgL(index) args = [c_resolve_name(space, charp2str_free(space, - call_capi(space, 'method_template_arg_name', [arg1, arg2, _Arg(l=iarg)])) + call_capi(space, 'method_template_arg_name', [arg1, arg2, _ArgL(iarg)])) ) for iarg in range(nargs)] return args def c_get_method(space, cppscope, index): - args = [_Arg(h=cppscope.handle), _Arg(l=index)] + args = [_ArgH(cppscope.handle), _ArgL(index)] return rffi.cast(C_METHOD, space.uint_w(call_capi(space, 'get_method', args))) def c_get_global_operator(space, nss, lc, rc, op): if nss is not None: - args = [_Arg(h=nss.handle), _Arg(h=lc.handle), _Arg(h=rc.handle), _Arg(s=op)] + args = [_ArgH(nss.handle), _ArgH(lc.handle), _ArgH(rc.handle), _ArgS(op)] return rffi.cast(WLAVC_INDEX, space.int_w(call_capi(space, 'get_global_operator', args))) return rffi.cast(WLAVC_INDEX, -1) # method properties ---------------------------------------------------------- def c_is_constructor(space, cppclass, index): - args = [_Arg(h=cppclass.handle), _Arg(l=index)] + args = [_ArgH(cppclass.handle), _ArgL(index)] return space.bool_w(call_capi(space, 'is_constructor', args)) def c_is_staticmethod(space, cppclass, index): - args = [_Arg(h=cppclass.handle), _Arg(l=index)] + args = [_ArgH(cppclass.handle), _ArgL(index)] return space.bool_w(call_capi(space, 'is_staticmethod', args)) # data member reflection information ----------------------------------------- def c_num_datamembers(space, cppscope): - return space.int_w(call_capi(space, 'num_datamembers', [_Arg(h=cppscope.handle)])) + return space.int_w(call_capi(space, 'num_datamembers', [_ArgH(cppscope.handle)])) def c_datamember_name(space, cppscope, datamember_index): - args = [_Arg(h=cppscope.handle), _Arg(l=datamember_index)] + args = [_ArgH(cppscope.handle), _ArgL(datamember_index)] return charp2str_free(space, call_capi(space, 'datamember_name', args)) def c_datamember_type(space, cppscope, datamember_index): - args = [_Arg(h=cppscope.handle), _Arg(l=datamember_index)] + args = [_ArgH(cppscope.handle), _ArgL(datamember_index)] return charp2str_free(space, call_capi(space, 'datamember_type', args)) def c_datamember_offset(space, cppscope, datamember_index): - args = [_Arg(h=cppscope.handle), _Arg(l=datamember_index)] + args = [_ArgH(cppscope.handle), _ArgL(datamember_index)] return _cdata_to_ptrdiff_t(space, call_capi(space, 'datamember_offset', args)) def c_datamember_index(space, cppscope, name): - args = [_Arg(h=cppscope.handle), _Arg(s=name)] + args = [_ArgH(cppscope.handle), _ArgS(name)] return space.int_w(call_capi(space, 'datamember_index', args)) # data member properties ----------------------------------------------------- def c_is_publicdata(space, cppscope, datamember_index): - args = [_Arg(h=cppscope.handle), _Arg(l=datamember_index)] + args = [_ArgH(cppscope.handle), _ArgL(datamember_index)] return space.bool_w(call_capi(space, 'is_publicdata', args)) def c_is_staticdata(space, cppscope, datamember_index): - args = [_Arg(h=cppscope.handle), _Arg(l=datamember_index)] + args = [_ArgH(cppscope.handle), _ArgL(datamember_index)] return space.bool_w(call_capi(space, 'is_staticdata', args)) # misc helpers --------------------------------------------------------------- def c_strtoll(space, svalue): - return space.r_longlong_w(call_capi(space, 'strtoll', [_Arg(s=svalue)])) + return space.r_longlong_w(call_capi(space, 'strtoll', [_ArgS(svalue)])) def c_strtoull(space, svalue): - return space.r_ulonglong_w(call_capi(space, 'strtoull', [_Arg(s=svalue)])) + return space.r_ulonglong_w(call_capi(space, 'strtoull', [_ArgS(svalue)])) def c_free(space, voidp): - call_capi(space, 'free', [_Arg(vp=voidp)]) + call_capi(space, 'free', [_ArgP(voidp)]) def charp2str_free(space, cdata): charp = rffi.cast(rffi.CCHARP, _cdata_to_ptr(space, cdata)) @@ -515,15 +570,60 @@ c_free(space, rffi.cast(rffi.VOIDP, charp)) return pystr -def c_charp2stdstring(space, svalue): - return _cdata_to_cobject(space, call_capi(space, 'charp2stdstring', [_Arg(s=svalue)])) +def c_charp2stdstring(space, svalue, sz): + return _cdata_to_cobject(space, call_capi(space, 'charp2stdstring', + [_ArgS(svalue), _ArgH(rffi.cast(rffi.ULONG, sz))])) +def c_stdstring2charp(space, cppstr): + sz = lltype.malloc(rffi.SIZE_TP.TO, 1, flavor='raw') + try: + w_cstr = call_capi(space, 'stdstring2charp', + [_ArgH(cppstr), _ArgP(rffi.cast(rffi.VOIDP, sz))]) + cstr_len = intmask(sz[0]) + finally: + lltype.free(sz, flavor='raw') + return rffi.charpsize2str(_cdata_to_ccharp(space, w_cstr), cstr_len) def c_stdstring2stdstring(space, cppobject): - return _cdata_to_cobject(space, call_capi(space, 'stdstring2stdstring', [_Arg(h=cppobject)])) + return _cdata_to_cobject(space, call_capi(space, 'stdstring2stdstring', [_ArgH(cppobject)])) -# loadable-capi-specific pythonizations (none, as the capi isn't known until runtime) +def c_stdvector_valuetype(space, pystr): + return charp2str_free(space, call_capi(space, 'stdvector_valuetype', [_ArgS(pystr)])) + +def c_stdvector_valuetype(space, pystr): + return charp2str_free(space, call_capi(space, 'stdvector_valuetype', [_ArgS(pystr)])) +def c_stdvector_valuesize(space, pystr): + return _cdata_to_size_t(space, call_capi(space, 'stdvector_valuesize', [_ArgS(pystr)])) + + +# TODO: factor these out ... +# pythonizations +def stdstring_c_str(space, w_self): + """Return a python string taking into account \0""" + + from pypy.module.cppyy import interp_cppyy + cppstr = space.interp_w(interp_cppyy.W_CPPInstance, w_self, can_be_None=False) + return space.wrap(c_stdstring2charp(space, cppstr._rawobject)) + +# setup pythonizations for later use at run-time +_pythonizations = {} def register_pythonizations(space): "NOT_RPYTHON" - pass + + allfuncs = [ + + ### std::string + stdstring_c_str, + + ] + + for f in allfuncs: + _pythonizations[f.__name__] = space.wrap(interp2app(f)) + +def _method_alias(space, w_pycppclass, m1, m2): + space.setattr(w_pycppclass, space.wrap(m1), + space.getattr(w_pycppclass, space.wrap(m2))) def pythonize(space, name, w_pycppclass): - pass + if name == "string": + space.setattr(w_pycppclass, space.wrap("c_str"), _pythonizations["stdstring_c_str"]) + _method_alias(space, w_pycppclass, "_cppyy_as_builtin", "c_str") + _method_alias(space, w_pycppclass, "__str__", "c_str") diff --git a/pypy/module/cppyy/capi/reflex_capi.py b/pypy/module/cppyy/capi/reflex_capi.py deleted file mode 100644 --- a/pypy/module/cppyy/capi/reflex_capi.py +++ /dev/null @@ -1,59 +0,0 @@ -import py, os - -from rpython.rlib import libffi -from rpython.translator.tool.cbuild import ExternalCompilationInfo - -__all__ = ['identify', 'std_string_name', 'eci', 'c_load_dictionary'] - -pkgpath = py.path.local(__file__).dirpath().join(os.pardir) -srcpath = pkgpath.join("src") -incpath = pkgpath.join("include") - -import commands -(config_stat, incdir) = commands.getstatusoutput("root-config --incdir") - -if os.environ.get("ROOTSYS"): - if config_stat != 0: # presumably Reflex-only - rootincpath = [os.path.join(os.environ["ROOTSYS"], "include")] - rootlibpath = [os.path.join(os.environ["ROOTSYS"], "lib64"), os.path.join(os.environ["ROOTSYS"], "lib")] - else: - rootincpath = [incdir] - rootlibpath = commands.getoutput("root-config --libdir").split() -else: - if config_stat == 0: - rootincpath = [incdir] - rootlibpath = commands.getoutput("root-config --libdir").split() - else: - rootincpath = [] - rootlibpath = [] - -def identify(): - return 'Reflex' - -ts_reflect = False -ts_call = 'auto' -ts_memory = 'auto' -ts_helper = 'auto' - -std_string_name = 'std::basic_string<char>' - -eci = ExternalCompilationInfo( - separate_module_files=[srcpath.join("reflexcwrapper.cxx")], - include_dirs=[incpath] + rootincpath, - includes=["reflexcwrapper.h"], - library_dirs=rootlibpath, - libraries=["Reflex"], - use_cpp_linker=True, -) - -def c_load_dictionary(name): - return libffi.CDLL(name) - - -# Reflex-specific pythonizations -def register_pythonizations(space): - "NOT_RPYTHON" - pass - -def pythonize(space, name, w_pycppclass): - pass diff --git a/pypy/module/cppyy/converter.py b/pypy/module/cppyy/converter.py --- a/pypy/module/cppyy/converter.py +++ b/pypy/module/cppyy/converter.py @@ -3,8 +3,8 @@ from pypy.interpreter.error import OperationError, oefmt from rpython.rtyper.lltypesystem import rffi, lltype -from rpython.rlib.rarithmetic import r_singlefloat -from rpython.rlib import jit_libffi, rfloat +from rpython.rlib.rarithmetic import r_singlefloat, r_longfloat +from rpython.rlib import rfloat from pypy.module._rawffi.interp_rawffi import letter2tp from pypy.module._rawffi.array import W_Array, W_ArrayInstance @@ -81,11 +81,11 @@ class TypeConverter(object): - _immutable_fields_ = ['libffitype', 'uses_local', 'name'] + _immutable_fields_ = ['cffi_name', 'uses_local', 'name'] - libffitype = lltype.nullptr(jit_libffi.FFI_TYPE_P.TO) + cffi_name = None uses_local = False - name = "" + name = "" def __init__(self, space, extra): pass @@ -103,6 +103,10 @@ raise oefmt(space.w_TypeError, "no converter available for '%s'", self.name) + def cffi_type(self, space): + from pypy.module.cppyy.interp_cppyy import FastCallNotPossible + raise FastCallNotPossible + def convert_argument(self, space, w_obj, address, call_local): self._is_abstract(space) @@ -143,9 +147,7 @@ class ArrayTypeConverterMixin(object): _mixin_ = True - _immutable_fields_ = ['libffitype', 'size'] - - libffitype = jit_libffi.types.pointer + _immutable_fields_ = ['size'] def __init__(self, space, array_size): if array_size <= 0: @@ -153,6 +155,10 @@ else: self.size = array_size + def cffi_type(self, space): + state = space.fromcache(ffitypes.State) + return state.c_voidp + def from_memory(self, space, w_obj, w_pycppclass, offset): # read access, so no copy needed address_value = self._get_raw_address(space, w_obj, offset) @@ -172,13 +178,15 @@ class PtrTypeConverterMixin(object): _mixin_ = True - _immutable_fields_ = ['libffitype', 'size'] - - libffitype = jit_libffi.types.pointer + _immutable_fields_ = ['size'] def __init__(self, space, array_size): self.size = sys.maxint + def cffi_type(self, space): + state = space.fromcache(ffitypes.State) + return state.c_voidp + def convert_argument(self, space, w_obj, address, call_local): w_tc = space.findattr(w_obj, space.wrap('typecode')) if w_tc is not None and space.str_w(w_tc) != self.typecode: @@ -241,6 +249,10 @@ uses_local = True + def cffi_type(self, space): + state = space.fromcache(ffitypes.State) + return state.c_voidp + def convert_argument_libffi(self, space, w_obj, address, call_local): assert rffi.sizeof(self.c_type) <= 2*rffi.sizeof(rffi.VOIDP) # see interp_cppyy.py obj = self._unwrap_object(space, w_obj) @@ -255,6 +267,8 @@ def convert_argument(self, space, w_obj, address, call_local): x = rffi.cast(self.c_ptrtype, address) x[0] = self._unwrap_object(space, w_obj) + ba = rffi.cast(rffi.CCHARP, address) + ba[capi.c_function_arg_typeoffset(space)] = self.typecode class FloatTypeConverterMixin(NumericTypeConverterMixin): _mixin_ = True @@ -267,13 +281,15 @@ class VoidConverter(TypeConverter): - _immutable_fields_ = ['libffitype', 'name'] - - libffitype = jit_libffi.types.void + _immutable_fields_ = ['name'] def __init__(self, space, name): self.name = name + def cffi_type(self, space): + state = space.fromcache(ffitypes.State) + return state.c_void + def convert_argument(self, space, w_obj, address, call_local): self._is_abstract(space) @@ -282,6 +298,8 @@ def convert_argument(self, space, w_obj, address, call_local): x = rffi.cast(rffi.LONGP, address) x[0] = self._unwrap_object(space, w_obj) + ba = rffi.cast(rffi.CCHARP, address) + ba[capi.c_function_arg_typeoffset(space)] = 'b' def convert_argument_libffi(self, space, w_obj, address, call_local): x = rffi.cast(rffi.LONGP, address) @@ -305,6 +323,8 @@ def convert_argument(self, space, w_obj, address, call_local): x = rffi.cast(rffi.CCHARP, address) x[0] = self._unwrap_object(space, w_obj) + ba = rffi.cast(rffi.CCHARP, address) + ba[capi.c_function_arg_typeoffset(space)] = 'b' def convert_argument_libffi(self, space, w_obj, address, call_local): x = rffi.cast(self.c_ptrtype, address) @@ -331,13 +351,15 @@ def from_memory(self, space, w_obj, w_pycppclass, offset): address = self._get_raw_address(space, w_obj, offset) rffiptr = rffi.cast(self.c_ptrtype, address) - return space.wrap(float(rffiptr[0])) + return self._wrap_object(space, rffiptr[0]) class ConstFloatRefConverter(FloatConverter): - _immutable_fields_ = ['libffitype', 'typecode'] + _immutable_fields_ = ['typecode'] + typecode = 'f' - libffitype = jit_libffi.types.pointer - typecode = 'F' + def cffi_type(self, space): + state = space.fromcache(ffitypes.State) + return state.c_voidp def convert_argument_libffi(self, space, w_obj, address, call_local): from pypy.module.cppyy.interp_cppyy import FastCallNotPossible @@ -353,11 +375,22 @@ self.default = rffi.cast(self.c_type, 0.) class ConstDoubleRefConverter(ConstRefNumericTypeConverterMixin, DoubleConverter): - _immutable_fields_ = ['libffitype', 'typecode'] + _immutable_fields_ = ['typecode'] + typecode = 'd' - libffitype = jit_libffi.types.pointer - typecode = 'D' +class LongDoubleConverter(ffitypes.typeid(rffi.LONGDOUBLE), FloatTypeConverterMixin, TypeConverter): + _immutable_fields_ = ['default'] + def __init__(self, space, default): + if default: + fval = float(rfloat.rstring_to_float(default)) + else: + fval = float(0.) + self.default = r_longfloat(fval) + +class ConstLongDoubleRefConverter(ConstRefNumericTypeConverterMixin, LongDoubleConverter): + _immutable_fields_ = ['typecode'] + typecode = 'g' class CStringConverter(TypeConverter): def convert_argument(self, space, w_obj, address, call_local): @@ -377,10 +410,6 @@ class VoidPtrConverter(TypeConverter): - _immutable_fields_ = ['libffitype'] - - libffitype = jit_libffi.types.pointer - def _unwrap_object(self, space, w_obj): try: obj = get_rawbuffer(space, w_obj) @@ -393,6 +422,10 @@ obj = rffi.cast(rffi.VOIDP, get_rawobject(space, w_obj)) return obj + def cffi_type(self, space): + state = space.fromcache(ffitypes.State) + return state.c_voidp + def convert_argument(self, space, w_obj, address, call_local): x = rffi.cast(rffi.VOIDPP, address) x[0] = self._unwrap_object(space, w_obj) @@ -422,9 +455,10 @@ address[0] = rffi.cast(rffi.VOIDP, self._unwrap_object(space, w_value)) class VoidPtrPtrConverter(TypeConverter): - _immutable_fields_ = ['uses_local'] + _immutable_fields_ = ['uses_local', 'typecode'] uses_local = True + typecode = 'a' def convert_argument(self, space, w_obj, address, call_local): x = rffi.cast(rffi.VOIDPP, address) @@ -435,7 +469,7 @@ except TypeError: r[0] = rffi.cast(rffi.VOIDP, get_rawobject(space, w_obj)) x[0] = rffi.cast(rffi.VOIDP, call_local) - ba[capi.c_function_arg_typeoffset(space)] = 'a' + ba[capi.c_function_arg_typeoffset(space)] = self.typecode def finalize_call(self, space, w_obj, call_local): _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit