diff options
| author | Benoit Germain <bnt.germain@gmail.com> | 2021-06-26 18:25:53 +0200 |
|---|---|---|
| committer | Benoit Germain <bnt.germain@gmail.com> | 2021-06-26 18:25:53 +0200 |
| commit | 909470be9f7ec1dd2d09ae1a371d69c9c652e957 (patch) | |
| tree | cd16d9976e90c1d8a61d8ee3423d4af1671bafc0 /src | |
| parent | 050e14dd7fa04e2262ae6b1cc984d76c4149b664 (diff) | |
| download | lanes-909470be9f7ec1dd2d09ae1a371d69c9c652e957.tar.gz lanes-909470be9f7ec1dd2d09ae1a371d69c9c652e957.tar.bz2 lanes-909470be9f7ec1dd2d09ae1a371d69c9c652e957.zip | |
fix stack overflow when transfering a clonable userdata referencing itself through a uservalue
Diffstat (limited to 'src')
| -rw-r--r-- | src/deep.c | 70 | ||||
| -rw-r--r-- | src/tools.c | 254 | ||||
| -rw-r--r-- | src/tools.h | 13 |
3 files changed, 199 insertions, 138 deletions
| @@ -49,59 +49,6 @@ THE SOFTWARE. | |||
| 49 | 49 | ||
| 50 | /*-- Metatable copying --*/ | 50 | /*-- Metatable copying --*/ |
| 51 | 51 | ||
| 52 | /* | ||
| 53 | * 'reg[ REG_MT_KNOWN ]'= { | ||
| 54 | * [ table ]= id_uint, | ||
| 55 | * ... | ||
| 56 | * [ id_uint ]= table, | ||
| 57 | * ... | ||
| 58 | * } | ||
| 59 | */ | ||
| 60 | |||
| 61 | /* | ||
| 62 | * Does what the original 'push_registry_subtable' function did, but adds an optional mode argument to it | ||
| 63 | */ | ||
| 64 | static void push_registry_subtable_mode( lua_State* L, UniqueKey key_, const char* mode_) | ||
| 65 | { | ||
| 66 | STACK_GROW( L, 3); | ||
| 67 | STACK_CHECK( L, 0); | ||
| 68 | |||
| 69 | REGISTRY_GET( L, key_); // {}|nil | ||
| 70 | STACK_MID( L, 1); | ||
| 71 | |||
| 72 | if( lua_isnil( L, -1)) | ||
| 73 | { | ||
| 74 | lua_pop( L, 1); // | ||
| 75 | lua_newtable( L); // {} | ||
| 76 | // _R[key_] = {} | ||
| 77 | REGISTRY_SET( L, key_, lua_pushvalue( L, -2)); // {} | ||
| 78 | STACK_MID( L, 1); | ||
| 79 | |||
| 80 | // Set its metatable if requested | ||
| 81 | if( mode_) | ||
| 82 | { | ||
| 83 | lua_newtable( L); // {} mt | ||
| 84 | lua_pushliteral( L, "__mode"); // {} mt "__mode" | ||
| 85 | lua_pushstring( L, mode_); // {} mt "__mode" mode | ||
| 86 | lua_rawset( L, -3); // {} mt | ||
| 87 | lua_setmetatable( L, -2); // {} | ||
| 88 | } | ||
| 89 | } | ||
| 90 | STACK_END( L, 1); | ||
| 91 | ASSERT_L( lua_istable( L, -1)); | ||
| 92 | } | ||
| 93 | |||
| 94 | |||
| 95 | /* | ||
| 96 | * Push a registry subtable (keyed by unique 'key_') onto the stack. | ||
| 97 | * If the subtable does not exist, it is created and chained. | ||
| 98 | */ | ||
| 99 | void push_registry_subtable( lua_State* L, UniqueKey key_) | ||
| 100 | { | ||
| 101 | push_registry_subtable_mode( L, key_, NULL); | ||
| 102 | } | ||
| 103 | |||
| 104 | |||
| 105 | /*---=== Deep userdata ===---*/ | 52 | /*---=== Deep userdata ===---*/ |
| 106 | 53 | ||
| 107 | /* | 54 | /* |
| @@ -503,10 +450,10 @@ void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index) | |||
| 503 | * the id function of the copied value, or NULL for non-deep userdata | 450 | * the id function of the copied value, or NULL for non-deep userdata |
| 504 | * (not copied) | 451 | * (not copied) |
| 505 | */ | 452 | */ |
| 506 | bool_t copydeep( Universe* U, lua_State* L, lua_State* L2, int index, LookupMode mode_) | 453 | bool_t copydeep( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_) |
| 507 | { | 454 | { |
| 508 | char const* errmsg; | 455 | char const* errmsg; |
| 509 | luaG_IdFunction idfunc = get_idfunc( L, index, mode_); | 456 | luaG_IdFunction idfunc = get_idfunc( L, i, mode_); |
| 510 | int nuv = 0; | 457 | int nuv = 0; |
| 511 | 458 | ||
| 512 | if( idfunc == NULL) | 459 | if( idfunc == NULL) |
| @@ -518,24 +465,25 @@ bool_t copydeep( Universe* U, lua_State* L, lua_State* L2, int index, LookupMode | |||
| 518 | STACK_CHECK( L2, 0); | 465 | STACK_CHECK( L2, 0); |
| 519 | 466 | ||
| 520 | // extract all uservalues of the source | 467 | // extract all uservalues of the source |
| 521 | while( lua_getiuservalue( L, index, nuv + 1) != LUA_TNONE) // ... u [uv]+ nil | 468 | while( lua_getiuservalue( L, i, nuv + 1) != LUA_TNONE) // ... u [uv]* nil |
| 522 | { | 469 | { |
| 523 | ++ nuv; | 470 | ++ nuv; |
| 524 | } | 471 | } |
| 525 | // last call returned TNONE and pushed nil, that we don't need | 472 | // last call returned TNONE and pushed nil, that we don't need |
| 526 | lua_pop( L, 1); // ... u [uv]+ | 473 | lua_pop( L, 1); // ... u [uv]* |
| 527 | STACK_MID( L, nuv); | 474 | STACK_MID( L, nuv); |
| 528 | 475 | ||
| 529 | errmsg = push_deep_proxy( U, L2, *(DeepPrelude**) lua_touserdata( L, index), nuv, mode_); // u | 476 | errmsg = push_deep_proxy( U, L2, *(DeepPrelude**) lua_touserdata( L, i), nuv, mode_); // u |
| 530 | 477 | ||
| 531 | // transfer all uservalues of the source in the destination | 478 | // transfer all uservalues of the source in the destination |
| 532 | { | 479 | { |
| 533 | int const clone_i = lua_gettop( L2); | 480 | int const clone_i = lua_gettop( L2); |
| 534 | luaG_inter_move( U, L, L2, nuv, mode_); // ... u // u [uv]+ | 481 | while( nuv) |
| 535 | while( nuv > 0) | ||
| 536 | { | 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]* | ||
| 537 | // this pops the value from the stack | 485 | // this pops the value from the stack |
| 538 | lua_setiuservalue( L2, clone_i, nuv); // ... u // u | 486 | lua_setiuservalue( L2, clone_i, nuv); // u |
| 539 | -- nuv; | 487 | -- nuv; |
| 540 | } | 488 | } |
| 541 | } | 489 | } |
diff --git a/src/tools.c b/src/tools.c index a0ba20e..18015c1 100644 --- a/src/tools.c +++ b/src/tools.c | |||
| @@ -48,12 +48,60 @@ THE SOFTWARE. | |||
| 48 | #include "uniquekey.h" | 48 | #include "uniquekey.h" |
| 49 | 49 | ||
| 50 | // functions implemented in deep.c | 50 | // functions implemented in deep.c |
| 51 | extern bool_t copydeep( Universe* U, lua_State* L, lua_State* L2, int index, LookupMode mode_); | 51 | extern bool_t copydeep( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_); |
| 52 | extern void push_registry_subtable( lua_State* L, UniqueKey key_); | 52 | extern void push_registry_subtable( lua_State* L, UniqueKey key_); |
| 53 | 53 | ||
| 54 | DEBUGSPEW_CODE( char const* debugspew_indent = "----+----!----+----!----+----!----+----!----+----!----+----!----+----!----+"); | 54 | DEBUGSPEW_CODE( char const* debugspew_indent = "----+----!----+----!----+----!----+----!----+----!----+----!----+----!----+"); |
| 55 | 55 | ||
| 56 | 56 | ||
| 57 | // ################################################################################################ | ||
| 58 | |||
| 59 | /* | ||
| 60 | * Does what the original 'push_registry_subtable' function did, but adds an optional mode argument to it | ||
| 61 | */ | ||
| 62 | void push_registry_subtable_mode( lua_State* L, UniqueKey key_, const char* mode_) | ||
| 63 | { | ||
| 64 | STACK_GROW( L, 3); | ||
| 65 | STACK_CHECK( L, 0); | ||
| 66 | |||
| 67 | REGISTRY_GET( L, key_); // {}|nil | ||
| 68 | STACK_MID( L, 1); | ||
| 69 | |||
| 70 | if( lua_isnil( L, -1)) | ||
| 71 | { | ||
| 72 | lua_pop( L, 1); // | ||
| 73 | lua_newtable( L); // {} | ||
| 74 | // _R[key_] = {} | ||
| 75 | REGISTRY_SET( L, key_, lua_pushvalue( L, -2)); // {} | ||
| 76 | STACK_MID( L, 1); | ||
| 77 | |||
| 78 | // Set its metatable if requested | ||
| 79 | if( mode_) | ||
| 80 | { | ||
| 81 | lua_newtable( L); // {} mt | ||
| 82 | lua_pushliteral( L, "__mode"); // {} mt "__mode" | ||
| 83 | lua_pushstring( L, mode_); // {} mt "__mode" mode | ||
| 84 | lua_rawset( L, -3); // {} mt | ||
| 85 | lua_setmetatable( L, -2); // {} | ||
| 86 | } | ||
| 87 | } | ||
| 88 | STACK_END( L, 1); | ||
| 89 | ASSERT_L( lua_istable( L, -1)); | ||
| 90 | } | ||
| 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 | */ | ||
| 98 | void push_registry_subtable( lua_State* L, UniqueKey key_) | ||
| 99 | { | ||
| 100 | push_registry_subtable_mode( L, key_, NULL); | ||
| 101 | } | ||
| 102 | |||
| 103 | // ################################################################################################ | ||
| 104 | |||
| 57 | /*---=== luaG_dump ===---*/ | 105 | /*---=== luaG_dump ===---*/ |
| 58 | #ifdef _DEBUG | 106 | #ifdef _DEBUG |
| 59 | void luaG_dump( lua_State* L) | 107 | void luaG_dump( lua_State* L) |
| @@ -695,7 +743,7 @@ static bool_t lookup_table( lua_State* L2, lua_State* L, uint_t i, LookupMode mo | |||
| 695 | return FALSE; | 743 | return FALSE; |
| 696 | } | 744 | } |
| 697 | // push the equivalent table in the destination's stack, retrieved from the lookup table | 745 | // push the equivalent table in the destination's stack, retrieved from the lookup table |
| 698 | STACK_CHECK( L2, 0); // L // L2 | 746 | STACK_CHECK( L2, 0); // L // L2 |
| 699 | STACK_GROW( L2, 3); // up to 3 slots are necessary on error | 747 | STACK_GROW( L2, 3); // up to 3 slots are necessary on error |
| 700 | switch( mode_) | 748 | switch( mode_) |
| 701 | { | 749 | { |
| @@ -1066,13 +1114,6 @@ static void lookup_native_func( lua_State* L2, lua_State* L, uint_t i, LookupMod | |||
| 1066 | * Copy a function over, which has not been found in the cache. | 1114 | * Copy a function over, which has not been found in the cache. |
| 1067 | * L2 has the cache key for this function at the top of the stack | 1115 | * L2 has the cache key for this function at the top of the stack |
| 1068 | */ | 1116 | */ |
| 1069 | enum e_vt | ||
| 1070 | { | ||
| 1071 | VT_NORMAL, | ||
| 1072 | VT_KEY, | ||
| 1073 | VT_METATABLE | ||
| 1074 | }; | ||
| 1075 | static bool_t inter_copy_one( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt value_type, LookupMode mode_, char const* upName_); | ||
| 1076 | 1117 | ||
| 1077 | #if USE_DEBUG_SPEW | 1118 | #if USE_DEBUG_SPEW |
| 1078 | static char const* lua_type_names[] = | 1119 | static char const* lua_type_names[] = |
| @@ -1412,17 +1453,41 @@ static void inter_copy_keyvaluepair( Universe* U, lua_State* L2, uint_t L2_cache | |||
| 1412 | } | 1453 | } |
| 1413 | } | 1454 | } |
| 1414 | 1455 | ||
| 1456 | /* | ||
| 1457 | * The clone cache is a weak valued table listing all clones, indexed by their userdatapointer | ||
| 1458 | * fnv164 of string "CLONABLES_CACHE_KEY" generated at https://www.pelock.com/products/hash-calculator | ||
| 1459 | */ | ||
| 1460 | static DECLARE_CONST_UNIQUE_KEY( CLONABLES_CACHE_KEY, 0xD04EE018B3DEE8F5); | ||
| 1461 | |||
| 1415 | static bool_t copyclone( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_) | 1462 | static bool_t copyclone( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_) |
| 1416 | { | 1463 | { |
| 1464 | void* const source = lua_touserdata( L, i); | ||
| 1465 | |||
| 1417 | STACK_CHECK( L, 0); | 1466 | STACK_CHECK( L, 0); |
| 1418 | STACK_CHECK( L2, 0); | 1467 | STACK_CHECK( L2, 0); |
| 1419 | 1468 | ||
| 1469 | // Check if the source was already cloned during this copy | ||
| 1470 | lua_pushlightuserdata( L2, source); // ... source | ||
| 1471 | lua_rawget( L2, L2_cache_i); // ... clone? | ||
| 1472 | if ( !lua_isnil( L2, -1)) | ||
| 1473 | { | ||
| 1474 | STACK_MID( L2, 1); | ||
| 1475 | return TRUE; | ||
| 1476 | } | ||
| 1477 | else | ||
| 1478 | { | ||
| 1479 | lua_pop( L2, 1); // ... | ||
| 1480 | } | ||
| 1481 | STACK_MID( L2, 0); | ||
| 1482 | |||
| 1483 | // no metatable? -> not clonable | ||
| 1420 | if( !lua_getmetatable( L, i)) // ... mt? | 1484 | if( !lua_getmetatable( L, i)) // ... mt? |
| 1421 | { | 1485 | { |
| 1422 | STACK_MID( L, 0); | 1486 | STACK_MID( L, 0); |
| 1423 | return FALSE; | 1487 | return FALSE; |
| 1424 | } | 1488 | } |
| 1425 | 1489 | ||
| 1490 | // no __lanesclone? -> not clonable | ||
| 1426 | lua_getfield( L, -1, "__lanesclone"); // ... mt __lanesclone? | 1491 | lua_getfield( L, -1, "__lanesclone"); // ... mt __lanesclone? |
| 1427 | if( lua_isnil( L, -1)) | 1492 | if( lua_isnil( L, -1)) |
| 1428 | { | 1493 | { |
| @@ -1432,8 +1497,8 @@ static bool_t copyclone( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_Stat | |||
| 1432 | } | 1497 | } |
| 1433 | 1498 | ||
| 1434 | { | 1499 | { |
| 1500 | int const mt = lua_absindex( L, -2); | ||
| 1435 | size_t userdata_size = 0; | 1501 | size_t userdata_size = 0; |
| 1436 | void* const source = lua_touserdata( L, i); | ||
| 1437 | void* clone = NULL; | 1502 | void* clone = NULL; |
| 1438 | lua_pushvalue( L, -1); // ... mt __lanesclone __lanesclone | 1503 | lua_pushvalue( L, -1); // ... mt __lanesclone __lanesclone |
| 1439 | // call the cloning function with 1 argument, should return the number of bytes to allocate for the clone | 1504 | // call the cloning function with 1 argument, should return the number of bytes to allocate for the clone |
| @@ -1443,69 +1508,77 @@ static bool_t copyclone( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_Stat | |||
| 1443 | userdata_size = (size_t) lua_tointeger( L, -1); // ... mt __lanesclone size | 1508 | userdata_size = (size_t) lua_tointeger( L, -1); // ... mt __lanesclone size |
| 1444 | lua_pop( L, 1); // ... mt __lanesclone | 1509 | lua_pop( L, 1); // ... mt __lanesclone |
| 1445 | // we need to copy over the uservalues of the userdata as well | 1510 | // we need to copy over the uservalues of the userdata as well |
| 1446 | lua_pushnil( L2); // ... nil | ||
| 1447 | { | 1511 | { |
| 1448 | int const clone_i = lua_gettop( L2); | 1512 | // extract all the uservalues, but don't transfer them yet |
| 1449 | int uvi = 0; | 1513 | int uvi = 0; |
| 1450 | while( lua_getiuservalue( L, i, uvi + 1) != LUA_TNONE) // ... mt __lanesclone uv | 1514 | while( lua_getiuservalue( L, i, uvi + 1) != LUA_TNONE) // ... mt __lanesclone [uv]+ nil |
| 1451 | { | 1515 | { |
| 1452 | luaG_inter_move( U, L, L2, 1, mode_); // ... mt __lanesclone // ... nil [uv]+ | ||
| 1453 | ++ uvi; | 1516 | ++ uvi; |
| 1454 | } | 1517 | } |
| 1455 | // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now | 1518 | // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now |
| 1456 | lua_pop( L, 1); // ... mt __lanesclone | 1519 | lua_pop( L, 1); // ... mt __lanesclone [uv]+ |
| 1457 | // create the clone userdata with the required number of uservalue slots | 1520 | // create the clone userdata with the required number of uservalue slots |
| 1458 | clone = lua_newuserdatauv( L2, userdata_size, uvi); // ... nil [uv]+ u | 1521 | clone = lua_newuserdatauv( L2, userdata_size, uvi); // ... u |
| 1459 | lua_replace( L2, clone_i); // ... u [uv]+ | 1522 | // copy the metatable in the target state, and give it to the clone we put there |
| 1523 | if( inter_copy_one( U, L2, L2_cache_i, L, mt, VT_NORMAL, mode_, upName_)) // ... u mt|sentinel | ||
| 1524 | { | ||
| 1525 | if( eLM_ToKeeper == mode_) // ... u sentinel | ||
| 1526 | { | ||
| 1527 | ASSERT_L( lua_tocfunction( L2, -1) == table_lookup_sentinel); | ||
| 1528 | // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn | ||
| 1529 | lua_getupvalue( L2, -1, 1); // ... u sentinel fqn | ||
| 1530 | lua_remove( L2, -2); // ... u fqn | ||
| 1531 | lua_insert( L2, -2); // ... fqn u | ||
| 1532 | lua_pushcclosure( L2, userdata_clone_sentinel, 2); // ... userdata_clone_sentinel | ||
| 1533 | } | ||
| 1534 | else // from keeper or direct // ... u mt | ||
| 1535 | { | ||
| 1536 | ASSERT_L( lua_istable( L2, -1)); | ||
| 1537 | lua_setmetatable( L2, -2); // ... u | ||
| 1538 | } | ||
| 1539 | STACK_MID( L2, 1); | ||
| 1540 | } | ||
| 1541 | else | ||
| 1542 | { | ||
| 1543 | (void) luaL_error( L, "Error copying a metatable"); | ||
| 1544 | } | ||
| 1545 | // first, add the entry in the cache (at this point it is either the actual userdata or the keeper sentinel | ||
| 1546 | lua_pushlightuserdata( L2, source); // ... u source | ||
| 1547 | lua_pushvalue( L2, -2); // ... u source u | ||
| 1548 | lua_rawset( L2, L2_cache_i); // ... u | ||
| 1549 | // make sure we have the userdata now | ||
| 1550 | if( eLM_ToKeeper == mode_) // ... userdata_clone_sentinel | ||
| 1551 | { | ||
| 1552 | lua_getupvalue( L2, -1, 2); // ... userdata_clone_sentinel u | ||
| 1553 | } | ||
| 1460 | // assign uservalues | 1554 | // assign uservalues |
| 1461 | while( uvi > 0) | 1555 | while( uvi > 0) |
| 1462 | { | 1556 | { |
| 1557 | inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_); // ... u uv | ||
| 1558 | lua_pop( L, 1); // ... mt __lanesclone [uv]* | ||
| 1463 | // this pops the value from the stack | 1559 | // this pops the value from the stack |
| 1464 | lua_setiuservalue( L2, clone_i, uvi); // ... u [uv]+ | 1560 | lua_setiuservalue( L2, -2, uvi); // ... u |
| 1465 | -- uvi; | 1561 | -- uvi; |
| 1466 | } | 1562 | } |
| 1467 | // when we are done, all uservalues are popped from the stack | 1563 | // when we are done, all uservalues are popped from the source stack, and we want only the single transferred value in the destination |
| 1468 | STACK_MID( L2, 1); // ... u | 1564 | if( eLM_ToKeeper == mode_) // ... userdata_clone_sentinel u |
| 1469 | } | ||
| 1470 | STACK_MID( L, 2); // ... mt __lanesclone | ||
| 1471 | // call cloning function in source state to perform the actual memory cloning | ||
| 1472 | lua_pushlightuserdata( L, clone); // ... mt __lanesclone clone | ||
| 1473 | lua_pushlightuserdata( L, source); // ... mt __lanesclone source | ||
| 1474 | lua_call( L, 2, 0); // ... mt | ||
| 1475 | STACK_MID( L, 1); | ||
| 1476 | // copy the metatable in the target state | ||
| 1477 | if( inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_)) // ... u mt? | ||
| 1478 | { | ||
| 1479 | lua_pop( L, 1); // ... | ||
| 1480 | STACK_MID( L, 0); | ||
| 1481 | // when writing to a keeper state, we have here a sentinel function with the metatable's fqn as upvalue | ||
| 1482 | if( eLM_ToKeeper == mode_) // ... u sentinel | ||
| 1483 | { | 1565 | { |
| 1484 | ASSERT_L( lua_tocfunction( L2, -1) == table_lookup_sentinel); | 1566 | lua_pop( L2, 1); // ... userdata_clone_sentinel |
| 1485 | // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn | ||
| 1486 | lua_getupvalue( L2, -1, 1); // ... u sentinel fqn | ||
| 1487 | lua_remove( L2, -2); // ... u fqn | ||
| 1488 | lua_insert( L2, -2); // ... fqn u | ||
| 1489 | lua_pushcclosure( L2, userdata_clone_sentinel, 2); // ... userdata_clone_sentinel | ||
| 1490 | } | ||
| 1491 | else // from keeper or direct, we have the userdata and the metatable | ||
| 1492 | { | ||
| 1493 | ASSERT_L( lua_istable( L2, -1)); | ||
| 1494 | lua_setmetatable( L2, -2); // ... u | ||
| 1495 | } | 1567 | } |
| 1496 | STACK_MID( L2, 1); | 1568 | STACK_MID( L2, 1); |
| 1497 | STACK_MID( L, 0); | 1569 | STACK_MID( L, 2); |
| 1498 | return TRUE; | 1570 | // call cloning function in source state to perform the actual memory cloning |
| 1499 | } | 1571 | lua_pushlightuserdata( L, clone); // ... mt __lanesclone clone |
| 1500 | else | 1572 | lua_pushlightuserdata( L, source); // ... mt __lanesclone clone source |
| 1501 | { | 1573 | lua_call( L, 2, 0); // ... mt |
| 1502 | (void) luaL_error( L, "Error copying a metatable"); | 1574 | STACK_MID( L, 1); |
| 1503 | } | 1575 | } |
| 1504 | } | 1576 | } |
| 1505 | 1577 | ||
| 1506 | STACK_END( L2, 1); | 1578 | STACK_END( L2, 1); |
| 1579 | lua_pop( L, 1); // ... | ||
| 1507 | STACK_END( L, 0); | 1580 | STACK_END( L, 0); |
| 1508 | return FALSE; | 1581 | return TRUE; |
| 1509 | } | 1582 | } |
| 1510 | 1583 | ||
| 1511 | static bool_t inter_copy_userdata( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_) | 1584 | static bool_t inter_copy_userdata( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_) |
| @@ -1516,17 +1589,21 @@ static bool_t inter_copy_userdata( Universe* U, lua_State* L2, uint_t L2_cache_i | |||
| 1516 | { | 1589 | { |
| 1517 | return FALSE; | 1590 | return FALSE; |
| 1518 | } | 1591 | } |
| 1519 | // Allow only deep userdata entities to be copied across | 1592 | |
| 1520 | DEBUGSPEW_CODE( fprintf( stderr, "USERDATA\n")); | 1593 | // try clonable userdata first |
| 1521 | if( copydeep( U, L, L2, i, mode_)) | 1594 | if( copyclone( U, L2, L2_cache_i, L, i, mode_, upName_)) |
| 1522 | { | 1595 | { |
| 1596 | STACK_MID( L, 0); | ||
| 1597 | STACK_MID( L2, 1); | ||
| 1523 | return TRUE; | 1598 | return TRUE; |
| 1524 | } | 1599 | } |
| 1525 | 1600 | ||
| 1526 | STACK_MID( L, 0); | 1601 | STACK_MID( L, 0); |
| 1527 | STACK_MID( L2, 0); | 1602 | STACK_MID( L2, 0); |
| 1528 | 1603 | ||
| 1529 | if( copyclone( U, L2, L2_cache_i, L, i, mode_, upName_)) | 1604 | // Allow only deep userdata entities to be copied across |
| 1605 | DEBUGSPEW_CODE( fprintf( stderr, "USERDATA\n")); | ||
| 1606 | if( copydeep( U, L2, L2_cache_i, L, i, mode_, upName_)) | ||
| 1530 | { | 1607 | { |
| 1531 | STACK_MID( L, 0); | 1608 | STACK_MID( L, 0); |
| 1532 | STACK_MID( L2, 1); | 1609 | STACK_MID( L2, 1); |
| @@ -1563,54 +1640,78 @@ static bool_t inter_copy_function( Universe* U, lua_State* L2, uint_t L2_cache_i | |||
| 1563 | STACK_CHECK( L2, 0); | 1640 | STACK_CHECK( L2, 0); |
| 1564 | DEBUGSPEW_CODE( fprintf( stderr, "FUNCTION %s\n", upName_)); | 1641 | DEBUGSPEW_CODE( fprintf( stderr, "FUNCTION %s\n", upName_)); |
| 1565 | 1642 | ||
| 1566 | if( lua_tocfunction( L, i) == userdata_clone_sentinel) // we are actually copying a clonable full userdata | 1643 | if( lua_tocfunction( L, i) == userdata_clone_sentinel) // we are actually copying a clonable full userdata from a keeper |
| 1567 | { | 1644 | { |
| 1568 | // clone the full userdata again | 1645 | // clone the full userdata again |
| 1569 | size_t userdata_size = 0; | 1646 | size_t userdata_size = 0; |
| 1570 | void* source; | 1647 | void* source; |
| 1571 | void* clone; | 1648 | void* clone; |
| 1572 | // this function has 2 upvalues: the fqn of its metatable, and the userdata itself | 1649 | |
| 1650 | // let's see if we already restored this userdata | ||
| 1573 | lua_getupvalue( L, i, 2); // ... u | 1651 | lua_getupvalue( L, i, 2); // ... u |
| 1574 | source = lua_touserdata( L, -1); | 1652 | source = lua_touserdata( L, -1); |
| 1575 | lookup_table( L2, L, i, mode_, upName_); // ... u // ... mt | 1653 | lua_pushlightuserdata( L2, source); // ... source |
| 1654 | lua_rawget( L2, L2_cache_i); // ... u? | ||
| 1655 | if( !lua_isnil( L2, -1)) | ||
| 1656 | { | ||
| 1657 | lua_pop( L, 1); // ... | ||
| 1658 | STACK_MID( L, 0); | ||
| 1659 | STACK_MID( L2, 1); | ||
| 1660 | return TRUE; | ||
| 1661 | } | ||
| 1662 | lua_pop( L2, 1); // ... | ||
| 1663 | |||
| 1664 | // this function has 2 upvalues: the fqn of its metatable, and the userdata itself | ||
| 1665 | lookup_table( L2, L, i, mode_, upName_); // ... mt | ||
| 1576 | // __lanesclone should always exist because we wouldn't be restoring data from a userdata_clone_sentinel closure to begin with | 1666 | // __lanesclone should always exist because we wouldn't be restoring data from a userdata_clone_sentinel closure to begin with |
| 1577 | lua_getfield( L2, -1, "__lanesclone"); // ... mt __lanesclone | 1667 | lua_getfield( L2, -1, "__lanesclone"); // ... mt __lanesclone |
| 1578 | lua_pushvalue( L2, -1); // ... mt __lanesclone __lanesclone | 1668 | lua_pushvalue( L2, -1); // ... mt __lanesclone __lanesclone |
| 1669 | // 'i' slot is the closure, but from now on it is the actual userdata | ||
| 1670 | i = lua_gettop( L); | ||
| 1671 | source = lua_touserdata( L, -1); | ||
| 1579 | // call the cloning function with 1 argument, should return the number of bytes to allocate for the clone | 1672 | // call the cloning function with 1 argument, should return the number of bytes to allocate for the clone |
| 1580 | lua_pushlightuserdata( L2, source); // ... mt __lanesclone __lanesclone source | 1673 | lua_pushlightuserdata( L2, source); // ... mt __lanesclone __lanesclone source |
| 1581 | lua_call( L2, 1, 1); // ... mt __lanesclone size | 1674 | lua_call( L2, 1, 1); // ... mt __lanesclone size |
| 1582 | userdata_size = (size_t) lua_tointeger( L2, -1); // ... mt __lanesclone size | 1675 | userdata_size = (size_t) lua_tointeger( L2, -1); // ... mt __lanesclone size |
| 1583 | lua_pop( L2, 1); // ... mt __lanesclone | 1676 | lua_pop( L2, 1); // ... mt __lanesclone |
| 1584 | lua_pushnil( L2); // ... mt __lanesclone nil | ||
| 1585 | { | 1677 | { |
| 1586 | int const clone_i = lua_gettop( L2); | 1678 | // extract uservalues (don't transfer them yet) |
| 1587 | int uvi = 0; | 1679 | int uvi = 0; |
| 1588 | while( lua_getiuservalue( L, -1, uvi + 1) != LUA_TNONE) // ... u uv | 1680 | while( lua_getiuservalue( L, i, uvi + 1) != LUA_TNONE) // ... u uv |
| 1589 | { | 1681 | { |
| 1590 | luaG_inter_move( U, L, L2, 1, mode_); // ... u // ... mt __lanesclone nil [uv]+ | ||
| 1591 | ++ uvi; | 1682 | ++ uvi; |
| 1592 | } | 1683 | } |
| 1593 | // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now at the same time as the rest | 1684 | // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now |
| 1594 | lua_pop( L, 2); // ... u | 1685 | lua_pop( L, 1); // ... u [uv]* |
| 1595 | STACK_MID( L, 0); // ... | 1686 | STACK_MID( L, uvi + 1); |
| 1596 | // create the clone userdata with the required number of uservalue slots | 1687 | // create the clone userdata with the required number of uservalue slots |
| 1597 | clone = lua_newuserdatauv( L2, userdata_size, uvi); // ... mt __lanesclone nil [uv]+ u | 1688 | clone = lua_newuserdatauv( L2, userdata_size, uvi); // ... mt __lanesclone u |
| 1598 | lua_replace( L2, clone_i); // ... mt __lanesclone u [uv]+ | 1689 | // add it in the cache |
| 1599 | // assign uservalues | 1690 | lua_pushlightuserdata( L2, source); // ... mt __lanesclone u source |
| 1691 | lua_pushvalue( L2, -2); // ... mt __lanesclone u source u | ||
| 1692 | lua_rawset( L2, L2_cache_i); // ... mt __lanesclone u | ||
| 1693 | // set metatable | ||
| 1694 | lua_pushvalue( L2, -3); // ... mt __lanesclone u mt | ||
| 1695 | lua_setmetatable( L2, -2); // ... mt __lanesclone u | ||
| 1696 | // transfer and assign uservalues | ||
| 1600 | while( uvi > 0) | 1697 | while( uvi > 0) |
| 1601 | { | 1698 | { |
| 1699 | inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), vt, mode_, upName_); // ... mt __lanesclone u uv | ||
| 1700 | lua_pop( L, 1); // ... u [uv]* | ||
| 1602 | // this pops the value from the stack | 1701 | // this pops the value from the stack |
| 1603 | lua_setiuservalue( L2, clone_i, uvi); // ... mt __lanesclone u [uv]+ | 1702 | lua_setiuservalue( L2, -2, uvi); // ... mt __lanesclone u |
| 1604 | -- uvi; | 1703 | -- uvi; |
| 1605 | } | 1704 | } |
| 1606 | // when we are done, all uservalues are popped from the stack | 1705 | // when we are done, all uservalues are popped from the stack |
| 1706 | lua_pop( L, 1); // ... | ||
| 1707 | STACK_MID( L, 0); | ||
| 1607 | STACK_MID( L2, 3); // ... mt __lanesclone u | 1708 | STACK_MID( L2, 3); // ... mt __lanesclone u |
| 1608 | } | 1709 | } |
| 1609 | lua_insert( L2, -3); // ... u mt __lanesclone | 1710 | // perform the custom cloning part |
| 1610 | lua_pushlightuserdata( L2, clone); // ... u mt __lanesclone clone | 1711 | lua_replace( L2, -3); // ... u __lanesclone |
| 1611 | lua_pushlightuserdata( L2, source); // ... u mt __lanesclone clone source | 1712 | lua_pushlightuserdata( L2, clone); // ... u __lanesclone clone |
| 1612 | lua_call( L2, 2, 0); // ... u mt | 1713 | lua_pushlightuserdata( L2, source); // ... u __lanesclone clone source |
| 1613 | lua_setmetatable( L2, -2); // ... u | 1714 | lua_call( L2, 2, 0); // ... u |
| 1614 | } | 1715 | } |
| 1615 | else | 1716 | else |
| 1616 | { | 1717 | { |
| @@ -1696,7 +1797,7 @@ static bool_t inter_copy_table( Universe* U, lua_State* L2, uint_t L2_cache_i, l | |||
| 1696 | * | 1797 | * |
| 1697 | * Returns TRUE if value was pushed, FALSE if its type is non-supported. | 1798 | * Returns TRUE if value was pushed, FALSE if its type is non-supported. |
| 1698 | */ | 1799 | */ |
| 1699 | static bool_t inter_copy_one( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_) | 1800 | bool_t inter_copy_one( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_) |
| 1700 | { | 1801 | { |
| 1701 | bool_t ret = TRUE; | 1802 | bool_t ret = TRUE; |
| 1702 | int val_type = lua_type( L, i); | 1803 | int val_type = lua_type( L, i); |
| @@ -1812,7 +1913,6 @@ static bool_t inter_copy_one( Universe* U, lua_State* L2, uint_t L2_cache_i, lua | |||
| 1812 | return ret; | 1913 | return ret; |
| 1813 | } | 1914 | } |
| 1814 | 1915 | ||
| 1815 | |||
| 1816 | /* | 1916 | /* |
| 1817 | * Akin to 'lua_xmove' but copies values between _any_ Lua states. | 1917 | * Akin to 'lua_xmove' but copies values between _any_ Lua states. |
| 1818 | * | 1918 | * |
diff --git a/src/tools.h b/src/tools.h index 0df88e9..3bf5a02 100644 --- a/src/tools.h +++ b/src/tools.h | |||
| @@ -22,6 +22,19 @@ void luaG_dump( lua_State* L); | |||
| 22 | 22 | ||
| 23 | // ################################################################################################ | 23 | // ################################################################################################ |
| 24 | 24 | ||
| 25 | void push_registry_subtable_mode( lua_State* L, UniqueKey key_, const char* mode_); | ||
| 26 | void push_registry_subtable( lua_State* L, UniqueKey key_); | ||
| 27 | |||
| 28 | enum e_vt | ||
| 29 | { | ||
| 30 | VT_NORMAL, | ||
| 31 | VT_KEY, | ||
| 32 | VT_METATABLE | ||
| 33 | }; | ||
| 34 | bool_t inter_copy_one( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_); | ||
| 35 | |||
| 36 | // ################################################################################################ | ||
| 37 | |||
| 25 | int luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int package_idx_, LookupMode mode_); | 38 | int luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int package_idx_, LookupMode mode_); |
| 26 | 39 | ||
| 27 | int luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_); | 40 | int luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_); |
