aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Germain <bnt period germain arrobase gmail period com>2017-05-16 10:07:31 +0200
committerBenoit Germain <bnt period germain arrobase gmail period com>2017-05-16 10:07:31 +0200
commit0ede50e2da00f2915ceb50184425c42bda83adfd (patch)
tree19415e5e30d53adc376911ca61d9608f93b972f8
parent568d085cc9dee5f569f8c750f419d913ac470ff7 (diff)
downloadlanes-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.c190
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 */
357static 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
352static void populate_func_lookup_table_recur( lua_State* L, int _ctx_base, int _i, int _depth) 424static 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 */
820static bool_t push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i) 836static 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