Re: super bad

Daniel Wallin <[email protected]>
Newsgroups gmane.comp.lang.lua.bind.user
Message-ID <[email protected]>
Nigel Atkinson wrote:

> I had a bug in an app I'm writing, and while it was my goof i.e. you
> shouldn't really rely on the order of objects being garbage collected, I
> thought I'd share a debugging post mortem.  I'm using 0.9.0 but it may
> apply to 0.8.1 and possibly 0.7 
>
> So I found that last created instance of a Lua class that has a base
> class, stayed around until lua_close, despite having any reference I
> knew about set to nil.
>
> In hind sight 'super' seems almost obvious.  Even though it is
> deprecated, it's still there, to tell you its deprecated when you use
> it. 
>
> It took me awhile to narrow it down to this, as I had shared pointers in
> the mix witch sent me barking up the wrong tree. ;-)
>
> I just commented out the 'super' code in class_rep.cpp in the 
>
> int luabind::detail::class_rep::constructor_dispatcher(lua_State* L)
>
> function.  It's pretty obvious what to zap.  
>
> Seeing as super is depreciated, this problem will go away eventually.  A
> quick fix I guess would be to set 'super' to nil, after you have created
> a class instance.
>   
Thanks for bringing this to my attention. I've pushed this fix to the
0.9 branch:

commit 2c99f0475afea7c282c2e432499fd22aa17744e3
Author: Daniel Wallin <[email protected]>
Date:   Wed Nov 25 21:23:52 2009 +0100

    Fix leak in "super" upvalue.
    
    The last created instance would stick around in the "super" upvalue
    unless "super()" was called from the constructor. This happened because
    "super" wasn't cleaned up after construction, only after being called.

diff --git a/src/class_rep.cpp b/src/class_rep.cpp
index 4ee657c..0f8ab1b 100755
--- a/src/class_rep.cpp
+++ b/src/class_rep.cpp
@@ -126,6 +126,12 @@ luabind::detail::class_rep::allocate(lua_State* L) const
 	return std::pair<void*,void*>(mem, 0);
 }
 
+namespace
+{
+
+  bool super_deprecation_disabled = false;
+
+} // namespace unnamed
 
 // this is called as metamethod __call on the class_rep.
 int luabind::detail::class_rep::constructor_dispatcher(lua_State* L)
@@ -142,8 +148,9 @@ int luabind::detail::class_rep::constructor_dispatcher(lua_State* L)
     lua_rawgeti(L, LUA_REGISTRYINDEX, cls->metatable_ref());
     lua_setmetatable(L, -2);
 
-    if (cls->get_class_type() == class_rep::lua_class
-     && !cls->bases().empty())
+    if (super_deprecation_disabled
+        && cls->get_class_type() == class_rep::lua_class
+        && !cls->bases().empty())
     {
         lua_pushstring(L, "super");
         lua_pushvalue(L, 1);
@@ -166,6 +173,13 @@ int luabind::detail::class_rep::constructor_dispatcher(lua_State* L)
 
     lua_call(L, args, 0);
 
+    if (super_deprecation_disabled)
+    {
+        lua_pushstring(L, "super");
+        lua_pushnil(L);
+        lua_settable(L, LUA_GLOBALSINDEX);
+    }
+
     return 1;
 }
 
@@ -192,13 +206,6 @@ void luabind::detail::class_rep::add_base_class(const luabind::detail::class_rep
 	m_bases.push_back(binfo);
 }
 
-namespace
-{
-
-  bool super_deprecation_disabled = false;
-
-} // namespace unnamed
-
 LUABIND_API void luabind::disable_super_deprecation()
 {
     super_deprecation_disabled = true;
@@ -206,16 +213,6 @@ LUABIND_API void luabind::disable_super_deprecation()
 
 int luabind::detail::class_rep::super_callback(lua_State* L)
 {
-	if (!super_deprecation_disabled)
-	{
-		lua_pushstring(L,
-			"DEPRECATION: 'super' has been deprecated in favor of "
-			"directly calling the base class __init() function. "
-			"This error can be disabled by calling 'luabind::disable_super_deprecation()'."
-		);
-		lua_error(L);
-	}
-
 	int args = lua_gettop(L);
 		
 	class_rep* crep = static_cast<class_rep*>(lua_touserdata(L, lua_upvalueindex(1)));
diff --git a/src/open.cpp b/src/open.cpp
index e39d53d..24ff4e8 100755
--- a/src/open.cpp
+++ b/src/open.cpp
@@ -52,6 +52,18 @@ namespace
 
   int main_thread_tag;
 
+  int deprecated_super(lua_State* L)
+  {
+      lua_pushstring(L,
+          "DEPRECATION: 'super' has been deprecated in favor of "
+          "directly calling the base class __init() function. "
+          "This error can be disabled by calling 'luabind::disable_super_deprecation()'."
+      );
+      lua_error(L);
+
+      return 0;
+  }
+
 } // namespace unnamed
 
     LUABIND_API lua_State* get_main_thread(lua_State* L)
@@ -142,6 +154,10 @@ namespace
         lua_pushlightuserdata(L, &main_thread_tag);
         lua_pushlightuserdata(L, L);
         lua_rawset(L, LUA_REGISTRYINDEX);
+
+        lua_pushstring(L, "super");
+        lua_pushcclosure(L, &deprecated_super, 0);
+        lua_settable(L, LUA_GLOBALSINDEX);
     }
 
 } // namespace luabind
diff --git a/test/Jamfile b/test/Jamfile
index 21f298c..d1343cb 100755
--- a/test/Jamfile
+++ b/test/Jamfile
@@ -42,6 +42,7 @@ SOURCES =
     test_create_in_thread.cpp
     test_extend_class_in_lua.cpp
     test_smart_ptr_attributes.cpp
+    test_super_leak.cpp
  ;
 
 obj main : main.cpp : <library>..//luabind : : <library>..//luabind ;
diff --git a/test/test_super_leak.cpp b/test/test_super_leak.cpp
new file mode 100644
index 0000000..e2d87e2
--- /dev/null
+++ b/test/test_super_leak.cpp
@@ -0,0 +1,60 @@
+// Copyright Daniel Wallin 2009. Use, modification and distribution is
+// subject to the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+
+#include "test.hpp"
+#include <luabind/luabind.hpp>
+
+struct Foo
+{
+    static std::size_t count;
+
+    Foo()
+    {
+        ++count;
+    }
+
+    ~Foo()
+    {
+        --count;
+    }
+};
+
+std::size_t Foo::count = 0;
+
+void test_main(lua_State* L)
+{
+    using namespace luabind;
+
+    module(L) [
+        class_<Foo>("Foo")
+          .def(constructor<>())
+    ];
+
+    // This used to leak the last created instance in one of the
+    // upvalues to the "super" function.
+
+    disable_super_deprecation();
+
+    DOSTRING(L,
+        "class 'Bar' (Foo)\n"
+        "function Bar.__init(self)\n"
+        "  Foo.__init(self)\n"
+        "end\n"
+    );
+
+    DOSTRING(L,
+        "x = Bar()\n"
+    );
+
+    assert(Foo::count == 1);
+
+    DOSTRING(L,
+        "x = nil\n"
+    );
+
+    lua_gc(L, LUA_GCCOLLECT, 0);
+
+    assert(Foo::count == 0);
+}


-- 
Daniel Wallin
BoostPro Computing
http://www.boostpro.com


------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day 
trial. Simplify your report design, integration and deployment - and focus on 
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.