← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~jarih/widelands/fix-lua-msvc into lp:widelands

 

Jari Hautio has proposed merging lp:~jarih/widelands/fix-lua-msvc into lp:widelands.

Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  #590472 Lua unit tests crash on win32/VS2008 build
  https://bugs.launchpad.net/bugs/590472


Fixes issues with lua scripting luna interfacing in Visual Studio 2008 (Bug #590472). Both multiple inheritance and virtual base classes are now supported on linux/gcc and windows/msvc.

Basic principle in the fixes was to ensure that whenever something is reinterpred_cast:ed to void* and stored to lua tables, it's always casted to exactly same type when it's casted back from void*.

Luna Mmethod dispatching is fixed to retrive method pointer using correct type using additional template parameter for parent type in m_method_dispatch.

Implemented property dispatching system so that proerties are accessed wit correct type information. Instead of storing lightuserdata with pointer to property structure to metatable, metatable now contains a table with lightuserdata for getter and setter as well as cfunction for dispatching the property. Templated dispatching function stores type information for parent type so that when property is accessed, types are same is when it was stored.

Implemented unit test support to test luna behaviour. There were some tricks needed to prevent linking with widelands main.cc or SDL_main:
 - WL_MAIN_SRCS contained main.cc due to greedy globbing. This is fixed by removing main.cc after globbing.
 - SDL_LIBRARY contained SDL_main.lib, which had to be removed before adding SDL libraries to test executable.

Test executable is only included in the build if boost unit_test_framework library is found. This way widelands can be built without compiling boost libraries.

To test:
 - run CMake tests after build using for example "make test".
 - run scripting unit test scenario ts.wmf
 - With windows/visual studio 2008 game crashed before this fix when unit tests or ts.wmf was run. 
 - On linux/mac this fix should not break anything.


-- 
https://code.launchpad.net/~jarih/widelands/fix-lua-msvc/+merge/30809
Your team Widelands Developers is requested to review the proposed merge of lp:~jarih/widelands/fix-lua-msvc into lp:widelands.
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt	2010-05-21 09:04:50 +0000
+++ CMakeLists.txt	2010-07-23 17:57:50 +0000
@@ -104,11 +104,13 @@
 set (CMAKE_CONFIGURATION_TYPES ${CMAKE_BUILD_TYPE})
 
 set (BUILD_SHARED_LIBS OFF)
+# we only include Boost Headers to the main executable, no libraries
+# unit_test_framework is for testing only
+set (Boost_FIND_COMPONENTS unit_test_framework)
 set (Boost_USE_STATIC_LIBS   ON)
 set (Boost_USE_MULTITHREADED ON)
-find_package(Boost 1.35.0 REQUIRED)
-include_directories(${Boost_INCLUDE_DIR})
-target_link_libraries(widelands ${Boost_LIBRARIES})
+set (Boost_DETAILED_FAILURE_MSG ON)
+find_package(Boost 1.35.0 COMPONENTS unit_test_framework REQUIRED)
 
 set (PARAMETER_COMPILERFLAG_OLDSTYLECAST_EXTENDED "-Wold-style-cast -isystem ${Boost_INCLUDE_DIR}")
 set (PARAMETER_COMPILERFLAG_OLDSTYLECAST "-Wold-style-cast")
@@ -176,6 +178,9 @@
   add_definitions( -Dssize_t=size_t )
   add_definitions( -Dmkdir=_mkdir )
 
+  include_directories(${Boost_INCLUDE_DIR})
+  # Automatic linking for boost requires setting lib dir
+  LINK_DIRECTORIES(${Boost_LIBRARY_DIRS})
 
   find_library(INTL_LIBRARY NAMES intl libintl)
 
@@ -285,6 +290,8 @@
   COMPONENT CoreVersionFile
 )
 
+enable_testing()
+
 include_directories(src ${CMAKE_CURRENT_BINARY_DIR}/src)
 
 include(CheckIncludeFile)

=== modified file 'src/CMakeLists.txt'
--- src/CMakeLists.txt	2010-05-21 09:04:50 +0000
+++ src/CMakeLists.txt	2010-07-23 17:57:50 +0000
@@ -26,6 +26,8 @@
 file(GLOB_RECURSE WL_SRCS_ALL *.cc)
 
 file(GLOB WL_MAIN_SRCS *.cc)
+file(GLOB WL_MAIN_CC main.cc)
+list(REMOVE_ITEM WL_MAIN_SRCS ${WL_MAIN_CC})
 add_library(widelands_main ${WL_MAIN_SRCS})
 
 #file (GLOB WL_SRCS *.cc economy/*.cc scripting/*.cc)

=== modified file 'src/scripting/CMakeLists.txt'
--- src/scripting/CMakeLists.txt	2010-02-05 01:13:20 +0000
+++ src/scripting/CMakeLists.txt	2010-07-23 17:57:50 +0000
@@ -4,3 +4,9 @@
 
 target_link_libraries(widelands_scripting ${LUA_LIBRARIES})
 
+# Build tests only if unit test framework is available.
+if(Boost_UNIT_TEST_FRAMEWORK_LIBRARY)
+  add_subdirectory(test)
+else()
+  message("Excluded unit tests for scripting because boost unit_test_framework was not found.")
+endif()

=== modified file 'src/scripting/luna.h'
--- src/scripting/luna.h	2010-03-21 18:12:27 +0000
+++ src/scripting/luna.h	2010-07-23 17:57:50 +0000
@@ -110,7 +110,7 @@
 
 	m_create_metatable_for_class<T>(L);
 
-	m_register_properties_in_metatable<T>(L);
+	m_register_properties_in_metatable<T,T>(L);
 	m_register_methods_in_metatable<T, T>(L);
 
 	if(!return_metatable)
@@ -124,7 +124,7 @@
 template <class T, class PT>
 void add_parent(lua_State * L)
 {
-	m_register_properties_in_metatable<PT>(L);
+	m_register_properties_in_metatable<T,PT>(L);
 	m_register_methods_in_metatable<T, PT>(L);
 }
 

=== modified file 'src/scripting/luna_impl.h'
--- src/scripting/luna_impl.h	2010-03-05 14:32:47 +0000
+++ src/scripting/luna_impl.h	2010-07-23 17:57:50 +0000
@@ -54,22 +54,45 @@
 template <class T> T * * get_user_class(lua_State * const L, int narg);
 
 template <class T>
-PropertyType<T> const * m_lookup_property_in_metatable(lua_State * const L) {
-	// stack: table name
-
+int m_dispatch_property_in_metatable(lua_State * const L, bool setter) {
+	// stack for getter: table name
+	// stack for setter: table name value
+	int ret = 0;
 	// Look up the key in the metatable
-	lua_getmetatable(L,  1); // table name mt
-	lua_pushvalue   (L,  2); // table name mt name
-	lua_rawget      (L, -2); // table name mt mt_val
-
-	const PropertyType<T> * rv = 0;
-
-	if (lua_islightuserdata(L, -1))
-		rv = static_cast<const PropertyType<T> *>(lua_touserdata(L, -1));
-
-	lua_remove(L, -2); // table name mt_val
-
-	return rv;
+	lua_getmetatable(L,  1); // table name <value> mt
+	lua_pushvalue   (L,  2); // table name <value> mt name
+	lua_rawget      (L, -2); // table name <value> mt mt_val
+	lua_remove(L, -2); // table name <value> mt_val
+	if (lua_istable(L, -1))
+	{	
+		// dispatcher 
+		
+		lua_pushstring(L, "dispatcher"); // table name <value> mt_val "dispatcher"
+		lua_gettable(L, -2); // table name <value> mt_val dispatcher_val
+		if (!lua_iscfunction(L, -1))
+		{
+			lua_pop(L, 2); //  table name <value>
+			return report_error(L, "invalid property without dispatcher function");
+		}
+		lua_CFunction dispatcher = lua_tocfunction(L, -1);
+		lua_pop(L, 1); // table name <value> mt_val 
+
+		// get property method to stack
+		lua_pushstring(L, setter ? "setter" : "getter");
+		lua_gettable(L, -2); // table name <value> mt_val getter_val/setter_val
+		lua_remove(L, -2); // table name <value> getter_val/setter_val
+		// dispatcher pops off getter/setter and returns whatever get/set property functions returns
+		ret = dispatcher(L); // table name value
+	} else {
+		if (setter) {
+			lua_rawset(L, 1);
+		} else {
+			// leave the value to stack
+			ret = 1;
+		}
+	}
+
+	return ret;
 }
 
 /**
@@ -94,42 +117,43 @@
 	}
 	lua_pop(L, 1); // table name
 
-	const PropertyType<T>* list = m_lookup_property_in_metatable<T>(L);
-	// stack: table name list
-
-	// Not in metatable?, return it
-	if (!list) {
-		return 1;
-	}
-	lua_pop(L, 1); // table name
-
-	T * * const obj = get_user_class<T>(L, 1);
-	// table name
-
-	// push value on top of the stack for the method call
-	return ((*obj)->*(list->getter))(L);
+	return m_dispatch_property_in_metatable<T>(L, false);
 }
 
 template <class T>
 int m_property_setter(lua_State * const L) {
 	// stack: table name value
-	PropertyType<T> const * list = m_lookup_property_in_metatable<T>(L);
-	lua_pop(L, 1); // table name value
-	// table name value rv
-
-	if (!list) {
-		// Not in metatable?
-		// do a normal set on the table
-		lua_rawset(L, 1);
-		return 0;
+	return  m_dispatch_property_in_metatable<T>(L, true);
+}
+
+/**
+ * This function calls a lua method in our given object.  we can already be
+ * sure that the called method is registered in our metatable, but we have to
+ * make sure that the method is called correctly: obj.method(obj) or
+ * obj:method(). We also check that we are not called with another object
+ */
+template <class T, class PT>
+int m_property_dispatch(lua_State * const L) {
+	// Check for invalid: obj.method()
+	int const n = lua_gettop(L);
+	if (!n)
+		return report_error(L, "Property needs at least the object as argument!");
+
+	// Check for invalid: obj.method(plainOldDatatype)
+	luaL_checktype(L, 1, LUA_TTABLE);
+
+	typedef int (PT::* const * ConstMethodPtr)(lua_State *);
+	ConstMethodPtr pfunc = reinterpret_cast<ConstMethodPtr>(lua_touserdata(L, -1));
+	lua_pop(L, 1); 
+
+	T * * const obj = get_user_class<T>(L, 1);
+
+	if (!*pfunc)
+	{
+		return report_error(L, "The property is read-only!\n");
 	}
-
-	T * * const obj = get_user_class<T>(L, 1);
-
-	if (list->setter == 0)
-		return report_error(L, "The property '%s' is read-only!\n", list->name);
-
-	return ((*obj)->*(list->setter))(L);
+	// Call it on our instance
+	return ((*obj)->*(*pfunc))(L);
 }
 
 /**
@@ -138,7 +162,7 @@
  * make sure that the method is called correctly: obj.method(obj) or
  * obj:method(). We also check that we are not called with another object
  */
-template <class T>
+template <class T, class PT>
 int m_method_dispatch(lua_State * const L) {
 	// Check for invalid: obj.method()
 	int const n = lua_gettop(L);
@@ -149,7 +173,7 @@
 	luaL_checktype(L, 1, LUA_TTABLE);
 
 	// Get the method pointer from the closure
-	typedef int (T::* const * ConstMethod)(lua_State *);
+	typedef int (PT::* const * ConstMethod)(lua_State *);
 	ConstMethod func = reinterpret_cast<ConstMethod>
 		(lua_touserdata(L, lua_upvalueindex(1)));
 
@@ -250,17 +274,33 @@
 	return metatable;
 }
 
-template <class T>
+template <class T, class PT>
 void m_register_properties_in_metatable
 	(lua_State * const L)
 {
-	for (int i = 0; T::Properties[i].name; ++i) {
+	for (int i = 0; PT::Properties[i].name; ++i) {
 		// metatable[prop_name] = Pointer to getter setter
-		lua_pushstring(L, T::Properties[i].name);
-		lua_pushlightuserdata
-			(L,
-			 const_cast<void *>
-			 	(reinterpret_cast<void const *>(&T::Properties[i])));
+		lua_pushstring(L, PT::Properties[i].name);
+		lua_newtable(L);
+		lua_pushstring(L, "getter");
+		lua_pushlightuserdata
+			(L,
+			 const_cast<void *>
+			 	(reinterpret_cast<const void *>
+					(&(PT::Properties[i].getter))));
+		lua_settable(L, -3);
+		lua_pushstring(L, "setter");
+		lua_pushlightuserdata
+			(L,
+			 const_cast<void *>
+			 	(reinterpret_cast<const void *>
+					(&(PT::Properties[i].setter))));
+		lua_settable(L, -3);
+
+		lua_pushstring(L, "dispatcher");
+		lua_pushcfunction(L, &(m_property_dispatch<T, PT>));
+		lua_settable(L, -3);
+
 		lua_settable(L, -3); // Metatable is directly before our pushed stuff
 	}
 }
@@ -279,7 +319,7 @@
 			(L,
 			 const_cast<void *>
 			 	(reinterpret_cast<void const *>(&PT::Methods[i].method)));
-		lua_pushcclosure(L, &(m_method_dispatch<T>), 1);
+		lua_pushcclosure(L, &(m_method_dispatch<T, PT>), 1);
 		lua_settable(L, -3); // Metatable is directly before our pushed stuff
 	}
 }

=== modified file 'src/scripting/scripting.cc'
--- src/scripting/scripting.cc	2010-05-23 19:00:49 +0000
+++ src/scripting/scripting.cc	2010-07-23 17:57:50 +0000
@@ -225,6 +225,8 @@
 	}
 
 	// Push the instance of this class into the registry
+	// MSVC2008 requires that stored and retrieved types are
+	// same, so use LuaInterface* on both sides.
 	lua_pushlightuserdata(m_L, reinterpret_cast<void*>(dynamic_cast<LuaInterface*>(this)));
 	lua_setfield(m_L, LUA_REGISTRYINDEX, "lua_interface");
 

=== added file 'src/scripting/test/CMakeLists.txt'
--- src/scripting/test/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ src/scripting/test/CMakeLists.txt	2010-07-23 17:57:50 +0000
@@ -0,0 +1,51 @@
+file(GLOB WL_SCRIPTING_TEST_SRCS *.cc)
+
+add_executable(test_widelands_scripting ${WL_SCRIPTING_TEST_SRCS} ../../build_info.cc)
+
+set(SDL_LIBRARY_NOMAIN ${SDL_LIBRARY})
+list(REMOVE_ITEM SDL_LIBRARY_NOMAIN  ${SDLMAIN_LIBRARY}) 
+
+target_link_libraries(test_widelands_scripting 
+	widelands_logic widelands_main widelands_ai widelands_scripting widelands_sound widelands_wui widelands_graphic widelands_economy widelands_map_io widelands_editor widelands_ui_fsmenu widelands_network widelands_game_io widelands_ui_basic widelands_profile widelands_io
+	widelands_io_filesystem widelands_editor widelands_editor_tools widelands_editor_ui_menus 
+	${Boost_LIBRARIES}
+	${PNG_LIBRARY}
+	${ZLIB_LIBRARY}
+	${JPEG_LIBRARY}
+	${TIFF_LIBRARY}
+	${SDL_LIBRARY_NOMAIN}
+	${SDLIMAGE_LIBRARY}
+	${SDLMIXER_LIBRARY}
+	${SDLNET_LIBRARY}
+	${SDLTTF_LIBRARY}
+	${LUA_LIBRARY}
+	${SDLGFX_LIBRARY}
+	)
+if (GGZ_CORE_FOUND)
+  target_link_libraries(test_widelands_scripting ${GGZ_CORE_LIBRARY})
+endif (GGZ_CORE_FOUND)
+
+if (APPLE)
+  target_link_libraries(test_widelands_scripting ${INTL_LIBRARY})
+endif (APPLE)
+
+if (WIN32)
+if (DEFINED MSVC)
+  target_link_libraries(test_widelands_scripting ${INTL_LIBRARY})
+  target_link_libraries(test_widelands_scripting ws2_32)
+  #GGZ on MSVC using 3rdparty bundle needs expat library
+  #if expat is not found, then 3rdparty bundle is not in use, so just continue.
+  if (GGZ_CORE_FOUND)
+    find_library(EXPAT_LIBRARY libexpat)
+    if (EXPAT_LIBRARY)
+      target_link_libraries(test_widelands_scripting ${EXPAT_LIBRARY})
+    endif (EXPAT_LIBRARY)
+  endif (GGZ_CORE_FOUND)
+else (DEFINED MSVC)
+  target_link_libraries(test_widelands_scripting intl)
+  target_link_libraries(test_widelands_scripting wsock32)
+endif (DEFINED MSVC) 
+endif (WIN32)
+
+
+add_test(test_widelands_scripting test_widelands_scripting)
\ No newline at end of file

=== added file 'src/scripting/test/scripting_test_main.cc'
--- src/scripting/test/scripting_test_main.cc	1970-01-01 00:00:00 +0000
+++ src/scripting/test/scripting_test_main.cc	2010-07-23 17:57:50 +0000
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2007-2008 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define BOOST_AUTO_TEST_MAIN
+#define BOOST_TEST_MODULE Scripting
+#include <boost/test/unit_test.hpp>
+

=== added file 'src/scripting/test/test_luna.cc'
--- src/scripting/test/test_luna.cc	1970-01-01 00:00:00 +0000
+++ src/scripting/test/test_luna.cc	2010-07-23 17:57:50 +0000
@@ -0,0 +1,354 @@
+#include <exception>
+#include <boost/test/unit_test.hpp>
+#include <boost/shared_ptr.hpp>
+#include <lua.hpp>
+#include "scripting/luna.h"
+
+#include "scripting/luna_impl.h"
+
+using namespace boost;
+
+#ifndef BEGIN_LUNA_PROPERTIES
+#define BEGIN_LUNA_PROPERTIES(klass) \
+	const PropertyType<klass> klass::Properties[] = {
+
+#define END_LUNA_PROPERTIES() {0, 0, 0}};
+#endif
+
+class L_Class : public LunaClass
+{
+	int x;
+	int prop;
+public:
+	LUNA_CLASS_HEAD(L_Class);
+	const char * get_modulename() {return "test";}
+	L_Class() :x(123), prop(246) {}
+	virtual ~L_Class() {}
+	L_Class(lua_State *L) :x(124), prop(248) {}
+	virtual int test(lua_State *L) 
+	{
+		lua_pushuint32(L, x);
+		return 1;
+	}
+	virtual int get_prop1(lua_State*L)
+	{
+		lua_pushint32(L, prop);
+		return 1;
+	}
+	virtual int get_propr(lua_State*L)
+	{
+		lua_pushint32(L, 0);
+		return 1;
+	}
+	virtual int set_prop1(lua_State*L)
+	{
+		prop = lua_tointeger(L, -1);
+		return 0;
+	}
+	virtual void __persist(lua_State * L) {}
+	virtual void __unpersist(lua_State * L) {}
+};
+const char L_Class::className[] = "Class";
+const MethodType<L_Class> L_Class::Methods[] = {
+	METHOD(L_Class, test),
+	{0, 0},
+};
+BEGIN_LUNA_PROPERTIES(L_Class)
+	PROP_RO(L_Class, propr),
+	PROP_RW(L_Class, prop1),
+END_LUNA_PROPERTIES()
+
+class L_SubClass : public L_Class
+{
+	int y;
+public:
+	LUNA_CLASS_HEAD(L_SubClass);
+	L_SubClass() :y(1230) {}
+	L_SubClass(lua_State *L) : L_Class(L), y(1240) {}
+	virtual int subtest(lua_State *L) 
+	{
+		lua_pushuint32(L, y);
+		return 1;
+	}
+	virtual void __persist(lua_State * L) {}
+	virtual void __unpersist(lua_State * L) {}
+};
+const char L_SubClass::className[] = "SubClass";
+const MethodType<L_SubClass> L_SubClass::Methods[] = {
+	METHOD(L_SubClass, subtest),
+	{0, 0},
+};
+BEGIN_LUNA_PROPERTIES(L_SubClass)
+END_LUNA_PROPERTIES()
+
+class L_VirtualClass : public virtual L_Class
+{
+	int z;
+public:
+	LUNA_CLASS_HEAD(L_VirtualClass);
+	L_VirtualClass() :z(12300) {}
+	L_VirtualClass(lua_State *L) : L_Class(L), z(12400) {}
+	virtual int virtualtest(lua_State *L) 
+	{
+		lua_pushuint32(L, z);
+		return 1;
+	}
+	virtual void __persist(lua_State * L) {}
+	virtual void __unpersist(lua_State * L) {}
+};
+const char L_VirtualClass::className[] = "VirtualClass";
+const MethodType<L_VirtualClass> L_VirtualClass::Methods[] = {
+	METHOD(L_VirtualClass, virtualtest),
+	{0, 0},
+};
+BEGIN_LUNA_PROPERTIES(L_VirtualClass)
+END_LUNA_PROPERTIES()
+
+class L_Second
+{
+public:
+	int get_second(lua_State *L) {lua_pushstring(L, "second"); return 1;}
+	virtual int multitest(lua_State *L) 
+	{
+		lua_pushstring(L, "secondfunc");
+		return 1;
+	}
+};
+
+class L_MultiClass : public L_Class, public L_Second
+{
+	int z;
+public:
+	LUNA_CLASS_HEAD(L_MultiClass );
+	L_MultiClass () :z(12300) {}
+	L_MultiClass (lua_State *L) : L_Class(L), z(12400) {}
+	virtual int virtualtest(lua_State *L) 
+	{
+		lua_pushuint32(L, z);
+		return 1;
+	}
+	virtual void __persist(lua_State * L) {}
+	virtual void __unpersist(lua_State * L) {}
+};
+const char L_MultiClass::className[] = "MultiClass";
+const MethodType<L_MultiClass> L_MultiClass::Methods[] = {
+	METHOD(L_MultiClass, virtualtest),
+	METHOD(L_Second, multitest),
+	{0, 0},
+};
+BEGIN_LUNA_PROPERTIES(L_MultiClass)
+	PROP_RO(L_MultiClass, second),
+END_LUNA_PROPERTIES()
+
+const static struct luaL_reg wltest [] = {
+	{0, 0}
+};
+const static struct luaL_reg wl [] = {
+	{0, 0}
+};
+
+BOOST_AUTO_TEST_CASE(test_luna_simple)
+{
+	const char * script1 =
+		"print( 'test_luna simple class' )\n"
+		"t = wl.test.Class()\n"
+		"x = t:test()\n"
+		"t.prop1 = 999\n"
+		"p = t.prop1\n"
+		"print( 'test(): ' .. x .. ' prop1: ' .. p )\n";
+
+	shared_ptr<lua_State> L_ptr(lua_open(),&lua_close);
+	lua_State*L = L_ptr.get();
+	luaL_openlibs(L);
+
+	luaL_register(L, "wl", wl);
+	lua_pop(L, 1); // pop the table from the stack
+	luaL_register(L, "wl.test", wltest);
+	lua_pop(L, 1); // pop the table from the stack
+	register_class<L_Class>(L, "test");
+
+	BOOST_CHECK_EQUAL(0, luaL_loadbuffer(L, script1, strlen(script1), "testscript1"));
+	BOOST_CHECK_EQUAL(0, lua_pcall(L, 0, 1, 0));
+	
+}
+BOOST_AUTO_TEST_CASE(test_luna_property_ro)
+{
+	const char * script1 =
+		"print( 'test_luna readonly property' )\n"
+		"t = wl.test.Class()\n"
+		"p = t.propr\n"
+		"t.propr = 1\n";
+
+	shared_ptr<lua_State> L_ptr(lua_open(),&lua_close);
+	lua_State*L = L_ptr.get();
+	luaL_openlibs(L);
+
+	luaL_register(L, "wl", wl);
+	lua_pop(L, 1); // pop the table from the stack
+	luaL_register(L, "wl.test", wltest);
+	lua_pop(L, 1); // pop the table from the stack
+	register_class<L_Class>(L, "test");
+
+	BOOST_CHECK_EQUAL(0, luaL_loadbuffer(L, script1, strlen(script1), "testscript1"));
+	// Should get LUA runtime error instead of for example crashing
+	BOOST_CHECK_EQUAL(LUA_ERRRUN, lua_pcall(L, 0, 1, 0));
+}
+
+BOOST_AUTO_TEST_CASE(test_luna_inheritance)
+{
+	const char * script2 =
+		"print( 'test_luna simple inheritance' )\n"
+		"t = wl.test.SubClass()\n"
+		"x = t:test()\n"
+		"p = t.prop1\n"
+		"print( 'test(): ' .. x .. ' prop1: ' .. p )\n";
+
+	shared_ptr<lua_State> L_ptr(lua_open(),&lua_close);
+	lua_State*L = L_ptr.get();
+	luaL_openlibs(L);
+
+	luaL_register(L, "wl", wl);
+	lua_pop(L, 1); // pop the table from the stack
+	luaL_register(L, "wl.test", wltest);
+	lua_pop(L, 1); // pop the table from the stack
+
+	// single inheritance
+	register_class<L_SubClass>(L, "test", true);
+	add_parent<L_SubClass, L_Class>(L);
+	lua_pop(L, 1); // Pop the meta table
+	
+	BOOST_CHECK_EQUAL(0, luaL_loadbuffer(L, script2, strlen(script2), "testscript2"));
+	BOOST_CHECK_EQUAL(0, lua_pcall(L, 0, 1, 0));
+
+}
+
+BOOST_AUTO_TEST_CASE(test_luna_virtualbase_method)
+{
+	const char * script3 =
+		"print( 'test_luna virtual inheritance method call' )\n"
+		"t = wl.test.VirtualClass()\n"
+		"x = t:test()\n"
+		"print( 'test(): ' .. x )\n";
+
+	shared_ptr<lua_State> L_ptr(lua_open(),&lua_close);
+	lua_State*L = L_ptr.get();
+	luaL_openlibs(L);
+
+	luaL_register(L, "wl", wl);
+	lua_pop(L, 1); // pop the table from the stack
+	luaL_register(L, "wl.test", wltest);
+	lua_pop(L, 1); // pop the table from the stack
+
+	// virtual base class 
+	register_class<L_VirtualClass>(L, "test", true);
+	add_parent<L_VirtualClass, L_Class>(L);
+	lua_pop(L, 1); // Pop the meta table
+
+	BOOST_CHECK_EQUAL(0, luaL_loadbuffer(L, script3, strlen(script3), "testscript3"));
+	BOOST_CHECK_EQUAL(0, lua_pcall(L, 0, 1, 0));
+}
+
+BOOST_AUTO_TEST_CASE(test_luna_virtualbase_property)
+{
+	const char * script4 =
+		"print( 'test_luna virtual inheritance properties' )\n"
+		"t = wl.test.VirtualClass()\n"
+		"p = t.prop1\n"
+		"print( 'prop1: ' .. p )\n";
+
+	shared_ptr<lua_State> L_ptr(lua_open(),&lua_close);
+	lua_State*L = L_ptr.get();
+	luaL_openlibs(L);
+
+	luaL_register(L, "wl", wl);
+	lua_pop(L, 1); // pop the table from the stack
+	luaL_register(L, "wl.test", wltest);
+	lua_pop(L, 1); // pop the table from the stack
+
+	// virtual base class 
+	register_class<L_VirtualClass>(L, "test", true);
+	add_parent<L_VirtualClass, L_Class>(L);
+	lua_pop(L, 1); // Pop the meta table
+
+	BOOST_CHECK_EQUAL(0, luaL_loadbuffer(L, script4, strlen(script4), "testscript4"));
+	BOOST_CHECK_EQUAL(0, lua_pcall(L, 0, 1, 0));
+}
+
+BOOST_AUTO_TEST_CASE(test_luna_multibase_method)
+{
+	const char * script5 =
+		"print( 'test_luna virtual inheritance method call' )\n"
+		"t = wl.test.MultiClass()\n"
+		"x = t:test()\n"
+		"y = t:multitest()\n"
+		"print( 'test(): ' .. x .. ' multitest(): ' .. y)\n";
+
+	shared_ptr<lua_State> L_ptr(lua_open(),&lua_close);
+	lua_State*L = L_ptr.get();
+	luaL_openlibs(L);
+
+	luaL_register(L, "wl", wl);
+	lua_pop(L, 1); // pop the table from the stack
+	luaL_register(L, "wl.test", wltest);
+	lua_pop(L, 1); // pop the table from the stack
+
+	// virtual base class 
+	register_class<L_MultiClass>(L, "test", true);
+	add_parent<L_MultiClass, L_Class>(L);
+	lua_pop(L, 1); // Pop the meta table
+
+	BOOST_CHECK_EQUAL(0, luaL_loadbuffer(L, script5, strlen(script5), "testscript5"));
+	BOOST_CHECK_EQUAL(0, lua_pcall(L, 0, 1, 0));
+}
+
+BOOST_AUTO_TEST_CASE(test_luna_multibase_property_get)
+{
+	const char * script6 =
+		"print( 'test_luna multiple inheritance properties' )\n"
+		"t = wl.test.MultiClass()\n"
+		"p1 = t.prop1\n"
+		"p2 = t.second\n"
+		"print( 'prop1: ' .. p1 .. ' second: ' .. p2 )\n";
+
+	shared_ptr<lua_State> L_ptr(lua_open(),&lua_close);
+	lua_State*L = L_ptr.get();
+	luaL_openlibs(L);
+
+	luaL_register(L, "wl", wl);
+	lua_pop(L, 1); // pop the table from the stack
+	luaL_register(L, "wl.test", wltest);
+	lua_pop(L, 1); // pop the table from the stack
+
+	register_class<L_MultiClass>(L, "test", true);
+	add_parent<L_MultiClass, L_Class>(L);
+	lua_pop(L, 1); // Pop the meta table
+
+	BOOST_CHECK_EQUAL(0, luaL_loadbuffer(L, script6, strlen(script6), "testscript6"));
+	BOOST_CHECK_EQUAL(0, lua_pcall(L, 0, 1, 0));
+}
+
+BOOST_AUTO_TEST_CASE(test_luna_multibase_property_set)
+{
+	const char * script6 =
+		"print( 'test_luna multiple inheritance properties' )\n"
+		"t = wl.test.MultiClass()\n"
+		"t.prop1 = 123\n"
+		"p1 = t.prop1\n"
+		"print( 'prop1: ' .. p1 )\n";
+
+	shared_ptr<lua_State> L_ptr(lua_open(),&lua_close);
+	lua_State*L = L_ptr.get();
+	luaL_openlibs(L);
+
+	luaL_register(L, "wl", wl);
+	lua_pop(L, 1); // pop the table from the stack
+	luaL_register(L, "wl.test", wltest);
+	lua_pop(L, 1); // pop the table from the stack
+
+	register_class<L_MultiClass>(L, "test", true);
+	add_parent<L_MultiClass, L_Class>(L);
+	lua_pop(L, 1); // Pop the meta table
+
+	BOOST_CHECK_EQUAL(0, luaL_loadbuffer(L, script6, strlen(script6), "testscript6"));
+	BOOST_CHECK_EQUAL(0, lua_pcall(L, 0, 1, 0));
+}