aboutsummaryrefslogtreecommitdiff
path: root/src/deep.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/deep.cpp')
-rw-r--r--src/deep.cpp501
1 files changed, 501 insertions, 0 deletions
diff --git a/src/deep.cpp b/src/deep.cpp
new file mode 100644
index 0000000..58da457
--- /dev/null
+++ b/src/deep.cpp
@@ -0,0 +1,501 @@
1/*
2 * DEEP.C Copyright (c) 2017, Benoit Germain
3 *
4 * Deep 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-17 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 <stdio.h>
36#include <assert.h>
37#include <string.h>
38#include <ctype.h>
39#include <stdlib.h>
40#if !defined(__APPLE__)
41#include <malloc.h>
42#endif
43
44#include "compat.h"
45#include "deep.h"
46#include "tools.h"
47#include "universe.h"
48#include "uniquekey.h"
49
50/*-- Metatable copying --*/
51
52/*---=== Deep userdata ===---*/
53
54/*
55* 'registry[REGKEY]' is a two-way lookup table for 'idfunc's and those type's
56* metatables:
57*
58* metatable -> idfunc
59* idfunc -> metatable
60*/
61// crc64/we of string "DEEP_LOOKUP_KEY" generated at http://www.nitrxgen.net/hashgen/
62static DECLARE_CONST_UNIQUE_KEY( DEEP_LOOKUP_KEY, 0x9fb9b4f3f633d83d);
63
64/*
65 * The deep proxy cache is a weak valued table listing all deep UD proxies indexed by the deep UD that they are proxying
66 * crc64/we of string "DEEP_PROXY_CACHE_KEY" generated at http://www.nitrxgen.net/hashgen/
67*/
68static DECLARE_CONST_UNIQUE_KEY( DEEP_PROXY_CACHE_KEY, 0x05773d6fc26be106);
69
70/*
71* Sets up [-1]<->[-2] two-way lookups, and ensures the lookup table exists.
72* Pops the both values off the stack.
73*/
74static void set_deep_lookup( lua_State* L)
75{
76 STACK_GROW( L, 3);
77 STACK_CHECK( L, 2); // a b
78 push_registry_subtable( L, DEEP_LOOKUP_KEY); // a b {}
79 STACK_MID( L, 3);
80 lua_insert( L, -3); // {} a b
81 lua_pushvalue( L, -1); // {} a b b
82 lua_pushvalue( L,-3); // {} a b b a
83 lua_rawset( L, -5); // {} a b
84 lua_rawset( L, -3); // {}
85 lua_pop( L, 1); //
86 STACK_END( L, 0);
87}
88
89/*
90* Pops the key (metatable or idfunc) off the stack, and replaces with the
91* deep lookup value (idfunc/metatable/nil).
92*/
93static void get_deep_lookup( lua_State* L)
94{
95 STACK_GROW( L, 1);
96 STACK_CHECK( L, 1); // a
97 REGISTRY_GET( L, DEEP_LOOKUP_KEY); // a {}
98 if( !lua_isnil( L, -1))
99 {
100 lua_insert( L, -2); // {} a
101 lua_rawget( L, -2); // {} b
102 }
103 lua_remove( L, -2); // a|b
104 STACK_END( L, 1);
105}
106
107/*
108* Return the registered ID function for 'index' (deep userdata proxy),
109* or NULL if 'index' is not a deep userdata proxy.
110*/
111static inline luaG_IdFunction get_idfunc( lua_State* L, int index, LookupMode mode_)
112{
113 // when looking inside a keeper, we are 100% sure the object is a deep userdata
114 if( mode_ == eLM_FromKeeper)
115 {
116 DeepPrelude** proxy = (DeepPrelude**) lua_touserdata( L, index);
117 // we can (and must) cast and fetch the internally stored idfunc
118 return (*proxy)->idfunc;
119 }
120 else
121 {
122 // essentially we are making sure that the metatable of the object we want to copy is stored in our metatable/idfunc database
123 // it is the only way to ensure that the userdata is indeed a deep userdata!
124 // of course, we could just trust the caller, but we won't
125 luaG_IdFunction ret;
126 STACK_GROW( L, 1);
127 STACK_CHECK( L, 0);
128
129 if( !lua_getmetatable( L, index)) // deep ... metatable?
130 {
131 return NULL; // no metatable: can't be a deep userdata object!
132 }
133
134 // replace metatable with the idfunc pointer, if it is actually a deep userdata
135 get_deep_lookup( L); // deep ... idfunc|nil
136
137 ret = (luaG_IdFunction) lua_touserdata( L, -1); // NULL if not a userdata
138 lua_pop( L, 1);
139 STACK_END( L, 0);
140 return ret;
141 }
142}
143
144
145void free_deep_prelude( lua_State* L, DeepPrelude* prelude_)
146{
147 // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup
148 lua_pushlightuserdata( L, prelude_);
149 ASSERT_L( prelude_->idfunc);
150 prelude_->idfunc( L, eDO_delete);
151}
152
153
154/*
155 * void= mt.__gc( proxy_ud )
156 *
157 * End of life for a proxy object; reduce the deep reference count and clean it up if reaches 0.
158 *
159 */
160static int deep_userdata_gc( lua_State* L)
161{
162 DeepPrelude** proxy = (DeepPrelude**) lua_touserdata( L, 1);
163 DeepPrelude* p = *proxy;
164 Universe* U = universe_get( L);
165 int v;
166
167 // can work without a universe if creating a deep userdata from some external C module when Lanes isn't loaded
168 // in that case, we are not multithreaded and locking isn't necessary anyway
169 if( U) MUTEX_LOCK( &U->deep_lock);
170 v = -- (p->refcount);
171 if (U) MUTEX_UNLOCK( &U->deep_lock);
172
173 if( v == 0)
174 {
175 // retrieve wrapped __gc
176 lua_pushvalue( L, lua_upvalueindex( 1)); // self __gc?
177 if( !lua_isnil( L, -1))
178 {
179 lua_insert( L, -2); // __gc self
180 lua_call( L, 1, 0); //
181 }
182 // 'idfunc' expects a clean stack to work on
183 lua_settop( L, 0);
184 free_deep_prelude( L, p);
185
186 // 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!
187 if ( lua_gettop( L) > 1)
188 {
189 luaL_error( L, "Bad idfunc(eDO_delete): should not push anything");
190 }
191 }
192 *proxy = NULL; // make sure we don't use it any more, just in case
193 return 0;
194}
195
196
197/*
198 * Push a proxy userdata on the stack.
199 * returns NULL if ok, else some error string related to bad idfunc behavior or module require problem
200 * (error cannot happen with mode_ == eLM_ToKeeper)
201 *
202 * Initializes necessary structures if it's the first time 'idfunc' is being
203 * used in this Lua state (metatable, registring it). Otherwise, increments the
204 * reference count.
205 */
206char const* push_deep_proxy( Universe* U, lua_State* L, DeepPrelude* prelude, int nuv_, LookupMode mode_)
207{
208 DeepPrelude** proxy;
209
210 // Check if a proxy already exists
211 push_registry_subtable_mode( L, DEEP_PROXY_CACHE_KEY, "v"); // DPC
212 lua_pushlightuserdata( L, prelude); // DPC deep
213 lua_rawget( L, -2); // DPC proxy
214 if ( !lua_isnil( L, -1))
215 {
216 lua_remove( L, -2); // proxy
217 return NULL;
218 }
219 else
220 {
221 lua_pop( L, 1); // DPC
222 }
223
224 // can work without a universe if creating a deep userdata from some external C module when Lanes isn't loaded
225 // in that case, we are not multithreaded and locking isn't necessary anyway
226 if( U) MUTEX_LOCK( &U->deep_lock);
227 ++ (prelude->refcount); // one more proxy pointing to this deep data
228 if( U) MUTEX_UNLOCK( &U->deep_lock);
229
230 STACK_GROW( L, 7);
231 STACK_CHECK( L, 0);
232
233 // a new full userdata, fitted with the specified number of uservalue slots (always 1 for Lua < 5.4)
234 proxy = (DeepPrelude**) lua_newuserdatauv( L, sizeof(DeepPrelude*), nuv_); // DPC proxy
235 ASSERT_L( proxy);
236 *proxy = prelude;
237
238 // Get/create metatable for 'idfunc' (in this state)
239 lua_pushlightuserdata( L, (void*)(ptrdiff_t)(prelude->idfunc)); // DPC proxy idfunc
240 get_deep_lookup( L); // DPC proxy metatable?
241
242 if( lua_isnil( L, -1)) // // No metatable yet.
243 {
244 char const* modname;
245 int oldtop = lua_gettop( L); // DPC proxy nil
246 lua_pop( L, 1); // DPC proxy
247 // 1 - make one and register it
248 if( mode_ != eLM_ToKeeper)
249 {
250 (void) prelude->idfunc( L, eDO_metatable); // DPC proxy metatable
251 if( lua_gettop( L) - oldtop != 0 || !lua_istable( L, -1))
252 {
253 lua_settop( L, oldtop); // DPC proxy X
254 lua_pop( L, 3); //
255 return "Bad idfunc(eOP_metatable): unexpected pushed value";
256 }
257 // if the metatable contains a __gc, we will call it from our own
258 lua_getfield( L, -1, "__gc"); // DPC proxy metatable __gc
259 }
260 else
261 {
262 // keepers need a minimal metatable that only contains our own __gc
263 lua_newtable( L); // DPC proxy metatable
264 lua_pushnil( L); // DPC proxy metatable nil
265 }
266 if( lua_isnil( L, -1))
267 {
268 // Add our own '__gc' method
269 lua_pop( L, 1); // DPC proxy metatable
270 lua_pushcfunction( L, deep_userdata_gc); // DPC proxy metatable deep_userdata_gc
271 }
272 else
273 {
274 // Add our own '__gc' method wrapping the original
275 lua_pushcclosure( L, deep_userdata_gc, 1); // DPC proxy metatable deep_userdata_gc
276 }
277 lua_setfield( L, -2, "__gc"); // DPC proxy metatable
278
279 // Memorize for later rounds
280 lua_pushvalue( L, -1); // DPC proxy metatable metatable
281 lua_pushlightuserdata( L, (void*)(ptrdiff_t)(prelude->idfunc)); // DPC proxy metatable metatable idfunc
282 set_deep_lookup( L); // DPC proxy metatable
283
284 // 2 - cause the target state to require the module that exported the idfunc
285 // this is needed because we must make sure the shared library is still loaded as long as we hold a pointer on the idfunc
286 {
287 int oldtop_module = lua_gettop( L);
288 modname = (char const*) prelude->idfunc( L, eDO_module); // DPC proxy metatable
289 // make sure the function pushed nothing on the stack!
290 if( lua_gettop( L) - oldtop_module != 0)
291 {
292 lua_pop( L, 3); //
293 return "Bad idfunc(eOP_module): should not push anything";
294 }
295 }
296 if( NULL != modname) // we actually got a module name
297 {
298 // L.registry._LOADED exists without having registered the 'package' library.
299 lua_getglobal( L, "require"); // DPC proxy metatable require()
300 // check that the module is already loaded (or being loaded, we are happy either way)
301 if( lua_isfunction( L, -1))
302 {
303 lua_pushstring( L, modname); // DPC proxy metatable require() "module"
304 lua_getfield( L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); // DPC proxy metatable require() "module" _R._LOADED
305 if( lua_istable( L, -1))
306 {
307 bool_t alreadyloaded;
308 lua_pushvalue( L, -2); // DPC proxy metatable require() "module" _R._LOADED "module"
309 lua_rawget( L, -2); // DPC proxy metatable require() "module" _R._LOADED module
310 alreadyloaded = lua_toboolean( L, -1);
311 if( !alreadyloaded) // not loaded
312 {
313 int require_result;
314 lua_pop( L, 2); // DPC proxy metatable require() "module"
315 // require "modname"
316 require_result = lua_pcall( L, 1, 0, 0); // DPC proxy metatable error?
317 if( require_result != LUA_OK)
318 {
319 // failed, return the error message
320 lua_pushfstring( L, "error while requiring '%s' identified by idfunc(eOP_module): ", modname);
321 lua_insert( L, -2); // DPC proxy metatable prefix error
322 lua_concat( L, 2); // DPC proxy metatable error
323 return lua_tostring( L, -1);
324 }
325 }
326 else // already loaded, we are happy
327 {
328 lua_pop( L, 4); // DPC proxy metatable
329 }
330 }
331 else // no L.registry._LOADED; can this ever happen?
332 {
333 lua_pop( L, 6); //
334 return "unexpected error while requiring a module identified by idfunc(eOP_module)";
335 }
336 }
337 else // a module name, but no require() function :-(
338 {
339 lua_pop( L, 4); //
340 return "lanes receiving deep userdata should register the 'package' library";
341 }
342 }
343 }
344 STACK_MID( L, 2); // DPC proxy metatable
345 ASSERT_L( lua_isuserdata( L, -2));
346 ASSERT_L( lua_istable( L, -1));
347 lua_setmetatable( L, -2); // DPC proxy
348
349 // If we're here, we obviously had to create a new proxy, so cache it.
350 lua_pushlightuserdata( L, prelude); // DPC proxy deep
351 lua_pushvalue( L, -2); // DPC proxy deep proxy
352 lua_rawset( L, -4); // DPC proxy
353 lua_remove( L, -2); // proxy
354 ASSERT_L( lua_isuserdata( L, -1));
355 STACK_END( L, 0);
356 return NULL;
357}
358
359/*
360* Create a deep userdata
361*
362* proxy_ud= deep_userdata( idfunc [, ...] )
363*
364* Creates a deep userdata entry of the type defined by 'idfunc'.
365* Parameters found on the stack are left as is passed on to the 'idfunc' "new" invocation.
366*
367* 'idfunc' must fulfill the following features:
368*
369* lightuserdata = idfunc( eDO_new [, ...] ) -- creates a new deep data instance
370* void = idfunc( eDO_delete, lightuserdata ) -- releases a deep data instance
371* tbl = idfunc( eDO_metatable ) -- gives metatable for userdata proxies
372*
373* Reference counting and true userdata proxying are taken care of for the
374* actual data type.
375*
376* Types using the deep userdata system (and only those!) can be passed between
377* separate Lua states via 'luaG_inter_move()'.
378*
379* Returns: 'proxy' userdata for accessing the deep data via 'luaG_todeep()'
380*/
381int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc, int nuv_)
382{
383 char const* errmsg;
384
385 STACK_GROW( L, 1);
386 STACK_CHECK( L, 0);
387 {
388 int const oldtop = lua_gettop( L);
389 DeepPrelude* prelude = (DeepPrelude*) idfunc( L, eDO_new);
390 if( prelude == NULL)
391 {
392 return luaL_error( L, "idfunc(eDO_new) failed to create deep userdata (out of memory)");
393 }
394 if( prelude->magic.value != DEEP_VERSION.value)
395 {
396 // just in case, don't leak the newly allocated deep userdata object
397 lua_pushlightuserdata( L, prelude);
398 idfunc( L, eDO_delete);
399 return luaL_error( L, "Bad idfunc(eDO_new): DEEP_VERSION is incorrect, rebuild your implementation with the latest deep implementation");
400 }
401 prelude->refcount = 0; // 'push_deep_proxy' will lift it to 1
402 prelude->idfunc = idfunc;
403
404 if( lua_gettop( L) - oldtop != 0)
405 {
406 // just in case, don't leak the newly allocated deep userdata object
407 lua_pushlightuserdata( L, prelude);
408 idfunc( L, eDO_delete);
409 return luaL_error( L, "Bad idfunc(eDO_new): should not push anything on the stack");
410 }
411 errmsg = push_deep_proxy( universe_get( L), L, prelude, nuv_, eLM_LaneBody); // proxy
412 if( errmsg != NULL)
413 {
414 return luaL_error( L, errmsg);
415 }
416 }
417 STACK_END( L, 1);
418 return 1;
419}
420
421
422/*
423* Access deep userdata through a proxy.
424*
425* Reference count is not changed, and access to the deep userdata is not
426* serialized. It is the module's responsibility to prevent conflicting usage.
427*/
428void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index)
429{
430 DeepPrelude** proxy;
431
432 STACK_CHECK( L, 0);
433 // ensure it is actually a deep userdata
434 if( get_idfunc( L, index, eLM_LaneBody) != idfunc)
435 {
436 return NULL; // no metatable, or wrong kind
437 }
438
439 proxy = (DeepPrelude**) lua_touserdata( L, index);
440 STACK_END( L, 0);
441
442 return *proxy;
443}
444
445
446/*
447 * Copy deep userdata between two separate Lua states (from L to L2)
448 *
449 * Returns:
450 * the id function of the copied value, or NULL for non-deep userdata
451 * (not copied)
452 */
453bool_t copydeep( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_)
454{
455 char const* errmsg;
456 luaG_IdFunction idfunc = get_idfunc( L, i, mode_);
457 int nuv = 0;
458
459 if( idfunc == NULL)
460 {
461 return FALSE; // not a deep userdata
462 }
463
464 STACK_CHECK( L, 0);
465 STACK_CHECK( L2, 0);
466
467 // extract all uservalues of the source
468 while( lua_getiuservalue( L, i, nuv + 1) != LUA_TNONE) // ... u [uv]* nil
469 {
470 ++ nuv;
471 }
472 // last call returned TNONE and pushed nil, that we don't need
473 lua_pop( L, 1); // ... u [uv]*
474 STACK_MID( L, nuv);
475
476 errmsg = push_deep_proxy( U, L2, *(DeepPrelude**) lua_touserdata( L, i), nuv, mode_); // u
477
478 // transfer all uservalues of the source in the destination
479 {
480 int const clone_i = lua_gettop( L2);
481 while( nuv)
482 {
483 inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_); // u uv
484 lua_pop( L, 1); // ... u [uv]*
485 // this pops the value from the stack
486 lua_setiuservalue( L2, clone_i, nuv); // u
487 -- nuv;
488 }
489 }
490
491 STACK_END( L2, 1);
492 STACK_END( L, 0);
493
494 if( errmsg != NULL)
495 {
496 // raise the error in the proper state (not the keeper)
497 lua_State* errL = (mode_ == eLM_FromKeeper) ? L2 : L;
498 luaL_error( errL, errmsg);
499 }
500 return TRUE;
501} \ No newline at end of file