[pypy-commit] pypy reflex-support: o) set of CINT-backend tests

wlav noreply at buildbot.pypy.org
Thu Jul 5 05:28:05 CEST 2012


Author: Wim Lavrijsen <WLavrijsen at lbl.gov>
Branch: reflex-support
Changeset: r55925:a54c37b4006f
Date: 2012-07-04 20:27 -0700
http://bitbucket.org/pypy/pypy/changeset/a54c37b4006f/

Log:	o) set of CINT-backend tests o) enable use of interpreted classes
	(CINT only) o) code cleanup

diff --git a/pypy/module/cppyy/interp_cppyy.py b/pypy/module/cppyy/interp_cppyy.py
--- a/pypy/module/cppyy/interp_cppyy.py
+++ b/pypy/module/cppyy/interp_cppyy.py
@@ -423,8 +423,7 @@
     def _build_methods(self):
         assert len(self.methods) == 0
         methods_temp = {}
-        N = capi.c_num_methods(self)
-        for i in range(N):
+        for i in range(capi.c_num_methods(self)):
             idx = capi.c_method_index_at(self, i)
             pyname = helper.map_operator_name(
                 capi.c_method_name(self, idx),
diff --git a/pypy/module/cppyy/pythonify.py b/pypy/module/cppyy/pythonify.py
--- a/pypy/module/cppyy/pythonify.py
+++ b/pypy/module/cppyy/pythonify.py
@@ -254,7 +254,7 @@
         except AttributeError:
             pass
 
-    if not (pycppitem is None):   # pycppitem could be a bound C++ NULL, so check explicitly for Py_None
+    if pycppitem is not None:      # pycppitem could be a bound C++ NULL, so check explicitly for Py_None
         return pycppitem
 
     raise AttributeError("'%s' has no attribute '%s'" % (str(scope), name))
diff --git a/pypy/module/cppyy/src/cintcwrapper.cxx b/pypy/module/cppyy/src/cintcwrapper.cxx
--- a/pypy/module/cppyy/src/cintcwrapper.cxx
+++ b/pypy/module/cppyy/src/cintcwrapper.cxx
@@ -1,8 +1,6 @@
 #include "cppyy.h"
 #include "cintcwrapper.h"
 
-#include "Api.h"
-
 #include "TROOT.h"
 #include "TError.h"
 #include "TList.h"
@@ -23,6 +21,8 @@
 #include "TMethod.h"
 #include "TMethodArg.h"
 
+#include "Api.h"
+
 #include <assert.h>
 #include <string.h>
 #include <map>
@@ -31,9 +31,8 @@
 #include <utility>
 
 
-/*  CINT internals (some won't work on Windows) -------------------------- */
+/* ROOT/CINT internals --------------------------------------------------- */
 extern long G__store_struct_offset;
-extern "C" void* G__SetShlHandle(char*);
 extern "C" void G__LockCriticalSection();
 extern "C" void G__UnlockCriticalSection();
 
@@ -66,26 +65,15 @@
 typedef std::map<std::string, ClassRefs_t::size_type> ClassRefIndices_t;
 static ClassRefIndices_t g_classref_indices;
 
-class ClassRefsInit {
-public:
-    ClassRefsInit() {   // setup dummy holders for global and std namespaces
-        assert(g_classrefs.size() == (ClassRefs_t::size_type)GLOBAL_HANDLE);
-        g_classref_indices[""] = (ClassRefs_t::size_type)GLOBAL_HANDLE;
-        g_classrefs.push_back(TClassRef(""));
-        g_classref_indices["std"] = g_classrefs.size();
-        g_classrefs.push_back(TClassRef(""));    // CINT ignores std
-        g_classref_indices["::std"] = g_classrefs.size();
-        g_classrefs.push_back(TClassRef(""));    // id.
-    }
-};
-static ClassRefsInit _classrefs_init;
-
 typedef std::vector<TFunction> GlobalFuncs_t;
 static GlobalFuncs_t g_globalfuncs;
 
 typedef std::vector<TGlobal> GlobalVars_t;
 static GlobalVars_t g_globalvars;
 
+typedef std::vector<G__MethodInfo> InterpretedFuncs_t;
+static InterpretedFuncs_t g_interpreted;
+
 
 /* initialization of the ROOT system (debatable ... ) --------------------- */
 namespace {
@@ -95,12 +83,12 @@
     TCppyyApplication(const char* acn, Int_t* argc, char** argv, Bool_t do_load = kTRUE)
            : TApplication(acn, argc, argv) {
 
-       // Explicitly load libMathCore as CINT will not auto load it when using one
-       // of its globals. Once moved to Cling, which should work correctly, we
-       // can remove this statement.
-       gSystem->Load("libMathCore");
+        // Explicitly load libMathCore as CINT will not auto load it when using
+        // one of its globals. Once moved to Cling, which should work correctly,
+        // we can remove this statement.
+        gSystem->Load("libMathCore");
 
-       if (do_load) {
+        if (do_load) {
             // follow TRint to minimize differences with CINT
             ProcessLine("#include <iostream>", kTRUE);
             ProcessLine("#include <_string>",  kTRUE); // for std::string iostream.
@@ -130,10 +118,30 @@
 class ApplicationStarter {
 public:
     ApplicationStarter() {
+        // setup dummy holders for global and std namespaces
+        assert(g_classrefs.size() == (ClassRefs_t::size_type)GLOBAL_HANDLE);
+        g_classref_indices[""] = (ClassRefs_t::size_type)GLOBAL_HANDLE;
+        g_classrefs.push_back(TClassRef(""));
+        g_classref_indices["std"] = g_classrefs.size();
+        g_classrefs.push_back(TClassRef(""));    // CINT ignores std
+        g_classref_indices["::std"] = g_classrefs.size();
+        g_classrefs.push_back(TClassRef(""));    // id.
+ 
+        // an offset for the interpreted methods
+        g_interpreted.push_back(G__MethodInfo());
+
+        // actual application init, if necessary
         if (!gApplication) {
             int argc = 1;
             char* argv[1]; argv[0] = (char*)appname;
             gApplication = new TCppyyApplication(appname, &argc, argv, kTRUE);
+            if (!gProgName)                  // should have been set by TApplication
+                gSystem->SetProgname(appname);
+        }
+
+        // program name should've been set by TApplication; just in case ...
+        if (!gProgName) {
+            gSystem->SetProgname(appname);
         }
     }
 } _applicationStarter;
@@ -325,11 +333,21 @@
 static inline G__value cppyy_call_T(cppyy_method_t method,
         cppyy_object_t self, int nargs, void* args) {
 
-    G__InterfaceMethod meth = (G__InterfaceMethod)method;
     G__param* libp = (G__param*)((char*)args - offsetof(G__param, para));
     assert(libp->paran == nargs);
     fixup_args(libp);
 
+    if ((InterpretedFuncs_t::size_type)method < g_interpreted.size()) {
+    // the idea here is that all these low values are invalid memory addresses,
+    // allowing the reuse of method to index the stored bytecodes
+        G__CallFunc callf;
+        callf.SetFunc(g_interpreted[(size_t)method]);
+        callf.SetArgs(*libp);
+        return callf.Execute((void*)self);
+    }
+
+    G__InterfaceMethod meth = (G__InterfaceMethod)method;
+
     G__value result;
     G__setnull(&result);
 
@@ -338,13 +356,13 @@
 
     long index = (long)&method;
     G__CurrentCall(G__SETMEMFUNCENV, 0, &index);
-    
+
     // TODO: access to store_struct_offset won't work on Windows
     long store_struct_offset = G__store_struct_offset;
     if (self)
         G__store_struct_offset = (long)self;
 
-    meth(&result, 0, libp, 0);
+    meth(&result, (char*)0, libp, 0);
     if (self)
         G__store_struct_offset = store_struct_offset;
 
@@ -663,8 +681,27 @@
 
 
 cppyy_method_t cppyy_get_method(cppyy_scope_t handle, cppyy_index_t idx) {
+    TClassRef cr = type_from_handle(handle);
     TFunction* f = type_get_method(handle, idx);
-    return (cppyy_method_t)f->InterfaceMethod();
+    if (cr && cr.GetClass() && !cr->IsLoaded()) {
+        G__ClassInfo* gcl = (G__ClassInfo*)cr->GetClassInfo();
+        if (gcl) {
+            long offset;
+            std::ostringstream sig;
+            int nArgs = f->GetNargs();
+            for (int iarg = 0; iarg < nArgs; ++iarg) {
+                sig << ((TMethodArg*)f->GetListOfMethodArgs()->At(iarg))->GetFullTypeName();
+                if (iarg != nArgs-1) sig << ", ";
+            }
+            G__MethodInfo gmi = gcl->GetMethod(
+                f->GetName(), sig.str().c_str(), &offset, G__ClassInfo::ExactMatch);
+            cppyy_method_t method = (cppyy_method_t)g_interpreted.size();
+            g_interpreted.push_back(gmi);
+            return method;
+        }
+    }
+    cppyy_method_t method = (cppyy_method_t)f->InterfaceMethod();
+    return method;
 }
 
 cppyy_index_t cppyy_get_global_operator(cppyy_scope_t lc, cppyy_scope_t rc, const char* op) {
diff --git a/pypy/module/cppyy/test/example01.cxx b/pypy/module/cppyy/test/example01.cxx
--- a/pypy/module/cppyy/test/example01.cxx
+++ b/pypy/module/cppyy/test/example01.cxx
@@ -156,6 +156,8 @@
    return ::globalAddOneToInt(a);
 }
 
+int ns_example01::gMyGlobalInt = 99;
+
 
 // argument passing
 #define typeValueImp(itype, tname)                                            \
diff --git a/pypy/module/cppyy/test/example01.h b/pypy/module/cppyy/test/example01.h
--- a/pypy/module/cppyy/test/example01.h
+++ b/pypy/module/cppyy/test/example01.h
@@ -60,10 +60,11 @@
 };
 
 
-// global functions
+// global functions and data
 int globalAddOneToInt(int a);
 namespace ns_example01 {
     int globalAddOneToInt(int a);
+    extern int gMyGlobalInt;
 }
 
 #define itypeValue(itype, tname) \
@@ -72,6 +73,7 @@
 #define ftypeValue(ftype) \
    ftype ftype##Value(ftype arg0, int argn=0, ftype arg1=1., ftype arg2=2.)
 
+
 // argument passing
 class ArgPasser {        // use a class for now as methptrgetter not
 public:                  // implemented for global functions
diff --git a/pypy/module/cppyy/test/example01.xml b/pypy/module/cppyy/test/example01.xml
--- a/pypy/module/cppyy/test/example01.xml
+++ b/pypy/module/cppyy/test/example01.xml
@@ -11,6 +11,7 @@
 
   <namespace name="ns_example01" />
   <function name="ns_example01::globalAddOneToInt" />
+  <variable name="ns_example01::gMyGlobalInt" />
 
   <class name="ArgPasser" />
 
diff --git a/pypy/module/cppyy/test/example01_LinkDef.h b/pypy/module/cppyy/test/example01_LinkDef.h
--- a/pypy/module/cppyy/test/example01_LinkDef.h
+++ b/pypy/module/cppyy/test/example01_LinkDef.h
@@ -16,4 +16,6 @@
 #pragma link C++ namespace ns_example01;
 #pragma link C++ function ns_example01::globalAddOneToInt(int);
 
+#pragma link C++ variable ns_example01::gMyGlobalInt;
+
 #endif
diff --git a/pypy/module/cppyy/test/simple_class.C b/pypy/module/cppyy/test/simple_class.C
new file mode 100644
--- /dev/null
+++ b/pypy/module/cppyy/test/simple_class.C
@@ -0,0 +1,15 @@
+class MySimpleBase {
+public:
+    MySimpleBase() {}
+};
+
+class MySimpleDerived : public MySimpleBase {
+public:
+    MySimpleDerived() { m_data = -42; }
+    int get_data() { return m_data; }
+    void set_data(int data) { m_data = data; }
+public:
+    int m_data;
+};
+
+typedef MySimpleDerived MySimpleDerived_t;
diff --git a/pypy/module/cppyy/test/test_cint.py b/pypy/module/cppyy/test/test_cint.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cppyy/test/test_cint.py
@@ -0,0 +1,87 @@
+import py, os, sys
+from pypy.conftest import gettestobjspace
+
+# These tests are for the CINT backend only (they exercise ROOT features
+# and classes that are not loaded/available with the Reflex backend). At
+# some point, these tests are likely covered by the CLang/LLVM backend.
+from pypy.module.cppyy import capi
+if capi.identify() != 'CINT':
+    py.test.skip("backend-specific: CINT-only tests")
+
+space = gettestobjspace(usemodules=['cppyy'])
+
+class AppTestCINT:
+    def setup_class(cls):
+        cls.space = space
+
+    def test01_globals(self):
+        """Test the availability of ROOT globals"""
+
+        import cppyy
+
+        assert cppyy.gbl.gROOT
+        assert cppyy.gbl.gApplication
+        assert cppyy.gbl.gSystem
+        assert cppyy.gbl.TInterpreter.Instance()
+        assert cppyy.gbl.TDirectory.CurrentDirectory()
+
+    def test02_write_access_to_globals(self):
+        """Test overwritability of ROOT globals"""
+
+        import cppyy
+
+        oldval = cppyy.gbl.gDebug
+        assert oldval != 3
+
+        proxy = cppyy.gbl.__class__.gDebug
+        cppyy.gbl.gDebug = 3
+        assert proxy.__get__(proxy) == 3
+
+        # this is where this test differs from test03_write_access_to_globals
+        # in test_pythonify.py
+        cppyy.gbl.gROOT.ProcessLine('int gDebugCopy = gDebug;')
+        assert cppyy.gbl.gDebugCopy == 3
+
+        cppyy.gbl.gDebug = oldval
+
+    def test03_create_access_to_globals(self):
+        """Test creation and access of new ROOT globals"""
+
+        import cppyy
+
+        cppyy.gbl.gROOT.ProcessLine('double gMyOwnGlobal = 3.1415')
+        assert cppyy.gbl.gMyOwnGlobal == 3.1415
+
+        proxy = cppyy.gbl.__class__.gMyOwnGlobal
+        assert proxy.__get__(proxy) == 3.1415
+
+    def test04_auto_loading(self):
+        """Test auto-loading by retrieving a non-preloaded class"""
+
+        import cppyy
+
+        l = cppyy.gbl.TLorentzVector()
+        assert isinstance(l, cppyy.gbl.TLorentzVector)
+
+    def test05_macro_loading(self):
+        """Test accessibility to macro classes"""
+
+        import cppyy
+
+        loadres = cppyy.gbl.gROOT.LoadMacro('simple_class.C')
+        assert loadres == 0
+
+        base = cppyy.gbl.MySimpleBase
+        simple = cppyy.gbl.MySimpleDerived
+        simple_t = cppyy.gbl.MySimpleDerived_t
+
+        assert issubclass(simple, base)
+        assert simple is simple_t
+
+        c = simple()
+        assert isinstance(c, simple)
+        assert c.m_data == c.get_data()
+
+        c.set_data(13)
+        assert c.m_data == 13
+        assert c.get_data() == 13
diff --git a/pypy/module/cppyy/test/test_pythonify.py b/pypy/module/cppyy/test/test_pythonify.py
--- a/pypy/module/cppyy/test/test_pythonify.py
+++ b/pypy/module/cppyy/test/test_pythonify.py
@@ -345,3 +345,17 @@
 
         example01_pythonize = 1
         raises(TypeError, cppyy.add_pythonization, 'example01', example01_pythonize)
+
+    def test03_write_access_to_globals(self):
+        """Test overwritability of globals"""
+
+        import cppyy
+
+        oldval = cppyy.gbl.ns_example01.gMyGlobalInt
+        assert oldval == 99
+
+        proxy = cppyy.gbl.ns_example01.__class__.gMyGlobalInt
+        cppyy.gbl.ns_example01.gMyGlobalInt = 3
+        assert proxy.__get__(proxy) == 3
+
+        cppyy.gbl.ns_example01.gMyGlobalInt = oldval
diff --git a/pypy/module/cppyy/test/test_zjit.py b/pypy/module/cppyy/test/test_zjit.py
--- a/pypy/module/cppyy/test/test_zjit.py
+++ b/pypy/module/cppyy/test/test_zjit.py
@@ -6,6 +6,9 @@
 from pypy.interpreter.baseobjspace import InternalSpaceCache, W_Root
 
 from pypy.module.cppyy import interp_cppyy, capi
+# These tests are for the backend that support the fast path only.
+if capi.identify() == 'CINT':
+    py.test.skip("CINT does not support fast path")
 
 # load cpyext early, or its global vars are counted as leaks in the test
 # (note that the module is not otherwise used in the test itself)
@@ -177,9 +180,6 @@
         return True
 
 class TestFastPathJIT(LLJitMixin):
-    if not capi.identify() != 'CINT':
-        py.test.skip("CINT does not support fast path")
-
     def _run_zjit(self, method_name):
         space = FakeSpace()
         drv = jit.JitDriver(greens=[], reds=["i", "inst", "cppmethod"])


More information about the pypy-commit mailing list