diff options
author | Benoit Germain <bnt.germain@gmail.com> | 2013-01-10 15:12:41 +0100 |
---|---|---|
committer | Benoit Germain <bnt.germain@gmail.com> | 2013-01-10 15:12:41 +0100 |
commit | 621cc9373c26d06ef4d88c66bbfb3aa2481b5d1b (patch) | |
tree | 1ea4d90f0639a539a6af6c8ba4f49aa55c327da7 | |
parent | e1140244a6ce596a9a6d647b45e413896f6d806f (diff) | |
download | lanes-621cc9373c26d06ef4d88c66bbfb3aa2481b5d1b.tar.gz lanes-621cc9373c26d06ef4d88c66bbfb3aa2481b5d1b.tar.bz2 lanes-621cc9373c26d06ef4d88c66bbfb3aa2481b5d1b.zip |
Enable transfer of recursive upvalued functions
Removed a limitation preventing Lua functions with indirect recursive upvalue references from being transferable.
-rw-r--r-- | src/tools.c | 123 |
1 files changed, 55 insertions, 68 deletions
diff --git a/src/tools.c b/src/tools.c index 0206897..2629fd3 100644 --- a/src/tools.c +++ b/src/tools.c | |||
@@ -8,6 +8,7 @@ | |||
8 | =============================================================================== | 8 | =============================================================================== |
9 | 9 | ||
10 | Copyright (C) 2002-10 Asko Kauppi <akauppi@gmail.com> | 10 | Copyright (C) 2002-10 Asko Kauppi <akauppi@gmail.com> |
11 | 2011-13 benoit Germain <bnt.germain@gmail.com> | ||
11 | 12 | ||
12 | Permission is hereby granted, free of charge, to any person obtaining a copy | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy |
13 | of this software and associated documentation files (the "Software"), to deal | 14 | of this software and associated documentation files (the "Software"), to deal |
@@ -1129,74 +1130,47 @@ static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uin | |||
1129 | 1130 | ||
1130 | static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) | 1131 | static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) |
1131 | { | 1132 | { |
1132 | void * const aspointer = (void*)lua_topointer( L, i ); | 1133 | void * const aspointer = (void*)lua_topointer( L, i); |
1133 | // TBD: Merge this and same code for tables | 1134 | // TBD: Merge this and same code for tables |
1134 | ASSERT_L( L2_cache_i != 0 ); | 1135 | ASSERT_L( L2_cache_i != 0 ); |
1135 | 1136 | ||
1136 | STACK_GROW(L2,3); | 1137 | STACK_GROW( L2, 2); |
1137 | 1138 | ||
1138 | // L2_cache[id_str]= function | 1139 | // L2_cache[id_str]= function |
1139 | // | 1140 | // |
1140 | STACK_CHECK(L2) | 1141 | STACK_CHECK( L2) |
1141 | 1142 | ||
1142 | // We don't need to use the from state ('L') in ID since the life span | 1143 | // We don't need to use the from state ('L') in ID since the life span |
1143 | // is only for the duration of a copy (both states are locked). | 1144 | // is only for the duration of a copy (both states are locked). |
1144 | // | 1145 | // |
1145 | lua_pushlightuserdata( L2, aspointer); // push a light userdata uniquely representing the function | 1146 | |
1147 | // push a light userdata uniquely representing the function | ||
1148 | lua_pushlightuserdata( L2, aspointer); // ... {cache} ... p | ||
1146 | 1149 | ||
1147 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) ); | 1150 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) ); |
1148 | 1151 | ||
1149 | lua_pushvalue( L2, -1 ); | 1152 | lua_pushvalue( L2, -1 ); // ... {cache} ... p p |
1150 | lua_rawget( L2, L2_cache_i ); | 1153 | lua_rawget( L2, L2_cache_i ); // ... {cache} ... p function|nil|true |
1151 | // | ||
1152 | // [-2]: identity lightuserdata function pointer | ||
1153 | // [-1]: function|nil|true (true means: we're working on it; recursive) | ||
1154 | 1154 | ||
1155 | if (lua_isnil(L2,-1)) | 1155 | if( lua_isnil(L2,-1)) // function is unknown |
1156 | { | 1156 | { |
1157 | lua_pop(L2,1); | 1157 | lua_pop( L2, 1); // ... {cache} ... p |
1158 | 1158 | ||
1159 | // Set to 'true' for the duration of creation; need to find self-references | 1159 | // Set to 'true' for the duration of creation; need to find self-references |
1160 | // via upvalues | 1160 | // via upvalues |
1161 | // | 1161 | // |
1162 | lua_pushvalue( L2, -1); | 1162 | // pushes a copy of the func, a stores a reference in the cache |
1163 | lua_pushboolean(L2,TRUE); | 1163 | inter_copy_func( L2, L2_cache_i, L, i); // ... {cache} ... function |
1164 | lua_rawset( L2, L2_cache_i); | ||
1165 | |||
1166 | inter_copy_func( L2, L2_cache_i, L, i ); // pushes a copy of the func | ||
1167 | |||
1168 | lua_pushvalue(L2,-1); | ||
1169 | lua_insert(L2,-3); | ||
1170 | // | ||
1171 | // [-3]: function (2nd ref) | ||
1172 | // [-2]: identity lightuserdata function pointer | ||
1173 | // [-1]: function | ||
1174 | |||
1175 | lua_rawset(L2,L2_cache_i); | ||
1176 | // | ||
1177 | // [-1]: function (tied to 'L2_cache' table') | ||
1178 | |||
1179 | } | ||
1180 | else if (lua_isboolean(L2,-1)) | ||
1181 | { | ||
1182 | // Loop in preparing upvalues; either direct or via a table | ||
1183 | // | ||
1184 | // Note: This excludes the case where a function directly addresses | ||
1185 | // itself as an upvalue (recursive lane creation). | ||
1186 | // | ||
1187 | STACK_GROW(L,1); | ||
1188 | luaL_error( L, "Recursive use of upvalues; cannot copy the function" ); | ||
1189 | |||
1190 | } | 1164 | } |
1191 | else | 1165 | else // found function in the cache |
1192 | { | 1166 | { |
1193 | lua_remove(L2,-2); | 1167 | lua_remove( L2, -2); // ... {cache} ... function |
1194 | } | 1168 | } |
1195 | STACK_END(L2,1) | 1169 | STACK_END( L2, 1) |
1196 | // | 1170 | // |
1197 | // L2 [-1]: function | 1171 | // L2 [-1]: function |
1198 | 1172 | ||
1199 | ASSERT_L( lua_isfunction(L2,-1)); | 1173 | ASSERT_L( lua_isfunction( L2, -1)); |
1200 | } | 1174 | } |
1201 | 1175 | ||
1202 | /* | 1176 | /* |
@@ -1360,20 +1334,23 @@ static void lookup_native_func( lua_State* L2, lua_State* L, uint_t i) | |||
1360 | #define LOG_FUNC_INFO_CODE(_code) | 1334 | #define LOG_FUNC_INFO_CODE(_code) |
1361 | #endif // LOG_FUNC_INFO | 1335 | #endif // LOG_FUNC_INFO |
1362 | 1336 | ||
1337 | LOG_FUNC_INFO_CODE( static char const* s_indent = "----+----!----+----!----+----!----+----!----+----!----+----!----+----!----+"); | ||
1338 | |||
1363 | /* | 1339 | /* |
1364 | * Copy a function over, which has not been found in the cache. | 1340 | * Copy a function over, which has not been found in the cache. |
1341 | * L2 has the cache key for this function at the top of the stack | ||
1365 | */ | 1342 | */ |
1366 | enum e_vt { | 1343 | enum e_vt { |
1367 | VT_NORMAL, VT_KEY, VT_METATABLE | 1344 | VT_NORMAL, VT_KEY, VT_METATABLE |
1368 | }; | 1345 | }; |
1369 | static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i, enum e_vt value_type ); | 1346 | static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i, enum e_vt value_type ); |
1370 | 1347 | ||
1371 | static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) | 1348 | static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i) |
1372 | { | 1349 | { |
1373 | FuncSubType funcSubType; | 1350 | FuncSubType funcSubType; |
1374 | lua_CFunction cfunc = luaG_tocfunction( L, i, &funcSubType); // NULL for LuaJIT-fast && bytecode functions | 1351 | lua_CFunction cfunc = luaG_tocfunction( L, i, &funcSubType); // NULL for LuaJIT-fast && bytecode functions |
1375 | 1352 | ||
1376 | ASSERT_L( L2_cache_i != 0 ); | 1353 | ASSERT_L( L2_cache_i != 0); // ... {cache} ... p |
1377 | STACK_GROW(L,2); | 1354 | STACK_GROW(L,2); |
1378 | STACK_CHECK(L) | 1355 | STACK_CHECK(L) |
1379 | 1356 | ||
@@ -1407,41 +1384,47 @@ static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uin | |||
1407 | 1384 | ||
1408 | // transfer the bytecode, then the upvalues, to create a similar closure | 1385 | // transfer the bytecode, then the upvalues, to create a similar closure |
1409 | { | 1386 | { |
1410 | const char *name= NULL; | 1387 | char const* name = NULL; |
1411 | 1388 | ||
1412 | #if LOG_FUNC_INFO | 1389 | #if LOG_FUNC_INFO |
1413 | // "To get information about a function you push it onto the | 1390 | // "To get information about a function you push it onto the |
1414 | // stack and start the what string with the character '>'." | 1391 | // stack and start the what string with the character '>'." |
1415 | // | 1392 | // |
1416 | { | 1393 | { |
1417 | lua_Debug ar; | 1394 | lua_Debug ar; |
1418 | lua_pushvalue( L, i ); | 1395 | lua_pushvalue( L, i); |
1419 | lua_getinfo(L, ">nS", &ar); // fills 'name' 'namewhat' and 'linedefined', pops function | 1396 | lua_getinfo(L, ">nS", &ar); // fills 'name' 'namewhat' and 'linedefined', pops function |
1420 | name= ar.namewhat; | 1397 | name = ar.namewhat; |
1421 | fprintf( stderr, "NAME: %s @ %d\n", ar.short_src, ar.linedefined); // just gives NULL | 1398 | fprintf( stderr, "%.*sFNAME: %s @ %d\n", i, s_indent, ar.short_src, ar.linedefined); // just gives NULL |
1422 | } | 1399 | } |
1423 | #endif // LOG_FUNC_INFO | 1400 | #endif // LOG_FUNC_INFO |
1424 | { | 1401 | { |
1425 | const char *s; | ||
1426 | size_t sz; | 1402 | size_t sz; |
1427 | s = lua_tolstring( L, -1, &sz); | 1403 | char const* s = lua_tolstring( L, -1, &sz); |
1428 | ASSERT_L( s && sz); | 1404 | ASSERT_L( s && sz); |
1429 | 1405 | STACK_GROW( L2, 2); | |
1430 | // Note: Line numbers seem to be taken precisely from the | 1406 | // Note: Line numbers seem to be taken precisely from the |
1431 | // original function. 'name' is not used since the chunk | 1407 | // original function. 'name' is not used since the chunk |
1432 | // is precompiled (it seems...). | 1408 | // is precompiled (it seems...). |
1433 | // | 1409 | // |
1434 | // TBD: Can we get the function's original name through, as well? | 1410 | // TBD: Can we get the function's original name through, as well? |
1435 | // | 1411 | // |
1436 | if (luaL_loadbuffer(L2, s, sz, name) != 0) | 1412 | if( luaL_loadbuffer( L2, s, sz, name) != 0) // ... {cache} ... p function |
1437 | { | 1413 | { |
1438 | // chunk is precompiled so only LUA_ERRMEM can happen | 1414 | // chunk is precompiled so only LUA_ERRMEM can happen |
1439 | // "Otherwise, it pushes an error message" | 1415 | // "Otherwise, it pushes an error message" |
1440 | // | 1416 | // |
1441 | STACK_GROW( L,1); | 1417 | STACK_GROW( L, 1); |
1442 | luaL_error( L, "%s", lua_tostring(L2,-1)); | 1418 | luaL_error( L, "%s", lua_tostring( L2, -1)); |
1443 | } | 1419 | } |
1444 | lua_pop( L, 1); // remove the dumped string | 1420 | lua_pop( L, 1); // remove the dumped string |
1421 | // now set the cache as soon as we can. | ||
1422 | // this is necessary if one of the function's upvalues references it indirectly | ||
1423 | // we need to find it in the cache even if it isn't fully transfered yet | ||
1424 | lua_insert( L2, -2); // ... {cache} ... function p | ||
1425 | lua_pushvalue( L2, -2); // ... {cache} ... function p function | ||
1426 | // cache[p] = function | ||
1427 | lua_rawset( L2, L2_cache_i); // ... {cache} ... function | ||
1445 | } | 1428 | } |
1446 | STACK_MID( L, 0) | 1429 | STACK_MID( L, 0) |
1447 | 1430 | ||
@@ -1452,16 +1435,16 @@ static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uin | |||
1452 | LOG_FUNC_INFO_CODE( char const* upname); | 1435 | LOG_FUNC_INFO_CODE( char const* upname); |
1453 | for( n = 0; (LOG_FUNC_INFO_CODE( upname =) lua_getupvalue( L, i, 1 + n)) != NULL; ++ n) | 1436 | for( n = 0; (LOG_FUNC_INFO_CODE( upname =) lua_getupvalue( L, i, 1 + n)) != NULL; ++ n) |
1454 | { | 1437 | { |
1455 | LOG_FUNC_INFO_CODE( fprintf( stderr, "UPNAME: %s\n", upname)); | 1438 | LOG_FUNC_INFO_CODE( fprintf( stderr, "%.*sUPNAME[%d]: %s\n", i, s_indent, n, upname)); |
1456 | if( (!cfunc) && lua_equal( L, i, -1)) | 1439 | // v 3.4.2: we now longer need to handle this special case, because the general mechanism can take care of it just fine |
1440 | /*if( (!cfunc) && lua_equal( L, i, -1)) | ||
1457 | { | 1441 | { |
1458 | /* Lua closure that has a (recursive) upvalue to itself | 1442 | // Lua closure that has a (recursive) upvalue to itself |
1459 | */ | 1443 | lua_pushvalue( L2, -n - 1); // ... {cache} ... function upvalues... |
1460 | lua_pushvalue( L2, -n - 1); | ||
1461 | } | 1444 | } |
1462 | else | 1445 | else*/ |
1463 | { | 1446 | { |
1464 | if( !inter_copy_one_( L2, L2_cache_i, L, lua_gettop(L), VT_NORMAL)) | 1447 | if( !inter_copy_one_( L2, L2_cache_i, L, lua_gettop( L), VT_NORMAL)) |
1465 | luaL_error( L, "Cannot copy upvalue type '%s'", luaL_typename( L, -1)); | 1448 | luaL_error( L, "Cannot copy upvalue type '%s'", luaL_typename( L, -1)); |
1466 | } | 1449 | } |
1467 | lua_pop( L, 1); | 1450 | lua_pop( L, 1); |
@@ -1476,21 +1459,24 @@ static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uin | |||
1476 | int func_index = lua_gettop( L2) - n; | 1459 | int func_index = lua_gettop( L2) - n; |
1477 | for( ; n > 0; -- n) | 1460 | for( ; n > 0; -- n) |
1478 | { | 1461 | { |
1479 | char const* rc = lua_setupvalue( L2, func_index, n); | 1462 | char const* rc = lua_setupvalue( L2, func_index, n); // ... {cache} ... function |
1480 | // | 1463 | // |
1481 | // "assigns the value at the top of the stack to the upvalue and returns its name. | 1464 | // "assigns the value at the top of the stack to the upvalue and returns its name. |
1482 | // It also pops the value from the stack." | 1465 | // It also pops the value from the stack." |
1483 | 1466 | ||
1484 | ASSERT_L( rc); // not having enough slots? | 1467 | ASSERT_L( rc); // not having enough slots? |
1485 | } | 1468 | } |
1469 | // once all upvalues have been set we are left | ||
1470 | // with the function at the top of the stack // ... {cache} ... function | ||
1486 | } | 1471 | } |
1487 | } | 1472 | } |
1488 | } | 1473 | } |
1489 | else // C function OR LuaJIT fast function!!! | 1474 | else // C function OR LuaJIT fast function!!! |
1490 | { | 1475 | { |
1491 | LOG_FUNC_INFO_CODE( fprintf( stderr, "NAME: [C] function %p \n", cfunc)); | 1476 | lua_pop( L2, 1); // ... {cache} ... |
1477 | LOG_FUNC_INFO_CODE( fprintf( stderr, "%.*sFNAME: [C] function %p \n", i, s_indent, cfunc)); | ||
1492 | // No need to transfer upvalues for C/JIT functions since they weren't actually copied, only looked up | 1478 | // No need to transfer upvalues for C/JIT functions since they weren't actually copied, only looked up |
1493 | lookup_native_func( L2, L, i); | 1479 | lookup_native_func( L2, L, i); // ... {cache} ... function |
1494 | } | 1480 | } |
1495 | STACK_END( L, 0) | 1481 | STACK_END( L, 0) |
1496 | } | 1482 | } |
@@ -1533,6 +1519,7 @@ static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, u | |||
1533 | 1519 | ||
1534 | case LUA_TSTRING: { | 1520 | case LUA_TSTRING: { |
1535 | size_t len; const char *s = lua_tolstring( L, i, &len ); | 1521 | size_t len; const char *s = lua_tolstring( L, i, &len ); |
1522 | LOG_FUNC_INFO_CODE( if( vt == VT_KEY) fprintf( stderr, "%.*sKEY: %s\n", i, s_indent, s)); | ||
1536 | lua_pushlstring( L2, s, len ); | 1523 | lua_pushlstring( L2, s, len ); |
1537 | } break; | 1524 | } break; |
1538 | 1525 | ||