Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Simplifying and improving identification of tables
[simgrid.git] / src / bindings / lua / lua_state_cloner.c
1 /* Copyright (c) 2010. The SimGrid Team.
2  * All rights reserved.                                                     */
3
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. */
6
7 /* SimGrid Lua state management                                             */
8
9 #include "lua_state_cloner.h"
10 #include "lua_utils.h"
11 #include "xbt.h"
12 #include "xbt/log.h"
13 #include <lauxlib.h>
14 #include <lualib.h>
15
16 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(lua_state_cloner, lua, "Lua state management");
17
18 static void sglua_add_maestro_table(lua_State* L, int index, void* maestro_table_ptr);
19 static void* sglua_get_maestro_table_ptr(lua_State* L, int index);
20 static void sglua_get_table_by_ptr(lua_State* L, void* table_ptr);
21 static int l_get_from_maestro(lua_State* L);
22
23 static void sglua_copy_nil(lua_State* src, lua_State* dst);
24 static void sglua_copy_number(lua_State* src, lua_State* dst);
25 static void sglua_copy_boolean(lua_State* src, lua_State* dst);
26 static void sglua_copy_string(lua_State* src, lua_State* dst);
27 static void sglua_copy_table(lua_State* src, lua_State* dst);
28 static void sglua_copy_function(lua_State* src, lua_State* dst);
29 static void sglua_copy_lightuserdata(lua_State* src, lua_State* dst);
30 static void sglua_copy_userdata(lua_State* src, lua_State* dst);
31 static void sglua_copy_thread(lua_State* src, lua_State* dst);
32
33 /**
34  * @brief Returns the father of a state, i.e. the state that created it.
35  * @param L a Lua state
36  * @return its father, or NULL if the state was not created by sglua_clone_state()
37  */
38 static lua_State* sglua_get_father(lua_State* L) {
39
40                                   /* ... */
41   lua_pushstring(L, "simgrid.father");
42                                   /* ... "simgrid.father" */
43   lua_rawget(L, LUA_REGISTRYINDEX);
44                                   /* ... father */
45   lua_State* father = lua_touserdata(L, -1);
46   lua_pop(L, 1);
47                                   /* ... */
48   return father;
49 }
50
51 /**
52  * @brief Adds a reference to a maestro table to the list of known maestro
53  * tables of a state.
54  *
55  * TODO identify maestro's tables with my own IDs instead of pointers
56  * to Lua internals
57  *
58  * @param L a state (can be maestro itself)
59  * @param index index of the copy of the maestro table in the stack of L
60  * @param maestro_table_ptr pointer to the original table in maestro's world
61  */
62 void sglua_add_maestro_table(lua_State* L, int index, void* maestro_table_ptr) {
63
64   /* we will set both [ptr] -> table and [table] -> ptr */
65
66                                   /* ... */
67   lua_pushvalue(L, index);
68                                   /* ... table */
69   lua_getfield(L, LUA_REGISTRYINDEX, "simgrid.maestro_tables");
70                                   /* ... table maestrotbs */
71   lua_pushvalue(L, -2);
72                                   /* ... table maestrotbs table */
73   lua_pushlightuserdata(L, maestro_table_ptr);
74                                   /* ... table maestrotbs table tableptr */
75   lua_pushvalue(L, -1);
76                                   /* ... table maestrotbs table tableptr tableptr */
77   lua_pushvalue(L, -3);
78                                   /* ... table maestrotbs table tableptr tableptr table */
79   lua_settable(L, -5);
80                                   /* ... table maestrotbs table tableptr */
81   lua_settable(L, -3);
82                                   /* ... table maestrotbs */
83   lua_pop(L, 2);
84                                   /* ... */
85 }
86
87 /**
88  * @brief For a table in the stack of L, returns a pointer that identifies the
89  * same table in in maestro's world.
90  * @param L a Lua state
91  * @param index index of a table in the stack of L
92  * @return a pointer to maestro's copy of this table, or NULL if this table
93  * did not come from maestro
94  */
95 static void* sglua_get_maestro_table_ptr(lua_State* L, int index) {
96
97   void* maestro_table_ptr = NULL;
98                                   /* ... */
99   lua_pushstring(L, "simgrid.maestro_tables");
100                                   /* ... "simgrid.maestro_tables" */
101   lua_rawget(L, LUA_REGISTRYINDEX);
102                                   /* ... maestrotbs */
103   lua_pushvalue(L, index);
104                                   /* ... maestrotbs table */
105   lua_gettable(L, -2);
106                                   /* ... maestrotbs tableptr/nil */
107   if (!lua_isnil(L, -1)) {
108                                   /* ... maestrotbs tableptr */
109     maestro_table_ptr = (void*) lua_topointer(L, -1);
110   }
111
112   lua_pop(L, 2);
113                                   /* ... */
114   return maestro_table_ptr;
115 }
116
117 /**
118  * @brief Pushes a table knowing a pointer to maestro's copy of this table.
119  *
120  * Pushes nil if L does not know this table in maestro.
121  *
122  * @param L a Lua state
123  * @param maestro_table_ptr pointer that identifies a table in maestro's world
124  */
125 static void sglua_get_table_by_ptr(lua_State* L, void* maestro_table_ptr) {
126
127                                   /* ... */
128   lua_pushstring(L, "simgrid.maestro_tables");
129                                   /* ... "simgrid.maestro_tables" */
130   lua_rawget(L, LUA_REGISTRYINDEX);
131                                   /* ... maestrotbs */
132   lua_pushlightuserdata(L, maestro_table_ptr);
133                                   /* ... maestrotbs tableptr */
134   lua_gettable(L, -2);
135                                   /* ... maestrotbs table/nil */
136   lua_remove(L, -2);
137                                   /* ... table/nil */
138 }
139
140 /**
141  * @brief Pops a value from the stack of a source state and pushes it on the
142  * stack of another state.
143  *
144  * If the value is a table, its content is copied recursively.
145  *
146  * TODO: add support of closures
147  *
148  * @param src the source state (not necessarily maestro)
149  * @param dst the destination state
150  */
151 void sglua_move_value(lua_State* src, lua_State *dst) {
152
153   luaL_checkany(src, -1);                  /* check the value to copy */
154
155   int indent = (lua_gettop(dst) - 1) * 6;
156   XBT_DEBUG("%sCopying data %s", sglua_get_spaces(indent), sglua_tostring(src, -1));
157
158   sglua_stack_dump("src before copying a value (should be ... value): ", src);
159   sglua_stack_dump("dst before copying a value (should be ...): ", dst);
160
161   switch (lua_type(src, -1)) {
162
163     case LUA_TNIL:
164       sglua_copy_nil(src, dst);
165       break;
166
167     case LUA_TNUMBER:
168       sglua_copy_number(src, dst);
169       break;
170
171     case LUA_TBOOLEAN:
172       sglua_copy_boolean(src, dst);
173       break;
174
175     case LUA_TSTRING:
176       sglua_copy_string(src, dst);
177       break;
178
179     case LUA_TFUNCTION:
180       sglua_copy_function(src, dst);
181       break;
182
183     case LUA_TTABLE:
184       sglua_copy_table(src, dst);
185       break;
186
187     case LUA_TLIGHTUSERDATA:
188       sglua_copy_lightuserdata(src, dst);
189       break;
190
191     case LUA_TUSERDATA:
192       sglua_copy_userdata(src, dst);
193       break;
194
195     case LUA_TTHREAD:
196       sglua_copy_thread(src, dst);
197       break;
198   }
199
200   /* the value has been copied to dst: remove it from src */
201   lua_pop(src, 1);
202
203   indent -= 2;
204   XBT_DEBUG("%sData copied", sglua_get_spaces(indent));
205
206   sglua_stack_dump("src after copying a value (should be ...): ", src);
207   sglua_stack_dump("dst after copying a value (should be ... value): ", dst);
208 }
209
210 /**
211  * @brief Copies the nil value on the top of src to the top of dst.
212  * @param src source state
213  * @param dst destination state
214  */
215 static void sglua_copy_nil(lua_State* src, lua_State* dst) {
216   lua_pushnil(dst);
217 }
218
219 /**
220  * @brief Copies the number value on the top of src to the top of dst.
221  * @param src source state
222  * @param dst destination state
223  */
224 static void sglua_copy_number(lua_State* src, lua_State* dst) {
225   lua_pushnumber(dst, lua_tonumber(src, -1));
226 }
227
228 /**
229  * @brief Copies the boolean value on the top of src to the top of dst.
230  * @param src source state
231  * @param dst destination state
232  */
233 static void sglua_copy_boolean(lua_State* src, lua_State* dst) {
234   lua_pushboolean(dst, lua_toboolean(src, -1));
235 }
236
237 /**
238  * @brief Copies the string value on the top of src to the top of dst.
239  * @param src source state
240  * @param dst destination state
241  */
242 static void sglua_copy_string(lua_State* src, lua_State* dst) {
243
244   /* no worries about memory: lua_pushstring makes a copy */
245   lua_pushstring(dst, lua_tostring(src, -1));
246 }
247
248 /**
249  * @brief Copies the table value on the top of src to the top of dst.
250  *
251  * A deep copy of the table is made. If the table has a metatable, the
252  * metatable is also copied.
253  * If the table is already known by the destination state, it is not copied
254  * again.
255  *
256  * @param src source state
257  * @param dst destination state
258  */
259 static void sglua_copy_table(lua_State* src, lua_State* dst) {
260
261                                   /* src: ... table
262                                      dst: ... */
263   int indent = lua_gettop(dst) * 6  + 2;
264
265   /* get from maestro the pointer that identifies this table */
266   void* table_ptr = sglua_get_maestro_table_ptr(src, -1);
267   if (table_ptr == NULL) {
268     /* the table didn't come from maestro: nevermind, use the pointer of src */
269     table_ptr = (void*) lua_topointer(src, -1);
270
271     if (!sglua_is_maestro(src)) {
272       XBT_DEBUG("Using a non-maestro table pointer");
273     }
274   }
275   else if (sglua_is_maestro(src)) {
276     /* register the table in maestro itself */
277     sglua_add_maestro_table(src, -1, table_ptr);
278   }
279
280   /* to avoid infinite recursion, see if this table is already known by dst */
281   sglua_get_table_by_ptr(dst, table_ptr);
282                                   /* dst: ... table/nil */
283   if (!lua_isnil(dst, -1)) {
284     XBT_DEBUG("%sNothing to do: table already known (%p)",
285         sglua_get_spaces(indent), table_ptr);
286                                   /* dst: ... table */
287   }
288   else {
289     XBT_DEBUG("%sFirst visit of this table (%p)", sglua_get_spaces(indent),
290         table_ptr);
291                                   /* dst: ... nil */
292     lua_pop(dst, 1);
293                                   /* dst: ... */
294
295     /* first visit: create the new table in dst */
296     lua_newtable(dst);
297                                   /* dst: ... table */
298
299     /* mark the table as known right now to avoid infinite recursion */
300     sglua_add_maestro_table(dst, -1, table_ptr);
301     /* FIXME: we may have added a table with a non-maestro pointer, is this a
302        problem? */
303     XBT_DEBUG("%sTable marked as known", sglua_get_spaces(indent));
304
305     sglua_stack_dump("dst after marking the table as known (should be ... table): ", dst);
306
307     /* copy the metatable if any */
308     int has_meta_table = lua_getmetatable(src, -1);
309                                   /* src: ... table mt? */
310     if (has_meta_table) {
311       XBT_DEBUG("%sCopying the metatable", sglua_get_spaces(indent));
312                                   /* src: ... table mt */
313       sglua_copy_table(src, dst);
314                                   /* dst: ... table mt */
315       lua_pop(src, 1);
316                                   /* src: ... table */
317       lua_setmetatable(dst, -2);
318                                   /* dst: ... table */
319     }
320     else {
321                                   /* src: ... table */
322       XBT_DEBUG("%sNo metatable", sglua_get_spaces(indent));
323     }
324
325     sglua_stack_dump("src before traversing the table (should be ... table): ", src);
326     sglua_stack_dump("dst before traversing the table (should be ... table): ", dst);
327
328     /* traverse the table of src and copy each element */
329     lua_pushnil(src);
330                                   /* src: ... table nil */
331     while (lua_next(src, -2) != 0) {
332                                   /* src: ... table key value */
333
334       XBT_DEBUG("%sCopying table element %s", sglua_get_spaces(indent),
335           sglua_keyvalue_tostring(src, -2, -1));
336
337       sglua_stack_dump("src before copying table element (should be ... table key value): ", src);
338       sglua_stack_dump("dst before copying table element (should be ... table): ", dst);
339
340       /* copy the key */
341       lua_pushvalue(src, -2);
342                                   /* src: ... table key value key */
343       indent += 2;
344       XBT_DEBUG("%sCopying the key part of the table element",
345           sglua_get_spaces(indent));
346       sglua_move_value(src, dst);
347                                   /* src: ... table key value
348                                      dst: ... table key */
349       XBT_DEBUG("%sCopied the key part of the table element",
350           sglua_get_spaces(indent));
351
352       /* copy the value */
353       XBT_DEBUG("%sCopying the value part of the table element",
354           sglua_get_spaces(indent));
355       sglua_move_value(src, dst);
356                                   /* src: ... table key
357                                      dst: ... table key value */
358       XBT_DEBUG("%sCopied the value part of the table element",
359           sglua_get_spaces(indent));
360       indent -= 2;
361
362       /* set the table element */
363       lua_settable(dst, -3);
364                                   /* dst: ... table */
365
366       /* the key stays on top of src for next iteration */
367       sglua_stack_dump("src before next iteration (should be ... table key): ", src);
368       sglua_stack_dump("dst before next iteration (should be ... table): ", dst);
369
370       XBT_DEBUG("%sTable element copied", sglua_get_spaces(indent));
371     }
372     XBT_DEBUG("%sFinished traversing the table", sglua_get_spaces(indent));
373   }
374 }
375
376 /**
377  * @brief Copies the function on the top of src to the top of dst.
378  *
379  * It can be a Lua function or a C function.
380  * Copying upvalues is not implemented yet (TODO).
381  *
382  * @param src source state
383  * @param dst destination state
384  */
385 static void sglua_copy_function(lua_State* src, lua_State* dst) {
386
387   if (lua_iscfunction(src, -1)) {
388     /* it's a C function: just copy the pointer */
389     lua_CFunction f = lua_tocfunction(src, -1);
390     lua_pushcfunction(dst, f);
391   }
392   else {
393     /* it's a Lua function: dump it from src */
394
395     s_sglua_buffer_t buffer;
396     buffer.capacity = 64;
397     buffer.size = 0;
398     buffer.data = xbt_new(char, buffer.capacity);
399
400     /* copy the binary chunk from src into a buffer */
401     int error = lua_dump(src, sglua_memory_writer, &buffer);
402     xbt_assert(!error, "Failed to dump the function from the source state: error %d",
403         error);
404
405     /* load the chunk into dst */
406     error = luaL_loadbuffer(dst, buffer.data, buffer.size, "(dumped function)");
407     xbt_assert(!error, "Failed to load the function into the destination state: %s",
408         lua_tostring(dst, -1));
409   }
410 }
411
412 /**
413  * @brief Copies the light userdata on the top of src to the top of dst.
414  * @param src source state
415  * @param dst destination state
416  */
417 static void sglua_copy_lightuserdata(lua_State* src, lua_State* dst) {
418   lua_pushlightuserdata(dst, lua_touserdata(src, -1));
419 }
420
421 /**
422  * @brief Copies the full userdata on the top of src to the top of dst.
423  *
424  * If the userdata has a metatable, the metatable is also copied.
425  *
426  * @param src source state
427  * @param dst destination state
428  */
429 static void sglua_copy_userdata(lua_State* src, lua_State* dst) {
430
431   int indent = lua_gettop(dst) * 6  + 2;
432
433   /* copy the data */
434                                   /* src: ... udata
435                                      dst: ... */
436   size_t size = lua_objlen(src, -1);
437   void* src_block = lua_touserdata(src, -1);
438   void* dst_block = lua_newuserdata(dst, size);
439                                   /* dst: ... udata */
440   memcpy(dst_block, src_block, size);
441
442   /* copy the metatable if any */
443   int has_meta_table = lua_getmetatable(src, -1);
444                                   /* src: ... udata mt? */
445   if (has_meta_table) {
446     XBT_DEBUG("%sCopying metatable of userdata (%p)",
447         sglua_get_spaces(indent), lua_topointer(src, -1));
448                                   /* src: ... udata mt */
449     sglua_copy_table(src, dst);
450                                   /* src: ... udata mt
451                                      dst: ... udata mt */
452     lua_pop(src, 1);
453                                   /* src: ... udata */
454     lua_setmetatable(dst, -2);
455                                   /* dst: ... udata */
456
457     XBT_DEBUG("%sMetatable of userdata copied", sglua_get_spaces(indent));
458   }
459   else {
460     XBT_DEBUG("%sNo metatable for this userdata",
461         sglua_get_spaces(indent));
462                                   /* src: ... udata */
463   }
464 }
465
466 /**
467  * @brief This operation is not supported (yet?) so it just pushes nil.
468  *
469  * @param src source state
470  * @param dst destination state
471  */
472 static void sglua_copy_thread(lua_State* src, lua_State* dst) {
473
474   XBT_WARN("Copying a thread from another state is not implemented (yet?).");
475   lua_pushnil(dst);
476 }
477
478 /**
479  * @brief Copies a global value or a registry value from the maestro state.
480  *
481  * The state L must have been created by sglua_clone_maestro_state().
482  * This function is meant to be an __index metamethod.
483  * Consequently, it assumes that the stack has two elements:
484  * a table (either the environment or the registry of L) and the string key of
485  * a value that does not exist yet in this table. It copies the corresponding
486  * value from maestro and pushes it on the stack of L.
487  * If the value does not exist in maestro state either, nil is pushed.
488  *
489  * TODO: make this function thread safe. If the simulation runs in parallel,
490  * several simulated processes may trigger this __index metamethod at the same
491  * time and get globals from maestro.
492  *
493  * @param L the current state
494  * @return number of return values pushed (always 1)
495  */
496 static int l_get_from_maestro(lua_State *L) {
497
498   /* check the arguments */
499   luaL_checktype(L, 1, LUA_TTABLE);
500   const char* key = luaL_checkstring(L, 2);
501                                   /* L:      table key */
502   XBT_DEBUG("__index of '%s' begins", key);
503
504   /* want a global or a registry value? */
505   int pseudo_index;
506   if (lua_equal(L, 1, LUA_REGISTRYINDEX)) {
507     /* registry */
508     pseudo_index = LUA_REGISTRYINDEX;
509     XBT_DEBUG("Will get the value from the registry of maestro");
510   }
511   else {
512     /* global */
513     pseudo_index = LUA_GLOBALSINDEX;
514     XBT_DEBUG("Will get the value from the globals of maestro");
515   }
516
517   /* get the father */
518   lua_State* maestro = sglua_get_maestro();
519
520                                   /* L:      table key */
521
522   /* get the value from maestro */
523   lua_getfield(maestro, pseudo_index, key);
524                                   /* maestro: ... value */
525
526   /* push the value onto the stack of L */
527   sglua_move_value(maestro, L);
528                                   /* maestro: ...
529                                      L:      table key value */
530
531   /* prepare the return value of __index */
532   lua_pushvalue(L, -1);
533                                   /* L:      table key value value */
534   lua_insert(L, 1);
535                                   /* L:      value table key value */
536
537   /* save the copied value in the table for subsequent accesses */
538   lua_settable(L, -3);
539                                   /* L:      value table */
540   lua_settop(L, 1);
541                                   /* L:      value */
542
543   XBT_DEBUG("__index of '%s' returns %s", key, sglua_tostring(L, -1));
544
545   return 1;
546 }
547
548 /**
549  * @brief Creates a new Lua state and get its environment from the maestro
550  * state.
551  *
552  * The state created is independent from maestro and has its own copies of
553  * global and registry values.
554  * However, the global and registry values are not copied right now from
555  * the original state; they are copied only the first time they are accessed.
556  * This behavior saves time and memory, and is okay for Simgrid's needs.
557  *
558  * TODO: if the simulation runs in parallel, copy everything right now?
559  *
560  * @return the state created
561  */
562 lua_State* sglua_clone_maestro(void) {
563
564   /* create the new state */
565   lua_State *L = luaL_newstate();
566
567   /* set its environment and its registry:
568    * - create a table newenv
569    * - create a metatable mt
570    * - set mt.__index = a function that copies the global from the father state
571    * - set mt as the metatable of the registry
572    * - set mt as the metatable of newenv
573    * - set newenv as the environment of the new state
574    */
575   lua_pushthread(L);                        /* thread */
576   lua_newtable(L);                          /* thread newenv */
577   lua_newtable(L);                          /* thread newenv mt */
578   lua_pushvalue(L, LUA_REGISTRYINDEX);      /* thread newenv mt reg */
579   lua_pushcfunction(L, l_get_from_maestro); /* thread newenv mt reg f */
580   lua_setfield(L, -3, "__index");           /* thread newenv mt reg */
581   lua_pushvalue(L, -2);                     /* thread newenv mt reg mt */
582   lua_setmetatable(L, -2);                  /* thread newenv mt reg */
583   lua_pop(L, 1);                            /* thread newenv mt */
584   lua_setmetatable(L, -2);                  /* thread newenv */
585   lua_setfenv(L, -2);                       /* thread */
586   lua_pop(L, 1);                            /* -- */
587
588   /* create the table of known tables from maestro */
589   lua_pushstring(L, "simgrid.maestro_tables");
590                                             /* "simgrid.maestro_tables" */
591   lua_newtable(L);                          /* "simgrid.maestro_tables" maestrotbs*/
592   lua_rawset(L, LUA_REGISTRYINDEX);
593                                             /* -- */
594
595   /* open the standard libs (theoretically, this is not necessary as they can
596    * be inherited like any global values, but without a proper support of
597    * closures, iterators like ipairs don't work). */
598   XBT_DEBUG("Metatable of globals and registry set, opening standard libraries now");
599   luaL_openlibs(L);
600
601   XBT_DEBUG("New state created");
602
603   return L;
604 }