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
-
- - improved LuaJIT2 compatibility by handling "*" library set through luaL_openlibs().
-
-
-
- Feb-2012
-
- - Added support for an on_state_create callback invoked on a pristine Lua state created by Lanes.
- - This required a change in the lanes.configure() signature, hence the minor version bump.
-
-
-
- Nov-2011
-
- - process exit change: close everything at GC when main state closes, not when atexit() handlers are processed
- - Lua 5.2-style module:
-
- - module() is no longer used to implement lanes.lua
- - a global "lanes" variable is no longer created when the module is required
- - the Lanes module table is returned instead
-
- - Lanes must be initialized before used:
-
- - the first occurence of 'require "lanes"' produces a minimal interface that only contains a configure() function
- - the remainder of the interface is made available once this function is called
- - subsequent calls to configure() do nothing
- - configure() controls the number of keeper states and the startup of timers
-
- - * LuaJIT 2 compatibility
-
- - non-Lua functions are no longer copied by creating a C closure from a C pointer, but through 2-way lookup tables
- - this means that if a lane function body pulls non-Lua functions, the lane generator description must contain the list of libraries and modules that exports them
- - introduces a change in configuration .globals management: contents are copied *after* std libs are loaded
- - new .required configuration entry to list modules that must be require()'ed before lane body is transferred
-
- - lane:cancel() wakes up waiting lindas like what is done at lane shutdown
- - removed packagepath and packagecpath options, replaced by a package table, whose fields path, cpath, loaders, preload are transfered
-
-
- Mar-2011 (not yet versioned)
-
- - linda honors __tostring and __concat
- - new accessor linda:count(), to get info about data stored inside a linda.
- - new lanes options packagepath and packagecpath, in case one needs to set them differently than the default.
-
-
- Mar-2011 (2.1.0)
-
- - fixed potential crash at application shutdown when calling lua_close() on a killed thread's VM.
- - exposed cancel_test() in the lanes to enable manual testing for cancellation requests.
- - removed kludgy {globals={threadName}} support, replaced with a new function set_debug_threadname().
-
-Feb-2011 (2.0.11):
-
- - Fixed bug where reference to Linda object was dropped for a short time (crashing if GC was run during that time).
- - Changed the atexit code to trip the timer thread's write signal.
- - Changed lanes.c to export functions as a module rather than writing them directly to the globals table.
-
-
-Jan-2011 (2.0.10):
-
- - linda_send was waiting on the wrong signal
- - buildfix when using i586-mingw32msvc-gcc cross compiler
- - lanes_h:cancel() returns a boolean as it should
- - timers could get blocked sometimes because they were initialized with negative values
- - prepare_timeout could generate an illegal setting
-
-
-Dec-2010 (2.0.9):
-
- - Fixed 'memory leak' in some situations where a free running lane is collected before application shutdown
- - Fix LuaJIT2 incompatibility (no 'tostring' hijack anymore)
- - Added support to generate a lane from a string
-
-
-Aug-2010 (2.0.6):
-
-- Fixed some memory leaks
-- Fixed error in passing parameters to finalizers
-- Fixed missing argument propagation in lane:cancel
-- Added thread name debugging in VS
-
-
-Jan-2009 (2.0.3):
-
- - Added 'finalizer' to lane options. (TBD: not implemented yet!)
-
- - Added call stack to errors coming from a lane.
-
-
-
-Jul-2008 (2.0):
-
- - Too many changes to list (you'll need to re-read this manual)
-
-
+ 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