mirror of
https://github.com/lua/lua.git
synced 2025-01-14 05:43:00 +08:00
A to-be-closed variable must have a closable value (or be nil)
It is an error for a to-be-closed variable to have a non-closable non-nil value when it is being closed. This situation does not seem to be useful and often hints to an error. (Particularly in the C API, it is easy to change a to-be-closed index by mistake.)
This commit is contained in:
parent
7696c6474f
commit
6d04537ea6
2
lapi.c
2
lapi.c
@ -1299,7 +1299,7 @@ static const char *aux_upvalue (TValue *fi, int n, TValue **val,
|
|||||||
*val = f->upvals[n-1]->v;
|
*val = f->upvals[n-1]->v;
|
||||||
if (owner) *owner = obj2gco(f->upvals[n - 1]);
|
if (owner) *owner = obj2gco(f->upvals[n - 1]);
|
||||||
name = p->upvalues[n-1].name;
|
name = p->upvalues[n-1].name;
|
||||||
return (name == NULL) ? "(*no name)" : getstr(name);
|
return (name == NULL) ? "(no name)" : getstr(name);
|
||||||
}
|
}
|
||||||
default: return NULL; /* not a closure */
|
default: return NULL; /* not a closure */
|
||||||
}
|
}
|
||||||
|
18
ldebug.c
18
ldebug.c
@ -192,15 +192,14 @@ static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
|
|||||||
int nextra = ci->u.l.nextraargs;
|
int nextra = ci->u.l.nextraargs;
|
||||||
if (n <= nextra) {
|
if (n <= nextra) {
|
||||||
*pos = ci->func - nextra + (n - 1);
|
*pos = ci->func - nextra + (n - 1);
|
||||||
return "(*vararg)"; /* generic name for any vararg */
|
return "(vararg)"; /* generic name for any vararg */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL; /* no such vararg */
|
return NULL; /* no such vararg */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static const char *findlocal (lua_State *L, CallInfo *ci, int n,
|
const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) {
|
||||||
StkId *pos) {
|
|
||||||
StkId base = ci->func + 1;
|
StkId base = ci->func + 1;
|
||||||
const char *name = NULL;
|
const char *name = NULL;
|
||||||
if (isLua(ci)) {
|
if (isLua(ci)) {
|
||||||
@ -211,12 +210,15 @@ static const char *findlocal (lua_State *L, CallInfo *ci, int n,
|
|||||||
}
|
}
|
||||||
if (name == NULL) { /* no 'standard' name? */
|
if (name == NULL) { /* no 'standard' name? */
|
||||||
StkId limit = (ci == L->ci) ? L->top : ci->next->func;
|
StkId limit = (ci == L->ci) ? L->top : ci->next->func;
|
||||||
if (limit - base >= n && n > 0) /* is 'n' inside 'ci' stack? */
|
if (limit - base >= n && n > 0) { /* is 'n' inside 'ci' stack? */
|
||||||
name = "(*temporary)"; /* generic name for any valid slot */
|
/* generic name for any valid slot */
|
||||||
|
name = isLua(ci) ? "(temporary)" : "(C temporary)";
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return NULL; /* no name */
|
return NULL; /* no name */
|
||||||
}
|
}
|
||||||
*pos = base + (n - 1);
|
if (pos)
|
||||||
|
*pos = base + (n - 1);
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,7 +234,7 @@ LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {
|
|||||||
}
|
}
|
||||||
else { /* active function; get information through 'ar' */
|
else { /* active function; get information through 'ar' */
|
||||||
StkId pos = NULL; /* to avoid warnings */
|
StkId pos = NULL; /* to avoid warnings */
|
||||||
name = findlocal(L, ar->i_ci, n, &pos);
|
name = luaG_findlocal(L, ar->i_ci, n, &pos);
|
||||||
if (name) {
|
if (name) {
|
||||||
setobjs2s(L, L->top, pos);
|
setobjs2s(L, L->top, pos);
|
||||||
api_incr_top(L);
|
api_incr_top(L);
|
||||||
@ -247,7 +249,7 @@ LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
|
|||||||
StkId pos = NULL; /* to avoid warnings */
|
StkId pos = NULL; /* to avoid warnings */
|
||||||
const char *name;
|
const char *name;
|
||||||
lua_lock(L);
|
lua_lock(L);
|
||||||
name = findlocal(L, ar->i_ci, n, &pos);
|
name = luaG_findlocal(L, ar->i_ci, n, &pos);
|
||||||
if (name) {
|
if (name) {
|
||||||
setobjs2s(L, pos, L->top - 1);
|
setobjs2s(L, pos, L->top - 1);
|
||||||
L->top--; /* pop value */
|
L->top--; /* pop value */
|
||||||
|
2
ldebug.h
2
ldebug.h
@ -22,6 +22,8 @@
|
|||||||
#define ABSLINEINFO (-0x80)
|
#define ABSLINEINFO (-0x80)
|
||||||
|
|
||||||
LUAI_FUNC int luaG_getfuncline (const Proto *f, int pc);
|
LUAI_FUNC int luaG_getfuncline (const Proto *f, int pc);
|
||||||
|
LUAI_FUNC const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n,
|
||||||
|
StkId *pos);
|
||||||
LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o,
|
LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o,
|
||||||
const char *opname);
|
const char *opname);
|
||||||
LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o,
|
LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o,
|
||||||
|
6
lfunc.c
6
lfunc.c
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include "lua.h"
|
#include "lua.h"
|
||||||
|
|
||||||
|
#include "ldebug.h"
|
||||||
#include "ldo.h"
|
#include "ldo.h"
|
||||||
#include "lfunc.h"
|
#include "lfunc.h"
|
||||||
#include "lgc.h"
|
#include "lgc.h"
|
||||||
@ -140,6 +141,11 @@ static int closeupval (lua_State *L, TValue *uv, StkId level, int status) {
|
|||||||
if (likely(status == LUA_OK)) {
|
if (likely(status == LUA_OK)) {
|
||||||
if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */
|
if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */
|
||||||
callclose(L, NULL); /* call closing method */
|
callclose(L, NULL); /* call closing method */
|
||||||
|
else if (!ttisnil(uv)) { /* non-closable non-nil value? */
|
||||||
|
const char *vname = luaG_findlocal(L, L->ci, level - L->ci->func, NULL);
|
||||||
|
if (vname == NULL) vname = "?";
|
||||||
|
luaG_runerror(L, "attempt to close non-closable variable '%s'", vname);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else { /* there was an error */
|
else { /* there was an error */
|
||||||
/* save error message and set stack top to 'level + 1' */
|
/* save error message and set stack top to 'level + 1' */
|
||||||
|
7
lvm.c
7
lvm.c
@ -1427,7 +1427,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||||||
}
|
}
|
||||||
vmcase(OP_CLOSE) {
|
vmcase(OP_CLOSE) {
|
||||||
L->top = ra + 1; /* everything is free after this slot */
|
L->top = ra + 1; /* everything is free after this slot */
|
||||||
ProtectNT(luaF_close(L, ra, LUA_OK));
|
Protect(luaF_close(L, ra, LUA_OK));
|
||||||
vmbreak;
|
vmbreak;
|
||||||
}
|
}
|
||||||
vmcase(OP_TBC) {
|
vmcase(OP_TBC) {
|
||||||
@ -1717,9 +1717,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||||||
vmbreak;
|
vmbreak;
|
||||||
}
|
}
|
||||||
vmcase(OP_TFORPREP) {
|
vmcase(OP_TFORPREP) {
|
||||||
/* is 'toclose' a function or has a '__close' metamethod? */
|
/* is 'toclose' not nil? */
|
||||||
if (ttisfunction(s2v(ra + 3)) ||
|
if (!ttisnil(s2v(ra + 3))) {
|
||||||
!ttisnil(luaT_gettmbyobj(L, s2v(ra + 3), TM_CLOSE))) {
|
|
||||||
/* create to-be-closed upvalue for it */
|
/* create to-be-closed upvalue for it */
|
||||||
halfProtect(luaF_newtbcupval(L, ra + 3));
|
halfProtect(luaF_newtbcupval(L, ra + 3));
|
||||||
}
|
}
|
||||||
|
@ -1063,11 +1063,16 @@ which start with @T{0x} or @T{0X}.
|
|||||||
Hexadecimal constants also accept an optional fractional part
|
Hexadecimal constants also accept an optional fractional part
|
||||||
plus an optional binary exponent,
|
plus an optional binary exponent,
|
||||||
marked by a letter @Char{p} or @Char{P}.
|
marked by a letter @Char{p} or @Char{P}.
|
||||||
|
|
||||||
A numeric constant with a radix point or an exponent
|
A numeric constant with a radix point or an exponent
|
||||||
denotes a float;
|
denotes a float;
|
||||||
otherwise,
|
otherwise,
|
||||||
if its value fits in an integer,
|
if its value fits in an integer or it is a hexadecimal constant,
|
||||||
it denotes an integer.
|
it denotes an integer;
|
||||||
|
otherwise (that is, a decimal integer numeral that overflows),
|
||||||
|
it denotes a float.
|
||||||
|
(Hexadecimal integer numerals that overflow @emph{wrap around};
|
||||||
|
they always denote an integer value.)
|
||||||
Examples of valid integer constants are
|
Examples of valid integer constants are
|
||||||
@verbatim{
|
@verbatim{
|
||||||
3 345 0xff 0xBEBADA
|
3 345 0xff 0xBEBADA
|
||||||
@ -1542,7 +1547,8 @@ If the value of the variable when it goes out of scope is a function,
|
|||||||
that function is called;
|
that function is called;
|
||||||
otherwise, if the value has a @idx{__close} metamethod,
|
otherwise, if the value has a @idx{__close} metamethod,
|
||||||
that metamethod is called;
|
that metamethod is called;
|
||||||
otherwise, nothing is done.
|
otherwise, if the value is @nil, nothing is done;
|
||||||
|
otherwise, an error is raised.
|
||||||
In the function case,
|
In the function case,
|
||||||
if the scope is being closed by an error,
|
if the scope is being closed by an error,
|
||||||
the error object is passed as an argument to the function;
|
the error object is passed as an argument to the function;
|
||||||
@ -1665,7 +1671,7 @@ If both operands are integers,
|
|||||||
the operation is performed over integers and the result is an integer.
|
the operation is performed over integers and the result is an integer.
|
||||||
Otherwise, if both operands are numbers,
|
Otherwise, if both operands are numbers,
|
||||||
then they are converted to floats,
|
then they are converted to floats,
|
||||||
the operation is performed following the usual rules
|
the operation is performed following the machine's rules
|
||||||
for floating-point arithmetic
|
for floating-point arithmetic
|
||||||
(usually the @x{IEEE 754} standard),
|
(usually the @x{IEEE 754} standard),
|
||||||
and the result is a float.
|
and the result is a float.
|
||||||
@ -4998,7 +5004,7 @@ This call leaves the final string on the top of the stack.
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
If you know beforehand the total size of the resulting string,
|
If you know beforehand the maximum size of the resulting string,
|
||||||
you can use the buffer like this:
|
you can use the buffer like this:
|
||||||
@itemize{
|
@itemize{
|
||||||
|
|
||||||
@ -5012,7 +5018,8 @@ size @id{sz} with a call @T{luaL_buffinitsize(L, &b, sz)}.}
|
|||||||
@item{
|
@item{
|
||||||
Finish by calling @T{luaL_pushresultsize(&b, sz)},
|
Finish by calling @T{luaL_pushresultsize(&b, sz)},
|
||||||
where @id{sz} is the total size of the resulting string
|
where @id{sz} is the total size of the resulting string
|
||||||
copied into that space.
|
copied into that space (which may be smaller than or
|
||||||
|
equal to the preallocated size).
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -5028,8 +5035,8 @@ when you call a buffer operation,
|
|||||||
the stack is at the same level
|
the stack is at the same level
|
||||||
it was immediately after the previous buffer operation.
|
it was immediately after the previous buffer operation.
|
||||||
(The only exception to this rule is @Lid{luaL_addvalue}.)
|
(The only exception to this rule is @Lid{luaL_addvalue}.)
|
||||||
After calling @Lid{luaL_pushresult} the stack is back to its
|
After calling @Lid{luaL_pushresult},
|
||||||
level when the buffer was initialized,
|
the stack is back to its level when the buffer was initialized,
|
||||||
plus the final string on its top.
|
plus the final string on its top.
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -7118,7 +7125,7 @@ empty string as a match immediately after another match.
|
|||||||
As an example,
|
As an example,
|
||||||
consider the results of the following code:
|
consider the results of the following code:
|
||||||
@verbatim{
|
@verbatim{
|
||||||
> string.gsub("abc", "()a*()", print)
|
> string.gsub("abc", "()a*()", print);
|
||||||
--> 1 2
|
--> 1 2
|
||||||
--> 3 3
|
--> 3 3
|
||||||
--> 4 4
|
--> 4 4
|
||||||
|
@ -366,7 +366,7 @@ do
|
|||||||
-- "argerror" without frames
|
-- "argerror" without frames
|
||||||
assert(T.checkpanic("loadstring 4") ==
|
assert(T.checkpanic("loadstring 4") ==
|
||||||
"bad argument #4 (string expected, got no value)")
|
"bad argument #4 (string expected, got no value)")
|
||||||
|
|
||||||
|
|
||||||
-- memory error
|
-- memory error
|
||||||
T.totalmem(T.totalmem()+10000) -- set low memory limit (+10k)
|
T.totalmem(T.totalmem()+10000) -- set low memory limit (+10k)
|
||||||
@ -987,12 +987,12 @@ do
|
|||||||
|
|
||||||
local a, b = T.testC([[
|
local a, b = T.testC([[
|
||||||
call 0 1 # create resource
|
call 0 1 # create resource
|
||||||
pushint 34
|
pushnil
|
||||||
toclose -2 # mark call result to be closed
|
toclose -2 # mark call result to be closed
|
||||||
toclose -1 # mark number to be closed (will be ignored)
|
toclose -1 # mark nil to be closed (will be ignored)
|
||||||
return 2
|
return 2
|
||||||
]], newresource)
|
]], newresource)
|
||||||
assert(a[1] == 11 and b == 34)
|
assert(a[1] == 11 and b == nil)
|
||||||
assert(#openresource == 0) -- was closed
|
assert(#openresource == 0) -- was closed
|
||||||
|
|
||||||
-- repeat the test, but calling function in a 'multret' context
|
-- repeat the test, but calling function in a 'multret' context
|
||||||
@ -1005,7 +1005,7 @@ do
|
|||||||
assert(#openresource == 0) -- was closed
|
assert(#openresource == 0) -- was closed
|
||||||
|
|
||||||
-- error
|
-- error
|
||||||
local a, b = pcall(T.testC, [[
|
local a, b = pcall(T.makeCfunc[[
|
||||||
call 0 1 # create resource
|
call 0 1 # create resource
|
||||||
toclose -1 # mark it to be closed
|
toclose -1 # mark it to be closed
|
||||||
error # resource is the error object
|
error # resource is the error object
|
||||||
@ -1038,6 +1038,13 @@ do
|
|||||||
]], newresource, check)
|
]], newresource, check)
|
||||||
assert(a == 3) -- no extra items left in the stack
|
assert(a == 3) -- no extra items left in the stack
|
||||||
|
|
||||||
|
-- non-closable value
|
||||||
|
local a, b = pcall(T.makeCfunc[[
|
||||||
|
pushint 32
|
||||||
|
toclose -1
|
||||||
|
]])
|
||||||
|
assert(not a and string.find(b, "(C temporary)"))
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -1249,9 +1256,9 @@ do -- closing state with no extra memory
|
|||||||
T.closestate(L)
|
T.closestate(L)
|
||||||
T.alloccount()
|
T.alloccount()
|
||||||
end
|
end
|
||||||
|
|
||||||
do -- garbage collection with no extra memory
|
do -- garbage collection with no extra memory
|
||||||
local L = T.newstate()
|
local L = T.newstate()
|
||||||
T.loadlib(L)
|
T.loadlib(L)
|
||||||
local res = (T.doremote(L, [[
|
local res = (T.doremote(L, [[
|
||||||
_ENV = require"_G"
|
_ENV = require"_G"
|
||||||
|
@ -214,14 +214,14 @@ local function foo (a, ...)
|
|||||||
local t = table.pack(...)
|
local t = table.pack(...)
|
||||||
for i = 1, t.n do
|
for i = 1, t.n do
|
||||||
local n, v = debug.getlocal(1, -i)
|
local n, v = debug.getlocal(1, -i)
|
||||||
assert(n == "(*vararg)" and v == t[i])
|
assert(n == "(vararg)" and v == t[i])
|
||||||
end
|
end
|
||||||
assert(not debug.getlocal(1, -(t.n + 1)))
|
assert(not debug.getlocal(1, -(t.n + 1)))
|
||||||
assert(not debug.setlocal(1, -(t.n + 1), 30))
|
assert(not debug.setlocal(1, -(t.n + 1), 30))
|
||||||
if t.n > 0 then
|
if t.n > 0 then
|
||||||
(function (x)
|
(function (x)
|
||||||
assert(debug.setlocal(2, -1, x) == "(*vararg)")
|
assert(debug.setlocal(2, -1, x) == "(vararg)")
|
||||||
assert(debug.setlocal(2, -t.n, x) == "(*vararg)")
|
assert(debug.setlocal(2, -t.n, x) == "(vararg)")
|
||||||
end)(430)
|
end)(430)
|
||||||
assert(... == 430)
|
assert(... == 430)
|
||||||
end
|
end
|
||||||
@ -328,9 +328,9 @@ assert(a[f] and a[g] and a[assert] and a[debug.getlocal] and not a[print])
|
|||||||
-- tests for manipulating non-registered locals (C and Lua temporaries)
|
-- tests for manipulating non-registered locals (C and Lua temporaries)
|
||||||
|
|
||||||
local n, v = debug.getlocal(0, 1)
|
local n, v = debug.getlocal(0, 1)
|
||||||
assert(v == 0 and n == "(*temporary)")
|
assert(v == 0 and n == "(C temporary)")
|
||||||
local n, v = debug.getlocal(0, 2)
|
local n, v = debug.getlocal(0, 2)
|
||||||
assert(v == 2 and n == "(*temporary)")
|
assert(v == 2 and n == "(C temporary)")
|
||||||
assert(not debug.getlocal(0, 3))
|
assert(not debug.getlocal(0, 3))
|
||||||
assert(not debug.getlocal(0, 0))
|
assert(not debug.getlocal(0, 0))
|
||||||
|
|
||||||
@ -607,7 +607,7 @@ co = load[[
|
|||||||
local a = 0
|
local a = 0
|
||||||
-- 'A' should be visible to debugger only after its complete definition
|
-- 'A' should be visible to debugger only after its complete definition
|
||||||
debug.sethook(function (e, l)
|
debug.sethook(function (e, l)
|
||||||
if l == 3 then a = a + 1; assert(debug.getlocal(2, 1) == "(*temporary)")
|
if l == 3 then a = a + 1; assert(debug.getlocal(2, 1) == "(temporary)")
|
||||||
elseif l == 4 then a = a + 1; assert(debug.getlocal(2, 1) == "A")
|
elseif l == 4 then a = a + 1; assert(debug.getlocal(2, 1) == "A")
|
||||||
end
|
end
|
||||||
end, "l")
|
end, "l")
|
||||||
@ -875,15 +875,15 @@ local debug = require'debug'
|
|||||||
local a = 12 -- a local variable
|
local a = 12 -- a local variable
|
||||||
|
|
||||||
local n, v = debug.getlocal(1, 1)
|
local n, v = debug.getlocal(1, 1)
|
||||||
assert(n == "(*temporary)" and v == debug) -- unkown name but known value
|
assert(n == "(temporary)" and v == debug) -- unkown name but known value
|
||||||
n, v = debug.getlocal(1, 2)
|
n, v = debug.getlocal(1, 2)
|
||||||
assert(n == "(*temporary)" and v == 12) -- unkown name but known value
|
assert(n == "(temporary)" and v == 12) -- unkown name but known value
|
||||||
|
|
||||||
-- a function with an upvalue
|
-- a function with an upvalue
|
||||||
local f = function () local x; return a end
|
local f = function () local x; return a end
|
||||||
n, v = debug.getupvalue(f, 1)
|
n, v = debug.getupvalue(f, 1)
|
||||||
assert(n == "(*no name)" and v == 12)
|
assert(n == "(no name)" and v == 12)
|
||||||
assert(debug.setupvalue(f, 1, 13) == "(*no name)")
|
assert(debug.setupvalue(f, 1, 13) == "(no name)")
|
||||||
assert(a == 13)
|
assert(a == 13)
|
||||||
|
|
||||||
local t = debug.getinfo(f)
|
local t = debug.getinfo(f)
|
||||||
|
@ -266,6 +266,27 @@ do -- errors in __close
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
do
|
||||||
|
|
||||||
|
-- errors due to non-closable values
|
||||||
|
local function foo ()
|
||||||
|
local *toclose x = 34
|
||||||
|
end
|
||||||
|
local stat, msg = pcall(foo)
|
||||||
|
assert(not stat and string.find(msg, "variable 'x'"))
|
||||||
|
|
||||||
|
|
||||||
|
-- with other errors, non-closable values are ignored
|
||||||
|
local function foo ()
|
||||||
|
local *toclose x = 34
|
||||||
|
local *toclose y = function () error(32) end
|
||||||
|
end
|
||||||
|
local stat, msg = pcall(foo)
|
||||||
|
assert(not stat and msg == 32)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
if rawget(_G, "T") then
|
if rawget(_G, "T") then
|
||||||
|
|
||||||
-- memory error inside closing function
|
-- memory error inside closing function
|
||||||
|
Loading…
x
Reference in New Issue
Block a user