•Runtime upgrade to Java 8 or newer.

•Feature: Support Java 21 VirtualThread
This commit is contained in:
qiaoenxin
2024-11-07 16:16:09 +08:00
parent daf3da94e3
commit 4c25b88076
12 changed files with 307 additions and 192 deletions

View File

@@ -1019,6 +1019,11 @@ and at <a href="http://luaj.sourceforge.net/api/2.0/index.html">http://luaj.sour
<li>Let JsePlatform.luaMain() return values returned by the main chunk.</li>
<li>Add synchronization to CoerceJavaToLua.COERCIONS map.</li>
</ul></td></tr>
<tr valign="top"><td>&nbsp;&nbsp;<b>3.1.0</b></td><td><ul>
<li>Runtime upgrade to Java 8 or newer.</li>
<li>Feature: Support Java 21 VirtualThread</li>
</ul></td></tr>
</table></td></tr></table>

View File

@@ -14,7 +14,7 @@
<attribute name="jars" default="**/*.jar"/>
<sequential>
<mkdir dir="lib"/>
<get src="http://luaj.sourceforge.net/lib/@{zipname}.tar.gz"
<get src="https://luaj.sourceforge.net/lib/@{zipname}.tar.gz"
dest="lib/@{zipname}.tar.gz"/>
<gunzip src="lib/@{zipname}.tar.gz" dest="lib/@{zipname}.tar"/>
<untar src="lib/@{zipname}.tar" dest="lib" overwrite="true">

View File

@@ -81,20 +81,22 @@
<pathelement path="lib/midpapi20.jar"/>
<pathelement path="lib/mmapi.jar"/>
</path>
<javac destdir="build/jme/classes" encoding="utf-8" source="1.3" target="1.2" bootclasspathref="wtk-libs"
<javac includeantruntime="false" destdir="build/jme/classes" encoding="utf-8" source="1.8" target="1.8"
debug="on"
srcdir="build/jme/src"/>
<javac destdir="build/jse/classes" encoding="utf-8" source="1.3" target="1.3"
srcdir="build/jme/src">
<classpath refid="wtk-libs"/>
</javac>
<javac includeantruntime="false" destdir="build/jse/classes" encoding="utf-8" source="1.8" target="1.8"
classpath="lib/bcel-5.2.jar"
debug="on"
srcdir="build/jse/src"
excludes="**/script/*,**/Lua2Java*,**/server/*,lua*"/>
<javac destdir="build/jse/classes" encoding="utf-8" source="1.5" target="1.5"
<javac includeantruntime="false" destdir="build/jse/classes" encoding="utf-8" source="1.8" target="1.8"
classpath="build/jse/classes"
debug="on"
srcdir="build/jse/src"
includes="**/script/*,**/Lua2Java*,**/server/*"/>
<javac destdir="build/jse/classes" encoding="utf-8" source="1.3" target="1.3"
<javac includeantruntime="false" destdir="build/jse/classes" encoding="utf-8" source="1.8" target="1.8"
classpath="build/jse/classes"
debug="on"
srcdir="build/jse/src"

View File

@@ -23,6 +23,10 @@ package org.luaj.vm2;
import java.lang.ref.WeakReference;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Subclass of {@link LuaValue} that implements
@@ -82,6 +86,19 @@ public class LuaThread extends LuaValue {
*/
public static long thread_orphan_check_interval = 5000;
public static final String USE_PLATFORM_THREAD = "USE_PLATFORM_THREAD";
private static boolean SUPPORT_VIRTUAL_THREAD = false;
static {
try {
Thread.class.getMethod("ofVirtual");
SUPPORT_VIRTUAL_THREAD = true;
} catch (Exception e) {
//e.printStackTrace();
}
}
public static final int STATUS_INITIAL = 0;
public static final int STATUS_SUSPENDED = 1;
public static final int STATUS_RUNNING = 2;
@@ -184,6 +201,8 @@ public class LuaThread extends LuaValue {
public int bytecodes;
public int status = LuaThread.STATUS_INITIAL;
private Lock locker = new ReentrantLock();
private Condition cond = locker.newCondition();
State(Globals globals, LuaThread lua_thread, LuaValue function) {
this.globals = globals;
@@ -191,68 +210,98 @@ public class LuaThread extends LuaValue {
this.function = function;
}
public synchronized void run() {
public void run() {
locker.lock();
try {
Varargs a = this.args;
this.args = LuaValue.NONE;
this.result = function.invoke(a);
} catch (Throwable t) {
this.error = t.getMessage();
} finally {
this.status = LuaThread.STATUS_DEAD;
this.notify();
}
}
public synchronized Varargs lua_resume(LuaThread new_thread, Varargs args) {
LuaThread previous_thread = globals.running;
try {
globals.running = new_thread;
this.args = args;
if (this.status == STATUS_INITIAL) {
this.status = STATUS_RUNNING;
new Thread(this, "Coroutine-"+(++coroutine_count)).start();
} else {
this.notify();
try {
Varargs a = this.args;
this.args = LuaValue.NONE;
this.result = function.invoke(a);
} catch (Throwable t) {
this.error = t.getMessage();
} finally {
this.status = LuaThread.STATUS_DEAD;
cond.signal();
}
if (previous_thread != null)
previous_thread.state.status = STATUS_NORMAL;
this.status = STATUS_RUNNING;
this.wait();
return (this.error != null?
LuaValue.varargsOf(LuaValue.FALSE, LuaValue.valueOf(this.error)):
LuaValue.varargsOf(LuaValue.TRUE, this.result));
} catch (InterruptedException ie) {
throw new OrphanedThread();
} finally {
this.args = LuaValue.NONE;
this.result = LuaValue.NONE;
this.error = null;
globals.running = previous_thread;
if (previous_thread != null)
globals.running.state.status =STATUS_RUNNING;
locker.unlock();
}
}
public synchronized Varargs lua_yield(Varargs args) {
public Varargs lua_resume(LuaThread new_thread, Varargs args) {
locker.lock();
try {
this.result = args;
this.status = STATUS_SUSPENDED;
this.notify();
do {
this.wait(thread_orphan_check_interval);
if (this.lua_thread.get() == null) {
this.status = STATUS_DEAD;
throw new OrphanedThread();
LuaThread previous_thread = globals.running;
try {
globals.running = new_thread;
this.args = args;
if (this.status == STATUS_INITIAL) {
this.status = STATUS_RUNNING;
Thread t = null;
if(SUPPORT_VIRTUAL_THREAD) {
LuaValue setting = globals.get(USE_PLATFORM_THREAD);
if(setting.isnil()) {//default
if(Thread.currentThread().isVirtual()) {
t = Thread.ofVirtual().name("Coroutine-"+(++coroutine_count)).start(this);
}
} else {
if(!setting.toboolean()) {
t = Thread.ofVirtual().name("Coroutine-"+(++coroutine_count)).start(this);
}
}
}
if (t == null){
new Thread(this, "Coroutine-"+(++coroutine_count)).start();
}
} else {
cond.signal();
}
} while (this.status == STATUS_SUSPENDED);
return this.args;
} catch (InterruptedException ie) {
this.status = STATUS_DEAD;
throw new OrphanedThread();
if (previous_thread != null)
previous_thread.state.status = STATUS_NORMAL;
this.status = STATUS_RUNNING;
cond.await();
return (this.error != null?
LuaValue.varargsOf(LuaValue.FALSE, LuaValue.valueOf(this.error)):
LuaValue.varargsOf(LuaValue.TRUE, this.result));
} catch (InterruptedException ie) {
throw new OrphanedThread();
} finally {
this.args = LuaValue.NONE;
this.result = LuaValue.NONE;
this.error = null;
globals.running = previous_thread;
if (previous_thread != null)
globals.running.state.status =STATUS_RUNNING;
}
} finally {
this.args = LuaValue.NONE;
this.result = LuaValue.NONE;
locker.unlock();
}
}
public Varargs lua_yield(Varargs args) {
locker.lock();
try {
try {
this.result = args;
this.status = STATUS_SUSPENDED;
cond.signal();
do {
cond.await(thread_orphan_check_interval,TimeUnit.MILLISECONDS);
if (this.lua_thread.get() == null) {
this.status = STATUS_DEAD;
throw new OrphanedThread();
}
} while (this.status == STATUS_SUSPENDED);
return this.args;
} catch (InterruptedException ie) {
this.status = STATUS_DEAD;
throw new OrphanedThread();
} finally {
this.args = LuaValue.NONE;
this.result = LuaValue.NONE;
}
} finally {
locker.unlock();
}
}
}

View File

@@ -479,7 +479,7 @@ public class FuncState extends Constants {
return ((Integer) h.get(v)).intValue();
}
final int idx = this.nk;
this.h.put(v, new Integer(idx));
this.h.put(v, idx);
final Prototype f = this.f;
if (f.k == null || nk + 1 >= f.k.length)
f.k = realloc( f.k, nk*2 + 1 );

View File

@@ -170,7 +170,7 @@ public class LexState extends Constants {
static {
for ( int i=0; i<NUM_RESERVED; i++ ) {
LuaString ts = (LuaString) LuaValue.valueOf( luaX_tokens[i] );
RESERVED.put(ts, new Integer(FIRST_RESERVED+i));
RESERVED.put(ts, FIRST_RESERVED+i);
}
}

View File

@@ -79,7 +79,7 @@ public class CoroutineLib extends TwoArgFunction {
coroutine.set("resume", new resume());
coroutine.set("running", new running());
coroutine.set("status", new status());
coroutine.set("yield", new yield());
coroutine.set("yield", new Yield());
coroutine.set("wrap", new wrap());
env.set("coroutine", coroutine);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("coroutine", coroutine);
@@ -113,7 +113,7 @@ public class CoroutineLib extends TwoArgFunction {
}
}
final class yield extends VarArgFunction {
private final class Yield extends VarArgFunction {
public Varargs invoke(Varargs args) {
return globals.yield( args );
}

View File

@@ -21,6 +21,9 @@
******************************************************************************/
package org.luaj.vm2.lib;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.luaj.vm2.Globals;
import org.luaj.vm2.Lua;
import org.luaj.vm2.LuaBoolean;
@@ -507,43 +510,75 @@ public class DebugLib extends TwoArgFunction {
final static CallFrame[] EMPTY = {};
CallFrame[] frame = EMPTY;
int calls = 0;
Lock lock = new ReentrantLock();
CallStack() {}
synchronized int currentline() {
return calls > 0? frame[calls-1].currentline(): -1;
}
private synchronized CallFrame pushcall() {
if (calls >= frame.length) {
int n = Math.max(4, frame.length * 3 / 2);
CallFrame[] f = new CallFrame[n];
System.arraycopy(frame, 0, f, 0, frame.length);
for (int i = frame.length; i < n; ++i)
f[i] = new CallFrame();
frame = f;
for (int i = 1; i < n; ++i)
f[i].previous = f[i-1];
int currentline() {
lock.lock();
try {
return calls > 0? frame[calls-1].currentline(): -1;
} finally {
lock.unlock();
}
return frame[calls++];
}
final synchronized void onCall(LuaFunction function) {
pushcall().set(function);
private CallFrame pushcall() {
lock.lock();
try {
if (calls >= frame.length) {
int n = Math.max(4, frame.length * 3 / 2);
CallFrame[] f = new CallFrame[n];
System.arraycopy(frame, 0, f, 0, frame.length);
for (int i = frame.length; i < n; ++i)
f[i] = new CallFrame();
frame = f;
for (int i = 1; i < n; ++i)
f[i].previous = f[i-1];
}
return frame[calls++];
} finally {
lock.unlock();
}
}
final synchronized void onCall(LuaClosure function, Varargs varargs, LuaValue[] stack) {
pushcall().set(function, varargs, stack);
final void onCall(LuaFunction function) {
lock.lock();
try {
pushcall().set(function);
} finally {
lock.unlock();
}
}
final synchronized void onReturn() {
if (calls > 0)
frame[--calls].reset();
final void onCall(LuaClosure function, Varargs varargs, LuaValue[] stack) {
lock.lock();
try {
pushcall().set(function, varargs, stack);
} finally {
lock.unlock();
}
}
final synchronized void onInstruction(int pc, Varargs v, int top) {
if (calls > 0)
frame[calls-1].instr(pc, v, top);
final void onReturn() {
lock.lock();
try {
if (calls > 0)
frame[--calls].reset();
} finally {
lock.unlock();
}
}
final void onInstruction(int pc, Varargs v, int top) {
lock.lock();
try {
if (calls > 0)
frame[calls-1].instr(pc, v, top);
} finally {
lock.unlock();
}
}
/**
@@ -551,102 +586,122 @@ public class DebugLib extends TwoArgFunction {
* @param level
* @return String containing the traceback.
*/
synchronized String traceback(int level) {
StringBuffer sb = new StringBuffer();
sb.append( "stack traceback:" );
for (DebugLib.CallFrame c; (c = getCallFrame(level++)) != null; ) {
sb.append("\n\t");
sb.append( c.shortsource() );
sb.append( ':' );
if (c.currentline() > 0)
sb.append( c.currentline()+":" );
sb.append( " in " );
DebugInfo ar = auxgetinfo("n", c.f, c);
if (c.linedefined() == 0)
sb.append("main chunk");
else if ( ar.name != null ) {
sb.append( "function '" );
sb.append( ar.name );
sb.append( '\'' );
} else {
sb.append( "function <" );
String traceback(int level) {
lock.lock();
try {
StringBuffer sb = new StringBuffer();
sb.append( "stack traceback:" );
for (DebugLib.CallFrame c; (c = getCallFrame(level++)) != null; ) {
sb.append("\n\t");
sb.append( c.shortsource() );
sb.append( ':' );
sb.append( c.linedefined() );
sb.append( '>' );
if (c.currentline() > 0)
sb.append( c.currentline()+":" );
sb.append( " in " );
DebugInfo ar = auxgetinfo("n", c.f, c);
if (c.linedefined() == 0)
sb.append("main chunk");
else if ( ar.name != null ) {
sb.append( "function '" );
sb.append( ar.name );
sb.append( '\'' );
} else {
sb.append( "function <" );
sb.append( c.shortsource() );
sb.append( ':' );
sb.append( c.linedefined() );
sb.append( '>' );
}
}
sb.append("\n\t[Java]: in ?");
return sb.toString();
} finally {
lock.unlock();
}
sb.append("\n\t[Java]: in ?");
return sb.toString();
}
synchronized DebugLib.CallFrame getCallFrame(int level) {
if (level < 1 || level > calls)
DebugLib.CallFrame getCallFrame(int level) {
lock.lock();
try {
if (level < 1 || level > calls)
return null;
return frame[calls-level];
} finally {
lock.unlock();
}
}
DebugLib.CallFrame findCallFrame(LuaValue func) {
lock.lock();
try {
for (int i = 1; i <= calls; ++i)
if (frame[calls-i].f == func)
return frame[i];
return null;
return frame[calls-level];
}
synchronized DebugLib.CallFrame findCallFrame(LuaValue func) {
for (int i = 1; i <= calls; ++i)
if (frame[calls-i].f == func)
return frame[i];
return null;
}
synchronized DebugInfo auxgetinfo(String what, LuaFunction f, CallFrame ci) {
DebugInfo ar = new DebugInfo();
for (int i = 0, n = what.length(); i < n; ++i) {
switch (what.charAt(i)) {
case 'S':
ar.funcinfo(f);
break;
case 'l':
ar.currentline = ci != null && ci.f.isclosure()? ci.currentline(): -1;
break;
case 'u':
if (f != null && f.isclosure()) {
Prototype p = f.checkclosure().p;
ar.nups = (short) p.upvalues.length;
ar.nparams = (short) p.numparams;
ar.isvararg = p.is_vararg != 0;
} else {
ar.nups = 0;
ar.isvararg = true;
ar.nparams = 0;
}
break;
case 't':
ar.istailcall = false;
break;
case 'n': {
/* calling function is a known Lua function? */
if (ci != null && ci.previous != null) {
if (ci.previous.f.isclosure()) {
NameWhat nw = getfuncname(ci.previous);
if (nw != null) {
ar.name = nw.name;
ar.namewhat = nw.namewhat;
}
}
}
if (ar.namewhat == null) {
ar.namewhat = ""; /* not found */
ar.name = null;
}
break;
}
case 'L':
case 'f':
break;
default:
// TODO: return bad status.
break;
}
} finally {
lock.unlock();
}
return ar;
}
DebugInfo auxgetinfo(String what, LuaFunction f, CallFrame ci) {
lock.lock();
try {
DebugInfo ar = new DebugInfo();
for (int i = 0, n = what.length(); i < n; ++i) {
switch (what.charAt(i)) {
case 'S':
ar.funcinfo(f);
break;
case 'l':
ar.currentline = ci != null && ci.f.isclosure()? ci.currentline(): -1;
break;
case 'u':
if (f != null && f.isclosure()) {
Prototype p = f.checkclosure().p;
ar.nups = (short) p.upvalues.length;
ar.nparams = (short) p.numparams;
ar.isvararg = p.is_vararg != 0;
} else {
ar.nups = 0;
ar.isvararg = true;
ar.nparams = 0;
}
break;
case 't':
ar.istailcall = false;
break;
case 'n': {
/* calling function is a known Lua function? */
if (ci != null && ci.previous != null) {
if (ci.previous.f.isclosure()) {
NameWhat nw = getfuncname(ci.previous);
if (nw != null) {
ar.name = nw.name;
ar.namewhat = nw.namewhat;
}
}
}
if (ar.namewhat == null) {
ar.namewhat = ""; /* not found */
ar.name = null;
}
break;
}
case 'L':
case 'f':
break;
default:
// TODO: return bad status.
break;
}
}
return ar;
} finally {
lock.unlock();
}
}
}
public static class CallFrame {

View File

@@ -119,11 +119,15 @@ public class IoLib extends TwoArgFunction {
return "file: " + Integer.toHexString(hashCode());
}
public void finalize() {
if (!isclosed()) {
try {
close();
} catch (IOException ignore) {}
public void finalize() throws Throwable {
try {
if (!isclosed()) {
try {
close();
} catch (IOException ignore) {}
}
} finally {
super.finalize();
}
}
}

View File

@@ -174,13 +174,13 @@ public class CoerceLuaToJava {
public Object coerce(LuaValue value) {
switch ( targetType ) {
case TARGET_TYPE_BYTE: return new Byte( (byte) value.toint() );
case TARGET_TYPE_CHAR: return new Character( (char) value.toint() );
case TARGET_TYPE_SHORT: return new Short( (short) value.toint() );
case TARGET_TYPE_INT: return new Integer( (int) value.toint() );
case TARGET_TYPE_LONG: return new Long( (long) value.todouble() );
case TARGET_TYPE_FLOAT: return new Float( (float) value.todouble() );
case TARGET_TYPE_DOUBLE: return new Double( (double) value.todouble() );
case TARGET_TYPE_BYTE: return (byte) value.toint() ;
case TARGET_TYPE_CHAR: return (char) value.toint() ;
case TARGET_TYPE_SHORT: return (short) value.toint();
case TARGET_TYPE_INT: return value.toint();
case TARGET_TYPE_LONG: return (long) value.todouble();
case TARGET_TYPE_FLOAT: return (float) value.todouble();
case TARGET_TYPE_DOUBLE: return (double) value.todouble();
default: return null;
}
}
@@ -308,7 +308,7 @@ public class CoerceLuaToJava {
public Object coerce(LuaValue value) {
switch ( value.type() ) {
case LuaValue.TNUMBER:
return value.isint()? (Object)new Integer(value.toint()): (Object)new Double(value.todouble());
return value.isint()? value.toint() : value.todouble();
case LuaValue.TBOOLEAN:
return value.toboolean()? Boolean.TRUE: Boolean.FALSE;
case LuaValue.TSTRING:

View File

@@ -244,8 +244,8 @@ public class LuaScriptEngine extends AbstractScriptEngine implements ScriptEngin
case LuaValue.TSTRING: return luajValue.tojstring();
case LuaValue.TUSERDATA: return luajValue.checkuserdata(Object.class);
case LuaValue.TNUMBER: return luajValue.isinttype()?
(Object) new Integer(luajValue.toint()):
(Object) new Double(luajValue.todouble());
luajValue.toint():
luajValue.todouble();
default: return luajValue;
}
}

View File

@@ -1 +1 @@
version: 3.0.2
version: 3.1.0