diff options
| author | Roberto I <roberto@inf.puc-rio.br> | 2025-11-08 11:43:42 -0300 |
|---|---|---|
| committer | Roberto I <roberto@inf.puc-rio.br> | 2025-11-08 11:43:42 -0300 |
| commit | e44f3a2ffc7ced5e75cca7657aaa60ef27da89aa (patch) | |
| tree | 090174ab9a66fbec195e9dd539710ab19370ec2f | |
| parent | f791bb69061c15f73395c5a95958ac18af5ef764 (diff) | |
| download | lua-e44f3a2ffc7ced5e75cca7657aaa60ef27da89aa.tar.gz lua-e44f3a2ffc7ced5e75cca7657aaa60ef27da89aa.tar.bz2 lua-e44f3a2ffc7ced5e75cca7657aaa60ef27da89aa.zip | |
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.
Diffstat (limited to '')
| -rw-r--r-- | lcode.c | 16 | ||||
| -rw-r--r-- | lcode.h | 2 | ||||
| -rw-r--r-- | ldebug.c | 8 | ||||
| -rw-r--r-- | ldebug.h | 1 | ||||
| -rw-r--r-- | ljumptab.h | 1 | ||||
| -rw-r--r-- | lopcodes.c | 1 | ||||
| -rw-r--r-- | lopcodes.h | 2 | ||||
| -rw-r--r-- | lopnames.h | 1 | ||||
| -rw-r--r-- | lparser.c | 19 | ||||
| -rw-r--r-- | lvm.c | 6 | ||||
| -rw-r--r-- | manual/manual.of | 14 | ||||
| -rw-r--r-- | testes/goto.lua | 21 | ||||
| -rw-r--r-- | testes/memerr.lua | 4 |
13 files changed, 87 insertions, 9 deletions
| @@ -706,6 +706,22 @@ static void luaK_float (FuncState *fs, int reg, lua_Number f) { | |||
| 706 | 706 | ||
| 707 | 707 | ||
| 708 | /* | 708 | /* |
| 709 | ** Get the value of 'var' in a register and generate an opcode to check | ||
| 710 | ** whether that register is nil. 'k' is the index of the variable name | ||
| 711 | ** in the list of constants. If its value cannot be encoded in Bx, a 0 | ||
| 712 | ** will use '?' for the name. | ||
| 713 | */ | ||
| 714 | void luaK_codecheckglobal (FuncState *fs, expdesc *var, int k, int line) { | ||
| 715 | luaK_exp2anyreg(fs, var); | ||
| 716 | luaK_fixline(fs, line); | ||
| 717 | k = (k >= MAXARG_Bx) ? 0 : k + 1; | ||
| 718 | luaK_codeABx(fs, OP_ERRNNIL, var->u.info, k); | ||
| 719 | luaK_fixline(fs, line); | ||
| 720 | freeexp(fs, var); | ||
| 721 | } | ||
| 722 | |||
| 723 | |||
| 724 | /* | ||
| 709 | ** Convert a constant in 'v' into an expression description 'e' | 725 | ** Convert a constant in 'v' into an expression description 'e' |
| 710 | */ | 726 | */ |
| 711 | static void const2exp (TValue *v, expdesc *e) { | 727 | static void const2exp (TValue *v, expdesc *e) { |
| @@ -68,6 +68,8 @@ LUAI_FUNC int luaK_codevABCk (FuncState *fs, OpCode o, int A, int B, int C, | |||
| 68 | LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v); | 68 | LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v); |
| 69 | LUAI_FUNC void luaK_fixline (FuncState *fs, int line); | 69 | LUAI_FUNC void luaK_fixline (FuncState *fs, int line); |
| 70 | LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); | 70 | LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); |
| 71 | LUAI_FUNC void luaK_codecheckglobal (FuncState *fs, expdesc *var, int k, | ||
| 72 | int line); | ||
| 71 | LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); | 73 | LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); |
| 72 | LUAI_FUNC void luaK_checkstack (FuncState *fs, int n); | 74 | LUAI_FUNC void luaK_checkstack (FuncState *fs, int n); |
| 73 | LUAI_FUNC void luaK_int (FuncState *fs, int reg, lua_Integer n); | 75 | LUAI_FUNC void luaK_int (FuncState *fs, int reg, lua_Integer n); |
| @@ -814,6 +814,14 @@ l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) { | |||
| 814 | } | 814 | } |
| 815 | 815 | ||
| 816 | 816 | ||
| 817 | l_noret luaG_errnnil (lua_State *L, LClosure *cl, int k) { | ||
| 818 | const char *globalname = "?"; /* default name if k == 0 */ | ||
| 819 | if (k > 0) | ||
| 820 | kname(cl->p, k - 1, &globalname); | ||
| 821 | luaG_runerror(L, "global '%s' already defined", globalname); | ||
| 822 | } | ||
| 823 | |||
| 824 | |||
| 817 | /* add src:line information to 'msg' */ | 825 | /* add src:line information to 'msg' */ |
| 818 | const char *luaG_addinfo (lua_State *L, const char *msg, TString *src, | 826 | const char *luaG_addinfo (lua_State *L, const char *msg, TString *src, |
| 819 | int line) { | 827 | int line) { |
| @@ -53,6 +53,7 @@ LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1, | |||
| 53 | const TValue *p2); | 53 | const TValue *p2); |
| 54 | LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1, | 54 | LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1, |
| 55 | const TValue *p2); | 55 | const TValue *p2); |
| 56 | LUAI_FUNC l_noret luaG_errnnil (lua_State *L, LClosure *cl, int k); | ||
| 56 | LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...); | 57 | LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...); |
| 57 | LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg, | 58 | LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg, |
| 58 | TString *src, int line); | 59 | TString *src, int line); |
| @@ -107,6 +107,7 @@ static const void *const disptab[NUM_OPCODES] = { | |||
| 107 | &&L_OP_CLOSURE, | 107 | &&L_OP_CLOSURE, |
| 108 | &&L_OP_VARARG, | 108 | &&L_OP_VARARG, |
| 109 | &&L_OP_GETVARG, | 109 | &&L_OP_GETVARG, |
| 110 | &&L_OP_ERRNNIL, | ||
| 110 | &&L_OP_VARARGPREP, | 111 | &&L_OP_VARARGPREP, |
| 111 | &&L_OP_EXTRAARG | 112 | &&L_OP_EXTRAARG |
| 112 | 113 | ||
| @@ -103,6 +103,7 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = { | |||
| 103 | ,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */ | 103 | ,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */ |
| 104 | ,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */ | 104 | ,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */ |
| 105 | ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETVARG */ | 105 | ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETVARG */ |
| 106 | ,opmode(0, 0, 0, 0, 0, iABx) /* OP_ERRNNIL */ | ||
| 106 | ,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */ | 107 | ,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */ |
| 107 | ,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */ | 108 | ,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */ |
| 108 | }; | 109 | }; |
| @@ -340,6 +340,8 @@ OP_VARARG,/* A C R[A], R[A+1], ..., R[A+C-2] = vararg */ | |||
| 340 | 340 | ||
| 341 | OP_GETVARG, /* A B C R[A] := R[B][R[C]], R[B] is vararg parameter */ | 341 | OP_GETVARG, /* A B C R[A] := R[B][R[C]], R[B] is vararg parameter */ |
| 342 | 342 | ||
| 343 | OP_ERRNNIL,/* A Bx raise error if R[A] ~= nil (K[Bx] is global name)*/ | ||
| 344 | |||
| 343 | OP_VARARGPREP,/* (adjust vararg parameters) */ | 345 | OP_VARARGPREP,/* (adjust vararg parameters) */ |
| 344 | 346 | ||
| 345 | OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ | 347 | OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ |
| @@ -95,6 +95,7 @@ static const char *const opnames[] = { | |||
| 95 | "CLOSURE", | 95 | "CLOSURE", |
| 96 | "VARARG", | 96 | "VARARG", |
| 97 | "GETVARG", | 97 | "GETVARG", |
| 98 | "ERRNNIL", | ||
| 98 | "VARARGPREP", | 99 | "VARARGPREP", |
| 99 | "EXTRAARG", | 100 | "EXTRAARG", |
| 100 | NULL | 101 | NULL |
| @@ -1875,6 +1875,16 @@ static lu_byte getglobalattribute (LexState *ls, lu_byte df) { | |||
| 1875 | } | 1875 | } |
| 1876 | 1876 | ||
| 1877 | 1877 | ||
| 1878 | static void checkglobal (LexState *ls, TString *varname, int line) { | ||
| 1879 | FuncState *fs = ls->fs; | ||
| 1880 | expdesc var; | ||
| 1881 | int k; | ||
| 1882 | buildglobal(ls, varname, &var); /* create global variable in 'var' */ | ||
| 1883 | k = var.u.ind.keystr; /* index of global name in 'k' */ | ||
| 1884 | luaK_codecheckglobal(fs, &var, k, line); | ||
| 1885 | } | ||
| 1886 | |||
| 1887 | |||
| 1878 | /* | 1888 | /* |
| 1879 | ** Recursively traverse list of globals to be initalized. When | 1889 | ** Recursively traverse list of globals to be initalized. When |
| 1880 | ** going, generate table description for the global. In the end, | 1890 | ** going, generate table description for the global. In the end, |
| @@ -1883,7 +1893,8 @@ static lu_byte getglobalattribute (LexState *ls, lu_byte df) { | |||
| 1883 | ** the stack to the corresponding table description. 'n' is the variable | 1893 | ** the stack to the corresponding table description. 'n' is the variable |
| 1884 | ** being handled, range [0, nvars - 1]. | 1894 | ** being handled, range [0, nvars - 1]. |
| 1885 | */ | 1895 | */ |
| 1886 | static void initglobal (LexState *ls, int nvars, int firstidx, int n) { | 1896 | static void initglobal (LexState *ls, int nvars, int firstidx, int n, |
| 1897 | int line) { | ||
| 1887 | if (n == nvars) { /* traversed all variables? */ | 1898 | if (n == nvars) { /* traversed all variables? */ |
| 1888 | expdesc e; | 1899 | expdesc e; |
| 1889 | int nexps = explist(ls, &e); /* read list of expressions */ | 1900 | int nexps = explist(ls, &e); /* read list of expressions */ |
| @@ -1895,8 +1906,9 @@ static void initglobal (LexState *ls, int nvars, int firstidx, int n) { | |||
| 1895 | TString *varname = getlocalvardesc(fs, firstidx + n)->vd.name; | 1906 | TString *varname = getlocalvardesc(fs, firstidx + n)->vd.name; |
| 1896 | buildglobal(ls, varname, &var); /* create global variable in 'var' */ | 1907 | buildglobal(ls, varname, &var); /* create global variable in 'var' */ |
| 1897 | enterlevel(ls); /* control recursion depth */ | 1908 | enterlevel(ls); /* control recursion depth */ |
| 1898 | initglobal(ls, nvars, firstidx, n + 1); | 1909 | initglobal(ls, nvars, firstidx, n + 1, line); |
| 1899 | leavelevel(ls); | 1910 | leavelevel(ls); |
| 1911 | checkglobal(ls, varname, line); | ||
| 1900 | storevartop(fs, &var); | 1912 | storevartop(fs, &var); |
| 1901 | } | 1913 | } |
| 1902 | } | 1914 | } |
| @@ -1913,7 +1925,7 @@ static void globalnames (LexState *ls, lu_byte defkind) { | |||
| 1913 | nvars++; | 1925 | nvars++; |
| 1914 | } while (testnext(ls, ',')); | 1926 | } while (testnext(ls, ',')); |
| 1915 | if (testnext(ls, '=')) /* initialization? */ | 1927 | if (testnext(ls, '=')) /* initialization? */ |
| 1916 | initglobal(ls, nvars, lastidx - nvars + 1, 0); | 1928 | initglobal(ls, nvars, lastidx - nvars + 1, 0, ls->linenumber); |
| 1917 | fs->nactvar = cast_short(fs->nactvar + nvars); /* activate declaration */ | 1929 | fs->nactvar = cast_short(fs->nactvar + nvars); /* activate declaration */ |
| 1918 | } | 1930 | } |
| 1919 | 1931 | ||
| @@ -1943,6 +1955,7 @@ static void globalfunc (LexState *ls, int line) { | |||
| 1943 | fs->nactvar++; /* enter its scope */ | 1955 | fs->nactvar++; /* enter its scope */ |
| 1944 | buildglobal(ls, fname, &var); | 1956 | buildglobal(ls, fname, &var); |
| 1945 | body(ls, &b, 0, ls->linenumber); /* compile and return closure in 'b' */ | 1957 | body(ls, &b, 0, ls->linenumber); /* compile and return closure in 'b' */ |
| 1958 | checkglobal(ls, fname, line); | ||
| 1946 | luaK_storevar(fs, &var, &b); | 1959 | luaK_storevar(fs, &var, &b); |
| 1947 | luaK_fixline(fs, line); /* definition "happens" in the first line */ | 1960 | luaK_fixline(fs, line); /* definition "happens" in the first line */ |
| 1948 | } | 1961 | } |
| @@ -1940,6 +1940,12 @@ void luaV_execute (lua_State *L, CallInfo *ci) { | |||
| 1940 | luaT_getvararg(ci, ra, rc); | 1940 | luaT_getvararg(ci, ra, rc); |
| 1941 | vmbreak; | 1941 | vmbreak; |
| 1942 | } | 1942 | } |
| 1943 | vmcase(OP_ERRNNIL) { | ||
| 1944 | TValue *ra = vRA(i); | ||
| 1945 | if (!ttisnil(ra)) | ||
| 1946 | halfProtect(luaG_errnnil(L, cl, GETARG_Bx(i))); | ||
| 1947 | vmbreak; | ||
| 1948 | } | ||
| 1943 | vmcase(OP_VARARGPREP) { | 1949 | vmcase(OP_VARARGPREP) { |
| 1944 | ProtectNT(luaT_adjustvarargs(L, ci, cl->p)); | 1950 | ProtectNT(luaT_adjustvarargs(L, ci, cl->p)); |
| 1945 | if (l_unlikely(trap)) { /* previous "Protect" updated trap */ | 1951 | if (l_unlikely(trap)) { /* previous "Protect" updated trap */ |
diff --git a/manual/manual.of b/manual/manual.of index 0127df02..eaf0ce78 100644 --- a/manual/manual.of +++ b/manual/manual.of | |||
| @@ -1660,9 +1660,15 @@ The declaration can include an initialization: | |||
| 1660 | @producname{stat}@producbody{@Rw{global} | 1660 | @producname{stat}@producbody{@Rw{global} |
| 1661 | attnamelist @bnfopt{@bnfter{=} explist}} | 1661 | attnamelist @bnfopt{@bnfter{=} explist}} |
| 1662 | } | 1662 | } |
| 1663 | If present, an initial assignment has the same semantics | 1663 | If there is no initialization, |
| 1664 | local variables are initialized with @nil; | ||
| 1665 | global variables are left unchanged. | ||
| 1666 | Otherwise, the initialization gets the same adjustment | ||
| 1664 | of a multiple assignment @see{assignment}. | 1667 | of a multiple assignment @see{assignment}. |
| 1665 | Otherwise, all local variables are initialized with @nil. | 1668 | Moreover, for global variables, |
| 1669 | the initialization will raise a runtime error | ||
| 1670 | if the variable is already defined, | ||
| 1671 | that is, it has a non-nil value. | ||
| 1666 | 1672 | ||
| 1667 | The list of names may be prefixed by an attribute | 1673 | The list of names may be prefixed by an attribute |
| 1668 | (a name between angle brackets) | 1674 | (a name between angle brackets) |
| @@ -2312,8 +2318,10 @@ global function f () @rep{body} end | |||
| 2312 | } | 2318 | } |
| 2313 | translates to | 2319 | translates to |
| 2314 | @verbatim{ | 2320 | @verbatim{ |
| 2315 | global f; f = function () @rep{body} end | 2321 | global f; global f = function () @rep{body} end |
| 2316 | } | 2322 | } |
| 2323 | The second @Rw{global} makes the assignment an initialization, | ||
| 2324 | which will raise an error if that global is already defined. | ||
| 2317 | 2325 | ||
| 2318 | The @emphx{colon} syntax | 2326 | The @emphx{colon} syntax |
| 2319 | is used to emulate @def{methods}, | 2327 | is used to emulate @def{methods}, |
diff --git a/testes/goto.lua b/testes/goto.lua index a692a623..906208b5 100644 --- a/testes/goto.lua +++ b/testes/goto.lua | |||
| @@ -293,6 +293,7 @@ end | |||
| 293 | foo() | 293 | foo() |
| 294 | -------------------------------------------------------------------------- | 294 | -------------------------------------------------------------------------- |
| 295 | 295 | ||
| 296 | -- check for compilation errors | ||
| 296 | local function checkerr (code, err) | 297 | local function checkerr (code, err) |
| 297 | local st, msg = load(code) | 298 | local st, msg = load(code) |
| 298 | assert(not st and string.find(msg, err)) | 299 | assert(not st and string.find(msg, err)) |
| @@ -414,22 +415,26 @@ end | |||
| 414 | do print "testing initialization in global declarations" | 415 | do print "testing initialization in global declarations" |
| 415 | global<const> a, b, c = 10, 20, 30 | 416 | global<const> a, b, c = 10, 20, 30 |
| 416 | assert(_ENV.a == 10 and b == 20 and c == 30) | 417 | assert(_ENV.a == 10 and b == 20 and c == 30) |
| 418 | _ENV.a = nil; _ENV.b = nil; _ENV.c = nil; | ||
| 417 | 419 | ||
| 418 | global<const> a, b, c = 10 | 420 | global<const> a, b, c = 10 |
| 419 | assert(_ENV.a == 10 and b == nil and c == nil) | 421 | assert(_ENV.a == 10 and b == nil and c == nil) |
| 422 | _ENV.a = nil; _ENV.b = nil; _ENV.c = nil; | ||
| 420 | 423 | ||
| 421 | global table | 424 | global table |
| 422 | global a, b, c, d = table.unpack{1, 2, 3, 6, 5} | 425 | global a, b, c, d = table.unpack{1, 2, 3, 6, 5} |
| 423 | assert(_ENV.a == 1 and b == 2 and c == 3 and d == 6) | 426 | assert(_ENV.a == 1 and b == 2 and c == 3 and d == 6) |
| 427 | a = nil; b = nil; c = nil; d = nil | ||
| 424 | 428 | ||
| 425 | local a, b = 100, 200 | 429 | local a, b = 100, 200 |
| 426 | do | 430 | do |
| 427 | global a, b = a, b | 431 | global a, b = a, b |
| 428 | end | 432 | end |
| 429 | assert(_ENV.a == 100 and _ENV.b == 200) | 433 | assert(_ENV.a == 100 and _ENV.b == 200) |
| 434 | _ENV.a = nil; _ENV.b = nil | ||
| 430 | 435 | ||
| 431 | 436 | ||
| 432 | _ENV.a, _ENV.b, _ENV.c, _ENV.d = nil -- erase these globals | 437 | assert(_ENV.a == nil and _ENV.b == nil and _ENV.c == nil and _ENV.d == nil) |
| 433 | end | 438 | end |
| 434 | 439 | ||
| 435 | do | 440 | do |
| @@ -454,5 +459,19 @@ do | |||
| 454 | assert(env.a == 10 and env.b == 20 and env.c == 30) | 459 | assert(env.a == 10 and env.b == 20 and env.c == 30) |
| 455 | end | 460 | end |
| 456 | 461 | ||
| 462 | |||
| 463 | do -- testing global redefinitions | ||
| 464 | -- cannot use 'checkerr' as errors are not compile time | ||
| 465 | global pcall | ||
| 466 | local f = assert(load("global print = 10")) | ||
| 467 | local st, msg = pcall(f) | ||
| 468 | assert(string.find(msg, "global 'print' already defined")) | ||
| 469 | |||
| 470 | local f = assert(load("local _ENV = {AA = false}; global AA = 10")) | ||
| 471 | local st, msg = pcall(f) | ||
| 472 | assert(string.find(msg, "global 'AA' already defined")) | ||
| 473 | |||
| 474 | end | ||
| 475 | |||
| 457 | print'OK' | 476 | print'OK' |
| 458 | 477 | ||
diff --git a/testes/memerr.lua b/testes/memerr.lua index 2cc8f481..9c940ca7 100644 --- a/testes/memerr.lua +++ b/testes/memerr.lua | |||
| @@ -166,9 +166,9 @@ local function expand (n,s) | |||
| 166 | e, s, expand(n-1,s), e) | 166 | e, s, expand(n-1,s), e) |
| 167 | end | 167 | end |
| 168 | 168 | ||
| 169 | G=0; collectgarbage(); a =collectgarbage("count") | 169 | G=0; collectgarbage() |
| 170 | load(expand(20,"G=G+1"))() | 170 | load(expand(20,"G=G+1"))() |
| 171 | assert(G==20); collectgarbage(); -- assert(gcinfo() <= a+1) | 171 | assert(G==20); collectgarbage() |
| 172 | G = nil | 172 | G = nil |
| 173 | 173 | ||
| 174 | testamem("running code on new thread", function () | 174 | testamem("running code on new thread", function () |
