1 /* Copyright (c) 2010. The SimGrid Team.
2 * All rights reserved. */
4 /* This program is free software; you can redistribute it and/or modify it
5 * under the terms of the license (GNU LGPL) which comes with this package. */
7 /* SimGrid Lua state management */
9 #include "lua_state_cloner.h"
10 #include "lua_utils.h"
16 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(lua_state_cloner, lua, "Lua state management");
18 static lua_State* sglua_get_father(lua_State* L);
19 static void sglua_move_value_impl(lua_State* src, lua_State* dst, const char* name);
20 static int l_get_from_father(lua_State* L);
23 * @brief Returns the father of a state, i.e. the state that created it.
24 * @param L a Lua state
25 * @return its father, or NULL if the state was not created by sglua_clone_state()
27 static lua_State* sglua_get_father(lua_State* L) {
30 lua_pushstring(L, "simgrid.father");
31 /* ... "simgrid.father" */
32 lua_rawget(L, LUA_REGISTRYINDEX);
34 lua_State* father = lua_touserdata(L, -1);
41 * @brief Pops a value from a state and pushes it onto the stack of another
44 * @param src the source state
45 * @param dst the destination state
47 void sglua_move_value(lua_State* src, lua_State* dst) {
51 /* get the list of visited tables from father at index 1 of dst */
54 lua_getfield(dst, LUA_REGISTRYINDEX, "simgrid.father_visited_tables");
55 /* dst: ... visited */
57 /* dst: visited ... */
59 sglua_move_value_impl(src, dst, sglua_tostring(src, -1));
61 dst: visited ... value */
64 sglua_stack_dump("src after xmove: ", src);
65 sglua_stack_dump("dst after xmove: ", dst);
70 * @brief Pops a value from the stack of a source state and pushes it on the
71 * stack of another state.
73 * If the value is a table, its content is copied recursively. To avoid cycles,
74 * a table of previously visited tables must be present at index 1 of dst.
75 * Its keys are pointers to visited tables in src and its values are the tables
78 * TODO: add support of closures
80 * @param src the source state
81 * @param dst the destination state, with a list of visited tables at index 1
82 * @param name a name describing the value
84 static void sglua_move_value_impl(lua_State* src, lua_State *dst, const char* name) {
86 luaL_checkany(src, -1); /* check the value to copy */
87 luaL_checktype(dst, 1, LUA_TTABLE); /* check the presence of a table of
88 previously visited tables */
90 int indent = (lua_gettop(dst) - 1) * 6;
91 XBT_DEBUG("%sCopying data %s", sglua_get_spaces(indent), name);
94 sglua_stack_dump("src before copying a value (should be ... value): ", src);
95 sglua_stack_dump("dst before copying a value (should be visited ...): ", dst);
97 switch (lua_type(src, -1)) {
98 /* TODO implement the copy of each type in a separate function */
105 lua_pushnumber(dst, lua_tonumber(src, -1));
109 lua_pushboolean(dst, lua_toboolean(src, -1));
113 /* no worries about memory: lua_pushstring makes a copy */
114 lua_pushstring(dst, lua_tostring(src, -1));
118 /* it's a function that does not exist yet in L2 */
120 if (lua_iscfunction(src, -1)) {
121 /* it's a C function: just copy the pointer */
122 lua_CFunction f = lua_tocfunction(src, -1);
123 lua_pushcfunction(dst, f);
126 /* it's a Lua function: dump it from src */
127 XBT_DEBUG("%sDumping Lua function '%s'", sglua_get_spaces(indent), name);
130 buffer.capacity = 64;
132 buffer.data = xbt_new(char, buffer.capacity);
134 /* copy the binary chunk from src into a buffer */
135 int error = lua_dump(src, sglua_memory_writer, &buffer);
136 xbt_assert(!error, "Failed to dump function '%s' from the source state: error %d",
139 /* load the chunk into dst */
140 error = luaL_loadbuffer(dst, buffer.data, buffer.size, name);
141 xbt_assert(!error, "Failed to load function '%s' into the destination state: %s",
142 name, lua_tostring(dst, -1));
143 XBT_DEBUG("%sFunction '%s' successfully loaded", sglua_get_spaces(indent), name);
149 /* first register the table in the source state itself */
150 lua_getfield(src, LUA_REGISTRYINDEX, "simgrid.visited_tables");
151 /* src: ... table visited */
152 lua_pushvalue(src, -2);
153 /* src: ... table visited table */
154 lua_pushlightuserdata(src, (void*) lua_topointer(src, -1));
155 /* src: ... table visited table psrctable */
156 lua_pushvalue(src, -1);
157 /* src: ... table visited table psrctable psrctable */
158 lua_pushvalue(src, -3);
159 /* src: ... table visited table psrctable psrctable table */
160 lua_settable(src, -5);
161 /* src: ... table visited table psrctable */
162 lua_settable(src, -3);
163 /* src: ... table visited */
167 /* see if this table was already known by dst */
168 lua_pushlightuserdata(dst, (void*) lua_topointer(src, -1));
169 /* dst: visited ... psrctable */
170 lua_gettable(dst, 1);
171 /* dst: visited ... table/nil */
172 if (lua_istable(dst, -1)) {
173 XBT_DEBUG("%sNothing to do: table already visited (%p)",
174 sglua_get_spaces(indent), lua_topointer(src, -1));
175 /* dst: visited ... table */
178 XBT_DEBUG("%sFirst visit of this table (%p)", sglua_get_spaces(indent),
179 lua_topointer(src, -1));
180 /* dst: visited ... nil */
182 /* dst: visited ... */
184 /* first visit: create the new table in dst */
186 /* dst: visited ... table */
188 /* mark the table as visited to avoid infinite recursion */
189 lua_pushlightuserdata(dst, (void*) lua_topointer(src, -1));
190 /* dst: visited ... table psrctable */
191 lua_pushvalue(dst, -2);
192 /* dst: visited ... table psrctable table */
193 lua_pushvalue(dst, -1);
194 /* dst: visited ... table psrctable table table */
195 lua_pushvalue(dst, -3);
196 /* dst: visited ... table psrctable table table psrctable */
197 lua_settable(dst, 1);
198 /* dst: visited ... table psrctable table */
199 lua_settable(dst, 1);
200 /* dst: visited ... table */
201 XBT_DEBUG("%sTable marked as visited", sglua_get_spaces(indent));
203 sglua_stack_dump("dst after marking the table as visited (should be visited ... table): ", dst);
205 /* copy the metatable if any */
206 int has_meta_table = lua_getmetatable(src, -1);
207 /* src: ... table mt? */
208 if (has_meta_table) {
209 XBT_DEBUG("%sCopying metatable", sglua_get_spaces(indent));
210 /* src: ... table mt */
211 sglua_move_value_impl(src, dst, "metatable");
213 dst: visited ... table mt */
214 lua_setmetatable(dst, -2);
215 /* dst: visited ... table */
218 XBT_DEBUG("%sNo metatable", sglua_get_spaces(indent));
221 sglua_stack_dump("src before traversing the table (should be ... table): ", src);
222 sglua_stack_dump("dst before traversing the table (should be visited ... table): ", dst);
224 /* traverse the table of src and copy each element */
226 /* src: ... table nil */
227 while (lua_next(src, -2) != 0) {
228 /* src: ... table key value */
230 XBT_DEBUG("%sCopying table element %s", sglua_get_spaces(indent),
231 sglua_keyvalue_tostring(src, -2, -1));
233 sglua_stack_dump("src before copying table element (should be ... table key value): ", src);
234 sglua_stack_dump("dst before copying table element (should be visited ... table): ", dst);
237 lua_pushvalue(src, -2);
238 /* src: ... table key value key */
240 XBT_DEBUG("%sCopying the key part of the table element",
241 sglua_get_spaces(indent));
242 sglua_move_value_impl(src, dst, sglua_tostring(src, -1));
243 /* src: ... table key value
244 dst: visited ... table key */
245 XBT_DEBUG("%sCopied the key part of the table element",
246 sglua_get_spaces(indent));
249 XBT_DEBUG("%sCopying the value part of the table element",
250 sglua_get_spaces(indent));
251 sglua_move_value_impl(src, dst, sglua_tostring(src, -1));
252 /* src: ... table key
253 dst: visited ... table key value */
254 XBT_DEBUG("%sCopied the value part of the table element",
255 sglua_get_spaces(indent));
258 /* set the table element */
259 lua_settable(dst, -3);
260 /* dst: visited ... table */
262 /* the key stays on top of src for next iteration */
263 sglua_stack_dump("src before next iteration (should be ... table key): ", src);
264 sglua_stack_dump("dst before next iteration (should be visited ... table): ", dst);
266 XBT_DEBUG("%sTable element copied", sglua_get_spaces(indent));
268 XBT_DEBUG("%sFinished traversing the table", sglua_get_spaces(indent));
272 case LUA_TLIGHTUSERDATA:
273 lua_pushlightuserdata(dst, lua_touserdata(src, -1));
281 size_t size = lua_objlen(src, -1);
282 void* src_block = lua_touserdata(src, -1);
283 void* dst_block = lua_newuserdata(dst, size);
284 /* dst: visited ... udata */
285 memcpy(dst_block, src_block, size);
287 /* copy the metatable if any */
288 int has_meta_table = lua_getmetatable(src, -1);
289 /* src: ... udata mt? */
290 if (has_meta_table) {
291 XBT_DEBUG("%sCopying metatable of userdata (%p)",
292 sglua_get_spaces(indent), lua_topointer(src, -1));
293 /* src: ... udata mt */
294 lua_State* father = sglua_get_father(dst);
296 if (father != NULL && src != father && sglua_get_father(src) == father) {
297 XBT_DEBUG("%sGet the metatable from my father",
298 sglua_get_spaces(indent));
299 /* I don't want the metatable of src, I want the father's copy of the
302 /* get from src the pointer to the father's copy of this metatable */
303 lua_pushstring(src, "simgrid.father_visited_tables");
304 /* src: ... udata mt "simgrid.visited_tables" */
305 lua_rawget(src, LUA_REGISTRYINDEX);
306 /* src: ... udata mt visited */
307 lua_pushvalue(src, -2);
308 /* src: ... udata mt visited mt */
309 lua_gettable(src, -2);
310 /* src: ... udata mt visited pfathermt */
312 /* copy the metatable from the father world into dst */
313 lua_pushstring(father, "simgrid.visited_tables");
314 /* father: ... "simgrid.visited_tables" */
315 lua_rawget(father, LUA_REGISTRYINDEX);
316 /* father: ... visited */
317 lua_pushlightuserdata(father, (void*) lua_topointer(src, -1));
318 /* father: ... visited pfathermt */
319 lua_gettable(father, -2);
320 /* father: ... visited mt */
321 sglua_move_value_impl(father, dst, "(father metatable)");
322 /* father: ... visited
323 dst: visited ... udata mt */
329 /* TODO make helper functions for this kind of operations */
332 XBT_DEBUG("%sI have no father", sglua_get_spaces(indent));
333 sglua_move_value_impl(src, dst, "metatable");
335 dst: visited ... udata mt */
337 lua_setmetatable(dst, -2);
338 /* dst: visited ... udata */
340 XBT_DEBUG("%sMetatable of userdata copied", sglua_get_spaces(indent));
343 XBT_DEBUG("%sNo metatable for this userdata",
344 sglua_get_spaces(indent));
351 XBT_WARN("Cannot copy a thread from the source state.");
356 /* pop the value from src */
360 XBT_DEBUG("%sData copied", sglua_get_spaces(indent));
362 sglua_stack_dump("src after copying a value (should be ...): ", src);
363 sglua_stack_dump("dst after copying a value (should be visited ... value): ", dst);
367 * @brief Copies a global value or a registry value from the father state.
369 * The state L must have a father, i.e. it should have been created by
371 * This function is meant to be an __index metamethod.
372 * Consequently, it assumes that the stack has two elements:
373 * a table (either the environment or the registry of L) and the string key of
374 * a value that does not exist yet in this table. It copies the corresponding
375 * value from the father state and pushes it on the stack of L.
376 * If the value does not exist in the father state either, nil is pushed.
378 * TODO: make this function thread safe. If the simulation runs in parallel,
379 * several simulated processes may trigger this __index metamethod at the same
380 * time and get globals from maestro.
382 * @param L the current state
383 * @return number of return values pushed (always 1)
385 static int l_get_from_father(lua_State *L) {
387 /* check the arguments */
388 luaL_checktype(L, 1, LUA_TTABLE);
389 const char* key = luaL_checkstring(L, 2);
391 XBT_DEBUG("__index of '%s' begins", key);
393 /* want a global or a registry value? */
395 if (lua_equal(L, 1, LUA_REGISTRYINDEX)) {
397 pseudo_index = LUA_REGISTRYINDEX;
398 XBT_DEBUG("Will get the value from the registry of the father");
402 pseudo_index = LUA_GLOBALSINDEX;
403 XBT_DEBUG("Will get the value from the globals of the father");
407 lua_State* father = sglua_get_father(L);
409 if (father == NULL) {
410 XBT_WARN("This state has no father");
417 /* get the list of visited tables */
418 lua_pushstring(L, "simgrid.father_visited_tables");
419 /* L: table key "simgrid.father_visited_tables" */
420 lua_rawget(L, LUA_REGISTRYINDEX);
421 /* L: table key visited */
423 /* L: visited table key */
425 /* get the value from the father */
426 lua_getfield(father, pseudo_index, key);
427 /* father: ... value */
429 /* push the value onto the stack of L */
430 sglua_move_value_impl(father, L, key);
432 L: visited table key value */
434 /* L: table key value */
436 /* prepare the return value of __index */
437 lua_pushvalue(L, -1);
438 /* L: table key value value */
440 /* L: value table key value */
442 /* save the copied value in the table for subsequent accesses */
448 XBT_DEBUG("__index of '%s' returns %s", key, sglua_tostring(L, -1));
454 * @brief Creates a new Lua state and get its environment from an existing state.
456 * The state created is independent from the existing one and has its own
457 * copies of global and registry values.
458 * However, the global and registry values are not copied right now from
459 * the original state; they are copied only the first time they are accessed.
460 * This behavior saves time and memory, and is okay for Simgrid's needs.
462 * TODO: if the simulation runs in parallel, copy everything right now?
464 * @param father an existing state
465 * @return the state created
467 lua_State* sglua_clone_state(lua_State *father) {
469 /* create the new state */
470 lua_State *L = luaL_newstate();
472 /* set its environment and its registry:
473 * - create a table newenv
474 * - create a metatable mt
475 * - set mt.__index = a function that copies the global from the father state
476 * - set mt as the metatable of the registry
477 * - set mt as the metatable of newenv
478 * - set newenv as the environment of the new state
480 lua_pushthread(L); /* thread */
481 lua_newtable(L); /* thread newenv */
482 lua_newtable(L); /* thread newenv mt */
483 lua_pushvalue(L, LUA_REGISTRYINDEX); /* thread newenv mt reg */
484 lua_pushcfunction(L, l_get_from_father); /* thread newenv mt reg f */
485 lua_setfield(L, -3, "__index"); /* thread newenv mt reg */
486 lua_pushvalue(L, -2); /* thread newenv mt reg mt */
487 lua_setmetatable(L, -2); /* thread newenv mt reg */
488 lua_pop(L, 1); /* thread newenv mt */
489 lua_setmetatable(L, -2); /* thread newenv */
490 lua_setfenv(L, -2); /* thread */
491 lua_pop(L, 1); /* -- */
493 /* set a pointer to the father */
494 lua_pushstring(L, "simgrid.father"); /* "simgrid.father" */
495 lua_pushlightuserdata(L, father); /* "simgrid.father" father */
496 lua_rawset(L, LUA_REGISTRYINDEX);
499 /* create the table of visited tables from the father */
500 lua_pushstring(L, "simgrid.father_visited_tables");
501 /* "simgrid.father_visited_tables" */
502 lua_newtable(L); /* "simgrid.father_visited_tables" visited */
503 lua_rawset(L, LUA_REGISTRYINDEX);
506 /* create the table of my own visited tables */
507 lua_pushstring(L, "simgrid.visited_tables");
508 /* "simgrid.visited_tables" */
509 lua_newtable(L); /* "simgrid.visited_tables" visited */
510 lua_rawset(L, LUA_REGISTRYINDEX);
513 /* open the standard libs (theoretically, this is not necessary as they can
514 * be inherited like any global values, but without a proper support of
515 * closures, iterators like ipairs don't work). */
516 XBT_DEBUG("Metatable of globals and registry set, opening standard libraries now");
519 XBT_DEBUG("New state created");