Global initialization checks name conflict
Initialization "global a = 10" raises an error if global 'a' is already defined, that is, it has a non-nil value.
This commit is contained in:
16
lcode.c
16
lcode.c
@@ -705,6 +705,22 @@ static void luaK_float (FuncState *fs, int reg, lua_Number f) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Get the value of 'var' in a register and generate an opcode to check
|
||||||
|
** whether that register is nil. 'k' is the index of the variable name
|
||||||
|
** in the list of constants. If its value cannot be encoded in Bx, a 0
|
||||||
|
** will use '?' for the name.
|
||||||
|
*/
|
||||||
|
void luaK_codecheckglobal (FuncState *fs, expdesc *var, int k, int line) {
|
||||||
|
luaK_exp2anyreg(fs, var);
|
||||||
|
luaK_fixline(fs, line);
|
||||||
|
k = (k >= MAXARG_Bx) ? 0 : k + 1;
|
||||||
|
luaK_codeABx(fs, OP_ERRNNIL, var->u.info, k);
|
||||||
|
luaK_fixline(fs, line);
|
||||||
|
freeexp(fs, var);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Convert a constant in 'v' into an expression description 'e'
|
** Convert a constant in 'v' into an expression description 'e'
|
||||||
*/
|
*/
|
||||||
|
|||||||
2
lcode.h
2
lcode.h
@@ -68,6 +68,8 @@ LUAI_FUNC int luaK_codevABCk (FuncState *fs, OpCode o, int A, int B, int C,
|
|||||||
LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v);
|
LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v);
|
||||||
LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
|
LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
|
||||||
LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
|
LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
|
||||||
|
LUAI_FUNC void luaK_codecheckglobal (FuncState *fs, expdesc *var, int k,
|
||||||
|
int line);
|
||||||
LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);
|
LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);
|
||||||
LUAI_FUNC void luaK_checkstack (FuncState *fs, int n);
|
LUAI_FUNC void luaK_checkstack (FuncState *fs, int n);
|
||||||
LUAI_FUNC void luaK_int (FuncState *fs, int reg, lua_Integer n);
|
LUAI_FUNC void luaK_int (FuncState *fs, int reg, lua_Integer n);
|
||||||
|
|||||||
8
ldebug.c
8
ldebug.c
@@ -814,6 +814,14 @@ l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
l_noret luaG_errnnil (lua_State *L, LClosure *cl, int k) {
|
||||||
|
const char *globalname = "?"; /* default name if k == 0 */
|
||||||
|
if (k > 0)
|
||||||
|
kname(cl->p, k - 1, &globalname);
|
||||||
|
luaG_runerror(L, "global '%s' already defined", globalname);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* add src:line information to 'msg' */
|
/* add src:line information to 'msg' */
|
||||||
const char *luaG_addinfo (lua_State *L, const char *msg, TString *src,
|
const char *luaG_addinfo (lua_State *L, const char *msg, TString *src,
|
||||||
int line) {
|
int line) {
|
||||||
|
|||||||
1
ldebug.h
1
ldebug.h
@@ -53,6 +53,7 @@ LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1,
|
|||||||
const TValue *p2);
|
const TValue *p2);
|
||||||
LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1,
|
LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1,
|
||||||
const TValue *p2);
|
const TValue *p2);
|
||||||
|
LUAI_FUNC l_noret luaG_errnnil (lua_State *L, LClosure *cl, int k);
|
||||||
LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...);
|
LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...);
|
||||||
LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg,
|
LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg,
|
||||||
TString *src, int line);
|
TString *src, int line);
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ static const void *const disptab[NUM_OPCODES] = {
|
|||||||
&&L_OP_CLOSURE,
|
&&L_OP_CLOSURE,
|
||||||
&&L_OP_VARARG,
|
&&L_OP_VARARG,
|
||||||
&&L_OP_GETVARG,
|
&&L_OP_GETVARG,
|
||||||
|
&&L_OP_ERRNNIL,
|
||||||
&&L_OP_VARARGPREP,
|
&&L_OP_VARARGPREP,
|
||||||
&&L_OP_EXTRAARG
|
&&L_OP_EXTRAARG
|
||||||
|
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
|
|||||||
,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */
|
,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */
|
||||||
,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */
|
,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */
|
||||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETVARG */
|
,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETVARG */
|
||||||
|
,opmode(0, 0, 0, 0, 0, iABx) /* OP_ERRNNIL */
|
||||||
,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */
|
,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */
|
||||||
,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */
|
,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -340,6 +340,8 @@ OP_VARARG,/* A C R[A], R[A+1], ..., R[A+C-2] = vararg */
|
|||||||
|
|
||||||
OP_GETVARG, /* A B C R[A] := R[B][R[C]], R[B] is vararg parameter */
|
OP_GETVARG, /* A B C R[A] := R[B][R[C]], R[B] is vararg parameter */
|
||||||
|
|
||||||
|
OP_ERRNNIL,/* A Bx raise error if R[A] ~= nil (K[Bx] is global name)*/
|
||||||
|
|
||||||
OP_VARARGPREP,/* (adjust vararg parameters) */
|
OP_VARARGPREP,/* (adjust vararg parameters) */
|
||||||
|
|
||||||
OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
|
OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ static const char *const opnames[] = {
|
|||||||
"CLOSURE",
|
"CLOSURE",
|
||||||
"VARARG",
|
"VARARG",
|
||||||
"GETVARG",
|
"GETVARG",
|
||||||
|
"ERRNNIL",
|
||||||
"VARARGPREP",
|
"VARARGPREP",
|
||||||
"EXTRAARG",
|
"EXTRAARG",
|
||||||
NULL
|
NULL
|
||||||
|
|||||||
19
lparser.c
19
lparser.c
@@ -1875,6 +1875,16 @@ static lu_byte getglobalattribute (LexState *ls, lu_byte df) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void checkglobal (LexState *ls, TString *varname, int line) {
|
||||||
|
FuncState *fs = ls->fs;
|
||||||
|
expdesc var;
|
||||||
|
int k;
|
||||||
|
buildglobal(ls, varname, &var); /* create global variable in 'var' */
|
||||||
|
k = var.u.ind.keystr; /* index of global name in 'k' */
|
||||||
|
luaK_codecheckglobal(fs, &var, k, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Recursively traverse list of globals to be initalized. When
|
** Recursively traverse list of globals to be initalized. When
|
||||||
** going, generate table description for the global. In the end,
|
** going, generate table description for the global. In the end,
|
||||||
@@ -1883,7 +1893,8 @@ static lu_byte getglobalattribute (LexState *ls, lu_byte df) {
|
|||||||
** the stack to the corresponding table description. 'n' is the variable
|
** the stack to the corresponding table description. 'n' is the variable
|
||||||
** being handled, range [0, nvars - 1].
|
** being handled, range [0, nvars - 1].
|
||||||
*/
|
*/
|
||||||
static void initglobal (LexState *ls, int nvars, int firstidx, int n) {
|
static void initglobal (LexState *ls, int nvars, int firstidx, int n,
|
||||||
|
int line) {
|
||||||
if (n == nvars) { /* traversed all variables? */
|
if (n == nvars) { /* traversed all variables? */
|
||||||
expdesc e;
|
expdesc e;
|
||||||
int nexps = explist(ls, &e); /* read list of expressions */
|
int nexps = explist(ls, &e); /* read list of expressions */
|
||||||
@@ -1895,8 +1906,9 @@ static void initglobal (LexState *ls, int nvars, int firstidx, int n) {
|
|||||||
TString *varname = getlocalvardesc(fs, firstidx + n)->vd.name;
|
TString *varname = getlocalvardesc(fs, firstidx + n)->vd.name;
|
||||||
buildglobal(ls, varname, &var); /* create global variable in 'var' */
|
buildglobal(ls, varname, &var); /* create global variable in 'var' */
|
||||||
enterlevel(ls); /* control recursion depth */
|
enterlevel(ls); /* control recursion depth */
|
||||||
initglobal(ls, nvars, firstidx, n + 1);
|
initglobal(ls, nvars, firstidx, n + 1, line);
|
||||||
leavelevel(ls);
|
leavelevel(ls);
|
||||||
|
checkglobal(ls, varname, line);
|
||||||
storevartop(fs, &var);
|
storevartop(fs, &var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1913,7 +1925,7 @@ static void globalnames (LexState *ls, lu_byte defkind) {
|
|||||||
nvars++;
|
nvars++;
|
||||||
} while (testnext(ls, ','));
|
} while (testnext(ls, ','));
|
||||||
if (testnext(ls, '=')) /* initialization? */
|
if (testnext(ls, '=')) /* initialization? */
|
||||||
initglobal(ls, nvars, lastidx - nvars + 1, 0);
|
initglobal(ls, nvars, lastidx - nvars + 1, 0, ls->linenumber);
|
||||||
fs->nactvar = cast_short(fs->nactvar + nvars); /* activate declaration */
|
fs->nactvar = cast_short(fs->nactvar + nvars); /* activate declaration */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1943,6 +1955,7 @@ static void globalfunc (LexState *ls, int line) {
|
|||||||
fs->nactvar++; /* enter its scope */
|
fs->nactvar++; /* enter its scope */
|
||||||
buildglobal(ls, fname, &var);
|
buildglobal(ls, fname, &var);
|
||||||
body(ls, &b, 0, ls->linenumber); /* compile and return closure in 'b' */
|
body(ls, &b, 0, ls->linenumber); /* compile and return closure in 'b' */
|
||||||
|
checkglobal(ls, fname, line);
|
||||||
luaK_storevar(fs, &var, &b);
|
luaK_storevar(fs, &var, &b);
|
||||||
luaK_fixline(fs, line); /* definition "happens" in the first line */
|
luaK_fixline(fs, line); /* definition "happens" in the first line */
|
||||||
}
|
}
|
||||||
|
|||||||
6
lvm.c
6
lvm.c
@@ -1940,6 +1940,12 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||||||
luaT_getvararg(ci, ra, rc);
|
luaT_getvararg(ci, ra, rc);
|
||||||
vmbreak;
|
vmbreak;
|
||||||
}
|
}
|
||||||
|
vmcase(OP_ERRNNIL) {
|
||||||
|
TValue *ra = vRA(i);
|
||||||
|
if (!ttisnil(ra))
|
||||||
|
halfProtect(luaG_errnnil(L, cl, GETARG_Bx(i)));
|
||||||
|
vmbreak;
|
||||||
|
}
|
||||||
vmcase(OP_VARARGPREP) {
|
vmcase(OP_VARARGPREP) {
|
||||||
ProtectNT(luaT_adjustvarargs(L, ci, cl->p));
|
ProtectNT(luaT_adjustvarargs(L, ci, cl->p));
|
||||||
if (l_unlikely(trap)) { /* previous "Protect" updated trap */
|
if (l_unlikely(trap)) { /* previous "Protect" updated trap */
|
||||||
|
|||||||
@@ -1660,9 +1660,15 @@ The declaration can include an initialization:
|
|||||||
@producname{stat}@producbody{@Rw{global}
|
@producname{stat}@producbody{@Rw{global}
|
||||||
attnamelist @bnfopt{@bnfter{=} explist}}
|
attnamelist @bnfopt{@bnfter{=} explist}}
|
||||||
}
|
}
|
||||||
If present, an initial assignment has the same semantics
|
If there is no initialization,
|
||||||
|
local variables are initialized with @nil;
|
||||||
|
global variables are left unchanged.
|
||||||
|
Otherwise, the initialization gets the same adjustment
|
||||||
of a multiple assignment @see{assignment}.
|
of a multiple assignment @see{assignment}.
|
||||||
Otherwise, all local variables are initialized with @nil.
|
Moreover, for global variables,
|
||||||
|
the initialization will raise a runtime error
|
||||||
|
if the variable is already defined,
|
||||||
|
that is, it has a non-nil value.
|
||||||
|
|
||||||
The list of names may be prefixed by an attribute
|
The list of names may be prefixed by an attribute
|
||||||
(a name between angle brackets)
|
(a name between angle brackets)
|
||||||
@@ -2312,8 +2318,10 @@ global function f () @rep{body} end
|
|||||||
}
|
}
|
||||||
translates to
|
translates to
|
||||||
@verbatim{
|
@verbatim{
|
||||||
global f; f = function () @rep{body} end
|
global f; global f = function () @rep{body} end
|
||||||
}
|
}
|
||||||
|
The second @Rw{global} makes the assignment an initialization,
|
||||||
|
which will raise an error if that global is already defined.
|
||||||
|
|
||||||
The @emphx{colon} syntax
|
The @emphx{colon} syntax
|
||||||
is used to emulate @def{methods},
|
is used to emulate @def{methods},
|
||||||
|
|||||||
@@ -293,6 +293,7 @@ end
|
|||||||
foo()
|
foo()
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- check for compilation errors
|
||||||
local function checkerr (code, err)
|
local function checkerr (code, err)
|
||||||
local st, msg = load(code)
|
local st, msg = load(code)
|
||||||
assert(not st and string.find(msg, err))
|
assert(not st and string.find(msg, err))
|
||||||
@@ -414,22 +415,26 @@ end
|
|||||||
do print "testing initialization in global declarations"
|
do print "testing initialization in global declarations"
|
||||||
global<const> a, b, c = 10, 20, 30
|
global<const> a, b, c = 10, 20, 30
|
||||||
assert(_ENV.a == 10 and b == 20 and c == 30)
|
assert(_ENV.a == 10 and b == 20 and c == 30)
|
||||||
|
_ENV.a = nil; _ENV.b = nil; _ENV.c = nil;
|
||||||
|
|
||||||
global<const> a, b, c = 10
|
global<const> a, b, c = 10
|
||||||
assert(_ENV.a == 10 and b == nil and c == nil)
|
assert(_ENV.a == 10 and b == nil and c == nil)
|
||||||
|
_ENV.a = nil; _ENV.b = nil; _ENV.c = nil;
|
||||||
|
|
||||||
global table
|
global table
|
||||||
global a, b, c, d = table.unpack{1, 2, 3, 6, 5}
|
global a, b, c, d = table.unpack{1, 2, 3, 6, 5}
|
||||||
assert(_ENV.a == 1 and b == 2 and c == 3 and d == 6)
|
assert(_ENV.a == 1 and b == 2 and c == 3 and d == 6)
|
||||||
|
a = nil; b = nil; c = nil; d = nil
|
||||||
|
|
||||||
local a, b = 100, 200
|
local a, b = 100, 200
|
||||||
do
|
do
|
||||||
global a, b = a, b
|
global a, b = a, b
|
||||||
end
|
end
|
||||||
assert(_ENV.a == 100 and _ENV.b == 200)
|
assert(_ENV.a == 100 and _ENV.b == 200)
|
||||||
|
_ENV.a = nil; _ENV.b = nil
|
||||||
|
|
||||||
|
|
||||||
_ENV.a, _ENV.b, _ENV.c, _ENV.d = nil -- erase these globals
|
assert(_ENV.a == nil and _ENV.b == nil and _ENV.c == nil and _ENV.d == nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
do
|
do
|
||||||
@@ -454,5 +459,19 @@ do
|
|||||||
assert(env.a == 10 and env.b == 20 and env.c == 30)
|
assert(env.a == 10 and env.b == 20 and env.c == 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
do -- testing global redefinitions
|
||||||
|
-- cannot use 'checkerr' as errors are not compile time
|
||||||
|
global pcall
|
||||||
|
local f = assert(load("global print = 10"))
|
||||||
|
local st, msg = pcall(f)
|
||||||
|
assert(string.find(msg, "global 'print' already defined"))
|
||||||
|
|
||||||
|
local f = assert(load("local _ENV = {AA = false}; global AA = 10"))
|
||||||
|
local st, msg = pcall(f)
|
||||||
|
assert(string.find(msg, "global 'AA' already defined"))
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
print'OK'
|
print'OK'
|
||||||
|
|
||||||
|
|||||||
@@ -166,9 +166,9 @@ local function expand (n,s)
|
|||||||
e, s, expand(n-1,s), e)
|
e, s, expand(n-1,s), e)
|
||||||
end
|
end
|
||||||
|
|
||||||
G=0; collectgarbage(); a =collectgarbage("count")
|
G=0; collectgarbage()
|
||||||
load(expand(20,"G=G+1"))()
|
load(expand(20,"G=G+1"))()
|
||||||
assert(G==20); collectgarbage(); -- assert(gcinfo() <= a+1)
|
assert(G==20); collectgarbage()
|
||||||
G = nil
|
G = nil
|
||||||
|
|
||||||
testamem("running code on new thread", function ()
|
testamem("running code on new thread", function ()
|
||||||
|
|||||||
Reference in New Issue
Block a user