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 | |
| 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.
| -rw-r--r-- | lcode.c | 34 | ||||
| -rw-r--r-- | lcode.h | 3 | ||||
| -rw-r--r-- | lopcodes.h | 9 | ||||
| -rw-r--r-- | lparser.c | 241 | ||||
| -rw-r--r-- | lparser.h | 2 | ||||
| -rw-r--r-- | ltests.c | 2 | ||||
| -rw-r--r-- | testes/code.lua | 19 | ||||
| -rw-r--r-- | testes/goto.lua | 3 |
8 files changed, 131 insertions, 182 deletions
| @@ -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 | */ | ||
| 284 | void 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 | */ | ||
| 304 | void 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 | */ |
| @@ -78,10 +78,7 @@ LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e); | |||
| 78 | LUAI_FUNC int luaK_jump (FuncState *fs); | 78 | LUAI_FUNC int luaK_jump (FuncState *fs); |
| 79 | LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret); | 79 | LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret); |
| 80 | LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target); | 80 | LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target); |
| 81 | LUAI_FUNC void luaK_patchgoto (FuncState *fs, int list, int target, | ||
| 82 | int hasclose); | ||
| 83 | LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list); | 81 | LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list); |
| 84 | LUAI_FUNC void luaK_patchclose (FuncState *fs, int list); | ||
| 85 | LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2); | 82 | LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2); |
| 86 | LUAI_FUNC int luaK_getlabel (FuncState *fs); | 83 | LUAI_FUNC int luaK_getlabel (FuncState *fs); |
| 87 | LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line); | 84 | LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line); |
| @@ -21,7 +21,7 @@ iABC C(8) | B(8) |k| A(8) | Op(7) | | |||
| 21 | iABx Bx(17) | A(8) | Op(7) | | 21 | iABx Bx(17) | A(8) | Op(7) | |
| 22 | iAsB sBx (signed)(17) | A(8) | Op(7) | | 22 | iAsB sBx (signed)(17) | A(8) | Op(7) | |
| 23 | iAx Ax(25) | Op(7) | | 23 | iAx Ax(25) | Op(7) | |
| 24 | isJ sJ(24) |m| Op(7) | | 24 | isJ 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) \ |
| @@ -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 */ |
| @@ -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 */ |
| @@ -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 |
| 300 | end, | 300 | end, |
| 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 |
| 333 | check(function (a, b, c, d, e) | 333 | check(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, |
| 342 | end, '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 | ||
| 345 | checkequal( | 340 | checkequal( |
| 346 | function (a) while a < 10 do a = a + 1 end end, | 341 | function (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 |
| 16 | errmsg([[ ::l1:: ::l1:: ]], "label 'l1'") | 16 | errmsg([[ ::l1:: ::l1:: ]], "label 'l1'") |
| 17 | errmsg([[ ::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) |
| 68 | end | 69 | end |
| 69 | 70 | ||
| 70 | -- goto to correct label when nested | ||
| 71 | do 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 |
| 74 | do | 73 | do |
