+ sglua_copy_userdata(src, dst);
+ break;
+
+ case LUA_TTHREAD:
+ sglua_copy_thread(src, dst);
+ break;
+ }
+
+ /* the value has been copied to dst: remove it from src */
+ lua_pop(src, 1);
+
+ indent -= 2;
+ XBT_DEBUG("%sData copied", sglua_get_spaces(indent));
+
+ sglua_stack_dump("src after copying a value (should be ...): ", src);
+ sglua_stack_dump("dst after copying a value (should be visited ... value): ", dst);
+}
+
+/**
+ * @brief Copies the nil value on the top of src to the top of dst.
+ * @param src source state
+ * @param dst destination state
+ */
+static void sglua_copy_nil(lua_State* src, lua_State* dst) {
+ lua_pushnil(dst);
+}
+
+/**
+ * @brief Copies the number value on the top of src to the top of dst.
+ * @param src source state
+ * @param dst destination state
+ */
+static void sglua_copy_number(lua_State* src, lua_State* dst) {
+ lua_pushnumber(dst, lua_tonumber(src, -1));
+}
+
+/**
+ * @brief Copies the boolean value on the top of src to the top of dst.
+ * @param src source state
+ * @param dst destination state
+ */
+static void sglua_copy_boolean(lua_State* src, lua_State* dst) {
+ lua_pushboolean(dst, lua_toboolean(src, -1));
+}
+
+static void sglua_copy_string(lua_State* src, lua_State* dst) {
+
+ /* no worries about memory: lua_pushstring makes a copy */
+ lua_pushstring(dst, lua_tostring(src, -1));
+}
+
+/**
+ * @brief Copies the table value on the top of src to the top of dst.
+ *
+ * A deep copy of the table is made. If the table has a metatable, the
+ * metatable is also copied.
+ * If the table is already known by the destination state, nothing is
+ * done.
+ *
+ * @param src source state
+ * @param dst destination state
+ */
+static void sglua_copy_table(lua_State* src, lua_State* dst) {
+
+ int indent = (lua_gettop(dst) - 1) * 6 + 2;
+
+ /* first register the table in the source state itself */
+ lua_getfield(src, LUA_REGISTRYINDEX, "simgrid.visited_tables");
+ /* src: ... table visited */
+ lua_pushvalue(src, -2);
+ /* src: ... table visited table */
+ lua_pushlightuserdata(src, (void*) lua_topointer(src, -1));
+ /* src: ... table visited table psrctable */
+ lua_pushvalue(src, -1);
+ /* src: ... table visited table psrctable psrctable */
+ lua_pushvalue(src, -3);
+ /* src: ... table visited table psrctable psrctable table */
+ lua_settable(src, -5);
+ /* src: ... table visited table psrctable */
+ lua_settable(src, -3);
+ /* src: ... table visited */
+ lua_pop(src, 1);
+ /* src: ... table */
+
+ /* see if this table was already known by dst */
+ lua_pushlightuserdata(dst, (void*) lua_topointer(src, -1));
+ /* dst: visited ... psrctable */
+ lua_gettable(dst, 1);
+ /* dst: visited ... table/nil */
+ if (lua_istable(dst, -1)) {
+ XBT_DEBUG("%sNothing to do: table already visited (%p)",
+ sglua_get_spaces(indent), lua_topointer(src, -1));
+ /* dst: visited ... table */
+ }
+ else {
+ XBT_DEBUG("%sFirst visit of this table (%p)", sglua_get_spaces(indent),
+ lua_topointer(src, -1));
+ /* dst: visited ... nil */
+ lua_pop(dst, 1);
+ /* dst: visited ... */
+
+ /* first visit: create the new table in dst */
+ lua_newtable(dst);
+ /* dst: visited ... table */
+
+ /* mark the table as visited right now to avoid infinite recursion */
+ lua_pushlightuserdata(dst, (void*) lua_topointer(src, -1));
+ /* dst: visited ... table psrctable */
+ lua_pushvalue(dst, -2);
+ /* dst: visited ... table psrctable table */
+ lua_pushvalue(dst, -1);
+ /* dst: visited ... table psrctable table table */
+ lua_pushvalue(dst, -3);
+ /* dst: visited ... table psrctable table table psrctable */
+ lua_settable(dst, 1);
+ /* dst: visited ... table psrctable table */
+ lua_settable(dst, 1);
+ /* dst: visited ... table */
+ XBT_DEBUG("%sTable marked as visited", sglua_get_spaces(indent));
+
+ sglua_stack_dump("dst after marking the table as visited (should be visited ... table): ", dst);
+
+ /* copy the metatable if any */
+ int has_meta_table = lua_getmetatable(src, -1);
+ /* src: ... table mt? */
+ if (has_meta_table) {
+ XBT_DEBUG("%sCopying metatable", sglua_get_spaces(indent));
+ /* src: ... table mt */
+ sglua_copy_table(src, dst);
+ /* dst: visited ... table mt */
+ lua_pop(src, 1);
+ /* src: ... table */
+ lua_setmetatable(dst, -2);
+ /* dst: visited ... table */
+ }
+ else {
+ XBT_DEBUG("%sNo metatable", sglua_get_spaces(indent));
+ }
+
+ sglua_stack_dump("src before traversing the table (should be ... table): ", src);
+ sglua_stack_dump("dst before traversing the table (should be visited ... table): ", dst);
+
+ /* traverse the table of src and copy each element */
+ lua_pushnil(src);
+ /* src: ... table nil */
+ while (lua_next(src, -2) != 0) {
+ /* src: ... table key value */
+
+ XBT_DEBUG("%sCopying table element %s", sglua_get_spaces(indent),
+ sglua_keyvalue_tostring(src, -2, -1));
+
+ sglua_stack_dump("src before copying table element (should be ... table key value): ", src);
+ sglua_stack_dump("dst before copying table element (should be visited ... table): ", dst);
+
+ /* copy the key */
+ lua_pushvalue(src, -2);
+ /* src: ... table key value key */
+ indent += 2;
+ XBT_DEBUG("%sCopying the key part of the table element",
+ sglua_get_spaces(indent));
+ sglua_move_value_impl(src, dst, sglua_tostring(src, -1));
+ /* src: ... table key value
+ dst: visited ... table key */
+ XBT_DEBUG("%sCopied the key part of the table element",
+ sglua_get_spaces(indent));
+
+ /* copy the value */
+ XBT_DEBUG("%sCopying the value part of the table element",
+ sglua_get_spaces(indent));
+ sglua_move_value_impl(src, dst, sglua_tostring(src, -1));
+ /* src: ... table key
+ dst: visited ... table key value */
+ XBT_DEBUG("%sCopied the value part of the table element",
+ sglua_get_spaces(indent));
+ indent -= 2;
+
+ /* set the table element */
+ lua_settable(dst, -3);
+ /* dst: visited ... table */
+
+ /* the key stays on top of src for next iteration */
+ sglua_stack_dump("src before next iteration (should be ... table key): ", src);
+ sglua_stack_dump("dst before next iteration (should be visited ... table): ", dst);
+
+ XBT_DEBUG("%sTable element copied", sglua_get_spaces(indent));
+ }
+ XBT_DEBUG("%sFinished traversing the table", sglua_get_spaces(indent));
+ }
+}
+
+/**
+ * @brief Copies the function on the top of src to the top of dst.
+ *
+ * It can be a Lua function or a C function.
+ * Copying upvalues is not implemented yet (TODO).
+ *
+ * @param src source state
+ * @param dst destination state
+ */
+static void sglua_copy_function(lua_State* src, lua_State* dst) {
+
+ if (lua_iscfunction(src, -1)) {
+ /* it's a C function: just copy the pointer */
+ lua_CFunction f = lua_tocfunction(src, -1);
+ lua_pushcfunction(dst, f);
+ }
+ else {
+ /* it's a Lua function: dump it from src */
+
+ s_sglua_buffer_t buffer;
+ buffer.capacity = 64;
+ buffer.size = 0;
+ buffer.data = xbt_new(char, buffer.capacity);
+
+ /* copy the binary chunk from src into a buffer */
+ int error = lua_dump(src, sglua_memory_writer, &buffer);
+ xbt_assert(!error, "Failed to dump the function from the source state: error %d",
+ error);
+
+ /* load the chunk into dst */
+ error = luaL_loadbuffer(dst, buffer.data, buffer.size, "(dumped function)");
+ xbt_assert(!error, "Failed to load the function into the destination state: %s",
+ lua_tostring(dst, -1));
+ }
+}
+
+/**
+ * @brief Copies the light userdata on the top of src to the top of dst.
+ * @param src source state
+ * @param dst destination state
+ */
+static void sglua_copy_lightuserdata(lua_State* src, lua_State* dst) {
+ lua_pushlightuserdata(dst, lua_touserdata(src, -1));
+}
+
+/**
+ * @brief Copies the full userdata on the top of src to the top of dst.
+ *
+ * If the userdata has a metatable, the metatable is also copied.
+ *
+ * @param src source state
+ * @param dst destination state
+ */
+static void sglua_copy_userdata(lua_State* src, lua_State* dst) {
+
+ int indent = (lua_gettop(dst) - 1) * 6 + 2;
+
+ /* copy the data */