From 6234c2113a2b52ddc9fa900e7848f2cd19bd9394 Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Wed, 27 Jun 2012 14:07:55 +0200 Subject: * when a transfered function is not found in source, guess its name to help the user find out what's wrong * new function lanes.nameof() --- CHANGES | 4 ++ docs/index.html | 111 ++++++-------------------------------------- src/lanes.c | 1 + src/lanes.lua | 1 + src/tools.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++------ src/tools.h | 2 + 6 files changed, 146 insertions(+), 112 deletions(-) diff --git a/CHANGES b/CHANGES index 52b0349..95e0e70 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,10 @@ CHANGES: +CHANGE 40: BGe 26-Jun-2012 + * when a transfered function is not found in source, guess its name to help the user find out what's wrong + * new function lanes.nameof() + CHANGE 39: BGe 23-Jun-2012 * lanes.timer() accepts a first_secs=nil to stop a timer * timer lane catches errors and prints them diff --git a/docs/index.html b/docs/index.html index 52ba852..2d1c78e 100644 --- a/docs/index.html +++ b/docs/index.html @@ -56,7 +56,7 @@


Copyright © 2007-12 Asko Kauppi, Benoit Germain. All rights reserved.
Lua Lanes is published under the same MIT license as Lua 5.1. -

This document was revised on 23-Jun-12, and applies to version 3.1.4 +

This document was revised on 26-Jun-12, and applies to version 3.1.4

@@ -359,6 +359,17 @@ also in the new lanes.

Each lane also gets a function set_debug_threadname() that it can use anytime to do as the name says. Supported debuggers are Microsoft Visual Studio (for the C side) and Decoda (for the Lua side). +

+

+ If a lane body pulls a C function imported by a module required before Lanes itself (thus not through a hooked require), the lane generator creation will raise an error. + The function name it shows is a path where it was found by scanning _G. As a utility, the name guessing functionality is exposed as such: + + +
+ + "type", "name" = lanes.nameof( o) + +

Free running lanes

@@ -1089,103 +1100,7 @@ its actual value.

Change log

- Apr-2012 -

- - - Feb-2012 - - - - Nov-2011 - - - Mar-2011 (not yet versioned) - - - Mar-2011 (2.1.0) - -Feb-2011 (2.0.11): - - -Jan-2011 (2.0.10): - - -Dec-2010 (2.0.9): - - -Aug-2010 (2.0.6): - - -Jan-2009 (2.0.3): - - -Jul-2008 (2.0): - + See CHANGES.

diff --git a/src/lanes.c b/src/lanes.c index eebe06a..3e906c0 100644 --- a/src/lanes.c +++ b/src/lanes.c @@ -2303,6 +2303,7 @@ static const struct luaL_reg lanes_functions [] = { {"linda", LG_linda}, {"now_secs", LG_now_secs}, {"wakeup_conv", LG_wakeup_conv}, + {"nameof", luaG_nameof}, {"_single", LG__single}, {NULL, NULL} }; diff --git a/src/lanes.lua b/src/lanes.lua index 5aeeaf4..d1082af 100644 --- a/src/lanes.lua +++ b/src/lanes.lua @@ -564,6 +564,7 @@ end lanes.gen = gen lanes.linda = mm.linda lanes.cancel_error = mm.cancel_error + lanes.nameof = mm.nameof lanes.timer = timer lanes.genlock = genlock lanes.genatomic = genatomic diff --git a/src/tools.c b/src/tools.c index ee2a1ca..65c70bf 100644 --- a/src/tools.c +++ b/src/tools.c @@ -1199,39 +1199,150 @@ static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, ui ASSERT_L( lua_isfunction(L2,-1)); } +/* + * Return some name helping to identify an object + */ +int discover_object_name_recur( lua_State* L, int _shortest, int _length) +{ + int const what = 1; // o "r" {c} {fqn} ... {?} + int const result = 2; + int const cache = 3; + int const fqn = 4; + // no need to scan this table if the name we will discover is longer than one we already know + if( _shortest <= _length + 1) + { + return _shortest; + } + STACK_GROW( L, 3); + STACK_CHECK(L) + // stack top contains the table to search in + lua_pushvalue( L, -1); // o "r" {c} {fqn} ... {?} {?} + lua_rawget( L, cache); // o "r" {c} {fqn} ... {?} nil/1 + // if table is already visited, we are done + if( !lua_isnil( L, -1)) + { + lua_pop( L, 1); // o "r" {c} {fqn} ... {?} + return _shortest; + } + // examined table is not in the cache, add it now + lua_pop( L, 1); // o "r" {c} {fqn} ... {?} + lua_pushvalue( L, -1); // o "r" {c} {fqn} ... {?} {?} + lua_pushinteger( L, 1); // o "r" {c} {fqn} ... {?} {?} 1 + lua_rawset( L, cache); // o "r" {c} {fqn} ... {?} + // scan table contents + lua_pushnil( L); // o "r" {c} {fqn} ... {?} nil + while( lua_next( L, -2)) // o "r" {c} {fqn} ... {?} k v + { + //char const *const key = lua_tostring( L, -2); // only for debugging (BEWARE, IT MAY CHANGE THE VALUE IF IT IS CONVERTIBLE, AND WRECK THE LOOP ITERATION PROCESS!) + // append key name to fqn stack + ++ _length; + lua_pushvalue( L, -2); // o "r" {c} {fqn} ... {?} k v k + lua_rawseti( L, fqn, _length); // o "r" {c} {fqn} ... {?} k v + if( lua_rawequal( L, -1, what)) // is it what we are looking for? + { + // update shortest name + if( _length < _shortest) + { + _shortest = _length; + luaG_pushFQN( L, fqn, _length); // o "r" {c} {fqn} ... {?} k v "fqn" + lua_replace( L, result); // o "r" {c} {fqn} ... {?} k v + } + // no need to search further at this level + lua_pop( L, 2); // o "r" {c} {fqn} ... {?} + break; + } + else if( lua_istable( L, -1)) + { + _shortest = discover_object_name_recur( L, _shortest, _length); + } + // make ready for next iteration + lua_pop( L, 1); // o "r" {c} {fqn} ... {?} k + // remove name from fqn stack + lua_pushnil( L); // o "r" {c} {fqn} ... {?} k nil + lua_rawseti( L, fqn, _length); // o "r" {c} {fqn} ... {?} k + -- _length; + } // o "r" {c} {fqn} ... {?} + // remove the visited table from the cache, in case a shorter path to the searched object exists + lua_pushvalue( L, -1); // o "r" {c} {fqn} ... {?} {?} + lua_pushnil( L); // o "r" {c} {fqn} ... {?} {?} nil + lua_rawset( L, cache); // o "r" {c} {fqn} ... {?} + STACK_END( L, 0) + return _shortest; +} + +/* + * "type", "name" = lanes.nameof( o) + */ +int luaG_nameof( lua_State* L) +{ + int what = lua_gettop( L); + if( what > 1) + { + luaL_argerror( L, what, "too many arguments."); + } + + // numbers, strings, booleans and nil can't be identified + if( lua_type( L, 1) < LUA_TTABLE) + { + lua_pushstring( L, luaL_typename( L, 1)); // o "type" + lua_insert( L, -2); // "type" o + return 2; + } + STACK_GROW( L, 4); + // this slot will contain the shortest name we found when we are done + lua_pushnil( L); // o nil + // push a cache that will contain all already visited tables + lua_newtable( L); // o nil {c} + // push a table whose contents are strings that, when concatenated, produce unique name + lua_newtable( L); // o nil {c} {fqn} + // this is where we start the search + lua_pushvalue( L, LUA_GLOBALSINDEX); // o nil {c} {fqn} _G + (void) discover_object_name_recur( L, 6666, 0); + lua_pop( L, 3); // o "result" + lua_pushstring( L, luaL_typename( L, 1)); // o "result" "type" + lua_replace( L, -3); // "type" "result" + return 2; +} + /* * Push a looked-up native/LuaJIT function. */ static void lookup_native_func( lua_State *L2, lua_State *L, uint_t i) { - char const *fqn; + char const *fqn; // L // L2 size_t len; - _ASSERT_L( L, lua_isfunction( L, i)); + _ASSERT_L( L, lua_isfunction( L, i)); // ... f ... STACK_CHECK( L) - STACK_CHECK( L2) // fetch the name from the source state's lookup table - lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY); // {} + lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY); // ... f ... {} _ASSERT_L( L, lua_istable( L, -1)); - lua_pushvalue( L, i); // {} f - lua_rawget( L, -2); // {} "f.q.n" + lua_pushvalue( L, i); // ... f ... {} f + lua_rawget( L, -2); // ... f ... {} "f.q.n" fqn = lua_tolstring( L, -1, &len); + // popping doesn't invalidate the pointer since this is an interned string gotten from the lookup database + lua_pop( L, 2); // ... f ... if( !fqn) { - luaL_error( L, "function not found in origin transfer database."); + lua_pushvalue( L, i); // ... f ... f + // try to discover the name of the function we want to send + luaG_nameof( L); // ... f ... "type" "name" + (void) luaL_error( L, "%s %s not found in origin transfer database.", lua_tostring( L, -2), lua_tostring( L, -1)); + return; } + STACK_END( L, 0) // push the equivalent function in the destination's stack, retrieved from the lookup table - lua_getfield( L2, LUA_REGISTRYINDEX, LOOKUP_KEY); // {} + STACK_CHECK( L2) + lua_getfield( L2, LUA_REGISTRYINDEX, LOOKUP_KEY); // {} _ASSERT_L( L2, lua_istable( L2, -1)); - lua_pushlstring( L2, fqn, len); // {} "f.q.n" - lua_pop( L, 2); // - lua_rawget( L2, -2); // {} f + lua_pushlstring( L2, fqn, len); // {} "f.q.n" + lua_rawget( L2, -2); // {} f if( !lua_isfunction( L2, -1)) { - luaL_error( L, "function %s not found in destination transfer database.", fqn); + (void) luaL_error( L, "function %s not found in destination transfer database.", fqn); + return; } - lua_remove( L2, -2); // f + lua_remove( L2, -2); // f STACK_END( L2, 1) - STACK_END( L, 0) } #define LOG_FUNC_INFO 0 diff --git a/src/tools.h b/src/tools.h index 67f9874..b8dc362 100644 --- a/src/tools.h +++ b/src/tools.h @@ -65,6 +65,8 @@ void luaG_push_proxy( lua_State *L, luaG_IdFunction idfunc, DEEP_PRELUDE *deep_u int luaG_inter_copy( lua_State *L, lua_State *L2, uint_t n); int luaG_inter_move( lua_State *L, lua_State *L2, uint_t n); +int luaG_nameof( lua_State* L); + // Lock for reference counter inc/dec locks (to be initialized by outside code) // extern MUTEX_T deep_lock; -- cgit v1.2.3-55-g6feb