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)
pull/22/head
Roberto Ierusalimschy 6 years ago
parent
commit
cf71a5ddc7
  1. 31
      lapi.c
  2. 3
      lfunc.c
  3. 15
      lopcodes.h
  4. 6
      lstrlib.c
  5. 7
      ltests.c
  6. 54
      manual/manual.of
  7. 21
      testes/api.lua
  8. 1
      testes/strings.lua

31
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;
}
}
}

3
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);
}

15
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).

6
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;
}

7
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);

54
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.
}
}
}

21
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
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")

Loading…
Cancel
Save