diff options
| author | Benoit Germain <bnt period germain arrobase gmail period com> | 2017-05-16 10:07:31 +0200 |
|---|---|---|
| committer | Benoit Germain <bnt period germain arrobase gmail period com> | 2017-05-16 10:07:31 +0200 |
| commit | 0ede50e2da00f2915ceb50184425c42bda83adfd (patch) | |
| tree | 19415e5e30d53adc376911ca61d9608f93b972f8 /src | |
| parent | 568d085cc9dee5f569f8c750f419d913ac470ff7 (diff) | |
| download | lanes-0ede50e2da00f2915ceb50184425c42bda83adfd.tar.gz lanes-0ede50e2da00f2915ceb50184425c42bda83adfd.tar.bz2 lanes-0ede50e2da00f2915ceb50184425c42bda83adfd.zip | |
prepare the way for module table lookup
Add tables when populating lookup databases.
The idea is, when transfering a known table, to perform a lookup in the
destination for the equivalent table instead of cloning the original
(not implemented yet).
Diffstat (limited to 'src')
| -rw-r--r-- | src/tools.c | 190 |
1 files changed, 91 insertions, 99 deletions
diff --git a/src/tools.c b/src/tools.c index 50a9310..d97b231 100644 --- a/src/tools.c +++ b/src/tools.c | |||
| @@ -348,12 +348,82 @@ static char const* luaG_pushFQN(lua_State *L, int t, int last, size_t* length) | |||
| 348 | return lua_tolstring( L, -1, length); | 348 | return lua_tolstring( L, -1, length); |
| 349 | } | 349 | } |
| 350 | 350 | ||
| 351 | /* | ||
| 352 | * add two entries ["fully.qualified.name"] = o | ||
| 353 | * and [o] = "fully.qualified.name" | ||
| 354 | * where <o> is either a table or a function | ||
| 355 | * if we already had an entry of type [o] = ..., replace the name if the new one is shorter | ||
| 356 | */ | ||
| 357 | static void update_lookup_entry( lua_State* L, int _ctx_base, int _depth) | ||
| 358 | { | ||
| 359 | // slot 1 in the stack contains the table that receives everything we found | ||
| 360 | int const dest = _ctx_base; | ||
| 361 | // slot 2 contains a table that, when concatenated, produces the fully qualified name of scanned elements in the table provided at slot _i | ||
| 362 | int const fqn = _ctx_base + 1; | ||
| 363 | |||
| 364 | size_t prevNameLength, newNameLength; | ||
| 365 | char const* prevName; | ||
| 366 | DEBUGSPEW_CODE( char const *newName); | ||
| 367 | // first, raise an error if the function is already known | ||
| 368 | lua_pushvalue( L, -1); // ... {bfc} k o o | ||
| 369 | lua_rawget( L, dest); // ... {bfc} k o name? | ||
| 370 | prevName = lua_tolstring( L, -1, &prevNameLength); // NULL if we got nil (first encounter of this object) | ||
| 371 | // push name in fqn stack (note that concatenation will crash if name is a not string or a number) | ||
| 372 | lua_pushvalue( L, -3); // ... {bfc} k o name? k | ||
| 373 | ++ _depth; | ||
| 374 | lua_rawseti( L, fqn, _depth); // ... {bfc} k o name? | ||
| 375 | // generate name | ||
| 376 | DEBUGSPEW_CODE( newName =) luaG_pushFQN( L, fqn, _depth, &newNameLength); // ... {bfc} k o name? "f.q.n" | ||
| 377 | // Lua 5.2 introduced a hash randomizer seed which causes table iteration to yield a different key order | ||
| 378 | // on different VMs even when the tables are populated the exact same way. | ||
| 379 | // When Lua is built with compatibility options (such as LUA_COMPAT_ALL), | ||
| 380 | // this causes several base libraries to register functions under multiple names. | ||
| 381 | // This, with the randomizer, can cause the first generated name of an object to be different on different VMs, | ||
| 382 | // which breaks function transfer. | ||
| 383 | // Also, nothing prevents any external module from exposing a given object under several names, so... | ||
| 384 | // Therefore, when we encounter an object for which a name was previously registered, we need to select the names | ||
| 385 | // based on some sorting order so that we end up with the same name in all databases whatever order the table walk yielded | ||
| 386 | if( prevName != NULL && (prevNameLength < newNameLength || lua_lessthan( L, -2, -1))) | ||
| 387 | { | ||
| 388 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s '%s' remained named '%s'\n" INDENT_END, lua_typename( L, -3), newName, prevName)); | ||
| 389 | // the previous name is 'smaller' than the one we just generated: keep it! | ||
| 390 | lua_pop( L, 3); // ... {bfc} k | ||
| 391 | } | ||
| 392 | else | ||
| 393 | { | ||
| 394 | // the name we generated is either the first one, or a better fit for our purposes | ||
| 395 | if( prevName) | ||
| 396 | { | ||
| 397 | // clear the previous name for the database to avoid clutter | ||
| 398 | lua_insert( L, -2); // ... {bfc} k o "f.q.n" prevName | ||
| 399 | // t[prevName] = nil | ||
| 400 | lua_pushnil( L); // ... {bfc} k o "f.q.n" prevName nil | ||
| 401 | lua_rawset( L, dest); // ... {bfc} k o "f.q.n" | ||
| 402 | } | ||
| 403 | else | ||
| 404 | { | ||
| 405 | lua_remove( L, -2); // ... {bfc} k o "f.q.n" | ||
| 406 | } | ||
| 407 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s '%s'\n" INDENT_END, lua_typename( L, -2), newName)); | ||
| 408 | // prepare the stack for database feed | ||
| 409 | lua_pushvalue( L, -1); // ... {bfc} k o "f.q.n" "f.q.n" | ||
| 410 | lua_pushvalue( L, -3); // ... {bfc} k o "f.q.n" "f.q.n" o | ||
| 411 | ASSERT_L( lua_rawequal( L, -1, -4)); | ||
| 412 | ASSERT_L( lua_rawequal( L, -2, -3)); | ||
| 413 | // t["f.q.n"] = o | ||
| 414 | lua_rawset( L, dest); // ... {bfc} k o "f.q.n" | ||
| 415 | // t[o] = "f.q.n" | ||
| 416 | lua_rawset( L, dest); // ... {bfc} k | ||
| 417 | // remove table name from fqn stack | ||
| 418 | lua_pushnil( L); // ... {bfc} k nil | ||
| 419 | lua_rawseti( L, fqn, _depth); // ... {bfc} k | ||
| 420 | } | ||
| 421 | -- _depth; | ||
| 422 | } | ||
| 351 | 423 | ||
| 352 | static void populate_func_lookup_table_recur( lua_State* L, int _ctx_base, int _i, int _depth) | 424 | static void populate_func_lookup_table_recur( lua_State* L, int _ctx_base, int _i, int _depth) |
| 353 | { | 425 | { |
| 354 | lua_Integer visit_count; | 426 | lua_Integer visit_count; |
| 355 | // slot 1 in the stack contains the table that receives everything we found | ||
| 356 | int const dest = _ctx_base; | ||
| 357 | // slot 2 contains a table that, when concatenated, produces the fully qualified name of scanned elements in the table provided at slot _i | 427 | // slot 2 contains a table that, when concatenated, produces the fully qualified name of scanned elements in the table provided at slot _i |
| 358 | int const fqn = _ctx_base + 1; | 428 | int const fqn = _ctx_base + 1; |
| 359 | // slot 3 contains a cache that stores all already visited tables to avoid infinite recursion loops | 429 | // slot 3 contains a cache that stores all already visited tables to avoid infinite recursion loops |
| @@ -412,69 +482,15 @@ static void populate_func_lookup_table_recur( lua_State* L, int _ctx_base, int _ | |||
| 412 | lua_rawset( L, cache); // ... {_i} {bfc} k {} | 482 | lua_rawset( L, cache); // ... {_i} {bfc} k {} |
| 413 | // store the table in the breadth-first cache | 483 | // store the table in the breadth-first cache |
| 414 | lua_pushvalue( L, -2); // ... {_i} {bfc} k {} k | 484 | lua_pushvalue( L, -2); // ... {_i} {bfc} k {} k |
| 415 | lua_insert( L, -2); // ... {_i} {bfc} k k {} | 485 | lua_pushvalue( L, -2); // ... {_i} {bfc} k {} k {} |
| 416 | lua_rawset( L, breadth_first_cache); // ... {_i} {bfc} k | 486 | lua_rawset( L, breadth_first_cache); // ... {_i} {bfc} k {} |
| 487 | // generate a name, and if we already had one name, keep whichever is the shorter | ||
| 488 | update_lookup_entry( L, _ctx_base, _depth); // ... {_i} {bfc} k | ||
| 417 | } | 489 | } |
| 418 | else if( lua_isfunction( L, -1) && (luaG_getfuncsubtype( L, -1) != FST_Bytecode)) // ... {_i} {bfc} k func | 490 | else if( lua_isfunction( L, -1) && (luaG_getfuncsubtype( L, -1) != FST_Bytecode)) // ... {_i} {bfc} k func |
| 419 | { | 491 | { |
| 420 | size_t prevNameLength, newNameLength; | 492 | // generate a name, and if we already had one name, keep whichever is the shorter |
| 421 | char const* prevName; | 493 | update_lookup_entry( L, _ctx_base, _depth); // ... {_i} {bfc} k |
| 422 | DEBUGSPEW_CODE( char const *newName); | ||
| 423 | // first, raise an error if the function is already known | ||
| 424 | lua_pushvalue( L, -1); // ... {_i} {bfc} k func func | ||
| 425 | lua_rawget( L, dest); // ... {_i} {bfc} k func name? | ||
| 426 | prevName = lua_tolstring( L, -1, &prevNameLength); // NULL if we got nil (first encounter of this function) | ||
| 427 | // push function name in fqn stack (note that concatenation will crash if name is a not string or a number) | ||
| 428 | lua_pushvalue( L, -3); // ... {_i} {bfc} k func name? k | ||
| 429 | ++ _depth; | ||
| 430 | lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} k func name? | ||
| 431 | // generate name | ||
| 432 | DEBUGSPEW_CODE( newName =) luaG_pushFQN( L, fqn, _depth, &newNameLength); // ... {_i} {bfc} k func name? "f.q.n" | ||
| 433 | // Lua 5.2 introduced a hash randomizer seed which causes table iteration to yield a different key order | ||
| 434 | // on different VMs even when the tables are populated the exact same way. | ||
| 435 | // When Lua is built with compatibility options (such as LUA_COMPAT_ALL), | ||
| 436 | // this causes several base libraries to register functions under multiple names. | ||
| 437 | // This, with the randomizer, can cause the first name of a function to be different on different VMs, | ||
| 438 | // which breaks function transfer. | ||
| 439 | // Also, nothing prevents any external module from exposing a given function under several names, so... | ||
| 440 | // Therefore, when we encounter a function for which a name was previously registered, we need to select the names | ||
| 441 | // based on some sorting order so that we end up with the same name in all databases whatever order the table walk yielded | ||
| 442 | if( prevName != NULL && (prevNameLength < newNameLength || lua_lessthan( L, -2, -1))) | ||
| 443 | { | ||
| 444 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "function '%s' remained named '%s'\n" INDENT_END, newName, prevName)); | ||
| 445 | // the previous name is 'smaller' than the one we just generated: keep it! | ||
| 446 | lua_pop( L, 3); // ... {_i} {bfc} k | ||
| 447 | } | ||
| 448 | else | ||
| 449 | { | ||
| 450 | // the name we generated is either the first one, or a better fit for our purposes | ||
| 451 | if( prevName) | ||
| 452 | { | ||
| 453 | // clear the previous name for the database to avoid clutter | ||
| 454 | lua_insert( L, -2); // ... {_i} {bfc} k func "f.q.n" prevName | ||
| 455 | // t[prevName] = nil | ||
| 456 | lua_pushnil( L); // ... {_i} {bfc} k func "f.q.n" prevName nil | ||
| 457 | lua_rawset( L, dest); // ... {_i} {bfc} k func "f.q.n" | ||
| 458 | } | ||
| 459 | else | ||
| 460 | { | ||
| 461 | lua_remove( L, -2); // ... {_i} {bfc} k func "f.q.n" | ||
| 462 | } | ||
| 463 | // prepare the stack for database feed | ||
| 464 | lua_pushvalue( L, -1); // ... {_i} {bfc} k func "f.q.n" "f.q.n" | ||
| 465 | lua_pushvalue( L, -3); // ... {_i} {bfc} k func "f.q.n" "f.q.n" func | ||
| 466 | ASSERT_L( lua_rawequal( L, -1, -4)); | ||
| 467 | ASSERT_L( lua_rawequal( L, -2, -3)); | ||
| 468 | // t["f.q.n"] = func | ||
| 469 | lua_rawset( L, dest); // ... {_i} {bfc} k func "f.q.n" | ||
| 470 | // t[func] = "f.q.n" | ||
| 471 | lua_rawset( L, dest); // ... {_i} {bfc} k | ||
| 472 | // remove table name from fqn stack | ||
| 473 | lua_pushnil( L); // ... {_i} {bfc} k nil | ||
| 474 | lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} k | ||
| 475 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "function '%s'\n" INDENT_END, newName)); | ||
| 476 | } | ||
| 477 | -- _depth; | ||
| 478 | } | 494 | } |
| 479 | else | 495 | else |
| 480 | { | 496 | { |
| @@ -819,57 +835,33 @@ static int buf_writer( lua_State *L, const void* b, size_t n, void* B ) { | |||
| 819 | */ | 835 | */ |
| 820 | static bool_t push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i) | 836 | static bool_t push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i) |
| 821 | { | 837 | { |
| 822 | bool_t ret; | 838 | bool_t not_found_in_cache; // L2 |
| 839 | void* const p = (void*)lua_topointer( L, i); | ||
| 823 | 840 | ||
| 824 | ASSERT_L( L2_cache_i != 0); | 841 | ASSERT_L( L2_cache_i != 0); |
| 825 | |||
| 826 | STACK_GROW( L2, 3); | 842 | STACK_GROW( L2, 3); |
| 827 | |||
| 828 | // L2_cache[id_str]= [{...}] | ||
| 829 | // | ||
| 830 | STACK_CHECK( L2); | 843 | STACK_CHECK( L2); |
| 831 | 844 | ||
| 832 | // We don't need to use the from state ('L') in ID since the life span | 845 | // We don't need to use the from state ('L') in ID since the life span |
| 833 | // is only for the duration of a copy (both states are locked). | 846 | // is only for the duration of a copy (both states are locked). |
| 834 | // | 847 | // push a light userdata uniquely representing the table |
| 835 | lua_pushlightuserdata( L2, (void*)lua_topointer( L, i)); // push a light userdata uniquely representing the table | 848 | lua_pushlightuserdata( L2, p); // ... p |
| 836 | 849 | ||
| 837 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) ); | 850 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) ); |
| 838 | 851 | ||
| 839 | lua_pushvalue( L2, -1); | 852 | lua_rawget( L2, L2_cache_i); // ... {cached|nil} |
| 840 | lua_rawget( L2, L2_cache_i); | 853 | not_found_in_cache = lua_isnil( L2, -1); |
| 841 | // | 854 | if( not_found_in_cache) |
| 842 | // [-2]: identity table pointer lightuserdata | ||
| 843 | // [-1]: table|nil | ||
| 844 | |||
| 845 | if( lua_isnil( L2, -1)) | ||
| 846 | { | ||
| 847 | lua_pop( L2, 1); | ||
| 848 | lua_newtable( L2); | ||
| 849 | lua_pushvalue( L2, -1); | ||
| 850 | lua_insert( L2, -3); | ||
| 851 | // | ||
| 852 | // [-3]: new table (2nd ref) | ||
| 853 | // [-2]: identity table pointer lightuserdata | ||
| 854 | // [-1]: new table | ||
| 855 | |||
| 856 | lua_rawset( L2, L2_cache_i); | ||
| 857 | // | ||
| 858 | // [-1]: new table (tied to 'L2_cache' table') | ||
| 859 | |||
| 860 | ret = FALSE; // brand new | ||
| 861 | } | ||
| 862 | else | ||
| 863 | { | 855 | { |
| 864 | lua_remove(L2,-2); | 856 | lua_pop( L2, 1); // ... |
| 865 | ret= TRUE; // from cache | 857 | lua_newtable( L2); // ... {} |
| 858 | lua_pushlightuserdata( L2, p); // ... {} p | ||
| 859 | lua_pushvalue( L2, -2); // ... {} p {} | ||
| 860 | lua_rawset( L2, L2_cache_i); // ... {} | ||
| 866 | } | 861 | } |
| 867 | STACK_END( L2, 1); | 862 | STACK_END( L2, 1); |
| 868 | // | ||
| 869 | // L2 [-1]: table to use as destination | ||
| 870 | |||
| 871 | ASSERT_L( lua_istable( L2, -1)); | 863 | ASSERT_L( lua_istable( L2, -1)); |
| 872 | return ret; | 864 | return !not_found_in_cache; |
| 873 | } | 865 | } |
| 874 | 866 | ||
| 875 | 867 | ||
