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 | |
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).
-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 | ||