Procházet zdrojové kódy

Fixed initialization of global variables

When calling 'luaK_storevar', the 'expdesc' for the variable must be
created before the one for the expression, to satisfy the assumptions
for register allocation. So, in a statement like 'global a = exp', where
'a' is actually '_ENV.a', this variable must be handled before the
initializing expression 'exp'.
Roberto I před 3 měsíci
rodič
revize
d4eff00234
3 změnil soubory, kde provedl 52 přidání a 13 odebrání
  1. 1 1
      lcode.c
  2. 29 12
      lparser.c
  3. 22 0
      testes/goto.lua

+ 1 - 1
lcode.c

@@ -1242,7 +1242,7 @@ static void codenot (FuncState *fs, expdesc *e) {
 ** Check whether expression 'e' is a short literal string
 */
 static int isKstr (FuncState *fs, expdesc *e) {
-  return (e->k == VK && !hasjumps(e) && e->u.info <= MAXARG_B &&
+  return (e->k == VK && !hasjumps(e) && e->u.info <= MAXINDEXRK &&
           ttisshrstring(&fs->f->k[e->u.info]));
 }
 

+ 29 - 12
lparser.c

@@ -1875,6 +1875,33 @@ static lu_byte getglobalattribute (LexState *ls, lu_byte df) {
 }
 
 
+/*
+** Recursively traverse list of globals to be initalized. When
+** going, generate table description for the global. In the end,
+** after all indices have been generated, read list of initializing
+** expressions. When returning, generate the assignment of the value on
+** the stack to the corresponding table description. 'n' is the variable
+** being handled, range [0, nvars - 1].
+*/
+static void initglobal (LexState *ls, int nvars, int firstidx, int n) {
+  if (n == nvars) {  /* traversed all variables? */
+    expdesc e;
+    int nexps = explist(ls, &e);  /* read list of expressions */
+    adjust_assign(ls, nvars, nexps, &e);
+  }
+  else {  /* handle variable 'n' */
+    FuncState *fs = ls->fs;
+    expdesc var;
+    TString *varname = getlocalvardesc(fs, firstidx + n)->vd.name;
+    buildglobal(ls, varname, &var);  /* create global variable in 'var' */
+    enterlevel(ls);  /* control recursion depth */
+    initglobal(ls, nvars, firstidx, n + 1);
+    leavelevel(ls);
+    storevartop(fs, &var);
+  }
+}
+
+
 static void globalnames (LexState *ls, lu_byte defkind) {
   FuncState *fs = ls->fs;
   int nvars = 0;
@@ -1885,18 +1912,8 @@ static void globalnames (LexState *ls, lu_byte defkind) {
     lastidx = new_varkind(ls, vname, kind);
     nvars++;
   } while (testnext(ls, ','));
-  if (testnext(ls, '=')) {  /* initialization? */
-    expdesc e;
-    int i;
-    int nexps = explist(ls, &e);  /* read list of expressions */
-    adjust_assign(ls, nvars, nexps, &e);
-    for (i = 0; i < nvars; i++) {  /* for each variable */
-      expdesc var;
-      TString *varname = getlocalvardesc(fs, lastidx - i)->vd.name;
-      buildglobal(ls, varname, &var);  /* create global variable in 'var' */
-      storevartop(fs, &var);
-    }
-  }
+  if (testnext(ls, '='))  /* initialization? */
+    initglobal(ls, nvars, lastidx - nvars + 1, 0);
   fs->nactvar = cast_short(fs->nactvar + nvars);  /* activate declaration */
 }
 

+ 22 - 0
testes/goto.lua

@@ -432,5 +432,27 @@ do  print "testing initialization in global declarations"
   _ENV.a, _ENV.b, _ENV.c, _ENV.d = nil   -- erase these globals
 end
 
+do
+  global table, string
+  -- global initialization when names don't fit in K
+
+  -- to fill constant table
+  local code = {}
+  for i = 1, 300 do code[i] = "'" .. i .. "'" end
+  code = table.concat(code, ",")
+  code = string.format([[
+    return function (_ENV)
+      local dummy = {%s}  -- fill initial positions in constant table,
+      -- so that initialization must use registers for global names
+      global a, b, c = 10, 20, 30
+    end]], code)
+
+  local fun = assert(load(code))()
+
+  local env = {}
+  fun(env)
+  assert(env.a == 10 and env.b == 20 and env.c == 30)
+end
+
 print'OK'