diff --git a/src/addon/java/lua/addon/compile/LexState.java b/src/addon/java/lua/addon/compile/LexState.java index e4a41453..7a30a654 100644 --- a/src/addon/java/lua/addon/compile/LexState.java +++ b/src/addon/java/lua/addon/compile/LexState.java @@ -2,6 +2,7 @@ package lua.addon.compile; import java.io.IOException; import java.io.Reader; +import java.util.Arrays; import java.util.Hashtable; import lua.Lua; @@ -15,7 +16,25 @@ import lua.value.LString; public class LexState extends LuaC { - private static final int EOZ = (-1); + protected static final String RESERVED_LOCAL_VAR_FOR_CONTROL = "(for control)"; + protected static final String RESERVED_LOCAL_VAR_FOR_STATE = "(for state)"; + protected static final String RESERVED_LOCAL_VAR_FOR_GENERATOR = "(for generator)"; + protected static final String RESERVED_LOCAL_VAR_FOR_STEP = "(for step)"; + protected static final String RESERVED_LOCAL_VAR_FOR_LIMIT = "(for limit)"; + protected static final String RESERVED_LOCAL_VAR_FOR_INDEX = "(for index)"; + + // sorted keywords array + // must keep it sorted, or binary search will fail + protected static final String[] RESERVED_LOCAL_VAR_KEYWORDS = new String[] { + RESERVED_LOCAL_VAR_FOR_CONTROL, + RESERVED_LOCAL_VAR_FOR_GENERATOR, + RESERVED_LOCAL_VAR_FOR_INDEX, + RESERVED_LOCAL_VAR_FOR_LIMIT, + RESERVED_LOCAL_VAR_FOR_STATE, + RESERVED_LOCAL_VAR_FOR_STEP + }; + + private static final int EOZ = (-1); private static final int MAXSRC = 80; private static final int MAX_INT = Integer.MAX_VALUE-2; private static final int UCHAR_MAX = 255; // TODO, convert to unicode CHAR_MAX? @@ -26,10 +45,13 @@ public class LexState extends LuaC { private static final int LUA_COMPAT_LSTR = 1; // 1 for compatibility, 2 for old behavior - private static final boolean LUA_COMPAT_VARARG = true; - - - + private static final boolean LUA_COMPAT_VARARG = true; + + public static boolean isReservedKeyword(String varName) { + int index = Arrays.binarySearch(RESERVED_LOCAL_VAR_KEYWORDS, varName); + return index >= 0; + } + /* ** Marks the end of a patch list. It is an invalid value both as an absolute ** address, and as a list link (would link an element to itself). @@ -1588,9 +1610,9 @@ public class LexState extends LuaC { /* fornum -> NAME = exp1,exp1[,exp1] forbody */ FuncState fs = this.fs; int base = fs.freereg; - this.new_localvarliteral("(for index)", 0); - this.new_localvarliteral("(for limit)", 1); - this.new_localvarliteral("(for step)", 2); + this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_INDEX, 0); + this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_LIMIT, 1); + this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_STEP, 2); this.new_localvar(varname, 3); this.checknext('='); this.exp1(); /* initial value */ @@ -1614,9 +1636,9 @@ public class LexState extends LuaC { int line; int base = fs.freereg; /* create control variables */ - this.new_localvarliteral("(for generator)", nvars++); - this.new_localvarliteral("(for state)", nvars++); - this.new_localvarliteral("(for control)", nvars++); + this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_GENERATOR, nvars++); + this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_STATE, nvars++); + this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_CONTROL, nvars++); /* create declared variables */ this.new_localvar(indexname, nvars++); while (this.testnext(',')) diff --git a/src/main/java/lua/Builtin.java b/src/main/java/lua/Builtin.java deleted file mode 100644 index 4a6eefde..00000000 --- a/src/main/java/lua/Builtin.java +++ /dev/null @@ -1,105 +0,0 @@ -/** - * - */ -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 deleted file mode 100644 index 35f6e1cf..00000000 --- a/src/main/java/lua/GlobalState.java +++ /dev/null @@ -1,41 +0,0 @@ -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 deleted file mode 100644 index 93eb0a91..00000000 --- a/src/main/java/lua/Lua.java +++ /dev/null @@ -1,357 +0,0 @@ -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/LuaJVM.java b/src/main/java/lua/LuaJVM.java index 0a19ddce..f9008c89 100644 --- a/src/main/java/lua/LuaJVM.java +++ b/src/main/java/lua/LuaJVM.java @@ -25,12 +25,21 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.util.StringTokenizer; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.Timer; +import java.util.TimerTask; +import lua.addon.luacompat.LuaCompat; import lua.addon.luajava.LuaJava; +import lua.debug.DebugEvent; +import lua.debug.DebugEventType; +import lua.debug.DebugRequest; import lua.debug.DebugRequestListener; -import lua.debug.DebugServer; +import lua.debug.DebugResponse; import lua.debug.DebugStackState; +import lua.debug.DebugSupport; +import lua.debug.DebugUtils; import lua.io.Closure; import lua.io.LoadState; import lua.io.Proto; @@ -54,12 +63,14 @@ import org.apache.commons.cli.ParseException; public class LuaJVM implements DebugRequestListener { protected Options options = new Options(); protected boolean isDebugMode = false; - protected DebugServer debugServer; + protected DebugSupport debugSupport; protected int requestPort; protected int eventPort; protected String script; protected String[] scriptArgs; - protected DebugStackState state; + protected StackState state; + protected boolean isReady = false; + protected boolean isTerminated = false; @SuppressWarnings("static-access") public LuaJVM() { @@ -107,15 +118,18 @@ public class LuaJVM implements DebugRequestListener { if (line.hasOption("file")) { String[] fileArgs = line.getOptionValues("file"); - this.script = fileArgs[0]; + this.script = URLDecoder.decode(fileArgs[0], "UTF-8"); + DebugUtils.println("Lua script to run: " + this.script); this.scriptArgs = new String[fileArgs.length - 1]; for (int i = 1; i < fileArgs.length; i++) { - this.scriptArgs[i-1] = fileArgs[i]; + this.scriptArgs[i-1] = URLDecoder.decode(fileArgs[i], "UTF-8"); } } } catch(NumberFormatException e) { throw new ParseException("Invalid port number: " + e.getMessage()); - } + } catch (UnsupportedEncodingException e) { + throw new ParseException("Malformed program argument strings: " + e.getMessage()); + } } protected boolean isDebug() { @@ -144,16 +158,28 @@ public class LuaJVM implements DebugRequestListener { public void run() throws IOException { if (isDebug()) { - setupDebugHooks(getRequestPort(), getEventPort()); + doDebug(); + } else { + doRun(); } - - // TODO: VM hook for debugging + } + + protected void init() { + // reset global states + GlobalState.resetGlobals(); // add LuaJava bindings LuaJava.install(); + // add LuaCompat bindings + LuaCompat.install(); + } + + public void doRun() throws IOException { + init(); + // new lua state - state = new DebugStackState(); + state = new StackState(); // convert args to lua int numOfScriptArgs = getScriptArgs().length; @@ -163,33 +189,99 @@ public class LuaJVM implements DebugRequestListener { } // load the Lua file - System.out.println("loading Lua script '" + getScript() + "'"); + DebugUtils.println("loading Lua script '" + getScript() + "'"); InputStream is = new FileInputStream(new File(getScript())); Proto p = LoadState.undump(state, is, getScript()); // create closure and execute Closure c = new Closure(state, p); - state.doCall(c, vargs); + state.doCall(c, vargs); } - protected void setupDebugHooks(int requestPort, int eventPort) - throws IOException { - this.debugServer = new DebugServer(this, requestPort, eventPort); - this.debugServer.start(); + private void doDebug() throws IOException { + DebugUtils.println("start debugging..."); + this.debugSupport = new DebugSupport(this, getRequestPort(), getEventPort()); + DebugUtils.println("created client request socket connection..."); + debugSupport.start(); + + DebugUtils.println("setting up LuaJava and debug stack state..."); + + init(); + + // new lua state + state = new DebugStackState(); + getDebugState().addDebugEventListener(debugSupport); + + // load the Lua file + DebugUtils.println("loading Lua script '" + getScript() + "'"); + InputStream is = new FileInputStream(new File(getScript())); + Proto p = LoadState.undump(state, is, getScript()); + + // create closure and execute + final Closure c = new Closure(state, p); + getDebugState().suspend(); + + new Thread(new Runnable() { + public void run() { + int numOfScriptArgs = getScriptArgs().length; + LValue[] vargs = new LValue[numOfScriptArgs]; + for (int i = 0; i < numOfScriptArgs; i++) { + vargs[i] = new LString(getScriptArgs()[i]); + } + + getDebugState().doCall(c, vargs); + stop(); + } + }).start(); + + debugSupport.fireEvent(new DebugEvent(DebugEventType.started)); + } + + private DebugStackState getDebugState() { + return (DebugStackState)state; } /* (non-Javadoc) * @see lua.debug.DebugRequestListener#handleRequest(java.lang.String) */ - public String handleRequest(String request) { - return state.handleRequest( request ); + public DebugResponse handleRequest(DebugRequest request) { + if (!isDebug()) { + throw new UnsupportedOperationException("Must be in debug mode to handle the debug requests"); + } + + DebugUtils.println("handling request: " + request.toString()); + switch (request.getType()) { + case suspend: + DebugResponse status = getDebugState().handleRequest(request); + DebugEvent event = new DebugEvent(DebugEventType.suspendedByClient); + debugSupport.fireEvent(event); + return status; + case resume: + status = getDebugState().handleRequest(request); + event = new DebugEvent(DebugEventType.resumedByClient); + debugSupport.fireEvent(event); + return status; + case exit: + stop(); + default: + return getDebugState().handleRequest(request); + } } - public void stop() { - if (this.debugServer != null) { - this.debugServer.stop(); - this.debugServer = null; + protected void stop() { + DebugUtils.println("exit LuaJ VM..."); + if (this.debugSupport != null) { + DebugEvent event = new DebugEvent(DebugEventType.terminated); + debugSupport.fireEvent(event); + Timer timer = new Timer("DebugServerDeathThread"); + timer.schedule(new TimerTask() { + public void run() { + debugSupport.stop(); + debugSupport = null; + } + }, 500); } + getDebugState().exit(); } /** @@ -198,18 +290,22 @@ public class LuaJVM implements DebugRequestListener { * [-debug requestPort eventPort] -file luaProgram args * @throws IOException */ - public static void main(String[] args) throws IOException { + public static void main(String[] args) { LuaJVM vm = new LuaJVM(); try { vm.parse(args); } catch (ParseException e) { - System.out.println(e.getMessage()); - System.out.println(); + DebugUtils.println(e.getMessage()); vm.printUsage(); return; } - vm.run(); + try { + vm.run(); + } catch (IOException e) { + //TODO: handle the error + e.printStackTrace(); + } } } diff --git a/src/main/java/lua/VM.java b/src/main/java/lua/VM.java deleted file mode 100644 index 4d3ae3d0..00000000 --- a/src/main/java/lua/VM.java +++ /dev/null @@ -1,197 +0,0 @@ -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/debug/AbortException.java b/src/main/java/lua/debug/AbortException.java new file mode 100644 index 00000000..74c0ba46 --- /dev/null +++ b/src/main/java/lua/debug/AbortException.java @@ -0,0 +1,30 @@ +/******************************************************************************* +* 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; + +public class AbortException extends RuntimeException { + private static final long serialVersionUID = 8043724992294286647L; + + public AbortException(String msg) { + super(msg); + } +} diff --git a/src/main/java/lua/debug/DebugEvent.java b/src/main/java/lua/debug/DebugEvent.java new file mode 100644 index 00000000..a5ce3898 --- /dev/null +++ b/src/main/java/lua/debug/DebugEvent.java @@ -0,0 +1,50 @@ +/******************************************************************************* +* 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.Serializable; + +public class DebugEvent implements Serializable { + + private static final long serialVersionUID = -6167781055176807311L; + protected DebugEventType type; + + public DebugEvent(DebugEventType type) { + this.type = type; + } + + public DebugEventType getType() { + return type; + } + + public void setType(DebugEventType type) { + this.type = type; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return type.toString(); + } +} diff --git a/src/main/java/lua/debug/DebugEventBreakpoint.java b/src/main/java/lua/debug/DebugEventBreakpoint.java new file mode 100644 index 00000000..4179f2c1 --- /dev/null +++ b/src/main/java/lua/debug/DebugEventBreakpoint.java @@ -0,0 +1,50 @@ +/******************************************************************************* +* 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; + +public class DebugEventBreakpoint extends DebugEvent { + private static final long serialVersionUID = -7573362646669094458L; + protected String source; + protected int lineNumber; + + public DebugEventBreakpoint(String source, int lineNumber) { + super(DebugEventType.suspendedOnBreakpoint); + this.source = source; + this.lineNumber = lineNumber; + } + + public String getSource() { + return this.source; + } + + public int getLineNumber() { + return this.lineNumber; + } + + /* (non-Javadoc) + * @see lua.debug.DebugEvent#toString() + */ + @Override + public String toString() { + return super.toString() + " source:" + getSource() + " line:" + getLineNumber(); + } +} diff --git a/src/main/java/lua/debug/DebugEventError.java b/src/main/java/lua/debug/DebugEventError.java new file mode 100644 index 00000000..ff2bb171 --- /dev/null +++ b/src/main/java/lua/debug/DebugEventError.java @@ -0,0 +1,44 @@ +/******************************************************************************* +* 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; + +public class DebugEventError extends DebugEvent { + private static final long serialVersionUID = -7911842790951966147L; + protected String detail; + + public DebugEventError(String detail) { + super(DebugEventType.error); + this.detail = detail; + } + + public String getDetail() { + return this.detail; + } + + /* (non-Javadoc) + * @see lua.debug.DebugEvent#toString() + */ + @Override + public String toString() { + return super.toString() + " detail: " + getDetail(); + } +} diff --git a/src/main/java/lua/debug/DebugEventListener.java b/src/main/java/lua/debug/DebugEventListener.java new file mode 100644 index 00000000..bacf6564 --- /dev/null +++ b/src/main/java/lua/debug/DebugEventListener.java @@ -0,0 +1,26 @@ +/******************************************************************************* +* 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; + +public interface DebugEventListener { + public void notifyDebugEvent(DebugEvent event); +} diff --git a/src/main/java/lua/debug/DebugEventStepping.java b/src/main/java/lua/debug/DebugEventStepping.java new file mode 100644 index 00000000..b8a1e9ec --- /dev/null +++ b/src/main/java/lua/debug/DebugEventStepping.java @@ -0,0 +1,30 @@ +/******************************************************************************* +* 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; + +public class DebugEventStepping extends DebugEvent { + private static final long serialVersionUID = 3902898567880012107L; + + public DebugEventStepping() { + super(DebugEventType.suspendedOnStepping); + } +} diff --git a/src/main/java/lua/debug/DebugEventType.java b/src/main/java/lua/debug/DebugEventType.java new file mode 100644 index 00000000..16e41c97 --- /dev/null +++ b/src/main/java/lua/debug/DebugEventType.java @@ -0,0 +1,36 @@ +/******************************************************************************* +* 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; + +public enum DebugEventType { + started, + suspendedByClient, + suspendedOnBreakpoint, + suspendedOnWatchpoint, + suspendedOnStepping, + suspendedOnError, + resumedByClient, + resumedOnStepping, + resumedOnError, + error, + terminated +} diff --git a/src/main/java/lua/debug/DebugRequest.java b/src/main/java/lua/debug/DebugRequest.java new file mode 100644 index 00000000..a6900b9d --- /dev/null +++ b/src/main/java/lua/debug/DebugRequest.java @@ -0,0 +1,45 @@ +/******************************************************************************* +* 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.Serializable; + +public class DebugRequest implements Serializable { + private static final long serialVersionUID = 2741129244733000595L; + protected DebugRequestType type; + + public DebugRequest(DebugRequestType type) { + this.type = type; + } + + public DebugRequestType getType() { + return this.type; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return type.toString(); + } +} diff --git a/src/main/java/lua/debug/DebugRequestLineBreakpointToggle.java b/src/main/java/lua/debug/DebugRequestLineBreakpointToggle.java new file mode 100644 index 00000000..8662fcb1 --- /dev/null +++ b/src/main/java/lua/debug/DebugRequestLineBreakpointToggle.java @@ -0,0 +1,53 @@ +/******************************************************************************* +* 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; + +public class DebugRequestLineBreakpointToggle extends DebugRequest { + private static final long serialVersionUID = -3954500569399285372L; + protected String source; + protected int lineNumber; + + public DebugRequestLineBreakpointToggle(DebugRequestType type, String source, int lineNumber) { + super(type); + if (lineNumber < 0) { + throw new IllegalArgumentException("lineNumber must be equal to greater than zero"); + } + this.source = source; + this.lineNumber = lineNumber; + } + + public int getLineNumber() { + return this.lineNumber; + } + + public String getSource() { + return this.source; + } + + /* (non-Javadoc) + * @see lua.debug.DebugRequest#toString() + */ + @Override + public String toString() { + return super.toString() + " Source:" + getSource() + " lineNumber:" + getLineNumber(); + } +} diff --git a/src/main/java/lua/debug/DebugRequestListener.java b/src/main/java/lua/debug/DebugRequestListener.java index 3f695f1e..36d1dedf 100644 --- a/src/main/java/lua/debug/DebugRequestListener.java +++ b/src/main/java/lua/debug/DebugRequestListener.java @@ -21,12 +21,6 @@ ******************************************************************************/ package lua.debug; -/** - * DebugRequestListener handles debugging requests. - * - * @author: Shu Lei - * @version: 1.0 - */ public interface DebugRequestListener { /** @@ -40,10 +34,7 @@ public interface DebugRequestListener { * old to new, include information about file, method, etc.) * stack -- return the content of the current stack frame, * listing the (variable, value) pairs - * step -- single step forward (go to next statement) - * variable N M - * -- return the value of variable M from the stack frame N - * (stack frames are indexed from 0) + * step -- single step forward (go to next statement) */ - public String handleRequest(String request); + public DebugResponse handleRequest(DebugRequest request); } diff --git a/src/main/java/lua/debug/DebugRequestStack.java b/src/main/java/lua/debug/DebugRequestStack.java new file mode 100644 index 00000000..17355e96 --- /dev/null +++ b/src/main/java/lua/debug/DebugRequestStack.java @@ -0,0 +1,44 @@ +/******************************************************************************* +* 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; + +public class DebugRequestStack extends DebugRequest { + private static final long serialVersionUID = 6270383432060791307L; + protected int index; + + public DebugRequestStack(int index) { + super(DebugRequestType.stack); + this.index = index; + } + + public int getIndex() { + return this.index; + } + + /* (non-Javadoc) + * @see lua.debug.DebugRequest#toString() + */ + @Override + public String toString() { + return super.toString() + " stack frame:" + getIndex(); + } +} diff --git a/src/main/java/lua/debug/DebugRequestType.java b/src/main/java/lua/debug/DebugRequestType.java new file mode 100644 index 00000000..fd0136cf --- /dev/null +++ b/src/main/java/lua/debug/DebugRequestType.java @@ -0,0 +1,35 @@ +/******************************************************************************* +* 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; + +public enum DebugRequestType { + suspend, + resume, + exit, + lineBreakpointSet, + lineBreakpointClear, + watchpointSet, + watchpointClear, + callgraph, + stack, + step +} diff --git a/src/main/java/lua/debug/DebugRequestWatchpointToggle.java b/src/main/java/lua/debug/DebugRequestWatchpointToggle.java new file mode 100644 index 00000000..d43f1913 --- /dev/null +++ b/src/main/java/lua/debug/DebugRequestWatchpointToggle.java @@ -0,0 +1,60 @@ +/******************************************************************************* +* 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; + +public class DebugRequestWatchpointToggle extends DebugRequest { + private static final long serialVersionUID = -2978341358052851046L; + + public enum AccessType { + Ignore, + Read, + Modify, + ReadAndModify + }; + + protected String functionName; + protected String variableName; + + public DebugRequestWatchpointToggle(String functionName, + String variableName, + AccessType accessType) { + super(accessType == AccessType.Ignore ? DebugRequestType.watchpointClear : DebugRequestType.watchpointSet); + this.functionName = functionName; + this.variableName = variableName; + } + + public String getFunctionName() { + return this.functionName; + } + + public String getVariableName() { + return this.variableName; + } + + /* (non-Javadoc) + * @see lua.debug.DebugRequest#toString() + */ + @Override + public String toString() { + return super.toString() + " functionName:" + getFunctionName() + " variableName:" + getVariableName(); + } +} diff --git a/src/main/java/lua/debug/DebugResponse.java b/src/main/java/lua/debug/DebugResponse.java new file mode 100644 index 00000000..753d490e --- /dev/null +++ b/src/main/java/lua/debug/DebugResponse.java @@ -0,0 +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; + +public interface DebugResponse {} diff --git a/src/main/java/lua/debug/DebugResponseCallgraph.java b/src/main/java/lua/debug/DebugResponseCallgraph.java new file mode 100644 index 00000000..ae2aabc8 --- /dev/null +++ b/src/main/java/lua/debug/DebugResponseCallgraph.java @@ -0,0 +1,51 @@ +/******************************************************************************* +* 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; + +public class DebugResponseCallgraph extends DebugResponseSimple { + private static final long serialVersionUID = -7761865402188853413L; + protected StackFrame[] stackFrames; + + public DebugResponseCallgraph(StackFrame[] callgraph) { + super(true); + this.stackFrames = callgraph; + } + + public StackFrame[] getCallgraph() { + return this.stackFrames; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(); + if (this.stackFrames != null) { + for (StackFrame frame : stackFrames) { + buffer.append(frame.toString()); + buffer.append("\n"); + } + } + return buffer.toString(); + } +} diff --git a/src/main/java/lua/debug/DebugResponseSimple.java b/src/main/java/lua/debug/DebugResponseSimple.java new file mode 100644 index 00000000..9da9f07f --- /dev/null +++ b/src/main/java/lua/debug/DebugResponseSimple.java @@ -0,0 +1,49 @@ +/******************************************************************************* +* 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.Serializable; + +public class DebugResponseSimple implements DebugResponse, Serializable { + private static final long serialVersionUID = 7042417813840650230L; + + protected boolean isSuccessful; + + public static final DebugResponseSimple SUCCESS = new DebugResponseSimple(true); + public static final DebugResponseSimple FAILURE = new DebugResponseSimple(false); + + public DebugResponseSimple(boolean isSuccessful) { + this.isSuccessful = isSuccessful; + } + + public boolean isSuccessful() { + return this.isSuccessful; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return String.valueOf(isSuccessful); + } +} diff --git a/src/main/java/lua/debug/DebugResponseStack.java b/src/main/java/lua/debug/DebugResponseStack.java new file mode 100644 index 00000000..4c5127be --- /dev/null +++ b/src/main/java/lua/debug/DebugResponseStack.java @@ -0,0 +1,48 @@ +/******************************************************************************* +* 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; + +public class DebugResponseStack extends DebugResponseSimple { + private static final long serialVersionUID = -2108425321162834731L; + protected Variable[] variables; + + public DebugResponseStack(Variable[] variables) { + super(true); + this.variables = variables; + } + + public Variable[] getVariables() { + return this.variables; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(); + for (int i = 0; variables != null && i < variables.length; i++) { + buffer.append("\t" + variables[i].getName() + ":" + variables[i].getIndex() + "\n"); + } + return buffer.toString(); + } +} diff --git a/src/main/java/lua/debug/DebugStackState.java b/src/main/java/lua/debug/DebugStackState.java index e19c7169..286d5594 100644 --- a/src/main/java/lua/debug/DebugStackState.java +++ b/src/main/java/lua/debug/DebugStackState.java @@ -1,25 +1,75 @@ +/******************************************************************************* +* 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.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; -import java.util.StringTokenizer; +import java.util.Set; import lua.CallInfo; import lua.StackState; +import lua.addon.compile.LexState; import lua.io.LocVars; import lua.io.Proto; +import lua.value.LTable; +import lua.value.LValue; +import lua.value.Type; public class DebugStackState extends StackState implements DebugRequestListener { - public Map breakpoints = new HashMap(); - private boolean exiting = false; - private boolean suspended = false; - private boolean stepping = false; - private int lastline = -1; + private static final boolean DEBUG = false; + + protected Map breakpoints = new HashMap(); + protected boolean exiting = false; + protected boolean suspended = false; + protected boolean stepping = false; + protected int lastline = -1; + protected List debugEventListeners + = new ArrayList(); public DebugStackState() { } + public void addDebugEventListener(DebugEventListener listener) { + if (!debugEventListeners.contains(listener)) { + debugEventListeners.add(listener); + } + } + + public void removeDebugEventListener(DebugEventListener listener) { + if (debugEventListeners.contains(listener)) { + debugEventListeners.remove(listener); + } + } + + protected void notifyDebugEventListeners(DebugEvent event) { + for (DebugEventListener listener : debugEventListeners) { + listener.notifyDebugEvent(event); + } + } + private String getFileLine(int cindex) { String func = "?"; String line = "?"; @@ -29,119 +79,136 @@ public class DebugStackState extends StackState implements DebugRequestListener Proto p = call.closure.p; if ( p != null && p.source != null ) source = p.source.toJavaString(); - if ( p.lineinfo != null && p.lineinfo.length > call.pc-1 ) - line = String.valueOf( p.lineinfo[call.pc-1] ); + if ( p.lineinfo != null && p.lineinfo.length > call.pc ) + line = String.valueOf( p.lineinfo[call.pc] ); // TODO: reverse lookup on function name ???? func = call.closure.luaAsString().toJavaString(); } return source+":"+line+"("+func+")"; } - private String addLineInfo( String message ) { - return getFileLine(cc)+": "+message; - } - private void printLuaTrace(String message) { - System.out.println( "Lua error: "+addLineInfo( message ) ); - for ( int cindex=cc-1; cindex>=0; cindex-- ) - System.out.println( "\tcalled by "+getFileLine( cindex ) ); - } - - protected void debugPcallError(Throwable t) { - System.out.println(addLineInfo("(caught in pcall) "+t.getMessage())); - System.out.flush(); - } - + // override and fill in line number info public void lua_error(String message) { - throw new RuntimeException( message ); + super.lua_error( getFileLine(cc)+": "+message ); + } + + private void printLuaTrace() { + System.out.println( "Lua location: "+getFileLine(cc) ); + for ( int cindex=cc-1; cindex>=0; cindex-- ) + System.out.println( "\tin "+getFileLine( cindex ) ); } // intercept exceptions and fill in line numbers public void exec() { try { super.exec(); - } catch ( RuntimeException t ) { - t.printStackTrace(); - printLuaTrace(t.getMessage()); + } catch (AbortException e) { + // ignored. Client aborts the debugging session. + } catch ( Exception t ) { + t.printStackTrace(); + printLuaTrace(); System.out.flush(); - throw t; } } // debug hooks public void debugHooks( int pc ) { + DebugUtils.println("entered debugHook..."); + if ( exiting ) - throw new java.lang.RuntimeException("exiting"); + throw new AbortException("exiting"); - // make sure line numbers are current in any stack traces - calls[cc].pc = pc; - synchronized ( this ) { - + // anytime the line doesn't change we keep going - int[] li = calls[cc].closure.p.lineinfo; - int line = (li!=null && li.length>pc? li[pc]: -1); - if ( lastline == line ) + int line = getLineNumber(calls[cc]); + DebugUtils.println("debugHook - executing line: " + line); + if ( !stepping && lastline == line ) { return; + } // save line in case next op is a step lastline = line; - if ( stepping ) - stepping = false; - - // check for a break point if we aren't suspended already - if ( ! suspended ) { - if ( breakpoints.containsKey(line) ) - suspended = true; - else - return; + + if ( stepping ) { + DebugUtils.println("suspended by stepping at pc=" + pc); + notifyDebugEventListeners(new DebugEventStepping()); + suspended = true; + } else if ( !suspended ) { + // check for a break point if we aren't suspended already + Proto p = calls[cc].closure.p; + String source = DebugUtils.getSourceFileName(p.source); + if ( breakpoints.containsKey(constructBreakpointKey(source, line))){ + DebugUtils.println("hitting breakpoint " + constructBreakpointKey(source, line)); + notifyDebugEventListeners( + new DebugEventBreakpoint(source, line)); + suspended = true; + } else { + return; + } } // wait for a state change - while ( suspended && (!exiting) && (!stepping) ) { + while (suspended && !exiting ) { try { this.wait(); + DebugUtils.println("resuming execution..."); } catch ( InterruptedException ie ) { ie.printStackTrace(); } } } } - - // ------------------ commands coming from the debugger ------------------- - - public enum RequestType { - suspend, - resume, - exit, - set, - clear, - callgraph, - stack, - step, - variable, + + /** + * Get the current line number + * @param pc program counter + * @return the line number corresponding to the pc + */ + private int getLineNumber(CallInfo ci) { + int[] lineNumbers = ci.closure.p.lineinfo; + int pc = ci.pc; + int line = (lineNumbers != null && lineNumbers.length > pc ? lineNumbers[pc] : -1); + return line; } + + // ------------------ commands coming from the debugger ------------------- - public String handleRequest(String request) { - StringTokenizer st = new StringTokenizer( request ); - String req = st.nextToken(); - RequestType rt = RequestType.valueOf(req); - switch ( rt ) { - case suspend: suspend(); return "true"; - case resume: resume(); return "true"; - case exit: exit(); return "true"; - case set: set( Integer.parseInt(st.nextToken()) ); return "true"; - case clear: clear( Integer.parseInt(st.nextToken()) ); return "true"; - case callgraph: return callgraph(); - case stack: return stack(); - case step: step(); return "true"; - case variable: - String N = st.nextToken(); - String M = st.nextToken(); - return variable( Integer.parseInt(N), Integer.parseInt(M) ); - } - throw new java.lang.IllegalArgumentException( "unkown request type: "+req ); + public DebugResponse handleRequest(DebugRequest request) { + DebugUtils.println("DebugStackState is handling request: " + request.toString()); + switch (request.getType()) { + case suspend: + suspend(); + return DebugResponseSimple.SUCCESS; + case resume: + resume(); + return DebugResponseSimple.SUCCESS; + case exit: + exit(); + return DebugResponseSimple.SUCCESS; + case lineBreakpointSet: + DebugRequestLineBreakpointToggle setBreakpointRequest + = (DebugRequestLineBreakpointToggle)request; + setBreakpoint(setBreakpointRequest.getSource(), setBreakpointRequest.getLineNumber()); + return DebugResponseSimple.SUCCESS; + case lineBreakpointClear: + DebugRequestLineBreakpointToggle clearBreakpointRequest + = (DebugRequestLineBreakpointToggle)request; + clearBreakpoint(clearBreakpointRequest.getSource(), clearBreakpointRequest.getLineNumber()); + return DebugResponseSimple.SUCCESS; + case callgraph: + return new DebugResponseCallgraph(getCallgraph()); + case stack: + DebugRequestStack stackRequest = (DebugRequestStack) request; + int index = stackRequest.getIndex(); + return new DebugResponseStack(getStack(index)); + case step: + step(); + return DebugResponseSimple.SUCCESS; + } + throw new java.lang.IllegalArgumentException( "unkown request type: "+request.getType() ); } /** @@ -155,13 +222,14 @@ public class DebugStackState extends StackState implements DebugRequestListener this.notify(); } } - + /** * resume the execution */ public void resume() { synchronized ( this ) { suspended = false; + stepping = false; this.notify(); } } @@ -180,18 +248,24 @@ public class DebugStackState extends StackState implements DebugRequestListener * set breakpoint at line N * @param N the line to set the breakpoint at */ - public void set( int N ) { + public void setBreakpoint(String source, int lineNumber) { + DebugUtils.println("adding breakpoint " + constructBreakpointKey(source, lineNumber)); synchronized ( this ) { - breakpoints.put( N, Boolean.TRUE ); + breakpoints.put(constructBreakpointKey(source, lineNumber), Boolean.TRUE ); } } + protected String constructBreakpointKey(String source, int lineNumber) { + return source + ":" + lineNumber; + } + /** - * clear breakpoint at line N + * clear breakpoint at line lineNumber of source source */ - public void clear( int N ) { + public void clearBreakpoint(String source, int lineNumber) { + DebugUtils.println("removing breakpoint " + constructBreakpointKey(source, lineNumber)); synchronized ( this ) { - breakpoints.remove( N ); + breakpoints.remove(constructBreakpointKey(source, lineNumber)); } } @@ -199,36 +273,71 @@ public class DebugStackState extends StackState implements DebugRequestListener * return the current call graph (i.e. stack frames from * old to new, include information about file, method, etc.) */ - public String callgraph() { + public StackFrame[] getCallgraph() { int n = cc; + if ( n < 0 || n >= calls.length ) - return ""; - StringBuffer sb = new StringBuffer(); - for ( int i=0; i<=n; i++ ) { + return new StackFrame[0]; + + StackFrame[] frames = new StackFrame[n+1]; + for ( int i = 0; i <= n; i++ ) { CallInfo ci = calls[i]; - // TODO: fill this out with proper format, names, etc. - sb.append( String.valueOf(ci.closure.p) ); - sb.append( "\n" ); + frames[i] = new StackFrame(ci, getLineNumber(ci)); } - return sb.toString(); + return frames; } - /** - * return the content of the current stack frame, - * listing the (variable, value) pairs - */ - public String stack() { - CallInfo ci; - if ( cc < 0 || cc >= calls.length || (ci=calls[cc]) == null ) - return ""; - LocVars[] lv = ci.closure.p.locvars; - int n = (lv != null? lv.length: 0); - StringBuffer sb = new StringBuffer(); - for ( int i=0; i= calls.length) { + //TODO: this is an error, handle it differently + return new Variable[0]; + } + + CallInfo callInfo = calls[index]; + DebugUtils.println("Stack Frame: " + index + "[" + callInfo.base + "," + callInfo.top + "]"); + int top = callInfo.top < callInfo.base ? callInfo.base : callInfo.top; + Proto prototype = callInfo.closure.p; + LocVars[] localVariables = prototype.locvars; + List variables = new ArrayList(); + int localVariableCount = 0; + Set variablesSeen = new HashSet(); + for (int i = 0; localVariables != null && i < localVariables.length && i <= top; i++) { + String varName = localVariables[i].varname.toString(); + DebugUtils.print("\tVariable: " + varName); + DebugUtils.print("\tValue: " + stack[callInfo.base + i]); + if (!variablesSeen.contains(varName) && + !LexState.isReservedKeyword(varName)) { + variablesSeen.add(varName); + LValue value = stack[callInfo.base + i]; + if (value != null) { + Type type = Type.valueOf(value.luaGetType().toJavaString()); + DebugUtils.print("\tType: " + type); + if (type == Type.table) { + DebugUtils.println(" (selected)"); + variables.add( + new TableVariable(localVariableCount++, + varName, + type, + (LTable) value)); + } else if (type != Type.function && + type != Type.thread) { + DebugUtils.println(" (selected)"); + variables.add( + new Variable(localVariableCount++, + varName, + type, + value.toString())); + } else { + DebugUtils.println(""); + } + } else { + DebugUtils.println(""); + } + } else { + DebugUtils.println(""); + } + } + return (Variable[])variables.toArray(new Variable[0]); } @@ -237,19 +346,9 @@ public class DebugStackState extends StackState implements DebugRequestListener */ public void step() { synchronized ( this ) { + suspended = false; stepping = true; this.notify(); } } - - /** - * return the value of variable M from the stack frame N - * (stack frames are indexed from 0) - */ - public String variable( int N, int M ) { - CallInfo ci; - if ( M < 0 || M >= calls.length || (ci=calls[M]) == null ) - return ""; - return String.valueOf( super.stack[ci.base] ); - } } diff --git a/src/main/java/lua/debug/DebugServer.java b/src/main/java/lua/debug/DebugSupport.java similarity index 67% rename from src/main/java/lua/debug/DebugServer.java rename to src/main/java/lua/debug/DebugSupport.java index b22c9914..5c115e16 100644 --- a/src/main/java/lua/debug/DebugServer.java +++ b/src/main/java/lua/debug/DebugSupport.java @@ -21,21 +21,14 @@ ******************************************************************************/ package lua.debug; -import java.io.BufferedReader; +import java.io.EOFException; import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintWriter; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.net.ServerSocket; import java.net.Socket; -/** - * DebugServer manages the communications between LuaJ VM and - * the debugging client. - * - * @author: Shu Lei - * @version: - */ -public class DebugServer { +public class DebugSupport implements DebugEventListener { public enum State { UNKNOWN, RUNNING, @@ -50,14 +43,14 @@ public class DebugServer { protected ServerSocket requestSocket; protected Socket clientRequestSocket; - protected BufferedReader requestReader; - protected PrintWriter requestWriter; + protected ObjectInputStream requestReader; + protected ObjectOutputStream requestWriter; protected ServerSocket eventSocket; protected Socket clientEventSocket; - protected PrintWriter eventWriter; + protected ObjectOutputStream eventWriter; - public DebugServer(DebugRequestListener listener, + public DebugSupport(DebugRequestListener listener, int requestPort, int eventPort) { this.listener = listener; @@ -65,7 +58,8 @@ public class DebugServer { this.eventPort = eventPort; } - protected void destroy() { + protected void releaseServer() { + DebugUtils.println("shutting down the debug server..."); if (requestReader != null) { try { requestReader.close(); @@ -73,7 +67,11 @@ public class DebugServer { } if (requestWriter != null) { - requestWriter.close(); + try { + requestWriter.close(); + } catch (IOException e) { + e.printStackTrace(); + } } if (clientRequestSocket != null) { @@ -89,7 +87,11 @@ public class DebugServer { } if (eventWriter != null) { - eventWriter.close(); + try { + eventWriter.close(); + } catch (IOException e) { + e.printStackTrace(); + } } if (clientEventSocket != null) { @@ -108,20 +110,22 @@ public class DebugServer { public synchronized void start() throws IOException { this.requestSocket = new ServerSocket(requestPort); this.clientRequestSocket = requestSocket.accept(); - this.requestReader = new BufferedReader( - new InputStreamReader(clientRequestSocket.getInputStream())); - this.requestWriter = new PrintWriter(clientRequestSocket.getOutputStream()); + this.requestReader + = new ObjectInputStream(clientRequestSocket.getInputStream()); + this.requestWriter + = new ObjectOutputStream(clientRequestSocket.getOutputStream()); this.eventSocket = new ServerSocket(eventPort); this.clientEventSocket = eventSocket.accept(); - this.eventWriter = new PrintWriter(clientEventSocket.getOutputStream()); + this.eventWriter + = new ObjectOutputStream(clientEventSocket.getOutputStream()); this.requestWatcherThread = new Thread(new Runnable() { public void run() { if (getState() != State.STOPPED) { handleRequest(); } else { - destroy(); + releaseServer(); } } }); @@ -139,25 +143,39 @@ public class DebugServer { public void handleRequest() { synchronized (clientRequestSocket) { - String request = null; try { - while (getState() != State.STOPPED && - (request = requestReader.readLine()) != null) { - System.out.println("SERVER receives request: " + request); - String response = listener.handleRequest(request); - requestWriter.write(response); + while (getState() != State.STOPPED) { + DebugRequest request + = (DebugRequest) requestReader.readObject(); + DebugUtils.println("SERVER receives request: " + request.toString()); + DebugResponse response = listener.handleRequest(request); + requestWriter.writeObject(response); requestWriter.flush(); + DebugUtils.println("SERVER sends response: " + response); } if (getState() == State.STOPPED) { - destroy(); + cleanup(); } + } catch (EOFException e) { + cleanup(); } catch (IOException e) { e.printStackTrace(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); } } } + /** + * + */ + private void cleanup() { + DebugUtils.println("SERVER terminated..."); + releaseServer(); + System.exit(0); + } + /** * This method provides the second communication channel with the debugging * client. The server can send events via this channel to notify the client @@ -175,10 +193,22 @@ public class DebugServer { * * @param event */ - public void fireEvent(String event) { + public void fireEvent(DebugEvent event) { + DebugUtils.println("SERVER sending event: " + event.toString()); synchronized (eventSocket) { - eventWriter.println(event); - eventWriter.flush(); + try { + eventWriter.writeObject(event); + eventWriter.flush(); + } catch (IOException e) { + e.printStackTrace(); + } } + } + + /* (non-Javadoc) + * @see lua.debug.DebugEventListener#notifyDebugEvent(lua.debug.DebugEvent) + */ + public void notifyDebugEvent(DebugEvent event) { + fireEvent(event); } } diff --git a/src/main/java/lua/debug/DebugUtils.java b/src/main/java/lua/debug/DebugUtils.java new file mode 100644 index 00000000..61d45219 --- /dev/null +++ b/src/main/java/lua/debug/DebugUtils.java @@ -0,0 +1,54 @@ +/******************************************************************************* +* 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.File; + +import lua.io.LoadState; +import lua.value.LString; + +public class DebugUtils { + public static final boolean IS_DEBUG = false; + + public static void println(String message) { + if (IS_DEBUG) { + System.out.println(message); + } + } + + public static void print(String message) { + if (IS_DEBUG) { + System.out.print(message); + } + } + + public static String getSourceFileName(LString source) { + String sourceStr = source.toJavaString(); + sourceStr = LoadState.getSourceName(sourceStr); + if (!LoadState.SOURCE_BINARY_STRING.equals(sourceStr)) { + File sourceFile = new File(sourceStr); + return sourceFile.getName(); + } else { + return sourceStr; + } + } +} diff --git a/src/main/java/lua/debug/StackFrame.java b/src/main/java/lua/debug/StackFrame.java new file mode 100644 index 00000000..694e726d --- /dev/null +++ b/src/main/java/lua/debug/StackFrame.java @@ -0,0 +1,77 @@ +/******************************************************************************* +* 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. +******************************************************************************/ +/******************************************************************************* +* 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.Serializable; + +import lua.CallInfo; +import lua.io.Proto; + +public class StackFrame implements Serializable { + private static final long serialVersionUID = 2102834561519501432L; + protected int lineNumber; + protected String source; + + public StackFrame(CallInfo callInfo, int lineNumber) { + Proto prototype = callInfo.closure.p; + + this.lineNumber = lineNumber; + this.source = DebugUtils.getSourceFileName(prototype.source); + } + + public int getLineNumber() { + return this.lineNumber; + } + + public String getSource() { + return this.source; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return getSource() + ":" + getLineNumber(); + } +} diff --git a/src/main/java/lua/debug/TableVariable.java b/src/main/java/lua/debug/TableVariable.java new file mode 100644 index 00000000..fa4cc116 --- /dev/null +++ b/src/main/java/lua/debug/TableVariable.java @@ -0,0 +1,61 @@ +/******************************************************************************* +* 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 lua.value.LTable; +import lua.value.LValue; +import lua.value.Type; + +public class TableVariable extends Variable { + + private static final long serialVersionUID = 1194778378382802700L; + protected String[] keys; + protected Object[] values; + + public TableVariable(int index, String name, Type type, LTable table) { + super(index, name, type, null); + + int size = table.size(); + DebugUtils.println("table size:" + size); + this.keys = new String[size]; + this.values = new Object[size]; + LValue[] keyValues = table.getKeys(); + for (int i = 0; i < size; i++) { + this.keys[i] = keyValues[i].toString(); + LValue value = table.get(keyValues[i]); + if (value instanceof LTable) { + this.values[i] = new TableVariable(i, "", Type.table, (LTable)value); + } else { + this.values[i] = value.toString(); + } + DebugUtils.println("["+ keys[i] + "," + values[i].toString() + "]"); + } + } + + public String[] getKeys() { + return this.keys; + } + + public Object[] getValues() { + return this.values; + } +} diff --git a/src/main/java/lua/debug/Variable.java b/src/main/java/lua/debug/Variable.java new file mode 100644 index 00000000..e361f9cd --- /dev/null +++ b/src/main/java/lua/debug/Variable.java @@ -0,0 +1,66 @@ +/******************************************************************************* +* 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.Serializable; + +import lua.value.Type; + +public class Variable implements Serializable { + private static final long serialVersionUID = 8194091816623233934L; + + protected int index; + protected String name; + protected String value; + protected Type type; + + public Variable(int index, String name, Type type, String value) { + this.index = index; + this.name = name; + this.type = type; + this.value = value; + } + + public String getName() { + return this.name; + } + + public Type getType() { + return this.type; + } + + public String getValue() { + return this.value; + } + + public int getIndex() { + return this.index; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "index: " + getIndex() + " name:" + getName() + " type: " + getType() + " value:" + getValue(); + } +} diff --git a/src/main/java/lua/io/LoadState.java b/src/main/java/lua/io/LoadState.java index a19dacea..615d0cb4 100644 --- a/src/main/java/lua/io/LoadState.java +++ b/src/main/java/lua/io/LoadState.java @@ -3,7 +3,6 @@ package lua.io; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import lua.VM; import lua.value.LBoolean; @@ -19,7 +18,10 @@ import lua.value.LValue; */ public class LoadState { - /** mark for precompiled code (`Lua') */ + public static final String SOURCE_BINARY_STRING = "binary string"; + + + /** mark for precompiled code (`Lua') */ public static final String LUA_SIGNATURE = "\033Lua"; @@ -251,17 +253,22 @@ public class LoadState { } // load file - String sname = name; - if ( name.startsWith("@") || name.startsWith("=") ) - sname = name.substring(1); - else if ( name.startsWith("\033") ) - sname = "binary string"; + String sname = getSourceName(name); LoadState s = new LoadState( L, stream, sname ); s.loadHeader(); LString literal = new LString("=?"); return s.loadFunction( literal ); } + public static String getSourceName(String name) { + String sname = name; + if ( name.startsWith("@") || name.startsWith("=") ) + sname = name.substring(1); + else if ( name.startsWith("\033") ) + sname = SOURCE_BINARY_STRING; + return sname; + } + /** Private constructor for create a load state */ private LoadState( VM L, InputStream stream, String name ) { this.L = L; diff --git a/src/main/java/lua/value/LBoolean.java b/src/main/java/lua/value/LBoolean.java deleted file mode 100644 index bf4ef7b6..00000000 --- a/src/main/java/lua/value/LBoolean.java +++ /dev/null @@ -1,38 +0,0 @@ -package lua.value; - -public final class LBoolean extends LValue { - - public static final LBoolean TRUE = new LBoolean("true",true); - - public static final LBoolean FALSE = new LBoolean("false",false); - - public static final LString TYPE_NAME = new LString("boolean"); - - private final LString m_name; - private final boolean m_value; - - private LBoolean( String name, boolean value ) { - this.m_name = new LString( name ); - this.m_value = value; - } - - public final LString luaAsString() { - return m_name; - } - - public final boolean luaAsBoolean() { - return m_value; - } - - public final int luaAsInt() { - return m_value? 1: 0; - } - - public final static LBoolean valueOf(boolean value) { - return value? TRUE: FALSE; - } - - public LString luaGetType() { - return TYPE_NAME; - } -} diff --git a/src/main/java/lua/value/LDouble.java b/src/main/java/lua/value/LDouble.java deleted file mode 100644 index 2613f8f7..00000000 --- a/src/main/java/lua/value/LDouble.java +++ /dev/null @@ -1,119 +0,0 @@ -package lua.value; - -import lua.Lua; - -public class LDouble extends LNumber { - - private final double m_value; - - public LDouble(double value) { - this.m_value = value; - } - - public int hashCode() { - return (int) m_value; - } - - public LString luaAsString() { - long l = (long) m_value; - if ( m_value == (double) l ) { - // TODO: is this a good idea? - return new LString( Long.toString( l ) ); - } else { - return LString.valueOf( m_value ); - } - } - - public boolean isInteger() { - // Cast to int and then back to double and see if the value - // survives the round trip. - return ( (double) ( (int) m_value ) ) == m_value; - } - - // binary operations on integers, first dispatch - public LValue luaBinOpUnknown(int opcode, LValue lhs) { - return lhs.luaBinOpDouble( opcode, this.m_value ); - } - - // binary operations on mixtures of doubles and integers - public LValue luaBinOpInteger(int opcode, int rhs) { - return luaBinOpDoubleDouble( opcode, m_value, (double) rhs ); - } - - // binary operations on doubles - public LValue luaBinOpDouble(int opcode, double rhs) { - return luaBinOpDoubleDouble( opcode, m_value, rhs ); - } - - public static LValue luaBinOpDoubleDouble( int opcode, double lhs, double rhs ) { - switch ( opcode ) { - case Lua.OP_ADD: return new LDouble( lhs + rhs ); - case Lua.OP_SUB: return new LDouble( lhs - rhs ); - case Lua.OP_MUL: return new LDouble( lhs * rhs ); - case Lua.OP_DIV: return new LDouble( lhs / rhs ); - case Lua.OP_MOD: return new LDouble( lhs - Math.floor(lhs/rhs) * rhs ); - // case Lua.OP_POW: return new LDouble( dpow(lhs, rhs) ); - } - return luaUnsupportedOperation(); - } - - /* - public static double dpow(double a, double b) { - if ( b < 0 ) - return 1 / dpow( a, -b ); - int p = 1; - int whole = (int) b; - for ( double v=a; whole > 0; whole>>=1, v=v*v ) - if ( (whole & 1) != 0 ) - p *= v; - int frac = (int) (0x10000 * b); - for ( ; (frac&0xffff)!=0; frac<<=1 ) { - a = Math.sqrt(a); - if ( (frac & 0x8000) != 0 ) - p *= a; - } - return p; - } - */ - - - public int luaAsInt() { - return (int) m_value; - } - - public double luaAsDouble() { - return m_value; - } - - // binary compares on integers, first dispatch - public boolean luaBinCmpUnknown(int opcode, LValue lhs) { - return lhs.luaBinCmpDouble( opcode, this.m_value ); - } - - // binary compares on mixtures of doubles and integers - public boolean luaBinCmpInteger(int opcode, int rhs) { - return luaBinCmpDoubleDouble( opcode, m_value, (double) rhs ); - } - - // binary compares on doubles - public boolean luaBinCmpDouble(int opcode, double rhs) { - return luaBinCmpDoubleDouble( opcode, m_value, rhs ); - } - - // compare two doubles - public static boolean luaBinCmpDoubleDouble( int opcode, double lhs, double rhs ) { - switch ( opcode ) { - case Lua.OP_EQ: return lhs == rhs; - case Lua.OP_LT: return lhs < rhs; - case Lua.OP_LE: return lhs <= rhs; - } - luaUnsupportedOperation(); - return false; - } - - /** Arithmetic negative */ - public LValue luaUnaryMinus() { - return new LDouble( -m_value ); - } - -} diff --git a/src/main/java/lua/value/LFunction.java b/src/main/java/lua/value/LFunction.java deleted file mode 100644 index 288e076c..00000000 --- a/src/main/java/lua/value/LFunction.java +++ /dev/null @@ -1,33 +0,0 @@ -package lua.value; - -import lua.VM; - - -public class LFunction extends LValue { - - public static final LString TYPE_NAME = new LString("function"); - - public LString luaAsString() { - return new LString( "function: "+hashCode() ); - } - - public void luaSetTable(VM vm, LValue table, LValue key, LValue val) { - vm.push( this ); - vm.push( table ); - vm.push( key ); - vm.push( val ); - vm.lua_call( 3, 0 ); - } - - public void luaGetTable(VM vm, LValue table, LValue key) { - vm.push( this ); - vm.push( table ); - vm.push( key ); - vm.lua_call( 2, 1 ); - } - - public LString luaGetType() { - return TYPE_NAME; - } - -} diff --git a/src/main/java/lua/value/LInteger.java b/src/main/java/lua/value/LInteger.java deleted file mode 100644 index eb866e9d..00000000 --- a/src/main/java/lua/value/LInteger.java +++ /dev/null @@ -1,91 +0,0 @@ -package lua.value; - -import lua.Lua; - -public class LInteger extends LNumber { - - private final int m_value; - - public LInteger(int value) { - this.m_value = value; - } - - public final int hashCode() { - return hashCodeOf( m_value ); - } - - public static int hashCodeOf( int v ) { - return v; - } - - public int luaAsInt() { - return m_value; - } - - public LString luaAsString() { - return LString.valueOf(m_value); - } - - public boolean isInteger() { - return true; - } - - // binary operations on integers, first dispatch - public LValue luaBinOpUnknown(int opcode, LValue lhs) { - return lhs.luaBinOpInteger( opcode, this.m_value ); - } - - // binary operations on integers - public LValue luaBinOpInteger(int opcode, int rhs) { - switch ( opcode ) { - case Lua.OP_ADD: return new LInteger( m_value + rhs ); - case Lua.OP_SUB: return new LInteger( m_value - rhs ); - case Lua.OP_MUL: return new LInteger( m_value * rhs ); - case Lua.OP_DIV: return new LInteger( m_value / rhs ); - case Lua.OP_MOD: return new LInteger( m_value - ((int) Math.floor(m_value/(double)rhs)) * rhs ); - case Lua.OP_POW: return new LInteger( ipow(m_value, rhs) ); - } - return luaUnsupportedOperation(); - } - - private static int ipow(int v, int rhs) { - int p = 1; - for ( ; rhs > 0; rhs>>=1, v=v*v ) - if ( (rhs & 1) != 0 ) - p *= v; - return p; - } - - // binary operations on mixed integer, double - public LValue luaBinOpDouble(int opcode, double rhs) { - return LDouble.luaBinOpDoubleDouble(opcode, (double) m_value, rhs ); - } - - // binary compare for integers, first dispatch - public boolean luaBinCmpUnknown(int opcode, LValue lhs) { - return lhs.luaBinCmpInteger( opcode, this.m_value ); - } - - // unsupported except for numbers - public boolean luaBinCmpInteger(int opcode, int rhs) { - switch ( opcode ) { - case Lua.OP_EQ: return m_value == rhs; - case Lua.OP_LT: return m_value < rhs; - case Lua.OP_LE: return m_value <= rhs; - } - luaUnsupportedOperation(); - return false; - } - - // unsupported except for numbers - public boolean luaBinCmpDouble(int opcode, double rhs) { - return LDouble.luaBinCmpDoubleDouble(opcode, (double) m_value, rhs ); - } - - /** Arithmetic negative */ - public LValue luaUnaryMinus() { - return new LInteger( -m_value ); - } - - -} diff --git a/src/main/java/lua/value/LNil.java b/src/main/java/lua/value/LNil.java deleted file mode 100644 index 99fa3400..00000000 --- a/src/main/java/lua/value/LNil.java +++ /dev/null @@ -1,23 +0,0 @@ -package lua.value; - -public final class LNil extends LValue { - public static final LNil NIL = new LNil(); - public static final LString TYPE_NAME = new LString("nil"); - - public final LString luaAsString() { - return TYPE_NAME; - } - - public boolean luaAsBoolean() { - return false; - } - - public LString luaGetType() { - return TYPE_NAME; - } - - public int luaAsInt() { - return 0; - } - -} diff --git a/src/main/java/lua/value/LNumber.java b/src/main/java/lua/value/LNumber.java deleted file mode 100644 index 78427f37..00000000 --- a/src/main/java/lua/value/LNumber.java +++ /dev/null @@ -1,27 +0,0 @@ -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/LString.java b/src/main/java/lua/value/LString.java deleted file mode 100644 index e624a353..00000000 --- a/src/main/java/lua/value/LString.java +++ /dev/null @@ -1,392 +0,0 @@ -package lua.value; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; - -import lua.Lua; - -/** - * A String implementation for Lua using bytes instead of chars. - * - * This should have the following advantages: - * - * (1) We can use strings as byte buffers, as Lua does, and therefore avoid - * questions about how to adapt Lua APIs that use strings with binary data. - * - * (2) Half the memory usage when strings are primarily ASCII - * - * - * TODO: Decide if/when to copy the bytes to a new array to ensure memory does - * not "leak" in the form of unused portions of byte arrays. Currently, for - * efficiency, new LStrings and substrings never create copies. - */ -public class LString extends LValue { - - public static final LString TYPE_NAME = new LString("string"); - - public final byte[] m_bytes; - public final int m_offset; - public final int m_length; - public final int m_hash; - - private static LTable s_stringMT; - - /** - * Construct a Lua string from the given Java string. Characters are encoded - * using UTF-8. - */ - public LString(String string) { - byte[] bytes; - try { - bytes = string.getBytes( "UTF-8" ); - } catch ( UnsupportedEncodingException exn ) { - bytes = stringToUtf8Bytes( string ); - } - this.m_bytes = bytes; - this.m_offset = 0; - this.m_length = m_bytes.length; - this.m_hash = hashBytes( m_bytes, 0, m_length ); - } - - /** - * Construct a string from the given byte array. - * - * new LString(b) is identical to new LString(b, 0, b.length) - */ - public LString(byte[] bytes) { - this( bytes, 0, bytes.length ); - } - - /** - * Construct a string from the given byte array and range. For efficiency, - * the byte array is not copied. Lua strings are immutable so the bytes must - * not be modified after the string is constructed. - */ - public LString(byte[] bytes, int off, int len) { - if ( off < 0 || len < 0 || off+len > bytes.length ) - throw new IndexOutOfBoundsException(); - this.m_bytes = bytes; - this.m_offset = off; - this.m_length = len; - this.m_hash = hashBytes( bytes, off, len ); - } - - public boolean equals(Object o) { - if ( o != null && o instanceof LString ) { - LString s = (LString) o; - return m_hash == s.m_hash && - m_length == s.m_length && - ( ( m_bytes == s.m_bytes && m_offset == s.m_offset ) || - equals( m_bytes, m_offset, s.m_bytes, s.m_offset, m_length ) ); - } - return false; - } - - public int compareTo( LString o ) { - final byte[] a = this.m_bytes; - final byte[] b = o.m_bytes; - int i = this.m_offset; - int j = o.m_offset; - final int imax = i + m_length; - final int jmax = j + o.m_length; - - if ( a == b && i == j && imax == jmax ) - return 0; - - while ( i < imax && j < jmax ) { - if ( a[i] != b[i] ) { - return ( ( (int)a[i] ) & 0x0FF ) - ( ( (int)b[j] ) & 0x0FF ); - } - i++; - j++; - } - - return m_length - o.m_length; - } - - public int hashCode() { - return m_hash; - } - - public int length() { - return m_length; - } - - public LString substring( int beginIndex, int endIndex ) { - return new LString( m_bytes, m_offset + beginIndex, endIndex - beginIndex ); - } - - public int charAt( int index ) { - if ( index < 0 || index >= m_length ) - throw new IndexOutOfBoundsException(); - return luaByte( index ); - } - - /** Java version of strpbrk, which is a terribly named C function. */ - public int indexOfAny( LString accept ) { - final int ilimit = m_offset + m_length; - final int jlimit = accept.m_offset + accept.m_length; - for ( int i = m_offset; i < ilimit; ++i ) { - for ( int j = accept.m_offset; j < jlimit; ++j ) { - if ( m_bytes[i] == accept.m_bytes[j] ) { - return i - m_offset; - } - } - } - return -1; - } - - public int indexOf( LString s, int start ) { - final int slen = s.length(); - final int limit = m_offset + m_length - slen; - for ( int i = m_offset + start; i <= limit; ++i ) { - if ( equals( m_bytes, i, s.m_bytes, s.m_offset, slen ) ) { - return i; - } - } - return -1; - } - - public static LString valueOf( double d ) { - return new LString( String.valueOf( d ) ); - } - - public static LString valueOf( int x ) { - return new LString( String.valueOf( x ) ); - } - - public static LString concat( final LString[] strings ) { - int length = 0; - for ( int i = 0; i < strings.length; ++i ) { - length += strings[i].length(); - } - byte[] bytes = new byte[length]; - - for ( int i = 0, offset = 0; i < strings.length; ++i ) { - LString s = strings[i]; - final int len = s.length(); - System.arraycopy( s.m_bytes, s.m_offset, bytes, offset, len ); - offset += len; - } - - return new LString( bytes ); - } - - /** - * Write the specified substring of this string to the given output stream. - */ - public void write( OutputStream os, int offset, int len ) throws IOException { - if ( offset < 0 || len < 0 ) - throw new IndexOutOfBoundsException(); - if ( offset + len > m_length ) - throw new IndexOutOfBoundsException(); - - os.write( m_bytes, m_offset+offset, len ); - } - - public void write(OutputStream os) throws IOException { - write(os, 0, m_length); - } - - /** - * Copy the bytes of the string into the given byte array. - */ - public void copyInto( int strOffset, byte[] bytes, int arrayOffset, int len ) { - System.arraycopy( m_bytes, m_offset+strOffset, bytes, arrayOffset, len ); - } - - /** - * Produce an InputStream instance from which the bytes of this LString can be read. - * Underlying byte array is not copied. - */ - public ByteArrayInputStream toInputStream() { - // Well, this is really something. - // Javadoc for java versions 1.3 and earlier states that if reset() is - // called on a ByteArrayInputStream constructed with the 3-argument - // constructor, then bytes 0 .. offset will be returned by the next - // calls to read(). In JDK 1.4, the behavior improved, so that the - // initial mark is set to the initial offset. We still need to - // override ByteArrayInputStream here just in case we run on a - // JVM with the older behavior. - return new ByteArrayInputStream( m_bytes, m_offset, m_length ) { - public synchronized void reset() { - pos = Math.max( m_offset, mark ); - } - }; - } - - public boolean luaBinCmpUnknown(int opcode, LValue lhs) { - return lhs.luaBinCmpString(opcode, this); - } - - public boolean luaBinCmpString(int opcode, LString rhs) { - switch ( opcode ) { - case Lua.OP_EQ: return equals(rhs); - case Lua.OP_LT: return compareTo(rhs) < 0; - case Lua.OP_LE: return compareTo(rhs) <= 0; - } - luaUnsupportedOperation(); - return false; - } - - public LValue luaBinOpDouble( int opcode, double m_value ) { - return luaToNumber().luaBinOpDouble( opcode, m_value ); - } - - public LValue luaBinOpInteger( int opcode, int m_value ) { - return luaToNumber().luaBinOpInteger( opcode, m_value ); - } - - public LValue luaBinOpUnknown( int opcode, LValue lhs ) { - return luaToNumber().luaBinOpUnknown( opcode, lhs ); - } - - public LValue luaUnaryMinus() { - return luaToNumber().luaUnaryMinus(); - } - - public LValue luaToNumber() { - return luaToNumber( 10 ); - } - - public LValue luaToNumber( int base ) { - if ( base >= 2 && base <= 36 ) { - String str = toJavaString().trim(); - try { - return new LInteger( Integer.parseInt( str, base ) ); - } catch ( NumberFormatException nfe ) { - if ( base == 10 ) { - try { - return new LDouble( Double.parseDouble( str ) ); - } catch ( NumberFormatException nfe2 ) { - } - } - } - } - - return LNil.NIL; - } - - public LString luaAsString() { - return this; - } - - public String toJavaString() { - try { - return new String( m_bytes, m_offset, m_length, "UTF-8" ); - } catch ( UnsupportedEncodingException uee ) { - throw new RuntimeException("toJavaString: UTF-8 decoding not implemented"); - } - } - - /** Built-in opcode LEN, for Strings and Tables */ - public LValue luaLength() { - return new LInteger( length() ); - } - - public LString luaGetType() { - return TYPE_NAME; - } - - public LTable luaGetMetatable() { - synchronized ( LString.class ) { - return s_stringMT; - } - } - - /** - * Get the metatable for all string values. Creates the table if it does not - * exist yet, and sets its __index entry to point to itself. - * - * @return metatable that will be used for all strings - */ - public static synchronized LTable getMetatable() { - if ( s_stringMT == null ) { - s_stringMT = new LTable(); - s_stringMT.put( TM_INDEX, s_stringMT ); - } - return s_stringMT; - } - - public static boolean equals( LString a, int i, LString b, int j, int n ) { - return equals( a.m_bytes, a.m_offset + i, b.m_bytes, b.m_offset + j, n ); - } - - public static boolean equals( byte[] a, int i, byte[] b, int j, int n ) { - if ( a.length < i + n || b.length < j + n ) - return false; - final int imax = i + n; - final int jmax = j + n; - while ( i < imax && j < jmax ) { - if ( a[i++] != b[j++] ) - return false; - } - return true; - } - - private static int hashBytes( byte[] bytes, int offset, int length ) { - // Compute the hash of the given bytes. - // This code comes right out of Lua 5.1.2 (translated from C to Java) - int h = length; /* seed */ - int step = (length>>5)+1; /* if string is too long, don't hash all its chars */ - for (int l1=length; l1>=step; l1-=step) /* compute hash */ - h = h ^ ((h<<5)+(h>>2)+(((int) bytes[offset+l1-1] ) & 0x0FF )); - return h; - } - - private static byte[] stringToUtf8Bytes( final String string ) { - final int strlen = string.length(); - byte[] bytes = new byte[ strlen ]; - byte b1 = 0, b2 = 0, b3 = 0; - - int j = 0; - for ( int i = 0; i < strlen; ++i ) { - int c = string.charAt( i ); - // TODO: combine 2-character combinations - int count; - if ( c > 0x07FF ) { - count = 3; - b3 = (byte)( 0xE0 | ( c >> 12 ) ); - b2 = (byte)( 0x80 | ( ( c >> 6 ) & 0x03F ) ); - b1 = (byte)( 0x80 | ( ( c ) & 0x03F ) ); - } else if ( c > 0x07F ) { - count = 2; - b2 = (byte)( 0xC0 | ( c >> 6 ) ); - b1 = (byte)( 0x80 | ( c & 0x03F ) ); - } else { - count = 1; - b1 = (byte) c; - } - if ( j + count > bytes.length ) { - bytes = realloc( bytes, ( j + count ) * 2 ); - } - switch ( count ) { - case 3: - bytes[j++] = b3; - case 2: - bytes[j++] = b2; - case 1: - bytes[j++] = b1; - } - } - - if ( j != bytes.length ) { - bytes = realloc( bytes, j ); - } - return bytes; - } - - private static byte[] realloc( byte[] a, int newSize ) { - final byte[] newbytes = new byte[ newSize ]; - System.arraycopy( a, 0, newbytes, 0, Math.min( newSize, a.length ) ); - return newbytes; - } - - public int luaByte(int index) { - return m_bytes[m_offset + index] & 0x0FF; - } - -} diff --git a/src/main/java/lua/value/LTable.java b/src/main/java/lua/value/LTable.java deleted file mode 100644 index 900e6235..00000000 --- a/src/main/java/lua/value/LTable.java +++ /dev/null @@ -1,540 +0,0 @@ -package lua.value; - -import lua.Lua; -import lua.VM; - -/** - * Simple implementation of table structure for Lua VM. Maintains both an array - * part and a hash part. Does not attempt to achieve the same performance as the - * C version. - * - * Java code can put values in the table or get values out (bypassing the - * metatable, if there is one) using put() and get(). There are specializations - * of put() and get() for integers and Strings to avoid allocating wrapper - * objects when possible. - * - * remove() methods are private: setting a key's value to nil is the correct way - * to remove an entry from the table. - * - * TODO: Support for weak tables has to be shoehorned in here somehow. - * - */ -public class LTable extends LValue { - - public static final LString TYPE_NAME = new LString("table"); - - /** - * Zero-length array to use instead of null, so that we don't need to - * check for null everywhere. - */ - private static final LValue[] EMPTY_ARRAY = new LValue[0]; - - /** - * Minimum legal capacity for the hash portion. Note that the hash portion - * must never be filled to capacity or findSlot() will run forever. - */ - private static final int MIN_HASH_CAPACITY = 2; - - /** - * Array of keys in the hash part. When there is no hash part this is null. - * Elements of m_hashKeys are never LNil.NIL - they are null to indicate - * the hash slot is empty and some non-null, non-nil value otherwise. - */ - private LValue[] m_hashKeys; - - /** - * Values in the hash part. Must be null when m_hashKeys is null and equal - * in size otherwise. - */ - private LValue[] m_hashValues; - - /** - * m_hashEntries is the number of slots that are used. Must always be less - * than m_hashKeys.length. - */ - private int m_hashEntries; - - /** - * Array of values to store the "array part" of the table, that is the - * entries with positive integer keys. Elements must never be null: "empty" - * slots are set to LNil.NIL. - */ - private LValue[] m_vector; - - /** - * Number of values in m_vector that non-nil. - */ - private int m_arrayEntries; - - private LTable m_metatable; - - - - /** Construct an empty LTable with no initial capacity. */ - public LTable() { - m_vector = EMPTY_ARRAY; - } - - /** - * Construct an empty LTable that is expected to contain entries with keys - * in the range 1 .. narray and nhash non-integer keys. - */ - public LTable( int narray, int nhash ) { - if ( nhash > 0 ) { - // Allocate arrays 25% bigger than nhash to account for load factor. - final int capacity = Math.max( nhash + ( nhash >> 2 ), nhash + 1 ); - m_hashKeys = new LValue[capacity]; - m_hashValues = new LValue[capacity]; - } - m_vector = new LValue[narray]; - for ( int i = 0; i < narray; ++i ) { - m_vector[i] = LNil.NIL; - } - } - - /** - * Return total number of keys mapped to non-nil values. Not to be confused - * with luaLength, which returns some number n such that the value at n+1 is - * nil. - */ - public int size() { - return m_hashEntries + m_arrayEntries; - } - - /** - * Generic put method for all types of keys, but does not use the metatable. - */ - public void put( LValue key, LValue val ) { - if ( key.isInteger() ) { - // call the integer-specific put method - put( key.luaAsInt(), val ); - } else if ( val == null || val == LNil.NIL ) { - // Remove the key if the value is nil. This comes after the check - // for an integer key so that values are properly removed from - // the array part. - remove( key ); - } else { - if ( checkLoadFactor() ) - rehash(); - int slot = findSlot( key ); - if ( fillHashSlot( slot, val ) ) - return; - m_hashKeys[slot] = key; - } - } - - /** - * Utility method for putting a string-keyed value directly, typically for - * initializing a table. Bypasses the metatable, if any. - */ - public void put( String key, LValue value ) { - put( new LString( key ), value ); - } - - /** - * Method for putting an integer-keyed value. Bypasses the metatable, if - * any. - */ - public void put( int key, LValue value ) { - if (value == null || value == LNil.NIL) { - remove( key ); - return; - } - if ( key > 0 ) { - final int index = key - 1; - for ( ;; ) { - if ( index < m_vector.length ) { - if ( m_vector[index] == LNil.NIL ) { - ++m_arrayEntries; - } - m_vector[index] = value; - return; - } else if ( index < ( m_arrayEntries + 1 ) * 2 ) { - resize( ( m_arrayEntries + 1 ) * 2 ); - } else { - break; - } - } - } - - // No room in array part, use hash part instead. - if ( checkLoadFactor() ) - rehash(); - int slot = findSlot( key ); - if ( fillHashSlot( slot, value ) ) - return; - m_hashKeys[ slot ] = new LInteger( key ); - } - - - /** - * Utility method to directly get the value in a table, without metatable - * calls. Must never return null, use LNil.NIL instead. - */ - public LValue get( LValue key ) { - if ( m_vector.length > 0 && key.isInteger() ) { - final int index = key.luaAsInt() - 1; - if ( index >= 0 && index < m_vector.length ) { - return m_vector[index]; - } - } - - if ( m_hashKeys == null ) - return LNil.NIL; - - int slot = findSlot( key ); - return ( m_hashKeys[slot] != null ) ? m_hashValues[slot] : LNil.NIL; - } - - /** Utility method for retrieving an integer-keyed value */ - public LValue get( int key ) { - if ( key > 0 && key <= m_vector.length ) { - return m_vector[key - 1]; - } - - int slot = findSlot( key ); - return ( m_hashKeys[slot] != null ) ? m_hashValues[slot] : LNil.NIL; - } - - - /** - * Return true if the table contains an entry with the given key, false if - * not. Ignores the metatable. - */ - public boolean containsKey( LValue key ) { - if ( m_vector.length > 0 && key.isInteger() ) { - final int index = key.luaAsInt() - 1; - if ( index >= 0 && index < m_vector.length ) { - final LValue v = m_vector[index]; - return v != LNil.NIL; - } - } - if ( m_hashKeys == null ) - return false; - final int slot = findSlot( key ); - return m_hashKeys[ slot ] != null; - } - - public void luaGetTable(VM vm, LValue table, LValue key) { - LValue v = get(key); - if ( v == LNil.NIL && m_metatable != null ) { - super.luaGetTable( vm, table, key ); - } else { - vm.push(v); - } - } - - public void luaSetTable(VM vm, LValue table, LValue key, LValue val) { - if ( !containsKey( key ) && m_metatable != null ) { - super.luaSetTable( vm, table, key, val ); - } else { - put(key, val); - } - } - - /** - * Return the "length" of this table. This will not return the same result - * as the C version in all cases, but that's ok because the length operation - * on a table where the integer keys are sparse is vaguely defined. - */ - public LValue luaLength() { - for ( int i = Math.max( 0, m_arrayEntries-1 ); i < m_vector.length; ++i ) { - if ( m_vector[i] != LNil.NIL && - ( i+1 == m_vector.length || m_vector[i+1] == LNil.NIL ) ) { - return new LInteger( i+1 ); - } - } - return new LInteger( 0 ); - } - - /** Valid for tables */ - public LTable luaGetMetatable() { - return this.m_metatable; - } - - /** Valid for tables */ - public void luaSetMetatable(LValue metatable) { - this.m_metatable = ( metatable != null && metatable != LNil.NIL ) ? - (LTable) metatable : null; - } - - public LString luaAsString() { - return new LString("table: "+id()); - } - - public LString luaGetType() { - return TYPE_NAME; - } - - /** Valid for tables */ - public LValue luaPairs(boolean isPairs) { - return new LTableIterator(isPairs); - } - - /** Iterator for tables */ - private final class LTableIterator extends LFunction { - private int arrayIndex; - private int hashIndex; - private final boolean isPairs; - - private LTableIterator(boolean isPairs) { - this.arrayIndex = 0; - this.hashIndex = 0; - this.isPairs = isPairs; - } - - // perform a lua call - public boolean luaStackCall(VM vm) { - vm.setResult(); - int i; - while ( ( i = arrayIndex++ ) < m_vector.length ) { - if ( m_vector[i] != LNil.NIL ) { - vm.push( new LInteger( arrayIndex ) ); - vm.push( m_vector[ i ] ); - return false; - } - } - if ( isPairs && (m_hashKeys != null) ) { - while ( ( i = hashIndex++ ) < m_hashKeys.length ) { - if ( m_hashKeys[i] != null ) { - vm.push( m_hashKeys[i] ); - vm.push( m_hashValues[i] ); - return false; - } - } - } - return false; - } - } - - /** - * Helper method to get all the keys in this table in an array. Meant to be - * used instead of keys() (which returns an enumeration) when an array is - * more convenient. Note that for a very large table, getting an Enumeration - * instead would be more space efficient. - */ - public LValue[] getKeys() { - LValue[] keys = new LValue[ m_arrayEntries + m_hashEntries ]; - int out = 0; - - for ( int i = 0; i < m_vector.length; ++i ) { - if ( m_vector[ i ] != LNil.NIL ) { - keys[ out++ ] = new LInteger( i + 1 ); - } - } - - if ( m_hashKeys != null ) { - for ( int i = 0; i < m_hashKeys.length; ++i ) { - if ( m_hashKeys[ i ] != null ) - keys[ out++ ] = m_hashKeys[i]; - } - } - - return keys; - } - - /** Remove the value in the table with the given integer key. */ - private void remove( int key ) { - if ( key > 0 ) { - final int index = key - 1; - if ( index < m_vector.length ) { - if ( m_vector[ index ] != LNil.NIL ) { - --m_arrayEntries; - } - return; - } - } - - if ( m_hashKeys != null ) { - int slot = findSlot( key ); - clearSlot( slot ); - } - } - - private void remove( LValue key ) { - if ( m_hashKeys != null ) { - int slot = findSlot( key ); - clearSlot( slot ); - } - } - - private void clearSlot( int i ) { - if ( m_hashKeys[ i ] != null ) { - - int j = i; - while ( m_hashKeys[ j = ( ( j + 1 ) % m_hashKeys.length ) ] != null ) { - final int k = hashToIndex( m_hashKeys[ j ].hashCode() ); - if ( ( j > i && ( k <= i || k > j ) ) || - ( j < i && ( k <= i && k > j ) ) ) { - m_hashKeys[ i ] = m_hashKeys[ j ]; - m_hashValues[ i ] = m_hashValues[ j ]; - i = j; - } - } - - --m_hashEntries; - m_hashKeys[ i ] = null; - m_hashValues[ i ] = null; - - if ( m_hashEntries == 0 ) { - m_hashKeys = null; - m_hashValues = null; - } - } - } - - private int findSlot( LValue key ) { - int i = hashToIndex( key.hashCode() ); - - // This loop is guaranteed to terminate as long as we never allow the - // table to get 100% full. - LValue k; - while ( ( k = m_hashKeys[i] ) != null && - !key.luaBinCmpUnknown( Lua.OP_EQ, k ) ) { - i = ( i + 1 ) % m_hashKeys.length; - } - return i; - } - - private int findSlot( int key ) { - int i = hashToIndex( LInteger.hashCodeOf( key ) ); - - // This loop is guaranteed to terminate as long as we never allow the - // table to get 100% full. - LValue k; - while ( ( k = m_hashKeys[i] ) != null && - !k.luaBinCmpInteger( Lua.OP_EQ, key ) ) { - i = ( i + 1 ) % m_hashKeys.length; - } - return i; - } - - /** - * @return true if the given slot was already occupied, false otherwise. - */ - private boolean fillHashSlot( int slot, LValue value ) { - m_hashValues[ slot ] = value; - if ( m_hashKeys[ slot ] != null ) { - return true; - } else { - ++m_hashEntries; - return false; - } - } - - private int hashToIndex( int hash ) { - return ( hash & 0x7FFFFFFF ) % m_hashKeys.length; - } - - /** - * Should be called before inserting a value into the hash. - * - * @return true if the hash portion of the LTable is at its capacity. - */ - private boolean checkLoadFactor() { - if ( m_hashKeys == null ) - return true; - // Using a load factor of 2/3 because that is easy to compute without - // overflow or division. - final int hashCapacity = m_hashKeys.length; - return ( hashCapacity >> 1 ) >= ( hashCapacity - m_hashEntries ); - } - - private void rehash() { - final int oldCapacity = ( m_hashKeys != null ) ? m_hashKeys.length : 0; - final int newCapacity = ( oldCapacity > 0 ) ? 2 * oldCapacity : MIN_HASH_CAPACITY; - - final LValue[] oldKeys = m_hashKeys; - final LValue[] oldValues = m_hashValues; - - m_hashKeys = new LValue[ newCapacity ]; - m_hashValues = new LValue[ newCapacity ]; - - for ( int i = 0; i < oldCapacity; ++i ) { - final LValue k = oldKeys[i]; - if ( k != null ) { - final LValue v = oldValues[i]; - final int slot = findSlot( k ); - m_hashKeys[slot] = k; - m_hashValues[slot] = v; - } - } - } - - private void resize( int newCapacity ) { - final int oldCapacity = m_vector.length; - LValue[] newVector = new LValue[ newCapacity ]; - System.arraycopy( m_vector, 0, newVector, 0, Math.min( oldCapacity, newCapacity ) ); - - // We need to move keys from hash part to array part if array part is - // getting bigger, and from array part to hash part if array is getting - // smaller. - if ( newCapacity > oldCapacity ) { - if ( m_hashKeys != null ) { - for ( int i = oldCapacity; i < newCapacity; ++i ) { - int slot = findSlot( i+1 ); - if ( m_hashKeys[ slot ] != null ) { - newVector[ i ] = m_hashValues[ slot ]; - m_hashKeys[ i ] = null; - --m_hashEntries; - } else { - // Make sure all array-part values are initialized to nil - // so that we can just do one compare instead of two - // whenever we need to check if a slot is full or not. - newVector[ i ] = LNil.NIL; - } - } - } else { - for ( int i = oldCapacity; i < newCapacity; ++i ) { - newVector[ i ] = LNil.NIL; - } - } - } else { - for ( int i = newCapacity; i < oldCapacity; ++i ) { - LValue v = m_vector[i]; - if ( v != LNil.NIL ) { - if (checkLoadFactor()) - rehash(); - final int slot = findSlot( i+1 ); - m_hashKeys[ slot ] = new LInteger( i+1 ); - m_hashValues[ slot ] = v; - ++m_hashEntries; - } - } - } - - m_vector = newVector; - } - - // hooks for junit - - int getHashCapacity() { - return ( m_hashKeys != null ) ? m_hashKeys.length : 0; - } - - int getArrayCapacity() { - return m_vector.length; - } - - /* - * @pos index to insert at, or 0 to insert at end. - */ - public void luaInsertPos(int pos, LValue value) { - if ( pos != 0 ) - throw new RuntimeException("luaInsertPos() not implemented"); - put( m_arrayEntries + m_hashEntries + 1, value ); - } - - public void luaSort() { - throw new RuntimeException("luaSort() not implemented"); - } - - public void luaRemovePos(int pos) { - throw new RuntimeException("luaRemovePos() not implemented"); - } - - public int luaMaxN() { - throw new RuntimeException("luaMaxN() not implemented"); - } - -} diff --git a/src/main/java/lua/value/LThread.java b/src/main/java/lua/value/LThread.java deleted file mode 100644 index 03faf58c..00000000 --- a/src/main/java/lua/value/LThread.java +++ /dev/null @@ -1,13 +0,0 @@ -package lua.value; - -public class LThread extends LValue { - public static final LString TYPE_NAME = new LString("thread"); - - public LString luaGetType() { - return TYPE_NAME; - } - - public LString luaAsString() { - return new LString("thread: "+hashCode()); - } -} diff --git a/src/main/java/lua/value/LUserData.java b/src/main/java/lua/value/LUserData.java deleted file mode 100644 index a89d347d..00000000 --- a/src/main/java/lua/value/LUserData.java +++ /dev/null @@ -1,33 +0,0 @@ -package lua.value; - -public class LUserData extends LValue { - public static final LString TYPE_NAME = new LString("userdata"); - - public final Object m_instance; - public LTable m_metatable; - - public LUserData(Object obj) { - m_instance = obj; - } - - public LString luaAsString() { - return new LString(m_instance.toString()); - } - - public boolean equals(Object obj) { - return (this == obj) || - (obj instanceof LUserData && this.m_instance == ((LUserData) obj).m_instance); - } - - public int hashCode() { - return System.identityHashCode( m_instance ); - } - - public LString luaGetType() { - return TYPE_NAME; - } - - public LTable luaGetMetatable() { - return m_metatable; - } -} diff --git a/src/main/java/lua/value/LValue.java b/src/main/java/lua/value/LValue.java deleted file mode 100644 index d419ac59..00000000 --- a/src/main/java/lua/value/LValue.java +++ /dev/null @@ -1,182 +0,0 @@ -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/main/java/lua/value/Type.java b/src/main/java/lua/value/Type.java new file mode 100644 index 00000000..ad651980 --- /dev/null +++ b/src/main/java/lua/value/Type.java @@ -0,0 +1,81 @@ +/******************************************************************************* +* 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.value; + +import java.io.Serializable; + +public class Type implements Serializable, Comparable { + private static final long serialVersionUID = 877640303374122782L; + + public static Type bool = new Type("boolean"); + public static Type function = new Type("function"); + public static Type nil = new Type("nil"); + public static Type number = new Type("number"); + public static Type string = new Type("string"); + public static Type table = new Type("table"); + public static Type thread = new Type("thread"); + public static Type userdata = new Type("userdata"); + protected static Type[] VALUES = new Type[] { + bool, + function, + nil, + number, + string, + table, + thread, + userdata + }; + protected static int ORDINAL = 0; + + private String name; + private int ordinal; + Type(String name) { + this.name = name; + this.ordinal = ORDINAL++; + } + + /* (non-Javadoc) + * @see java.lang.Enum#toString() + */ + @Override + public String toString() { + return name; + } + + public static Type valueOf(String strValue) { + Type[] values = Type.VALUES; + for (Type value : values) { + if (value.toString().equals(strValue)) { + return value; + } + } + + throw new IllegalArgumentException("String '" + strValue + "' cannot be converted to enum Type"); + } + + /* (non-Javadoc) + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + public int compareTo(Object o) { + return this.ordinal - ((Type)o).ordinal; + } +} diff --git a/src/test/java/lua/debug/DebugStackStateTest.java b/src/test/java/lua/debug/DebugStackStateTest.java deleted file mode 100644 index 0441c486..00000000 --- a/src/test/java/lua/debug/DebugStackStateTest.java +++ /dev/null @@ -1,57 +0,0 @@ -package lua.debug; - -import java.io.IOException; -import java.io.InputStream; - -import junit.framework.TestCase; -import lua.io.Closure; -import lua.io.LoadState; -import lua.io.Proto; -import lua.value.LValue; - -public class DebugStackStateTest extends TestCase { - - public void testDebugStackState() throws InterruptedException, IOException { - String script = "/test6.luac"; - - // set up the vm - final DebugStackState state = new DebugStackState(); - InputStream is = getClass().getResourceAsStream( script ); - Proto p = LoadState.undump(state, is, script); - - // create closure and execute - final Closure c = new Closure( state, p ); - - // suspend the vm right away - state.suspend(); - state.set( 14 ); - - // start the call processing in its own thread - new Thread() { - public void run() { - try { - state.doCall( c, new LValue[0] ); - } catch ( Exception e ) { - e.printStackTrace(); - } - } - }.start(); - - // step for 5 steps - for ( int i=0; i<5; i++ ) { - state.step(); - Thread.sleep(500); - System.out.println("--- callgraph="+state.callgraph() ); - System.out.println("--- stack="+state.stack() ); - System.out.println("--- variable(1,0)="+state.variable(1,0) ); - } - - // resume the vm - state.resume(); - Thread.sleep(500); - System.out.println("--- callgraph="+state.callgraph() ); - state.resume(); - Thread.sleep(500); - System.out.println("--- callgraph="+state.callgraph() ); - } -} diff --git a/src/test/java/lua/debug/LuaJVMTest.java b/src/test/java/lua/debug/LuaJVMTest.java new file mode 100644 index 00000000..7dc186d4 --- /dev/null +++ b/src/test/java/lua/debug/LuaJVMTest.java @@ -0,0 +1,81 @@ +/******************************************************************************* +* 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.net.URL; + +import junit.framework.TestCase; +import lua.LuaJVM; + +/** + * Sanity test for LuaJVM. + */ +public class LuaJVMTest extends TestCase { + protected void doTestRun(String testName) { + String[] args = new String[2]; + args[0] = "-file"; + URL filePath = getClass().getResource("/"+ testName); + if (filePath != null) { + args[1] = filePath.getPath(); + try { + LuaJVM.main(args); + } catch (Exception e) { + e.printStackTrace(); + fail("Test " + testName + " failed due to " + e.getMessage()); + } + } + } + + public void testRun() { + String[] tests = new String[] { + "autoload", + "boolean", + "calls", + "coercions", + "compare", + "math", + "mathlib", + "metatables", + "select", + "setlist", + "swingapp", + "test1", + "test2", + "test3", + "test4", + "test5", + "test6", + "test7", + "type", + "upvalues", + //"strlib" + }; + + for (String test : tests) { + System.out.println("==> running test: " + test + ".lua"); + doTestRun(test + ".lua"); + System.out.println("==> running test: " + test + ".luac"); + doTestRun(test + ".luac"); + System.out.println(); + } + } +} diff --git a/src/test/java/lua/debug/TypeTest.java b/src/test/java/lua/debug/TypeTest.java new file mode 100644 index 00000000..6ac64cf7 --- /dev/null +++ b/src/test/java/lua/debug/TypeTest.java @@ -0,0 +1,39 @@ +/******************************************************************************* +* 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 junit.framework.TestCase; +import lua.value.Type; + +/** + * + */ +public class TypeTest extends TestCase { + public void testEnumToString() { + assertEquals("boolean", Type.bool.toString()); + } + + public void testStringToEnum() { + String boolStr = "boolean"; + assertEquals(Type.bool, Type.valueOf(boolStr)); + } +} diff --git a/src/test/java/lua/io/LoadStateTest.java b/src/test/java/lua/io/LoadStateTest.java deleted file mode 100644 index e23e0e59..00000000 --- a/src/test/java/lua/io/LoadStateTest.java +++ /dev/null @@ -1,92 +0,0 @@ -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 ); - } -}