Преглед на файлове

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.
Roberto I преди 2 месеца
родител
ревизия
e44f3a2ffc
променени са 13 файла, в които са добавени 87 реда и са изтрити 9 реда
  1. 16 0
      lcode.c
  2. 2 0
      lcode.h
  3. 8 0
      ldebug.c
  4. 1 0
      ldebug.h
  5. 1 0
      ljumptab.h
  6. 1 0
      lopcodes.c
  7. 2 0
      lopcodes.h
  8. 1 0
      lopnames.h
  9. 16 3
      lparser.c
  10. 6 0
      lvm.c
  11. 11 3
      manual/manual.of
  12. 20 1
      testes/goto.lua
  13. 2 2
      testes/memerr.lua

+ 16 - 0
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 - 0
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 - 0
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 - 0
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);

+ 1 - 0
ljumptab.h

@@ -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
 
 

+ 1 - 0
lopcodes.c

@@ -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 */
 };
 };

+ 2 - 0
lopcodes.h

@@ -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	*/

+ 1 - 0
lopnames.h

@@ -95,6 +95,7 @@ static const char *const opnames[] = {
   "CLOSURE",
   "CLOSURE",
   "VARARG",
   "VARARG",
   "GETVARG",
   "GETVARG",
+  "ERRNNIL",
   "VARARGPREP",
   "VARARGPREP",
   "EXTRAARG",
   "EXTRAARG",
   NULL
   NULL

+ 16 - 3
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 - 0
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 */

+ 11 - 3
manual/manual.of

@@ -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},

+ 20 - 1
testes/goto.lua

@@ -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'
 
 

+ 2 - 2
testes/memerr.lua

@@ -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 ()