diff --git a/Agents.md b/Agents.md new file mode 100644 index 00000000..327bd596 --- /dev/null +++ b/Agents.md @@ -0,0 +1,29 @@ +# Agent Instructions + +## Rules + +- NEVER add an `#include` header to any file. To control header include order, edit `rice/rice.hpp`. +- NEVER run `ruby lib/rice/make_rice_headers.rb`. The `include/` folder is autogenerated by a GitHub hook. + +## Building + +Configure and build from the project root directory: + +```bash +cmake --preset linux-debug +cmake --build --preset linux-debug +``` + +## Running Tests + +Run all tests via the built `unittest` executable: + +```bash +./build/linux-debug/test/unittest +``` + +Run specific test suites by passing suite names as arguments: + +```bash +./build/linux-debug/test/unittest Array Hash Iterator +``` diff --git a/CHANGELOG.md b/CHANGELOG.md index 967a3cca..e00d8537 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Changelog -## 4.11.0 (Unreleased) +## 4.11.0 (2026-02-17) + +Enhancements: +This release focuses on improving memory management: +* C++ API is now GC safe +* C++ API wrappers no longer default to rb_cObject, avoiding unintended Object changes +* Enable Instance Registry for Ruby owned C++ objects to avoid double frees Incompatible Changes: * `InstanceRegistry.isEnabled` (boolean) has been replaced by an `InstanceRegistry.isEnabled` which is an enum (`Off`, `Owned`, `All`). @@ -9,6 +15,7 @@ Incompatible Changes: * C++ API wrappers now store their Ruby `VALUE` in a `Pin` instead of a raw `VALUE` field. Previously, wrappers were not GC-safe and Ruby could reclaim wrapped objects while C++ still referenced them. This is now fixed, and wrappers such as `Object` can be stored safely in containers like `std::vector`. * The global `Object` constants (`Rice::Nil`, `Rice::True`, `Rice::False`, `Rice::Undef`) have been removed. * `Object::test()` has been removed. Use `operator bool()` or `is_nil()` depending on the desired semantics. +* Remove `Builtin_Object` since it didn't serve a useful purpose ## 4.10.0 (2026-02-07) diff --git a/docs/cpp_api/struct.md b/docs/cpp_api/struct.md index 8320ed5a..44c7b63f 100644 --- a/docs/cpp_api/struct.md +++ b/docs/cpp_api/struct.md @@ -240,7 +240,7 @@ Struct inherits from [Class](class.md), which means it has access to: * `define_singleton_method` - Add class methods * `create` - Create instances -Struct::Instance inherits from `Builtin_Object`, providing access to [Object](object.md) methods. +Struct::Instance inherits from [Object](object.md) and validates that wrapped values are Ruby structs (`T_STRUCT`). --- diff --git a/lib/rice/version.rb b/lib/rice/version.rb index 0e008019..1e188d5e 100644 --- a/lib/rice/version.rb +++ b/lib/rice/version.rb @@ -1,3 +1,3 @@ module Rice - VERSION = "4.10.0" + VERSION = "4.11.0" end diff --git a/rice/Director.hpp b/rice/Director.hpp index e2af1d48..a6a68b0c 100644 --- a/rice/Director.hpp +++ b/rice/Director.hpp @@ -13,9 +13,7 @@ namespace Rice public: //! Construct new Director. Needs the Ruby object so that the // proxy class can call methods on that object. - Director(Object self) : self_(self) - { - } + Director(Object self); virtual ~Director() = default; @@ -24,13 +22,10 @@ namespace Rice * method, use this method to throw an exception in this case. */ [[noreturn]] - void raisePureVirtual() const - { - rb_raise(rb_eNotImpError, "Cannot call super() into a pure-virtual C++ method"); - } + void raisePureVirtual() const; //! Get the Ruby object linked to this C++ instance - Object getSelf() const { return self_; } + Object getSelf() const; private: diff --git a/rice/Director.ipp b/rice/Director.ipp new file mode 100644 index 00000000..9d8df8e6 --- /dev/null +++ b/rice/Director.ipp @@ -0,0 +1,20 @@ +#ifndef Rice__Director__ipp_ +#define Rice__Director__ipp_ + +namespace Rice +{ + inline Director::Director(Object self) : self_(self) + { + } + + inline void Director::raisePureVirtual() const + { + rb_raise(rb_eNotImpError, "Cannot call super() into a pure-virtual C++ method"); + } + + inline Object Director::getSelf() const + { + return self_; + } +} +#endif // Rice__Director__ipp_ diff --git a/rice/cpp_api/Array.hpp b/rice/cpp_api/Array.hpp index ff749a7c..282dcb81 100644 --- a/rice/cpp_api/Array.hpp +++ b/rice/cpp_api/Array.hpp @@ -17,7 +17,7 @@ namespace Rice * \endcode */ class Array - : public Builtin_Object + : public Object { public: //! Construct a new array @@ -243,4 +243,4 @@ namespace Rice Array::Iterator const& it); } // namespace Rice -#endif // Rice__Array__hpp_ \ No newline at end of file +#endif // Rice__Array__hpp_ diff --git a/rice/cpp_api/Array.ipp b/rice/cpp_api/Array.ipp index 2f678104..4f35b899 100644 --- a/rice/cpp_api/Array.ipp +++ b/rice/cpp_api/Array.ipp @@ -3,24 +3,26 @@ namespace Rice { - inline Array::Array() : Builtin_Object(detail::protect(rb_ary_new)) + inline Array::Array() : Object(detail::protect(rb_ary_new)) { } - inline Array::Array(long capacity) : Builtin_Object(detail::protect(rb_ary_new_capa, capacity)) + inline Array::Array(long capacity) : Object(detail::protect(rb_ary_new_capa, capacity)) { } - inline Array::Array(Object v) : Builtin_Object(v) + inline Array::Array(Object v) : Object(v) { + detail::protect(rb_check_type, this->value(), T_ARRAY); } - inline Array::Array(VALUE v) : Builtin_Object(v) + inline Array::Array(VALUE v) : Object(v) { + detail::protect(rb_check_type, this->value(), T_ARRAY); } template - inline Array::Array(Iter_T it, Iter_T end) : Builtin_Object(detail::protect(rb_ary_new)) + inline Array::Array(Iter_T it, Iter_T end) : Object(detail::protect(rb_ary_new)) { for (; it != end; ++it) { @@ -29,7 +31,7 @@ namespace Rice } template - inline Array::Array(T (&a)[n]) : Builtin_Object(detail::protect(rb_ary_new)) + inline Array::Array(T (&a)[n]) : Object(detail::protect(rb_ary_new)) { for (long j = 0; j < n; ++j) { @@ -442,4 +444,4 @@ namespace Rice::detail Arg* arg_ = nullptr; }; } -#endif // Rice__Array__ipp_ \ No newline at end of file +#endif // Rice__Array__ipp_ diff --git a/rice/cpp_api/Builtin_Object.hpp b/rice/cpp_api/Builtin_Object.hpp deleted file mode 100644 index 45a0a25d..00000000 --- a/rice/cpp_api/Builtin_Object.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef Rice__Builtin_Object__hpp_ -#define Rice__Builtin_Object__hpp_ - -namespace Rice -{ - //! A smartpointer-like wrapper for Ruby builtin objects. - /*! A builtin object is one of Ruby's internal types, e.g. RArray or - * RString. Every builtin type structure has a corresponding integer - * type number (e.g T_ARRAY for RArray or T_STRING for RString). This - * class is a wrapper for those types of objects, primarily useful as a - * base class for other wrapper classes like Array and Hash. - */ - template - class Builtin_Object - : public Object - { - public: - //! Wrap an already allocated Ruby object. - /*! Checks to see if the object is an object of type Builtin_Type; a - * C++ exception is thrown if this is not the case. - * \param value the object to be wrapped. - */ - Builtin_Object(Object value); - - RObject& operator*() const; //!< Return a reference to obj_ - RObject* operator->() const; //!< Return a pointer to obj_ - RObject* get() const; //!< Return a pointer to obj_ - }; -} // namespace Rice - -#endif // Rice__Builtin_Object__hpp_ \ No newline at end of file diff --git a/rice/cpp_api/Builtin_Object.ipp b/rice/cpp_api/Builtin_Object.ipp deleted file mode 100644 index 12bae2ec..00000000 --- a/rice/cpp_api/Builtin_Object.ipp +++ /dev/null @@ -1,37 +0,0 @@ -#include - -namespace Rice -{ - namespace detail - { - inline VALUE check_type(Object value, int type) - { - detail::protect(rb_check_type, value.value(), type); - return Qnil; - } - } - - template - inline Builtin_Object::Builtin_Object(Object value) : Object(value) - { - detail::check_type(value, Builtin_Type); - } - - template - inline RObject& Builtin_Object::operator*() const - { - return *ROBJECT(this->value()); - } - - template - inline RObject* Builtin_Object::operator->() const - { - return ROBJECT(this->value()); - } - - template - inline RObject* Builtin_Object::get() const - { - return ROBJECT(this->value()); - } -} // namespace Rice diff --git a/rice/cpp_api/Hash.hpp b/rice/cpp_api/Hash.hpp index a9d25a96..5eba64b3 100644 --- a/rice/cpp_api/Hash.hpp +++ b/rice/cpp_api/Hash.hpp @@ -16,7 +16,7 @@ namespace Rice //! h[10] = String("bar"); //! std::cout << String(h[42]) << std::endl; //! \endcode - class Hash: public Builtin_Object + class Hash: public Object { public: //! Construct a new hash. @@ -191,4 +191,3 @@ namespace Rice } // namespace Rice #endif // Rice__Hash__hpp_ - diff --git a/rice/cpp_api/Hash.ipp b/rice/cpp_api/Hash.ipp index 062245c8..b6a941d5 100644 --- a/rice/cpp_api/Hash.ipp +++ b/rice/cpp_api/Hash.ipp @@ -2,12 +2,13 @@ namespace Rice { - inline Hash::Hash() : Builtin_Object(detail::protect(rb_hash_new)) + inline Hash::Hash() : Object(detail::protect(rb_hash_new)) { } - inline Hash::Hash(Object v) : Builtin_Object(v) + inline Hash::Hash(Object v) : Object(v) { + detail::protect(rb_check_type, this->value(), T_HASH); } inline size_t Hash::size() const diff --git a/rice/cpp_api/Module.hpp b/rice/cpp_api/Module.hpp index 310a67e2..39e8deac 100644 --- a/rice/cpp_api/Module.hpp +++ b/rice/cpp_api/Module.hpp @@ -13,8 +13,7 @@ namespace Rice * Many of the methods are defined in Module_impl.hpp so that they can * return a reference to the most derived type. */ - // TODO: we can't inherit from Builtin_Object, because Class needs - // type T_CLASS and Module needs type T_MODULE + // Module and Class both derive from Object to preserve Ruby's hierarchy. class Module : public Object { public: diff --git a/rice/cpp_api/String.hpp b/rice/cpp_api/String.hpp index 7b1de685..605a61d1 100644 --- a/rice/cpp_api/String.hpp +++ b/rice/cpp_api/String.hpp @@ -13,7 +13,7 @@ namespace Rice * std::cout << s.length() << std::endl; * \endcode */ - class String : public Builtin_Object + class String : public Object { public: //! Construct a new string. @@ -70,4 +70,4 @@ namespace Rice }; } // namespace Rice -#endif // Rice__String__hpp_ \ No newline at end of file +#endif // Rice__String__hpp_ diff --git a/rice/cpp_api/String.ipp b/rice/cpp_api/String.ipp index 7da96979..a59b3c33 100644 --- a/rice/cpp_api/String.ipp +++ b/rice/cpp_api/String.ipp @@ -1,30 +1,32 @@ namespace Rice { - inline String::String() : Builtin_Object(detail::protect(rb_str_new2, "")) + inline String::String() : Object(detail::protect(rb_str_new2, "")) { } - inline String::String(VALUE v) : Builtin_Object(v) + inline String::String(VALUE v) : Object(v) { + detail::protect(rb_check_type, this->value(), T_STRING); } - inline String::String(Object v) : Builtin_Object(v) + inline String::String(Object v) : Object(v) { + detail::protect(rb_check_type, this->value(), T_STRING); } - inline String::String(char const* s) : Builtin_Object(detail::protect(rb_utf8_str_new_cstr, s)) + inline String::String(char const* s) : Object(detail::protect(rb_utf8_str_new_cstr, s)) { } - inline String::String(std::string const& s) : Builtin_Object(detail::protect(rb_utf8_str_new, s.data(), (long)s.length())) + inline String::String(std::string const& s) : Object(detail::protect(rb_utf8_str_new, s.data(), (long)s.length())) { } - inline String::String(std::string_view const& s) : Builtin_Object(detail::protect(rb_utf8_str_new, s.data(), (long)s.length())) + inline String::String(std::string_view const& s) : Object(detail::protect(rb_utf8_str_new, s.data(), (long)s.length())) { } - inline String::String(Identifier id) : Builtin_Object(detail::protect(rb_utf8_str_new_cstr, id.c_str())) + inline String::String(Identifier id) : Object(detail::protect(rb_utf8_str_new_cstr, id.c_str())) { } @@ -140,4 +142,4 @@ namespace Rice::detail private: Arg* arg_ = nullptr; }; -} \ No newline at end of file +} diff --git a/rice/cpp_api/Struct.hpp b/rice/cpp_api/Struct.hpp index 099451bf..94301791 100644 --- a/rice/cpp_api/Struct.hpp +++ b/rice/cpp_api/Struct.hpp @@ -78,7 +78,7 @@ namespace Rice //! An instance of a Struct //! \sa Struct - class Struct::Instance : public Builtin_Object + class Struct::Instance : public Object { public: //! Create a new Instance of a Struct. @@ -110,4 +110,4 @@ namespace Rice } // namespace Rice -#endif // Rice__ruby_struct__hpp_ \ No newline at end of file +#endif // Rice__ruby_struct__hpp_ diff --git a/rice/cpp_api/Struct.ipp b/rice/cpp_api/Struct.ipp index b52ecbfd..48d59d52 100644 --- a/rice/cpp_api/Struct.ipp +++ b/rice/cpp_api/Struct.ipp @@ -46,13 +46,15 @@ namespace Rice } inline Struct::Instance::Instance(Struct const& type, Array args) : - Builtin_Object(type.new_instance(args)), type_(type) + Object(type.new_instance(args)), type_(type) { + detail::protect(rb_check_type, this->value(), T_STRUCT); } inline Struct::Instance::Instance(Struct const& type, Object s) : - Builtin_Object(s), type_(type) + Object(s), type_(type) { + detail::protect(rb_check_type, this->value(), T_STRUCT); } inline Struct define_struct() @@ -94,4 +96,4 @@ namespace Rice::detail return rb_cStruct; } }; -} \ No newline at end of file +} diff --git a/rice/detail/cpp_protect.hpp b/rice/detail/cpp_protect.hpp index 068d7fe7..8b2e99b5 100644 --- a/rice/detail/cpp_protect.hpp +++ b/rice/detail/cpp_protect.hpp @@ -20,6 +20,9 @@ namespace Rice::detail template auto cpp_protect(Callable_T&& func) { + VALUE excValue = Qnil; + int jumpTag = 0; + try { return func(); @@ -33,69 +36,76 @@ namespace Rice::detail } catch (::Rice::Exception const& ex) { - rb_exc_raise(ex.value()); + excValue = ex.value(); } catch (::Rice::JumpException const& ex) { - rb_jump_tag(ex.tag); + jumpTag = ex.tag; } catch (std::bad_alloc const& ex) { /* This won't work quite right if the rb_exc_new2 fails; not much we can do about that, since Ruby doesn't give us access to a pre-allocated NoMemoryError object */ - rb_exc_raise(rb_exc_new2(rb_eNoMemError, ex.what())); + excValue = rb_exc_new2(rb_eNoMemError, ex.what()); } catch (std::domain_error const& ex) { - rb_exc_raise(rb_exc_new2(rb_eFloatDomainError, ex.what())); + excValue = rb_exc_new2(rb_eFloatDomainError, ex.what()); } catch (std::invalid_argument const& ex) { - rb_exc_raise(rb_exc_new2(rb_eArgError, ex.what())); + excValue = rb_exc_new2(rb_eArgError, ex.what()); } catch (fs::filesystem_error const& ex) { - rb_exc_raise(rb_exc_new2(rb_eIOError, ex.what())); + excValue = rb_exc_new2(rb_eIOError, ex.what()); } catch (std::length_error const& ex) { - rb_exc_raise(rb_exc_new2(rb_eIndexError, ex.what())); + excValue = rb_exc_new2(rb_eIndexError, ex.what()); } catch (std::out_of_range const& ex) { - rb_exc_raise(rb_exc_new2(rb_eIndexError, ex.what())); + excValue = rb_exc_new2(rb_eIndexError, ex.what()); } catch (std::overflow_error const& ex) { - rb_exc_raise(rb_exc_new2(rb_eRangeError, ex.what())); + excValue = rb_exc_new2(rb_eRangeError, ex.what()); } catch (std::range_error const& ex) { - rb_exc_raise(rb_exc_new2(rb_eRangeError, ex.what())); + excValue = rb_exc_new2(rb_eRangeError, ex.what()); } catch (std::regex_error const& ex) { - rb_exc_raise(rb_exc_new2(rb_eRegexpError, ex.what())); + excValue = rb_exc_new2(rb_eRegexpError, ex.what()); } catch (std::system_error const& ex) { - rb_exc_raise(rb_exc_new2(rb_eSystemCallError, ex.what())); + excValue = rb_exc_new2(rb_eSystemCallError, ex.what()); } catch (std::underflow_error const& ex) { - rb_exc_raise(rb_exc_new2(rb_eRangeError, ex.what())); + excValue = rb_exc_new2(rb_eRangeError, ex.what()); } catch (std::exception const& ex) { - rb_exc_raise(rb_exc_new2(rb_eRuntimeError, ex.what())); + excValue = rb_exc_new2(rb_eRuntimeError, ex.what()); } catch (...) { - rb_exc_raise(rb_exc_new2(rb_eRuntimeError, "Unknown C++ exception thrown")); + excValue = rb_exc_new2(rb_eRuntimeError, "Unknown C++ exception thrown"); } - throw std::runtime_error("Should never get here - just making compilers happy"); } + // All C++ exception objects and the handler are now destroyed. + // It is safe to call rb_jump_tag/rb_exc_raise which use longjmp. + if (jumpTag) + rb_jump_tag(jumpTag); + else + rb_exc_raise(excValue); + + throw std::runtime_error("Should never get here - just making compilers happy"); } } #endif // Rice__detail__cpp_protect__hpp_ diff --git a/rice/rice.hpp b/rice/rice.hpp index 9c59773a..45a0cd46 100644 --- a/rice/rice.hpp +++ b/rice/rice.hpp @@ -56,7 +56,6 @@ #include "cpp_api/Identifier.hpp" #include "cpp_api/Identifier.ipp" #include "cpp_api/Object.hpp" -#include "cpp_api/Builtin_Object.hpp" #include "cpp_api/String.hpp" #include "cpp_api/Symbol.hpp" #include "cpp_api/Array.hpp" @@ -133,7 +132,6 @@ // C++ API definitions #include "cpp_api/Encoding.ipp" #include "cpp_api/Object.ipp" -#include "cpp_api/Builtin_Object.ipp" #include "cpp_api/String.ipp" #include "cpp_api/Array.ipp" #include "cpp_api/Hash.ipp" @@ -150,6 +148,7 @@ #include "ruby_mark.hpp" #include "detail/default_allocation_func.hpp" #include "Director.hpp" +#include "Director.ipp" #include "Data_Type.ipp" #include "detail/default_allocation_func.ipp" #include "Constructor.ipp" diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 68c7d3ae..2ccba670 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -49,7 +49,6 @@ target_sources(${PROJECT_NAME} PRIVATE "test_Array.cpp" "test_Attribute.cpp" "test_Buffer.cpp" - "test_Builtin_Object.cpp" "test_Callback.cpp" "test_Class.cpp" "test_Constructor.cpp" diff --git a/test/test_Builtin_Object.cpp b/test/test_Builtin_Object.cpp deleted file mode 100644 index fce33ec5..00000000 --- a/test/test_Builtin_Object.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include "unittest.hpp" -#include "embed_ruby.hpp" -#include - -using namespace Rice; - -TESTSUITE(Builtin_Object); - -SETUP(Builtin_Object) -{ - embed_ruby(); -} - -TEARDOWN(Builtin_Object) -{ - rb_gc_start(); -} - -TESTCASE(construct_with_object) -{ - Class c(rb_cObject); - Object o(c.call("new")); - Builtin_Object b(o); - ASSERT_EQUAL(o.value(), b.value()); - ASSERT_EQUAL(T_OBJECT, rb_type(b.value())); - ASSERT_EQUAL(rb_cObject, b.class_of().value()); - ASSERT_EQUAL(rb_cObject, CLASS_OF(b.value())); -} - -TESTCASE(copy_construct) -{ - Class c(rb_cObject); - Object o(c.call("new")); - Builtin_Object b(o); - Builtin_Object b2(b); - ASSERT_EQUAL(o.value(), b2.value()); - ASSERT_EQUAL(T_OBJECT, rb_type(b2.value())); - ASSERT_EQUAL(rb_cObject, b2.class_of().value()); - ASSERT_EQUAL(rb_cObject, CLASS_OF(b2.value())); -} - -TESTCASE(copy_assign) -{ - Class c(rb_cObject); - Builtin_Object b1(c.call("new")); - Builtin_Object b2(c.call("new")); - - b2 = b1; - - ASSERT_EQUAL(b2.value(), b1.value()); -} - -TESTCASE(move_constructor) -{ - Class c(rb_cObject); - Builtin_Object b1(c.call("new")); - Builtin_Object b2(std::move(b1)); - - ASSERT(!b2.is_nil()); - ASSERT(b1.is_nil()); -} - -TESTCASE(move_assign) -{ - Class c(rb_cObject); - Builtin_Object b1(c.call("new")); - Builtin_Object b2(c.call("new")); - - b2 = std::move(b1); - - ASSERT(!b2.is_nil()); - ASSERT(b1.is_nil()); -} - -TESTCASE(dereference) -{ - Class c(rb_cObject); - Object o(c.call("new")); - Builtin_Object b(o); - ASSERT_EQUAL(ROBJECT(o.value()), &*b); -} - -TESTCASE(arrow) -{ - Class c(rb_cObject); - Object o(c.call("new")); - Builtin_Object b(o); - ASSERT_EQUAL(rb_cObject, b->basic.klass); -} - -TESTCASE(get) -{ - Class c(rb_cObject); - Object o(c.call("new")); - Builtin_Object b(o); - ASSERT_EQUAL(ROBJECT(o.value()), b.get()); -} diff --git a/test/test_Director.cpp b/test/test_Director.cpp index 6db240a3..266ffab3 100644 --- a/test/test_Director.cpp +++ b/test/test_Director.cpp @@ -110,7 +110,11 @@ TESTCASE(exposes_worker_as_instantiatable_class) .define_method("get_number", &Worker::getNumber); Module m = define_module("Testing"); - Object result = m.module_eval("worker = Worker.new; worker.get_number"); + + std::string code = R"(worker = Worker.new + worker.get_number)"; + + Object result = m.module_eval(code); ASSERT_EQUAL(12, detail::From_Ruby().convert(result.value())); } @@ -125,7 +129,10 @@ TESTCASE(can_call_virtual_methods_on_base_class) Module m = define_module("Testing"); - Object result = m.module_eval("worker = Worker.new; worker.do_something(4)"); + std::string code = R"(worker = Worker.new + worker.do_something(4))"; + + Object result = m.module_eval(code); ASSERT_EQUAL(16, detail::From_Ruby().convert(result.value())); } @@ -138,9 +145,19 @@ TESTCASE(super_calls_pass_execution_up_the_inheritance_chain) .define_method("do_something", &WorkerDirector::default_doSomething); Module m = define_module("Testing"); - m.module_eval("class RubyWorker < Worker; def do_something(num); super * num; end; end"); - Object result = m.module_eval("worker = RubyWorker.new; worker.do_something(10)"); + std::string code = R"(class RubyWorker < Worker + def do_something(num) + super * num + end + end)"; + + m.module_eval(code); + + code = R"(worker = RubyWorker.new + worker.do_something(10))"; + + Object result = m.module_eval(code); ASSERT_EQUAL(400, detail::From_Ruby().convert(result.value())); } @@ -153,16 +170,23 @@ TESTCASE(super_calls_on_pure_virtual_raise_error) .define_method("process", &WorkerDirector::default_process); Module m = define_module("Testing"); - m.module_eval("class RubyWorker < Worker; def process(num); super; end; end"); + + std::string code = R"(class RubyWorker < Worker + def process(num) + super + end + end)"; + + m.module_eval(code); + + code = R"(worker = RubyWorker.new + worker.process(10))"; ASSERT_EXCEPTION_CHECK( Exception, - m.module_eval("worker = RubyWorker.new; worker.process(10)"), - ASSERT_EQUAL( - Object(rb_eNotImpError), - Object(CLASS_OF(ex.value())) - ) - ); + m.module_eval(code), + ASSERT_EQUAL(rb_eNotImpError, rb_class_of(ex.value())) + ); } TESTCASE(polymorphic_calls_head_down_the_call_chain) @@ -179,13 +203,23 @@ TESTCASE(polymorphic_calls_head_down_the_call_chain) Module m = define_module("Testing"); - m.module_eval( - "class EchoWorker < Worker; def process(num); num + 2; end; end;" - "class DoubleWorker < Worker; def process(num); num * 2; end; end;" - "$handler = Handler.new;" - "$handler.add_worker(EchoWorker.new);" - "$handler.add_worker(DoubleWorker.new);" - ); + std::string code = R"(class EchoWorker < Worker + def process(num) + num + 2 + end + end + + class DoubleWorker < Worker + def process(num) + num * 2 + end + end + + $handler = Handler.new + $handler.add_worker(EchoWorker.new) + $handler.add_worker(DoubleWorker.new))"; + + m.module_eval(code); Object result = m.module_eval("$handler.process_workers(5)"); @@ -263,10 +297,16 @@ TESTCASE(mix_of_polymorphic_calls_and_inheritance_dont_cause_infinite_loops) Module m = define_module("Testing"); - Object result = m.module_eval( - "class MySelf < CallsSelf; def do_it_impl(num); num * 10; end; end;" - "c = MySelf.new; c.do_it(10)" - ); + std::string code = R"(class MySelf < CallsSelf + def do_it_impl(num) + num * 10 + end + end + + c = MySelf.new + c.do_it(10))"; + + Object result = m.module_eval(code); ASSERT_EQUAL(100, detail::From_Ruby().convert(result.value())); } @@ -298,11 +338,16 @@ TESTCASE(director_allows_abstract_types_used_as_parameters_pointers) .define_method("do_it_impl", &CallsSelfDirector::default_doItImpl) .define_method("do_it", &CallsSelf::doIt); - Object result = m.module_eval( - "class MySelf < CallsSelf; def do_it_impl(num); num * 10; end; end;" - "c = MySelf.new;" - "Testing::do_it_on_pointer(c, 5)" - ); + std::string code = R"(class MySelf < CallsSelf + def do_it_impl(num) + num * 10 + end + end + + c = MySelf.new + Testing::do_it_on_pointer(c, 5))"; + + Object result = m.module_eval(code); ASSERT_EQUAL(50, detail::From_Ruby().convert(result.value())); } @@ -318,11 +363,16 @@ TESTCASE(director_allows_abstract_types_used_as_parameters_reference) .define_method("do_it_impl", &CallsSelfDirector::default_doItImpl) .define_method("do_it", &CallsSelf::doIt); - Object result = m.module_eval( - "class MySelf < CallsSelf; def do_it_impl(num); num * 10; end; end;" - "c = MySelf.new;" - "Testing::do_it_on_ref(c, 3)" - ); + std::string code = R"(class MySelf < CallsSelf + def do_it_impl(num) + num * 10 + end + end + + c = MySelf.new + Testing::do_it_on_ref(c, 3))"; + + Object result = m.module_eval(code); ASSERT_EQUAL(30, detail::From_Ruby().convert(result.value())); } \ No newline at end of file diff --git a/test/test_Exception.cpp b/test/test_Exception.cpp index 33efe931..a43039f2 100644 --- a/test/test_Exception.cpp +++ b/test/test_Exception.cpp @@ -279,21 +279,4 @@ TESTCASE(subclasses) m.instance_eval(code), ASSERT_EQUAL("My custom exception occurred!", ex.what()) ); -} - -/*TESTCASE(memory) -{ - Module m = define_module("Testing"); - - define_global_function("hello", [](int i) - { - throw std::runtime_error("error"); - }); - - std::string code = R"(10_000.times do |i| - hello(i) rescue nil - end)"; - - m.instance_eval(code); - ASSERT(true); -}*/ +} \ No newline at end of file diff --git a/test/unittest.cpp b/test/unittest.cpp index d1fdc7cb..08e2eb09 100644 --- a/test/unittest.cpp +++ b/test/unittest.cpp @@ -68,7 +68,7 @@ run(Test_Result & result) std::cout << "."; } catch(Assertion_Failed const & ex) - { + { std::cout << "F"; result.add_failure(Failure(name(), it->name(), ex.what())); }