From 885397a74cdbf6cce78ca4acb841bc64e03bae5d Mon Sep 17 00:00:00 2001 From: Shu Lei Date: Wed, 3 Oct 2007 23:33:45 +0000 Subject: [PATCH] added back the files that were accidentally deleted --- src/main/java/lua/Builtin.java | 105 ++++++ src/main/java/lua/GlobalState.java | 41 ++ src/main/java/lua/Lua.java | 357 ++++++++++++++++++ src/main/java/lua/VM.java | 197 ++++++++++ src/main/java/lua/value/LNumber.java | 27 ++ src/main/java/lua/value/LValue.java | 182 +++++++++ .../java/lua/debug/DebugStackStateTest.java | 21 ++ src/test/java/lua/io/LoadStateTest.java | 92 +++++ 8 files changed, 1022 insertions(+) create mode 100644 src/main/java/lua/Builtin.java create mode 100644 src/main/java/lua/GlobalState.java create mode 100644 src/main/java/lua/Lua.java create mode 100644 src/main/java/lua/VM.java create mode 100644 src/main/java/lua/value/LNumber.java create mode 100644 src/main/java/lua/value/LValue.java create mode 100644 src/test/java/lua/io/LoadStateTest.java diff --git a/src/main/java/lua/Builtin.java b/src/main/java/lua/Builtin.java new file mode 100644 index 00000000..4a6eefde --- /dev/null +++ b/src/main/java/lua/Builtin.java @@ -0,0 +1,105 @@ +/** + * + */ +package lua; + +import java.io.OutputStream; +import java.io.PrintStream; + +import lua.value.LBoolean; +import lua.value.LFunction; +import lua.value.LNil; +import lua.value.LTable; +import lua.value.LValue; + +final class Builtin extends LFunction { + + static void addBuiltins(LTable table) { + for ( int i=0; i 0 ) + stdout.print( "\t" ); + stdout.print( vm.getArg(i).luaAsString() ); + } + stdout.println(); + vm.setResult(); + } + break; + case PAIRS: + case IPAIRS: + vm.setResult( vm.getArg(0).luaPairs(id==PAIRS) ); + break; + case GETMETATABLE: + vm.setResult( vm.getArg(0).luaGetMetatable() ); + break; + case SETMETATABLE: + LValue t = vm.getArg(0); + t.luaSetMetatable(vm.getArg(1)); + vm.setResult( t ); + break; + case TYPE: + vm.setResult( vm.getArg(0).luaGetType() ); + break; + case PCALL: { + int n = vm.getArgCount(); + int s = vm.lua_pcall( n-1, Lua.LUA_MULTRET ); + if ( s != 0 ) { + LValue v = vm.lua_tolvalue(-1); + vm.setResult( LBoolean.FALSE ); + vm.push( v ); + } else { + vm.setResult( LBoolean.TRUE ); + } + } + break; + default: + luaUnsupportedOperation(); + } + return false; + } + + static void redirectOutput( OutputStream newStdOut ) { + stdout = new PrintStream( newStdOut ); + } + + static void restoreStandardOutput() { + stdout = System.out; + } + +} diff --git a/src/main/java/lua/GlobalState.java b/src/main/java/lua/GlobalState.java new file mode 100644 index 00000000..35f6e1cf --- /dev/null +++ b/src/main/java/lua/GlobalState.java @@ -0,0 +1,41 @@ +package lua; + +import java.util.Hashtable; + +import lua.value.LTable; + +/** +** `global state', shared by all threads of this state +*/ +public class GlobalState { + +// typedef struct global_State { + Hashtable strt; /* hash table for strings */ + StringBuffer buff; /* temporary buffer for string concatentation */ +// lu_mem totalbytes; /* number of bytes currently allocated */ +// lu_mem estimate; /* an estimate of number of bytes actually in use */ +// lua_CFunction panic; /* to be called in unprotected errors */ +// TValue l_registry; +// struct lua_State *mainthread; + StackState mainthread; +// UpVal uvhead; /* head of double-linked list of all open upvalues */ +// struct Table *mt[NUM_TAGS]; /* metatables for basic types */ +// TString *tmname[TM_N]; /* array with tag-method names */ +// } global_State; +// + private static LTable _G; + + static { + resetGlobals(); + } + + static void resetGlobals() { + _G = new LTable(); + _G .put( "_G", _G ); + Builtin.addBuiltins( _G ); + } + + public static LTable getGlobalsTable() { + return _G; + } +} diff --git a/src/main/java/lua/Lua.java b/src/main/java/lua/Lua.java new file mode 100644 index 00000000..93eb0a91 --- /dev/null +++ b/src/main/java/lua/Lua.java @@ -0,0 +1,357 @@ +package lua; +/** + * Constants for lua limits and opcodes + * + * @author jim_roseborough + * + */ +public class Lua { + + // from llimits.h + + /** maximum stack for a Lua function */ + public static final int MAXSTACK = 250; + + /** minimum size for the string table (must be power of 2) */ + public static final int MINSTRTABSIZE = 32; + + /** minimum size for string buffer */ + public static final int LUA_MINBUFFER = 32; + + /** use return values from previous op */ + public static final int LUA_MULTRET = -1; + + + // from lopcodes.h + + /*=========================================================================== + We assume that instructions are unsigned numbers. + All instructions have an opcode in the first 6 bits. + Instructions can have the following fields: + `A' : 8 bits + `B' : 9 bits + `C' : 9 bits + `Bx' : 18 bits (`B' and `C' together) + `sBx' : signed Bx + + A signed argument is represented in excess K; that is, the number + value is the unsigned value minus K. K is exactly the maximum value + for that argument (so that -max is represented by 0, and +max is + represented by 2*max), which is half the maximum for the corresponding + unsigned argument. + ===========================================================================*/ + + + /* basic instruction format */ + public static final int iABC = 0; + public static final int iABx = 1; + public static final int iAsBx = 2; + + + /* + ** size and position of opcode arguments. + */ + public static final int SIZE_C = 9; + public static final int SIZE_B = 9; + public static final int SIZE_Bx = (SIZE_C + SIZE_B); + public static final int SIZE_A = 8; + + public static final int SIZE_OP = 6; + + public static final int POS_OP = 0; + public static final int POS_A = (POS_OP + SIZE_OP); + public static final int POS_C = (POS_A + SIZE_A); + public static final int POS_B = (POS_C + SIZE_C); + public static final int POS_Bx = POS_C; + + + public static final int MAX_OP = ((1<>1); /* `sBx' is signed */ + + public static final int MASK_OP = ((1<> POS_OP) & MAX_OP; + } + + public static int GETARG_A(int i) { + return (i >> POS_A) & MAXARG_A; + } + + public static int GETARG_B(int i) { + return (i >> POS_B) & MAXARG_B; + } + + public static int GETARG_C(int i) { + return (i >> POS_C) & MAXARG_C; + } + + public static int GETARG_Bx(int i) { + return (i >> POS_Bx) & MAXARG_Bx; + } + + public static int GETARG_sBx(int i) { + return ((i >> POS_Bx) & MAXARG_Bx) - MAXARG_sBx; + } + + + /* + ** Macros to operate RK indices + */ + + /** this bit 1 means constant (0 means register) */ + public static final int BITRK = (1 << (SIZE_B - 1)); + + /** test whether value is a constant */ + public static boolean ISK(int x) { + return 0 != ((x) & BITRK); + } + + /** gets the index of the constant */ + public static int INDEXK(int r) { + return ((int)(r) & ~BITRK); + } + + public static final int MAXINDEXRK = (BITRK - 1); + + /** code a constant index as a RK value */ + public static int RKASK(int x) { + return ((x) | BITRK); + } + + + /** + ** invalid register that fits in 8 bits + */ + public static final int NO_REG = MAXARG_A; + + + /* + ** R(x) - register + ** Kst(x) - constant (in constant table) + ** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x) + */ + + + /* + ** grep "ORDER OP" if you change these enums + */ + + /*---------------------------------------------------------------------- + name args description + ------------------------------------------------------------------------*/ + public static final int OP_MOVE = 0;/* A B R(A) := R(B) */ + public static final int OP_LOADK = 1;/* A Bx R(A) := Kst(Bx) */ + public static final int OP_LOADBOOL = 2;/* A B C R(A) := (Bool)B; if (C) pc++ */ + public static final int OP_LOADNIL = 3; /* A B R(A) := ... := R(B) := nil */ + public static final int OP_GETUPVAL = 4; /* A B R(A) := UpValue[B] */ + + public static final int OP_GETGLOBAL = 5; /* A Bx R(A) := Gbl[Kst(Bx)] */ + public static final int OP_GETTABLE = 6; /* A B C R(A) := R(B)[RK(C)] */ + + public static final int OP_SETGLOBAL = 7; /* A Bx Gbl[Kst(Bx)] := R(A) */ + public static final int OP_SETUPVAL = 8; /* A B UpValue[B] := R(A) */ + public static final int OP_SETTABLE = 9; /* A B C R(A)[RK(B)] := RK(C) */ + + public static final int OP_NEWTABLE = 10; /* A B C R(A) := {} (size = B,C) */ + + public static final int OP_SELF = 11; /* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */ + + public static final int OP_ADD = 12; /* A B C R(A) := RK(B) + RK(C) */ + public static final int OP_SUB = 13; /* A B C R(A) := RK(B) - RK(C) */ + public static final int OP_MUL = 14; /* A B C R(A) := RK(B) * RK(C) */ + public static final int OP_DIV = 15; /* A B C R(A) := RK(B) / RK(C) */ + public static final int OP_MOD = 16; /* A B C R(A) := RK(B) % RK(C) */ + public static final int OP_POW = 17; /* A B C R(A) := RK(B) ^ RK(C) */ + public static final int OP_UNM = 18; /* A B R(A) := -R(B) */ + public static final int OP_NOT = 19; /* A B R(A) := not R(B) */ + public static final int OP_LEN = 20; /* A B R(A) := length of R(B) */ + + public static final int OP_CONCAT = 21; /* A B C R(A) := R(B).. ... ..R(C) */ + + public static final int OP_JMP = 22; /* sBx pc+=sBx */ + + public static final int OP_EQ = 23; /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */ + public static final int OP_LT = 24; /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */ + public static final int OP_LE = 25; /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */ + + public static final int OP_TEST = 26; /* A C if not (R(A) <=> C) then pc++ */ + public static final int OP_TESTSET = 27; /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ + + public static final int OP_CALL = 28; /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ + public static final int OP_TAILCALL = 29; /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ + public static final int OP_RETURN = 30; /* A B return R(A), ... ,R(A+B-2) (see note) */ + + public static final int OP_FORLOOP = 31; /* A sBx R(A)+=R(A+2); + if R(A) =) R(A)*/ + public static final int OP_CLOSURE = 36; /* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */ + + public static final int OP_VARARG = 37; /* A B R(A), R(A+1), ..., R(A+B-1) = vararg */ + + + public static final int NUM_OPCODES = OP_VARARG + 1; + + + + /*=========================================================================== + Notes: + (*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1, + and can be 0: OP_CALL then sets `top' to last_result+1, so + next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'. + + (*) In OP_VARARG, if (B == 0) then use actual number of varargs and + set top (like in OP_CALL with C == 0). + + (*) In OP_RETURN, if (B == 0) then return up to `top' + + (*) In OP_SETLIST, if (B == 0) then B = `top'; + if (C == 0) then next `instruction' is real C + + (*) For comparisons, A specifies what condition the test should accept + (true or false). + + (*) All `skips' (pc++) assume that next instruction is a jump + ===========================================================================*/ + + + /* + ** masks for instruction properties. The format is: + ** bits 0-1: op mode + ** bits 2-3: C arg mode + ** bits 4-5: B arg mode + ** bit 6: instruction set register A + ** bit 7: operator is a test + */ + + public static final int OpArgN = 0; /* argument is not used */ + public static final int OpArgU = 1; /* argument is used */ + public static final int OpArgR = 2; /* argument is a register or a jump offset */ + public static final int OpArgK = 3; /* argument is a constant or register/constant */ + + public static final int[] luaP_opmodes = { + /* T A B C mode opcode */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_MOVE */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgN<<2) | (iABx), /* OP_LOADK */ + (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_LOADBOOL */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_LOADNIL */ + (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_GETUPVAL */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgN<<2) | (iABx), /* OP_GETGLOBAL */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgK<<2) | (iABC), /* OP_GETTABLE */ + (0<<7) | (0<<6) | (OpArgK<<4) | (OpArgN<<2) | (iABx), /* OP_SETGLOBAL */ + (0<<7) | (0<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_SETUPVAL */ + (0<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_SETTABLE */ + (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_NEWTABLE */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgK<<2) | (iABC), /* OP_SELF */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_ADD */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_SUB */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_MUL */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_DIV */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_MOD */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_POW */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_UNM */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_NOT */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_LEN */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgR<<2) | (iABC), /* OP_CONCAT */ + (0<<7) | (0<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_JMP */ + (1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_EQ */ + (1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_LT */ + (1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_LE */ + (1<<7) | (1<<6) | (OpArgR<<4) | (OpArgU<<2) | (iABC), /* OP_TEST */ + (1<<7) | (1<<6) | (OpArgR<<4) | (OpArgU<<2) | (iABC), /* OP_TESTSET */ + (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_CALL */ + (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_TAILCALL */ + (0<<7) | (0<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_RETURN */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_FORLOOP */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_FORPREP */ + (1<<7) | (0<<6) | (OpArgN<<4) | (OpArgU<<2) | (iABC), /* OP_TFORLOOP */ + (0<<7) | (0<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_SETLIST */ + (0<<7) | (0<<6) | (OpArgN<<4) | (OpArgN<<2) | (iABC), /* OP_CLOSE */ + (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABx), /* OP_CLOSURE */ + (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_VARARG */ + }; + + public static int getOpMode(int m) { + return luaP_opmodes[m] & 3; + } + public static int getBMode(int m) { + return (luaP_opmodes[m] >> 4) & 3; + } + public static int getCMode(int m) { + return (luaP_opmodes[m] >> 2) & 3; + } + public static boolean testAMode(int m) { + return 0 != (luaP_opmodes[m] & (1 << 6)); + } + public static boolean testTMode(int m) { + return 0 != (luaP_opmodes[m] & (1 << 7)); + } + + + /** opcode names */ + public static final String[] luaP_opnames = { + "MOVE", + "LOADK", + "LOADBOOL", + "LOADNIL", + "GETUPVAL", + "GETGLOBAL", + "GETTABLE", + "SETGLOBAL", + "SETUPVAL", + "SETTABLE", + "NEWTABLE", + "SELF", + "ADD", + "SUB", + "MUL", + "DIV", + "MOD", + "POW", + "UNM", + "NOT", + "LEN", + "CONCAT", + "JMP", + "EQ", + "LT", + "LE", + "TEST", + "TESTSET", + "CALL", + "TAILCALL", + "RETURN", + "FORLOOP", + "FORPREP", + "TFORLOOP", + "SETLIST", + "CLOSE", + "CLOSURE", + "VARARG", + null, + }; + +} diff --git a/src/main/java/lua/VM.java b/src/main/java/lua/VM.java new file mode 100644 index 00000000..4d3ae3d0 --- /dev/null +++ b/src/main/java/lua/VM.java @@ -0,0 +1,197 @@ +package lua; + +import java.io.InputStream; + +import lua.io.Closure; +import lua.value.LNil; +import lua.value.LString; +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 ); + + /** + * 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 prepStackCall(); + + /** + * Execute bytecodes until the current call completes + * or the vm yields. + */ + public void execute(); + + /** + * 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); + + /** + * 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 + + /** + * 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 ); + + /** + * Get the index-th argument as an LString value, or "" if fewer than index arguments were supplied. + * @param index + * @return + */ + public LString getArgAsLuaString( 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); + + + /** + * Set up an error result on the stack. + * @param value the LValue to return as the first return value + * @param message the String error message to supply + */ + public void setErrorResult(LValue value, String message); + + // ====================== lua Java API ======================= + + /** + * Raises an error. The message is pushed onto the stack and used as the error message. + * It also adds at the beginning of the message the file name and the line number where + * the error occurred, if this information is available. + * + * In the java implementation this throws a RuntimeException, possibly filling + * line number information first. + */ + public void lua_error(String message); + + /** + * Run the method on the stack, propagating any error that occurs. + * @param nArgs number of arguments on the stack + * @param nResults number of results on the stack + */ + public void lua_call(int nArgs, int nResults); + + /** + * Run the method on the stack in protected mode. + * @param nArgs number of arguments on the stack + * @param nResults number of results on the stack + * @return 0 if successful, LUA_ERRMEM if no memory, LUA_ERRRUN for any other error + */ + public int lua_pcall(int nArgs, int nResults); + + + /** + * + * @param is InputStream providing the data to be loaded + * @param chunkname Name of the chunk to be used in debugging + * @return 0 if successful, LUA_ERRMEM if no memory, LUA_ERRSYNTAX for i/o or any other errors + */ + public int lua_load( InputStream is, String chunkname ); + + /** + * Get a value on the stack, relative to the top or bottom + * @param i index from bottom if 0 or greater, index from top if less than 0 + * @return + */ + public LValue lua_tolvalue(int i); + + /** + * Pop some number of items off the stack. + */ + public void lua_pop(int n); +} diff --git a/src/main/java/lua/value/LNumber.java b/src/main/java/lua/value/LNumber.java new file mode 100644 index 00000000..78427f37 --- /dev/null +++ b/src/main/java/lua/value/LNumber.java @@ -0,0 +1,27 @@ +package lua.value; + +import lua.Lua; + +abstract +public class LNumber extends LValue { + + public static final LString TYPE_NAME = new LString("number"); + + /** Compare for equivalence by using lua op comparator */ + public boolean equals(Object o) { + if ( ! ( o instanceof LValue) ) + return false; + LValue v = (LValue) o; + return this.luaBinCmpUnknown(Lua.OP_EQ, v ); + } + + public LString luaGetType() { + return TYPE_NAME; + } + + /** + * Returns false by default for non-LNumbers, but subclasses of LNumber must + * override. + */ + public abstract boolean isInteger(); +} diff --git a/src/main/java/lua/value/LValue.java b/src/main/java/lua/value/LValue.java new file mode 100644 index 00000000..d419ac59 --- /dev/null +++ b/src/main/java/lua/value/LValue.java @@ -0,0 +1,182 @@ +package lua.value; + +import lua.Lua; +import lua.VM; + +abstract +public class LValue { + + /** Metatable tag for intercepting table gets */ + public static final LString TM_INDEX = new LString("__index"); + + /** Metatable tag for intercepting table sets */ + public static final LString TM_NEWINDEX = new LString("__newindex"); + + protected static LValue luaUnsupportedOperation() { + throw new java.lang.RuntimeException( "not supported" ); + } + + public String id() { + return Integer.toHexString(hashCode()); + } + + // test if value is true + public boolean luaAsBoolean() { + return true; + } + + /** Return true if this value can be represented as an "int" */ + public boolean isInteger() { + return false; + } + + // 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) { + vm.lua_error("attempt to call "+this); + return false; + } + + // unsupported except for numbers + public LValue luaBinOpUnknown(int opcode, LValue lhs) { + return luaUnsupportedOperation(); + } + + // unsupported except for numbers + public LValue luaBinOpInteger(int opcode, int m_value) { + return luaUnsupportedOperation(); + } + + // unsupported except for numbers + public LValue luaBinOpDouble(int opcode, double m_value) { + return luaUnsupportedOperation(); + } + + // unsupported except for numbers, strings, and == with various combinations of Nil, Boolean, etc. + public boolean luaBinCmpUnknown(int opcode, LValue lhs) { + if ( opcode == Lua.OP_EQ ) + return lhs == this; + luaUnsupportedOperation(); + return false; + } + + // unsupported except for strings + public boolean luaBinCmpString(int opcode, LString rhs) { + if ( opcode == Lua.OP_EQ ) + return false; + luaUnsupportedOperation(); + return false; + } + + // unsupported except for numbers + public boolean luaBinCmpInteger(int opcode, int rhs) { + if ( opcode == Lua.OP_EQ ) + return false; + luaUnsupportedOperation(); + return false; + } + + // unsupported except for numbers + public boolean luaBinCmpDouble(int opcode, double rhs) { + if ( opcode == Lua.OP_EQ ) + return false; + luaUnsupportedOperation(); + return false; + } + + /** set a value in a table + * For non-tables, goes straight to the meta-table. + * @param vm the calling vm + * @param table the table to operate on + * @param the key to set + * @param the value to set + */ + public void luaSetTable(VM vm, LValue table, LValue key, LValue val) { + LTable mt = luaGetMetatable(); + if ( mt != null ) { + LValue event = mt.get( TM_NEWINDEX ); + if ( event != null && event != LNil.NIL ) { + event.luaSetTable( vm, table, key, val ); + return; + } + } + vm.push( LNil.NIL ); + } + + /** Get a value from a table + * @param vm the calling vm + * @param table the table from which to get the value + * @param key the key to look up + */ + public void luaGetTable(VM vm, LValue table, LValue key) { + LTable mt = luaGetMetatable(); + if ( mt != null ) { + LValue event = mt.get( TM_INDEX ); + if ( event != null && event != LNil.NIL ) { + event.luaGetTable( vm, table, key ); + return; + } + } + vm.push(LNil.NIL); + } + + /** Get the value as a String + */ + public abstract LString luaAsString(); + + /** Override standard toString with lua String conversion by default */ + public String toString() { + return luaAsString().toJavaString(); + } + + /** Return value as an integer */ + public int luaAsInt() { + luaUnsupportedOperation(); + return 0; + } + + /** Return value as a double */ + public double luaAsDouble() { + return luaAsInt(); + } + + /** Arithmetic negative */ + public LValue luaUnaryMinus() { + return luaUnsupportedOperation(); + } + + /** Built-in opcode LEN, for Strings and Tables */ + public LValue luaLength() { + // TODO: call meta-method TM_LEN here + return luaUnsupportedOperation(); + } + + /** Valid for tables + * @param isPairs true to iterate over non-integers as well */ + public LValue luaPairs(boolean isPairs) { + return luaUnsupportedOperation(); + } + + /** + * Valid for all types: get a metatable. Only tables and userdata can have a + * different metatable per instance, though, other types are restricted to + * one metatable per type. + * + * Since metatables on non-tables can only be set through Java and not Lua, + * this function should be overridden for each value type as necessary. + * + * @return null if there is no meta-table + */ + public LTable luaGetMetatable() { + return null; + } + + /** Valid for tables */ + public void luaSetMetatable(LValue metatable) { + luaUnsupportedOperation(); + } + + /** Valid for all types: return the type of this value as an LString */ + public abstract LString luaGetType(); + +} diff --git a/src/test/java/lua/debug/DebugStackStateTest.java b/src/test/java/lua/debug/DebugStackStateTest.java index ae17e5fb..c7b256ae 100644 --- a/src/test/java/lua/debug/DebugStackStateTest.java +++ b/src/test/java/lua/debug/DebugStackStateTest.java @@ -1,3 +1,24 @@ +/******************************************************************************* +* Copyright (c) 2007 LuaJ. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ package lua.debug; import java.io.IOException; diff --git a/src/test/java/lua/io/LoadStateTest.java b/src/test/java/lua/io/LoadStateTest.java new file mode 100644 index 00000000..e23e0e59 --- /dev/null +++ b/src/test/java/lua/io/LoadStateTest.java @@ -0,0 +1,92 @@ +package lua.io; + +import java.util.Random; + +import junit.framework.TestCase; +import lua.value.LDouble; +import lua.value.LInteger; +import lua.value.LNumber; + +public class LoadStateTest extends TestCase { + double[] DOUBLE_VALUES = { + 0.0, + 1.0, + 2.5, + 10.0, + 16.0, + 16.125, + -1.0, + 2.0, + -2.0, + -10.0, + -0.25, + -25, + Integer.MAX_VALUE, + Integer.MAX_VALUE - 1, + (double)Integer.MAX_VALUE + 1.0, + Integer.MIN_VALUE, + Integer.MIN_VALUE + 1, + (double)Integer.MIN_VALUE - 1.0, + Double.NEGATIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.MAX_VALUE, + Double.MAX_VALUE + }; + + public void testLongBitsToLuaNumber() { + for ( int i = 0; i < DOUBLE_VALUES.length; ++i ) { + double v = DOUBLE_VALUES[i]; + long bits = Double.doubleToLongBits( v ); + LNumber luaNumber = LoadState.longBitsToLuaNumber( bits ); + + assertEquals( v, luaNumber.luaAsDouble() ); + + if ( v != Integer.MIN_VALUE ) { + // Special case of MIN_VALUE is probably not worth dealing with. + // (Unlike zero, which is also a special case but much more common.) + assertEquals( "Value "+v+" (at index "+i+") can be represented as integer but was not", + luaNumber instanceof LInteger, v == (double)( (int) v ) ); + } + } + } + + private LNumber simpleBitsToLuaNumber( long bits ) { + double value = Double.longBitsToDouble( bits ); + int valueAsInt = (int) value; + + if ( value == (double) valueAsInt ) { + return new LInteger( valueAsInt ); + } else { + return new LDouble( value ); + } + } + + public void testLongBitsToLuaNumberSpeed() { + long[] BITS = new long[ 500000 ]; + Random r = new Random(); + + for ( int i = 0; i < DOUBLE_VALUES.length; ++i ) { + BITS[i] = Double.doubleToLongBits( DOUBLE_VALUES[i] ); + } + for ( int i = DOUBLE_VALUES.length; i < BITS.length; i += 2 ) { + BITS[i ] = r.nextLong(); + BITS[i+1] = Double.doubleToLongBits( r.nextDouble() ); + } + + long startTime = System.currentTimeMillis(); + for ( int j = 0; j < BITS.length; ++j ) { + LoadState.longBitsToLuaNumber( BITS[j] ); + } + long endTime = System.currentTimeMillis(); + long complexConversionTime = endTime - startTime; + + startTime = System.currentTimeMillis(); + for ( int j = 0; j < BITS.length; ++j ) { + simpleBitsToLuaNumber( BITS[j] ); + } + endTime = System.currentTimeMillis(); + long simpleConversionTime = endTime - startTime; + + assertTrue( complexConversionTime < simpleConversionTime ); + } +}