From: Thomas Geymayer Date: Sun, 23 Nov 2014 22:39:56 +0000 (+0100) Subject: Nasal: add an naRef to ghosts to allow for proper gc of dependent objects/ghosts. X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=9537876bba38b31a7ba6be6ab79f21b335c7b659;p=simgear.git Nasal: add an naRef to ghosts to allow for proper gc of dependent objects/ghosts. This allows for binding the lifetime of any nasal object to the lifetime of a ghost. Otherwise circular references from objects saved within the ghost would prevent the ghost from being garbage collected. --- diff --git a/simgear/nasal/cppbind/CMakeLists.txt b/simgear/nasal/cppbind/CMakeLists.txt index 1e52d9b9..9d005394 100644 --- a/simgear/nasal/cppbind/CMakeLists.txt +++ b/simgear/nasal/cppbind/CMakeLists.txt @@ -35,16 +35,21 @@ simgear_component(nasal/cppbind nasal/cppbind "${SOURCES}" "${HEADERS}") simgear_component(nasal/cppbind/detail nasal/cppbind/detail "" "${DETAIL_HEADERS}") add_boost_test(cppbind_ghost - SOURCES cppbind_test_ghost.cxx + SOURCES test/cppbind_test_ghost.cxx LIBRARIES ${TEST_LIBS} ) add_boost_test(cppbind_misc - SOURCES cppbind_test.cxx + SOURCES test/cppbind_test.cxx + LIBRARIES ${TEST_LIBS} +) + +add_boost_test(nasal_gc_test + SOURCES test/nasal_gc_test.cxx LIBRARIES ${TEST_LIBS} ) add_boost_test(nasal_num - SOURCES nasal_num_test.cxx + SOURCES test/nasal_num_test.cxx LIBRARIES ${TEST_LIBS} ) \ No newline at end of file diff --git a/simgear/nasal/cppbind/cppbind_test.cxx b/simgear/nasal/cppbind/cppbind_test.cxx deleted file mode 100644 index 2ea918d8..00000000 --- a/simgear/nasal/cppbind/cppbind_test.cxx +++ /dev/null @@ -1,459 +0,0 @@ -#define BOOST_TEST_MODULE cppbind -#include - -#include "Ghost.hxx" -#include "NasalHash.hxx" -#include "NasalString.hxx" - -#include -#include - -#include -#include - -#include - -enum MyEnum -{ - ENUM_FIRST, - ENUM_ANOTHER, - ENUM_LAST -}; -struct Base -{ - naRef member(const nasal::CallContext&) { return naNil(); } - virtual ~Base(){}; - - std::string getString() const { return ""; } - void setString(const std::string&) {} - void constVoidFunc() const {} - size_t test1Arg(const std::string& str) const { return str.length(); } - bool test2Args(const std::string& s, bool c) { return c && s.empty(); } - - std::string var; - const std::string& getVar() const { return var; } - void setVar(const std::string v) { var = v; } - - unsigned long getThis() const { return (unsigned long)this; } - bool genericSet(const std::string& key, const std::string& val) - { - return key == "test"; - } - bool genericGet(const std::string& key, std::string& val_out) const - { - if( key != "get_test" ) - return false; - - val_out = "generic-get"; - return true; - } -}; - -void baseVoidFunc(Base& b) {} -void baseConstVoidFunc(const Base& b) {} -size_t baseFunc2Args(Base& b, int x, const std::string& s) { return x + s.size(); } -std::string testPtr(Base& b) { return b.getString(); } -void baseFuncCallContext(const Base&, const nasal::CallContext&) {} - -struct Derived: - public Base -{ - int _x; - int getX() const { return _x; } - void setX(int x) { _x = x; } -}; - -naRef f_derivedGetRandom(const Derived&, naContext) -{ - return naNil(); -} -void f_derivedSetX(Derived& d, naContext, naRef r) -{ - d._x = static_cast(naNumValue(r).num); -} - -struct DoubleDerived: - public Derived -{ - -}; - -typedef boost::shared_ptr BasePtr; -typedef std::vector BaseVec; - -struct DoubleDerived2: - public Derived -{ - const BasePtr& getBase() const{return _base;} - BasePtr _base; - BaseVec doSomeBaseWork(const BaseVec& v) { return v; } -}; - -class SGReferenceBasedClass: - public SGReferenced -{ - -}; - -class SGWeakReferenceBasedClass: - public SGWeakReferenced -{ - -}; - -typedef boost::shared_ptr DerivedPtr; -typedef boost::shared_ptr DoubleDerivedPtr; -typedef boost::shared_ptr DoubleDerived2Ptr; -typedef SGSharedPtr SGRefBasedPtr; -typedef SGSharedPtr SGWeakRefBasedPtr; - -typedef boost::weak_ptr DerivedWeakPtr; - -naRef derivedFreeMember(Derived&, const nasal::CallContext&) { return naNil(); } -naRef f_derivedGetX(const Derived& d, naContext c) -{ - return nasal::to_nasal(c, d.getX()); -} -naRef f_freeFunction(nasal::CallContext c) { return c.requireArg(0); } - -BOOST_AUTO_TEST_CASE( cppbind_misc_testing ) -{ - naContext c = naNewContext(); - naRef r; - - using namespace nasal; - - r = to_nasal(c, ENUM_ANOTHER); - BOOST_CHECK_EQUAL(from_nasal(c, r), ENUM_ANOTHER); - - r = to_nasal(c, "Test"); - BOOST_CHECK( strncmp("Test", naStr_data(r), naStr_len(r)) == 0 ); - BOOST_CHECK_EQUAL(from_nasal(c, r), "Test"); - - r = to_nasal(c, std::string("Test")); - BOOST_CHECK( strncmp("Test", naStr_data(r), naStr_len(r)) == 0 ); - BOOST_CHECK_EQUAL(from_nasal(c, r), "Test"); - - r = to_nasal(c, 42); - BOOST_CHECK_EQUAL(naNumValue(r).num, 42); - BOOST_CHECK_EQUAL(from_nasal(c, r), 42); - - r = to_nasal(c, 4.2f); - BOOST_CHECK_EQUAL(naNumValue(r).num, 4.2f); - BOOST_CHECK_EQUAL(from_nasal(c, r), 4.2f); - - float test_data[3] = {0, 4, 2}; - r = to_nasal(c, test_data); - - SGVec2f vec(0,2); - r = to_nasal(c, vec); - BOOST_CHECK_EQUAL(from_nasal(c, r), vec); - - std::vector std_vec; - r = to_nasal(c, std_vec); - - r = to_nasal(c, "string"); - BOOST_CHECK_THROW(from_nasal(c, r), bad_nasal_cast); - - Hash hash(c); - hash.set("vec", r); - hash.set("vec2", vec); - hash.set("name", "my-name"); - hash.set("string", std::string("blub")); - hash.set("func", &f_freeFunction); - - BOOST_CHECK_EQUAL(hash.size(), 5); - for(Hash::const_iterator it = hash.begin(); it != hash.end(); ++it) - BOOST_CHECK_EQUAL( hash.get(it->getKey()), - it->getValue() ); - - Hash::iterator it1, it2; - Hash::const_iterator it3 = it1, it4(it2); - it1 = it2; - it3 = it2; - - r = to_nasal(c, hash); - BOOST_REQUIRE( naIsHash(r) ); - - simgear::StringMap string_map = from_nasal(c, r); - BOOST_CHECK_EQUAL(string_map.at("vec"), "string"); - BOOST_CHECK_EQUAL(string_map.at("name"), "my-name"); - BOOST_CHECK_EQUAL(string_map.at("string"), "blub"); - - BOOST_CHECK_EQUAL(hash.get("name"), "my-name"); - BOOST_CHECK(naIsString(hash.get("name"))); - - Hash mod = hash.createHash("mod"); - mod.set("parent", hash); - - - // 'func' is a C++ function registered to Nasal and now converted back to C++ - boost::function f = hash.get("func"); - BOOST_REQUIRE( f ); - BOOST_CHECK_EQUAL(f(3), 3); - - boost::function fs = hash.get("func"); - BOOST_REQUIRE( fs ); - BOOST_CHECK_EQUAL(fs(14), "14"); - - typedef boost::function FuncVoidInt; - FuncVoidInt fvi = hash.get("func"); - BOOST_REQUIRE( fvi ); - fvi(123); - - typedef boost::function FuncMultiArg; - FuncMultiArg fma = hash.get("func"); - BOOST_REQUIRE( fma ); - BOOST_CHECK_EQUAL(fma("test", 3, .5), "test"); - - typedef boost::function naRefMemFunc; - naRefMemFunc fmem = hash.get("func"); - BOOST_REQUIRE( fmem ); - naRef ret = fmem(hash.get_naRef()), - hash_ref = hash.get_naRef(); - BOOST_CHECK( naIsIdentical(ret, hash_ref) ); - - // Check if nasal::Me gets passed as self/me and remaining arguments are - // passed on to function - typedef boost::function MeIntFunc; - MeIntFunc fmeint = hash.get("func"); - BOOST_CHECK_EQUAL(fmeint(naNil(), 5), 5); - - //---------------------------------------------------------------------------- - // Test exposing classes to Nasal - //---------------------------------------------------------------------------- - - Ghost::init("BasePtr") - .method("member", &Base::member) - .method("strlen", &Base::test1Arg) - .member("str", &Base::getString, &Base::setString) - .method("str_m", &Base::getString) - .method("void", &Base::constVoidFunc) - .member("var_r", &Base::getVar) - .member("var_w", &Base::setVar) - .member("var", &Base::getVar, &Base::setVar) - .method("void", &baseVoidFunc) - .method("void_c", &baseConstVoidFunc) - .method("int2args", &baseFunc2Args) - .method("bool2args", &Base::test2Args) - .method("str_ptr", &testPtr) - .method("this", &Base::getThis) - ._set(&Base::genericSet) - ._get(&Base::genericGet); - Ghost::init("DerivedPtr") - .bases() - .member("x", &Derived::getX, &Derived::setX) - .member("x_alternate", &f_derivedGetX) - .member("x_mixed", &f_derivedGetRandom, &Derived::setX) - .member("x_mixed2", &Derived::getX, &f_derivedSetX) - .member("x_w", &f_derivedSetX) - .method("free_fn", &derivedFreeMember) - .method("free_member", &derivedFreeMember) - .method("baseDoIt", &baseFuncCallContext); - Ghost::init("DoubleDerivedPtr") - .bases(); - Ghost::init("DoubleDerived2Ptr") - .bases< Ghost >() - .member("base", &DoubleDerived2::getBase) - .method("doIt", &DoubleDerived2::doSomeBaseWork); - - Ghost::init("SGRefBasedPtr"); - Ghost::init("SGWeakRefBasedPtr"); - - SGWeakRefBasedPtr weak_ptr(new SGWeakReferenceBasedClass()); - naRef nasal_ref = to_nasal(c, weak_ptr), - nasal_ptr = to_nasal(c, weak_ptr.get()); - - BOOST_REQUIRE( naIsGhost(nasal_ref) ); - BOOST_REQUIRE( naIsGhost(nasal_ptr) ); - - SGWeakRefBasedPtr ptr1 = from_nasal(c, nasal_ref), - ptr2 = from_nasal(c, nasal_ptr); - - BOOST_CHECK_EQUAL(weak_ptr, ptr1); - BOOST_CHECK_EQUAL(weak_ptr, ptr2); - - - BOOST_REQUIRE( Ghost::isInit() ); - nasal::to_nasal(c, DoubleDerived2Ptr()); - - BasePtr d( new Derived ); - naRef derived = to_nasal(c, d); - BOOST_REQUIRE( naIsGhost(derived) ); - BOOST_CHECK_EQUAL( std::string("DerivedPtr"), naGhost_type(derived)->name ); - - // Get member function from ghost... - naRef thisGetter = naNil(); - BOOST_CHECK( naMember_get(c, derived, to_nasal(c, "this"), &thisGetter) ); - BOOST_CHECK( naIsFunc(thisGetter) ); - - // ...and check if it really gets passed the correct instance - typedef boost::function MemFunc; - MemFunc fGetThis = from_nasal(c, thisGetter); - BOOST_REQUIRE( fGetThis ); - BOOST_CHECK_EQUAL( fGetThis(derived), (unsigned long)d.get() ); - - BasePtr d2( new DoubleDerived ); - derived = to_nasal(c, d2); - BOOST_CHECK( naIsGhost(derived) ); - BOOST_CHECK_EQUAL( std::string("DoubleDerivedPtr"), - naGhost_type(derived)->name ); - - BasePtr d3( new DoubleDerived2 ); - derived = to_nasal(c, d3); - BOOST_CHECK( naIsGhost(derived) ); - BOOST_CHECK_EQUAL( std::string("DoubleDerived2Ptr"), - naGhost_type(derived)->name ); - - SGRefBasedPtr ref_based( new SGReferenceBasedClass ); - naRef na_ref_based = to_nasal(c, ref_based.get()); - BOOST_CHECK( naIsGhost(na_ref_based) ); - BOOST_CHECK_EQUAL( from_nasal(c, na_ref_based), - ref_based.get() ); - BOOST_CHECK_EQUAL( from_nasal(c, na_ref_based), ref_based ); - - BOOST_CHECK_EQUAL( from_nasal(c, derived), d3 ); - BOOST_CHECK_NE( from_nasal(c, derived), d2 ); - BOOST_CHECK_EQUAL( from_nasal(c, derived), - boost::dynamic_pointer_cast(d3) ); - BOOST_CHECK_EQUAL( from_nasal(c, derived), - boost::dynamic_pointer_cast(d3) ); - BOOST_CHECK_THROW( from_nasal(c, derived), bad_nasal_cast ); - - std::map instances; - BOOST_CHECK( naIsHash(to_nasal(c, instances)) ); - - std::map instances_d; - BOOST_CHECK( naIsHash(to_nasal(c, instances_d)) ); - - std::map int_map; - BOOST_CHECK( naIsHash(to_nasal(c, int_map)) ); - - std::map > int_vector_map; - BOOST_CHECK( naIsHash(to_nasal(c, int_vector_map)) ); - - simgear::StringMap dict = - simgear::StringMap("hello", "value") - ("key2", "value2"); - naRef na_dict = to_nasal(c, dict); - BOOST_REQUIRE( naIsHash(na_dict) ); - BOOST_CHECK_EQUAL( Hash(na_dict, c).get("key2"), "value2" ); - - // Check converting to Ghost if using Nasal hashes with actual ghost inside - // the hashes parents vector - std::vector parents; - parents.push_back(hash.get_naRef()); - parents.push_back(derived); - - Hash obj(c); - obj.set("parents", parents); - BOOST_CHECK_EQUAL( from_nasal(c, obj.get_naRef()), d3 ); - - // Check recursive parents (aka parent-of-parent) - std::vector parents2; - parents2.push_back(obj.get_naRef()); - Hash derived_obj(c); - derived_obj.set("parents", parents2); - BOOST_CHECK_EQUAL( from_nasal(c, derived_obj.get_naRef()), d3 ); - - std::vector nasal_objects; - nasal_objects.push_back( Ghost::makeGhost(c, d) ); - nasal_objects.push_back( Ghost::makeGhost(c, d2) ); - nasal_objects.push_back( Ghost::makeGhost(c, d3) ); - naRef obj_vec = to_nasal(c, nasal_objects); - - std::vector objects = from_nasal >(c, obj_vec); - BOOST_CHECK_EQUAL( objects[0], d ); - BOOST_CHECK_EQUAL( objects[1], d2 ); - BOOST_CHECK_EQUAL( objects[2], d3 ); - - { - // Calling fallback setter for unset values - const char* src_code = "me.test = 3;"; - int errLine = -1; - naRef code = naParseCode( c, to_nasal(c, "source.nas"), 0, - (char*)src_code, strlen(src_code), - &errLine ); - ret = naCallMethod(code, derived, 0, 0, naNil()); - - BOOST_REQUIRE( !naGetError(c) ); // TODO real error check (this seems to - // always return 0... - BOOST_CHECK_EQUAL( from_nasal(c, ret), 3 ); - } - { - // Calling generic (fallback) getter - const char* src_code = "var a = me.get_test;"; - int errLine = -1; - naRef code = naParseCode( c, to_nasal(c, "source.nas"), 0, - (char*)src_code, strlen(src_code), - &errLine ); - ret = naCallMethod(code, derived, 0, 0, naNil()); - - BOOST_REQUIRE( !naGetError(c) ); // TODO real error check (this seems to - // always return 0... - BOOST_CHECK_EQUAL( from_nasal(c, ret), "generic-get" ); - } - - //---------------------------------------------------------------------------- - // Test nasal::CallContext - //---------------------------------------------------------------------------- - - - int int_vec[] = {1,2,3}; - std::map map; - naRef args[] = { - to_nasal(c, std::string("test-arg")), - to_nasal(c, 4), - to_nasal(c, int_vec), - to_nasal(c, map) - }; - CallContext cc(c, naNil(), sizeof(args)/sizeof(args[0]), args); - BOOST_CHECK_EQUAL( cc.requireArg(0), "test-arg" ); - BOOST_CHECK_EQUAL( cc.getArg(0), "test-arg" ); - BOOST_CHECK_EQUAL( cc.getArg(10), "" ); - BOOST_CHECK( cc.isString(0) ); - BOOST_CHECK( !cc.isNumeric(0) ); - BOOST_CHECK( !cc.isVector(0) ); - BOOST_CHECK( !cc.isHash(0) ); - BOOST_CHECK( !cc.isGhost(0) ); - BOOST_CHECK( cc.isNumeric(1) ); - BOOST_CHECK( cc.isVector(2) ); - BOOST_CHECK( cc.isHash(3) ); - - naRef args_vec = nasal::to_nasal(c, args); - BOOST_CHECK( naIsVector(args_vec) ); - - //---------------------------------------------------------------------------- - // Test nasal::String - //---------------------------------------------------------------------------- - - String string( to_nasal(c, "Test") ); - BOOST_CHECK_EQUAL( from_nasal(c, string.get_naRef()), "Test" ); - BOOST_CHECK_EQUAL( string.c_str(), std::string("Test") ); - BOOST_CHECK( string.starts_with(string) ); - BOOST_CHECK( string.starts_with(String(c, "T")) ); - BOOST_CHECK( string.starts_with(String(c, "Te")) ); - BOOST_CHECK( string.starts_with(String(c, "Tes")) ); - BOOST_CHECK( string.starts_with(String(c, "Test")) ); - BOOST_CHECK( !string.starts_with(String(c, "Test1")) ); - BOOST_CHECK( !string.starts_with(String(c, "bb")) ); - BOOST_CHECK( !string.starts_with(String(c, "bbasdasdafasd")) ); - BOOST_CHECK( string.ends_with(String(c, "t")) ); - BOOST_CHECK( string.ends_with(String(c, "st")) ); - BOOST_CHECK( string.ends_with(String(c, "est")) ); - BOOST_CHECK( string.ends_with(String(c, "Test")) ); - BOOST_CHECK( !string.ends_with(String(c, "1Test")) ); - BOOST_CHECK( !string.ends_with(String(c, "abc")) ); - BOOST_CHECK( !string.ends_with(String(c, "estasdasd")) ); - BOOST_CHECK_EQUAL( string.find('e'), 1 ); - BOOST_CHECK_EQUAL( string.find('9'), String::npos ); - BOOST_CHECK_EQUAL( string.find_first_of(String(c, "st")), 2 ); - BOOST_CHECK_EQUAL( string.find_first_of(String(c, "st"), 3), 3 ); - BOOST_CHECK_EQUAL( string.find_first_of(String(c, "xyz")), String::npos ); - BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "Tst")), 1 ); - BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "Tse"), 2), 3 ); - BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "abc")), 0 ); - BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "abc"), 20), String::npos ); - - naFreeContext(c); -} diff --git a/simgear/nasal/cppbind/cppbind_test_ghost.cxx b/simgear/nasal/cppbind/cppbind_test_ghost.cxx deleted file mode 100644 index 98924989..00000000 --- a/simgear/nasal/cppbind/cppbind_test_ghost.cxx +++ /dev/null @@ -1,188 +0,0 @@ -#define BOOST_TEST_MODULE cppbind -#include - -#include "Ghost.hxx" -#include "NasalContext.hxx" - -#include -#include - -class Base1: - public virtual SGVirtualWeakReferenced -{}; - -class Base2: - public virtual SGVirtualWeakReferenced -{}; - -class Derived: - public Base1, - public Base2 -{}; - -typedef SGSharedPtr Base1Ptr; -typedef SGSharedPtr Base2Ptr; -typedef SGSharedPtr DerivedPtr; -typedef SGWeakPtr DerivedWeakPtr; - -typedef SGSharedPtr SGReferencedPtr; - -// Check if shared_ptr_traits give correct types for strong and weak shared -// pointer -#define CHECK_PTR_TRAIT_TYPE(ptr_t, type_name, type)\ - BOOST_STATIC_ASSERT((boost::is_same<\ - nasal::shared_ptr_traits::type_name,\ - type\ - >::value)); - -#define CHECK_PTR_TRAIT(ref, weak)\ - CHECK_PTR_TRAIT_TYPE(ref, strong_ref, ref)\ - CHECK_PTR_TRAIT_TYPE(weak, weak_ref, weak)\ - -CHECK_PTR_TRAIT(DerivedPtr, DerivedWeakPtr) -CHECK_PTR_TRAIT(boost::shared_ptr, boost::weak_ptr) - -#undef CHECK_PTR_TRAIT -#undef CHECK_PTR_TRAIT_TYPE - -BOOST_STATIC_ASSERT(( nasal::supports_weak_ref::value)); -BOOST_STATIC_ASSERT(( nasal::supports_weak_ref::value)); -BOOST_STATIC_ASSERT(( nasal::supports_weak_ref::value)); -BOOST_STATIC_ASSERT(( nasal::supports_weak_ref::value)); -BOOST_STATIC_ASSERT((!nasal::supports_weak_ref::value)); - -static void setupGhosts() -{ - nasal::Ghost::init("Base1"); - nasal::Ghost::init("Base2"); - nasal::Ghost::init("Derived") - .bases() - .bases(); -} - -//------------------------------------------------------------------------------ -BOOST_AUTO_TEST_CASE( ghost_weak_strong_nasal_conversion ) -{ - setupGhosts(); - naContext c = naNewContext(); - - DerivedPtr d = new Derived(); - DerivedWeakPtr weak(d); - - // store weak pointer and extract strong pointer - naRef na_d = nasal::to_nasal(c, DerivedWeakPtr(d)); - BOOST_REQUIRE( naIsGhost(na_d) ); - BOOST_CHECK_EQUAL( nasal::from_nasal(c, na_d), d ); - BOOST_CHECK_EQUAL( nasal::from_nasal(c, na_d), d ); - BOOST_CHECK_EQUAL( nasal::from_nasal(c, na_d), d ); - - d.reset(); - BOOST_REQUIRE_THROW( nasal::from_nasal(c, na_d), - nasal::bad_nasal_cast ); - - // store strong pointer and extract weak pointer - d.reset(new Derived); - na_d = nasal::to_nasal(c, d); - BOOST_REQUIRE( naIsGhost(na_d) ); - - weak = nasal::from_nasal(c, na_d); - BOOST_CHECK_EQUAL( weak.lock(), d ); - - d.reset(); - BOOST_REQUIRE( nasal::from_nasal(c, na_d) ); - BOOST_REQUIRE( weak.lock() ); - - naFreeContext(c); - naGC(); - nasal::ghostProcessDestroyList(); - - BOOST_REQUIRE( !weak.lock() ); -} - -//------------------------------------------------------------------------------ -BOOST_AUTO_TEST_CASE( ghost_casting_storage ) -{ - setupGhosts(); - nasal::Context c; - - // Check converting to and from every class in the hierarchy for an instance - // of the leaf class - DerivedPtr d = new Derived(); - - naRef na_d = nasal::to_nasal(c, d), - na_b1 = nasal::to_nasal(c, Base1Ptr(d)), - na_b2 = nasal::to_nasal(c, Base2Ptr(d)); - - Derived *d0 = nasal::from_nasal(c, na_d), - *d1 = nasal::from_nasal(c, na_b1), - *d2 = nasal::from_nasal(c, na_b2); - - BOOST_CHECK_EQUAL(d0, d.get()); - BOOST_CHECK_EQUAL(d1, d.get()); - BOOST_CHECK_EQUAL(d2, d.get()); - - Base1 *b1 = nasal::from_nasal(c, na_b1); - BOOST_CHECK_EQUAL(b1, static_cast(d.get())); - - Base2 *b2 = nasal::from_nasal(c, na_b2); - BOOST_CHECK_EQUAL(b2, static_cast(d.get())); - - // Check converting from base class instance to derived classes is not - // possible - Base1Ptr b1_ref = new Base1(); - na_b1 = nasal::to_nasal(c, b1_ref); - - BOOST_CHECK_EQUAL(nasal::from_nasal(c, na_b1), b1_ref.get()); - BOOST_CHECK_THROW(nasal::from_nasal(c, na_b1), nasal::bad_nasal_cast); - BOOST_CHECK_THROW(nasal::from_nasal(c, na_b1), nasal::bad_nasal_cast); -} - -//------------------------------------------------------------------------------ -#define CHECK_PTR_STORAGE_TRAIT_TYPE(ptr_t, storage)\ - BOOST_STATIC_ASSERT((boost::is_same<\ - nasal::shared_ptr_storage::storage_type,\ - storage\ - >::value)); - -CHECK_PTR_STORAGE_TRAIT_TYPE(DerivedPtr, Derived) -CHECK_PTR_STORAGE_TRAIT_TYPE(DerivedWeakPtr, DerivedWeakPtr) - -typedef boost::shared_ptr BoostDerivedPtr; -CHECK_PTR_STORAGE_TRAIT_TYPE(BoostDerivedPtr, BoostDerivedPtr) - -typedef boost::weak_ptr BoostDerivedWeakPtr; -CHECK_PTR_STORAGE_TRAIT_TYPE(BoostDerivedWeakPtr, BoostDerivedWeakPtr) - -#undef CHECK_PTR_STORAGE_TRAIT_TYPE - -BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits::is_intrusive::value)); -BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits::is_intrusive::value)); -BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits::is_intrusive::value)); -BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits::is_intrusive::value)); -BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits::is_intrusive::value)); - -BOOST_STATIC_ASSERT((!nasal::shared_ptr_traits >::is_intrusive::value)); -BOOST_STATIC_ASSERT((!nasal::shared_ptr_traits >::is_intrusive::value)); - -BOOST_AUTO_TEST_CASE( storage_traits ) -{ - DerivedPtr d = new Derived(); - - Derived* d_raw = nasal::shared_ptr_storage::ref(d); - BOOST_REQUIRE_EQUAL(d_raw, d.get()); - BOOST_REQUIRE_EQUAL(d.getNumRefs(), 2); - - DerivedWeakPtr* d_weak = nasal::shared_ptr_storage::ref(d); - BOOST_REQUIRE_EQUAL( - nasal::shared_ptr_storage::get(d_weak), - d_raw - ); - - d.reset(); - BOOST_REQUIRE_EQUAL(Derived::count(d_raw), 1); - - nasal::shared_ptr_storage::unref(d_raw); - BOOST_REQUIRE(d_weak->expired()); - - nasal::shared_ptr_storage::unref(d_weak); -} diff --git a/simgear/nasal/cppbind/nasal_num_test.cxx b/simgear/nasal/cppbind/nasal_num_test.cxx deleted file mode 100644 index e1e7dd0f..00000000 --- a/simgear/nasal/cppbind/nasal_num_test.cxx +++ /dev/null @@ -1,96 +0,0 @@ -#define BOOST_TEST_MODULE cppbind -#include - -#include "NasalCallContext.hxx" - -class TestContext: - public nasal::CallContext -{ - public: - TestContext(): - CallContext(naNewContext(), naNil(), 0, 0) - {} - - ~TestContext() - { - naFreeContext(c); - } - - template - T from_str(const std::string& str) - { - return from_nasal(to_nasal(str)); - } - - naRef exec(const std::string& code_str, nasal::Me me) - { - int err_line = -1; - naRef code = naParseCode( c, to_nasal(""), 0, - (char*)code_str.c_str(), code_str.length(), - &err_line ); - if( !naIsCode(code) ) - throw std::runtime_error("Failed to parse code: " + code_str); - - return naCallMethod(code, me, 0, 0, naNil()); - } - - template - T exec(const std::string& code) - { - return from_nasal(exec(code, naNil())); - } - - template - T convert(const std::string& str) - { - return from_nasal(to_nasal(str)); - } -}; - -static void runNumTests( double (TestContext::*test_double)(const std::string&), - int (TestContext::*test_int)(const std::string&) ) -{ - TestContext c; - - BOOST_CHECK_CLOSE((c.*test_double)("0.5"), 0.5, 1e-5); - BOOST_CHECK_CLOSE((c.*test_double)(".6"), 0.6, 1e-5); - BOOST_CHECK_CLOSE((c.*test_double)("-.7"), -0.7, 1e-5); - BOOST_CHECK_CLOSE((c.*test_double)("-0.8"), -0.8, 1e-5); - BOOST_CHECK_SMALL((c.*test_double)("0.0"), 1e-5); - BOOST_CHECK_SMALL((c.*test_double)("-.0"), 1e-5); - - BOOST_CHECK_CLOSE((c.*test_double)("1.23e4"), 1.23e4, 1e-5); - BOOST_CHECK_CLOSE((c.*test_double)("1.23e-4"), 1.23e-4, 1e-5); - BOOST_CHECK_CLOSE((c.*test_double)("-1.23e4"), -1.23e4, 1e-5); - BOOST_CHECK_CLOSE((c.*test_double)("-1.23e-4"), -1.23e-4, 1e-5); - BOOST_CHECK_CLOSE((c.*test_double)("1e-4"), 1e-4, 1e-5); - BOOST_CHECK_CLOSE((c.*test_double)("-1e-4"), -1e-4, 1e-5); - - BOOST_CHECK_EQUAL((c.*test_int)("123"), 123); - BOOST_CHECK_EQUAL((c.*test_int)("-958"), -958); - - BOOST_CHECK_CLOSE((c.*test_int)("-1e7"), -1e7, 1e-5); - BOOST_CHECK_CLOSE((c.*test_int)("2E07"), 2e07, 1e-5); - - BOOST_CHECK_EQUAL((c.*test_int)("0755"), 755); - BOOST_CHECK_EQUAL((c.*test_int)("0055"), 55); - BOOST_CHECK_EQUAL((c.*test_int)("-0155"), -155); - - BOOST_CHECK_EQUAL((c.*test_int)("0o755"), 0755); - BOOST_CHECK_EQUAL((c.*test_int)("0o055"), 055); - BOOST_CHECK_EQUAL((c.*test_int)("-0o155"), -0155); - - BOOST_CHECK_EQUAL((c.*test_int)("0x755"), 0x755); - BOOST_CHECK_EQUAL((c.*test_int)("0x055"), 0x55); - BOOST_CHECK_EQUAL((c.*test_int)("-0x155"), -0x155); -} - -BOOST_AUTO_TEST_CASE( parse_num ) -{ - runNumTests(&TestContext::convert, &TestContext::convert); -} - -BOOST_AUTO_TEST_CASE( lex_num ) -{ - runNumTests(&TestContext::exec, &TestContext::exec); -} diff --git a/simgear/nasal/cppbind/test/TestContext.hxx b/simgear/nasal/cppbind/test/TestContext.hxx new file mode 100644 index 00000000..69c2113e --- /dev/null +++ b/simgear/nasal/cppbind/test/TestContext.hxx @@ -0,0 +1,76 @@ +///@file +/// Nasal context for testing and executing code +/// +// Copyright (C) 2014 Thomas Geymayer +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef SG_NASAL_TESTCONTEXT_HXX_ +#define SG_NASAL_TESTCONTEXT_HXX_ + +#include + +class TestContext: + public nasal::CallContext +{ + public: + TestContext(): + CallContext(naNewContext(), naNil(), 0, 0) + {} + + ~TestContext() + { + naFreeContext(c); + } + + void runGC() + { + naFreeContext(c); + naGC(); + c = naNewContext(); + } + + template + T from_str(const std::string& str) + { + return from_nasal(to_nasal(str)); + } + + naRef exec(const std::string& code_str, nasal::Me me) + { + int err_line = -1; + naRef code = naParseCode( c, to_nasal(""), 0, + (char*)code_str.c_str(), code_str.length(), + &err_line ); + if( !naIsCode(code) ) + throw std::runtime_error("Failed to parse code: " + code_str); + + return naCallMethod(code, me, 0, 0, naNil()); + } + + template + T exec(const std::string& code) + { + return from_nasal(exec(code, naNil())); + } + + template + T convert(const std::string& str) + { + return from_nasal(to_nasal(str)); + } +}; + +#endif /* SG_NASAL_TESTCONTEXT_HXX_ */ diff --git a/simgear/nasal/cppbind/test/cppbind_test.cxx b/simgear/nasal/cppbind/test/cppbind_test.cxx new file mode 100644 index 00000000..b4371f18 --- /dev/null +++ b/simgear/nasal/cppbind/test/cppbind_test.cxx @@ -0,0 +1,458 @@ +#define BOOST_TEST_MODULE cppbind +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +enum MyEnum +{ + ENUM_FIRST, + ENUM_ANOTHER, + ENUM_LAST +}; +struct Base +{ + naRef member(const nasal::CallContext&) { return naNil(); } + virtual ~Base(){}; + + std::string getString() const { return ""; } + void setString(const std::string&) {} + void constVoidFunc() const {} + size_t test1Arg(const std::string& str) const { return str.length(); } + bool test2Args(const std::string& s, bool c) { return c && s.empty(); } + + std::string var; + const std::string& getVar() const { return var; } + void setVar(const std::string v) { var = v; } + + unsigned long getThis() const { return (unsigned long)this; } + bool genericSet(const std::string& key, const std::string& val) + { + return key == "test"; + } + bool genericGet(const std::string& key, std::string& val_out) const + { + if( key != "get_test" ) + return false; + + val_out = "generic-get"; + return true; + } +}; + +void baseVoidFunc(Base& b) {} +void baseConstVoidFunc(const Base& b) {} +size_t baseFunc2Args(Base& b, int x, const std::string& s) { return x + s.size(); } +std::string testPtr(Base& b) { return b.getString(); } +void baseFuncCallContext(const Base&, const nasal::CallContext&) {} + +struct Derived: + public Base +{ + int _x; + int getX() const { return _x; } + void setX(int x) { _x = x; } +}; + +naRef f_derivedGetRandom(const Derived&, naContext) +{ + return naNil(); +} +void f_derivedSetX(Derived& d, naContext, naRef r) +{ + d._x = static_cast(naNumValue(r).num); +} + +struct DoubleDerived: + public Derived +{ + +}; + +typedef boost::shared_ptr BasePtr; +typedef std::vector BaseVec; + +struct DoubleDerived2: + public Derived +{ + const BasePtr& getBase() const{return _base;} + BasePtr _base; + BaseVec doSomeBaseWork(const BaseVec& v) { return v; } +}; + +class SGReferenceBasedClass: + public SGReferenced +{ + +}; + +class SGWeakReferenceBasedClass: + public SGWeakReferenced +{ + +}; + +typedef boost::shared_ptr DerivedPtr; +typedef boost::shared_ptr DoubleDerivedPtr; +typedef boost::shared_ptr DoubleDerived2Ptr; +typedef SGSharedPtr SGRefBasedPtr; +typedef SGSharedPtr SGWeakRefBasedPtr; + +typedef boost::weak_ptr DerivedWeakPtr; + +naRef derivedFreeMember(Derived&, const nasal::CallContext&) { return naNil(); } +naRef f_derivedGetX(const Derived& d, naContext c) +{ + return nasal::to_nasal(c, d.getX()); +} +naRef f_freeFunction(nasal::CallContext c) { return c.requireArg(0); } + +BOOST_AUTO_TEST_CASE( cppbind_misc_testing ) +{ + naContext c = naNewContext(); + naRef r; + + using namespace nasal; + + r = to_nasal(c, ENUM_ANOTHER); + BOOST_CHECK_EQUAL(from_nasal(c, r), ENUM_ANOTHER); + + r = to_nasal(c, "Test"); + BOOST_CHECK( strncmp("Test", naStr_data(r), naStr_len(r)) == 0 ); + BOOST_CHECK_EQUAL(from_nasal(c, r), "Test"); + + r = to_nasal(c, std::string("Test")); + BOOST_CHECK( strncmp("Test", naStr_data(r), naStr_len(r)) == 0 ); + BOOST_CHECK_EQUAL(from_nasal(c, r), "Test"); + + r = to_nasal(c, 42); + BOOST_CHECK_EQUAL(naNumValue(r).num, 42); + BOOST_CHECK_EQUAL(from_nasal(c, r), 42); + + r = to_nasal(c, 4.2f); + BOOST_CHECK_EQUAL(naNumValue(r).num, 4.2f); + BOOST_CHECK_EQUAL(from_nasal(c, r), 4.2f); + + float test_data[3] = {0, 4, 2}; + r = to_nasal(c, test_data); + + SGVec2f vec(0,2); + r = to_nasal(c, vec); + BOOST_CHECK_EQUAL(from_nasal(c, r), vec); + + std::vector std_vec; + r = to_nasal(c, std_vec); + + r = to_nasal(c, "string"); + BOOST_CHECK_THROW(from_nasal(c, r), bad_nasal_cast); + + Hash hash(c); + hash.set("vec", r); + hash.set("vec2", vec); + hash.set("name", "my-name"); + hash.set("string", std::string("blub")); + hash.set("func", &f_freeFunction); + + BOOST_CHECK_EQUAL(hash.size(), 5); + for(Hash::const_iterator it = hash.begin(); it != hash.end(); ++it) + BOOST_CHECK_EQUAL( hash.get(it->getKey()), + it->getValue() ); + + Hash::iterator it1, it2; + Hash::const_iterator it3 = it1, it4(it2); + it1 = it2; + it3 = it2; + + r = to_nasal(c, hash); + BOOST_REQUIRE( naIsHash(r) ); + + simgear::StringMap string_map = from_nasal(c, r); + BOOST_CHECK_EQUAL(string_map.at("vec"), "string"); + BOOST_CHECK_EQUAL(string_map.at("name"), "my-name"); + BOOST_CHECK_EQUAL(string_map.at("string"), "blub"); + + BOOST_CHECK_EQUAL(hash.get("name"), "my-name"); + BOOST_CHECK(naIsString(hash.get("name"))); + + Hash mod = hash.createHash("mod"); + mod.set("parent", hash); + + + // 'func' is a C++ function registered to Nasal and now converted back to C++ + boost::function f = hash.get("func"); + BOOST_REQUIRE( f ); + BOOST_CHECK_EQUAL(f(3), 3); + + boost::function fs = hash.get("func"); + BOOST_REQUIRE( fs ); + BOOST_CHECK_EQUAL(fs(14), "14"); + + typedef boost::function FuncVoidInt; + FuncVoidInt fvi = hash.get("func"); + BOOST_REQUIRE( fvi ); + fvi(123); + + typedef boost::function FuncMultiArg; + FuncMultiArg fma = hash.get("func"); + BOOST_REQUIRE( fma ); + BOOST_CHECK_EQUAL(fma("test", 3, .5), "test"); + + typedef boost::function naRefMemFunc; + naRefMemFunc fmem = hash.get("func"); + BOOST_REQUIRE( fmem ); + naRef ret = fmem(hash.get_naRef()), + hash_ref = hash.get_naRef(); + BOOST_CHECK( naIsIdentical(ret, hash_ref) ); + + // Check if nasal::Me gets passed as self/me and remaining arguments are + // passed on to function + typedef boost::function MeIntFunc; + MeIntFunc fmeint = hash.get("func"); + BOOST_CHECK_EQUAL(fmeint(naNil(), 5), 5); + + //---------------------------------------------------------------------------- + // Test exposing classes to Nasal + //---------------------------------------------------------------------------- + + Ghost::init("BasePtr") + .method("member", &Base::member) + .method("strlen", &Base::test1Arg) + .member("str", &Base::getString, &Base::setString) + .method("str_m", &Base::getString) + .method("void", &Base::constVoidFunc) + .member("var_r", &Base::getVar) + .member("var_w", &Base::setVar) + .member("var", &Base::getVar, &Base::setVar) + .method("void", &baseVoidFunc) + .method("void_c", &baseConstVoidFunc) + .method("int2args", &baseFunc2Args) + .method("bool2args", &Base::test2Args) + .method("str_ptr", &testPtr) + .method("this", &Base::getThis) + ._set(&Base::genericSet) + ._get(&Base::genericGet); + Ghost::init("DerivedPtr") + .bases() + .member("x", &Derived::getX, &Derived::setX) + .member("x_alternate", &f_derivedGetX) + .member("x_mixed", &f_derivedGetRandom, &Derived::setX) + .member("x_mixed2", &Derived::getX, &f_derivedSetX) + .member("x_w", &f_derivedSetX) + .method("free_fn", &derivedFreeMember) + .method("free_member", &derivedFreeMember) + .method("baseDoIt", &baseFuncCallContext); + Ghost::init("DoubleDerivedPtr") + .bases(); + Ghost::init("DoubleDerived2Ptr") + .bases< Ghost >() + .member("base", &DoubleDerived2::getBase) + .method("doIt", &DoubleDerived2::doSomeBaseWork); + + Ghost::init("SGRefBasedPtr"); + Ghost::init("SGWeakRefBasedPtr"); + + SGWeakRefBasedPtr weak_ptr(new SGWeakReferenceBasedClass()); + naRef nasal_ref = to_nasal(c, weak_ptr), + nasal_ptr = to_nasal(c, weak_ptr.get()); + + BOOST_REQUIRE( naIsGhost(nasal_ref) ); + BOOST_REQUIRE( naIsGhost(nasal_ptr) ); + + SGWeakRefBasedPtr ptr1 = from_nasal(c, nasal_ref), + ptr2 = from_nasal(c, nasal_ptr); + + BOOST_CHECK_EQUAL(weak_ptr, ptr1); + BOOST_CHECK_EQUAL(weak_ptr, ptr2); + + + BOOST_REQUIRE( Ghost::isInit() ); + nasal::to_nasal(c, DoubleDerived2Ptr()); + + BasePtr d( new Derived ); + naRef derived = to_nasal(c, d); + BOOST_REQUIRE( naIsGhost(derived) ); + BOOST_CHECK_EQUAL( std::string("DerivedPtr"), naGhost_type(derived)->name ); + + // Get member function from ghost... + naRef thisGetter = naNil(); + BOOST_CHECK( naMember_get(c, derived, to_nasal(c, "this"), &thisGetter) ); + BOOST_CHECK( naIsFunc(thisGetter) ); + + // ...and check if it really gets passed the correct instance + typedef boost::function MemFunc; + MemFunc fGetThis = from_nasal(c, thisGetter); + BOOST_REQUIRE( fGetThis ); + BOOST_CHECK_EQUAL( fGetThis(derived), (unsigned long)d.get() ); + + BasePtr d2( new DoubleDerived ); + derived = to_nasal(c, d2); + BOOST_CHECK( naIsGhost(derived) ); + BOOST_CHECK_EQUAL( std::string("DoubleDerivedPtr"), + naGhost_type(derived)->name ); + + BasePtr d3( new DoubleDerived2 ); + derived = to_nasal(c, d3); + BOOST_CHECK( naIsGhost(derived) ); + BOOST_CHECK_EQUAL( std::string("DoubleDerived2Ptr"), + naGhost_type(derived)->name ); + + SGRefBasedPtr ref_based( new SGReferenceBasedClass ); + naRef na_ref_based = to_nasal(c, ref_based.get()); + BOOST_CHECK( naIsGhost(na_ref_based) ); + BOOST_CHECK_EQUAL( from_nasal(c, na_ref_based), + ref_based.get() ); + BOOST_CHECK_EQUAL( from_nasal(c, na_ref_based), ref_based ); + + BOOST_CHECK_EQUAL( from_nasal(c, derived), d3 ); + BOOST_CHECK_NE( from_nasal(c, derived), d2 ); + BOOST_CHECK_EQUAL( from_nasal(c, derived), + boost::dynamic_pointer_cast(d3) ); + BOOST_CHECK_EQUAL( from_nasal(c, derived), + boost::dynamic_pointer_cast(d3) ); + BOOST_CHECK_THROW( from_nasal(c, derived), bad_nasal_cast ); + + std::map instances; + BOOST_CHECK( naIsHash(to_nasal(c, instances)) ); + + std::map instances_d; + BOOST_CHECK( naIsHash(to_nasal(c, instances_d)) ); + + std::map int_map; + BOOST_CHECK( naIsHash(to_nasal(c, int_map)) ); + + std::map > int_vector_map; + BOOST_CHECK( naIsHash(to_nasal(c, int_vector_map)) ); + + simgear::StringMap dict = + simgear::StringMap("hello", "value") + ("key2", "value2"); + naRef na_dict = to_nasal(c, dict); + BOOST_REQUIRE( naIsHash(na_dict) ); + BOOST_CHECK_EQUAL( Hash(na_dict, c).get("key2"), "value2" ); + + // Check converting to Ghost if using Nasal hashes with actual ghost inside + // the hashes parents vector + std::vector parents; + parents.push_back(hash.get_naRef()); + parents.push_back(derived); + + Hash obj(c); + obj.set("parents", parents); + BOOST_CHECK_EQUAL( from_nasal(c, obj.get_naRef()), d3 ); + + // Check recursive parents (aka parent-of-parent) + std::vector parents2; + parents2.push_back(obj.get_naRef()); + Hash derived_obj(c); + derived_obj.set("parents", parents2); + BOOST_CHECK_EQUAL( from_nasal(c, derived_obj.get_naRef()), d3 ); + + std::vector nasal_objects; + nasal_objects.push_back( Ghost::makeGhost(c, d) ); + nasal_objects.push_back( Ghost::makeGhost(c, d2) ); + nasal_objects.push_back( Ghost::makeGhost(c, d3) ); + naRef obj_vec = to_nasal(c, nasal_objects); + + std::vector objects = from_nasal >(c, obj_vec); + BOOST_CHECK_EQUAL( objects[0], d ); + BOOST_CHECK_EQUAL( objects[1], d2 ); + BOOST_CHECK_EQUAL( objects[2], d3 ); + + { + // Calling fallback setter for unset values + const char* src_code = "me.test = 3;"; + int errLine = -1; + naRef code = naParseCode( c, to_nasal(c, "source.nas"), 0, + (char*)src_code, strlen(src_code), + &errLine ); + ret = naCallMethod(code, derived, 0, 0, naNil()); + + BOOST_REQUIRE( !naGetError(c) ); // TODO real error check (this seems to + // always return 0... + BOOST_CHECK_EQUAL( from_nasal(c, ret), 3 ); + } + { + // Calling generic (fallback) getter + const char* src_code = "var a = me.get_test;"; + int errLine = -1; + naRef code = naParseCode( c, to_nasal(c, "source.nas"), 0, + (char*)src_code, strlen(src_code), + &errLine ); + ret = naCallMethod(code, derived, 0, 0, naNil()); + + BOOST_REQUIRE( !naGetError(c) ); // TODO real error check (this seems to + // always return 0... + BOOST_CHECK_EQUAL( from_nasal(c, ret), "generic-get" ); + } + + //---------------------------------------------------------------------------- + // Test nasal::CallContext + //---------------------------------------------------------------------------- + + + int int_vec[] = {1,2,3}; + std::map map; + naRef args[] = { + to_nasal(c, std::string("test-arg")), + to_nasal(c, 4), + to_nasal(c, int_vec), + to_nasal(c, map) + }; + CallContext cc(c, naNil(), sizeof(args)/sizeof(args[0]), args); + BOOST_CHECK_EQUAL( cc.requireArg(0), "test-arg" ); + BOOST_CHECK_EQUAL( cc.getArg(0), "test-arg" ); + BOOST_CHECK_EQUAL( cc.getArg(10), "" ); + BOOST_CHECK( cc.isString(0) ); + BOOST_CHECK( !cc.isNumeric(0) ); + BOOST_CHECK( !cc.isVector(0) ); + BOOST_CHECK( !cc.isHash(0) ); + BOOST_CHECK( !cc.isGhost(0) ); + BOOST_CHECK( cc.isNumeric(1) ); + BOOST_CHECK( cc.isVector(2) ); + BOOST_CHECK( cc.isHash(3) ); + + naRef args_vec = nasal::to_nasal(c, args); + BOOST_CHECK( naIsVector(args_vec) ); + + //---------------------------------------------------------------------------- + // Test nasal::String + //---------------------------------------------------------------------------- + + String string( to_nasal(c, "Test") ); + BOOST_CHECK_EQUAL( from_nasal(c, string.get_naRef()), "Test" ); + BOOST_CHECK_EQUAL( string.c_str(), std::string("Test") ); + BOOST_CHECK( string.starts_with(string) ); + BOOST_CHECK( string.starts_with(String(c, "T")) ); + BOOST_CHECK( string.starts_with(String(c, "Te")) ); + BOOST_CHECK( string.starts_with(String(c, "Tes")) ); + BOOST_CHECK( string.starts_with(String(c, "Test")) ); + BOOST_CHECK( !string.starts_with(String(c, "Test1")) ); + BOOST_CHECK( !string.starts_with(String(c, "bb")) ); + BOOST_CHECK( !string.starts_with(String(c, "bbasdasdafasd")) ); + BOOST_CHECK( string.ends_with(String(c, "t")) ); + BOOST_CHECK( string.ends_with(String(c, "st")) ); + BOOST_CHECK( string.ends_with(String(c, "est")) ); + BOOST_CHECK( string.ends_with(String(c, "Test")) ); + BOOST_CHECK( !string.ends_with(String(c, "1Test")) ); + BOOST_CHECK( !string.ends_with(String(c, "abc")) ); + BOOST_CHECK( !string.ends_with(String(c, "estasdasd")) ); + BOOST_CHECK_EQUAL( string.find('e'), 1 ); + BOOST_CHECK_EQUAL( string.find('9'), String::npos ); + BOOST_CHECK_EQUAL( string.find_first_of(String(c, "st")), 2 ); + BOOST_CHECK_EQUAL( string.find_first_of(String(c, "st"), 3), 3 ); + BOOST_CHECK_EQUAL( string.find_first_of(String(c, "xyz")), String::npos ); + BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "Tst")), 1 ); + BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "Tse"), 2), 3 ); + BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "abc")), 0 ); + BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "abc"), 20), String::npos ); + + naFreeContext(c); +} diff --git a/simgear/nasal/cppbind/test/cppbind_test_ghost.cxx b/simgear/nasal/cppbind/test/cppbind_test_ghost.cxx new file mode 100644 index 00000000..d74f6a14 --- /dev/null +++ b/simgear/nasal/cppbind/test/cppbind_test_ghost.cxx @@ -0,0 +1,188 @@ +#define BOOST_TEST_MODULE cppbind +#include + +#include +#include + +#include +#include + +class Base1: + public virtual SGVirtualWeakReferenced +{}; + +class Base2: + public virtual SGVirtualWeakReferenced +{}; + +class Derived: + public Base1, + public Base2 +{}; + +typedef SGSharedPtr Base1Ptr; +typedef SGSharedPtr Base2Ptr; +typedef SGSharedPtr DerivedPtr; +typedef SGWeakPtr DerivedWeakPtr; + +typedef SGSharedPtr SGReferencedPtr; + +// Check if shared_ptr_traits give correct types for strong and weak shared +// pointer +#define CHECK_PTR_TRAIT_TYPE(ptr_t, type_name, type)\ + BOOST_STATIC_ASSERT((boost::is_same<\ + nasal::shared_ptr_traits::type_name,\ + type\ + >::value)); + +#define CHECK_PTR_TRAIT(ref, weak)\ + CHECK_PTR_TRAIT_TYPE(ref, strong_ref, ref)\ + CHECK_PTR_TRAIT_TYPE(weak, weak_ref, weak)\ + +CHECK_PTR_TRAIT(DerivedPtr, DerivedWeakPtr) +CHECK_PTR_TRAIT(boost::shared_ptr, boost::weak_ptr) + +#undef CHECK_PTR_TRAIT +#undef CHECK_PTR_TRAIT_TYPE + +BOOST_STATIC_ASSERT(( nasal::supports_weak_ref::value)); +BOOST_STATIC_ASSERT(( nasal::supports_weak_ref::value)); +BOOST_STATIC_ASSERT(( nasal::supports_weak_ref::value)); +BOOST_STATIC_ASSERT(( nasal::supports_weak_ref::value)); +BOOST_STATIC_ASSERT((!nasal::supports_weak_ref::value)); + +static void setupGhosts() +{ + nasal::Ghost::init("Base1"); + nasal::Ghost::init("Base2"); + nasal::Ghost::init("Derived") + .bases() + .bases(); +} + +//------------------------------------------------------------------------------ +BOOST_AUTO_TEST_CASE( ghost_weak_strong_nasal_conversion ) +{ + setupGhosts(); + naContext c = naNewContext(); + + DerivedPtr d = new Derived(); + DerivedWeakPtr weak(d); + + // store weak pointer and extract strong pointer + naRef na_d = nasal::to_nasal(c, DerivedWeakPtr(d)); + BOOST_REQUIRE( naIsGhost(na_d) ); + BOOST_CHECK_EQUAL( nasal::from_nasal(c, na_d), d ); + BOOST_CHECK_EQUAL( nasal::from_nasal(c, na_d), d ); + BOOST_CHECK_EQUAL( nasal::from_nasal(c, na_d), d ); + + d.reset(); + BOOST_REQUIRE_THROW( nasal::from_nasal(c, na_d), + nasal::bad_nasal_cast ); + + // store strong pointer and extract weak pointer + d.reset(new Derived); + na_d = nasal::to_nasal(c, d); + BOOST_REQUIRE( naIsGhost(na_d) ); + + weak = nasal::from_nasal(c, na_d); + BOOST_CHECK_EQUAL( weak.lock(), d ); + + d.reset(); + BOOST_REQUIRE( nasal::from_nasal(c, na_d) ); + BOOST_REQUIRE( weak.lock() ); + + naFreeContext(c); + naGC(); + nasal::ghostProcessDestroyList(); + + BOOST_REQUIRE( !weak.lock() ); +} + +//------------------------------------------------------------------------------ +BOOST_AUTO_TEST_CASE( ghost_casting_storage ) +{ + setupGhosts(); + nasal::Context c; + + // Check converting to and from every class in the hierarchy for an instance + // of the leaf class + DerivedPtr d = new Derived(); + + naRef na_d = nasal::to_nasal(c, d), + na_b1 = nasal::to_nasal(c, Base1Ptr(d)), + na_b2 = nasal::to_nasal(c, Base2Ptr(d)); + + Derived *d0 = nasal::from_nasal(c, na_d), + *d1 = nasal::from_nasal(c, na_b1), + *d2 = nasal::from_nasal(c, na_b2); + + BOOST_CHECK_EQUAL(d0, d.get()); + BOOST_CHECK_EQUAL(d1, d.get()); + BOOST_CHECK_EQUAL(d2, d.get()); + + Base1 *b1 = nasal::from_nasal(c, na_b1); + BOOST_CHECK_EQUAL(b1, static_cast(d.get())); + + Base2 *b2 = nasal::from_nasal(c, na_b2); + BOOST_CHECK_EQUAL(b2, static_cast(d.get())); + + // Check converting from base class instance to derived classes is not + // possible + Base1Ptr b1_ref = new Base1(); + na_b1 = nasal::to_nasal(c, b1_ref); + + BOOST_CHECK_EQUAL(nasal::from_nasal(c, na_b1), b1_ref.get()); + BOOST_CHECK_THROW(nasal::from_nasal(c, na_b1), nasal::bad_nasal_cast); + BOOST_CHECK_THROW(nasal::from_nasal(c, na_b1), nasal::bad_nasal_cast); +} + +//------------------------------------------------------------------------------ +#define CHECK_PTR_STORAGE_TRAIT_TYPE(ptr_t, storage)\ + BOOST_STATIC_ASSERT((boost::is_same<\ + nasal::shared_ptr_storage::storage_type,\ + storage\ + >::value)); + +CHECK_PTR_STORAGE_TRAIT_TYPE(DerivedPtr, Derived) +CHECK_PTR_STORAGE_TRAIT_TYPE(DerivedWeakPtr, DerivedWeakPtr) + +typedef boost::shared_ptr BoostDerivedPtr; +CHECK_PTR_STORAGE_TRAIT_TYPE(BoostDerivedPtr, BoostDerivedPtr) + +typedef boost::weak_ptr BoostDerivedWeakPtr; +CHECK_PTR_STORAGE_TRAIT_TYPE(BoostDerivedWeakPtr, BoostDerivedWeakPtr) + +#undef CHECK_PTR_STORAGE_TRAIT_TYPE + +BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits::is_intrusive::value)); +BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits::is_intrusive::value)); +BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits::is_intrusive::value)); +BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits::is_intrusive::value)); +BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits::is_intrusive::value)); + +BOOST_STATIC_ASSERT((!nasal::shared_ptr_traits >::is_intrusive::value)); +BOOST_STATIC_ASSERT((!nasal::shared_ptr_traits >::is_intrusive::value)); + +BOOST_AUTO_TEST_CASE( storage_traits ) +{ + DerivedPtr d = new Derived(); + + Derived* d_raw = nasal::shared_ptr_storage::ref(d); + BOOST_REQUIRE_EQUAL(d_raw, d.get()); + BOOST_REQUIRE_EQUAL(d.getNumRefs(), 2); + + DerivedWeakPtr* d_weak = nasal::shared_ptr_storage::ref(d); + BOOST_REQUIRE_EQUAL( + nasal::shared_ptr_storage::get(d_weak), + d_raw + ); + + d.reset(); + BOOST_REQUIRE_EQUAL(Derived::count(d_raw), 1); + + nasal::shared_ptr_storage::unref(d_raw); + BOOST_REQUIRE(d_weak->expired()); + + nasal::shared_ptr_storage::unref(d_weak); +} diff --git a/simgear/nasal/cppbind/test/nasal_gc_test.cxx b/simgear/nasal/cppbind/test/nasal_gc_test.cxx new file mode 100644 index 00000000..c1cb6ca3 --- /dev/null +++ b/simgear/nasal/cppbind/test/nasal_gc_test.cxx @@ -0,0 +1,93 @@ +#define BOOST_TEST_MODULE nasal +#include + +#include "TestContext.hxx" +#include +#include + +static std::set active_instances; + +static void ghost_destroy(void* p) +{ + active_instances.erase((intptr_t)p); +} + +static naGhostType ghost_type = { + &ghost_destroy, + "TestGhost", + 0, // get_member + 0 // set_member +}; + +static naRef createTestGhost(TestContext& c, intptr_t p) +{ + active_instances.insert(p); + return naNewGhost(c.c, &ghost_type, (void*)p); +} + +//------------------------------------------------------------------------------ +BOOST_AUTO_TEST_CASE( ghost_gc ) +{ + TestContext c; + BOOST_REQUIRE(active_instances.empty()); + + //----------------------------------------------- + // Just create ghosts and let the gc destroy them + + naRef g1 = createTestGhost(c, 1), + g2 = createTestGhost(c, 2); + + BOOST_CHECK_EQUAL(active_instances.count(1), 1); + BOOST_CHECK_EQUAL(active_instances.count(2), 1); + BOOST_CHECK_EQUAL(active_instances.size(), 2); + + c.runGC(); + + BOOST_REQUIRE(active_instances.empty()); + + + //----------------------------------------------- + // Create some more ghosts and save one instance + // from being garbage collected. + + g1 = createTestGhost(c, 1); + g2 = createTestGhost(c, 2); + + int gc1 = naGCSave(g1); + c.runGC(); + + BOOST_CHECK_EQUAL(active_instances.count(1), 1); + BOOST_CHECK_EQUAL(active_instances.size(), 1); + + naGCRelease(gc1); + c.runGC(); + + BOOST_REQUIRE(active_instances.empty()); + + + //----------------------------------------------- + // Now test attaching one ghost to another + + g1 = createTestGhost(c, 1); + g2 = createTestGhost(c, 2); + + gc1 = naGCSave(g1); + naGhost_setData(g1, g2); // bind lifetime of g2 to g1... + + c.runGC(); + + BOOST_CHECK_EQUAL(active_instances.count(1), 1); + BOOST_CHECK_EQUAL(active_instances.count(2), 1); + BOOST_CHECK_EQUAL(active_instances.size(), 2); + + naGhost_setData(g1, naNil()); // cut connection + c.runGC(); + + BOOST_CHECK_EQUAL(active_instances.count(1), 1); + BOOST_CHECK_EQUAL(active_instances.size(), 1); + + naGCRelease(gc1); + c.runGC(); + + BOOST_REQUIRE(active_instances.empty()); +} diff --git a/simgear/nasal/cppbind/test/nasal_num_test.cxx b/simgear/nasal/cppbind/test/nasal_num_test.cxx new file mode 100644 index 00000000..99000662 --- /dev/null +++ b/simgear/nasal/cppbind/test/nasal_num_test.cxx @@ -0,0 +1,52 @@ +#define BOOST_TEST_MODULE nasal +#include + +#include "TestContext.hxx" + +static void runNumTests( double (TestContext::*test_double)(const std::string&), + int (TestContext::*test_int)(const std::string&) ) +{ + TestContext c; + + BOOST_CHECK_CLOSE((c.*test_double)("0.5"), 0.5, 1e-5); + BOOST_CHECK_CLOSE((c.*test_double)(".6"), 0.6, 1e-5); + BOOST_CHECK_CLOSE((c.*test_double)("-.7"), -0.7, 1e-5); + BOOST_CHECK_CLOSE((c.*test_double)("-0.8"), -0.8, 1e-5); + BOOST_CHECK_SMALL((c.*test_double)("0.0"), 1e-5); + BOOST_CHECK_SMALL((c.*test_double)("-.0"), 1e-5); + + BOOST_CHECK_CLOSE((c.*test_double)("1.23e4"), 1.23e4, 1e-5); + BOOST_CHECK_CLOSE((c.*test_double)("1.23e-4"), 1.23e-4, 1e-5); + BOOST_CHECK_CLOSE((c.*test_double)("-1.23e4"), -1.23e4, 1e-5); + BOOST_CHECK_CLOSE((c.*test_double)("-1.23e-4"), -1.23e-4, 1e-5); + BOOST_CHECK_CLOSE((c.*test_double)("1e-4"), 1e-4, 1e-5); + BOOST_CHECK_CLOSE((c.*test_double)("-1e-4"), -1e-4, 1e-5); + + BOOST_CHECK_EQUAL((c.*test_int)("123"), 123); + BOOST_CHECK_EQUAL((c.*test_int)("-958"), -958); + + BOOST_CHECK_CLOSE((c.*test_int)("-1e7"), -1e7, 1e-5); + BOOST_CHECK_CLOSE((c.*test_int)("2E07"), 2e07, 1e-5); + + BOOST_CHECK_EQUAL((c.*test_int)("0755"), 755); + BOOST_CHECK_EQUAL((c.*test_int)("0055"), 55); + BOOST_CHECK_EQUAL((c.*test_int)("-0155"), -155); + + BOOST_CHECK_EQUAL((c.*test_int)("0o755"), 0755); + BOOST_CHECK_EQUAL((c.*test_int)("0o055"), 055); + BOOST_CHECK_EQUAL((c.*test_int)("-0o155"), -0155); + + BOOST_CHECK_EQUAL((c.*test_int)("0x755"), 0x755); + BOOST_CHECK_EQUAL((c.*test_int)("0x055"), 0x55); + BOOST_CHECK_EQUAL((c.*test_int)("-0x155"), -0x155); +} + +BOOST_AUTO_TEST_CASE( parse_num ) +{ + runNumTests(&TestContext::convert, &TestContext::convert); +} + +BOOST_AUTO_TEST_CASE( lex_num ) +{ + runNumTests(&TestContext::exec, &TestContext::exec); +} diff --git a/simgear/nasal/data.h b/simgear/nasal/data.h index 25f7008e..9a91c3e8 100644 --- a/simgear/nasal/data.h +++ b/simgear/nasal/data.h @@ -175,6 +175,7 @@ struct naGhost { GC_HEADER; naGhostType* gtype; void* ptr; + naRef data; //!< Nasal data bound to the lifetime of the ghost. }; struct naPool { diff --git a/simgear/nasal/gc.c b/simgear/nasal/gc.c index a965aa79..5ac9c43c 100644 --- a/simgear/nasal/gc.c +++ b/simgear/nasal/gc.c @@ -177,7 +177,7 @@ static void newBlock(struct naPool* p, int need) newb->next = p->blocks; p->blocks = newb; naBZero(newb->block, need * p->elemsz); - + if(need > p->freesz - p->freetop) need = p->freesz - p->freetop; p->nfree = 0; p->free = p->free0 + p->freetop; @@ -263,6 +263,9 @@ static void mark(naRef r) mark(PTR(r).func->namespace); mark(PTR(r).func->next); break; + case T_GHOST: + mark(PTR(r).ghost->data); + break; } } @@ -300,7 +303,7 @@ static void reap(struct naPool* p) // allocs of this type until the next collection globals->allocCount += total/2; - + // Allocate more if necessary (try to keep 25-50% of the objects // available) if(p->nfree < total/4) { diff --git a/simgear/nasal/misc.c b/simgear/nasal/misc.c index 5a531d53..0a5c8615 100644 --- a/simgear/nasal/misc.c +++ b/simgear/nasal/misc.c @@ -143,10 +143,11 @@ naRef naNewGhost(naContext c, naGhostType* type, void* ptr) // ensure 'simple' ghost users don't see garbage for these fields type->get_member = 0; type->set_member = 0; - + ghost = naNew(c, T_GHOST); PTR(ghost).ghost->gtype = type; PTR(ghost).ghost->ptr = ptr; + PTR(ghost).ghost->data = naNil(); return ghost; } @@ -155,6 +156,7 @@ naRef naNewGhost2(naContext c, naGhostType* t, void* ptr) naRef ghost = naNew(c, T_GHOST); PTR(ghost).ghost->gtype = t; PTR(ghost).ghost->ptr = ptr; + PTR(ghost).ghost->data = naNil(); return ghost; } @@ -170,9 +172,21 @@ void* naGhost_ptr(naRef ghost) return PTR(ghost).ghost->ptr; } +void naGhost_setData(naRef ghost, naRef data) +{ + if(IS_GHOST(ghost)) + PTR(ghost).ghost->data = data; +} + +naRef naGhost_data(naRef ghost) +{ + if(!IS_GHOST(ghost)) return naNil(); + return PTR(ghost).ghost->data; +} + naRef naNil() { - naRef r; + naRef r; SETPTR(r, 0); return r; } diff --git a/simgear/nasal/nasal.h b/simgear/nasal/nasal.h index 83272447..8b6b2c92 100644 --- a/simgear/nasal/nasal.h +++ b/simgear/nasal/nasal.h @@ -281,6 +281,16 @@ naRef naNewGhost(naContext c, naGhostType* t, void* ghost); naRef naNewGhost2(naContext c, naGhostType* t, void* ghost); naGhostType* naGhost_type(naRef ghost); void* naGhost_ptr(naRef ghost); +/** + * Attach a nasal object to the given ghost. Binds the lifetime of @a data to + * the lifetime of the @a ghost. + */ +void naGhost_setData(naRef ghost, naRef data); +/** + * Retrieve the object attached to the @a ghost, previously set with + * naGhost_setData(). + */ +naRef naGhost_data(naRef ghost); int naIsGhost(naRef r); // Acquires a "modification lock" on a context, allowing the C code to