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? */ |