diff options
-rw-r--r-- | src/lapi.js | 8 | ||||
-rw-r--r-- | src/lcorolib.js | 72 | ||||
-rw-r--r-- | tests/lcorolib.js | 50 |
3 files changed, 126 insertions, 4 deletions
diff --git a/src/lapi.js b/src/lapi.js index 2f2802f..c841ba2 100644 --- a/src/lapi.js +++ b/src/lapi.js @@ -279,6 +279,13 @@ const lua_pushlightuserdata = function(L, p) { assert(L.top <= L.ci.top, "stack overflow"); }; +const lua_pushthread = function(L) { + L.stack[L.top++] = L; + assert(L.top <= L.ci.top, "stack overflow"); + + return L.l_G.mainthread === L; +}; + const lua_pushglobaltable = function(L) { lua_rawgeti(L, lua.LUA_REGISTRYINDEX, lua.LUA_RIDX_GLOBALS); }; @@ -746,6 +753,7 @@ module.exports.lua_pushlstring = lua_pushlstring; module.exports.lua_pushnil = lua_pushnil; module.exports.lua_pushnumber = lua_pushnumber; module.exports.lua_pushstring = lua_pushstring; +module.exports.lua_pushthread = lua_pushthread; module.exports.lua_pushvalue = lua_pushvalue; module.exports.lua_rawequal = lua_rawequal; module.exports.lua_rawget = lua_rawget; diff --git a/src/lcorolib.js b/src/lcorolib.js index 73699a7..b595d4d 100644 --- a/src/lcorolib.js +++ b/src/lcorolib.js @@ -8,6 +8,7 @@ const lapi = require('./lapi.js'); const lauxlib = require('./lauxlib.js'); const lstate = require('./lstate.js'); const ldo = require('./ldo.js'); +const ldebug = require('./ldebug.js'); const CT = lua.constant_types; const TS = lua.thread_status; @@ -46,7 +47,7 @@ const auxresume = function(L, co, narg) { } }; -const luaB_resume = function(L) { +const luaB_coresume = function(L) { let co = getco(L); let r = auxresume(L, co, lapi.lua_gettop(L) - 1); if (r < 0) { @@ -60,6 +61,22 @@ const luaB_resume = function(L) { } }; +const luaB_auxwrap = function(L) { + let co = lapi.lua_tothread(L, lapi.lua_upvalueindex(1)); + let r = auxresume(L, co, lapi.lua_gettop(L)); + if (r < 0) { + if (lapi.lua_type(L, -1) === CT.LUA_TSTRING) { /* error object is a string? */ + lauxlib.luaL_where(L, 1); /* add extra info */ + lapi.lua_insert(L, -2); + lapi.lua_concat(L, 2); + } + + return lapi.lua_error(L); /* propagate error */ + } + + return r; +}; + const luaB_cocreate = function(L) { lauxlib.luaL_checktype(L, 1, CT.LUA_TFUNCTION); let NL = lstate.lua_newthread(L); @@ -68,14 +85,61 @@ const luaB_cocreate = function(L) { return 1; }; +const luaB_cowrap = function(L) { + luaB_cocreate(L); + lapi.lua_pushcclosure(L, luaB_auxwrap, 1); + return 1; +}; + const luaB_yield = function(L) { return ldo.lua_yield(L, lapi.lua_gettop(L)); }; +const luaB_costatus = function(L) { + let co = getco(L); + if (L === co) lapi.lua_pushliteral(L, "running"); + else { + switch (lapi.lua_status(co)) { + case TS.LUA_YIELD: + lapi.lua_pushliteral(L, "suspended"); + break; + case TS.LUA_OK: { + let ar = new lua.lua_Debug(); + if (ldebug.lua_getstack(co, 0, ar) > 0) /* does it have frames? */ + lapi.lua_pushliteral(L, "normal"); /* it is running */ + else if (lapi.lua_gettop(co) === 0) + lapi.lua_pushliteral(L, "dead"); + else + lapi.lua_pushliteral(L, "suspended"); /* initial state */ + break; + } + default: /* some error occurred */ + lapi.lua_pushliteral(L, "dead"); + break; + } + } + + return 1; +}; + +const luaB_yieldable = function(L) { + lapi.lua_pushboolean(L, lapi.lua_isyieldable(L)); + return 1; +}; + +const luaB_corunning = function(L) { + lapi.lua_pushboolean(L, lapi.lua_pushthread(L)); + return 2; +}; + const co_funcs = { - "create": luaB_cocreate, - "yield": luaB_yield, - "resume": luaB_resume + "create": luaB_cocreate, + "isyieldable": luaB_yieldable, + "resume": luaB_coresume, + "running": luaB_corunning, + "status": luaB_costatus, + "wrap": luaB_cowrap, + "yield": luaB_yield }; const luaopen_coroutine = function(L) { diff --git a/tests/lcorolib.js b/tests/lcorolib.js index 6b48912..400e721 100644 --- a/tests/lcorolib.js +++ b/tests/lcorolib.js @@ -16,6 +16,7 @@ const lua = require('../src/lua.js'); const linit = require('../src/linit.js'); const CT = lua.constant_types; + test('simple coroutine', function (t) { let luaCode = ` local co = coroutine.create(function (start) @@ -50,4 +51,53 @@ test('simple coroutine', function (t) { 625, "Correct element(s) on the stack" ); +}); + + +test('simple coroutine', function (t) { + let luaCode = ` + local co = coroutine.create(function (start) + local b = coroutine.yield(start * start); + coroutine.yield(b * b) + end) + + local s1 = coroutine.status(co) + + local success, pow = coroutine.resume(co, 5) + success, pow = coroutine.resume(co, pow) + + coroutine.resume(co, pow) + + local s2 = coroutine.status(co) + + return s1, s2 + `, L; + + t.plan(3); + + // t.doesNotThrow(function () { + + let bc = toByteCode(luaCode).dataView; + + L = lauxlib.luaL_newstate(); + + linit.luaL_openlibs(L); + + lapi.lua_load(L, bc, "test-coroutine"); + + lapi.lua_call(L, 0, -1); + + // }, "JS Lua program ran without error"); + + t.strictEqual( + lapi.lua_tostring(L, -2), + "suspended", + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tostring(L, -1), + "dead", + "Correct element(s) on the stack" + ); });
\ No newline at end of file |