Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ target_compile_definitions(cppjit PRIVATE
CPPINTEROP_INCLUDE_DIR="${CPPJIT_INTEROP_RUNTIME_INCLUDES}"
CPPJIT_CLANG_MAJOR="${CPPJIT_INTEROP_CLANG_MAJOR}"
CPPJIT_CLANG_INCLUDE_DIR="${CPPJIT_INTEROP_CLANG_DIR}"
$<$<BOOL:${CPPJIT_USE_CLING}>:CPPJIT_USE_CLING>
)

target_include_directories(cppjit PRIVATE
Expand Down
20 changes: 3 additions & 17 deletions src/cpyrt/CPPInstance.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ using namespace cppjit;
#include "CPPInstance.h"
#include "CPPOverload.h"
#include "CPPScope.h"
#include "Compatibility.h"
#include "MemoryRegulator.h"
#include "ProxyWrappers.h"
#include "PyStrings.h"
Expand Down Expand Up @@ -841,7 +842,7 @@ static PyObject* op_str(CPPInstance* self) {
// otherwise not found
// TODO: ToString() still leaks ...
const std::string& pretty =
interop::ToString(self->ObjectIsA(), self->GetObject());
compat::ObjToString(self->ObjectIsA(), self->GetObject());
if (!pretty.empty())
return cpyrt_PyText_FromString(pretty.c_str());
continue;
Expand Down Expand Up @@ -870,22 +871,7 @@ static PyObject* op_str(CPPInstance* self) {
// 2. Cling's pretty printing (not done through backend for performance
// reasons)
if (!ScopeFlagCheck(self, CPPScope::kNoPrettyPrint)) {
static PyObject* printValue = nullptr;
if (!printValue) {
PyObject* gbl =
PyDict_GetItemString(PySys_GetObject((char*)"modules"), "cppjit.gbl");
PyObject* cl = PyObject_GetAttrString(gbl, (char*)"cling");
printValue = PyObject_GetAttrString(cl, (char*)"printValue");
Py_DECREF(cl);
// gbl is borrowed
if (printValue) {
Py_DECREF(printValue); // make borrowed
if (!PyCallable_Check(printValue))
printValue = nullptr; // unusable ...
}
if (!printValue) // unlikely
ScopeFlagSet(self, CPPScope::kNoPrettyPrint);
}
PyObject* printValue = compat::GetClingPrintValue();

if (printValue) {
// as printValue only works well for templates taking pointer arguments,
Expand Down
48 changes: 48 additions & 0 deletions src/cpyrt/Compatibility.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#ifndef CPYRT_COMPATIBILITY_H
#define CPYRT_COMPATIBILITY_H

#include "Python.h"

#include "cppjit_interop.h"

#include <string>

namespace cppjit::cpyrt::compat {

inline PyObject* GetClingPrintValue() {
#ifdef CPPJIT_USE_CLING
static PyObject* printValue = nullptr;
if (printValue)
return printValue;

PyObject* gbl =
PyDict_GetItemString(PySys_GetObject((char*)"modules"), "cppjit.gbl");
PyObject* cling = gbl ? PyObject_GetAttrString(gbl, (char*)"cling") : nullptr;
printValue =
cling ? PyObject_GetAttrString(cling, (char*)"printValue") : nullptr;
Py_XDECREF(cling);

if (printValue) {
Py_DECREF(printValue); // make borrowed
if (!PyCallable_Check(printValue))
printValue = nullptr;
}

if (!printValue)
PyErr_Clear();

return printValue;
#else
return nullptr;
#endif
}

// Interpreter::toString is an assert(0) stub (CppInterOp#1100); skip it.
inline std::string ObjToString(interop::TCppScope_t /*klass*/,
interop::TCppObject_t /*obj*/) {
return "";
}

} // namespace cppjit::cpyrt::compat

#endif // CPYRT_COMPATIBILITY_H
5 changes: 0 additions & 5 deletions test/test_crossinheritance.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import py
from pytest import mark, raises, skip
from support import (
IS_CLANG_DEBUG,
IS_CLING,
IS_LINUX_ARM,
IS_MAC,
Expand All @@ -27,10 +26,6 @@ def setup_class(cls):

cls.example01 = cppjit.load_reflection_info(cls.test_dct)

@mark.xfail(
run=not (IS_CLANG_DEBUG or IS_CLING),
reason="Crashes with ClangRepl with 'toString not implemented' and on Cling",
)
def test01_override_function(self):
"""Test ability to override a simple function"""

Expand Down
6 changes: 0 additions & 6 deletions test/test_doc_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import py
from pytest import mark, raises, skip
from support import (
IS_CLANG_REPL,
IS_CLING,
IS_LINUX_ARM,
IS_MAC,
Expand Down Expand Up @@ -1040,11 +1039,6 @@ def test08_voidptr_array(self):
assert n.p[2] == 0x3
assert len(n.p) == 3

@mark.xfail(
condition=IS_CLANG_REPL and IS_MAC,
run=False,
reason="Crashes with ClangRepl with 'toString not implemented'",
)
def test09_custom_str(self):
"""Example of customized str"""

Expand Down
18 changes: 14 additions & 4 deletions test/test_regression.py
Original file line number Diff line number Diff line change
Expand Up @@ -1040,10 +1040,6 @@ class ReferenceWavefunction {};
cppjit.gbl.std.get[0](cppjit.gbl.property_types.run_as[pt_type]()) == 20.0
)

@mark.xfail(
run=False,
reason="Crashes on ClangRepl with 'toString not implemented', and on Cling",
)
def test34_print_empty_collection(self):
"""Print empty collection through Cling"""

Expand Down Expand Up @@ -1648,3 +1644,17 @@ def test52_no_cpp_name_for_template_arg(self):

with raises(TypeError):
cppjit.gbl.std.vector[object()]

def test53_str_fallback_without_ostream_insertion(self):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do have existing tests that are marked crashing with clang-repl due to ToString. I am not sure if adding a Python test that spawns a process guaranteed to crash is a good idea, especially since this does not live on the API surface, and this test suite is meant to be user-facing. Imo this can be moved into a CppInterOp only reproducer. If compat::GetClingPrintValue() returns null, can we prevent the crash and gracefully error out instead?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The null return was already safe. op_str falls through to the generic repr there.

The crash the test caught happens one step earlier. op_str calls Cpp::ObjToString, and Interpreter::toString is an assert(0) stub. It aborts with assertions on and returns "" without them. The stub is not backend specific, so the existing markers say "and on Cling".

I moved that call behind the same compatibility header and made it return the empty string on every build, so op_str reaches the generic repr everywhere. #1100 turns the helper back into a one-line forward.

The subprocess test is gone. The new test53 runs in process and checks that str(obj) equals repr(obj). The three xfail markers that named this crash now pass on an assertions-on build, so I removed them.

"""str() of an instance with no operator<< used to crash"""

import cppjit

cppjit.cppdef("namespace StrFallback { struct Bare { int x; }; }")

bare = cppjit.gbl.StrFallback.Bare()

# no ostream inserter and no usable pretty printer, so op_str must
# degrade to the generic repr
assert str(bare) == repr(bare)
assert "Bare object at" in str(bare)
Loading