Bläddra i källkod

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.)
Roberto Ierusalimschy 6 år sedan
förälder
incheckning
6d04537ea6
9 ändrade filer med 83 tillägg och 39 borttagningar
  1. 1 1
      lapi.c
  2. 10 8
      ldebug.c
  3. 2 0
      ldebug.h
  4. 6 0
      lfunc.c
  5. 3 4
      lvm.c
  6. 16 9
      manual/manual.of
  7. 14 7
      testes/api.lua
  8. 10 10
      testes/db.lua
  9. 21 0
      testes/locals.lua

+ 1 - 1
lapi.c

@@ -1299,7 +1299,7 @@ static const char *aux_upvalue (TValue *fi, int n, TValue **val,
       *val = f->upvals[n-1]->v;
       if (owner) *owner = obj2gco(f->upvals[n - 1]);
       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 */
   }

+ 10 - 8
ldebug.c

@@ -192,15 +192,14 @@ static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
     int nextra = ci->u.l.nextraargs;
     if (n <= nextra) {
       *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 */
 }
 
 
-static const char *findlocal (lua_State *L, CallInfo *ci, int n,
-                              StkId *pos) {
+const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) {
   StkId base = ci->func + 1;
   const char *name = NULL;
   if (isLua(ci)) {
@@ -211,12 +210,15 @@ static const char *findlocal (lua_State *L, CallInfo *ci, int n,
   }
   if (name == NULL) {  /* no 'standard' name? */
     StkId limit = (ci == L->ci) ? L->top : ci->next->func;
-    if (limit - base >= n && n > 0)  /* is 'n' inside 'ci' stack? */
-      name = "(*temporary)";  /* generic name for any valid slot */
+    if (limit - base >= n && n > 0) {  /* is 'n' inside 'ci' stack? */
+      /* generic name for any valid slot */
+      name = isLua(ci) ? "(temporary)" : "(C temporary)";
+    }
     else
       return NULL;  /* no name */
   }
-  *pos = base + (n - 1);
+  if (pos)
+    *pos = base + (n - 1);
   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' */
     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) {
       setobjs2s(L, L->top, pos);
       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 */
   const char *name;
   lua_lock(L);
-  name = findlocal(L, ar->i_ci, n, &pos);
+  name = luaG_findlocal(L, ar->i_ci, n, &pos);
   if (name) {
     setobjs2s(L, pos, L->top - 1);
     L->top--;  /* pop value */

+ 2 - 0
ldebug.h

@@ -22,6 +22,8 @@
 #define ABSLINEINFO	(-0x80)
 
 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,
                                                 const char *opname);
 LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o,

+ 6 - 0
lfunc.c

@@ -14,6 +14,7 @@
 
 #include "lua.h"
 
+#include "ldebug.h"
 #include "ldo.h"
 #include "lfunc.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 (prepclosingmethod(L, uv, &G(L)->nilvalue))  /* something to call? */
       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 */
     /* save error message and set stack top to 'level + 1' */

+ 3 - 4
lvm.c

@@ -1427,7 +1427,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
       }
       vmcase(OP_CLOSE) {
         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;
       }
       vmcase(OP_TBC) {
@@ -1717,9 +1717,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
         vmbreak;
       }
       vmcase(OP_TFORPREP) {
-        /* is 'toclose' a function or has a '__close' metamethod? */
-        if (ttisfunction(s2v(ra + 3)) ||
-            !ttisnil(luaT_gettmbyobj(L, s2v(ra + 3), TM_CLOSE))) {
+        /* is 'toclose' not nil? */
+        if (!ttisnil(s2v(ra + 3))) {
           /* create to-be-closed upvalue for it */
           halfProtect(luaF_newtbcupval(L, ra + 3));
         }

+ 16 - 9
manual/manual.of

@@ -1063,11 +1063,16 @@ which start with @T{0x} or @T{0X}.
 Hexadecimal constants also accept an optional fractional part
 plus an optional binary exponent,
 marked by a letter @Char{p} or @Char{P}.
+
 A numeric constant with a radix point or an exponent
 denotes a float;
 otherwise,
-if its value fits in an integer,
-it denotes an integer.
+if its value fits in an integer or it is a hexadecimal constant,
+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
 @verbatim{
 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;
 otherwise, if the value has a @idx{__close} metamethod,
 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,
 if the scope is being closed by an error,
 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.
 Otherwise, if both operands are numbers,
 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
 (usually the @x{IEEE 754} standard),
 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:
 @itemize{
 
@@ -5012,7 +5018,8 @@ size @id{sz} with a call @T{luaL_buffinitsize(L, &b, sz)}.}
 @item{
 Finish by calling @T{luaL_pushresultsize(&b, sz)},
 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
 it was immediately after the previous buffer operation.
 (The only exception to this rule is @Lid{luaL_addvalue}.)
-After calling @Lid{luaL_pushresult} the stack is back to its
-level when the buffer was initialized,
+After calling @Lid{luaL_pushresult},
+the stack is back to its level when the buffer was initialized,
 plus the final string on its top.
 
 }
@@ -7118,7 +7125,7 @@ empty string as a match immediately after another match.
 As an example,
 consider the results of the following code:
 @verbatim{
-> string.gsub("abc", "()a*()", print)
+> string.gsub("abc", "()a*()", print);
 --> 1   2
 --> 3   3
 --> 4   4

+ 14 - 7
testes/api.lua

@@ -366,7 +366,7 @@ do
   -- "argerror" without frames
   assert(T.checkpanic("loadstring 4") ==
       "bad argument #4 (string expected, got no value)")
-  
+
 
   -- memory error
   T.totalmem(T.totalmem()+10000)   -- set low memory limit (+10k)
@@ -987,12 +987,12 @@ do
 
   local a, b = T.testC([[
     call 0 1   # create resource
-    pushint 34
+    pushnil
     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
   ]], newresource)
-  assert(a[1] == 11 and b == 34) 
+  assert(a[1] == 11 and b == nil)
   assert(#openresource == 0)    -- was closed
 
   -- repeat the test, but calling function in a 'multret' context
@@ -1005,7 +1005,7 @@ do
   assert(#openresource == 0)    -- was closed
 
   -- error
-  local a, b = pcall(T.testC, [[
+  local a, b = pcall(T.makeCfunc[[
     call 0 1   # create resource
     toclose -1 # mark it to be closed
     error       # resource is the error object
@@ -1038,6 +1038,13 @@ do
   ]], newresource, check)
   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
 
 
@@ -1249,9 +1256,9 @@ do   -- closing state with no extra memory
   T.closestate(L)
   T.alloccount()
 end
-  
+
 do   -- garbage collection with no extra memory
-  local L = T.newstate() 
+  local L = T.newstate()
   T.loadlib(L)
   local res = (T.doremote(L, [[
     _ENV = require"_G"

+ 10 - 10
testes/db.lua

@@ -214,14 +214,14 @@ local function foo (a, ...)
   local t = table.pack(...)
   for i = 1, t.n do
     local n, v = debug.getlocal(1, -i)
-    assert(n == "(*vararg)" and v == t[i])
+    assert(n == "(vararg)" and v == t[i])
   end
   assert(not debug.getlocal(1, -(t.n + 1)))
   assert(not debug.setlocal(1, -(t.n + 1), 30))
   if t.n > 0 then
     (function (x)
-      assert(debug.setlocal(2, -1, x) == "(*vararg)")
-      assert(debug.setlocal(2, -t.n, x) == "(*vararg)")
+      assert(debug.setlocal(2, -1, x) == "(vararg)")
+      assert(debug.setlocal(2, -t.n, x) == "(vararg)")
      end)(430)
      assert(... == 430)
   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)
 
 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)
-assert(v == 2 and n == "(*temporary)")
+assert(v == 2 and n == "(C temporary)")
 assert(not debug.getlocal(0, 3))
 assert(not debug.getlocal(0, 0))
 
@@ -607,7 +607,7 @@ co = load[[
 local a = 0
 -- 'A' should be visible to debugger only after its complete definition
 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")
   end
 end, "l")
@@ -875,15 +875,15 @@ local debug = require'debug'
 local a = 12  -- a local variable
 
 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)
-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
 local f = function () local x; return a end
 n, v = debug.getupvalue(f, 1)
-assert(n == "(*no name)" and v == 12)
-assert(debug.setupvalue(f, 1, 13) == "(*no name)")
+assert(n == "(no name)" and v == 12)
+assert(debug.setupvalue(f, 1, 13) == "(no name)")
 assert(a == 13)
 
 local t = debug.getinfo(f)

+ 21 - 0
testes/locals.lua

@@ -266,6 +266,27 @@ do   -- errors in __close
 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
 
   -- memory error inside closing function