Browse Source

Details

Several small improvements (code style, warnings, comments, more tests),
in particular:

- 'lua_topointer' extended to handle strings
- raises an error in 'string.format("%10q")' ('%q' with modifiers)
- in the manual for 'string.format', the term "option" replaced by
  "conversion specifier" (the term used by the C standard)
Roberto Ierusalimschy 6 years ago
parent
commit
cf71a5ddc7
8 changed files with 87 additions and 51 deletions
  1. 22 9
      lapi.c
  2. 2 1
      lfunc.c
  3. 7 8
      lopcodes.h
  4. 4 2
      lstrlib.c
  5. 5 2
      ltests.c
  6. 32 22
      manual/manual.of
  7. 14 7
      testes/api.lua
  8. 1 0
      testes/strings.lua

+ 22 - 9
lapi.c

@@ -414,8 +414,7 @@ LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) {
 }
 
 
-LUA_API void *lua_touserdata (lua_State *L, int idx) {
-  const TValue *o = index2value(L, idx);
+static void *touserdata (const TValue *o) {
   switch (ttype(o)) {
     case LUA_TUSERDATA: return getudatamem(uvalue(o));
     case LUA_TLIGHTUSERDATA: return pvalue(o);
@@ -424,23 +423,37 @@ LUA_API void *lua_touserdata (lua_State *L, int idx) {
 }
 
 
+LUA_API void *lua_touserdata (lua_State *L, int idx) {
+  const TValue *o = index2value(L, idx);
+  return touserdata(o);
+}
+
+
 LUA_API lua_State *lua_tothread (lua_State *L, int idx) {
   const TValue *o = index2value(L, idx);
   return (!ttisthread(o)) ? NULL : thvalue(o);
 }
 
 
+/*
+** Returns a pointer to the internal representation of an object.
+** Note that ANSI C does not allow the conversion of a pointer to
+** function to a 'void*', so the conversion here goes through
+** a 'size_t'. (As the returned pointer is only informative, this
+** conversion should not be a problem.)
+*/
 LUA_API const void *lua_topointer (lua_State *L, int idx) {
   const TValue *o = index2value(L, idx);
   switch (ttypetag(o)) {
-    case LUA_TTABLE: return hvalue(o);
-    case LUA_TLCL: return clLvalue(o);
-    case LUA_TCCL: return clCvalue(o);
     case LUA_TLCF: return cast_voidp(cast_sizet(fvalue(o)));
-    case LUA_TTHREAD: return thvalue(o);
-    case LUA_TUSERDATA: return getudatamem(uvalue(o));
-    case LUA_TLIGHTUSERDATA: return pvalue(o);
-    default: return NULL;
+    case LUA_TUSERDATA: case LUA_TLIGHTUSERDATA:
+      return touserdata(o);
+    default: {
+      if (iscollectable(o))
+        return gcvalue(o);
+      else
+        return NULL;
+    }
   }
 }
 

+ 2 - 1
lfunc.c

@@ -138,7 +138,8 @@ static int callclosemth (lua_State *L, TValue *uv, StkId level, int status) {
     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);
+      int idx = cast_int(level - L->ci->func);
+      const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
       if (vname == NULL) vname = "?";
       luaG_runerror(L, "attempt to close non-closable variable '%s'", vname);
     }

+ 7 - 8
lopcodes.h

@@ -90,7 +90,6 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ};  /* basic instruction formats */
 #define MAXARG_B	((1<<SIZE_B)-1)
 #define MAXARG_C	((1<<SIZE_C)-1)
 #define OFFSET_sC	(MAXARG_C >> 1)
-#define MAXARG_Cx	((1<<(SIZE_C + 1))-1)
 
 
 /* creates a mask with 'n' 1 bits at position 'p' */
@@ -233,8 +232,8 @@ OP_BANDK,/*	A B C	R(A) := R(B) & K(C):integer			*/
 OP_BORK,/*	A B C	R(A) := R(B) | K(C):integer			*/
 OP_BXORK,/*	A B C	R(A) := R(B) ~ K(C):integer			*/
 
-OP_SHRI,/*	A B C	R(A) := R(B) >> C				*/
-OP_SHLI,/*	A B C	R(A) := C << R(B)				*/
+OP_SHRI,/*	A B sC	R(A) := R(B) >> C				*/
+OP_SHLI,/*	A B sC	R(A) := C << R(B)				*/
 
 OP_ADD,/*	A B C	R(A) := R(B) + R(C)				*/
 OP_SUB,/*	A B C	R(A) := R(B) - R(C)				*/
@@ -272,7 +271,7 @@ OP_GTI,/*	A sB	if ((R(A) > sB) ~= k) then pc++			*/
 OP_GEI,/*	A sB	if ((R(A) >= sB) ~= k) then pc++		*/
 
 OP_TEST,/*	A 	if (not R(A) == k) then pc++			*/
-OP_TESTSET,/*	A B	if (not R(B) == k) then R(A) := R(B) else pc++	*/
+OP_TESTSET,/*	A B	if (not R(B) == k) then pc++ else R(A) := R(B)	*/
 
 OP_CALL,/*	A B C	R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
 OP_TAILCALL,/*	A B C	return R(A)(R(A+1), ... ,R(A+B-1))		*/
@@ -305,15 +304,15 @@ OP_EXTRAARG/*	Ax	extra (larger) argument for previous opcode	*/
 } OpCode;
 
 
-#define NUM_OPCODES	(cast_int(OP_EXTRAARG) + 1)
+#define NUM_OPCODES	((int)(OP_EXTRAARG) + 1)
 
 
 
 /*===========================================================================
   Notes:
-  (*) In OP_CALL, if (B == 0) then B = top. If (C == 0), then 'top' is
-  set to last_result+1, so next open instruction (OP_CALL, OP_RETURN*,
-  OP_SETLIST) may use 'top'.
+  (*) In OP_CALL, if (B == 0) then B = top - A. If (C == 0), then
+  'top' is set to last_result+1, so next open instruction (OP_CALL,
+  OP_RETURN*, OP_SETLIST) may use 'top'.
 
   (*) In OP_VARARG, if (C == 0) then use actual number of varargs and
   set top (like in OP_CALL with C == 0).

+ 4 - 2
lstrlib.c

@@ -181,7 +181,7 @@ static int str_byte (lua_State *L) {
   size_t pose = getendpos(L, 3, pi, l);
   int n, i;
   if (posi > pose) return 0;  /* empty interval; return no values */
-  if (pose - posi >= INT_MAX)  /* arithmetic overflow? */
+  if (pose - posi >= (size_t)INT_MAX)  /* arithmetic overflow? */
     return luaL_error(L, "string slice too long");
   n = (int)(pose -  posi) + 1;
   luaL_checkstack(L, n, "string slice too long");
@@ -1159,7 +1159,7 @@ static int str_format (lua_State *L) {
       char *buff = luaL_prepbuffsize(&b, MAX_ITEM);  /* to put formatted item */
       int nb = 0;  /* number of bytes in added item */
       if (++arg > top)
-        luaL_argerror(L, arg, "no value");
+        return luaL_argerror(L, arg, "no value");
       strfrmt = scanformat(L, strfrmt, form);
       switch (*strfrmt++) {
         case 'c': {
@@ -1186,6 +1186,8 @@ static int str_format (lua_State *L) {
           break;
         }
         case 'q': {
+          if (form[2] != '\0')  /* modifiers? */
+            return luaL_error(L, "specifier '%%q' cannot have modifiers");
           addliteral(L, &b, arg);
           break;
         }

+ 5 - 2
ltests.c

@@ -164,7 +164,7 @@ typedef union Header {
 
 
 Memcontrol l_memcontrol =
-  {0L, 0L, 0L, 0L, (~0L), {0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L}};
+  {0UL, 0UL, 0UL, 0UL, (~0UL), {0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL}};
 
 
 static void freeblock (Memcontrol *mc, Header *block) {
@@ -1596,7 +1596,10 @@ static struct X { int x; } x;
       lua_pushnumber(L1, lua_tonumber(L1, getindex));
     }
     else if EQ("topointer") {
-      lua_pushnumber(L1, cast_sizet(lua_topointer(L1, getindex)));
+      lua_pushlightuserdata(L1, cast_voidp(lua_topointer(L1, getindex)));
+    }
+    else if EQ("touserdata") {
+      lua_pushlightuserdata(L1, lua_touserdata(L1, getindex));
     }
     else if EQ("tostring") {
       const char *s = lua_tostring(L1, getindex);

+ 32 - 22
manual/manual.of

@@ -143,7 +143,7 @@ that is, @x{arrays} that can have as indices not only numbers,
 but any Lua value except @nil and @x{NaN}.
 (@emphx{Not a Number} is a special floating-point value
 used by the @x{IEEE 754} standard to represent
-undefined or unrepresentable numerical results, such as @T{0/0}.)
+undefined numerical results, such as @T{0/0}.)
 Tables can be @emph{heterogeneous};
 that is, they can contain values of all types (except @nil).
 Any key with value @nil is not considered part of the table.
@@ -670,8 +670,8 @@ are called when the garbage collector detects that the
 corresponding table or userdata is unreachable.
 Finalizers allow you to coordinate Lua's garbage collection
 with external resource management
-(such as closing files, network or database connections,
-or freeing your own memory).
+such as closing files, network or database connections,
+or freeing your own memory.
 
 For an object (table or userdata) to be finalized when collected,
 you must @emph{mark} it for finalization.
@@ -1323,11 +1323,12 @@ labels in Lua are considered statements too:
 }
 
 A label is visible in the entire block where it is defined,
-except
-inside nested blocks where a label with the same name is defined and
-inside nested functions.
+except inside nested functions.
 A goto may jump to any visible label as long as it does not
 enter into the scope of a local variable.
+A label should not be declared
+where a label with the same name is visible,
+even if this other label has been declared in an enclosing block.
 
 Labels and empty statements are called @def{void statements},
 as they perform no actions.
@@ -1537,7 +1538,7 @@ goes out of scope, including normal block termination,
 exiting its block by @Rw{break}/@Rw{goto}/@Rw{return},
 or exiting by an error.
 
-Here, to \emph{close} a value means
+Here, to @emph{close} a value means
 to call its @idx{__close} metamethod.
 If the value is @nil, it is ignored;
 otherwise,
@@ -4236,7 +4237,7 @@ indicates whether the operation succeeded.
 
 Converts the value at the given index to a generic
 @N{C pointer} (@T{void*}).
-The value can be a userdata, a table, a thread, or a function;
+The value can be a userdata, a table, a thread, a string, or a function;
 otherwise, @id{lua_topointer} returns @id{NULL}.
 Different objects will give different pointers.
 There is no way to convert the pointer back to its original value.
@@ -6712,8 +6713,10 @@ to save space.
 
 Functions with upvalues have only their number of upvalues saved.
 When (re)loaded,
-those upvalues receive fresh instances containing @nil.
-(You can use the debug library to serialize
+those upvalues receive fresh instances.
+(See the @Lid{load} function for details about
+how these upvalues are initialized.
+You can use the debug library to serialize
 and reload the upvalues of a function
 in a way adequate to your needs.)
 
@@ -6747,12 +6750,12 @@ after the two indices.
 Returns a formatted version of its variable number of arguments
 following the description given in its first argument (which must be a string).
 The format string follows the same rules as the @ANSI{sprintf}.
-The only differences are that the options/modifiers
+The only differences are that the conversion specifiers and modifiers
 @T{*}, @id{h}, @id{L}, @id{l}, @id{n},
 and @id{p} are not supported
-and that there is an extra option, @id{q}.
+and that there is an extra specifier, @id{q}.
 
-The @id{q} option formats booleans, nil, numbers, and strings
+The specifier @id{q} formats booleans, nil, numbers, and strings
 in a way that the result is a valid constant in Lua source code.
 Booleans and nil are written in the obvious way
 (@id{true}, @id{false}, @id{nil}).
@@ -6770,22 +6773,23 @@ may produce the string:
 "a string with \"quotes\" and \
  new line"
 }
+This specifier does not support modifiers (flags, width, length).
 
-Options
+The conversion specifiers
 @id{A}, @id{a}, @id{E}, @id{e}, @id{f},
 @id{G}, and @id{g} all expect a number as argument.
-Options @id{c}, @id{d},
+The specifiers @id{c}, @id{d},
 @id{i}, @id{o}, @id{u}, @id{X}, and @id{x}
 expect an integer.
 When Lua is compiled with a C89 compiler,
-options @id{A} and @id{a} (hexadecimal floats)
-do not support any modifier (flags, width, length).
+the specifiers @id{A} and @id{a} (hexadecimal floats)
+do not support modifiers.
 
-Option @id{s} expects a string;
+The specifier @id{s} expects a string;
 if its argument is not a string,
 it is converted to one following the same rules of @Lid{tostring}.
-If the option has any modifier (flags, width, length),
-the string argument should not contain @x{embedded zeros}.
+If the specifier has any modifier,
+the corresponding string argument should not contain @x{embedded zeros}.
 
 }
 
@@ -8009,8 +8013,8 @@ or there is any input from some special files
 }
 
 }
-For the last two cases, @id{size}
-specifies the size of the buffer, in bytes.
+For the last two cases,
+@id{size} is a hint for the size of the buffer, in bytes.
 The default is an appropriate size.
 
 }
@@ -8698,6 +8702,12 @@ When a coroutine finishes with an error,
 its stack is unwound (to run any pending closing methods).
 }
 
+@item{
+A label for a @Rw{goto} cannot be declared where a label with the same
+name is visible, even if this other label is declared in an enclosing
+block.
+}
+
 }
 
 }

+ 14 - 7
testes/api.lua

@@ -332,6 +332,7 @@ function to (s, x, n)
   return T.testC(string.format("%s %d; return 1", s, n), x)
 end
 
+local null = T.pushuserdata(0)
 local hfunc = string.gmatch("", "")    -- a "heavy C function" (with upvalues)
 assert(debug.getupvalue(hfunc, 1))
 assert(to("tostring", {}) == nil)
@@ -349,13 +350,19 @@ assert(to("tonumber", {}) == 0)
 assert(to("tonumber", "12") == 12)
 assert(to("tonumber", "s2") == 0)
 assert(to("tonumber", 1, 20) == 0)
-assert(to("topointer", 10) == 0)
-assert(to("topointer", true) == 0)
-assert(to("topointer", T.pushuserdata(20)) == 20)
-assert(to("topointer", io.read) ~= 0)           -- light C function
-assert(to("topointer", hfunc) ~= 0)        -- "heavy" C function
-assert(to("topointer", function () end) ~= 0)   -- Lua function
-assert(to("topointer", io.stdin) ~= 0)   -- full userdata
+assert(to("topointer", 10) == null)
+assert(to("topointer", true) == null)
+assert(to("topointer", nil) == null)
+assert(to("topointer", "abc") ~= null)
+assert(to("topointer", string.rep("x", 10)) ==
+       to("topointer", string.rep("x", 10)))    -- short strings
+assert(to("topointer", string.rep("x", 300)) ~=
+       to("topointer", string.rep("x", 300)))    -- long strings
+assert(to("topointer", T.pushuserdata(20)) ~= null)
+assert(to("topointer", io.read) ~= null)           -- light C function
+assert(to("topointer", hfunc) ~= null)        -- "heavy" C function
+assert(to("topointer", function () end) ~= null)   -- Lua function
+assert(to("topointer", io.stdin) ~= null)   -- full userdata
 assert(to("func2num", 20) == 0)
 assert(to("func2num", T.pushuserdata(10)) == 0)
 assert(to("func2num", io.read) ~= 0)     -- light C function

+ 1 - 0
testes/strings.lua

@@ -199,6 +199,7 @@ end
 
 assert(string.format("\0%s\0", "\0\0\1") == "\0\0\0\1\0")
 checkerror("contains zeros", string.format, "%10s", "\0")
+checkerror("cannot have modifiers", string.format, "%10q", "1")
 
 -- format x tostring
 assert(string.format("%s %s", nil, true) == "nil true")