Initial draft of coroutines library.
This commit is contained in:
95
src/addon/java/lua/addon/luacompat/CoroutinesLib.java
Normal file
95
src/addon/java/lua/addon/luacompat/CoroutinesLib.java
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
package lua.addon.luacompat;
|
||||||
|
|
||||||
|
import lua.GlobalState;
|
||||||
|
import lua.VM;
|
||||||
|
import lua.io.Closure;
|
||||||
|
import lua.value.LFunction;
|
||||||
|
import lua.value.LTable;
|
||||||
|
import lua.value.LThread;
|
||||||
|
|
||||||
|
|
||||||
|
public class CoroutinesLib extends LFunction {
|
||||||
|
|
||||||
|
public static void install() {
|
||||||
|
LTable lib = new LTable(0,6);
|
||||||
|
lib.put("create", new CoroutinesLib(1));
|
||||||
|
lib.put("resume", new CoroutinesLib(2));
|
||||||
|
lib.put("running", new CoroutinesLib(3));
|
||||||
|
lib.put("status", new CoroutinesLib(4));
|
||||||
|
lib.put("wrap", new CoroutinesLib(5));
|
||||||
|
lib.put("yield", new CoroutinesLib(6));
|
||||||
|
GlobalState.getGlobalsTable().put("coroutine",lib);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int id = 0;
|
||||||
|
|
||||||
|
private static LThread running;
|
||||||
|
|
||||||
|
public CoroutinesLib() {
|
||||||
|
this(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CoroutinesLib( int id ) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean luaStackCall( VM vm ) {
|
||||||
|
switch ( id ) {
|
||||||
|
case 0: { // load lib
|
||||||
|
install();
|
||||||
|
vm.pushnil();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: { // create
|
||||||
|
Closure c = (Closure) vm.topointer(2);
|
||||||
|
vm.pushlvalue( new LThread(c) );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {// resume
|
||||||
|
LThread t = (LThread) vm.topointer(2);
|
||||||
|
LThread prior = running;
|
||||||
|
try {
|
||||||
|
// whatever is left on the stack by the resumeFrom() implementation
|
||||||
|
// becomes return values!
|
||||||
|
running = t;
|
||||||
|
t.resumeFrom( vm, prior );
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
running = prior;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 3: { // running
|
||||||
|
if ( running != null ) {
|
||||||
|
vm.pushlvalue( running );
|
||||||
|
} else {
|
||||||
|
vm.pushnil();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4: { // status
|
||||||
|
vm.pushstring( ((LThread) vm.topointer(2)).getStatus() );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 5: { // wrap
|
||||||
|
vm.error( "wrap() not supported" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case 6: { // yield
|
||||||
|
if ( running == null )
|
||||||
|
vm.error("main thread can't yield");
|
||||||
|
else {
|
||||||
|
return running.yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 7: { // wrapped resume
|
||||||
|
vm.error( "wrap() not supported" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vm.insert(1);
|
||||||
|
vm.settop(1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -55,6 +55,9 @@ public class LuaCompat extends LFunction {
|
|||||||
LTable table = new LTable();
|
LTable table = new LTable();
|
||||||
installNames( pckg, TABLE_NAMES, TABLES_BASE );
|
installNames( pckg, TABLE_NAMES, TABLES_BASE );
|
||||||
globals.put( "table", pckg );
|
globals.put( "table", pckg );
|
||||||
|
|
||||||
|
// coroutines
|
||||||
|
CoroutinesLib.install();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void installNames( LTable table, String[] names, int indexBase ) {
|
private static void installNames( LTable table, String[] names, int indexBase ) {
|
||||||
|
|||||||
@@ -1382,7 +1382,7 @@ public interface VM {
|
|||||||
* This function pops <code>n</code> values from the stack
|
* This function pops <code>n</code> values from the stack
|
||||||
* <code>from</code>, and pushes them onto the stack <code>to</code>.
|
* <code>from</code>, and pushes them onto the stack <code>to</code>.
|
||||||
*/
|
*/
|
||||||
public void xmove(StackState to, int n);
|
public void xmove(VM to, int n);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Yields a coroutine. <span class="apii">[-?, +?, <em>-</em>]</span>
|
* Yields a coroutine. <span class="apii">[-?, +?, <em>-</em>]</span>
|
||||||
|
|||||||
@@ -3,20 +3,35 @@ package lua.io;
|
|||||||
import lua.StackState;
|
import lua.StackState;
|
||||||
import lua.VM;
|
import lua.VM;
|
||||||
import lua.value.LFunction;
|
import lua.value.LFunction;
|
||||||
import lua.value.LValue;
|
import lua.value.LTable;
|
||||||
|
|
||||||
public class Closure extends LFunction {
|
public class Closure extends LFunction {
|
||||||
public LValue env;
|
public LTable env;
|
||||||
public Proto p;
|
public Proto p;
|
||||||
public UpVal[] upVals;
|
public UpVal[] upVals;
|
||||||
|
|
||||||
// TODO: change arg type to VM?
|
/**
|
||||||
|
* @deprecated construct with environment instead
|
||||||
|
* @param state
|
||||||
|
* @param p
|
||||||
|
*/
|
||||||
public Closure(StackState state, Proto p) {
|
public Closure(StackState state, Proto p) {
|
||||||
this.env = state._G;
|
this.env = state._G;
|
||||||
this.p = p;
|
this.p = p;
|
||||||
upVals = new UpVal[p.nups];
|
upVals = new UpVal[p.nups];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct using a prototype and initial environment.
|
||||||
|
* @param p
|
||||||
|
* @param env
|
||||||
|
*/
|
||||||
|
public Closure(Proto p, LTable env) {
|
||||||
|
this.p = p;
|
||||||
|
this.env = env;
|
||||||
|
upVals = new UpVal[p.nups];
|
||||||
|
}
|
||||||
|
|
||||||
// called by vm when there is an OP_CALL
|
// called by vm when there is an OP_CALL
|
||||||
// in this case, we are on the stack,
|
// in this case, we are on the stack,
|
||||||
// and simply need to cue the VM to treat it as a stack call
|
// and simply need to cue the VM to treat it as a stack call
|
||||||
|
|||||||
@@ -1,9 +1,33 @@
|
|||||||
package lua.value;
|
package lua.value;
|
||||||
|
|
||||||
import lua.Lua;
|
import lua.Lua;
|
||||||
|
import lua.StackState;
|
||||||
|
import lua.VM;
|
||||||
|
import lua.io.Closure;
|
||||||
|
|
||||||
public class LThread extends LValue {
|
public class LThread extends LValue {
|
||||||
|
|
||||||
|
private static final int STATUS_SUSPENDED = 1;
|
||||||
|
private static final int STATUS_NORMAL = 2;
|
||||||
|
private static final int STATUS_ACTIVE = 3;
|
||||||
|
private static final int STATUS_DEAD = 4;
|
||||||
|
private static final String[] NAMES = {
|
||||||
|
"suspended",
|
||||||
|
"normal",
|
||||||
|
"active",
|
||||||
|
"dead" };
|
||||||
|
|
||||||
|
private int status = STATUS_SUSPENDED;
|
||||||
|
|
||||||
|
private StackState threadVm;
|
||||||
|
|
||||||
|
|
||||||
|
public LThread(Closure c) {
|
||||||
|
// TODO: inherit globals!
|
||||||
|
threadVm = new StackState();
|
||||||
|
threadVm.pushlvalue(new Closure(c.p, threadVm._G));
|
||||||
|
}
|
||||||
|
|
||||||
public int luaGetType() {
|
public int luaGetType() {
|
||||||
return Lua.LUA_TTHREAD;
|
return Lua.LUA_TTHREAD;
|
||||||
}
|
}
|
||||||
@@ -11,4 +35,74 @@ public class LThread extends LValue {
|
|||||||
public String toJavaString() {
|
public String toJavaString() {
|
||||||
return "thread: "+hashCode();
|
return "thread: "+hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return NAMES[status];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This needs to leave any values returned by yield in the corouting
|
||||||
|
* on the calling vm stack
|
||||||
|
* @param vm
|
||||||
|
* @param prior
|
||||||
|
*/
|
||||||
|
public void resumeFrom(VM vm, LThread prior) {
|
||||||
|
|
||||||
|
if ( status == STATUS_DEAD ) {
|
||||||
|
vm.settop(0);
|
||||||
|
vm.pushboolean(false);
|
||||||
|
vm.pushstring("cannot resume dead coroutine");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set prior thread to normal status while we are running
|
||||||
|
if ( prior != null )
|
||||||
|
prior.status = STATUS_NORMAL;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// copy args in
|
||||||
|
if ( threadVm.cc < 0 ) {
|
||||||
|
vm.xmove(threadVm, vm.gettop() - 2);
|
||||||
|
threadVm.prepStackCall();
|
||||||
|
} else {
|
||||||
|
threadVm.settop(0);
|
||||||
|
vm.xmove(threadVm, vm.gettop() - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// run this vm until it yields
|
||||||
|
status = STATUS_ACTIVE;
|
||||||
|
while ( threadVm.cc >= 0 && status == STATUS_ACTIVE )
|
||||||
|
threadVm.exec();
|
||||||
|
|
||||||
|
// copy return values from yielding stack state
|
||||||
|
vm.settop(0);
|
||||||
|
vm.pushboolean(true);
|
||||||
|
if ( threadVm.cc >= 0 ) {
|
||||||
|
threadVm.xmove(vm, threadVm.gettop() - 1);
|
||||||
|
} else {
|
||||||
|
threadVm.base = 0;
|
||||||
|
threadVm.xmove(vm, threadVm.gettop());
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch ( Throwable t ) {
|
||||||
|
status = STATUS_DEAD;
|
||||||
|
vm.settop(0);
|
||||||
|
vm.pushboolean(false);
|
||||||
|
vm.pushstring("thread: "+t);
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
if ( threadVm.cc < 0 )
|
||||||
|
status = STATUS_DEAD;
|
||||||
|
|
||||||
|
// reset prior thread status
|
||||||
|
if ( prior != null )
|
||||||
|
prior.status = STATUS_ACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean yield() {
|
||||||
|
if ( status == STATUS_ACTIVE )
|
||||||
|
status = STATUS_SUSPENDED;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,10 @@ public class LuaJTest extends TestCase {
|
|||||||
runTest( "coercions" );
|
runTest( "coercions" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testCoroutines() throws IOException, InterruptedException {
|
||||||
|
runTest( "coroutines" );
|
||||||
|
}
|
||||||
|
|
||||||
public void testCompare() throws IOException, InterruptedException {
|
public void testCompare() throws IOException, InterruptedException {
|
||||||
runTest( "compare" );
|
runTest( "compare" );
|
||||||
}
|
}
|
||||||
|
|||||||
18
src/test/res/coroutines.lua
Normal file
18
src/test/res/coroutines.lua
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
function foo (a)
|
||||||
|
print("foo", a)
|
||||||
|
return coroutine.yield(2*a)
|
||||||
|
end
|
||||||
|
|
||||||
|
co = coroutine.create(function (a,b)
|
||||||
|
print("co-body", a, b)
|
||||||
|
local r = foo(a+1)
|
||||||
|
print("co-body", r)
|
||||||
|
local r, s = coroutine.yield(a+b, a-b)
|
||||||
|
print("co-body", r, s)
|
||||||
|
return b, "end"
|
||||||
|
end)
|
||||||
|
|
||||||
|
print("main", coroutine.resume(co, 1, 10))
|
||||||
|
print("main", coroutine.resume(co, "r"))
|
||||||
|
print("main", coroutine.resume(co, "x", "y"))
|
||||||
|
print("main", coroutine.resume(co, "x", "y"))
|
||||||
BIN
src/test/res/coroutines.luac
Normal file
BIN
src/test/res/coroutines.luac
Normal file
Binary file not shown.
Reference in New Issue
Block a user