aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-10-29 14:26:48 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-10-29 14:26:48 -0300
commita006514ea138a29b6031058d9002b48a572b5dd6 (patch)
treeb289a8af0c0497f2555784a0cf666659ceab0236
parent6e9b719694bffb8de711f182d405ec37d32ae0b1 (diff)
downloadlua-a006514ea138a29b6031058d9002b48a572b5dd6.tar.gz
lua-a006514ea138a29b6031058d9002b48a572b5dd6.tar.bz2
lua-a006514ea138a29b6031058d9002b48a572b5dd6.zip
Big revamp in the implmentation of labels/gotos
Added restriction that, when a label is created, there cannot be another label with the same name visible. That allows backward goto's to be resolved when they are read. Backward goto's get a close if they jump out of the scope of some variable; labels get a close only if previous goto to it jumps out of the scope of some upvalue.
-rw-r--r--lcode.c34
-rw-r--r--lcode.h3
-rw-r--r--lopcodes.h9
-rw-r--r--lparser.c241
-rw-r--r--lparser.h2
-rw-r--r--ltests.c2
-rw-r--r--testes/code.lua19
-rw-r--r--testes/goto.lua3
8 files changed, 131 insertions, 182 deletions
diff --git a/lcode.c b/lcode.c
index e84b85ac..054b28fd 100644
--- a/lcode.c
+++ b/lcode.c
@@ -276,40 +276,6 @@ void luaK_patchtohere (FuncState *fs, int list) {
276 276
277 277
278/* 278/*
279** Correct a jump list to jump to 'target'. If 'hasclose' is true,
280** 'target' contains an OP_CLOSE instruction (see first assert).
281** Only the jumps with ('m' == true) need that close; other jumps
282** avoid it jumping to the next instruction.
283*/
284void luaK_patchgoto (FuncState *fs, int list, int target, int hasclose) {
285 lua_assert(!hasclose || GET_OPCODE(fs->f->code[target]) == OP_CLOSE);
286 while (list != NO_JUMP) {
287 int next = getjump(fs, list);
288 lua_assert(!GETARG_m(fs->f->code[list]) || hasclose);
289 patchtestreg(fs, list, NO_REG); /* do not generate values */
290 if (!hasclose || GETARG_m(fs->f->code[list]))
291 fixjump(fs, list, target);
292 else /* there is a CLOSE instruction but jump does not need it */
293 fixjump(fs, list, target + 1); /* avoid CLOSE instruction */
294 list = next;
295 }
296}
297
298
299/*
300** Mark (using the 'm' arg) all jumps in 'list' to close upvalues. Mark
301** will instruct 'luaK_patchgoto' to make these jumps go to OP_CLOSE
302** instructions.
303*/
304void luaK_patchclose (FuncState *fs, int list) {
305 for (; list != NO_JUMP; list = getjump(fs, list)) {
306 lua_assert(GET_OPCODE(fs->f->code[list]) == OP_JMP);
307 SETARG_m(fs->f->code[list], 1);
308 }
309}
310
311
312/*
313** MAXimum number of successive Instructions WiTHout ABSolute line 279** MAXimum number of successive Instructions WiTHout ABSolute line
314** information. 280** information.
315*/ 281*/
diff --git a/lcode.h b/lcode.h
index dd091c78..0758f88d 100644
--- a/lcode.h
+++ b/lcode.h
@@ -78,10 +78,7 @@ LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e);
78LUAI_FUNC int luaK_jump (FuncState *fs); 78LUAI_FUNC int luaK_jump (FuncState *fs);
79LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret); 79LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret);
80LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target); 80LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target);
81LUAI_FUNC void luaK_patchgoto (FuncState *fs, int list, int target,
82 int hasclose);
83LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list); 81LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list);
84LUAI_FUNC void luaK_patchclose (FuncState *fs, int list);
85LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2); 82LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2);
86LUAI_FUNC int luaK_getlabel (FuncState *fs); 83LUAI_FUNC int luaK_getlabel (FuncState *fs);
87LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line); 84LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line);
diff --git a/lopcodes.h b/lopcodes.h
index 4797d7c3..5a38f767 100644
--- a/lopcodes.h
+++ b/lopcodes.h
@@ -21,7 +21,7 @@ iABC C(8) | B(8) |k| A(8) | Op(7) |
21iABx Bx(17) | A(8) | Op(7) | 21iABx Bx(17) | A(8) | Op(7) |
22iAsB sBx (signed)(17) | A(8) | Op(7) | 22iAsB sBx (signed)(17) | A(8) | Op(7) |
23iAx Ax(25) | Op(7) | 23iAx Ax(25) | Op(7) |
24isJ sJ(24) |m| Op(7) | 24isJ sJ(25) | Op(7) |
25 25
26 A signed argument is represented in excess K: the represented value is 26 A signed argument is represented in excess K: the represented value is
27 the written unsigned value minus K, where K is half the maximum for the 27 the written unsigned value minus K, where K is half the maximum for the
@@ -40,7 +40,7 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
40#define SIZE_Bx (SIZE_C + SIZE_B + 1) 40#define SIZE_Bx (SIZE_C + SIZE_B + 1)
41#define SIZE_A 8 41#define SIZE_A 8
42#define SIZE_Ax (SIZE_Bx + SIZE_A) 42#define SIZE_Ax (SIZE_Bx + SIZE_A)
43#define SIZE_sJ (SIZE_Bx + SIZE_A - 1) 43#define SIZE_sJ (SIZE_Bx + SIZE_A)
44 44
45#define SIZE_OP 7 45#define SIZE_OP 7
46 46
@@ -55,8 +55,7 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
55 55
56#define POS_Ax POS_A 56#define POS_Ax POS_A
57 57
58#define POS_m POS_A 58#define POS_sJ POS_A
59#define POS_sJ (POS_A + 1)
60 59
61/* 60/*
62** limits for opcode arguments. 61** limits for opcode arguments.
@@ -144,8 +143,6 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
144 check_exp(checkopm(i, isJ), getarg(i, POS_sJ, SIZE_sJ) - OFFSET_sJ) 143 check_exp(checkopm(i, isJ), getarg(i, POS_sJ, SIZE_sJ) - OFFSET_sJ)
145#define SETARG_sJ(i,j) \ 144#define SETARG_sJ(i,j) \
146 setarg(i, cast_uint((j)+OFFSET_sJ), POS_sJ, SIZE_sJ) 145 setarg(i, cast_uint((j)+OFFSET_sJ), POS_sJ, SIZE_sJ)
147#define GETARG_m(i) check_exp(checkopm(i, isJ), getarg(i, POS_m, 1))
148#define SETARG_m(i,m) setarg(i, m, POS_m, 1)
149 146
150 147
151#define CREATE_ABCk(o,a,b,c,k) ((cast(Instruction, o)<<POS_OP) \ 148#define CREATE_ABCk(o,a,b,c,k) ((cast(Instruction, o)<<POS_OP) \
diff --git a/lparser.c b/lparser.c
index c3dc6362..499e9531 100644
--- a/lparser.c
+++ b/lparser.c
@@ -49,8 +49,6 @@ typedef struct BlockCnt {
49 struct BlockCnt *previous; /* chain */ 49 struct BlockCnt *previous; /* chain */
50 int firstlabel; /* index of first label in this block */ 50 int firstlabel; /* index of first label in this block */
51 int firstgoto; /* index of first pending goto in this block */ 51 int firstgoto; /* index of first pending goto in this block */
52 int brks; /* list of break jumps in this block */
53 lu_byte brkcls; /* true if some 'break' needs to close upvalues */
54 lu_byte nactvar; /* # active locals outside the block */ 52 lu_byte nactvar; /* # active locals outside the block */
55 lu_byte upval; /* true if some variable in the block is an upvalue */ 53 lu_byte upval; /* true if some variable in the block is an upvalue */
56 lu_byte isloop; /* true if 'block' is a loop */ 54 lu_byte isloop; /* true if 'block' is a loop */
@@ -330,50 +328,56 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
330#define leavelevel(ls) ((ls)->L->nCcalls--) 328#define leavelevel(ls) ((ls)->L->nCcalls--)
331 329
332 330
333static void closegoto (LexState *ls, int g, Labeldesc *label) { 331/*
332** Generates an error that a goto jumps into the scope of some
333** local variable.
334*/
335static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) {
336 const char *varname = getstr(getlocvar(ls->fs, gt->nactvar)->varname);
337 const char *msg = "<goto %s> at line %d jumps into the scope of local '%s'";
338 msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname);
339 luaK_semerror(ls, msg); /* raise the error */
340}
341
342
343/*
344** Solves the goto at index 'g' to given 'label' and removes it
345** from the list of pending goto's.
346** If it jumps into the scope of some variable, raises an error.
347*/
348static void solvegoto (LexState *ls, int g, Labeldesc *label) {
334 int i; 349 int i;
335 FuncState *fs = ls->fs; 350 Labellist *gl = &ls->dyd->gt; /* list of goto's */
336 Labellist *gl = &ls->dyd->gt; 351 Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */
337 Labeldesc *gt = &gl->arr[g];
338 lua_assert(eqstr(gt->name, label->name)); 352 lua_assert(eqstr(gt->name, label->name));
339 if (gt->nactvar < label->nactvar) { 353 if (gt->nactvar < label->nactvar) /* enter some scope? */
340 TString *vname = getlocvar(fs, gt->nactvar)->varname; 354 jumpscopeerror(ls, gt);
341 const char *msg = luaO_pushfstring(ls->L, 355 luaK_patchlist(ls->fs, gt->pc, label->pc);
342 "<goto %s> at line %d jumps into the scope of local '%s'", 356 for (i = g; i < gl->n - 1; i++) /* remove goto from pending list */
343 getstr(gt->name), gt->line, getstr(vname));
344 luaK_semerror(ls, msg);
345 }
346 luaK_patchgoto(fs, gt->pc, label->pc, 1);
347 /* remove goto from pending list */
348 for (i = g; i < gl->n - 1; i++)
349 gl->arr[i] = gl->arr[i + 1]; 357 gl->arr[i] = gl->arr[i + 1];
350 gl->n--; 358 gl->n--;
351} 359}
352 360
353 361
354/* 362/*
355** try to close a goto with existing labels; this solves backward jumps 363** Search for an active label with the given name.
356*/ 364*/
357static int solvelabel (LexState *ls, int g) { 365static Labeldesc *findlabel (LexState *ls, TString *name) {
358 int i; 366 int i;
359 BlockCnt *bl = ls->fs->bl;
360 Dyndata *dyd = ls->dyd; 367 Dyndata *dyd = ls->dyd;
361 Labeldesc *gt = &dyd->gt.arr[g]; 368 /* check labels in current function for a match */
362 /* check labels in current block for a match */ 369 for (i = ls->fs->firstlabel; i < dyd->label.n; i++) {
363 for (i = bl->firstlabel; i < dyd->label.n; i++) {
364 Labeldesc *lb = &dyd->label.arr[i]; 370 Labeldesc *lb = &dyd->label.arr[i];
365 if (eqstr(lb->name, gt->name)) { /* correct label? */ 371 if (eqstr(lb->name, name)) /* correct label? */
366 if (gt->nactvar > lb->nactvar && 372 return lb;
367 (bl->upval || dyd->label.n > bl->firstlabel))
368 luaK_patchclose(ls->fs, gt->pc);
369 closegoto(ls, g, lb); /* close it */
370 return 1;
371 }
372 } 373 }
373 return 0; /* label not found; cannot close goto */ 374 return NULL; /* label not found */
374} 375}
375 376
376 377
378/*
379** Adds a new label/goto in the corresponding list.
380*/
377static int newlabelentry (LexState *ls, Labellist *l, TString *name, 381static int newlabelentry (LexState *ls, Labellist *l, TString *name,
378 int line, int pc) { 382 int line, int pc) {
379 int n = l->n; 383 int n = l->n;
@@ -382,6 +386,7 @@ static int newlabelentry (LexState *ls, Labellist *l, TString *name,
382 l->arr[n].name = name; 386 l->arr[n].name = name;
383 l->arr[n].line = line; 387 l->arr[n].line = line;
384 l->arr[n].nactvar = ls->fs->nactvar; 388 l->arr[n].nactvar = ls->fs->nactvar;
389 l->arr[n].close = 0;
385 l->arr[n].pc = pc; 390 l->arr[n].pc = pc;
386 l->n = n + 1; 391 l->n = n + 1;
387 return n; 392 return n;
@@ -389,51 +394,64 @@ static int newlabelentry (LexState *ls, Labellist *l, TString *name,
389 394
390 395
391/* 396/*
392** check whether new label 'lb' matches any pending gotos in current 397** Solves forward jumps. Check whether new label 'lb' matches any
393** block; solves forward jumps 398** pending gotos in current block and solves them. Return true
399** if any of the goto's need to close upvalues.
394*/ 400*/
395static void solvegotos (LexState *ls, Labeldesc *lb) { 401static int solvegotos (LexState *ls, Labeldesc *lb) {
396 Labellist *gl = &ls->dyd->gt; 402 Labellist *gl = &ls->dyd->gt;
397 int i = ls->fs->bl->firstgoto; 403 int i = ls->fs->bl->firstgoto;
404 int needsclose = 0;
398 while (i < gl->n) { 405 while (i < gl->n) {
399 if (eqstr(gl->arr[i].name, lb->name)) 406 if (eqstr(gl->arr[i].name, lb->name)) {
400 closegoto(ls, i, lb); /* will remove 'i' from the list */ 407 needsclose |= gl->arr[i].close;
408 solvegoto(ls, i, lb); /* will remove 'i' from the list */
409 }
401 else 410 else
402 i++; 411 i++;
403 } 412 }
413 return needsclose;
414}
415
416
417/*
418** Create a new label with the given 'name' at the given 'line'.
419** 'last' tells whether label is the last non-op statement in its
420** block. Solves all pending goto's to this new label and adds
421** a close instruction if necessary.
422** Returns true iff it added a close instruction.
423*/
424static int createlabel (LexState *ls, TString *name, int line,
425 int last) {
426 FuncState *fs = ls->fs;
427 Labellist *ll = &ls->dyd->label;
428 int l = newlabelentry(ls, ll, name, line, luaK_getlabel(fs));
429 if (last) { /* label is last no-op statement in the block? */
430 /* assume that locals are already out of scope */
431 ll->arr[l].nactvar = fs->bl->nactvar;
432 }
433 if (solvegotos(ls, &ll->arr[l])) { /* need close? */
434 luaK_codeABC(fs, OP_CLOSE, fs->nactvar, 0, 0);
435 return 1;
436 }
437 return 0;
404} 438}
405 439
406 440
407/* 441/*
408** export pending gotos to outer level, to check them against 442** Adjust pending gotos to outer level of a block.
409** outer labels; if the block being exited has upvalues, and
410** the goto exits the scope of any variable (which can be the
411** upvalue), close those variables being exited. Also export
412** break list.
413*/ 443*/
414static void movegotosout (FuncState *fs, BlockCnt *bl) { 444static void movegotosout (FuncState *fs, BlockCnt *bl) {
415 int i = bl->firstgoto; 445 int i;
416 Labellist *gl = &fs->ls->dyd->gt; 446 Labellist *gl = &fs->ls->dyd->gt;
417 /* correct pending gotos to current block and try to close it 447 /* correct pending gotos to current block */
418 with visible labels */ 448 for (i = bl->firstgoto; i < gl->n; i++) { /* for each pending goto */
419 while (i < gl->n) { /* for each pending goto */
420 Labeldesc *gt = &gl->arr[i]; 449 Labeldesc *gt = &gl->arr[i];
421 if (gt->nactvar > bl->nactvar) { /* leaving a variable scope? */ 450 if (gt->nactvar > bl->nactvar) { /* leaving a variable scope? */
422 if (bl->upval) /* variable may be an upvalue? */
423 luaK_patchclose(fs, gt->pc); /* jump will need a close */
424 gt->nactvar = bl->nactvar; /* update goto level */ 451 gt->nactvar = bl->nactvar; /* update goto level */
452 gt->close |= bl->upval; /* jump may need a close */
425 } 453 }
426 if (!solvelabel(fs->ls, i))
427 i++; /* move to next one */
428 /* else, 'solvelabel' removed current goto from the list
429 and 'i' now points to next one */
430 } 454 }
431 /* handles break list */
432 if (bl->upval) /* exiting the scope of an upvalue? */
433 luaK_patchclose(fs, bl->brks); /* breaks will need OP_CLOSE */
434 /* move breaks to outer block */
435 luaK_concat(fs, &bl->previous->brks, bl->brks);
436 bl->previous->brkcls |= bl->brkcls;
437} 455}
438 456
439 457
@@ -442,8 +460,6 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) {
442 bl->nactvar = fs->nactvar; 460 bl->nactvar = fs->nactvar;
443 bl->firstlabel = fs->ls->dyd->label.n; 461 bl->firstlabel = fs->ls->dyd->label.n;
444 bl->firstgoto = fs->ls->dyd->gt.n; 462 bl->firstgoto = fs->ls->dyd->gt.n;
445 bl->brks = NO_JUMP;
446 bl->brkcls = 0;
447 bl->upval = 0; 463 bl->upval = 0;
448 bl->previous = fs->bl; 464 bl->previous = fs->bl;
449 fs->bl = bl; 465 fs->bl = bl;
@@ -452,20 +468,6 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) {
452 468
453 469
454/* 470/*
455** Fix all breaks in block 'bl' to jump to the end of the block.
456*/
457static void fixbreaks (FuncState *fs, BlockCnt *bl) {
458 int target = fs->pc;
459 if (bl->brkcls) /* does the block need to close upvalues? */
460 luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);
461 luaK_patchgoto(fs, bl->brks, target, bl->brkcls);
462 bl->brks = NO_JUMP; /* no more breaks to fix */
463 bl->brkcls = 0; /* no more need to close upvalues */
464 lua_assert(!bl->upval); /* loop body cannot have local variables */
465}
466
467
468/*
469** generates an error for an undefined 'goto'. 471** generates an error for an undefined 'goto'.
470*/ 472*/
471static l_noret undefgoto (LexState *ls, Labeldesc *gt) { 473static l_noret undefgoto (LexState *ls, Labeldesc *gt) {
@@ -478,11 +480,10 @@ static l_noret undefgoto (LexState *ls, Labeldesc *gt) {
478static void leaveblock (FuncState *fs) { 480static void leaveblock (FuncState *fs) {
479 BlockCnt *bl = fs->bl; 481 BlockCnt *bl = fs->bl;
480 LexState *ls = fs->ls; 482 LexState *ls = fs->ls;
481 if (bl->upval && bl->brks != NO_JUMP) /* breaks in upvalue scopes? */ 483 int hasclose = 0;
482 bl->brkcls = 1; /* these breaks must close the upvalues */ 484 if (bl->isloop) /* fix pending breaks? */
483 if (bl->isloop) 485 hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0);
484 fixbreaks(fs, bl); /* fix pending breaks */ 486 if (!hasclose && bl->previous && bl->upval)
485 if (bl->previous && bl->upval)
486 luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0); 487 luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);
487 fs->bl = bl->previous; 488 fs->bl = bl->previous;
488 removevars(fs, bl->nactvar); 489 removevars(fs, bl->nactvar);
@@ -492,7 +493,6 @@ static void leaveblock (FuncState *fs) {
492 if (bl->previous) /* inner block? */ 493 if (bl->previous) /* inner block? */
493 movegotosout(fs, bl); /* update pending gotos to outer block */ 494 movegotosout(fs, bl); /* update pending gotos to outer block */
494 else { 495 else {
495 lua_assert(bl->brks == NO_JUMP); /* no pending breaks */
496 if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */ 496 if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */
497 undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */ 497 undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */
498 } 498 }
@@ -550,6 +550,7 @@ static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) {
550 fs->nactvar = 0; 550 fs->nactvar = 0;
551 fs->needclose = 0; 551 fs->needclose = 0;
552 fs->firstlocal = ls->dyd->actvar.n; 552 fs->firstlocal = ls->dyd->actvar.n;
553 fs->firstlabel = ls->dyd->label.n;
553 fs->bl = NULL; 554 fs->bl = NULL;
554 f->source = ls->source; 555 f->source = ls->source;
555 f->maxstacksize = 2; /* registers 0/1 are always valid */ 556 f->maxstacksize = 2; /* registers 0/1 are always valid */
@@ -1204,63 +1205,59 @@ static int cond (LexState *ls) {
1204} 1205}
1205 1206
1206 1207
1207static void gotostat (LexState *ls, int pc) { 1208static void gotostat (LexState *ls) {
1209 FuncState *fs = ls->fs;
1208 int line = ls->linenumber; 1210 int line = ls->linenumber;
1209 int g; 1211 TString *name = str_checkname(ls); /* label's name */
1210 luaX_next(ls); /* skip 'goto' */ 1212 Labeldesc *lb = findlabel(ls, name);
1211 g = newlabelentry(ls, &ls->dyd->gt, str_checkname(ls), line, pc); 1213 if (lb == NULL) /* no label? */
1212 solvelabel(ls, g); /* close it if label already defined */ 1214 /* forward jump; will be resolved when the label is declared */
1215 newlabelentry(ls, &ls->dyd->gt, name, line, luaK_jump(fs));
1216 else { /* found a label */
1217 /* backward jump; will be resolved here */
1218 if (fs->nactvar > lb->nactvar) /* leaving the scope of some variable? */
1219 luaK_codeABC(fs, OP_CLOSE, lb->nactvar, 0, 0);
1220 /* create jump and link it to the label */
1221 luaK_patchlist(fs, luaK_jump(fs), lb->pc);
1222 }
1213} 1223}
1214 1224
1215 1225
1226/*
1227** Break statement. Semantically equivalent to "goto break".
1228*/
1216static void breakstat (LexState *ls, int pc) { 1229static void breakstat (LexState *ls, int pc) {
1217 FuncState *fs = ls->fs; 1230 FuncState *fs = ls->fs;
1231 int line = ls->linenumber;
1218 BlockCnt *bl = fs->bl; 1232 BlockCnt *bl = fs->bl;
1219 luaX_next(ls); /* skip break */ 1233 luaX_next(ls); /* skip break */
1234 newlabelentry(ls, &ls->dyd->gt, luaS_newliteral(ls->L, "break"), line, pc);
1220 while (bl && !bl->isloop) { bl = bl->previous; } 1235 while (bl && !bl->isloop) { bl = bl->previous; }
1221 if (!bl) 1236 if (!bl)
1222 luaX_syntaxerror(ls, "no loop to break"); 1237 luaX_syntaxerror(ls, "no loop to break");
1223 luaK_concat(fs, &fs->bl->brks, pc);
1224} 1238}
1225 1239
1226 1240
1227/* check for repeated labels on the same block */ 1241/*
1228static void checkrepeated (FuncState *fs, Labellist *ll, TString *label) { 1242** Check whether there is already a label with the given 'name'.
1229 int i; 1243*/
1230 for (i = fs->bl->firstlabel; i < ll->n; i++) { 1244static void checkrepeated (LexState *ls, TString *name) {
1231 if (eqstr(label, ll->arr[i].name)) { 1245 Labeldesc *lb = findlabel(ls, name);
1232 const char *msg = luaO_pushfstring(fs->ls->L, 1246 if (lb != NULL) { /* already defined? */
1233 "label '%s' already defined on line %d", 1247 const char *msg = "label '%s' already defined on line %d";
1234 getstr(label), ll->arr[i].line); 1248 msg = luaO_pushfstring(ls->L, msg, getstr(name), lb->line);
1235 luaK_semerror(fs->ls, msg); 1249 luaK_semerror(ls, msg); /* error */
1236 }
1237 } 1250 }
1238} 1251}
1239 1252
1240 1253
1241/* skip no-op statements */ 1254static void labelstat (LexState *ls, TString *name, int line) {
1242static void skipnoopstat (LexState *ls) {
1243 while (ls->t.token == ';' || ls->t.token == TK_DBCOLON)
1244 statement(ls);
1245}
1246
1247
1248static void labelstat (LexState *ls, TString *label, int line) {
1249 /* label -> '::' NAME '::' */ 1255 /* label -> '::' NAME '::' */
1250 FuncState *fs = ls->fs;
1251 Labellist *ll = &ls->dyd->label;
1252 int l; /* index of new label being created */
1253 checkrepeated(fs, ll, label); /* check for repeated labels */
1254 checknext(ls, TK_DBCOLON); /* skip double colon */ 1256 checknext(ls, TK_DBCOLON); /* skip double colon */
1255 /* create new entry for this label */ 1257 while (ls->t.token == ';' || ls->t.token == TK_DBCOLON)
1256 l = newlabelentry(ls, ll, label, line, luaK_getlabel(fs)); 1258 statement(ls); /* skip other no-op statements */
1257 luaK_codeABC(fs, OP_CLOSE, fs->nactvar, 0, 0); 1259 checkrepeated(ls, name); /* check for repeated labels */
1258 skipnoopstat(ls); /* skip other no-op statements */ 1260 createlabel(ls, name, line, block_follow(ls, 0));
1259 if (block_follow(ls, 0)) { /* label is last no-op statement in the block? */
1260 /* assume that locals are already out of scope */
1261 ll->arr[l].nactvar = fs->bl->nactvar;
1262 }
1263 solvegotos(ls, &ll->arr[l]);
1264} 1261}
1265 1262
1266 1263
@@ -1295,8 +1292,6 @@ static void repeatstat (LexState *ls, int line) {
1295 statlist(ls); 1292 statlist(ls);
1296 check_match(ls, TK_UNTIL, TK_REPEAT, line); 1293 check_match(ls, TK_UNTIL, TK_REPEAT, line);
1297 condexit = cond(ls); /* read condition (inside scope block) */ 1294 condexit = cond(ls); /* read condition (inside scope block) */
1298 if (bl2.upval) /* upvalues? */
1299 luaK_patchclose(fs, condexit);
1300 leaveblock(fs); /* finish scope */ 1295 leaveblock(fs); /* finish scope */
1301 if (bl2.upval) { /* upvalues? */ 1296 if (bl2.upval) { /* upvalues? */
1302 int exit = luaK_jump(fs); /* normal exit must jump over fix */ 1297 int exit = luaK_jump(fs); /* normal exit must jump over fix */
@@ -1453,15 +1448,12 @@ static void test_then_block (LexState *ls, int *escapelist) {
1453 luaX_next(ls); /* skip IF or ELSEIF */ 1448 luaX_next(ls); /* skip IF or ELSEIF */
1454 expr(ls, &v); /* read condition */ 1449 expr(ls, &v); /* read condition */
1455 checknext(ls, TK_THEN); 1450 checknext(ls, TK_THEN);
1456 if (ls->t.token == TK_GOTO || ls->t.token == TK_BREAK) { 1451 if (ls->t.token == TK_BREAK) {
1457 luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */ 1452 luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */
1458 enterblock(fs, &bl, 0); /* must enter block before 'goto' */ 1453 enterblock(fs, &bl, 0); /* must enter block before 'goto' */
1459 if (ls->t.token == TK_GOTO) 1454 breakstat(ls, v.t); /* handle break */
1460 gotostat(ls, v.t); /* handle goto */
1461 else
1462 breakstat(ls, v.t); /* handle break */
1463 while (testnext(ls, ';')) {} /* skip semicolons */ 1455 while (testnext(ls, ';')) {} /* skip semicolons */
1464 if (block_follow(ls, 0)) { /* 'goto'/'break' is the entire block? */ 1456 if (block_follow(ls, 0)) { /* 'break' is the entire block? */
1465 leaveblock(fs); 1457 leaveblock(fs);
1466 return; /* and that is it */ 1458 return; /* and that is it */
1467 } 1459 }
@@ -1683,7 +1675,8 @@ static void statement (LexState *ls) {
1683 break; 1675 break;
1684 } 1676 }
1685 case TK_GOTO: { /* stat -> 'goto' NAME */ 1677 case TK_GOTO: { /* stat -> 'goto' NAME */
1686 gotostat(ls, luaK_jump(ls->fs)); 1678 luaX_next(ls); /* skip 'goto' */
1679 gotostat(ls);
1687 break; 1680 break;
1688 } 1681 }
1689 default: { /* stat -> func | assignment */ 1682 default: { /* stat -> func | assignment */
diff --git a/lparser.h b/lparser.h
index 1b94a97a..8b070b0e 100644
--- a/lparser.h
+++ b/lparser.h
@@ -88,6 +88,7 @@ typedef struct Labeldesc {
88 int pc; /* position in code */ 88 int pc; /* position in code */
89 int line; /* line where it appeared */ 89 int line; /* line where it appeared */
90 lu_byte nactvar; /* local level where it appears in current block */ 90 lu_byte nactvar; /* local level where it appears in current block */
91 lu_byte close; /* goto that escapes upvalues */
91} Labeldesc; 92} Labeldesc;
92 93
93 94
@@ -128,6 +129,7 @@ typedef struct FuncState {
128 int np; /* number of elements in 'p' */ 129 int np; /* number of elements in 'p' */
129 int nabslineinfo; /* number of elements in 'abslineinfo' */ 130 int nabslineinfo; /* number of elements in 'abslineinfo' */
130 int firstlocal; /* index of first local var (in Dyndata array) */ 131 int firstlocal; /* index of first local var (in Dyndata array) */
132 int firstlabel; /* index of first label (in 'dyd->label->arr') */
131 short nlocvars; /* number of elements in 'f->locvars' */ 133 short nlocvars; /* number of elements in 'f->locvars' */
132 lu_byte nactvar; /* number of active local variables */ 134 lu_byte nactvar; /* number of active local variables */
133 lu_byte nups; /* number of upvalues */ 135 lu_byte nups; /* number of upvalues */
diff --git a/ltests.c b/ltests.c
index fbcb7475..7cf08891 100644
--- a/ltests.c
+++ b/ltests.c
@@ -550,7 +550,7 @@ static char *buildop (Proto *p, int pc, char *buff) {
550 sprintf(buff, "%-12s%4d", name, GETARG_Ax(i)); 550 sprintf(buff, "%-12s%4d", name, GETARG_Ax(i));
551 break; 551 break;
552 case isJ: 552 case isJ:
553 sprintf(buff, "%-12s%4d (%1d)", name, GETARG_sJ(i), !!GETARG_m(i)); 553 sprintf(buff, "%-12s%4d", name, GETARG_sJ(i));
554 break; 554 break;
555 } 555 }
556 return obuff; 556 return obuff;
diff --git a/testes/code.lua b/testes/code.lua
index ad484485..9b3f2b68 100644
--- a/testes/code.lua
+++ b/testes/code.lua
@@ -297,7 +297,7 @@ check(function ()
297 b[a], a = c, b 297 b[a], a = c, b
298 a, b = c, a 298 a, b = c, a
299 a = a 299 a = a
300end, 300end,
301 'LOADNIL', 301 'LOADNIL',
302 'MOVE', 'MOVE', 'SETTABLE', 302 'MOVE', 'MOVE', 'SETTABLE',
303 'MOVE', 'MOVE', 'MOVE', 'SETTABLE', 303 'MOVE', 'MOVE', 'MOVE', 'SETTABLE',
@@ -329,18 +329,13 @@ checkequal(function (l) local a; return 0 <= a and a <= l end,
329 function (l) local a; return not (not(a >= 0) or not(a <= l)) end) 329 function (l) local a; return not (not(a >= 0) or not(a <= l)) end)
330 330
331 331
332-- if-goto optimizations 332-- if-break optimizations
333check(function (a, b, c, d, e) 333check(function (a, b)
334 if a == b then goto l1; 334 while a do
335 elseif a == c then goto l2; 335 if b then break else a = a + 1 end
336 elseif a == d then goto l2;
337 else if a == e then goto l3;
338 else goto l3
339 end
340 end 336 end
341 ::l1:: ::l2:: ::l3:: ::l4:: 337 end,
342end, 'EQ', 'JMP', 'EQ', 'JMP', 'EQ', 'JMP', 'EQ', 'JMP', 'JMP', 338'TEST', 'JMP', 'TEST', 'JMP', 'ADDI', 'JMP', 'RETURN0')
343'CLOSE', 'CLOSE', 'CLOSE', 'CLOSE', 'RETURN0')
344 339
345checkequal( 340checkequal(
346function (a) while a < 10 do a = a + 1 end end, 341function (a) while a < 10 do a = a + 1 end end,
diff --git a/testes/goto.lua b/testes/goto.lua
index 238bc04a..85038d02 100644
--- a/testes/goto.lua
+++ b/testes/goto.lua
@@ -14,6 +14,7 @@ errmsg([[ do ::l1:: end goto l1; ]], "label 'l1'")
14 14
15-- repeated label 15-- repeated label
16errmsg([[ ::l1:: ::l1:: ]], "label 'l1'") 16errmsg([[ ::l1:: ::l1:: ]], "label 'l1'")
17errmsg([[ ::l1:: do ::l1:: end]], "label 'l1'")
17 18
18 19
19-- undefined label 20-- undefined label
@@ -67,8 +68,6 @@ do
67 assert(assert(load(prog))() == 31) 68 assert(assert(load(prog))() == 31)
68end 69end
69 70
70-- goto to correct label when nested
71do goto l3; ::l3:: end -- does not loop jumping to previous label 'l3'
72 71
73-- ok to jump over local dec. to end of block 72-- ok to jump over local dec. to end of block
74do 73do