first implementation of coroutines
This commit is contained in:
126
ldo.c
126
ldo.c
@@ -18,6 +18,7 @@
|
||||
#include "lgc.h"
|
||||
#include "lmem.h"
|
||||
#include "lobject.h"
|
||||
#include "lopcodes.h"
|
||||
#include "lparser.h"
|
||||
#include "lstate.h"
|
||||
#include "lstring.h"
|
||||
@@ -112,8 +113,6 @@ void luaD_callHook (lua_State *L, lua_Hook callhook, const char *event) {
|
||||
}
|
||||
|
||||
|
||||
#define newci(L) ((++L->ci == L->end_ci) ? growci(L) : L->ci)
|
||||
|
||||
static CallInfo *growci (lua_State *L) {
|
||||
lua_assert(L->ci == L->end_ci);
|
||||
luaM_reallocvector(L, L->base_ci, L->size_ci, 2*L->size_ci, CallInfo);
|
||||
@@ -124,9 +123,32 @@ static CallInfo *growci (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
static void adjust_varargs (lua_State *L, StkId base, int nfixargs) {
|
||||
int i;
|
||||
Table *htab;
|
||||
TObject n, nname;
|
||||
StkId firstvar = base + nfixargs; /* position of first vararg */
|
||||
if (L->top < firstvar) {
|
||||
luaD_checkstack(L, firstvar - L->top);
|
||||
while (L->top < firstvar)
|
||||
setnilvalue(L->top++);
|
||||
}
|
||||
htab = luaH_new(L, 0, 0);
|
||||
for (i=0; firstvar+i<L->top; i++)
|
||||
luaH_setnum(L, htab, i+1, firstvar+i);
|
||||
/* store counter in field `n' */
|
||||
setnvalue(&n, i);
|
||||
setsvalue(&nname, luaS_newliteral(L, "n"));
|
||||
luaH_set(L, htab, &nname, &n);
|
||||
L->top = firstvar; /* remove elements from the stack */
|
||||
sethvalue(L->top, htab);
|
||||
incr_top;
|
||||
}
|
||||
|
||||
|
||||
StkId luaD_precall (lua_State *L, StkId func) {
|
||||
CallInfo *ci;
|
||||
int n;
|
||||
LClosure *cl;
|
||||
if (ttype(func) != LUA_TFUNCTION) {
|
||||
/* `func' is not a function; check the `function' tag method */
|
||||
const TObject *tm = luaT_gettmbyobj(L, func, TM_CALL);
|
||||
@@ -135,21 +157,39 @@ StkId luaD_precall (lua_State *L, StkId func) {
|
||||
luaD_openstack(L, func);
|
||||
setobj(func, tm); /* tag method is the new function to be called */
|
||||
}
|
||||
ci = newci(L);
|
||||
ci = ++L->ci;
|
||||
if (L->ci == L->end_ci) ci = growci(L);
|
||||
ci->base = func+1;
|
||||
ci->savedpc = NULL;
|
||||
if (L->callhook)
|
||||
luaD_callHook(L, L->callhook, "call");
|
||||
if (!clvalue(func)->c.isC) return NULL;
|
||||
/* if is a C function, call it */
|
||||
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
|
||||
lua_unlock(L);
|
||||
cl = &clvalue(func)->l;
|
||||
if (!cl->isC) { /* Lua function? prepare its call */
|
||||
StkId base = func+1;
|
||||
Proto *p = cl->p;
|
||||
ci->linehook = L->linehook;
|
||||
ci->savedpc = p->code; /* starting point */
|
||||
if (p->is_vararg) /* varargs? */
|
||||
adjust_varargs(L, base, p->numparams);
|
||||
if (base > L->stack_last - p->maxstacksize)
|
||||
luaD_stackerror(L);
|
||||
ci->top = base + p->maxstacksize;
|
||||
while (L->top < ci->top)
|
||||
setnilvalue(L->top++);
|
||||
L->top = ci->top;
|
||||
return NULL;
|
||||
}
|
||||
else { /* if is a C function, call it */
|
||||
int n;
|
||||
ci->savedpc = NULL;
|
||||
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
|
||||
lua_unlock(L);
|
||||
#if LUA_COMPATUPVALUES
|
||||
lua_pushupvalues(L);
|
||||
lua_pushupvalues(L);
|
||||
#endif
|
||||
n = (*clvalue(func)->c.f)(L); /* do the actual call */
|
||||
lua_lock(L);
|
||||
return L->top - n;
|
||||
n = (*clvalue(func)->c.f)(L); /* do the actual call */
|
||||
lua_lock(L);
|
||||
return L->top - n;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -179,12 +219,60 @@ void luaD_poscall (lua_State *L, int wanted, StkId firstResult) {
|
||||
*/
|
||||
void luaD_call (lua_State *L, StkId func, int nResults) {
|
||||
StkId firstResult = luaD_precall(L, func);
|
||||
if (firstResult == NULL) /* is a Lua function? */
|
||||
firstResult = luaV_execute(L, &clvalue(func)->l, func+1); /* call it */
|
||||
if (firstResult == NULL) { /* is a Lua function? */
|
||||
firstResult = luaV_execute(L); /* call it */
|
||||
if (firstResult == NULL) {
|
||||
luaD_poscall(L, 0, L->top);
|
||||
luaD_error(L, "attempt to `yield' across tag-method/C-call boundary");
|
||||
}
|
||||
}
|
||||
luaD_poscall(L, nResults, firstResult);
|
||||
}
|
||||
|
||||
|
||||
LUA_API void lua_cobegin (lua_State *L, int nargs) {
|
||||
StkId func;
|
||||
lua_lock(L);
|
||||
func = L->top - (nargs+1); /* coroutine main function */
|
||||
if (luaD_precall(L, func) != NULL)
|
||||
luaD_error(L, "coroutine started with a C function");
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
||||
|
||||
LUA_API void lua_resume (lua_State *L, lua_State *co) {
|
||||
StkId firstResult;
|
||||
lua_lock(L);
|
||||
if (co->ci->savedpc == NULL) /* no activation record? */
|
||||
luaD_error(L, "thread is dead - cannot be resumed");
|
||||
lua_assert(co->errorJmp == NULL);
|
||||
co->errorJmp = L->errorJmp;
|
||||
firstResult = luaV_execute(co);
|
||||
if (firstResult != NULL) /* `return'? */
|
||||
luaD_poscall(co, LUA_MULTRET, firstResult); /* ends this coroutine */
|
||||
else { /* `yield' */
|
||||
int nresults = GETARG_C(*((co->ci-1)->savedpc - 1)) - 1;
|
||||
luaD_poscall(co, nresults, co->top); /* complete it */
|
||||
if (nresults >= 0) co->top = co->ci->top;
|
||||
}
|
||||
co->errorJmp = NULL;
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
||||
|
||||
LUA_API int lua_yield (lua_State *L, int nresults) {
|
||||
CallInfo *ci;
|
||||
int ibase;
|
||||
lua_lock(L);
|
||||
ci = L->ci - 1; /* call info of calling function */
|
||||
if (ci->pc == NULL)
|
||||
luaD_error(L, "cannot `yield' a C function");
|
||||
ibase = L->top - ci->base;
|
||||
lua_unlock(L);
|
||||
return ibase;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Execute a protected call.
|
||||
*/
|
||||
@@ -330,7 +418,13 @@ static void message (lua_State *L, const char *s) {
|
||||
** Reports an error, and jumps up to the available recovery label
|
||||
*/
|
||||
void luaD_error (lua_State *L, const char *s) {
|
||||
if (s) message(L, s);
|
||||
if (s) {
|
||||
if (L->ci->savedpc) { /* error in Lua function preamble? */
|
||||
L->ci->savedpc = NULL; /* pretend function was already running */
|
||||
L->ci->pc = NULL;
|
||||
}
|
||||
message(L, s);
|
||||
}
|
||||
luaD_breakrun(L, LUA_ERRRUN);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user