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 /src | |
| 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.
Diffstat (limited to 'src')
| -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 | ||
