diff options
author | Benoit Germain <b n t DOT g e r m a i n AT g m a i l DOT c o m> | 2018-10-30 14:18:10 +0100 |
---|---|---|
committer | Benoit Germain <b n t DOT g e r m a i n AT g m a i l DOT c o m> | 2018-10-30 14:18:10 +0100 |
commit | d82bf70e9db3a25ec451b599660f3a837f21caee (patch) | |
tree | e9e861252a118c8c66e874fedb93c2a32733aa0f /src | |
parent | ea9e8a3af1c2357c454ef18c8136c14a22b8675a (diff) | |
download | lanes-d82bf70e9db3a25ec451b599660f3a837f21caee.tar.gz lanes-d82bf70e9db3a25ec451b599660f3a837f21caee.tar.bz2 lanes-d82bf70e9db3a25ec451b599660f3a837f21caee.zip |
Improve LuaJIT-x64 compatibility
Restrict internal "light userdata constants" to 47 significant bits when
compiling against LuaJIT-x64
Diffstat (limited to 'src')
-rw-r--r-- | src/deep.c | 20 | ||||
-rw-r--r-- | src/keeper.c | 16 | ||||
-rw-r--r-- | src/keeper.h | 4 | ||||
-rw-r--r-- | src/lanes.c | 74 | ||||
-rw-r--r-- | src/tools.c | 7 | ||||
-rw-r--r-- | src/uniquekey.h | 25 | ||||
-rw-r--r-- | src/universe.c | 11 |
7 files changed, 98 insertions, 59 deletions
@@ -44,6 +44,7 @@ THE SOFTWARE. | |||
44 | #include "deep.h" | 44 | #include "deep.h" |
45 | #include "tools.h" | 45 | #include "tools.h" |
46 | #include "universe.h" | 46 | #include "universe.h" |
47 | #include "uniquekey.h" | ||
47 | 48 | ||
48 | /*-- Metatable copying --*/ | 49 | /*-- Metatable copying --*/ |
49 | 50 | ||
@@ -59,19 +60,19 @@ THE SOFTWARE. | |||
59 | /* | 60 | /* |
60 | * Does what the original 'push_registry_subtable' function did, but adds an optional mode argument to it | 61 | * Does what the original 'push_registry_subtable' function did, but adds an optional mode argument to it |
61 | */ | 62 | */ |
62 | void push_registry_subtable_mode( lua_State* L, void* key_, const char* mode_) | 63 | static void push_registry_subtable_mode( lua_State* L, UniqueKey key_, const char* mode_) |
63 | { | 64 | { |
64 | STACK_GROW( L, 3); | 65 | STACK_GROW( L, 3); |
65 | STACK_CHECK( L); | 66 | STACK_CHECK( L); |
66 | 67 | ||
67 | lua_pushlightuserdata( L, key_); // key | 68 | push_unique_key( L, key_); // key |
68 | lua_rawget( L, LUA_REGISTRYINDEX); // {}|nil | 69 | lua_rawget( L, LUA_REGISTRYINDEX); // {}|nil |
69 | 70 | ||
70 | if( lua_isnil( L, -1)) | 71 | if( lua_isnil( L, -1)) |
71 | { | 72 | { |
72 | lua_pop( L, 1); // | 73 | lua_pop( L, 1); // |
73 | lua_newtable( L); // {} | 74 | lua_newtable( L); // {} |
74 | lua_pushlightuserdata( L, key_); // {} key | 75 | push_unique_key( L, key_); // {} key |
75 | lua_pushvalue( L, -2); // {} key {} | 76 | lua_pushvalue( L, -2); // {} key {} |
76 | 77 | ||
77 | // _R[key_] = {} | 78 | // _R[key_] = {} |
@@ -96,7 +97,7 @@ void push_registry_subtable_mode( lua_State* L, void* key_, const char* mode_) | |||
96 | * Push a registry subtable (keyed by unique 'key_') onto the stack. | 97 | * Push a registry subtable (keyed by unique 'key_') onto the stack. |
97 | * If the subtable does not exist, it is created and chained. | 98 | * If the subtable does not exist, it is created and chained. |
98 | */ | 99 | */ |
99 | void push_registry_subtable( lua_State* L, void* key_) | 100 | void push_registry_subtable( lua_State* L, UniqueKey key_) |
100 | { | 101 | { |
101 | push_registry_subtable_mode( L, key_, NULL); | 102 | push_registry_subtable_mode( L, key_, NULL); |
102 | } | 103 | } |
@@ -121,15 +122,14 @@ void luaG_pushdeepversion( lua_State* L) { (void) lua_pushliteral( L, "ab8743e5- | |||
121 | * metatable -> idfunc | 122 | * metatable -> idfunc |
122 | * idfunc -> metatable | 123 | * idfunc -> metatable |
123 | */ | 124 | */ |
124 | // crc64/we of string "DEEP_LOOKUP_KEY" generated at https://www.nitrxgen.net/hashgen/ | 125 | // crc64/we of string "DEEP_LOOKUP_KEY" generated at http://www.nitrxgen.net/hashgen/ |
125 | #define DEEP_LOOKUP_KEY ((void*)0x9fb9b4f3f633d83d) | 126 | static DECLARE_CONST_UNIQUE_KEY( DEEP_LOOKUP_KEY, (void*)0x9fb9b4f3f633d83d); |
126 | |||
127 | 127 | ||
128 | /* | 128 | /* |
129 | * The deep proxy cache is a weak valued table listing all deep UD proxies indexed by the deep UD that they are proxying | 129 | * The deep proxy cache is a weak valued table listing all deep UD proxies indexed by the deep UD that they are proxying |
130 | * crc64/we of string "DEEP_PROXY_CACHE_KEY" generated at https://www.nitrxgen.net/hashgen/ | 130 | * crc64/we of string "DEEP_PROXY_CACHE_KEY" generated at http://www.nitrxgen.net/hashgen/ |
131 | */ | 131 | */ |
132 | #define DEEP_PROXY_CACHE_KEY ((void*)0x05773d6fc26be106) | 132 | static DECLARE_CONST_UNIQUE_KEY( DEEP_PROXY_CACHE_KEY, (void*)0x05773d6fc26be106); |
133 | 133 | ||
134 | /* | 134 | /* |
135 | * Sets up [-1]<->[-2] two-way lookups, and ensures the lookup table exists. | 135 | * Sets up [-1]<->[-2] two-way lookups, and ensures the lookup table exists. |
@@ -158,7 +158,7 @@ static void get_deep_lookup( lua_State* L) | |||
158 | { | 158 | { |
159 | STACK_GROW( L, 1); | 159 | STACK_GROW( L, 1); |
160 | STACK_CHECK( L); // a | 160 | STACK_CHECK( L); // a |
161 | lua_pushlightuserdata( L, DEEP_LOOKUP_KEY); // a DLK | 161 | push_unique_key( L, DEEP_LOOKUP_KEY); // a DLK |
162 | lua_rawget( L, LUA_REGISTRYINDEX); // a {} | 162 | lua_rawget( L, LUA_REGISTRYINDEX); // a {} |
163 | 163 | ||
164 | if( !lua_isnil( L, -1)) | 164 | if( !lua_isnil( L, -1)) |
diff --git a/src/keeper.c b/src/keeper.c index 907334f..715583b 100644 --- a/src/keeper.c +++ b/src/keeper.c | |||
@@ -48,6 +48,7 @@ | |||
48 | #include "compat.h" | 48 | #include "compat.h" |
49 | #include "tools.h" | 49 | #include "tools.h" |
50 | #include "universe.h" | 50 | #include "universe.h" |
51 | #include "uniquekey.h" | ||
51 | 52 | ||
52 | //################################################################################### | 53 | //################################################################################### |
53 | // Keeper implementation | 54 | // Keeper implementation |
@@ -160,13 +161,14 @@ static void fifo_pop( lua_State* L, keeper_fifo* fifo_, lua_Integer count_) | |||
160 | 161 | ||
161 | // in: linda_ud expected at *absolute* stack slot idx | 162 | // in: linda_ud expected at *absolute* stack slot idx |
162 | // out: fifos[ud] | 163 | // out: fifos[ud] |
163 | static void* const fifos_key = (void*) prepare_fifo_access; | 164 | // crc64/we of string "FIFOS_KEY" generated at http://www.nitrxgen.net/hashgen/ |
165 | static DECLARE_CONST_UNIQUE_KEY( FIFOS_KEY, 0xdce50bbc351cd465); | ||
164 | static void push_table( lua_State* L, int idx_) | 166 | static void push_table( lua_State* L, int idx_) |
165 | { | 167 | { |
166 | STACK_GROW( L, 4); | 168 | STACK_GROW( L, 4); |
167 | STACK_CHECK( L); | 169 | STACK_CHECK( L); |
168 | idx_ = lua_absindex( L, idx_); | 170 | idx_ = lua_absindex( L, idx_); |
169 | lua_pushlightuserdata( L, fifos_key); // ud fifos_key | 171 | push_unique_key( L, FIFOS_KEY); // ud fifos_key |
170 | lua_rawget( L, LUA_REGISTRYINDEX); // ud fifos | 172 | lua_rawget( L, LUA_REGISTRYINDEX); // ud fifos |
171 | lua_pushvalue( L, idx_); // ud fifos ud | 173 | lua_pushvalue( L, idx_); // ud fifos ud |
172 | lua_rawget( L, -2); // ud fifos fifos[ud] | 174 | lua_rawget( L, -2); // ud fifos fifos[ud] |
@@ -191,7 +193,7 @@ int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, ptrdiff_t | |||
191 | if( KL == NULL) return 0; | 193 | if( KL == NULL) return 0; |
192 | STACK_GROW( KL, 4); | 194 | STACK_GROW( KL, 4); |
193 | STACK_CHECK( KL); | 195 | STACK_CHECK( KL); |
194 | lua_pushlightuserdata( KL, fifos_key); // fifos_key | 196 | push_unique_key( KL, FIFOS_KEY); // fifos_key |
195 | lua_rawget( KL, LUA_REGISTRYINDEX); // fifos | 197 | lua_rawget( KL, LUA_REGISTRYINDEX); // fifos |
196 | lua_pushlightuserdata( KL, ptr_); // fifos ud | 198 | lua_pushlightuserdata( KL, ptr_); // fifos ud |
197 | lua_rawget( KL, -2); // fifos storage | 199 | lua_rawget( KL, -2); // fifos storage |
@@ -239,7 +241,7 @@ int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, ptrdiff_t | |||
239 | int keepercall_clear( lua_State* L) | 241 | int keepercall_clear( lua_State* L) |
240 | { | 242 | { |
241 | STACK_GROW( L, 3); | 243 | STACK_GROW( L, 3); |
242 | lua_pushlightuserdata( L, fifos_key); // ud fifos_key | 244 | push_unique_key( L, FIFOS_KEY); // ud fifos_key |
243 | lua_rawget( L, LUA_REGISTRYINDEX); // ud fifos | 245 | lua_rawget( L, LUA_REGISTRYINDEX); // ud fifos |
244 | lua_pushvalue( L, 1); // ud fifos ud | 246 | lua_pushvalue( L, 1); // ud fifos ud |
245 | lua_pushnil( L); // ud fifos ud nil | 247 | lua_pushnil( L); // ud fifos ud nil |
@@ -704,7 +706,7 @@ void init_keepers( Universe* U, lua_State* L) | |||
704 | lua_setglobal( K, "decoda_name"); // | 706 | lua_setglobal( K, "decoda_name"); // |
705 | 707 | ||
706 | // create the fifos table in the keeper state | 708 | // create the fifos table in the keeper state |
707 | lua_pushlightuserdata( K, fifos_key); // fifo_key | 709 | push_unique_key( K, FIFOS_KEY); // fifo_key |
708 | lua_newtable( K); // fifo_key {} | 710 | lua_newtable( K); // fifo_key {} |
709 | lua_rawset( K, LUA_REGISTRYINDEX); // | 711 | lua_rawset( K, LUA_REGISTRYINDEX); // |
710 | 712 | ||
@@ -754,13 +756,13 @@ void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode mode_) | |||
754 | { | 756 | { |
755 | if( lua_isnil( L, i)) | 757 | if( lua_isnil( L, i)) |
756 | { | 758 | { |
757 | lua_pushlightuserdata( L, NIL_SENTINEL); | 759 | push_unique_key( L, NIL_SENTINEL); |
758 | lua_replace( L, i); | 760 | lua_replace( L, i); |
759 | } | 761 | } |
760 | } | 762 | } |
761 | else | 763 | else |
762 | { | 764 | { |
763 | if( lua_touserdata( L, i) == NIL_SENTINEL) | 765 | if( equal_unique_key( L, i, NIL_SENTINEL)) |
764 | { | 766 | { |
765 | lua_pushnil( L); | 767 | lua_pushnil( L); |
766 | lua_replace( L, i); | 768 | lua_replace( L, i); |
diff --git a/src/keeper.h b/src/keeper.h index 06cf3be..37922fb 100644 --- a/src/keeper.h +++ b/src/keeper.h | |||
@@ -3,6 +3,7 @@ | |||
3 | 3 | ||
4 | #include "lua.h" | 4 | #include "lua.h" |
5 | #include "threading.h" | 5 | #include "threading.h" |
6 | #include "uniquekey.h" | ||
6 | 7 | ||
7 | // forwards | 8 | // forwards |
8 | struct s_Universe; | 9 | struct s_Universe; |
@@ -34,7 +35,8 @@ void keeper_release( Keeper* K); | |||
34 | void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode const mode_); | 35 | void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode const mode_); |
35 | int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, ptrdiff_t magic_); | 36 | int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, ptrdiff_t magic_); |
36 | 37 | ||
37 | #define NIL_SENTINEL ((void*)keeper_toggle_nil_sentinels) | 38 | // crc64/we of string "NIL_SENTINEL" generated at http://www.nitrxgen.net/hashgen/ |
39 | static DECLARE_CONST_UNIQUE_KEY( NIL_SENTINEL, 0x7eaafa003a1d11a1); | ||
38 | 40 | ||
39 | typedef lua_CFunction keeper_api_t; | 41 | typedef lua_CFunction keeper_api_t; |
40 | #define KEEPER_API( _op) keepercall_ ## _op | 42 | #define KEEPER_API( _op) keepercall_ ## _op |
diff --git a/src/lanes.c b/src/lanes.c index 0a04d88..5a1f94a 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -93,6 +93,7 @@ THE SOFTWARE. | |||
93 | #include "tools.h" | 93 | #include "tools.h" |
94 | #include "universe.h" | 94 | #include "universe.h" |
95 | #include "keeper.h" | 95 | #include "keeper.h" |
96 | #include "uniquekey.h" | ||
96 | 97 | ||
97 | #if !(defined( PLATFORM_XBOX) || defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC)) | 98 | #if !(defined( PLATFORM_XBOX) || defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC)) |
98 | # include <sys/time.h> | 99 | # include <sys/time.h> |
@@ -193,13 +194,15 @@ typedef struct s_Lane Lane; | |||
193 | // | 194 | // |
194 | #define lua_toLane( L, i) (*((Lane**) luaL_checkudata( L, i, "Lane"))) | 195 | #define lua_toLane( L, i) (*((Lane**) luaL_checkudata( L, i, "Lane"))) |
195 | 196 | ||
196 | #define CANCEL_TEST_KEY ((void*)get_lane_from_registry) // used as registry key | 197 | // crc64/we of string "CANCEL_TEST_KEY" generated at http://www.nitrxgen.net/hashgen/ |
198 | static DECLARE_CONST_UNIQUE_KEY( CANCEL_TEST_KEY, 0xe66f5960c57d133a); // used as registry key | ||
199 | |||
197 | static inline Lane* get_lane_from_registry( lua_State* L) | 200 | static inline Lane* get_lane_from_registry( lua_State* L) |
198 | { | 201 | { |
199 | Lane* s; | 202 | Lane* s; |
200 | STACK_GROW( L, 1); | 203 | STACK_GROW( L, 1); |
201 | STACK_CHECK( L); | 204 | STACK_CHECK( L); |
202 | lua_pushlightuserdata( L, CANCEL_TEST_KEY); | 205 | push_unique_key( L, CANCEL_TEST_KEY); |
203 | lua_rawget( L, LUA_REGISTRYINDEX); | 206 | lua_rawget( L, LUA_REGISTRYINDEX); |
204 | s = lua_touserdata( L, -1); // lightuserdata (true 's_lane' pointer) / nil | 207 | s = lua_touserdata( L, -1); // lightuserdata (true 's_lane' pointer) / nil |
205 | lua_pop( L, 1); | 208 | lua_pop( L, 1); |
@@ -238,11 +241,13 @@ static inline enum e_cancel_request cancel_test( lua_State* L) | |||
238 | return s ? s->cancel_request : CANCEL_NONE; | 241 | return s ? s->cancel_request : CANCEL_NONE; |
239 | } | 242 | } |
240 | 243 | ||
241 | #define CANCEL_ERROR ((void*)cancel_error) // 'cancel_error' sentinel | 244 | // crc64/we of string "CANCEL_ERROR" generated at http://www.nitrxgen.net/hashgen/ |
245 | static DECLARE_CONST_UNIQUE_KEY( CANCEL_ERROR, 0xe97d41626cc97577); // 'cancel_error' sentinel | ||
246 | |||
242 | static int cancel_error( lua_State* L) | 247 | static int cancel_error( lua_State* L) |
243 | { | 248 | { |
244 | STACK_GROW( L, 1); | 249 | STACK_GROW( L, 1); |
245 | lua_pushlightuserdata( L, CANCEL_ERROR); // special error value | 250 | push_unique_key( L, CANCEL_ERROR); // special error value |
246 | return lua_error( L); // doesn't return | 251 | return lua_error( L); // doesn't return |
247 | } | 252 | } |
248 | 253 | ||
@@ -258,7 +263,8 @@ static void cancel_hook( lua_State* L, lua_Debug* ar) | |||
258 | 263 | ||
259 | #if ERROR_FULL_STACK | 264 | #if ERROR_FULL_STACK |
260 | static int lane_error( lua_State* L); | 265 | static int lane_error( lua_State* L); |
261 | #define STACK_TRACE_KEY ((void*)lane_error) // used as registry key | 266 | // crc64/we of string "STACK_TRACE_KEY" generated at http://www.nitrxgen.net/hashgen/ |
267 | static DECLARE_CONST_UNIQUE_KEY( STACK_TRACE_KEY, 0x024d5411677ce879); | ||
262 | #endif // ERROR_FULL_STACK | 268 | #endif // ERROR_FULL_STACK |
263 | 269 | ||
264 | /* | 270 | /* |
@@ -463,7 +469,7 @@ LUAG_FUNC( linda_send) | |||
463 | int pushed; | 469 | int pushed; |
464 | time_d timeout = -1.0; | 470 | time_d timeout = -1.0; |
465 | uint_t key_i = 2; // index of first key, if timeout not there | 471 | uint_t key_i = 2; // index of first key, if timeout not there |
466 | void* as_nil_sentinel; // if not NULL, send() will silently send a single nil if nothing is provided | 472 | bool_t as_nil_sentinel; // if not NULL, send() will silently send a single nil if nothing is provided |
467 | 473 | ||
468 | if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion | 474 | if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion |
469 | { | 475 | { |
@@ -475,8 +481,8 @@ LUAG_FUNC( linda_send) | |||
475 | ++ key_i; | 481 | ++ key_i; |
476 | } | 482 | } |
477 | 483 | ||
478 | as_nil_sentinel = lua_touserdata( L, key_i); | 484 | as_nil_sentinel = equal_unique_key( L, key_i, NIL_SENTINEL); |
479 | if( as_nil_sentinel == NIL_SENTINEL) | 485 | if( as_nil_sentinel) |
480 | { | 486 | { |
481 | // the real key to send data to is after the NIL_SENTINEL marker | 487 | // the real key to send data to is after the NIL_SENTINEL marker |
482 | ++ key_i; | 488 | ++ key_i; |
@@ -490,10 +496,10 @@ LUAG_FUNC( linda_send) | |||
490 | // make sure there is something to send | 496 | // make sure there is something to send |
491 | if( (uint_t)lua_gettop( L) == key_i) | 497 | if( (uint_t)lua_gettop( L) == key_i) |
492 | { | 498 | { |
493 | if( as_nil_sentinel == NIL_SENTINEL) | 499 | if( as_nil_sentinel) |
494 | { | 500 | { |
495 | // send a single nil if nothing is provided | 501 | // send a single nil if nothing is provided |
496 | lua_pushlightuserdata( L, NIL_SENTINEL); | 502 | push_unique_key( L, NIL_SENTINEL); |
497 | } | 503 | } |
498 | else | 504 | else |
499 | { | 505 | { |
@@ -584,7 +590,7 @@ LUAG_FUNC( linda_send) | |||
584 | { | 590 | { |
585 | case CANCEL_SOFT: | 591 | case CANCEL_SOFT: |
586 | // if user wants to soft-cancel, the call returns lanes.cancel_error | 592 | // if user wants to soft-cancel, the call returns lanes.cancel_error |
587 | lua_pushlightuserdata( L, CANCEL_ERROR); | 593 | push_unique_key( L, CANCEL_ERROR); |
588 | return 1; | 594 | return 1; |
589 | 595 | ||
590 | case CANCEL_HARD: | 596 | case CANCEL_HARD: |
@@ -741,7 +747,7 @@ LUAG_FUNC( linda_receive) | |||
741 | { | 747 | { |
742 | case CANCEL_SOFT: | 748 | case CANCEL_SOFT: |
743 | // if user wants to soft-cancel, the call returns CANCEL_ERROR | 749 | // if user wants to soft-cancel, the call returns CANCEL_ERROR |
744 | lua_pushlightuserdata( L, CANCEL_ERROR); | 750 | push_unique_key( L, CANCEL_ERROR); |
745 | return 1; | 751 | return 1; |
746 | 752 | ||
747 | case CANCEL_HARD: | 753 | case CANCEL_HARD: |
@@ -803,7 +809,7 @@ LUAG_FUNC( linda_set) | |||
803 | else // linda is cancelled | 809 | else // linda is cancelled |
804 | { | 810 | { |
805 | // do nothing and return lanes.cancel_error | 811 | // do nothing and return lanes.cancel_error |
806 | lua_pushlightuserdata( L, CANCEL_ERROR); | 812 | push_unique_key( L, CANCEL_ERROR); |
807 | pushed = 1; | 813 | pushed = 1; |
808 | } | 814 | } |
809 | keeper_release( K); | 815 | keeper_release( K); |
@@ -871,7 +877,7 @@ LUAG_FUNC( linda_get) | |||
871 | else // linda is cancelled | 877 | else // linda is cancelled |
872 | { | 878 | { |
873 | // do nothing and return lanes.cancel_error | 879 | // do nothing and return lanes.cancel_error |
874 | lua_pushlightuserdata( L, CANCEL_ERROR); | 880 | push_unique_key( L, CANCEL_ERROR); |
875 | pushed = 1; | 881 | pushed = 1; |
876 | } | 882 | } |
877 | keeper_release( K); | 883 | keeper_release( K); |
@@ -922,7 +928,7 @@ LUAG_FUNC( linda_limit) | |||
922 | else // linda is cancelled | 928 | else // linda is cancelled |
923 | { | 929 | { |
924 | // do nothing and return lanes.cancel_error | 930 | // do nothing and return lanes.cancel_error |
925 | lua_pushlightuserdata( L, CANCEL_ERROR); | 931 | push_unique_key( L, CANCEL_ERROR); |
926 | pushed = 1; | 932 | pushed = 1; |
927 | } | 933 | } |
928 | keeper_release( K); | 934 | keeper_release( K); |
@@ -1249,7 +1255,7 @@ static void* linda_id( lua_State* L, enum eDeepOp op_) | |||
1249 | lua_pushliteral( L, BATCH_SENTINEL); | 1255 | lua_pushliteral( L, BATCH_SENTINEL); |
1250 | lua_setfield(L, -2, "batched"); | 1256 | lua_setfield(L, -2, "batched"); |
1251 | 1257 | ||
1252 | lua_pushlightuserdata( L, NIL_SENTINEL); | 1258 | push_unique_key( L, NIL_SENTINEL); |
1253 | lua_setfield(L, -2, "null"); | 1259 | lua_setfield(L, -2, "null"); |
1254 | 1260 | ||
1255 | luaG_pushdeepversion( L); | 1261 | luaG_pushdeepversion( L); |
@@ -1815,7 +1821,8 @@ LUAG_FUNC( set_singlethreaded) | |||
1815 | */ | 1821 | */ |
1816 | #if ERROR_FULL_STACK | 1822 | #if ERROR_FULL_STACK |
1817 | 1823 | ||
1818 | # define EXTENDED_STACK_TRACE_KEY ((void*)LG_set_error_reporting) // used as registry key | 1824 | // crc64/we of string "EXTENDED_STACK_TRACE_REGKEY" generated at http://www.nitrxgen.net/hashgen/ |
1825 | static DECLARE_CONST_UNIQUE_KEY( EXTENDED_STACK_TRACE_REGKEY, 0x7a59821071066e49); // used as registry key | ||
1819 | 1826 | ||
1820 | LUAG_FUNC( set_error_reporting) | 1827 | LUAG_FUNC( set_error_reporting) |
1821 | { | 1828 | { |
@@ -1836,7 +1843,7 @@ LUAG_FUNC( set_error_reporting) | |||
1836 | return luaL_error( L, "unsupported error reporting model"); | 1843 | return luaL_error( L, "unsupported error reporting model"); |
1837 | } | 1844 | } |
1838 | done: | 1845 | done: |
1839 | lua_pushlightuserdata( L, EXTENDED_STACK_TRACE_KEY); | 1846 | push_unique_key( L, EXTENDED_STACK_TRACE_REGKEY); |
1840 | lua_pushboolean( L, equal); | 1847 | lua_pushboolean( L, equal); |
1841 | lua_rawset( L, LUA_REGISTRYINDEX); | 1848 | lua_rawset( L, LUA_REGISTRYINDEX); |
1842 | return 0; | 1849 | return 0; |
@@ -1853,13 +1860,13 @@ static int lane_error( lua_State* L) | |||
1853 | 1860 | ||
1854 | // Don't do stack survey for cancelled lanes. | 1861 | // Don't do stack survey for cancelled lanes. |
1855 | // | 1862 | // |
1856 | if( lua_touserdata( L, 1) == CANCEL_ERROR) | 1863 | if( equal_unique_key( L, 1, CANCEL_ERROR)) |
1857 | { | 1864 | { |
1858 | return 1; // just pass on | 1865 | return 1; // just pass on |
1859 | } | 1866 | } |
1860 | 1867 | ||
1861 | STACK_GROW( L, 3); | 1868 | STACK_GROW( L, 3); |
1862 | lua_pushlightuserdata( L, EXTENDED_STACK_TRACE_KEY); // some_error estk | 1869 | push_unique_key( L, EXTENDED_STACK_TRACE_REGKEY); // some_error estk |
1863 | lua_rawget( L, LUA_REGISTRYINDEX); // some_error basic|extended | 1870 | lua_rawget( L, LUA_REGISTRYINDEX); // some_error basic|extended |
1864 | extended = lua_toboolean( L, -1); | 1871 | extended = lua_toboolean( L, -1); |
1865 | lua_pop( L, 1); // some_error | 1872 | lua_pop( L, 1); // some_error |
@@ -1913,7 +1920,7 @@ static int lane_error( lua_State* L) | |||
1913 | lua_rawseti( L, -2, (lua_Integer) n); // some_error {} | 1920 | lua_rawseti( L, -2, (lua_Integer) n); // some_error {} |
1914 | } | 1921 | } |
1915 | 1922 | ||
1916 | lua_pushlightuserdata( L, STACK_TRACE_KEY); // some_error {} stk | 1923 | push_unique_key( L, STACK_TRACE_KEY); // some_error {} stk |
1917 | lua_insert( L, -2); // some_error stk {} | 1924 | lua_insert( L, -2); // some_error stk {} |
1918 | lua_rawset( L, LUA_REGISTRYINDEX); // some_error | 1925 | lua_rawset( L, LUA_REGISTRYINDEX); // some_error |
1919 | 1926 | ||
@@ -1936,14 +1943,14 @@ static void push_stack_trace( lua_State* L, int rc_, int stk_base_) | |||
1936 | { | 1943 | { |
1937 | // fetch the call stack table from the registry where the handler stored it | 1944 | // fetch the call stack table from the registry where the handler stored it |
1938 | STACK_GROW( L, 1); | 1945 | STACK_GROW( L, 1); |
1939 | lua_pushlightuserdata( L, STACK_TRACE_KEY); // err STACK_TRACE_KEY | 1946 | push_unique_key( L, STACK_TRACE_KEY); // err STACK_TRACE_KEY |
1940 | // yields nil if no stack was generated (in case of cancellation for example) | 1947 | // yields nil if no stack was generated (in case of cancellation for example) |
1941 | lua_rawget( L, LUA_REGISTRYINDEX); // err trace|nil | 1948 | lua_rawget( L, LUA_REGISTRYINDEX); // err trace|nil |
1942 | ASSERT_L( lua_gettop( L) == 1 + stk_base_); | 1949 | ASSERT_L( lua_gettop( L) == 1 + stk_base_); |
1943 | 1950 | ||
1944 | // For cancellation the error message is CANCEL_ERROR, and a stack trace isn't placed | 1951 | // For cancellation the error message is CANCEL_ERROR, and a stack trace isn't placed |
1945 | // For other errors, the message can be whatever was thrown, and we should have a stack trace table | 1952 | // For other errors, the message can be whatever was thrown, and we should have a stack trace table |
1946 | ASSERT_L( lua_type( L, 1 + stk_base_) == ((lua_touserdata( L, stk_base_) == CANCEL_ERROR) ? LUA_TNIL : LUA_TTABLE)); | 1953 | ASSERT_L( lua_type( L, 1 + stk_base_) == (equal_unique_key( L, stk_base_, CANCEL_ERROR) ? LUA_TNIL : LUA_TTABLE)); |
1947 | // Just leaving the stack trace table on the stack is enough to get it through to the master. | 1954 | // Just leaving the stack trace table on the stack is enough to get it through to the master. |
1948 | break; | 1955 | break; |
1949 | } | 1956 | } |
@@ -1953,18 +1960,19 @@ static void push_stack_trace( lua_State* L, int rc_, int stk_base_) | |||
1953 | case LUA_ERRERR: // error while running the error handler (if any, for example an out-of-memory condition) | 1960 | case LUA_ERRERR: // error while running the error handler (if any, for example an out-of-memory condition) |
1954 | default: | 1961 | default: |
1955 | // we should have a single value which is either a string (the error message) or CANCEL_ERROR | 1962 | // we should have a single value which is either a string (the error message) or CANCEL_ERROR |
1956 | ASSERT_L( (lua_gettop( L) == stk_base_) && ((lua_type( L, stk_base_) == LUA_TSTRING) || (lua_touserdata( L, stk_base_) == CANCEL_ERROR))); | 1963 | ASSERT_L( (lua_gettop( L) == stk_base_) && ((lua_type( L, stk_base_) == LUA_TSTRING) || equal_unique_key( L, stk_base_, CANCEL_ERROR))); |
1957 | break; | 1964 | break; |
1958 | } | 1965 | } |
1959 | } | 1966 | } |
1960 | 1967 | ||
1961 | LUAG_FUNC( set_debug_threadname) | 1968 | LUAG_FUNC( set_debug_threadname) |
1962 | { | 1969 | { |
1970 | DECLARE_CONST_UNIQUE_KEY( hidden_regkey, LG_set_debug_threadname); | ||
1963 | // C s_lane structure is a light userdata upvalue | 1971 | // C s_lane structure is a light userdata upvalue |
1964 | Lane* s = lua_touserdata( L, lua_upvalueindex( 1)); | 1972 | Lane* s = lua_touserdata( L, lua_upvalueindex( 1)); |
1965 | luaL_checktype( L, -1, LUA_TSTRING); // "name" | 1973 | luaL_checktype( L, -1, LUA_TSTRING); // "name" |
1966 | // store a hidden reference in the registry to make sure the string is kept around even if a lane decides to manually change the "decoda_name" global... | 1974 | // store a hidden reference in the registry to make sure the string is kept around even if a lane decides to manually change the "decoda_name" global... |
1967 | lua_pushlightuserdata( L, LG_set_debug_threadname); // "name" lud | 1975 | push_unique_key( L, hidden_regkey); // "name" lud |
1968 | lua_pushvalue( L, -2); // "name" lud "name" | 1976 | lua_pushvalue( L, -2); // "name" lud "name" |
1969 | lua_rawset( L, LUA_REGISTRYINDEX); // "name" | 1977 | lua_rawset( L, LUA_REGISTRYINDEX); // "name" |
1970 | s->debug_name = lua_tostring( L, -1); | 1978 | s->debug_name = lua_tostring( L, -1); |
@@ -2089,7 +2097,7 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void* vs) | |||
2089 | // in case of error and if it exists, fetch stack trace from registry and push it | 2097 | // in case of error and if it exists, fetch stack trace from registry and push it |
2090 | push_stack_trace( L, rc, 1); // retvals|error [trace] | 2098 | push_stack_trace( L, rc, 1); // retvals|error [trace] |
2091 | 2099 | ||
2092 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END, L, get_errcode_name( rc), (lua_touserdata( L, 1)==CANCEL_ERROR) ? "cancelled" : lua_typename( L, lua_type( L, 1)))); | 2100 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END, L, get_errcode_name( rc), equal_unique_key( L, 1, CANCEL_ERROR) ? "cancelled" : lua_typename( L, lua_type( L, 1)))); |
2093 | //STACK_DUMP(L); | 2101 | //STACK_DUMP(L); |
2094 | // Call finalizers, if the script has set them up. | 2102 | // Call finalizers, if the script has set them up. |
2095 | // | 2103 | // |
@@ -2118,7 +2126,7 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void* vs) | |||
2118 | { | 2126 | { |
2119 | // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them | 2127 | // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them |
2120 | 2128 | ||
2121 | enum e_status st = (rc == 0) ? DONE : (lua_touserdata( L, 1) == CANCEL_ERROR) ? CANCELLED : ERROR_ST; | 2129 | enum e_status st = (rc == 0) ? DONE : equal_unique_key( L, 1, CANCEL_ERROR) ? CANCELLED : ERROR_ST; |
2122 | 2130 | ||
2123 | // Posix no PTHREAD_TIMEDJOIN: | 2131 | // Posix no PTHREAD_TIMEDJOIN: |
2124 | // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change | 2132 | // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change |
@@ -2183,9 +2191,9 @@ LUAG_FUNC( register) | |||
2183 | return 0; | 2191 | return 0; |
2184 | } | 2192 | } |
2185 | 2193 | ||
2194 | // crc64/we of string "GCCB_KEY" generated at http://www.nitrxgen.net/hashgen/ | ||
2195 | static DECLARE_CONST_UNIQUE_KEY( GCCB_KEY, 0xcfb1f046ef074e88); | ||
2186 | 2196 | ||
2187 | LUAG_FUNC( thread_gc); | ||
2188 | #define GCCB_KEY (void*)LG_thread_gc | ||
2189 | //--- | 2197 | //--- |
2190 | // lane_ud = lane_new( function | 2198 | // lane_ud = lane_new( function |
2191 | // , [libs_str] | 2199 | // , [libs_str] |
@@ -2429,7 +2437,7 @@ LUAG_FUNC( lane_new) | |||
2429 | // Store the gc_cb callback in the uservalue | 2437 | // Store the gc_cb callback in the uservalue |
2430 | if( gc_cb_idx > 0) | 2438 | if( gc_cb_idx > 0) |
2431 | { | 2439 | { |
2432 | lua_pushlightuserdata( L, GCCB_KEY); // func libs cancelstep priority globals package required gc_cb lane uv k | 2440 | push_unique_key( L, GCCB_KEY); // func libs cancelstep priority globals package required gc_cb lane uv k |
2433 | lua_pushvalue( L, gc_cb_idx); // func libs cancelstep priority globals package required gc_cb lane uv k gc_cb | 2441 | lua_pushvalue( L, gc_cb_idx); // func libs cancelstep priority globals package required gc_cb lane uv k gc_cb |
2434 | lua_rawset( L, -3); // func libs cancelstep priority globals package required gc_cb lane uv | 2442 | lua_rawset( L, -3); // func libs cancelstep priority globals package required gc_cb lane uv |
2435 | } | 2443 | } |
@@ -2437,7 +2445,7 @@ LUAG_FUNC( lane_new) | |||
2437 | lua_setuservalue( L, -2); // func libs cancelstep priority globals package required gc_cb lane | 2445 | lua_setuservalue( L, -2); // func libs cancelstep priority globals package required gc_cb lane |
2438 | 2446 | ||
2439 | // Store 's' in the lane's registry, for 'cancel_test()' (even if 'cs'==0 we still do cancel tests at pending send/receive). | 2447 | // Store 's' in the lane's registry, for 'cancel_test()' (even if 'cs'==0 we still do cancel tests at pending send/receive). |
2440 | lua_pushlightuserdata( L2, CANCEL_TEST_KEY); // func [... args ...] k | 2448 | push_unique_key( L2, CANCEL_TEST_KEY); // func [... args ...] k |
2441 | lua_pushlightuserdata( L2, s); // func [... args ...] k s | 2449 | lua_pushlightuserdata( L2, s); // func [... args ...] k s |
2442 | lua_rawset( L2, LUA_REGISTRYINDEX); // func [... args ...] | 2450 | lua_rawset( L2, LUA_REGISTRYINDEX); // func [... args ...] |
2443 | 2451 | ||
@@ -2476,7 +2484,7 @@ LUAG_FUNC( thread_gc) | |||
2476 | 2484 | ||
2477 | // if there a gc callback? | 2485 | // if there a gc callback? |
2478 | lua_getuservalue( L, 1); // ud uservalue | 2486 | lua_getuservalue( L, 1); // ud uservalue |
2479 | lua_pushlightuserdata( L, GCCB_KEY); // ud uservalue __gc | 2487 | push_unique_key( L, GCCB_KEY); // ud uservalue __gc |
2480 | lua_rawget( L, -2); // ud uservalue gc_cb|nil | 2488 | lua_rawget( L, -2); // ud uservalue gc_cb|nil |
2481 | if( !lua_isnil( L, -1)) | 2489 | if( !lua_isnil( L, -1)) |
2482 | { | 2490 | { |
@@ -3209,7 +3217,7 @@ LUAG_FUNC( configure) | |||
3209 | lua_pushinteger(L, THREAD_PRIO_MAX); // settings M THREAD_PRIO_MAX | 3217 | lua_pushinteger(L, THREAD_PRIO_MAX); // settings M THREAD_PRIO_MAX |
3210 | lua_setfield( L, -2, "max_prio"); // settings M | 3218 | lua_setfield( L, -2, "max_prio"); // settings M |
3211 | 3219 | ||
3212 | lua_pushlightuserdata( L, CANCEL_ERROR); // settings M CANCEL_ERROR | 3220 | push_unique_key( L, CANCEL_ERROR); // settings M CANCEL_ERROR |
3213 | lua_setfield( L, -2, "cancel_error"); // settings M | 3221 | lua_setfield( L, -2, "cancel_error"); // settings M |
3214 | 3222 | ||
3215 | // we'll need this every time we transfer some C function from/to this state | 3223 | // we'll need this every time we transfer some C function from/to this state |
diff --git a/src/tools.c b/src/tools.c index 051e3cc..c13d80d 100644 --- a/src/tools.c +++ b/src/tools.c | |||
@@ -44,6 +44,7 @@ THE SOFTWARE. | |||
44 | #include "universe.h" | 44 | #include "universe.h" |
45 | #include "keeper.h" | 45 | #include "keeper.h" |
46 | #include "lanes.h" | 46 | #include "lanes.h" |
47 | #include "uniquekey.h" | ||
47 | 48 | ||
48 | // functions implemented in deep.c | 49 | // functions implemented in deep.c |
49 | extern luaG_IdFunction copydeep( Universe* U, lua_State* L, lua_State* L2, int index, LookupMode mode_); | 50 | extern luaG_IdFunction copydeep( Universe* U, lua_State* L, lua_State* L2, int index, LookupMode mode_); |
@@ -977,7 +978,7 @@ static bool_t lookup_table( lua_State* L2, lua_State* L, uint_t i, LookupMode mo | |||
977 | static bool_t push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i) | 978 | static bool_t push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i) |
978 | { | 979 | { |
979 | bool_t not_found_in_cache; // L2 | 980 | bool_t not_found_in_cache; // L2 |
980 | void* const p = (void*)lua_topointer( L, i); | 981 | DECLARE_CONST_UNIQUE_KEY( p, lua_topointer( L, i)); |
981 | 982 | ||
982 | ASSERT_L( L2_cache_i != 0); | 983 | ASSERT_L( L2_cache_i != 0); |
983 | STACK_GROW( L2, 3); | 984 | STACK_GROW( L2, 3); |
@@ -986,7 +987,7 @@ static bool_t push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L, | |||
986 | // We don't need to use the from state ('L') in ID since the life span | 987 | // We don't need to use the from state ('L') in ID since the life span |
987 | // is only for the duration of a copy (both states are locked). | 988 | // is only for the duration of a copy (both states are locked). |
988 | // push a light userdata uniquely representing the table | 989 | // push a light userdata uniquely representing the table |
989 | lua_pushlightuserdata( L2, p); // ... p | 990 | push_unique_key( L2, p); // ... p |
990 | 991 | ||
991 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1)); | 992 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1)); |
992 | 993 | ||
@@ -996,7 +997,7 @@ static bool_t push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L, | |||
996 | { | 997 | { |
997 | lua_pop( L2, 1); // ... | 998 | lua_pop( L2, 1); // ... |
998 | lua_newtable( L2); // ... {} | 999 | lua_newtable( L2); // ... {} |
999 | lua_pushlightuserdata( L2, p); // ... {} p | 1000 | push_unique_key( L2, p); // ... {} p |
1000 | lua_pushvalue( L2, -2); // ... {} p {} | 1001 | lua_pushvalue( L2, -2); // ... {} p {} |
1001 | lua_rawset( L2, L2_cache_i); // ... {} | 1002 | lua_rawset( L2, L2_cache_i); // ... {} |
1002 | } | 1003 | } |
diff --git a/src/uniquekey.h b/src/uniquekey.h new file mode 100644 index 0000000..c90d09a --- /dev/null +++ b/src/uniquekey.h | |||
@@ -0,0 +1,25 @@ | |||
1 | #if !defined __LANES_UNIQUEKEY_H__ | ||
2 | #define __LANES_UNIQUEKEY_H__ 1 | ||
3 | |||
4 | #include "lualib.h" | ||
5 | |||
6 | // Lua light userdata can hold a pointer. | ||
7 | struct s_UniqueKey | ||
8 | { | ||
9 | void* value; | ||
10 | }; | ||
11 | typedef struct s_UniqueKey UniqueKey; | ||
12 | |||
13 | #if defined(LUA_JITLIBNAME) && (defined(__x86_64__) || defined(_M_X64)) // building against LuaJIT headers, light userdata is restricted to 47 significant bits. | ||
14 | #define MAKE_UNIQUE_KEY( p_) ((void*)(p_) & 0x7fffffffffffull) | ||
15 | #else // LUA_JITLIBNAME | ||
16 | #define MAKE_UNIQUE_KEY( p_) ((void*) p_) | ||
17 | #endif // LUA_JITLIBNAME | ||
18 | |||
19 | #define DECLARE_UNIQUE_KEY( name_) UniqueKey name_ | ||
20 | #define DECLARE_CONST_UNIQUE_KEY( name_, p_) UniqueKey const name_ = { MAKE_UNIQUE_KEY( p_)} | ||
21 | |||
22 | #define push_unique_key( L, key_) lua_pushlightuserdata( L, key_.value) | ||
23 | #define equal_unique_key( L, i, key_) (lua_touserdata( L, i) == key_.value) | ||
24 | |||
25 | #endif // __LANES_UNIQUEKEY_H__ | ||
diff --git a/src/universe.c b/src/universe.c index 8bcdcfe..ca94930 100644 --- a/src/universe.c +++ b/src/universe.c | |||
@@ -31,9 +31,10 @@ THE SOFTWARE. | |||
31 | #include "universe.h" | 31 | #include "universe.h" |
32 | #include "compat.h" | 32 | #include "compat.h" |
33 | #include "macros_and_utils.h" | 33 | #include "macros_and_utils.h" |
34 | #include "uniquekey.h" | ||
34 | 35 | ||
35 | // crc64/we of string "UNIVERSE_REGKEY" generated at https://www.nitrxgen.net/hashgen/ | 36 | // crc64/we of string "UNIVERSE_REGKEY" generated at http://www.nitrxgen.net/hashgen/ |
36 | static void* const UNIVERSE_REGKEY = ((void*)0x9f877b2cf078f17f); | 37 | static DECLARE_UNIQUE_KEY( UNIVERSE_REGKEY, 0x9f877b2cf078f17f); |
37 | 38 | ||
38 | // ################################################################################################ | 39 | // ################################################################################################ |
39 | 40 | ||
@@ -41,7 +42,7 @@ Universe* universe_create( lua_State* L) | |||
41 | { | 42 | { |
42 | Universe* U = (Universe*) lua_newuserdata( L, sizeof(Universe)); // universe | 43 | Universe* U = (Universe*) lua_newuserdata( L, sizeof(Universe)); // universe |
43 | memset( U, 0, sizeof( Universe)); | 44 | memset( U, 0, sizeof( Universe)); |
44 | lua_pushlightuserdata( L, UNIVERSE_REGKEY); // universe UNIVERSE_REGKEY | 45 | push_unique_key( L, UNIVERSE_REGKEY); // universe UNIVERSE_REGKEY |
45 | lua_pushvalue( L, -2); // universe UNIVERSE_REGKEY universe | 46 | lua_pushvalue( L, -2); // universe UNIVERSE_REGKEY universe |
46 | lua_rawset( L, LUA_REGISTRYINDEX); // universe | 47 | lua_rawset( L, LUA_REGISTRYINDEX); // universe |
47 | return U; | 48 | return U; |
@@ -52,7 +53,7 @@ Universe* universe_create( lua_State* L) | |||
52 | void universe_store( lua_State* L, Universe* U) | 53 | void universe_store( lua_State* L, Universe* U) |
53 | { | 54 | { |
54 | STACK_CHECK( L); | 55 | STACK_CHECK( L); |
55 | lua_pushlightuserdata( L, UNIVERSE_REGKEY); | 56 | push_unique_key( L, UNIVERSE_REGKEY); |
56 | lua_pushlightuserdata( L, U); | 57 | lua_pushlightuserdata( L, U); |
57 | lua_rawset( L, LUA_REGISTRYINDEX); | 58 | lua_rawset( L, LUA_REGISTRYINDEX); |
58 | STACK_END( L, 0); | 59 | STACK_END( L, 0); |
@@ -65,7 +66,7 @@ Universe* universe_get( lua_State* L) | |||
65 | Universe* universe; | 66 | Universe* universe; |
66 | STACK_GROW( L, 2); | 67 | STACK_GROW( L, 2); |
67 | STACK_CHECK( L); | 68 | STACK_CHECK( L); |
68 | lua_pushlightuserdata( L, UNIVERSE_REGKEY); | 69 | push_unique_key( L, UNIVERSE_REGKEY); |
69 | lua_rawget( L, LUA_REGISTRYINDEX); | 70 | lua_rawget( L, LUA_REGISTRYINDEX); |
70 | universe = lua_touserdata( L, -1); // NULL if nil | 71 | universe = lua_touserdata( L, -1); // NULL if nil |
71 | lua_pop( L, 1); | 72 | lua_pop( L, 1); |