aboutsummaryrefslogtreecommitdiff
path: root/src/deep.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/deep.c')
-rw-r--r--src/deep.c524
1 files changed, 524 insertions, 0 deletions
diff --git a/src/deep.c b/src/deep.c
new file mode 100644
index 0000000..52a6485
--- /dev/null
+++ b/src/deep.c
@@ -0,0 +1,524 @@
1/*
2 * DEEP.C Copyright (c) 2014, Benoit Germain
3 *
4 * Depp userdata support, separate in its own source file to help integration
5 * without enforcing a Lanes dependency
6 */
7
8/*
9===============================================================================
10
11Copyright (C) 2002-10 Asko Kauppi <akauppi@gmail.com>
12 2011-14 Benoit Germain <bnt.germain@gmail.com>
13
14Permission is hereby granted, free of charge, to any person obtaining a copy
15of this software and associated documentation files (the "Software"), to deal
16in the Software without restriction, including without limitation the rights
17to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18copies of the Software, and to permit persons to whom the Software is
19furnished to do so, subject to the following conditions:
20
21The above copyright notice and this permission notice shall be included in
22all copies or substantial portions of the Software.
23
24THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30THE SOFTWARE.
31
32===============================================================================
33*/
34
35#include "compat.h"
36#include "tools.h"
37#include "deep.h"
38
39#include <stdio.h>
40#include <string.h>
41#include <ctype.h>
42#include <stdlib.h>
43#if !defined(__APPLE__)
44#include <malloc.h>
45#endif
46
47/*-- Metatable copying --*/
48
49/*
50 * 'reg[ REG_MT_KNOWN ]'= {
51 * [ table ]= id_uint,
52 * ...
53 * [ id_uint ]= table,
54 * ...
55 * }
56 */
57
58/*
59* Does what the original 'push_registry_subtable' function did, but adds an optional mode argument to it
60*/
61void push_registry_subtable_mode( lua_State* L, void* key_, const char* mode_)
62{
63 STACK_GROW( L, 3);
64 STACK_CHECK( L);
65
66 lua_pushlightuserdata( L, key_); // key
67 lua_rawget( L, LUA_REGISTRYINDEX); // {}|nil
68
69 if( lua_isnil( L, -1))
70 {
71 lua_pop( L, 1); //
72 lua_newtable( L); // {}
73 lua_pushlightuserdata( L, key_); // {} key
74 lua_pushvalue( L, -2); // {} key {}
75
76 // _R[key_] = {}
77 lua_rawset( L, LUA_REGISTRYINDEX); // {}
78
79 // Set its metatable if requested
80 if( mode_)
81 {
82 lua_newtable( L); // {} mt
83 lua_pushliteral( L, "__mode"); // {} mt "__mode"
84 lua_pushstring( L, mode_); // {} mt "__mode" mode
85 lua_rawset( L, -3); // {} mt
86 lua_setmetatable( L, -2); // {}
87 }
88 }
89 STACK_END( L, 1);
90 ASSERT_L( lua_istable( L, -1));
91}
92
93
94/*
95* Push a registry subtable (keyed by unique 'key_') onto the stack.
96* If the subtable does not exist, it is created and chained.
97*/
98void push_registry_subtable( lua_State* L, void* key_)
99{
100 push_registry_subtable_mode( L, key_, NULL);
101}
102
103
104/*---=== Deep userdata ===---*/
105
106void luaG_pushdeepversion( lua_State* L) { (void) lua_pushliteral( L, "f248e77a-a84d-44b5-9ad0-96c05679b885");}
107
108
109
110/* The deep portion must be allocated separately of any Lua state's; it's
111* lifespan may be longer than that of the creating state.
112*/
113#define DEEP_MALLOC malloc
114#define DEEP_FREE free
115
116/*
117* 'registry[REGKEY]' is a two-way lookup table for 'idfunc's and those type's
118* metatables:
119*
120* metatable -> idfunc
121* idfunc -> metatable
122*/
123#define DEEP_LOOKUP_KEY ((void*)set_deep_lookup)
124 // any unique light userdata
125
126
127/*
128* The deep proxy cache is a weak valued table listing all deep UD proxies indexed by the deep UD that they are proxying
129*/
130#define DEEP_PROXY_CACHE_KEY ((void*)push_deep_proxy)
131
132/*
133* Sets up [-1]<->[-2] two-way lookups, and ensures the lookup table exists.
134* Pops the both values off the stack.
135*/
136static void set_deep_lookup( lua_State* L)
137{
138 STACK_GROW( L, 3);
139 STACK_CHECK( L); // a b
140 push_registry_subtable( L, DEEP_LOOKUP_KEY); // a b {}
141 STACK_MID( L, 1);
142 lua_insert( L, -3); // {} a b
143 lua_pushvalue( L, -1); // {} a b b
144 lua_pushvalue( L,-3); // {} a b b a
145 lua_rawset( L, -5); // {} a b
146 lua_rawset( L, -3); // {}
147 lua_pop( L, 1); //
148 STACK_END( L, -2);
149}
150
151/*
152* Pops the key (metatable or idfunc) off the stack, and replaces with the
153* deep lookup value (idfunc/metatable/nil).
154*/
155static void get_deep_lookup( lua_State* L)
156{
157 STACK_GROW( L, 1);
158 STACK_CHECK( L); // a
159 lua_pushlightuserdata( L, DEEP_LOOKUP_KEY); // a DLK
160 lua_rawget( L, LUA_REGISTRYINDEX); // a {}
161
162 if( !lua_isnil( L, -1))
163 {
164 lua_insert( L, -2); // {} a
165 lua_rawget( L, -2); // {} b
166 }
167 lua_remove( L, -2); // a|b
168 STACK_END( L, 0);
169}
170
171/*
172* Return the registered ID function for 'index' (deep userdata proxy),
173* or NULL if 'index' is not a deep userdata proxy.
174*/
175static inline luaG_IdFunction get_idfunc( lua_State* L, int index, enum eLookupMode mode_)
176{
177 // when looking inside a keeper, we are 100% sure the object is a deep userdata
178 if( mode_ == eLM_FromKeeper)
179 {
180 DEEP_PRELUDE** proxy = (DEEP_PRELUDE**) lua_touserdata( L, index);
181 // we can (and must) cast and fetch the internally stored idfunc
182 return (*proxy)->idfunc;
183 }
184 else
185 {
186 // essentially we are making sure that the metatable of the object we want to copy is stored in our metatable/idfunc database
187 // it is the only way to ensure that the userdata is indeed a deep userdata!
188 // of course, we could just trust the caller, but we won't
189 luaG_IdFunction ret;
190 STACK_GROW( L, 1);
191 STACK_CHECK( L);
192
193 if( !lua_getmetatable( L, index)) // deep ... metatable?
194 {
195 return NULL; // no metatable: can't be a deep userdata object!
196 }
197
198 // replace metatable with the idfunc pointer, if it is actually a deep userdata
199 get_deep_lookup( L); // deep ... idfunc|nil
200
201 ret = (luaG_IdFunction) lua_touserdata( L, -1); // NULL if not a userdata
202 lua_pop( L, 1);
203 STACK_END( L, 0);
204 return ret;
205 }
206}
207
208
209void free_deep_prelude( lua_State* L, DEEP_PRELUDE* prelude_)
210{
211 // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup
212 lua_pushlightuserdata( L, prelude_->deep);
213 ASSERT_L( prelude_->idfunc);
214 prelude_->idfunc( L, eDO_delete);
215 DEEP_FREE( (void*) prelude_);
216}
217
218
219/*
220* void= mt.__gc( proxy_ud )
221*
222* End of life for a proxy object; reduce the deep reference count and clean
223* it up if reaches 0.
224*/
225static int deep_userdata_gc( lua_State* L)
226{
227 DEEP_PRELUDE** proxy = (DEEP_PRELUDE**) lua_touserdata( L, 1);
228 DEEP_PRELUDE* p = *proxy;
229 struct s_Universe* U = get_universe( L);
230 int v;
231
232 *proxy = 0; // make sure we don't use it any more
233
234 MUTEX_LOCK( &U->deep_lock);
235 v = -- (p->refcount);
236 MUTEX_UNLOCK( &U->deep_lock);
237
238 if( v == 0)
239 {
240 // 'idfunc' expects a clean stack to work on
241 lua_settop( L, 0);
242 free_deep_prelude( L, p);
243
244 // top was set to 0, then userdata was pushed. "delete" might want to pop the userdata (we don't care), but should not push anything!
245 if ( lua_gettop( L) > 1)
246 {
247 luaL_error( L, "Bad idfunc(eDO_delete): should not push anything");
248 }
249 }
250 return 0;
251}
252
253
254/*
255 * Push a proxy userdata on the stack.
256 * returns NULL if ok, else some error string related to bad idfunc behavior or module require problem
257 * (error cannot happen with mode_ == eLM_ToKeeper)
258 *
259 * Initializes necessary structures if it's the first time 'idfunc' is being
260 * used in this Lua state (metatable, registring it). Otherwise, increments the
261 * reference count.
262 */
263char const* push_deep_proxy( struct s_Universe* U, lua_State* L, DEEP_PRELUDE* prelude, enum eLookupMode mode_)
264{
265 DEEP_PRELUDE** proxy;
266
267 // Check if a proxy already exists
268 push_registry_subtable_mode( L, DEEP_PROXY_CACHE_KEY, "v"); // DPC
269 lua_pushlightuserdata( L, prelude->deep); // DPC deep
270 lua_rawget( L, -2); // DPC proxy
271 if ( !lua_isnil( L, -1))
272 {
273 lua_remove( L, -2); // proxy
274 return NULL;
275 }
276 else
277 {
278 lua_pop( L, 1); // DPC
279 }
280
281 MUTEX_LOCK( &U->deep_lock);
282 ++ (prelude->refcount); // one more proxy pointing to this deep data
283 MUTEX_UNLOCK( &U->deep_lock);
284
285 STACK_GROW( L, 7);
286 STACK_CHECK( L);
287
288 proxy = lua_newuserdata( L, sizeof( DEEP_PRELUDE*)); // DPC proxy
289 ASSERT_L( proxy);
290 *proxy = prelude;
291
292 // Get/create metatable for 'idfunc' (in this state)
293 lua_pushlightuserdata( L, prelude->idfunc); // DPC proxy idfunc
294 get_deep_lookup( L); // DPC proxy metatable?
295
296 if( lua_isnil( L, -1)) // // No metatable yet.
297 {
298 char const* modname;
299 int oldtop = lua_gettop( L); // DPC proxy nil
300 lua_pop( L, 1); // DPC proxy
301 // 1 - make one and register it
302 if( mode_ != eLM_ToKeeper)
303 {
304 prelude->idfunc( L, eDO_metatable); // DPC proxy metatable deepversion
305 if( lua_gettop( L) - oldtop != 1 || !lua_istable( L, -2) || !lua_isstring( L, -1))
306 {
307 lua_settop( L, oldtop); // DPC proxy X
308 lua_pop( L, 3); //
309 return "Bad idfunc(eOP_metatable): unexpected pushed value";
310 }
311 luaG_pushdeepversion( L); // DPC proxy metatable deepversion deepversion
312 if( !lua_equal( L, -1, -2))
313 {
314 lua_pop( L, 5); //
315 return "Bad idfunc(eOP_metatable): mismatched deep version";
316 }
317 lua_pop( L, 2); // DPC proxy metatable
318 // make sure the idfunc didn't export __gc, as we will store our own
319 lua_getfield( L, -1, "__gc"); // DPC proxy metatable __gc
320 if( !lua_isnil( L, -1))
321 {
322 lua_pop( L, 4); //
323 return "idfunc-created metatable shouldn't contain __gc";
324 }
325 lua_pop( L, 1); // DPC proxy metatable
326 }
327 else
328 {
329 // keepers need a minimal metatable that only contains __gc
330 lua_newtable( L); // DPC proxy metatable
331 }
332 // Add our own '__gc' method
333 lua_pushcfunction( L, deep_userdata_gc); // DPC proxy metatable __gc
334 lua_setfield( L, -2, "__gc"); // DPC proxy metatable
335
336 // Memorize for later rounds
337 lua_pushvalue( L, -1); // DPC proxy metatable metatable
338 lua_pushlightuserdata( L, prelude->idfunc); // DPC proxy metatable metatable idfunc
339 set_deep_lookup( L); // DPC proxy metatable
340
341 // 2 - cause the target state to require the module that exported the idfunc
342 // this is needed because we must make sure the shared library is still loaded as long as we hold a pointer on the idfunc
343 {
344 int oldtop = lua_gettop( L);
345 modname = (char const*) prelude->idfunc( L, eDO_module); // DPC proxy metatable
346 // make sure the function pushed nothing on the stack!
347 if( lua_gettop( L) - oldtop != 0)
348 {
349 lua_pop( L, 3); //
350 return "Bad idfunc(eOP_module): should not push anything";
351 }
352 }
353 if( modname) // we actually got a module name
354 {
355 // somehow, L.registry._LOADED can exist without having registered the 'package' library.
356 lua_getglobal( L, "require"); // DPC proxy metatable require()
357 // check that the module is already loaded (or being loaded, we are happy either way)
358 if( lua_isfunction( L, -1))
359 {
360 lua_pushstring( L, modname); // DPC proxy metatable require() "module"
361 lua_getfield( L, LUA_REGISTRYINDEX, "_LOADED"); // DPC proxy metatable require() "module" _R._LOADED
362 if( lua_istable( L, -1))
363 {
364 bool_t alreadyloaded;
365 lua_pushvalue( L, -2); // DPC proxy metatable require() "module" _R._LOADED "module"
366 lua_rawget( L, -2); // DPC proxy metatable require() "module" _R._LOADED module
367 alreadyloaded = lua_toboolean( L, -1);
368 if( !alreadyloaded) // not loaded
369 {
370 int require_result;
371 lua_pop( L, 2); // DPC proxy metatable require() "module"
372 // require "modname"
373 require_result = lua_pcall( L, 1, 0, 0); // DPC proxy metatable error?
374 if( require_result != LUA_OK)
375 {
376 // failed, return the error message
377 lua_pushfstring( L, "error while requiring '%s' identified by idfunc(eOP_module): ", modname);
378 lua_insert( L, -2); // DPC proxy metatable prefix error
379 lua_concat( L, 2); // DPC proxy metatable error
380 return lua_tostring( L, -1);
381 }
382 }
383 else // already loaded, we are happy
384 {
385 lua_pop( L, 4); // DPC proxy metatable
386 }
387 }
388 else // no L.registry._LOADED; can this ever happen?
389 {
390 lua_pop( L, 6); //
391 return "unexpected error while requiring a module identified by idfunc(eOP_module)";
392 }
393 }
394 else // a module name, but no require() function :-(
395 {
396 lua_pop( L, 4); //
397 return "lanes receiving deep userdata should register the 'package' library";
398 }
399 }
400 }
401 STACK_MID( L, 2); // DPC proxy metatable
402 ASSERT_L( lua_isuserdata( L, -2));
403 ASSERT_L( lua_istable( L, -1));
404 lua_setmetatable( L, -2); // DPC proxy
405
406 // If we're here, we obviously had to create a new proxy, so cache it.
407 lua_pushlightuserdata( L, (*proxy)->deep); // DPC proxy deep
408 lua_pushvalue( L, -2); // DPC proxy deep proxy
409 lua_rawset( L, -4); // DPC proxy
410 lua_remove( L, -2); // proxy
411 ASSERT_L( lua_isuserdata( L, -1));
412 STACK_END( L, 0);
413 return NULL;
414}
415
416
417/*
418* Create a deep userdata
419*
420* proxy_ud= deep_userdata( idfunc [, ...] )
421*
422* Creates a deep userdata entry of the type defined by 'idfunc'.
423* Other parameters are passed on to the 'idfunc' "new" invocation.
424*
425* 'idfunc' must fulfill the following features:
426*
427* lightuserdata = idfunc( eDO_new [, ...] ) -- creates a new deep data instance
428* void = idfunc( eDO_delete, lightuserdata ) -- releases a deep data instance
429* tbl = idfunc( eDO_metatable ) -- gives metatable for userdata proxies
430*
431* Reference counting and true userdata proxying are taken care of for the
432* actual data type.
433*
434* Types using the deep userdata system (and only those!) can be passed between
435* separate Lua states via 'luaG_inter_move()'.
436*
437* Returns: 'proxy' userdata for accessing the deep data via 'luaG_todeep()'
438*/
439int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc)
440{
441 char const* errmsg;
442 DEEP_PRELUDE* prelude = DEEP_MALLOC( sizeof(DEEP_PRELUDE));
443 if( prelude == NULL)
444 {
445 return luaL_error( L, "couldn't not allocate deep prelude: out of memory");
446 }
447
448 prelude->refcount = 0; // 'push_deep_proxy' will lift it to 1
449 prelude->idfunc = idfunc;
450
451 STACK_GROW( L, 1);
452 STACK_CHECK( L);
453 {
454 int oldtop = lua_gettop( L);
455 prelude->deep = idfunc( L, eDO_new);
456 if( prelude->deep == NULL)
457 {
458 luaL_error( L, "idfunc(eDO_new) failed to create deep userdata (out of memory)");
459 }
460
461 if( lua_gettop( L) - oldtop != 0)
462 {
463 luaL_error( L, "Bad idfunc(eDO_new): should not push anything on the stack");
464 }
465 }
466 errmsg = push_deep_proxy( get_universe( L), L, prelude, eLM_LaneBody); // proxy
467 if( errmsg != NULL)
468 {
469 luaL_error( L, errmsg);
470 }
471 STACK_END( L, 1);
472 return 1;
473}
474
475
476/*
477* Access deep userdata through a proxy.
478*
479* Reference count is not changed, and access to the deep userdata is not
480* serialized. It is the module's responsibility to prevent conflicting usage.
481*/
482void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index)
483{
484 DEEP_PRELUDE** proxy;
485
486 STACK_CHECK( L);
487 // ensure it is actually a deep userdata
488 if( get_idfunc( L, index, eLM_LaneBody) != idfunc)
489 {
490 return NULL; // no metatable, or wrong kind
491 }
492
493 proxy = (DEEP_PRELUDE**) lua_touserdata( L, index);
494 STACK_END( L, 0);
495
496 return (*proxy)->deep;
497}
498
499
500/*
501 * Copy deep userdata between two separate Lua states (from L to L2)
502 *
503 * Returns:
504 * the id function of the copied value, or NULL for non-deep userdata
505 * (not copied)
506 */
507luaG_IdFunction copydeep( struct s_Universe* U, lua_State* L, lua_State* L2, int index, enum eLookupMode mode_)
508{
509 char const* errmsg;
510 luaG_IdFunction idfunc = get_idfunc( L, index, mode_);
511 if( idfunc == NULL)
512 {
513 return NULL; // not a deep userdata
514 }
515
516 errmsg = push_deep_proxy( U, L2, *(DEEP_PRELUDE**) lua_touserdata( L, index), mode_);
517 if( errmsg != NULL)
518 {
519 // raise the error in the proper state (not the keeper)
520 lua_State* errL = (mode_ == eLM_FromKeeper) ? L2 : L;
521 luaL_error( errL, errmsg);
522 }
523 return idfunc;
524}