Various changes:
(1) New lua compatibility bindings, including select() and math functions (2) fix some VM bugs (3) fix some table bugs, and attempt to restore metatable functionality.
This commit is contained in:
@@ -5,12 +5,15 @@ import java.io.InputStream;
|
||||
|
||||
import lua.CallInfo;
|
||||
import lua.GlobalState;
|
||||
import lua.Lua;
|
||||
import lua.StackState;
|
||||
import lua.VM;
|
||||
import lua.io.Closure;
|
||||
import lua.io.LoadState;
|
||||
import lua.io.Proto;
|
||||
import lua.value.LDouble;
|
||||
import lua.value.LFunction;
|
||||
import lua.value.LInteger;
|
||||
import lua.value.LNil;
|
||||
import lua.value.LNumber;
|
||||
import lua.value.LString;
|
||||
@@ -21,33 +24,61 @@ public class LuaCompat extends LFunction {
|
||||
|
||||
public static void install() {
|
||||
LTable globals = GlobalState.getGlobalsTable();
|
||||
for ( int i = 0; i < NAMES.length; ++i ) {
|
||||
globals.put( NAMES[i], new LuaCompat( i ) );
|
||||
for ( int i = 0; i < GLOBAL_NAMES.length; ++i ) {
|
||||
globals.put( GLOBAL_NAMES[i], new LuaCompat( i ) );
|
||||
}
|
||||
|
||||
LTable math = new LTable();
|
||||
for ( int i = 0; i < MATH_NAMES.length; ++i ) {
|
||||
math.put( MATH_NAMES[i], new LuaCompat( MATH_BASE + i ) );
|
||||
}
|
||||
|
||||
// Some handy constants
|
||||
math.put( "huge", new LDouble( Double.MAX_VALUE ) );
|
||||
math.put( "pi", new LDouble( Math.PI ) );
|
||||
|
||||
globals.put( "math", math );
|
||||
}
|
||||
|
||||
public static final String[] NAMES = {
|
||||
public static final String[] GLOBAL_NAMES = {
|
||||
"assert",
|
||||
"collectgarbage",
|
||||
"loadfile",
|
||||
"tonumber",
|
||||
"rawget",
|
||||
"setfenv"
|
||||
"setfenv",
|
||||
"select",
|
||||
"collectgarbage",
|
||||
};
|
||||
|
||||
public static final String[] MATH_NAMES = {
|
||||
"abs",
|
||||
"max",
|
||||
"min",
|
||||
"modf",
|
||||
"sin"
|
||||
};
|
||||
|
||||
private static final int ASSERT = 0;
|
||||
private static final int COLLECTGARBAGE = 1;
|
||||
private static final int LOADFILE = 2;
|
||||
private static final int TONUMBER = 3;
|
||||
private static final int RAWGET = 4;
|
||||
private static final int SETFENV = 5;
|
||||
private static final int LOADFILE = 1;
|
||||
private static final int TONUMBER = 2;
|
||||
private static final int RAWGET = 3;
|
||||
private static final int SETFENV = 4;
|
||||
private static final int SELECT = 5;
|
||||
private static final int COLLECTGARBAGE = 6;
|
||||
|
||||
private static final int MATH_BASE = 10;
|
||||
private static final int ABS = MATH_BASE + 0;
|
||||
private static final int MAX = MATH_BASE + 1;
|
||||
private static final int MIN = MATH_BASE + 2;
|
||||
private static final int MODF = MATH_BASE + 3;
|
||||
private static final int SIN = MATH_BASE + 4;
|
||||
|
||||
private final int id;
|
||||
private LuaCompat( int id ) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void luaStackCall( VM vm ) {
|
||||
public boolean luaStackCall( VM vm ) {
|
||||
switch ( id ) {
|
||||
case ASSERT: {
|
||||
if ( !vm.getArgAsBoolean(0) ) {
|
||||
@@ -61,10 +92,6 @@ public class LuaCompat extends LFunction {
|
||||
}
|
||||
vm.setResult();
|
||||
} break;
|
||||
case COLLECTGARBAGE:
|
||||
System.gc();
|
||||
vm.setResult();
|
||||
break;
|
||||
case LOADFILE:
|
||||
vm.setResult( loadfile(vm, vm.getArg(0)) );
|
||||
break;
|
||||
@@ -86,9 +113,82 @@ public class LuaCompat extends LFunction {
|
||||
case SETFENV:
|
||||
setfenv( (StackState) vm );
|
||||
break;
|
||||
case SELECT:
|
||||
select( vm );
|
||||
break;
|
||||
case COLLECTGARBAGE:
|
||||
System.gc();
|
||||
vm.setResult();
|
||||
break;
|
||||
|
||||
// Math functions
|
||||
case ABS:
|
||||
vm.setResult( abs( vm.getArg( 0 ) ) );
|
||||
break;
|
||||
case MAX:
|
||||
vm.setResult( max( vm.getArg( 0 ), vm.getArg( 1 ) ) );
|
||||
break;
|
||||
case MIN:
|
||||
vm.setResult( min( vm.getArg( 0 ), vm.getArg( 1 ) ) );
|
||||
break;
|
||||
case MODF:
|
||||
modf( vm );
|
||||
break;
|
||||
case SIN:
|
||||
vm.setResult( new LDouble( Math.sin( vm.getArgAsDouble( 0 ) ) ) );
|
||||
break;
|
||||
default:
|
||||
luaUnsupportedOperation();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void select( VM vm ) {
|
||||
LValue arg = vm.getArg( 0 );
|
||||
if ( arg instanceof LNumber ) {
|
||||
final int start;
|
||||
final int numResults;
|
||||
if ( ( start = arg.luaAsInt() ) > 0 &&
|
||||
( numResults = Math.max( vm.getArgCount() - start,
|
||||
vm.getExpectedResultCount() ) ) > 0 ) {
|
||||
// since setResult trashes the arguments, we have to save them somewhere.
|
||||
LValue[] results = new LValue[numResults];
|
||||
for ( int i = 0; i < numResults; ++i ) {
|
||||
results[i] = vm.getArg( i+start );
|
||||
}
|
||||
vm.setResult();
|
||||
for ( int i = 0; i < numResults; ++i ) {
|
||||
vm.push( results[i] );
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else if ( arg.luaAsString().equals( "#" ) ) {
|
||||
vm.setResult( new LInteger( vm.getArgCount() - 1 ) );
|
||||
}
|
||||
vm.setResult();
|
||||
}
|
||||
|
||||
private LValue abs( final LValue v ) {
|
||||
LValue nv = v.luaUnaryMinus();
|
||||
return max( v, nv );
|
||||
}
|
||||
|
||||
private LValue max( LValue lhs, LValue rhs ) {
|
||||
return rhs.luaBinCmpUnknown( Lua.OP_LT, lhs ) ? rhs: lhs;
|
||||
}
|
||||
|
||||
private LValue min( LValue lhs, LValue rhs ) {
|
||||
return rhs.luaBinCmpUnknown( Lua.OP_LT, lhs ) ? lhs: rhs;
|
||||
}
|
||||
|
||||
private void modf( VM vm ) {
|
||||
LValue arg = vm.getArg( 0 );
|
||||
double v = arg.luaAsDouble();
|
||||
double intPart = ( v > 0 ) ? Math.floor( v ) : Math.ceil( v );
|
||||
double fracPart = v - intPart;
|
||||
vm.setResult();
|
||||
vm.push( intPart );
|
||||
vm.push( fracPart );
|
||||
}
|
||||
|
||||
private LValue toNumber( VM vm ) {
|
||||
|
||||
@@ -53,7 +53,7 @@ public final class LuaJava extends LFunction {
|
||||
}
|
||||
|
||||
// perform a lua call
|
||||
public void luaStackCall(VM vm) {
|
||||
public boolean luaStackCall(VM vm) {
|
||||
String className;
|
||||
switch ( id ) {
|
||||
case BINDCLASS:
|
||||
@@ -87,6 +87,7 @@ public final class LuaJava extends LFunction {
|
||||
default:
|
||||
luaUnsupportedOperation();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class ParamsList {
|
||||
@@ -159,7 +160,7 @@ public final class LuaJava extends LFunction {
|
||||
public String toString() {
|
||||
return clazz.getName()+"."+s+"()";
|
||||
}
|
||||
public void luaStackCall(VM vm) {
|
||||
public boolean luaStackCall(VM vm) {
|
||||
try {
|
||||
// find the method
|
||||
ParamsList params = new ParamsList( vm );
|
||||
@@ -171,7 +172,7 @@ public final class LuaJava extends LFunction {
|
||||
|
||||
// coerce the result
|
||||
vm.setResult( CoerceJavaToLua.coerce(result) );
|
||||
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ final class Builtin extends LFunction {
|
||||
}
|
||||
|
||||
// perform a lua call
|
||||
public void luaStackCall(VM vm) {
|
||||
public boolean luaStackCall(VM vm) {
|
||||
switch ( id ) {
|
||||
case PRINT:
|
||||
int n = vm.getArgCount();
|
||||
@@ -66,6 +66,7 @@ final class Builtin extends LFunction {
|
||||
default:
|
||||
luaUnsupportedOperation();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void redirectOutput( OutputStream newStdOut ) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package lua;
|
||||
|
||||
import lua.io.Closure;
|
||||
import lua.value.LTable;
|
||||
import lua.value.LValue;
|
||||
|
||||
public interface VM {
|
||||
@@ -29,17 +28,17 @@ public interface VM {
|
||||
public void push( String value );
|
||||
|
||||
/**
|
||||
* Perform a call that has been set up on the stack.
|
||||
* The first value on the stack must be a Closure,
|
||||
* Create a call frame for a call that has been set up on
|
||||
* the stack. The first value on the stack must be a Closure,
|
||||
* and subsequent values are arguments to the closure.
|
||||
*/
|
||||
public void stackCall();
|
||||
public void prepStackCall();
|
||||
|
||||
/**
|
||||
* Execute bytecodes until the current call completes
|
||||
* or the vm yields.
|
||||
*/
|
||||
public void exec();
|
||||
public void execute();
|
||||
|
||||
/**
|
||||
* Put the closure on the stack with arguments,
|
||||
@@ -50,7 +49,25 @@ public interface VM {
|
||||
* @param values
|
||||
*/
|
||||
public void doCall(Closure c, LValue[] values);
|
||||
|
||||
|
||||
/**
|
||||
* Set the number of results that are expected from the function being called.
|
||||
* (This should be called before prepStackCall)
|
||||
*/
|
||||
public void setExpectedResultCount( int nresults );
|
||||
|
||||
/**
|
||||
* Returns the number of results that are expected by the calling function,
|
||||
* or -1 if the calling function can accept a variable number of results.
|
||||
*/
|
||||
public int getExpectedResultCount();
|
||||
|
||||
/**
|
||||
* Adjust the stack to contain the expected number of results by adjusting
|
||||
* the top.
|
||||
*/
|
||||
public void adjustResults();
|
||||
|
||||
// ================ interfaces for getting arguments when called
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,7 +20,8 @@ public class Closure extends LFunction {
|
||||
// called by vm when there is an OP_CALL
|
||||
// in this case, we are on the stack,
|
||||
// and simply need to cue the VM to treat it as a stack call
|
||||
public void luaStackCall(VM vm) {
|
||||
vm.stackCall();
|
||||
public boolean luaStackCall(VM vm) {
|
||||
vm.prepStackCall();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,22 @@ public class LFunction extends LValue {
|
||||
vm.push( table );
|
||||
vm.push( key );
|
||||
vm.push( val );
|
||||
this.luaStackCall(vm);
|
||||
vm.setExpectedResultCount( 0 );
|
||||
if ( this.luaStackCall( vm ) )
|
||||
vm.execute();
|
||||
else
|
||||
vm.adjustResults();
|
||||
}
|
||||
|
||||
public void luaGetTable(VM vm, LValue table, LValue key) {
|
||||
vm.push( this );
|
||||
vm.push( table );
|
||||
vm.push( key );
|
||||
this.luaStackCall(vm);
|
||||
vm.setExpectedResultCount( 1 );
|
||||
if ( this.luaStackCall( vm ) )
|
||||
vm.execute();
|
||||
else
|
||||
vm.adjustResults();
|
||||
}
|
||||
|
||||
public LString luaGetType() {
|
||||
|
||||
@@ -11,12 +11,10 @@ public class LTable extends LValue {
|
||||
public static final LString TYPE_NAME = new LString("table");
|
||||
|
||||
/** Metatable tag for intercepting table gets */
|
||||
// TODO: see note below
|
||||
// private static final LString TM_INDEX = new LString("__index");
|
||||
private static final LString TM_INDEX = new LString("__index");
|
||||
|
||||
/** Metatable tag for intercepting table sets */
|
||||
// TODO: see note below
|
||||
// private static final LString TM_NEWINDEX = new LString("__newindex");
|
||||
private static final LString TM_NEWINDEX = new LString("__newindex");
|
||||
|
||||
private Hashtable m_hash = new Hashtable();
|
||||
|
||||
@@ -46,10 +44,10 @@ public class LTable extends LValue {
|
||||
public void rawSet(LValue key, LValue val) {
|
||||
|
||||
if (key instanceof LInteger) {
|
||||
int iKey = ((LInteger) key).luaAsInt();
|
||||
int iKey = ((LInteger) key).luaAsInt() - 1;
|
||||
|
||||
// implementation where m_vector stores keys
|
||||
// {0, ..., size-1}
|
||||
// {1, ..., size}
|
||||
// where size = m_vector.size()
|
||||
//
|
||||
if (m_vector == null) {
|
||||
@@ -114,13 +112,25 @@ public class LTable extends LValue {
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
public boolean containsKey(LValue key) {
|
||||
if (m_vector != null) {
|
||||
if (key instanceof LInteger) {
|
||||
int iKey = ((LInteger) key).luaAsInt();
|
||||
if ((iKey >= 1) && (iKey <= m_vector.size())) {
|
||||
return m_vector.elementAt(iKey-1) != LNil.NIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return m_hash.containsKey( key );
|
||||
}
|
||||
|
||||
/** Utility method to directly get the value in a table, without metatable calls */
|
||||
public LValue rawGet(LValue key) {
|
||||
|
||||
if (m_vector != null) {
|
||||
if (key instanceof LInteger) {
|
||||
int iKey = ((LInteger) key).luaAsInt();
|
||||
int iKey = ((LInteger) key).luaAsInt() - 1;
|
||||
|
||||
// implementation where m_vector stores keys
|
||||
// {0, ..., size-1}
|
||||
@@ -147,33 +157,32 @@ public class LTable extends LValue {
|
||||
}
|
||||
}
|
||||
|
||||
return (LValue) m_hash.get(key);
|
||||
|
||||
/* TODO: this is old incorrect code, kept here until metatables are fixed
|
||||
LValue val;
|
||||
if ( val == null || val == LNil.NIL ) {
|
||||
if ( m_metatable != null ) {
|
||||
LValue event = (LValue) m_metatable.m_hash.get( TM_INDEX );
|
||||
if ( event != null && event != LNil.NIL ) {
|
||||
event.luaGetTable( vm, table, key );
|
||||
return;
|
||||
}
|
||||
}
|
||||
val = LNil.NIL;
|
||||
}
|
||||
return val;
|
||||
*/
|
||||
LValue v = (LValue) m_hash.get(key);
|
||||
return ( v != null ) ? v : LNil.NIL;
|
||||
}
|
||||
|
||||
public void luaGetTable(VM vm, LValue table, LValue key) {
|
||||
LValue v = rawGet(key);
|
||||
if ( v == LNil.NIL && m_metatable != null ) {
|
||||
LValue event = m_metatable.rawGet( TM_INDEX );
|
||||
if ( event != null && event != LNil.NIL ) {
|
||||
event.luaGetTable( vm, table, key );
|
||||
return;
|
||||
}
|
||||
}
|
||||
// TODO: table is unused -- is this correct?
|
||||
// stack.stack[base] = val;
|
||||
LValue val = rawGet(key);
|
||||
vm.push(val!=null? val: LNil.NIL);
|
||||
vm.push(v!=null? v: LNil.NIL);
|
||||
}
|
||||
|
||||
public void luaSetTable(VM vm, LValue table, LValue key, LValue val) {
|
||||
// TODO: table is unused -- is this correct?
|
||||
if ( !containsKey( key ) && m_metatable != null ) {
|
||||
LValue event = m_metatable.rawGet( TM_NEWINDEX );
|
||||
if ( event != null && event != LNil.NIL ) {
|
||||
event.luaSetTable( vm, table, key, val );
|
||||
return;
|
||||
}
|
||||
}
|
||||
rawSet(key, val);
|
||||
}
|
||||
|
||||
@@ -222,18 +231,18 @@ public class LTable extends LValue {
|
||||
}
|
||||
|
||||
// perform a lua call
|
||||
public void luaStackCall(VM vm) {
|
||||
if ( e.hasMoreElements() ) {
|
||||
LValue key = (LValue) e.nextElement();
|
||||
vm.setResult();
|
||||
vm.push( key );
|
||||
vm.push((LValue) t.m_hash.get(key));
|
||||
} else if ((i >= 0) && (i < t.m_vector.size())) {
|
||||
vm.setResult();
|
||||
vm.push(new LInteger(i));
|
||||
public boolean luaStackCall(VM vm) {
|
||||
vm.setResult();
|
||||
if ((i >= 0) && (i < t.m_vector.size())) {
|
||||
vm.push(new LInteger(i+1));
|
||||
vm.push((LValue) t.m_vector.get(i));
|
||||
++i;
|
||||
} else if ( e.hasMoreElements() ) {
|
||||
LValue key = (LValue) e.nextElement();
|
||||
vm.push( key );
|
||||
vm.push((LValue) t.m_hash.get(key));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,9 +19,11 @@ public class LValue {
|
||||
return true;
|
||||
}
|
||||
|
||||
// perform a lua call, return number of results actually produced
|
||||
public void luaStackCall(VM vm) {
|
||||
// perform a lua call, return true if the call is to a lua function, false
|
||||
// if it ran to completion.
|
||||
public boolean luaStackCall(VM vm) {
|
||||
luaUnsupportedOperation();
|
||||
return false;
|
||||
}
|
||||
|
||||
// unsupported except for numbers
|
||||
|
||||
@@ -4,10 +4,10 @@ import java.io.InputStream;
|
||||
|
||||
import lua.StackState;
|
||||
import lua.VM;
|
||||
import lua.addon.luacompat.LuaCompat;
|
||||
import lua.io.Closure;
|
||||
import lua.io.LoadState;
|
||||
import lua.io.Proto;
|
||||
import lua.value.LString;
|
||||
import lua.value.LValue;
|
||||
|
||||
/**
|
||||
@@ -23,6 +23,9 @@ public class LuacRunner {
|
||||
String script = (args.length>0? args[0]: "/test2.luac");
|
||||
System.out.println("loading '"+script+"'");
|
||||
|
||||
// add LuaCompat bindings
|
||||
LuaCompat.install();
|
||||
|
||||
// new lua state
|
||||
StackState state = new StackState();
|
||||
VM vm = state;
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.io.OutputStream;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import lua.StackState;
|
||||
import lua.addon.luacompat.LuaCompat;
|
||||
import lua.addon.luajava.LuaJava;
|
||||
import lua.io.Closure;
|
||||
import lua.io.LoadState;
|
||||
@@ -58,7 +59,11 @@ public class LuaJTest extends TestCase {
|
||||
public void testCompare() throws IOException, InterruptedException {
|
||||
runTest( "compare" );
|
||||
}
|
||||
|
||||
|
||||
public void testSelect() throws IOException, InterruptedException {
|
||||
runTest( "select" );
|
||||
}
|
||||
|
||||
public void testSetlist() throws IOException, InterruptedException {
|
||||
runTest( "setlist" );
|
||||
}
|
||||
@@ -82,6 +87,9 @@ public class LuaJTest extends TestCase {
|
||||
|
||||
// add LuaJava bindings
|
||||
LuaJava.install();
|
||||
|
||||
// add LuaCompat bindings
|
||||
LuaCompat.install();
|
||||
|
||||
// new lua state
|
||||
StackState state = new StackState();
|
||||
|
||||
@@ -18,6 +18,8 @@ import lua.addon.luacompat.LuaCompat;
|
||||
import lua.io.Closure;
|
||||
import lua.io.LoadState;
|
||||
import lua.io.Proto;
|
||||
import lua.value.LNil;
|
||||
import lua.value.LString;
|
||||
import lua.value.LValue;
|
||||
|
||||
public class StandardTest extends TestCase {
|
||||
@@ -71,6 +73,12 @@ public class StandardTest extends TestCase {
|
||||
public void runTest() {
|
||||
GlobalState.resetGlobals();
|
||||
LuaCompat.install();
|
||||
// hack: it's unpleasant when the test cases fail to terminate;
|
||||
// unfortunately, there is a test in the standard suite that
|
||||
// relies on weak tables having their elements removed by
|
||||
// the garbage collector. Until we implement that, remove the
|
||||
// built-in collectgarbage function.
|
||||
GlobalState.getGlobalsTable().rawSet( new LString("collectgarbage"), LNil.NIL );
|
||||
StackState state = new StackState();
|
||||
Closure c = new Closure( state, code );
|
||||
|
||||
|
||||
25
src/test/res/select.lua
Normal file
25
src/test/res/select.lua
Normal file
@@ -0,0 +1,25 @@
|
||||
-- Parts of this test are commented out because it looks like
|
||||
-- there is a problem with our argument passing, particularly in the
|
||||
-- presence of the VARARG instruction.
|
||||
|
||||
--[[ local function f(...)
|
||||
print("arg count:", select('#', ...))
|
||||
end
|
||||
|
||||
local function g(...)
|
||||
local a, b, c = select(2, ...)
|
||||
print(a, b, c)
|
||||
end
|
||||
]]--
|
||||
|
||||
print((select(1, "a", "b", "c")))
|
||||
print( select(1, "a", "b", "c"))
|
||||
|
||||
print((select(2, "a", "b", "c")))
|
||||
print( select(2, "a", "b", "c"))
|
||||
|
||||
print((select(3, "a", "b", "c")))
|
||||
print( select(3, "a", "b", "c"))
|
||||
|
||||
-- f("hello", "world")
|
||||
-- g(1, 2, 3, 4, 5, 6, 7)
|
||||
BIN
src/test/res/select.luac
Normal file
BIN
src/test/res/select.luac
Normal file
Binary file not shown.
Reference in New Issue
Block a user