diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-10-29 14:26:48 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-10-29 14:26:48 -0300 |
commit | a006514ea138a29b6031058d9002b48a572b5dd6 (patch) | |
tree | b289a8af0c0497f2555784a0cf666659ceab0236 /lparser.c | |
parent | 6e9b719694bffb8de711f182d405ec37d32ae0b1 (diff) | |
download | lua-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.
Diffstat (limited to 'lparser.c')
-rw-r--r-- | lparser.c | 241 |
1 files changed, 117 insertions, 124 deletions
@@ -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 | ||
333 | static 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 | */ | ||
335 | static 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 | */ | ||
348 | static 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 | */ |
357 | static int solvelabel (LexState *ls, int g) { | 365 | static 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 | */ | ||
377 | static int newlabelentry (LexState *ls, Labellist *l, TString *name, | 381 | static 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 | */ |
395 | static void solvegotos (LexState *ls, Labeldesc *lb) { | 401 | static 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 | */ | ||
424 | static 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 | */ |
414 | static void movegotosout (FuncState *fs, BlockCnt *bl) { | 444 | static 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 | */ | ||
457 | static 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 | */ |
471 | static l_noret undefgoto (LexState *ls, Labeldesc *gt) { | 473 | static l_noret undefgoto (LexState *ls, Labeldesc *gt) { |
@@ -478,11 +480,10 @@ static l_noret undefgoto (LexState *ls, Labeldesc *gt) { | |||
478 | static void leaveblock (FuncState *fs) { | 480 | static 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 | ||
1207 | static void gotostat (LexState *ls, int pc) { | 1208 | static 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 | */ | ||
1216 | static void breakstat (LexState *ls, int pc) { | 1229 | static 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 | /* |
1228 | static 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++) { | 1244 | static 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 */ | 1254 | static void labelstat (LexState *ls, TString *name, int line) { |
1242 | static void skipnoopstat (LexState *ls) { | ||
1243 | while (ls->t.token == ';' || ls->t.token == TK_DBCOLON) | ||
1244 | statement(ls); | ||
1245 | } | ||
1246 | |||
1247 | |||
1248 | static 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 */ |