Rework the main API"s that implement the calling convention. Provide utility methods to get arguments that were supplied, and provide return values. Add a VM interface to clarify the relationship between the VM, things that call the VM, and things that are called by the VM. Make the code more closely aligned with the C++ version.

This commit is contained in:
James Roseborough
2007-07-24 05:06:10 +00:00
parent 56f33b373d
commit 8bf4c82a12
17 changed files with 424 additions and 862 deletions

View File

@@ -3,15 +3,14 @@ package lua.addon.luacompat;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import lua.CallFrame; import lua.CallInfo;
import lua.GlobalState; import lua.GlobalState;
import lua.StackState; import lua.StackState;
import lua.VM;
import lua.io.Closure; import lua.io.Closure;
import lua.io.LoadState; import lua.io.LoadState;
import lua.io.Proto; import lua.io.Proto;
import lua.value.LDouble;
import lua.value.LFunction; import lua.value.LFunction;
import lua.value.LInteger;
import lua.value.LNil; import lua.value.LNil;
import lua.value.LNumber; import lua.value.LNumber;
import lua.value.LString; import lua.value.LString;
@@ -48,36 +47,33 @@ public class LuaCompat extends LFunction {
this.id = id; this.id = id;
} }
public void luaStackCall( CallFrame call, int base, int top, int nresults ) { public void luaStackCall( VM vm ) {
switch ( id ) { switch ( id ) {
case ASSERT: { case ASSERT: {
LValue v = call.stack[base+1]; if ( !vm.getArgAsBoolean(0) ) {
if ( !v.luaAsBoolean() ) {
String message; String message;
if ( top > base+2 ) { if ( vm.getArgCount() > 1 ) {
message = call.stack[base+2].luaAsString(); message = vm.getArgAsString(1);
} else { } else {
message = "assertion failed!"; message = "assertion failed!";
} }
throw new RuntimeException(message); throw new RuntimeException(message);
} }
call.top = base; vm.setResult();
} break; } break;
case COLLECTGARBAGE: case COLLECTGARBAGE:
System.gc(); System.gc();
call.top = base; vm.setResult();
break; break;
case LOADFILE: case LOADFILE:
call.stack[base] = loadfile(call, ( top > base ) ? call.stack[base+1] : null); vm.setResult( loadfile(vm, vm.getArg(0)) );
call.top = base+1;
break; break;
case TONUMBER: case TONUMBER:
call.stack[base] = toNumber( call.stack, base+1, top ); vm.setResult( toNumber( vm ) );
call.top = base+1;
break; break;
case RAWGET: { case RAWGET: {
LValue t = call.stack[base+1]; LValue t = vm.getArg(0);;
LValue k = call.stack[base+2]; LValue k = vm.getArg(1);
LValue result = LNil.NIL; LValue result = LNil.NIL;
if ( t instanceof LTable ) { if ( t instanceof LTable ) {
LValue v = (LValue) ( (LTable) t ).m_hash.get( k ); LValue v = (LValue) ( (LTable) t ).m_hash.get( k );
@@ -85,39 +81,33 @@ public class LuaCompat extends LFunction {
result = v; result = v;
} }
} }
call.stack[base] = result; vm.setResult( result );
call.top = base+1;
} break; } break;
case SETFENV: case SETFENV:
call.top = base + setfenv( call.stack, base, base+1, top, call.state ); setfenv( (StackState) vm );
break; break;
default: default:
luaUnsupportedOperation(); luaUnsupportedOperation();
} }
if (nresults >= 0)
call.adjustTop(base + nresults);
} }
private LValue toNumber( LValue[] stack, int first, int top ) { private LValue toNumber( VM vm ) {
LValue result = LNil.NIL; LValue input = vm.getArg(0);
if ( first < top ) {
LValue input = stack[first];
if ( input instanceof LNumber ) { if ( input instanceof LNumber ) {
result = input; return input;
} else if ( input instanceof LString ) { } else if ( input instanceof LString ) {
int base = 10; int base = 10;
if ( first+1 < top ) { if ( vm.getArgCount()>1 ) {
base = stack[first+1].luaAsInt(); base = vm.getArgAsInt(1);
} }
return ( (LString) input ).luaToNumber( base ); return ( (LString) input ).luaToNumber( base );
} }
} return LNil.NIL;
return result;
} }
private int setfenv( LValue[] stack, int result, int argbase, int arglimit, StackState state ) { private void setfenv( StackState state ) {
LValue f = stack[argbase]; LValue f = state.getArg(0);
LValue newenv = stack[argbase+1]; LValue newenv = state.getArg(1);
Closure c = null; Closure c = null;
@@ -129,9 +119,9 @@ public class LuaCompat extends LFunction {
} else { } else {
int callStackDepth = f.luaAsInt(); int callStackDepth = f.luaAsInt();
if ( callStackDepth > 0 ) { if ( callStackDepth > 0 ) {
CallFrame frame = state.getStackFrame( callStackDepth ); CallInfo frame = state.getStackFrame( callStackDepth );
if ( frame != null ) { if ( frame != null ) {
c = frame.cl; c = frame.closure;
} }
} else { } else {
// This is supposed to set the environment of the current // This is supposed to set the environment of the current
@@ -144,14 +134,15 @@ public class LuaCompat extends LFunction {
if ( newenv instanceof LTable ) { if ( newenv instanceof LTable ) {
c.env = (LTable) newenv; c.env = (LTable) newenv;
} }
stack[ result ] = c; state.setResult( c );
return 1; return;
} }
return 0; state.setResult();
return;
} }
private LValue loadfile( CallFrame call, LValue fileName ) { private LValue loadfile( VM vm, LValue fileName ) {
InputStream is; InputStream is;
String script; String script;
@@ -165,8 +156,8 @@ public class LuaCompat extends LFunction {
if ( is != null ) { if ( is != null ) {
try { try {
Proto p = LoadState.undump(call.state, is, script); Proto p = LoadState.undump(vm, is, script);
return new Closure(call.state, p); return new Closure( (StackState) vm, p);
} catch (IOException e) { } catch (IOException e) {
} finally { } finally {
if ( is != System.in ) { if ( is != System.in ) {

View File

@@ -3,8 +3,6 @@ package lua.addon.luajava;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import lua.CallFrame;
import lua.addon.luajava.LuaJava.LInstance;
import lua.value.LBoolean; import lua.value.LBoolean;
import lua.value.LDouble; import lua.value.LDouble;
import lua.value.LInteger; import lua.value.LInteger;
@@ -122,11 +120,12 @@ public class CoerceLuaToJava {
return v; return v;
} }
static Object[] coerceArgs(CallFrame call, int base, int nargs, Class[] parameterTypes) { static Object[] coerceArgs(LValue[] suppliedArgs, Class[] parameterTypes) {
int nargs = suppliedArgs.length;
int n = parameterTypes.length; int n = parameterTypes.length;
Object[] args = new Object[n]; Object[] args = new Object[n];
for ( int i=0; i<n && i<nargs; i++ ) for ( int i=0; i<n && i<nargs; i++ )
args[i] = coerceArg( call.stack[base+i], parameterTypes[i] ); args[i] = coerceArg( suppliedArgs[i], parameterTypes[i] );
return args; return args;
} }
@@ -138,17 +137,18 @@ public class CoerceLuaToJava {
* 3) java has less args * 3) java has less args
* 4) types coerce well * 4) types coerce well
*/ */
static int scoreParamTypes(LValue[] stack, int base, int nargs, Class[] paramTypes) { static int scoreParamTypes(LValue[] suppliedArgs, Class[] paramTypes) {
int nargs = suppliedArgs.length;
int njava = paramTypes.length; int njava = paramTypes.length;
int score = (njava == nargs? 0: njava > nargs? 0x4000: 0x8000); int score = (njava == nargs? 0: njava > nargs? 0x4000: 0x8000);
for ( int i=0; i<nargs && i<njava; i++ ) { for ( int i=0; i<nargs && i<njava; i++ ) {
LValue v = stack[base+i]; LValue a = suppliedArgs[i];
Class c = paramTypes[i]; Class c = paramTypes[i];
Coercion co = COERCIONS.get( c ); Coercion co = COERCIONS.get( c );
if ( co != null ) { if ( co != null ) {
score += co.score( v ); score += co.score( a );
} else if ( v instanceof LUserData ) { } else if ( a instanceof LUserData ) {
Object o = ((LUserData) v).m_instance; Object o = ((LUserData) a).m_instance;
if ( ! c.isAssignableFrom(o.getClass()) ) if ( ! c.isAssignableFrom(o.getClass()) )
score += 0x10000; score += 0x10000;
} else { } else {

View File

@@ -8,11 +8,15 @@ package lua.addon.luajava;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lua.CallFrame;
import lua.GlobalState; import lua.GlobalState;
import lua.VM;
import lua.value.LFunction; import lua.value.LFunction;
import lua.value.LString;
import lua.value.LTable; import lua.value.LTable;
import lua.value.LUserData; import lua.value.LUserData;
import lua.value.LValue; import lua.value.LValue;
@@ -49,43 +53,33 @@ public final class LuaJava extends LFunction {
} }
// perform a lua call // perform a lua call
public void luaStackCall(CallFrame call, int base, int top, int nresults) { public void luaStackCall(VM vm) {
String className; String className;
switch ( id ) { switch ( id ) {
case BINDCLASS: case BINDCLASS:
className = call.stack[base+1].luaAsString(); className = vm.getArgAsString(0);
try { try {
Class clazz = Class.forName(className); Class clazz = Class.forName(className);
call.stack[base] = new LInstance( clazz, clazz ); vm.setResult( new LInstance( clazz, clazz ) );
call.top = base+1;
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
break; break;
case NEWINSTANCE: case NEWINSTANCE:
className = call.stack[base+1].luaAsString(); className = vm.getArgAsString(0);
try { try {
// get constructor
Class clazz = Class.forName(className); Class clazz = Class.forName(className);
Constructor[] cons = clazz.getConstructors(); ParamsList params = new ParamsList( vm );
Constructor con = cons[0]; Constructor con = resolveConstructor( clazz, params );
Class[] paramTypes = con.getParameterTypes();
int paramsBase = base + 2; // coerce args
int nargs = top - paramsBase; Object[] args = CoerceLuaToJava.coerceArgs( params.values, con.getParameterTypes() );
int score = CoerceLuaToJava.scoreParamTypes( call.stack, paramsBase, nargs, paramTypes );
for ( int i=1; i<cons.length; i++ ) {
Constructor c = cons[i];
Class[] p = c.getParameterTypes();
int s = CoerceLuaToJava.scoreParamTypes( call.stack, paramsBase, nargs, p );
if ( s < score ) {
con = c;
paramTypes = p;
score = s;
}
}
Object[] args = CoerceLuaToJava.coerceArgs( call, paramsBase, nargs, paramTypes );
Object o = con.newInstance( args ); Object o = con.newInstance( args );
call.stack[base] = new LInstance( o, clazz );
call.top = base+1; // set the result
vm.setResult( new LInstance( o, clazz ) );
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -93,8 +87,30 @@ public final class LuaJava extends LFunction {
default: default:
luaUnsupportedOperation(); luaUnsupportedOperation();
} }
if (nresults >= 0) }
call.adjustTop(base + nresults);
public static class ParamsList {
public final LValue[] values;
public final Class[] classes;
public int hash;
ParamsList( VM vm ) {
int n = vm.getArgCount()-1;
values = new LValue[n];
classes = new Class[n];
for ( int i=0; i<n; i++ ) {
values[i] = vm.getArg(i+1);
classes[i] = values[i].getClass();
hash += classes[i].hashCode();
}
}
public int hashCode() {
return hash;
}
public boolean equals( Object o ) {
return ( o instanceof ParamsList )?
Arrays.equals( classes, ((ParamsList) o).classes ):
false;
}
} }
public static class LInstance extends LUserData { public static class LInstance extends LUserData {
@@ -103,29 +119,27 @@ public final class LuaJava extends LFunction {
super(o); super(o);
this.clazz = clazz; this.clazz = clazz;
} }
public void luaGetTable(CallFrame call, int base, LValue table, LValue key) { public void luaGetTable(VM vm, LValue table, LValue key) {
final String s = key.luaAsString(); final String s = key.luaAsString();
try { try {
Field f = clazz.getField(s); Field f = clazz.getField(s);
Object o = f.get(m_instance); Object o = f.get(m_instance);
LValue v = CoerceJavaToLua.coerce( o ); LValue v = CoerceJavaToLua.coerce( o );
call.stack[base] = v; vm.setResult( v );
call.top = base + 1;
} catch (NoSuchFieldException nsfe) { } catch (NoSuchFieldException nsfe) {
call.stack[base] = new LMethod(m_instance,clazz,s); vm.setResult( new LMethod(m_instance,clazz,s) );
call.top = base + 1;
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
public void luaSetTable(CallFrame call, int base, LValue table, LValue key, LValue val) { public void luaSetTable(VM vm, LValue table, LValue key, LValue val) {
Class c = m_instance.getClass(); Class c = m_instance.getClass();
String s = key.luaAsString(); String s = key.luaAsString();
try { try {
Field f = c.getField(s); Field f = c.getField(s);
Object v = CoerceLuaToJava.coerceArg(val, f.getType()); Object v = CoerceLuaToJava.coerceArg(val, f.getType());
f.set(m_instance,v); f.set(m_instance,v);
call.top = base; vm.setResult();
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -145,34 +159,146 @@ public final class LuaJava extends LFunction {
public String toString() { public String toString() {
return clazz.getName()+"."+s+"()"; return clazz.getName()+"."+s+"()";
} }
public void luaStackCall(CallFrame call, int base, int top, int nresults) { public void luaStackCall(VM vm) {
try { try {
Method[] meths = clazz.getMethods(); // find the method
Method meth = null; ParamsList params = new ParamsList( vm );
Class[] paramTypes = null; Method meth = resolveMethod( clazz, s, params );
int score = Integer.MAX_VALUE;
int paramsBase = base + 2; // coerce the arguments
int nargs = top - paramsBase; Object[] args = CoerceLuaToJava.coerceArgs( params.values, meth.getParameterTypes() );
for ( int i=0; i<meths.length; i++ ) {
Method m = meths[i];
String name = m.getName();
if ( s.equals(name) ) {
Class[] p = m.getParameterTypes();
int s = CoerceLuaToJava.scoreParamTypes( call.stack, paramsBase, nargs, p );
if ( s < score ) {
meth = m;
paramTypes = p;
score = s;
}
}
}
Object[] args = CoerceLuaToJava.coerceArgs( call, paramsBase, nargs, paramTypes );
Object result = meth.invoke( instance, args ); Object result = meth.invoke( instance, args );
call.stack[base] = CoerceJavaToLua.coerce(result);
call.top = base + 1; // coerce the result
vm.setResult( CoerceJavaToLua.coerce(result) );
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
} }
private static Map<Class,Map<ParamsList,Constructor>> consCache =
new HashMap<Class,Map<ParamsList,Constructor>>();
private static Map<Class,Map<Integer,List<Constructor>>> consIndex =
new HashMap<Class,Map<Integer,List<Constructor>>>();
private static Constructor resolveConstructor(Class clazz, ParamsList params ) {
// get the cache
Map<ParamsList,Constructor> cache = consCache.get( clazz );
if ( cache == null )
consCache.put( clazz, cache = new HashMap<ParamsList,Constructor>() );
// look up in the cache
Constructor c = cache.get( params );
if ( c != null )
return c;
// get index
Map<Integer,List<Constructor>> index = consIndex.get( clazz );
if ( index == null ) {
consIndex.put( clazz, index = new HashMap<Integer,List<Constructor>>() );
Constructor[] cons = clazz.getConstructors();
for ( Constructor con : cons ) {
int n = con.getParameterTypes().length;
List<Constructor> list = index.get(n);
if ( list == null )
index.put( n, list = new ArrayList<Constructor>() );
list.add( con );
}
}
// figure out best list of arguments == supplied args
int n = params.classes.length;
List<Constructor> list = index.get(n);
if ( list == null )
throw new IllegalArgumentException("no constructor with "+n+" args");
// find constructor with best score
int bests = Integer.MAX_VALUE;
int besti = 0;
for ( int i=0, size=list.size(); i<size; i++ ) {
Constructor con = list.get(i);
int s = CoerceLuaToJava.scoreParamTypes(params.values, con.getParameterTypes());
if ( s < bests ) {
bests = s;
besti = i;
}
}
// put into cache
c = list.get(besti);
cache.put( params, c );
return c;
}
private static Map<Class,Map<String,Map<ParamsList,Method>>> methCache =
new HashMap<Class,Map<String,Map<ParamsList,Method>>>();
private static Map<Class,Map<String,Map<Integer,List<Method>>>> methIndex =
new HashMap<Class,Map<String,Map<Integer,List<Method>>>>();
private static Method resolveMethod(Class clazz, String methodName, ParamsList params ) {
// get the cache
Map<String,Map<ParamsList,Method>> nameCache = methCache.get( clazz );
if ( nameCache == null )
methCache.put( clazz, nameCache = new HashMap<String,Map<ParamsList,Method>>() );
Map<ParamsList,Method> cache = nameCache.get( methodName );
if ( cache == null )
nameCache.put( methodName, cache = new HashMap<ParamsList,Method>() );
// look up in the cache
Method m = cache.get( params );
if ( m != null )
return m;
// get index
Map<String,Map<Integer,List<Method>>> index = methIndex.get( clazz );
if ( index == null ) {
methIndex.put( clazz, index = new HashMap<String,Map<Integer,List<Method>>>() );
Method[] meths = clazz.getMethods();
for ( Method meth : meths ) {
String s = meth.getName();
int n = meth.getParameterTypes().length;
Map<Integer,List<Method>> map = index.get(s);
if ( map == null )
index.put( s, map = new HashMap<Integer,List<Method>>() );
List<Method> list = map.get(n);
if ( list == null )
map.put( n, list = new ArrayList<Method>() );
list.add( meth );
}
}
// figure out best list of arguments == supplied args
Map<Integer,List<Method>> map = index.get(methodName);
if ( map == null )
throw new IllegalArgumentException("no method named '"+methodName+"'");
int n = params.classes.length;
List<Method> list = map.get(n);
if ( list == null )
throw new IllegalArgumentException("no method named '"+methodName+"' with "+n+" args");
// find constructor with best score
int bests = Integer.MAX_VALUE;
int besti = 0;
for ( int i=0, size=list.size(); i<size; i++ ) {
Method meth = list.get(i);
int s = CoerceLuaToJava.scoreParamTypes(params.values, meth.getParameterTypes());
if ( s < bests ) {
bests = s;
besti = i;
}
}
// put into cache
m = list.get(besti);
cache.put( params, m );
return m;
}
} }

View File

@@ -37,41 +37,35 @@ final class Builtin extends LFunction {
} }
// perform a lua call // perform a lua call
public void luaStackCall(CallFrame call, int base, int top, int nresults) { public void luaStackCall(VM vm) {
switch ( id ) { switch ( id ) {
case PRINT: case PRINT:
if ( base+1<top ) int n = vm.getArgCount();
stdout.print( call.stack[base+1].luaAsString() ); for ( int i=0; i<n; i++ ) {
for ( int i=base+2; i<top; i++ ) { if ( i > 0 )
stdout.print( "\t" ); stdout.print( "\t" );
stdout.print( call.stack[i].luaAsString() ); stdout.print( vm.getArg(i).luaAsString() );
} }
stdout.println(); stdout.println();
call.top = base; vm.setResult();
break; break;
case PAIRS: case PAIRS:
LValue value = call.stack[base+1].luaPairs(); vm.setResult( vm.getArg(0).luaPairs() );
call.stack[base] = value;
call.top = base+1;
break; break;
case GETMETATABLE: case GETMETATABLE:
call.stack[base] = call.stack[base+1].luaGetMetatable(); vm.setResult( vm.getArg(0).luaGetMetatable() );
call.top = base+1;
break; break;
case SETMETATABLE: case SETMETATABLE:
call.stack[base+1].luaSetMetatable(call.stack[base+2]); LValue t = vm.getArg(0);
call.stack[base] = call.stack[base+1]; t.luaSetMetatable(vm.getArg(1));
call.top = base+1; vm.setResult( t );
break; break;
case TYPE: case TYPE:
call.stack[base] = call.stack[base+1].luaGetType(); vm.setResult( vm.getArg(0).luaGetType() );
call.top = base+1;
break; break;
default: default:
luaUnsupportedOperation(); luaUnsupportedOperation();
} }
if (nresults >= 0)
call.adjustTop(base + nresults);
} }
static void redirectOutput( OutputStream newStdOut ) { static void redirectOutput( OutputStream newStdOut ) {

View File

@@ -1,457 +0,0 @@
/**
*
*/
package lua;
import lua.io.Closure;
import lua.io.Proto;
import lua.io.UpVal;
import lua.value.LBoolean;
import lua.value.LInteger;
import lua.value.LNil;
import lua.value.LString;
import lua.value.LTable;
import lua.value.LValue;
public class CallFrame {
private final static boolean DEBUG = false;
public final StackState state;
public final LValue[] stack;
public int base;
public int top;
public final Closure cl;
public final Proto p;
private final LValue[] k;
private final int nresults;
int pc = 0;
boolean done = false;
CallFrame(StackState state, Closure c, int base, int nargs, int nresults) {
this.state = state;
this.stack = state.stack;
this.cl = c;
this.p = c.p;
this.k = p.k;
this.base = base;
this.nresults = nresults;
int nparams = p.numparams;
int nvalues = (p.is_vararg && nargs > nparams ? nargs : nparams);
for (int i = nargs; i < nvalues; i++)
this.state.stack[base + i] = LNil.NIL;
this.top = base + nvalues;
this.state.calls[++this.state.cc] = this;
this.state.avail = base + p.maxstacksize;
}
private LValue RKBC(int bc) {
return StackState.ISK(bc) ? k[StackState.INDEXK(bc)]
: this.state.stack[base + bc];
}
private LValue GETARG_RKB(int i) {
return RKBC(StackState.GETARG_B(i));
}
private LValue GETARG_RKC(int i) {
return RKBC(StackState.GETARG_C(i));
}
public void adjustTop(int newTop) {
while (top < newTop)
this.stack[top++] = LNil.NIL;
top = newTop;
}
public void exec() {
int i, a, b, c, o, n, cb;
LValue rkb, rkc, nvarargs, key, val;
StringBuffer sb;
LValue i0, step, idx, limit, init, table;
boolean back, body;
Proto proto;
Closure newClosure;
// reload the current calling context
int[] code = p.code;
while (true) {
if (DEBUG)
Print.printState(state, base, top, state.avail, cl, pc);
i = code[pc++];
// TODO: implement debug hooks
// if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) &&
// (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) {
// traceexec(L, pc);
// if (L->status == LUA_YIELD) { // did hook yield?
// L->savedpc = pc - 1;
// return;
// }
// base = L->base;
// }
a = StackState.GETARG_A(i);
switch (StackState.GET_OPCODE(i)) {
case StackState.OP_MOVE: {
b = StackState.GETARG_B(i);
this.stack[base + a] = this.stack[base + b];
continue;
}
case StackState.OP_LOADK: {
b = StackState.GETARG_Bx(i);
this.stack[base + a] = k[b];
continue;
}
case StackState.OP_LOADBOOL: {
b = StackState.GETARG_B(i);
c = StackState.GETARG_C(i);
this.stack[base + a] = (b != 0 ? LBoolean.TRUE
: LBoolean.FALSE);
if (c != 0)
pc++; /* skip next instruction (if C) */
continue;
}
case StackState.OP_LOADNIL: {
b = StackState.GETARG_B(i);
do {
this.stack[base + b] = LNil.NIL;
} while ((--b) >= a);
continue;
}
case StackState.OP_GETUPVAL: {
b = StackState.GETARG_B(i);
this.stack[base + a] = cl.upVals[b].getValue();
continue;
}
case StackState.OP_GETGLOBAL: {
b = StackState.GETARG_Bx(i);
key = k[b];
table = cl.env;
table.luaGetTable(this, base + a, table, key);
continue;
}
case StackState.OP_GETTABLE: {
b = StackState.GETARG_B(i);
key = GETARG_RKC(i);
table = this.stack[base + b];
table.luaGetTable(this, base + a, table, key);
continue;
}
case StackState.OP_SETGLOBAL: {
b = StackState.GETARG_Bx(i);
key = k[b];
val = this.stack[base + a];
table = cl.env;
table.luaSetTable(this, this.state.avail, table, key, val);
continue;
}
case StackState.OP_SETUPVAL: {
b = StackState.GETARG_B(i);
cl.upVals[b].setValue( this.stack[base + a] );
continue;
}
case StackState.OP_SETTABLE: {
key = GETARG_RKB(i);
val = GETARG_RKC(i);
table = this.stack[base + a];
table.luaSetTable(this, state.avail, table, key, val);
continue;
}
case StackState.OP_NEWTABLE: {
b = StackState.GETARG_B(i);
c = StackState.GETARG_C(i);
this.stack[base + a] = new LTable(b, c);
continue;
}
case StackState.OP_SELF: {
rkb = GETARG_RKB(i);
rkc = GETARG_RKC(i);
this.stack[base + a + 1] = rkb;
rkb.luaGetTable(this, base + a, rkb, rkc);
// StkId rb = RB(i);
// setobjs2s(L, ra+1, rb);
// Protect(luaV_gettable(L, rb, RKC(i), ra));
continue;
}
case StackState.OP_ADD:
case StackState.OP_SUB:
case StackState.OP_MUL:
case StackState.OP_DIV:
case StackState.OP_MOD:
case StackState.OP_POW: {
o = StackState.GET_OPCODE(i);
rkb = GETARG_RKB(i);
rkc = GETARG_RKC(i);
this.stack[base + a] = rkc.luaBinOpUnknown(o, rkb);
continue;
}
case StackState.OP_UNM: {
rkb = GETARG_RKB(i);
this.stack[base + a] = rkb.luaUnaryMinus();
continue;
}
case StackState.OP_NOT: {
rkb = GETARG_RKB(i);
this.stack[base + a] = (!rkb.luaAsBoolean() ? LBoolean.TRUE
: LBoolean.FALSE);
continue;
}
case StackState.OP_LEN: {
rkb = GETARG_RKB(i);
this.stack[base + a] = rkb.luaLength();
continue;
}
case StackState.OP_CONCAT: {
b = StackState.GETARG_B(i);
c = StackState.GETARG_C(i);
sb = new StringBuffer();
for (int j = b; j <= c; j++)
sb.append(this.stack[base + j].luaAsString());
this.stack[base + a] = new LString(sb.toString());
continue;
}
case StackState.OP_JMP: {
pc += StackState.GETARG_sBx(i);
continue;
}
case StackState.OP_EQ:
case StackState.OP_LT:
case StackState.OP_LE: {
o = StackState.GET_OPCODE(i);
rkb = GETARG_RKB(i);
rkc = GETARG_RKC(i);
boolean test = rkc.luaBinCmpUnknown(o, rkb);
if (test == (a == 0))
pc++;
continue;
}
case StackState.OP_TEST: {
c = StackState.GETARG_C(i);
if (this.stack[base + a].luaAsBoolean() != (c != 0))
pc++;
continue;
}
case StackState.OP_TESTSET: {
rkb = GETARG_RKB(i);
c = StackState.GETARG_C(i);
if (rkb.luaAsBoolean() != (c != 0))
pc++;
else
this.stack[base + a] = rkb;
continue;
}
case StackState.OP_CALL: {
/* ra is start of result location */
b = StackState.GETARG_B(i); // number of stack spaces to reserve
// for
// closure plus args
c = StackState.GETARG_C(i); // num results plus 1
if (b != 0) // else use previous instruction set top
top = base + a + b;
// make or set up the call
this.stack[base + a].luaStackCall(this, base + a, top, c - 1);
// force re-entry into current call
if (this.state.calls[this.state.cc] != this)
return;
// adjustTop only for case when call was completed
if (c > 0)
adjustTop(base + a + c - 1);
continue;
}
case StackState.OP_TAILCALL: {
b = StackState.GETARG_B(i); // number of stack spaces to reserve
// for
// closure plus args ??
c = StackState.GETARG_C(i); // number of return args - must be
// LUA_MULTRET
if (b != 0) // else use previous instruction set top
top = base + a + b;
close( base ); // Close open upvals
// make or set up the call
this.stack[base + a].luaStackCall(this, base + a, top, c - 1);
// adjustTop only for case when call was completed
if (this.state.calls[this.state.cc] != this) {
// was a vm call, or a Java call that re-entered the stack.
// copy down the stack variables and substitute the stack
// frame.
CallFrame ci = this.state.calls[this.state.cc];
n = ci.top - ci.base;
System.arraycopy(this.stack, ci.base,
this.stack, base, n);
ci.base = base;
ci.top = base + n;
this.state.calls[this.state.cc - 1] = this.state.calls[this.state.cc];
--this.state.cc;
// force a reset of the calling context state
return;
}
continue;
}
case StackState.OP_RETURN: {
b = StackState.GETARG_B(i); // number of return vals
if (b != 0) // else use previous top
top = base + a + b - 1;
close( base ); // close open upvals
n = top - (base + a); // number to copy down
System.arraycopy(this.stack, base + a, this.stack,
base - 1, n);
top = base - 1 + n;
// adjust results to what caller expected
if (nresults >= 0)
adjustTop(base + nresults);
// pop the call stack
done = true;
if ( --state.cc >= 0 ) {
CallFrame call = state.calls[state.cc];
call.top = top;
}
// force a reload of the calling context
return;
}
case StackState.OP_FORLOOP: {
i0 = this.stack[base + a];
step = this.stack[base + a + 2];
idx = step.luaBinOpUnknown(Lua.OP_ADD, i0);
limit = this.stack[base + a + 1];
back = step.luaBinCmpInteger(Lua.OP_LT, 0);
body = (back ? idx.luaBinCmpUnknown(Lua.OP_LE, limit) : limit
.luaBinCmpUnknown(Lua.OP_LE, idx));
if (body) {
this.stack[base + a] = idx;
this.stack[base + a + 3] = idx;
pc += StackState.GETARG_sBx(i);
}
continue;
}
case StackState.OP_FORPREP: {
init = this.stack[base + a];
step = this.stack[base + a + 2];
this.stack[base + a] = step.luaBinOpUnknown(Lua.OP_SUB,
init);
b = StackState.GETARG_sBx(i);
pc += b;
continue;
}
case StackState.OP_TFORLOOP: {
cb = a + 3; /* call base */
System.arraycopy(this.stack, base + a, this.stack,
base + cb, 3);
top = base + cb + 3; /* func. + 2 args (state and index) */
// call the iterator
c = StackState.GETARG_C(i);
this.stack[base + a].luaStackCall(this, base + cb, top, c - 1);
// test for continuation
if (this.stack[base + cb] != LNil.NIL) { // continue?
this.stack[base + cb - 1] = this.stack[base
+ cb]; // save control variable
} else {
pc++; // skip over jump
}
continue;
}
case StackState.OP_SETLIST: {
b = StackState.GETARG_B(i);
c = StackState.GETARG_C(i);
int listBase = base + a;
if (b == 0) {
b = top - listBase - 1;
}
if (c == 0) {
c = code[pc++];
}
table = this.stack[base + a];
for (int index = 1; index <= b; index++) {
val = this.stack[listBase + index];
table.luaSetTable(this, this.state.avail, table,
new LInteger(index), val);
}
top = base + a - 1;
continue;
}
case StackState.OP_CLOSE: {
close( a ); // close upvals higher in the stack than position a
continue;
}
case StackState.OP_CLOSURE: {
b = StackState.GETARG_Bx(i);
proto = cl.p.p[b];
newClosure = new Closure(this.state, proto);
for (int j = 0; j < newClosure.upVals.length; j++, pc++) {
i = code[pc];
o = StackState.GET_OPCODE(i);
b = StackState.GETARG_B(i);
if (o == StackState.OP_GETUPVAL) {
newClosure.upVals[j] = cl.upVals[b];
} else if (o == StackState.OP_MOVE) {
newClosure.upVals[j] = findUpVal( proto.upvalues[j], base + b );
} else {
throw new java.lang.IllegalArgumentException(
"bad opcode: " + o);
}
}
this.stack[base + a] = newClosure;
continue;
}
case StackState.OP_VARARG: {
// figure out how many args to copy
b = StackState.GETARG_B(i) - 1;
nvarargs = this.stack[base - 1];
n = nvarargs.luaAsInt();
if (b == StackState.LUA_MULTRET) {
b = n; // use entire varargs supplied
}
// copy args up to call stack area
for (int j = 0; j < b; j++)
this.stack[base + a + j] = (j < n ? this.stack[base
- n + j - 1]
: LNil.NIL);
top = base + a + b;
continue;
}
}
}
}
private UpVal findUpVal( LString upValName, int target ) {
UpVal up;
int i;
for ( i = this.state.upvals.size() - 1; i >= 0; --i ) {
up = (UpVal) this.state.upvals.elementAt( i );
if ( up.stack == this.stack && up.position == target ) {
return up;
} else if ( up.position < target ) {
break;
}
}
up = new UpVal( upValName, this.stack, target );
this.state.upvals.insertElementAt( up, i + 1 );
return up;
}
private void close( int limit ) {
while ( !state.upvals.empty() && ( (UpVal) this.state.upvals.lastElement() ).close( limit ) ) {
this.state.upvals.pop();
}
}
public void push(LValue value) {
stack[top++] = value;
}
}

114
src/main/java/lua/VM.java Normal file
View File

@@ -0,0 +1,114 @@
package lua;
import lua.io.Closure;
import lua.value.LTable;
import lua.value.LValue;
public interface VM {
// ================ interfaces for performing calls
/** Push an argument or return value onto the stack
*/
public void push( LValue value );
/** Push an int argument or return value onto the stack
*/
public void push( int value );
/** Push a double argument or return value onto the stack
*/
public void push( double value );
/** Push a boolean argument or return value onto the stack
*/
public void push( boolean value );
/** Push a String argument or return value onto the stack
*/
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,
* and subsequent values are arguments to the closure.
*/
public void stackCall();
/**
* Execute bytecodes until the current call completes
* or the vm yields.
*/
public void exec();
/**
* Put the closure on the stack with arguments,
* then perform the call. Leave return values
* on the stack for later querying.
*
* @param c
* @param values
*/
public void doCall(Closure c, LValue[] values);
// ================ interfaces for getting arguments when called
/**
* Get the number of argumnets supplied in the call.
*/
public int getArgCount();
/**
* Get the index-th argument supplied, or NIL if fewer than index were supplied.
* @param index
* @return
*/
public LValue getArg(int index);
/**
* Get the index-th argument as an int value, or 0 if fewer than index arguments were supplied.
* @param index
* @return
*/
public int getArgAsInt( int index );
/**
* Get the index-th argument as a double value, or 0 if fewer than index arguments were supplied.
* @param index
* @return
*/
public double getArgAsDouble( int index );
/**
* Get the index-th argument as a boolean value, or false if fewer than index arguments were supplied.
* @param index
* @return
*/
public boolean getArgAsBoolean( int index );
/**
* Get the index-th argument as a String value, or "" if fewer than index arguments were supplied.
* @param index
* @return
*/
public String getArgAsString( int index );
/** Set top to base in preparation for pushing return values.
* Can be used when returning no values.
*
* Once this is called, calls to getArg() are undefined.
*
* @see push() to push additional results once result is reset.
*/
public void setResult();
/** Convenience utility to set val to stack[base] and top to base + 1
* in preparation for returning one value
*
* Once this is called, calls to getArg() are undefined.
*
* @param val value to provide as the only result.
*/
public void setResult(LValue val);
}

View File

@@ -1,7 +1,7 @@
package lua.io; package lua.io;
import lua.CallFrame;
import lua.StackState; import lua.StackState;
import lua.VM;
import lua.value.LFunction; import lua.value.LFunction;
import lua.value.LValue; import lua.value.LValue;
@@ -9,14 +9,18 @@ public class Closure extends LFunction {
public LValue env; public LValue env;
public Proto p; public Proto p;
public UpVal[] upVals; public UpVal[] upVals;
// TODO: change arg type to VM?
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];
} }
// perform a lua call // called by vm when there is an OP_CALL
public void luaStackCall(CallFrame call, int base, int top, int nresults) { // in this case, we are on the stack,
call.state.stackCall( this, base, top, nresults ); // and simply need to cue the VM to treat it as a stack call
public void luaStackCall(VM vm) {
vm.stackCall();
} }
} }

View File

@@ -4,7 +4,7 @@ import java.io.DataInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import lua.StackState; import lua.VM;
import lua.value.LBoolean; import lua.value.LBoolean;
import lua.value.LDouble; import lua.value.LDouble;
import lua.value.LInteger; import lua.value.LInteger;
@@ -44,15 +44,14 @@ public class LoadState {
private int luacSizeofLuaNumber; private int luacSizeofLuaNumber;
private boolean luacIsNumberIntegral; private boolean luacIsNumberIntegral;
/** The lua state that is loading the code */
private StackState L;
/** input stream from which we are loading */ /** input stream from which we are loading */
private DataInputStream is; private DataInputStream is;
/** Name of what is being loaded? */ /** Name of what is being loaded? */
String name; String name;
/** The VM doing the loading */
VM L;
private static final int LUA_TNONE = (-1); private static final int LUA_TNONE = (-1);
@@ -66,75 +65,10 @@ public class LoadState {
private static final int LUA_TUSERDATA = 7; private static final int LUA_TUSERDATA = 7;
private static final int LUA_TTHREAD = 8; private static final int LUA_TTHREAD = 8;
// /*
// ** $Id$
// ** load precompiled Lua chunks
// ** See Copyright Notice in lua.h
// */
//
// #include <string.h>
//
// #define lundump_c
// #define LUA_CORE
//
// #include "lua.h"
//
// #include "ldebug.h"
// #include "ldo.h"
// #include "lfunc.h"
// #include "lmem.h"
// #include "lobject.h"
// #include "lstring.h"
// #include "lundump.h"
// #include "lzio.h"
//
// typedef struct {
// lua_State* L;
// ZIO* Z;
// Mbuffer* b;
// const char* name;
// } LoadState;
//
// #ifdef LUAC_TRUST_BINARIES
// #define IF(c,s)
// #else
// #define IF(c,s) if (c) error(S,s)
//
// static void error(LoadState* S, const char* why)
// {
// luaO_pushfstring(S->L,"%s: %s in precompiled chunk",S->name,why);
// luaD_throw(S->L,LUA_ERRSYNTAX);
// }
// #endif
//
// #define LoadMem(S,b,n,size) LoadBlock(S,b,(n)*(size))
// #define LoadByte(S) (lu_byte)LoadChar(S)
// #define LoadVar(S,x) LoadMem(S,&x,1,sizeof(x))
// #define LoadVector(S,b,n,size) LoadMem(S,b,n,size)
//
// static void LoadBlock(LoadState* S, void* b, size_t size)
// {
// size_t r=luaZ_read(S->Z,b,size);
// IF (r!=0, "unexpected end");
// }
//
// static int LoadChar(LoadState* S)
// {
// char x;
// LoadVar(S,x);
// return x;
// }
int loadByte() throws IOException { int loadByte() throws IOException {
return is.readUnsignedByte(); return is.readUnsignedByte();
} }
//
// static int LoadInt(LoadState* S)
// {
// int x;
// LoadVar(S,x);
// IF (x<0, "bad integer");
// return x;
// }
int loadInt() throws IOException { int loadInt() throws IOException {
if ( this.luacLittleEndian ) { if ( this.luacLittleEndian ) {
int a = is.readUnsignedByte(); int a = is.readUnsignedByte();
@@ -158,27 +92,7 @@ public class LoadState {
} }
return (((long)b)<<32) | (((long)a)&0xffffffffL); return (((long)b)<<32) | (((long)a)&0xffffffffL);
} }
//
// static lua_Number LoadNumber(LoadState* S)
// {
// lua_Number x;
// LoadVar(S,x);
// return x;
// }
//
// static TString* LoadString(LoadState* S)
// {
// size_t size;
// LoadVar(S,size);
// if (size==0)
// return NULL;
// else
// {
// char* s=luaZ_openspace(S->L,S->b,size);
// LoadBlock(S,s,size);
// return luaS_newlstr(S->L,s,size-1); /* remove trailing '\0' */
// }
// }
LString loadString() throws IOException { LString loadString() throws IOException {
int size = loadInt(); int size = loadInt();
if ( size == 0 ) if ( size == 0 )
@@ -218,14 +132,7 @@ public class LoadState {
return longBitsToLuaNumber( loadInt64() ); return longBitsToLuaNumber( loadInt64() );
} }
} }
//
// static void LoadCode(LoadState* S, Proto* f)
// {
// int n=LoadInt(S);
// f->code=luaM_newvector(S->L,n,Instruction);
// f->sizecode=n;
// LoadVector(S,f->code,n,sizeof(Instruction));
// }
public void loadCode( Proto f ) throws IOException { public void loadCode( Proto f ) throws IOException {
int n = loadInt(); int n = loadInt();
int[] code = new int[n]; int[] code = new int[n];
@@ -233,45 +140,7 @@ public class LoadState {
code[i] = loadInt(); code[i] = loadInt();
f.code = code; f.code = code;
} }
//
// static Proto* LoadFunction(LoadState* S, TString* p);
//
// static void LoadConstants(LoadState* S, Proto* f)
// {
// int i,n;
// n=LoadInt(S);
// f->k=luaM_newvector(S->L,n,TValue);
// f->sizek=n;
// for (i=0; i<n; i++) setnilvalue(&f->k[i]);
// for (i=0; i<n; i++)
// {
// TValue* o=&f->k[i];
// int t=LoadChar(S);
// switch (t)
// {
// case LUA_TNIL:
// setnilvalue(o);
// break;
// case LUA_TBOOLEAN:
// setbvalue(o,LoadChar(S));
// break;
// case LUA_TNUMBER:
// setnvalue(o,LoadNumber(S));
// break;
// case LUA_TSTRING:
// setsvalue2n(S->L,o,LoadString(S));
// break;
// default:
// IF (1, "bad constant");
// break;
// }
// }
// n=LoadInt(S);
// f->p=luaM_newvector(S->L,n,Proto*);
// f->sizep=n;
// for (i=0; i<n; i++) f->p[i]=NULL;
// for (i=0; i<n; i++) f->p[i]=LoadFunction(S,f->source);
// }
void loadConstants(Proto f) throws IOException { void loadConstants(Proto f) throws IOException {
int n = loadInt(); int n = loadInt();
LValue[] values = new LValue[n]; LValue[] values = new LValue[n];
@@ -301,32 +170,7 @@ public class LoadState {
protos[i] = loadFunction(f.source); protos[i] = loadFunction(f.source);
f.p = protos; f.p = protos;
} }
//
// static void LoadDebug(LoadState* S, Proto* f)
// {
// int i,n;
// n=LoadInt(S);
// f->lineinfo=luaM_newvector(S->L,n,int);
// f->sizelineinfo=n;
// LoadVector(S,f->lineinfo,n,sizeof(int));
// n=LoadInt(S);
// f->locvars=luaM_newvector(S->L,n,LocVar);
// f->sizelocvars=n;
// for (i=0; i<n; i++) f->locvars[i].varname=NULL;
// for (i=0; i<n; i++)
// {
// f->locvars[i].varname=LoadString(S);
// f->locvars[i].startpc=LoadInt(S);
// f->locvars[i].endpc=LoadInt(S);
// }
// n=LoadInt(S);
// f->upvalues=luaM_newvector(S->L,n,TString*);
// f->sizeupvalues=n;
// for (i=0; i<n; i++) f->upvalues[i]=NULL;
// for (i=0; i<n; i++) f->upvalues[i]=LoadString(S);
// }
void loadDebug( Proto f ) throws IOException { void loadDebug( Proto f ) throws IOException {
int n = loadInt(); int n = loadInt();
f.lineinfo = new int[n]; f.lineinfo = new int[n];
@@ -348,27 +192,9 @@ public class LoadState {
f.upvalues[i] = loadString(); f.upvalues[i] = loadString();
} }
} }
//
// static Proto* LoadFunction(LoadState* S, TString* p)
// {
// Proto* f=luaF_newproto(S->L);
// setptvalue2s(S->L,S->L->top,f); incr_top(S->L);
// f->source=LoadString(S); if (f->source==NULL) f->source=p;
// f->linedefined=LoadInt(S);
// f->lastlinedefined=LoadInt(S);
// f->nups=LoadByte(S);
// f->numparams=LoadByte(S);
// f->is_vararg=LoadByte(S);
// f->maxstacksize=LoadByte(S);
// LoadCode(S,f);
// LoadConstants(S,f);
// LoadDebug(S,f);
// IF (!luaG_checkcode(f), "bad code");
// S->L->top--;
// return f;
// }
public Proto loadFunction(LString p) throws IOException { public Proto loadFunction(LString p) throws IOException {
Proto f = new Proto(this.L); Proto f = new Proto();
// this.L.push(f); // this.L.push(f);
f.source = loadString(); f.source = loadString();
f.linedefined = loadInt(); f.linedefined = loadInt();
@@ -410,27 +236,8 @@ public class LoadState {
if ( sig != LUAC_HEADER_SIGNATURE ) if ( sig != LUAC_HEADER_SIGNATURE )
throw new IllegalArgumentException("bad signature"); throw new IllegalArgumentException("bad signature");
} }
//
// /*
// ** load precompiled chunk
// */
// Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name)
// {
// LoadState S;
// if (*name=='@' || *name=='=')
// S.name=name+1;
// else if (*name==LUA_SIGNATURE[0])
// S.name="binary string";
// else
// S.name=name;
// S.L=L;
// S.Z=Z;
// S.b=buff;
// LoadHeader(&S);
// return LoadFunction(&S,luaS_newliteral(L,"=?"));
// }
public static Proto undump( StackState L, InputStream stream, String name ) throws IOException { public static Proto undump( VM L, InputStream stream, String name ) throws IOException {
String sname = name; String sname = name;
if ( name.startsWith("@") || name.startsWith("=") ) if ( name.startsWith("@") || name.startsWith("=") )
sname = name.substring(1); sname = name.substring(1);
@@ -438,12 +245,12 @@ public class LoadState {
sname = "binary string"; sname = "binary string";
LoadState s = new LoadState( L, stream, sname ); LoadState s = new LoadState( L, stream, sname );
s.loadHeader(); s.loadHeader();
LString literal = new LString(L, "=?"); LString literal = new LString("=?");
return s.loadFunction( literal ); return s.loadFunction( literal );
} }
/** Private constructor for create a load state */ /** Private constructor for create a load state */
private LoadState( StackState L, InputStream stream, String name ) { private LoadState( VM L, InputStream stream, String name ) {
this.L = L; this.L = L;
this.name = name; this.name = name;
this.is = new DataInputStream( stream ); this.is = new DataInputStream( stream );

View File

@@ -8,8 +8,6 @@ import lua.value.LString;
** Function Prototypes ** Function Prototypes
*/ */
public class Proto { public class Proto {
public Proto(StackState l) {
}
public Proto() { public Proto() {
} }

View File

@@ -1,6 +1,6 @@
package lua.value; package lua.value;
import lua.CallFrame; import lua.VM;
public class LFunction extends LValue { public class LFunction extends LValue {
@@ -11,21 +11,19 @@ public class LFunction extends LValue {
return "function: "+hashCode(); return "function: "+hashCode();
} }
public void luaSetTable(CallFrame call, int base, LValue table, LValue key, LValue val) { public void luaSetTable(VM vm, LValue table, LValue key, LValue val) {
call.top = base; vm.push( this );
call.push( this ); vm.push( table );
call.push( table ); vm.push( key );
call.push( key ); vm.push( val );
call.push( val ); this.luaStackCall(vm);
this.luaStackCall(call, base, call.top, 1);
} }
public void luaGetTable(CallFrame call, int base, LValue table, LValue key) { public void luaGetTable(VM vm, LValue table, LValue key) {
call.top = base; vm.push( this );
call.push( this ); vm.push( table );
call.push( table ); vm.push( key );
call.push( key ); this.luaStackCall(vm);
this.luaStackCall(call, base, call.top, 1);
} }
public LString luaGetType() { public LString luaGetType() {

View File

@@ -27,11 +27,6 @@ public class LString extends LValue {
return m_hash; return m_hash;
} }
// TODO: what to do with LuaState?
public LString(StackState l, String string) {
this(string);
}
public boolean luaBinCmpUnknown(int opcode, LValue lhs) { public boolean luaBinCmpUnknown(int opcode, LValue lhs) {
return lhs.luaBinCmpString(opcode, m_string); return lhs.luaBinCmpString(opcode, m_string);
} }

View File

@@ -3,7 +3,7 @@ package lua.value;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Hashtable; import java.util.Hashtable;
import lua.CallFrame; import lua.VM;
public class LTable extends LValue { public class LTable extends LValue {
@@ -39,12 +39,12 @@ public class LTable extends LValue {
return (LValue) m_hash.get( new LString(key) ); return (LValue) m_hash.get( new LString(key) );
} }
public void luaSetTable(CallFrame call, int base, LValue table, LValue key, LValue val) { public void luaSetTable(VM vm, LValue table, LValue key, LValue val) {
if ( m_metatable != null ) { if ( m_metatable != null ) {
if ( ! m_hash.containsKey(key) ) { if ( ! m_hash.containsKey(key) ) {
LValue event = (LValue) m_metatable.m_hash.get( TM_NEWINDEX ); LValue event = (LValue) m_metatable.m_hash.get( TM_NEWINDEX );
if ( event != null && event != LNil.NIL ) { if ( event != null && event != LNil.NIL ) {
event.luaSetTable( call, base, table, key, val ); event.luaSetTable( vm, table, key, val );
return; return;
} }
} }
@@ -52,19 +52,21 @@ public class LTable extends LValue {
m_hash.put( key, val ); m_hash.put( key, val );
} }
public void luaGetTable(CallFrame call, int base, LValue table, LValue key) { public void luaGetTable(VM vm, LValue table, LValue key) {
LValue val = (LValue) m_hash.get(key); LValue val = (LValue) m_hash.get(key);
if ( val == null || val == LNil.NIL ) { if ( val == null || val == LNil.NIL ) {
if ( m_metatable != null ) { if ( m_metatable != null ) {
LValue event = (LValue) m_metatable.m_hash.get( TM_INDEX ); LValue event = (LValue) m_metatable.m_hash.get( TM_INDEX );
if ( event != null && event != LNil.NIL ) { if ( event != null && event != LNil.NIL ) {
event.luaGetTable( call, base, table, key ); event.luaGetTable( vm, table, key );
return; return;
} }
} }
val = LNil.NIL; val = LNil.NIL;
} }
call.stack[base] = val;
// stack.stack[base] = val;
vm.push(val);
} }
public String toString() { public String toString() {
@@ -107,19 +109,14 @@ public class LTable extends LValue {
} }
// perform a lua call // perform a lua call
public void luaStackCall(CallFrame call, int base, int top, int nresults) { public void luaStackCall(VM vm) {
if ( e.hasMoreElements() ) { if ( e.hasMoreElements() ) {
LValue key = (LValue) e.nextElement(); LValue key = (LValue) e.nextElement();
LValue val = (LValue) t.m_hash.get(key); LValue val = (LValue) t.m_hash.get(key);
call.stack[base] = key; vm.setResult();
call.stack[base+1] = val; vm.push( key );
call.top = base+2; vm.push( val );
} else {
call.stack[base] = LNil.NIL;
call.top = base+1;
} }
if ( nresults >= 0 )
call.adjustTop(base + nresults);
} }
} }

View File

@@ -1,7 +1,7 @@
package lua.value; package lua.value;
import lua.CallFrame;
import lua.Lua; import lua.Lua;
import lua.VM;
abstract abstract
public class LValue { public class LValue {
@@ -20,7 +20,7 @@ public class LValue {
} }
// perform a lua call, return number of results actually produced // perform a lua call, return number of results actually produced
public void luaStackCall(CallFrame call, int base, int top, int nresults) { public void luaStackCall(VM vm) {
luaUnsupportedOperation(); luaUnsupportedOperation();
} }
@@ -72,20 +72,21 @@ public class LValue {
} }
/** set a value in a table /** set a value in a table
* @param call the stack state * @param vm the calling vm
* @param base the base of the stack, in case a function is put on the stack
* @param table the table to operate on * @param table the table to operate on
* @param the key to set * @param the key to set
* @param the value to set * @param the value to set
*/ */
public void luaSetTable(CallFrame call, int base, LValue table, LValue key, LValue val) { public void luaSetTable(VM vm, LValue table, LValue key, LValue val) {
luaUnsupportedOperation(); luaUnsupportedOperation();
} }
/** Get a value from a table /** Get a value from a table
* @param base TODO * @param vm the calling vm
* @param table TODO*/ * @param table the table from which to get the value
public void luaGetTable(CallFrame call, int base, LValue table, LValue key) { * @param key the key to look up
*/
public void luaGetTable(VM vm, LValue table, LValue key) {
luaUnsupportedOperation(); luaUnsupportedOperation();
} }

View File

@@ -30,18 +30,13 @@ public class LuaJavaAppRunner {
// new lua state // new lua state
StackState state = new StackState(); StackState state = new StackState();
// convert args to lua
LValue[] vargs = new LValue[args.length];
for ( int i=1; i<args.length; i++ )
vargs[i] = new LString(args[i]);
// load the file // load the file
InputStream is = LuaJavaAppRunner.class.getResourceAsStream( script ); InputStream is = LuaJavaAppRunner.class.getResourceAsStream( script );
Proto p = LoadState.undump(state, is, script); Proto p = LoadState.undump(state, is, script);
// create closure and execute // create closure and execute
Closure c = new Closure( state, p ); Closure c = new Closure( state, p );
state.doCall(c, vargs, 0); state.doCall(c, new LValue[0]);
} }
} }

View File

@@ -3,6 +3,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import lua.StackState; import lua.StackState;
import lua.VM;
import lua.io.Closure; import lua.io.Closure;
import lua.io.LoadState; import lua.io.LoadState;
import lua.io.Proto; import lua.io.Proto;
@@ -19,16 +20,12 @@ public class LuacRunner {
public static void main( String[] args ) throws IOException { public static void main( String[] args ) throws IOException {
// get script name // get script name
String script = (args.length>0? args[0]: "/test1.luac"); String script = (args.length>0? args[0]: "/test2.luac");
System.out.println("loading '"+script+"'"); System.out.println("loading '"+script+"'");
// new lua state // new lua state
StackState state = new StackState(); StackState state = new StackState();
VM vm = state;
// convert args to lua
LValue[] vargs = new LValue[args.length];
for ( int i=1; i<args.length; i++ )
vargs[i] = new LString(args[i]);
// load the file // load the file
InputStream is = LuacRunner.class.getResourceAsStream( script ); InputStream is = LuacRunner.class.getResourceAsStream( script );
@@ -36,6 +33,8 @@ public class LuacRunner {
// create closure and execute // create closure and execute
Closure c = new Closure( state, p ); Closure c = new Closure( state, p );
state.doCall(c, vargs, 0);
// do the call
vm.doCall( c, new LValue[0] );
} }
} }

View File

@@ -95,7 +95,7 @@ public class LuaJTest extends TestCase {
try { try {
// create closure and execute // create closure and execute
Closure c = new Closure( state, p ); Closure c = new Closure( state, p );
state.doCall(c, new LValue[0], 0); state.doCall(c, new LValue[0]);
final String actualOutput = new String( outputStream.toByteArray() ); final String actualOutput = new String( outputStream.toByteArray() );
final String expectedOutput = getExpectedOutput( testName ); final String expectedOutput = getExpectedOutput( testName );

View File

@@ -78,14 +78,14 @@ public class StandardTest extends TestCase {
Builtin.redirectOutput( output ); Builtin.redirectOutput( output );
try { try {
try { try {
state.doCall( c, new LValue[0], 0 ); state.doCall( c, new LValue[0] );
} catch ( RuntimeException exn ) { } catch ( RuntimeException exn ) {
StackTraceElement[] stackTrace = new StackTraceElement[state.cc+1]; StackTraceElement[] stackTrace = new StackTraceElement[state.cc+1];
for ( int i = 0; i <= state.cc; ++i ) { for ( int i = 0; i <= state.cc; ++i ) {
CallFrame call = state.calls[i]; CallInfo call = state.calls[i];
Proto p = call.p; Proto p = call.closure.p;
int line = p.lineinfo[call.pc]; int line = p.lineinfo[call.pc];
String func = call.cl.luaAsString(); String func = call.closure.luaAsString();
stackTrace[state.cc - i] = new StackTraceElement(getName(), func, getName()+".lua", line ); stackTrace[state.cc - i] = new StackTraceElement(getName(), func, getName()+".lua", line );
} }