added debugging support and integrated with Eclipse debugger

This commit is contained in:
Shu Lei
2007-10-03 17:39:37 +00:00
parent cd5f278e7b
commit 421eface40
46 changed files with 1586 additions and 2547 deletions

View File

@@ -2,6 +2,7 @@ package lua.addon.compile;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.util.Arrays;
import java.util.Hashtable; import java.util.Hashtable;
import lua.Lua; import lua.Lua;
@@ -15,6 +16,24 @@ import lua.value.LString;
public class LexState extends LuaC { public class LexState extends LuaC {
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 EOZ = (-1);
private static final int MAXSRC = 80; private static final int MAXSRC = 80;
private static final int MAX_INT = Integer.MAX_VALUE-2; private static final int MAX_INT = Integer.MAX_VALUE-2;
@@ -28,7 +47,10 @@ public class LexState extends LuaC {
private static final int LUA_COMPAT_LSTR = 1; // 1 for compatibility, 2 for old behavior 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 ** Marks the end of a patch list. It is an invalid value both as an absolute
@@ -1588,9 +1610,9 @@ public class LexState extends LuaC {
/* fornum -> NAME = exp1,exp1[,exp1] forbody */ /* fornum -> NAME = exp1,exp1[,exp1] forbody */
FuncState fs = this.fs; FuncState fs = this.fs;
int base = fs.freereg; int base = fs.freereg;
this.new_localvarliteral("(for index)", 0); this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_INDEX, 0);
this.new_localvarliteral("(for limit)", 1); this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_LIMIT, 1);
this.new_localvarliteral("(for step)", 2); this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_STEP, 2);
this.new_localvar(varname, 3); this.new_localvar(varname, 3);
this.checknext('='); this.checknext('=');
this.exp1(); /* initial value */ this.exp1(); /* initial value */
@@ -1614,9 +1636,9 @@ public class LexState extends LuaC {
int line; int line;
int base = fs.freereg; int base = fs.freereg;
/* create control variables */ /* create control variables */
this.new_localvarliteral("(for generator)", nvars++); this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_GENERATOR, nvars++);
this.new_localvarliteral("(for state)", nvars++); this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_STATE, nvars++);
this.new_localvarliteral("(for control)", nvars++); this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_CONTROL, nvars++);
/* create declared variables */ /* create declared variables */
this.new_localvar(indexname, nvars++); this.new_localvar(indexname, nvars++);
while (this.testnext(',')) while (this.testnext(','))

View File

@@ -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<NAMES.length; i++ )
table.put( NAMES[i], new Builtin(i) );
}
private static final String[] NAMES = {
"print",
"pairs",
"getmetatable",
"setmetatable",
"type",
"pcall",
"ipairs",
};
private static final int PRINT = 0;
private static final int PAIRS = 1;
private static final int GETMETATABLE = 2;
private static final int SETMETATABLE = 3;
private static final int TYPE = 4;
private static final int PCALL = 5;
private static final int IPAIRS = 6;
private static PrintStream stdout = System.out;
private int id;
private Builtin( int id ) {
this.id = id;
}
public String toString() {
return "builtin."+NAMES[id];
}
// perform a lua call
public boolean luaStackCall(VM vm) {
switch ( id ) {
case PRINT: {
int n = vm.getArgCount();
for ( int i=0; i<n; i++ ) {
if ( 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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<<SIZE_OP)-1);
public static final int MAXARG_A = ((1<<SIZE_A)-1);
public static final int MAXARG_B = ((1<<SIZE_B)-1);
public static final int MAXARG_C = ((1<<SIZE_C)-1);
public static final int MAXARG_Bx = ((1<<SIZE_Bx)-1);
public static final int MAXARG_sBx = (MAXARG_Bx>>1); /* `sBx' is signed */
public static final int MASK_OP = ((1<<SIZE_OP)-1)<<POS_OP;
public static final int MASK_A = ((1<<SIZE_A)-1)<<POS_A;
public static final int MASK_B = ((1<<SIZE_B)-1)<<POS_B;
public static final int MASK_C = ((1<<SIZE_C)-1)<<POS_C;
public static final int MASK_Bx = ((1<<SIZE_Bx)-1)<<POS_Bx;
public static final int MASK_NOT_OP = ~MASK_OP;
public static final int MASK_NOT_A = ~MASK_A;
public static final int MASK_NOT_B = ~MASK_B;
public static final int MASK_NOT_C = ~MASK_C;
public static final int MASK_NOT_Bx = ~MASK_Bx;
/*
** the following macros help to manipulate instructions
*/
public static int GET_OPCODE(int i) {
return (i >> 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+1) then { pc+=sBx; R(A+3)=R(A) }*/
public static final int OP_FORPREP = 32; /* A sBx R(A)-=R(A+2); pc+=sBx */
public static final int OP_TFORLOOP = 33; /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++ */
public static final int OP_SETLIST = 34; /* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
public static final int OP_CLOSE = 35; /* A close all variables in the stack up to (>=) 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,
};
}

View File

@@ -25,12 +25,21 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; 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.addon.luajava.LuaJava;
import lua.debug.DebugEvent;
import lua.debug.DebugEventType;
import lua.debug.DebugRequest;
import lua.debug.DebugRequestListener; import lua.debug.DebugRequestListener;
import lua.debug.DebugServer; import lua.debug.DebugResponse;
import lua.debug.DebugStackState; import lua.debug.DebugStackState;
import lua.debug.DebugSupport;
import lua.debug.DebugUtils;
import lua.io.Closure; import lua.io.Closure;
import lua.io.LoadState; import lua.io.LoadState;
import lua.io.Proto; import lua.io.Proto;
@@ -54,12 +63,14 @@ import org.apache.commons.cli.ParseException;
public class LuaJVM implements DebugRequestListener { public class LuaJVM implements DebugRequestListener {
protected Options options = new Options(); protected Options options = new Options();
protected boolean isDebugMode = false; protected boolean isDebugMode = false;
protected DebugServer debugServer; protected DebugSupport debugSupport;
protected int requestPort; protected int requestPort;
protected int eventPort; protected int eventPort;
protected String script; protected String script;
protected String[] scriptArgs; protected String[] scriptArgs;
protected DebugStackState state; protected StackState state;
protected boolean isReady = false;
protected boolean isTerminated = false;
@SuppressWarnings("static-access") @SuppressWarnings("static-access")
public LuaJVM() { public LuaJVM() {
@@ -107,14 +118,17 @@ public class LuaJVM implements DebugRequestListener {
if (line.hasOption("file")) { if (line.hasOption("file")) {
String[] fileArgs = line.getOptionValues("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]; this.scriptArgs = new String[fileArgs.length - 1];
for (int i = 1; i < fileArgs.length; i++) { 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) { } catch(NumberFormatException e) {
throw new ParseException("Invalid port number: " + e.getMessage()); throw new ParseException("Invalid port number: " + e.getMessage());
} catch (UnsupportedEncodingException e) {
throw new ParseException("Malformed program argument strings: " + e.getMessage());
} }
} }
@@ -144,16 +158,28 @@ public class LuaJVM implements DebugRequestListener {
public void run() throws IOException { public void run() throws IOException {
if (isDebug()) { if (isDebug()) {
setupDebugHooks(getRequestPort(), getEventPort()); doDebug();
} else {
doRun();
}
} }
// TODO: VM hook for debugging protected void init() {
// reset global states
GlobalState.resetGlobals();
// add LuaJava bindings // add LuaJava bindings
LuaJava.install(); LuaJava.install();
// add LuaCompat bindings
LuaCompat.install();
}
public void doRun() throws IOException {
init();
// new lua state // new lua state
state = new DebugStackState(); state = new StackState();
// convert args to lua // convert args to lua
int numOfScriptArgs = getScriptArgs().length; int numOfScriptArgs = getScriptArgs().length;
@@ -163,7 +189,7 @@ public class LuaJVM implements DebugRequestListener {
} }
// load the Lua file // load the Lua file
System.out.println("loading Lua script '" + getScript() + "'"); DebugUtils.println("loading Lua script '" + getScript() + "'");
InputStream is = new FileInputStream(new File(getScript())); InputStream is = new FileInputStream(new File(getScript()));
Proto p = LoadState.undump(state, is, getScript()); Proto p = LoadState.undump(state, is, getScript());
@@ -172,44 +198,114 @@ public class LuaJVM implements DebugRequestListener {
state.doCall(c, vargs); state.doCall(c, vargs);
} }
protected void setupDebugHooks(int requestPort, int eventPort) private void doDebug() throws IOException {
throws IOException { DebugUtils.println("start debugging...");
this.debugServer = new DebugServer(this, requestPort, eventPort); this.debugSupport = new DebugSupport(this, getRequestPort(), getEventPort());
this.debugServer.start(); 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) /* (non-Javadoc)
* @see lua.debug.DebugRequestListener#handleRequest(java.lang.String) * @see lua.debug.DebugRequestListener#handleRequest(java.lang.String)
*/ */
public String handleRequest(String request) { public DebugResponse handleRequest(DebugRequest request) {
return state.handleRequest( request ); if (!isDebug()) {
throw new UnsupportedOperationException("Must be in debug mode to handle the debug requests");
} }
public void stop() { DebugUtils.println("handling request: " + request.toString());
if (this.debugServer != null) { switch (request.getType()) {
this.debugServer.stop(); case suspend:
this.debugServer = null; 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);
} }
} }
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();
}
/** /**
* Parses the command line arguments and executes/debugs the lua program. * Parses the command line arguments and executes/debugs the lua program.
* @param args -- command line arguments: * @param args -- command line arguments:
* [-debug requestPort eventPort] -file luaProgram args * [-debug requestPort eventPort] -file luaProgram args
* @throws IOException * @throws IOException
*/ */
public static void main(String[] args) throws IOException { public static void main(String[] args) {
LuaJVM vm = new LuaJVM(); LuaJVM vm = new LuaJVM();
try { try {
vm.parse(args); vm.parse(args);
} catch (ParseException e) { } catch (ParseException e) {
System.out.println(e.getMessage()); DebugUtils.println(e.getMessage());
System.out.println();
vm.printUsage(); vm.printUsage();
return; return;
} }
try {
vm.run(); vm.run();
} catch (IOException e) {
//TODO: handle the error
e.printStackTrace();
}
} }
} }

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -21,12 +21,6 @@
******************************************************************************/ ******************************************************************************/
package lua.debug; package lua.debug;
/**
* <code>DebugRequestListener</code> handles debugging requests.
*
* @author: Shu Lei
* @version: 1.0
*/
public interface DebugRequestListener { public interface DebugRequestListener {
/** /**
@@ -41,9 +35,6 @@ public interface DebugRequestListener {
* stack -- return the content of the current stack frame, * stack -- return the content of the current stack frame,
* listing the (variable, value) pairs * listing the (variable, value) pairs
* step -- single step forward (go to next statement) * 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)
*/ */
public String handleRequest(String request); public DebugResponse handleRequest(DebugRequest request);
} }

View File

@@ -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();
}
}

View File

@@ -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
}

View File

@@ -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();
}
}

View File

@@ -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 {}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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; package lua.debug;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.StringTokenizer; import java.util.Set;
import lua.CallInfo; import lua.CallInfo;
import lua.StackState; import lua.StackState;
import lua.addon.compile.LexState;
import lua.io.LocVars; import lua.io.LocVars;
import lua.io.Proto; import lua.io.Proto;
import lua.value.LTable;
import lua.value.LValue;
import lua.value.Type;
public class DebugStackState extends StackState implements DebugRequestListener { public class DebugStackState extends StackState implements DebugRequestListener {
public Map<Integer,Boolean> breakpoints = new HashMap<Integer,Boolean>(); private static final boolean DEBUG = false;
private boolean exiting = false;
private boolean suspended = false; protected Map<String,Boolean> breakpoints = new HashMap<String,Boolean>();
private boolean stepping = false; protected boolean exiting = false;
private int lastline = -1; protected boolean suspended = false;
protected boolean stepping = false;
protected int lastline = -1;
protected List<DebugEventListener> debugEventListeners
= new ArrayList<DebugEventListener>();
public DebugStackState() { 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) { private String getFileLine(int cindex) {
String func = "?"; String func = "?";
String line = "?"; String line = "?";
@@ -29,79 +79,82 @@ public class DebugStackState extends StackState implements DebugRequestListener
Proto p = call.closure.p; Proto p = call.closure.p;
if ( p != null && p.source != null ) if ( p != null && p.source != null )
source = p.source.toJavaString(); source = p.source.toJavaString();
if ( p.lineinfo != null && p.lineinfo.length > call.pc-1 ) if ( p.lineinfo != null && p.lineinfo.length > call.pc )
line = String.valueOf( p.lineinfo[call.pc-1] ); line = String.valueOf( p.lineinfo[call.pc] );
// TODO: reverse lookup on function name ???? // TODO: reverse lookup on function name ????
func = call.closure.luaAsString().toJavaString(); func = call.closure.luaAsString().toJavaString();
} }
return source+":"+line+"("+func+")"; 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) { 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 // intercept exceptions and fill in line numbers
public void exec() { public void exec() {
try { try {
super.exec(); super.exec();
} catch ( RuntimeException t ) { } catch (AbortException e) {
// ignored. Client aborts the debugging session.
} catch ( Exception t ) {
t.printStackTrace(); t.printStackTrace();
printLuaTrace(t.getMessage()); printLuaTrace();
System.out.flush(); System.out.flush();
throw t;
} }
} }
// debug hooks // debug hooks
public void debugHooks( int pc ) { public void debugHooks( int pc ) {
if ( exiting ) DebugUtils.println("entered debugHook...");
throw new java.lang.RuntimeException("exiting");
// make sure line numbers are current in any stack traces if ( exiting )
calls[cc].pc = pc; throw new AbortException("exiting");
synchronized ( this ) { synchronized ( this ) {
// anytime the line doesn't change we keep going // anytime the line doesn't change we keep going
int[] li = calls[cc].closure.p.lineinfo; int line = getLineNumber(calls[cc]);
int line = (li!=null && li.length>pc? li[pc]: -1); DebugUtils.println("debugHook - executing line: " + line);
if ( lastline == 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; return;
} }
// save line in case next op is a step
lastline = line;
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 // wait for a state change
while ( suspended && (!exiting) && (!stepping) ) { while (suspended && !exiting ) {
try { try {
this.wait(); this.wait();
DebugUtils.println("resuming execution...");
} catch ( InterruptedException ie ) { } catch ( InterruptedException ie ) {
ie.printStackTrace(); ie.printStackTrace();
} }
@@ -109,39 +162,53 @@ public class DebugStackState extends StackState implements DebugRequestListener
} }
} }
/**
* 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 ------------------- // ------------------ commands coming from the debugger -------------------
public enum RequestType { public DebugResponse handleRequest(DebugRequest request) {
suspend, DebugUtils.println("DebugStackState is handling request: " + request.toString());
resume, switch (request.getType()) {
exit, case suspend:
set, suspend();
clear, return DebugResponseSimple.SUCCESS;
callgraph, case resume:
stack, resume();
step, return DebugResponseSimple.SUCCESS;
variable, 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() );
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 );
} }
/** /**
@@ -162,6 +229,7 @@ public class DebugStackState extends StackState implements DebugRequestListener
public void resume() { public void resume() {
synchronized ( this ) { synchronized ( this ) {
suspended = false; suspended = false;
stepping = false;
this.notify(); this.notify();
} }
} }
@@ -180,18 +248,24 @@ public class DebugStackState extends StackState implements DebugRequestListener
* set breakpoint at line N * set breakpoint at line N
* @param N the line to set the breakpoint at * @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 ) { 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 ) { 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 * return the current call graph (i.e. stack frames from
* old to new, include information about file, method, etc.) * old to new, include information about file, method, etc.)
*/ */
public String callgraph() { public StackFrame[] getCallgraph() {
int n = cc; int n = cc;
if ( n < 0 || n >= calls.length ) if ( n < 0 || n >= calls.length )
return ""; return new StackFrame[0];
StringBuffer sb = new StringBuffer();
StackFrame[] frames = new StackFrame[n+1];
for ( int i = 0; i <= n; i++ ) { for ( int i = 0; i <= n; i++ ) {
CallInfo ci = calls[i]; CallInfo ci = calls[i];
// TODO: fill this out with proper format, names, etc. frames[i] = new StackFrame(ci, getLineNumber(ci));
sb.append( String.valueOf(ci.closure.p) );
sb.append( "\n" );
} }
return sb.toString(); return frames;
} }
/** public Variable[] getStack(int index) {
* return the content of the current stack frame, if (index < 0 || index >= calls.length) {
* listing the (variable, value) pairs //TODO: this is an error, handle it differently
*/ return new Variable[0];
public String stack() {
CallInfo ci;
if ( cc < 0 || cc >= calls.length || (ci=calls[cc]) == null )
return "<out of scope>";
LocVars[] lv = ci.closure.p.locvars;
int n = (lv != null? lv.length: 0);
StringBuffer sb = new StringBuffer();
for ( int i=0; i<n; i++ ) {
// TODO: figure out format
sb.append( "(" + lv[i].varname + "," + super.stack[ci.base+i] + ")\n" );
} }
return sb.toString();
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<Variable> variables = new ArrayList<Variable>();
int localVariableCount = 0;
Set<String> variablesSeen = new HashSet<String>();
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() { public void step() {
synchronized ( this ) { synchronized ( this ) {
suspended = false;
stepping = true; stepping = true;
this.notify(); 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 "<out of scope>";
return String.valueOf( super.stack[ci.base] );
}
} }

View File

@@ -21,21 +21,14 @@
******************************************************************************/ ******************************************************************************/
package lua.debug; package lua.debug;
import java.io.BufferedReader; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.ObjectInputStream;
import java.io.PrintWriter; import java.io.ObjectOutputStream;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
/** public class DebugSupport implements DebugEventListener {
* <code>DebugServer</code> manages the communications between LuaJ VM and
* the debugging client.
*
* @author: Shu Lei
* @version: <version>
*/
public class DebugServer {
public enum State { public enum State {
UNKNOWN, UNKNOWN,
RUNNING, RUNNING,
@@ -50,14 +43,14 @@ public class DebugServer {
protected ServerSocket requestSocket; protected ServerSocket requestSocket;
protected Socket clientRequestSocket; protected Socket clientRequestSocket;
protected BufferedReader requestReader; protected ObjectInputStream requestReader;
protected PrintWriter requestWriter; protected ObjectOutputStream requestWriter;
protected ServerSocket eventSocket; protected ServerSocket eventSocket;
protected Socket clientEventSocket; protected Socket clientEventSocket;
protected PrintWriter eventWriter; protected ObjectOutputStream eventWriter;
public DebugServer(DebugRequestListener listener, public DebugSupport(DebugRequestListener listener,
int requestPort, int requestPort,
int eventPort) { int eventPort) {
this.listener = listener; this.listener = listener;
@@ -65,7 +58,8 @@ public class DebugServer {
this.eventPort = eventPort; this.eventPort = eventPort;
} }
protected void destroy() { protected void releaseServer() {
DebugUtils.println("shutting down the debug server...");
if (requestReader != null) { if (requestReader != null) {
try { try {
requestReader.close(); requestReader.close();
@@ -73,7 +67,11 @@ public class DebugServer {
} }
if (requestWriter != null) { if (requestWriter != null) {
try {
requestWriter.close(); requestWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
} }
if (clientRequestSocket != null) { if (clientRequestSocket != null) {
@@ -89,7 +87,11 @@ public class DebugServer {
} }
if (eventWriter != null) { if (eventWriter != null) {
try {
eventWriter.close(); eventWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
} }
if (clientEventSocket != null) { if (clientEventSocket != null) {
@@ -108,20 +110,22 @@ public class DebugServer {
public synchronized void start() throws IOException { public synchronized void start() throws IOException {
this.requestSocket = new ServerSocket(requestPort); this.requestSocket = new ServerSocket(requestPort);
this.clientRequestSocket = requestSocket.accept(); this.clientRequestSocket = requestSocket.accept();
this.requestReader = new BufferedReader( this.requestReader
new InputStreamReader(clientRequestSocket.getInputStream())); = new ObjectInputStream(clientRequestSocket.getInputStream());
this.requestWriter = new PrintWriter(clientRequestSocket.getOutputStream()); this.requestWriter
= new ObjectOutputStream(clientRequestSocket.getOutputStream());
this.eventSocket = new ServerSocket(eventPort); this.eventSocket = new ServerSocket(eventPort);
this.clientEventSocket = eventSocket.accept(); this.clientEventSocket = eventSocket.accept();
this.eventWriter = new PrintWriter(clientEventSocket.getOutputStream()); this.eventWriter
= new ObjectOutputStream(clientEventSocket.getOutputStream());
this.requestWatcherThread = new Thread(new Runnable() { this.requestWatcherThread = new Thread(new Runnable() {
public void run() { public void run() {
if (getState() != State.STOPPED) { if (getState() != State.STOPPED) {
handleRequest(); handleRequest();
} else { } else {
destroy(); releaseServer();
} }
} }
}); });
@@ -139,25 +143,39 @@ public class DebugServer {
public void handleRequest() { public void handleRequest() {
synchronized (clientRequestSocket) { synchronized (clientRequestSocket) {
String request = null;
try { try {
while (getState() != State.STOPPED && while (getState() != State.STOPPED) {
(request = requestReader.readLine()) != null) { DebugRequest request
System.out.println("SERVER receives request: " + request); = (DebugRequest) requestReader.readObject();
String response = listener.handleRequest(request); DebugUtils.println("SERVER receives request: " + request.toString());
requestWriter.write(response); DebugResponse response = listener.handleRequest(request);
requestWriter.writeObject(response);
requestWriter.flush(); requestWriter.flush();
DebugUtils.println("SERVER sends response: " + response);
} }
if (getState() == State.STOPPED) { if (getState() == State.STOPPED) {
destroy(); cleanup();
} }
} catch (EOFException e) {
cleanup();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); 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 * This method provides the second communication channel with the debugging
* client. The server can send events via this channel to notify the client * client. The server can send events via this channel to notify the client
@@ -175,10 +193,22 @@ public class DebugServer {
* *
* @param event * @param event
*/ */
public void fireEvent(String event) { public void fireEvent(DebugEvent event) {
DebugUtils.println("SERVER sending event: " + event.toString());
synchronized (eventSocket) { synchronized (eventSocket) {
eventWriter.println(event); try {
eventWriter.writeObject(event);
eventWriter.flush(); eventWriter.flush();
} catch (IOException e) {
e.printStackTrace();
} }
} }
} }
/* (non-Javadoc)
* @see lua.debug.DebugEventListener#notifyDebugEvent(lua.debug.DebugEvent)
*/
public void notifyDebugEvent(DebugEvent event) {
fireEvent(event);
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}

View File

@@ -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, "<table>", 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;
}
}

View File

@@ -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();
}
}

View File

@@ -3,7 +3,6 @@ package lua.io;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader;
import lua.VM; import lua.VM;
import lua.value.LBoolean; import lua.value.LBoolean;
@@ -19,6 +18,9 @@ import lua.value.LValue;
*/ */
public class LoadState { public class LoadState {
public static final String SOURCE_BINARY_STRING = "binary string";
/** mark for precompiled code (`<esc>Lua') */ /** mark for precompiled code (`<esc>Lua') */
public static final String LUA_SIGNATURE = "\033Lua"; public static final String LUA_SIGNATURE = "\033Lua";
@@ -251,17 +253,22 @@ public class LoadState {
} }
// load file // load file
String sname = name; String sname = getSourceName(name);
if ( name.startsWith("@") || name.startsWith("=") )
sname = name.substring(1);
else if ( name.startsWith("\033") )
sname = "binary string";
LoadState s = new LoadState( L, stream, sname ); LoadState s = new LoadState( L, stream, sname );
s.loadHeader(); s.loadHeader();
LString literal = new LString("=?"); LString literal = new LString("=?");
return s.loadFunction( literal ); 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 constructor for create a load state */
private LoadState( VM L, InputStream stream, String name ) { private LoadState( VM L, InputStream stream, String name ) {
this.L = L; this.L = L;

View File

@@ -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;
}
}

View File

@@ -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 );
}
}

View File

@@ -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;
}
}

View File

@@ -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 );
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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");
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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() );
}
}

View File

@@ -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();
}
}
}

View File

@@ -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));
}
}

View File

@@ -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 );
}
}