diff options
| -rw-r--r-- | lauxlib.c | 28 | ||||
| -rw-r--r-- | lstate.c | 3 | ||||
| -rw-r--r-- | ltests.c | 28 | ||||
| -rw-r--r-- | lua.h | 5 | ||||
| -rw-r--r-- | manual/manual.of | 26 | ||||
| -rw-r--r-- | testes/api.lua | 37 | ||||
| -rw-r--r-- | testes/coroutine.lua | 8 |
7 files changed, 92 insertions, 43 deletions
| @@ -672,13 +672,10 @@ LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) { | |||
| 672 | ** ======================================================= | 672 | ** ======================================================= |
| 673 | */ | 673 | */ |
| 674 | 674 | ||
| 675 | /* index of free-list header (after the predefined values) */ | ||
| 676 | #define freelist (LUA_RIDX_LAST + 1) | ||
| 677 | |||
| 678 | /* | 675 | /* |
| 679 | ** The previously freed references form a linked list: | 676 | ** The previously freed references form a linked list: t[1] is the index |
| 680 | ** t[freelist] is the index of a first free index, or zero if list is | 677 | ** of a first free index, t[t[1]] is the index of the second element, |
| 681 | ** empty; t[t[freelist]] is the index of the second element; etc. | 678 | ** etc. A zero signals the end of the list. |
| 682 | */ | 679 | */ |
| 683 | LUALIB_API int luaL_ref (lua_State *L, int t) { | 680 | LUALIB_API int luaL_ref (lua_State *L, int t) { |
| 684 | int ref; | 681 | int ref; |
| @@ -687,19 +684,18 @@ LUALIB_API int luaL_ref (lua_State *L, int t) { | |||
| 687 | return LUA_REFNIL; /* 'nil' has a unique fixed reference */ | 684 | return LUA_REFNIL; /* 'nil' has a unique fixed reference */ |
| 688 | } | 685 | } |
| 689 | t = lua_absindex(L, t); | 686 | t = lua_absindex(L, t); |
| 690 | if (lua_rawgeti(L, t, freelist) == LUA_TNIL) { /* first access? */ | 687 | if (lua_rawgeti(L, t, 1) == LUA_TNUMBER) /* already initialized? */ |
| 688 | ref = (int)lua_tointeger(L, -1); /* ref = t[1] */ | ||
| 689 | else { /* first access */ | ||
| 690 | lua_assert(!lua_toboolean(L, -1)); /* must be nil or false */ | ||
| 691 | ref = 0; /* list is empty */ | 691 | ref = 0; /* list is empty */ |
| 692 | lua_pushinteger(L, 0); /* initialize as an empty list */ | 692 | lua_pushinteger(L, 0); /* initialize as an empty list */ |
| 693 | lua_rawseti(L, t, freelist); /* ref = t[freelist] = 0 */ | 693 | lua_rawseti(L, t, 1); /* ref = t[1] = 0 */ |
| 694 | } | ||
| 695 | else { /* already initialized */ | ||
| 696 | lua_assert(lua_isinteger(L, -1)); | ||
| 697 | ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */ | ||
| 698 | } | 694 | } |
| 699 | lua_pop(L, 1); /* remove element from stack */ | 695 | lua_pop(L, 1); /* remove element from stack */ |
| 700 | if (ref != 0) { /* any free element? */ | 696 | if (ref != 0) { /* any free element? */ |
| 701 | lua_rawgeti(L, t, ref); /* remove it from list */ | 697 | lua_rawgeti(L, t, ref); /* remove it from list */ |
| 702 | lua_rawseti(L, t, freelist); /* (t[freelist] = t[ref]) */ | 698 | lua_rawseti(L, t, 1); /* (t[1] = t[ref]) */ |
| 703 | } | 699 | } |
| 704 | else /* no free elements */ | 700 | else /* no free elements */ |
| 705 | ref = (int)lua_rawlen(L, t) + 1; /* get a new reference */ | 701 | ref = (int)lua_rawlen(L, t) + 1; /* get a new reference */ |
| @@ -711,11 +707,11 @@ LUALIB_API int luaL_ref (lua_State *L, int t) { | |||
| 711 | LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { | 707 | LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { |
| 712 | if (ref >= 0) { | 708 | if (ref >= 0) { |
| 713 | t = lua_absindex(L, t); | 709 | t = lua_absindex(L, t); |
| 714 | lua_rawgeti(L, t, freelist); | 710 | lua_rawgeti(L, t, 1); |
| 715 | lua_assert(lua_isinteger(L, -1)); | 711 | lua_assert(lua_isinteger(L, -1)); |
| 716 | lua_rawseti(L, t, ref); /* t[ref] = t[freelist] */ | 712 | lua_rawseti(L, t, ref); /* t[ref] = t[1] */ |
| 717 | lua_pushinteger(L, ref); | 713 | lua_pushinteger(L, ref); |
| 718 | lua_rawseti(L, t, freelist); /* t[freelist] = ref */ | 714 | lua_rawseti(L, t, 1); /* t[1] = ref */ |
| 719 | } | 715 | } |
| 720 | } | 716 | } |
| 721 | 717 | ||
| @@ -189,6 +189,9 @@ static void init_registry (lua_State *L, global_State *g) { | |||
| 189 | Table *registry = luaH_new(L); | 189 | Table *registry = luaH_new(L); |
| 190 | sethvalue(L, &g->l_registry, registry); | 190 | sethvalue(L, &g->l_registry, registry); |
| 191 | luaH_resize(L, registry, LUA_RIDX_LAST, 0); | 191 | luaH_resize(L, registry, LUA_RIDX_LAST, 0); |
| 192 | /* registry[1] = false */ | ||
| 193 | setbfvalue(&aux); | ||
| 194 | luaH_setint(L, registry, 1, &aux); | ||
| 192 | /* registry[LUA_RIDX_MAINTHREAD] = L */ | 195 | /* registry[LUA_RIDX_MAINTHREAD] = L */ |
| 193 | setthvalue(L, &aux, L); | 196 | setthvalue(L, &aux, L); |
| 194 | luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &aux); | 197 | luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &aux); |
| @@ -1084,27 +1084,39 @@ static int string_query (lua_State *L) { | |||
| 1084 | } | 1084 | } |
| 1085 | 1085 | ||
| 1086 | 1086 | ||
| 1087 | static int getreftable (lua_State *L) { | ||
| 1088 | if (lua_istable(L, 2)) /* is there a table as second argument? */ | ||
| 1089 | return 2; /* use it as the table */ | ||
| 1090 | else | ||
| 1091 | return LUA_REGISTRYINDEX; /* default is to use the register */ | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | |||
| 1087 | static int tref (lua_State *L) { | 1095 | static int tref (lua_State *L) { |
| 1096 | int t = getreftable(L); | ||
| 1088 | int level = lua_gettop(L); | 1097 | int level = lua_gettop(L); |
| 1089 | luaL_checkany(L, 1); | 1098 | luaL_checkany(L, 1); |
| 1090 | lua_pushvalue(L, 1); | 1099 | lua_pushvalue(L, 1); |
| 1091 | lua_pushinteger(L, luaL_ref(L, LUA_REGISTRYINDEX)); | 1100 | lua_pushinteger(L, luaL_ref(L, t)); |
| 1092 | cast_void(level); /* to avoid warnings */ | 1101 | cast_void(level); /* to avoid warnings */ |
| 1093 | lua_assert(lua_gettop(L) == level+1); /* +1 for result */ | 1102 | lua_assert(lua_gettop(L) == level+1); /* +1 for result */ |
| 1094 | return 1; | 1103 | return 1; |
| 1095 | } | 1104 | } |
| 1096 | 1105 | ||
| 1106 | |||
| 1097 | static int getref (lua_State *L) { | 1107 | static int getref (lua_State *L) { |
| 1108 | int t = getreftable(L); | ||
| 1098 | int level = lua_gettop(L); | 1109 | int level = lua_gettop(L); |
| 1099 | lua_rawgeti(L, LUA_REGISTRYINDEX, luaL_checkinteger(L, 1)); | 1110 | lua_rawgeti(L, t, luaL_checkinteger(L, 1)); |
| 1100 | cast_void(level); /* to avoid warnings */ | 1111 | cast_void(level); /* to avoid warnings */ |
| 1101 | lua_assert(lua_gettop(L) == level+1); | 1112 | lua_assert(lua_gettop(L) == level+1); |
| 1102 | return 1; | 1113 | return 1; |
| 1103 | } | 1114 | } |
| 1104 | 1115 | ||
| 1105 | static int unref (lua_State *L) { | 1116 | static int unref (lua_State *L) { |
| 1117 | int t = getreftable(L); | ||
| 1106 | int level = lua_gettop(L); | 1118 | int level = lua_gettop(L); |
| 1107 | luaL_unref(L, LUA_REGISTRYINDEX, cast_int(luaL_checkinteger(L, 1))); | 1119 | luaL_unref(L, t, cast_int(luaL_checkinteger(L, 1))); |
| 1108 | cast_void(level); /* to avoid warnings */ | 1120 | cast_void(level); /* to avoid warnings */ |
| 1109 | lua_assert(lua_gettop(L) == level); | 1121 | lua_assert(lua_gettop(L) == level); |
| 1110 | return 0; | 1122 | return 0; |
| @@ -1373,6 +1385,16 @@ static int getnum_aux (lua_State *L, lua_State *L1, const char **pc) { | |||
| 1373 | (*pc)++; | 1385 | (*pc)++; |
| 1374 | return res; | 1386 | return res; |
| 1375 | } | 1387 | } |
| 1388 | else if (**pc == '!') { | ||
| 1389 | (*pc)++; | ||
| 1390 | if (**pc == 'G') | ||
| 1391 | res = LUA_RIDX_GLOBALS; | ||
| 1392 | else if (**pc == 'M') | ||
| 1393 | res = LUA_RIDX_MAINTHREAD; | ||
| 1394 | else lua_assert(0); | ||
| 1395 | (*pc)++; | ||
| 1396 | return res; | ||
| 1397 | } | ||
| 1376 | else if (**pc == '-') { | 1398 | else if (**pc == '-') { |
| 1377 | sig = -1; | 1399 | sig = -1; |
| 1378 | (*pc)++; | 1400 | (*pc)++; |
| @@ -80,9 +80,10 @@ typedef struct lua_State lua_State; | |||
| 80 | 80 | ||
| 81 | 81 | ||
| 82 | /* predefined values in the registry */ | 82 | /* predefined values in the registry */ |
| 83 | #define LUA_RIDX_MAINTHREAD 1 | 83 | /* index 1 is reserved for the reference mechanism */ |
| 84 | #define LUA_RIDX_GLOBALS 2 | 84 | #define LUA_RIDX_GLOBALS 2 |
| 85 | #define LUA_RIDX_LAST LUA_RIDX_GLOBALS | 85 | #define LUA_RIDX_MAINTHREAD 3 |
| 86 | #define LUA_RIDX_LAST 3 | ||
| 86 | 87 | ||
| 87 | 88 | ||
| 88 | /* type of numbers in Lua */ | 89 | /* type of numbers in Lua */ |
diff --git a/manual/manual.of b/manual/manual.of index ae38d7c6..64bb5473 100644 --- a/manual/manual.of +++ b/manual/manual.of | |||
| @@ -2645,8 +2645,8 @@ string keys starting with an underscore followed by | |||
| 2645 | uppercase letters are reserved for Lua. | 2645 | uppercase letters are reserved for Lua. |
| 2646 | 2646 | ||
| 2647 | The integer keys in the registry are used | 2647 | The integer keys in the registry are used |
| 2648 | by the reference mechanism @seeC{luaL_ref} | 2648 | by the reference mechanism @seeC{luaL_ref}, |
| 2649 | and by some predefined values. | 2649 | with some predefined values. |
| 2650 | Therefore, integer keys in the registry | 2650 | Therefore, integer keys in the registry |
| 2651 | must not be used for other purposes. | 2651 | must not be used for other purposes. |
| 2652 | 2652 | ||
| @@ -6018,11 +6018,21 @@ Creates and returns a @def{reference}, | |||
| 6018 | in the table at index @id{t}, | 6018 | in the table at index @id{t}, |
| 6019 | for the object on the top of the stack (and pops the object). | 6019 | for the object on the top of the stack (and pops the object). |
| 6020 | 6020 | ||
| 6021 | A reference is a unique integer key. | 6021 | The reference system uses the integer keys of the table. |
| 6022 | As long as you do not manually add integer keys into the table @id{t}, | 6022 | A reference is a unique integer key; |
| 6023 | @Lid{luaL_ref} ensures the uniqueness of the key it returns. | 6023 | @Lid{luaL_ref} ensures the uniqueness of the keys it returns. |
| 6024 | The entry 1 is reserved for internal use. | ||
| 6025 | Before the first use of @Lid{luaL_ref}, | ||
| 6026 | the integer keys of the table | ||
| 6027 | should form a proper sequence (no holes), | ||
| 6028 | and the value at entry 1 should be false: | ||
| 6029 | @nil if the sequence is empty, | ||
| 6030 | @false otherwise. | ||
| 6031 | You should not manually set integer keys in the table | ||
| 6032 | after the first use of @Lid{luaL_ref}. | ||
| 6033 | |||
| 6024 | You can retrieve an object referred by the reference @id{r} | 6034 | You can retrieve an object referred by the reference @id{r} |
| 6025 | by calling @T{lua_rawgeti(L, t, r)}. | 6035 | by calling @T{lua_rawgeti(L, t, r)} or @T{lua_geti(L, t, r)}. |
| 6026 | The function @Lid{luaL_unref} frees a reference. | 6036 | The function @Lid{luaL_unref} frees a reference. |
| 6027 | 6037 | ||
| 6028 | If the object on the top of the stack is @nil, | 6038 | If the object on the top of the stack is @nil, |
| @@ -6188,8 +6198,8 @@ Returns the name of the type of the value at the given index. | |||
| 6188 | Releases the reference @id{ref} from the table at index @id{t} | 6198 | Releases the reference @id{ref} from the table at index @id{t} |
| 6189 | @seeC{luaL_ref}. | 6199 | @seeC{luaL_ref}. |
| 6190 | The entry is removed from the table, | 6200 | The entry is removed from the table, |
| 6191 | so that the referred object can be collected. | 6201 | so that the referred object can be collected and |
| 6192 | The reference @id{ref} is also freed to be used again. | 6202 | the reference @id{ref} can be used again by @Lid{luaL_ref}. |
| 6193 | 6203 | ||
| 6194 | If @id{ref} is @Lid{LUA_NOREF} or @Lid{LUA_REFNIL}, | 6204 | If @id{ref} is @Lid{LUA_NOREF} or @Lid{LUA_REFNIL}, |
| 6195 | @Lid{luaL_unref} does nothing. | 6205 | @Lid{luaL_unref} does nothing. |
diff --git a/testes/api.lua b/testes/api.lua index 85dadb69..ca4b3fb4 100644 --- a/testes/api.lua +++ b/testes/api.lua | |||
| @@ -467,7 +467,7 @@ for i = 1,lim do | |||
| 467 | prog[#prog + 1] = "pushnum " .. i * 10 | 467 | prog[#prog + 1] = "pushnum " .. i * 10 |
| 468 | end | 468 | end |
| 469 | 469 | ||
| 470 | prog[#prog + 1] = "rawgeti R 2" -- get global table in registry | 470 | prog[#prog + 1] = "rawgeti R !G" -- get global table in registry |
| 471 | prog[#prog + 1] = "insert " .. -(2*lim + 2) | 471 | prog[#prog + 1] = "insert " .. -(2*lim + 2) |
| 472 | 472 | ||
| 473 | for i = 1,lim do | 473 | for i = 1,lim do |
| @@ -930,28 +930,30 @@ checkerr("FILE%* expected, got userdata", io.input, x) | |||
| 930 | 930 | ||
| 931 | assert(debug.getmetatable(x) == nil and debug.getmetatable(y) == nil) | 931 | assert(debug.getmetatable(x) == nil and debug.getmetatable(y) == nil) |
| 932 | 932 | ||
| 933 | local d = T.ref(a); | 933 | -- Test references in an arbitrary table |
| 934 | local e = T.ref(b); | 934 | local reftable = {} |
| 935 | local f = T.ref(c); | 935 | local d = T.ref(a, reftable); |
| 936 | t = {T.getref(d), T.getref(e), T.getref(f)} | 936 | local e = T.ref(b, reftable); |
| 937 | local f = T.ref(c, reftable); | ||
| 938 | t = {T.getref(d, reftable), T.getref(e, reftable), T.getref(f, reftable)} | ||
| 937 | assert(t[1] == a and t[2] == b and t[3] == c) | 939 | assert(t[1] == a and t[2] == b and t[3] == c) |
| 938 | 940 | ||
| 939 | t=nil; a=nil; c=nil; | 941 | t=nil; a=nil; c=nil; |
| 940 | T.unref(e); T.unref(f) | 942 | T.unref(e, reftable); T.unref(f, reftable) |
| 941 | 943 | ||
| 942 | collectgarbage() | 944 | collectgarbage() |
| 943 | 945 | ||
| 944 | -- check that unref objects have been collected | 946 | -- check that unref objects have been collected |
| 945 | assert(#cl == 1 and cl[1] == nc) | 947 | assert(#cl == 1 and cl[1] == nc) |
| 946 | 948 | ||
| 947 | x = T.getref(d) | 949 | x = T.getref(d, reftable) |
| 948 | assert(type(x) == 'userdata' and debug.getmetatable(x) == tt) | 950 | assert(type(x) == 'userdata' and debug.getmetatable(x) == tt) |
| 949 | x =nil | 951 | x =nil |
| 950 | tt.b = b -- create cycle | 952 | tt.b = b -- create cycle |
| 951 | tt=nil -- frees tt for GC | 953 | tt=nil -- frees tt for GC |
| 952 | A = nil | 954 | A = nil |
| 953 | b = nil | 955 | b = nil |
| 954 | T.unref(d); | 956 | T.unref(d, reftable); |
| 955 | local n5 = T.newuserdata(0) | 957 | local n5 = T.newuserdata(0) |
| 956 | debug.setmetatable(n5, {__gc=F}) | 958 | debug.setmetatable(n5, {__gc=F}) |
| 957 | n5 = T.udataval(n5) | 959 | n5 = T.udataval(n5) |
| @@ -960,6 +962,21 @@ assert(#cl == 4) | |||
| 960 | -- check order of collection | 962 | -- check order of collection |
| 961 | assert(cl[2] == n5 and cl[3] == nb and cl[4] == na) | 963 | assert(cl[2] == n5 and cl[3] == nb and cl[4] == na) |
| 962 | 964 | ||
| 965 | -- reuse a reference in 'reftable' | ||
| 966 | T.unref(T.ref(23, reftable), reftable) | ||
| 967 | |||
| 968 | do -- check reftable | ||
| 969 | local count = 0 | ||
| 970 | local i = 1 | ||
| 971 | while reftable[i] ~= 0 do | ||
| 972 | i = reftable[i] -- traverse linked list of free references | ||
| 973 | count = count + 1 | ||
| 974 | end | ||
| 975 | -- maximum number of simultaneously locked objects was 3 | ||
| 976 | assert(count == 3 and #reftable == 3 + 1) -- +1 for reserved [1] | ||
| 977 | end | ||
| 978 | |||
| 979 | |||
| 963 | collectgarbage"restart" | 980 | collectgarbage"restart" |
| 964 | 981 | ||
| 965 | 982 | ||
| @@ -1363,8 +1380,8 @@ end) | |||
| 1363 | 1380 | ||
| 1364 | -- testing threads | 1381 | -- testing threads |
| 1365 | 1382 | ||
| 1366 | -- get main thread from registry (at index LUA_RIDX_MAINTHREAD == 1) | 1383 | -- get main thread from registry |
| 1367 | local mt = T.testC("rawgeti R 1; return 1") | 1384 | local mt = T.testC("rawgeti R !M; return 1") |
| 1368 | assert(type(mt) == "thread" and coroutine.running() == mt) | 1385 | assert(type(mt) == "thread" and coroutine.running() == mt) |
| 1369 | 1386 | ||
| 1370 | 1387 | ||
diff --git a/testes/coroutine.lua b/testes/coroutine.lua index 990da8c4..6c15db03 100644 --- a/testes/coroutine.lua +++ b/testes/coroutine.lua | |||
| @@ -681,7 +681,7 @@ else | |||
| 681 | c == "ERRRUN" and d == 4) | 681 | c == "ERRRUN" and d == 4) |
| 682 | 682 | ||
| 683 | a, b, c, d = T.testC([[ | 683 | a, b, c, d = T.testC([[ |
| 684 | rawgeti R 1 # get main thread | 684 | rawgeti R !M # get main thread |
| 685 | pushnum 10; | 685 | pushnum 10; |
| 686 | pushnum 20; | 686 | pushnum 20; |
| 687 | resume -3 2; | 687 | resume -3 2; |
| @@ -699,7 +699,7 @@ else | |||
| 699 | assert(T.testC(state, "newthread; isyieldable -1; remove 1; return 1")) | 699 | assert(T.testC(state, "newthread; isyieldable -1; remove 1; return 1")) |
| 700 | 700 | ||
| 701 | -- main thread is not yieldable | 701 | -- main thread is not yieldable |
| 702 | assert(not T.testC(state, "rawgeti R 1; isyieldable -1; remove 1; return 1")) | 702 | assert(not T.testC(state, "rawgeti R !M; isyieldable -1; remove 1; return 1")) |
| 703 | 703 | ||
| 704 | T.testC(state, "settop 0") | 704 | T.testC(state, "settop 0") |
| 705 | 705 | ||
| @@ -711,7 +711,7 @@ else | |||
| 711 | return 'ok']])) | 711 | return 'ok']])) |
| 712 | 712 | ||
| 713 | local t = table.pack(T.testC(state, [[ | 713 | local t = table.pack(T.testC(state, [[ |
| 714 | rawgeti R 1 # get main thread | 714 | rawgeti R !M # get main thread |
| 715 | pushstring 'XX' | 715 | pushstring 'XX' |
| 716 | getglobal X # get function for body | 716 | getglobal X # get function for body |
| 717 | pushstring AA # arg | 717 | pushstring AA # arg |
| @@ -720,7 +720,7 @@ else | |||
| 720 | setglobal T # top | 720 | setglobal T # top |
| 721 | setglobal B # second yielded value | 721 | setglobal B # second yielded value |
| 722 | setglobal A # fist yielded value | 722 | setglobal A # fist yielded value |
| 723 | rawgeti R 1 # get main thread | 723 | rawgeti R !M # get main thread |
| 724 | pushnum 5 # arg (noise) | 724 | pushnum 5 # arg (noise) |
| 725 | resume 1 1 # after coroutine ends, previous stack is back | 725 | resume 1 1 # after coroutine ends, previous stack is back |
| 726 | pushstatus | 726 | pushstatus |
