diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2008-10-28 14:53:16 -0200 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2008-10-28 14:53:16 -0200 |
| commit | f6978173e03fdbe5536887b08beb53884f7c1a18 (patch) | |
| tree | bc31173c72e358649dda82a532c29066db352a70 | |
| parent | e02483dbc031ca0cdba17cb511c1e674551f90c5 (diff) | |
| download | lua-f6978173e03fdbe5536887b08beb53884f7c1a18.tar.gz lua-f6978173e03fdbe5536887b08beb53884f7c1a18.tar.bz2 lua-f6978173e03fdbe5536887b08beb53884f7c1a18.zip | |
yields accross metamethods and for iterators (except for __concat)
| -rw-r--r-- | ldo.c | 69 | ||||
| -rw-r--r-- | lvm.c | 55 |
2 files changed, 84 insertions, 40 deletions
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: ldo.c,v 2.47 2008/08/13 17:02:42 roberto Exp roberto $ | 2 | ** $Id: ldo.c,v 2.48 2008/08/26 13:27:42 roberto Exp roberto $ |
| 3 | ** Stack and Call structure of Lua | 3 | ** Stack and Call structure of Lua |
| 4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
| 5 | */ | 5 | */ |
| @@ -374,7 +374,6 @@ int luaD_poscall (lua_State *L, StkId firstResult) { | |||
| 374 | */ | 374 | */ |
| 375 | void luaD_call (lua_State *L, StkId func, int nResults) { | 375 | void luaD_call (lua_State *L, StkId func, int nResults) { |
| 376 | global_State *g = G(L); | 376 | global_State *g = G(L); |
| 377 | lua_assert(g->nCcalls >= L->baseCcalls); | ||
| 378 | if (++g->nCcalls >= LUAI_MAXCCALLS) { | 377 | if (++g->nCcalls >= LUAI_MAXCCALLS) { |
| 379 | if (g->nCcalls == LUAI_MAXCCALLS) | 378 | if (g->nCcalls == LUAI_MAXCCALLS) |
| 380 | luaG_runerror(L, "C stack overflow"); | 379 | luaG_runerror(L, "C stack overflow"); |
| @@ -388,6 +387,55 @@ void luaD_call (lua_State *L, StkId func, int nResults) { | |||
| 388 | } | 387 | } |
| 389 | 388 | ||
| 390 | 389 | ||
| 390 | static void unroll (lua_State *L) { | ||
| 391 | for (;;) { | ||
| 392 | Instruction inst; | ||
| 393 | luaV_execute(L); /* execute up to higher C 'boundary' */ | ||
| 394 | if (L->ci == L->base_ci) /* stack is empty? */ | ||
| 395 | return; /* coroutine finished normally */ | ||
| 396 | L->baseCcalls--; /* undo increment that allows yields */ | ||
| 397 | inst = *(L->savedpc - 1); /* interrupted instruction */ | ||
| 398 | switch (GET_OPCODE(inst)) { /* finish its execution */ | ||
| 399 | case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: | ||
| 400 | case OP_MOD: case OP_POW: case OP_UNM: case OP_LEN: | ||
| 401 | case OP_GETGLOBAL: case OP_GETTABLE: case OP_SELF: { | ||
| 402 | setobjs2s(L, L->base + GETARG_A(inst), --L->top); | ||
| 403 | break; | ||
| 404 | } | ||
| 405 | case OP_LE: case OP_LT: case OP_EQ: { | ||
| 406 | int res; | ||
| 407 | L->top--; | ||
| 408 | res = !l_isfalse(L->top); | ||
| 409 | /* cannot call metamethod with K operand */ | ||
| 410 | lua_assert(!ISK(GETARG_B(inst))); | ||
| 411 | if (GET_OPCODE(inst) == OP_LE && /* "<=" using "<" instead? */ | ||
| 412 | ttisnil(luaT_gettmbyobj(L, L->base + GETARG_B(inst), TM_LE))) | ||
| 413 | res = !res; /* invert result */ | ||
| 414 | lua_assert(GET_OPCODE(*L->savedpc) == OP_JMP); | ||
| 415 | if (res == GETARG_A(inst)) | ||
| 416 | L->savedpc += GETARG_sBx(*L->savedpc); /* jump */ | ||
| 417 | L->savedpc++; /* skip jump instruction */ | ||
| 418 | break; | ||
| 419 | } | ||
| 420 | case OP_SETGLOBAL: case OP_SETTABLE: | ||
| 421 | break; /* nothing to be done */ | ||
| 422 | case OP_TFORLOOP: { | ||
| 423 | StkId cb = L->base + GETARG_A(inst) + 3; | ||
| 424 | L->top = L->ci->top; | ||
| 425 | lua_assert(GET_OPCODE(*L->savedpc) == OP_JMP); | ||
| 426 | if (!ttisnil(cb)) { /* continue loop? */ | ||
| 427 | setobjs2s(L, cb - 1, cb); /* save control variable */ | ||
| 428 | L->savedpc += GETARG_sBx(*L->savedpc); /* jump back */ | ||
| 429 | } | ||
| 430 | L->savedpc++; | ||
| 431 | break; | ||
| 432 | } | ||
| 433 | default: lua_assert(0); | ||
| 434 | } | ||
| 435 | } | ||
| 436 | } | ||
| 437 | |||
| 438 | |||
| 391 | static void resume (lua_State *L, void *ud) { | 439 | static void resume (lua_State *L, void *ud) { |
| 392 | StkId firstArg = cast(StkId, ud); | 440 | StkId firstArg = cast(StkId, ud); |
| 393 | CallInfo *ci = L->ci; | 441 | CallInfo *ci = L->ci; |
| @@ -399,17 +447,17 @@ static void resume (lua_State *L, void *ud) { | |||
| 399 | else { /* resuming from previous yield */ | 447 | else { /* resuming from previous yield */ |
| 400 | lua_assert(L->status == LUA_YIELD); | 448 | lua_assert(L->status == LUA_YIELD); |
| 401 | L->status = LUA_OK; | 449 | L->status = LUA_OK; |
| 402 | if (!isLua(ci)) { /* `common' yield? */ | 450 | if (isLua(ci)) /* yielded inside a hook? */ |
| 451 | L->base = L->ci->base; /* just continue its execution */ | ||
| 452 | else { /* 'common' yield */ | ||
| 403 | /* finish interrupted execution of `OP_CALL' */ | 453 | /* finish interrupted execution of `OP_CALL' */ |
| 404 | lua_assert(GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_CALL || | 454 | lua_assert(GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_CALL || |
| 405 | GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_TAILCALL); | 455 | GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_TAILCALL); |
| 406 | if (luaD_poscall(L, firstArg)) /* complete it... */ | 456 | if (luaD_poscall(L, firstArg)) /* complete it... */ |
| 407 | L->top = L->ci->top; /* and correct top if not multiple results */ | 457 | L->top = L->ci->top; /* and correct top if not multiple results */ |
| 408 | } | 458 | } |
| 409 | else /* yielded inside a hook: just continue its execution */ | ||
| 410 | L->base = L->ci->base; | ||
| 411 | } | 459 | } |
| 412 | luaV_execute(L); | 460 | unroll(L); |
| 413 | } | 461 | } |
| 414 | 462 | ||
| 415 | 463 | ||
| @@ -432,10 +480,11 @@ LUA_API int lua_resume (lua_State *L, int nargs) { | |||
| 432 | return resume_error(L, "cannot resume non-suspended coroutine"); | 480 | return resume_error(L, "cannot resume non-suspended coroutine"); |
| 433 | } | 481 | } |
| 434 | luai_userstateresume(L, nargs); | 482 | luai_userstateresume(L, nargs); |
| 435 | lua_assert(L->errfunc == 0 && L->baseCcalls == 0); | 483 | lua_assert(L->errfunc == 0); |
| 436 | if (G(L)->nCcalls >= LUAI_MAXCCALLS) | 484 | if (G(L)->nCcalls >= LUAI_MAXCCALLS) |
| 437 | return resume_error(L, "C stack overflow"); | 485 | return resume_error(L, "C stack overflow"); |
| 438 | L->baseCcalls = ++G(L)->nCcalls; | 486 | ++G(L)->nCcalls; /* count resume */ |
| 487 | L->baseCcalls += G(L)->nCcalls; | ||
| 439 | status = luaD_rawrunprotected(L, resume, L->top - nargs); | 488 | status = luaD_rawrunprotected(L, resume, L->top - nargs); |
| 440 | if (status != LUA_OK && status != LUA_YIELD) { /* error? */ | 489 | if (status != LUA_OK && status != LUA_YIELD) { /* error? */ |
| 441 | L->status = cast_byte(status); /* mark thread as `dead' */ | 490 | L->status = cast_byte(status); /* mark thread as `dead' */ |
| @@ -443,11 +492,10 @@ LUA_API int lua_resume (lua_State *L, int nargs) { | |||
| 443 | L->ci->top = L->top; | 492 | L->ci->top = L->top; |
| 444 | } | 493 | } |
| 445 | else { | 494 | else { |
| 446 | lua_assert(L->baseCcalls == G(L)->nCcalls); | ||
| 447 | lua_assert(status == L->status); | 495 | lua_assert(status == L->status); |
| 448 | } | 496 | } |
| 497 | L->baseCcalls -= G(L)->nCcalls; | ||
| 449 | --G(L)->nCcalls; | 498 | --G(L)->nCcalls; |
| 450 | L->baseCcalls = 0; | ||
| 451 | lua_unlock(L); | 499 | lua_unlock(L); |
| 452 | return status; | 500 | return status; |
| 453 | } | 501 | } |
| @@ -456,6 +504,7 @@ LUA_API int lua_resume (lua_State *L, int nargs) { | |||
| 456 | LUA_API int lua_yield (lua_State *L, int nresults) { | 504 | LUA_API int lua_yield (lua_State *L, int nresults) { |
| 457 | luai_userstateyield(L, nresults); | 505 | luai_userstateyield(L, nresults); |
| 458 | lua_lock(L); | 506 | lua_lock(L); |
| 507 | /*printf("yield: %d - %d\n", G(L)->nCcalls, L->baseCcalls);*/ | ||
| 459 | if (G(L)->nCcalls > L->baseCcalls) | 508 | if (G(L)->nCcalls > L->baseCcalls) |
| 460 | luaG_runerror(L, "attempt to yield across metamethod/C-call boundary"); | 509 | luaG_runerror(L, "attempt to yield across metamethod/C-call boundary"); |
| 461 | L->base = L->top - nresults; /* protect stack slots below */ | 510 | L->base = L->top - nresults; /* protect stack slots below */ |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: lvm.c,v 2.76 2008/08/26 13:27:42 roberto Exp roberto $ | 2 | ** $Id: lvm.c,v 2.77 2008/09/09 13:53:02 roberto Exp roberto $ |
| 3 | ** Lua virtual machine | 3 | ** Lua virtual machine |
| 4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
| 5 | */ | 5 | */ |
| @@ -76,31 +76,24 @@ static void traceexec (lua_State *L) { | |||
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | 78 | ||
| 79 | static void callTMres (lua_State *L, StkId res, const TValue *f, | ||
| 80 | const TValue *p1, const TValue *p2) { | ||
| 81 | ptrdiff_t result = savestack(L, res); | ||
| 82 | setobj2s(L, L->top, f); /* push function */ | ||
| 83 | setobj2s(L, L->top+1, p1); /* 1st argument */ | ||
| 84 | setobj2s(L, L->top+2, p2); /* 2nd argument */ | ||
| 85 | L->top += 3; | ||
| 86 | luaD_checkstack(L, 0); | ||
| 87 | luaD_call(L, L->top - 3, 1); | ||
| 88 | res = restorestack(L, result); | ||
| 89 | L->top--; | ||
| 90 | setobjs2s(L, res, L->top); | ||
| 91 | } | ||
| 92 | |||
| 93 | |||
| 94 | |||
| 95 | static void callTM (lua_State *L, const TValue *f, const TValue *p1, | 79 | static void callTM (lua_State *L, const TValue *f, const TValue *p1, |
| 96 | const TValue *p2, const TValue *p3) { | 80 | const TValue *p2, TValue *p3, int hasres) { |
| 97 | setobj2s(L, L->top, f); /* push function */ | 81 | ptrdiff_t result = savestack(L, p3); |
| 98 | setobj2s(L, L->top+1, p1); /* 1st argument */ | 82 | int oldbase = L->baseCcalls; |
| 99 | setobj2s(L, L->top+2, p2); /* 2nd argument */ | 83 | setobj2s(L, L->top++, f); /* push function */ |
| 100 | setobj2s(L, L->top+3, p3); /* 3th argument */ | 84 | setobj2s(L, L->top++, p1); /* 1st argument */ |
| 101 | L->top += 4; | 85 | setobj2s(L, L->top++, p2); /* 2nd argument */ |
| 86 | if (!hasres) /* no result? 'p3' is third argument */ | ||
| 87 | setobj2s(L, L->top++, p3); /* 3th argument */ | ||
| 102 | luaD_checkstack(L, 0); | 88 | luaD_checkstack(L, 0); |
| 103 | luaD_call(L, L->top - 4, 0); | 89 | if (isLua(L->ci)) /* metamethod invoked from a Lua function? */ |
| 90 | L->baseCcalls++; /* allow it to yield */ | ||
| 91 | luaD_call(L, L->top - (4 - hasres), hasres); | ||
| 92 | L->baseCcalls = oldbase; | ||
| 93 | if (hasres) { /* if has result, move it to its place */ | ||
| 94 | p3 = restorestack(L, result); | ||
| 95 | setobjs2s(L, p3, --L->top); | ||
| 96 | } | ||
| 104 | } | 97 | } |
| 105 | 98 | ||
| 106 | 99 | ||
| @@ -121,7 +114,7 @@ void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) { | |||
| 121 | else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX))) | 114 | else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX))) |
| 122 | luaG_typeerror(L, t, "index"); | 115 | luaG_typeerror(L, t, "index"); |
| 123 | if (ttisfunction(tm)) { | 116 | if (ttisfunction(tm)) { |
| 124 | callTMres(L, val, tm, t, key); | 117 | callTM(L, tm, t, key, val, 1); |
| 125 | return; | 118 | return; |
| 126 | } | 119 | } |
| 127 | t = tm; /* else repeat with `tm' */ | 120 | t = tm; /* else repeat with `tm' */ |
| @@ -148,7 +141,7 @@ void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) { | |||
| 148 | else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX))) | 141 | else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX))) |
| 149 | luaG_typeerror(L, t, "index"); | 142 | luaG_typeerror(L, t, "index"); |
| 150 | if (ttisfunction(tm)) { | 143 | if (ttisfunction(tm)) { |
| 151 | callTM(L, tm, t, key, val); | 144 | callTM(L, tm, t, key, val, 0); |
| 152 | return; | 145 | return; |
| 153 | } | 146 | } |
| 154 | t = tm; /* else repeat with `tm' */ | 147 | t = tm; /* else repeat with `tm' */ |
| @@ -163,7 +156,7 @@ static int call_binTM (lua_State *L, const TValue *p1, const TValue *p2, | |||
| 163 | if (ttisnil(tm)) | 156 | if (ttisnil(tm)) |
| 164 | tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ | 157 | tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ |
| 165 | if (ttisnil(tm)) return 0; | 158 | if (ttisnil(tm)) return 0; |
| 166 | callTMres(L, res, tm, p1, p2); | 159 | callTM(L, tm, p1, p2, res, 1); |
| 167 | return 1; | 160 | return 1; |
| 168 | } | 161 | } |
| 169 | 162 | ||
| @@ -190,7 +183,7 @@ static int call_orderTM (lua_State *L, const TValue *p1, const TValue *p2, | |||
| 190 | tm2 = luaT_gettmbyobj(L, p2, event); | 183 | tm2 = luaT_gettmbyobj(L, p2, event); |
| 191 | if (!luaO_rawequalObj(tm1, tm2)) /* different metamethods? */ | 184 | if (!luaO_rawequalObj(tm1, tm2)) /* different metamethods? */ |
| 192 | return -1; | 185 | return -1; |
| 193 | callTMres(L, L->top, tm1, p1, p2); | 186 | callTM(L, tm1, p1, p2, L->top, 1); |
| 194 | return !l_isfalse(L->top); | 187 | return !l_isfalse(L->top); |
| 195 | } | 188 | } |
| 196 | 189 | ||
| @@ -268,7 +261,7 @@ int luaV_equalval_ (lua_State *L, const TValue *t1, const TValue *t2) { | |||
| 268 | default: return gcvalue(t1) == gcvalue(t2); | 261 | default: return gcvalue(t1) == gcvalue(t2); |
| 269 | } | 262 | } |
| 270 | if (tm == NULL) return 0; /* no TM? */ | 263 | if (tm == NULL) return 0; /* no TM? */ |
| 271 | callTMres(L, L->top, tm, t1, t2); /* call TM */ | 264 | callTM(L, tm, t1, t2, L->top, 1); /* call TM */ |
| 272 | return !l_isfalse(L->top); | 265 | return !l_isfalse(L->top); |
| 273 | } | 266 | } |
| 274 | 267 | ||
| @@ -336,7 +329,7 @@ static void objlen (lua_State *L, StkId ra, const TValue *rb) { | |||
| 336 | break; | 329 | break; |
| 337 | } | 330 | } |
| 338 | } | 331 | } |
| 339 | callTMres(L, ra, tm, rb, luaO_nilobject); | 332 | callTM(L, tm, rb, luaO_nilobject, ra, 1); |
| 340 | } | 333 | } |
| 341 | 334 | ||
| 342 | 335 | ||
| @@ -680,7 +673,9 @@ void luaV_execute (lua_State *L) { | |||
| 680 | setobjs2s(L, cb+1, ra+1); | 673 | setobjs2s(L, cb+1, ra+1); |
| 681 | setobjs2s(L, cb, ra); | 674 | setobjs2s(L, cb, ra); |
| 682 | L->top = cb+3; /* func. + 2 args (state and index) */ | 675 | L->top = cb+3; /* func. + 2 args (state and index) */ |
| 676 | L->baseCcalls++; | ||
| 683 | Protect(luaD_call(L, cb, GETARG_C(i))); | 677 | Protect(luaD_call(L, cb, GETARG_C(i))); |
| 678 | L->baseCcalls--; | ||
| 684 | L->top = L->ci->top; | 679 | L->top = L->ci->top; |
| 685 | cb = RA(i) + 3; /* previous call may change the stack */ | 680 | cb = RA(i) + 3; /* previous call may change the stack */ |
| 686 | if (!ttisnil(cb)) { /* continue loop? */ | 681 | if (!ttisnil(cb)) { /* continue loop? */ |
