added back the files that were accidentally deleted
This commit is contained in:
105
src/main/java/lua/Builtin.java
Normal file
105
src/main/java/lua/Builtin.java
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package lua;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import lua.value.LBoolean;
|
||||
import lua.value.LFunction;
|
||||
import lua.value.LNil;
|
||||
import lua.value.LTable;
|
||||
import lua.value.LValue;
|
||||
|
||||
final class Builtin extends LFunction {
|
||||
|
||||
static void addBuiltins(LTable table) {
|
||||
for ( int i=0; i<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;
|
||||
}
|
||||
|
||||
}
|
||||
41
src/main/java/lua/GlobalState.java
Normal file
41
src/main/java/lua/GlobalState.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package lua;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
import lua.value.LTable;
|
||||
|
||||
/**
|
||||
** `global state', shared by all threads of this state
|
||||
*/
|
||||
public class GlobalState {
|
||||
|
||||
// typedef struct global_State {
|
||||
Hashtable strt; /* hash table for strings */
|
||||
StringBuffer buff; /* temporary buffer for string concatentation */
|
||||
// lu_mem totalbytes; /* number of bytes currently allocated */
|
||||
// lu_mem estimate; /* an estimate of number of bytes actually in use */
|
||||
// lua_CFunction panic; /* to be called in unprotected errors */
|
||||
// TValue l_registry;
|
||||
// struct lua_State *mainthread;
|
||||
StackState mainthread;
|
||||
// UpVal uvhead; /* head of double-linked list of all open upvalues */
|
||||
// struct Table *mt[NUM_TAGS]; /* metatables for basic types */
|
||||
// TString *tmname[TM_N]; /* array with tag-method names */
|
||||
// } global_State;
|
||||
//
|
||||
private static LTable _G;
|
||||
|
||||
static {
|
||||
resetGlobals();
|
||||
}
|
||||
|
||||
static void resetGlobals() {
|
||||
_G = new LTable();
|
||||
_G .put( "_G", _G );
|
||||
Builtin.addBuiltins( _G );
|
||||
}
|
||||
|
||||
public static LTable getGlobalsTable() {
|
||||
return _G;
|
||||
}
|
||||
}
|
||||
357
src/main/java/lua/Lua.java
Normal file
357
src/main/java/lua/Lua.java
Normal file
@@ -0,0 +1,357 @@
|
||||
package lua;
|
||||
/**
|
||||
* Constants for lua limits and opcodes
|
||||
*
|
||||
* @author jim_roseborough
|
||||
*
|
||||
*/
|
||||
public class Lua {
|
||||
|
||||
// from llimits.h
|
||||
|
||||
/** maximum stack for a Lua function */
|
||||
public static final int MAXSTACK = 250;
|
||||
|
||||
/** minimum size for the string table (must be power of 2) */
|
||||
public static final int MINSTRTABSIZE = 32;
|
||||
|
||||
/** minimum size for string buffer */
|
||||
public static final int LUA_MINBUFFER = 32;
|
||||
|
||||
/** use return values from previous op */
|
||||
public static final int LUA_MULTRET = -1;
|
||||
|
||||
|
||||
// from lopcodes.h
|
||||
|
||||
/*===========================================================================
|
||||
We assume that instructions are unsigned numbers.
|
||||
All instructions have an opcode in the first 6 bits.
|
||||
Instructions can have the following fields:
|
||||
`A' : 8 bits
|
||||
`B' : 9 bits
|
||||
`C' : 9 bits
|
||||
`Bx' : 18 bits (`B' and `C' together)
|
||||
`sBx' : signed Bx
|
||||
|
||||
A signed argument is represented in excess K; that is, the number
|
||||
value is the unsigned value minus K. K is exactly the maximum value
|
||||
for that argument (so that -max is represented by 0, and +max is
|
||||
represented by 2*max), which is half the maximum for the corresponding
|
||||
unsigned argument.
|
||||
===========================================================================*/
|
||||
|
||||
|
||||
/* basic instruction format */
|
||||
public static final int iABC = 0;
|
||||
public static final int iABx = 1;
|
||||
public static final int iAsBx = 2;
|
||||
|
||||
|
||||
/*
|
||||
** size and position of opcode arguments.
|
||||
*/
|
||||
public static final int SIZE_C = 9;
|
||||
public static final int SIZE_B = 9;
|
||||
public static final int SIZE_Bx = (SIZE_C + SIZE_B);
|
||||
public static final int SIZE_A = 8;
|
||||
|
||||
public static final int SIZE_OP = 6;
|
||||
|
||||
public static final int POS_OP = 0;
|
||||
public static final int POS_A = (POS_OP + SIZE_OP);
|
||||
public static final int POS_C = (POS_A + SIZE_A);
|
||||
public static final int POS_B = (POS_C + SIZE_C);
|
||||
public static final int POS_Bx = POS_C;
|
||||
|
||||
|
||||
public static final int MAX_OP = ((1<<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,
|
||||
};
|
||||
|
||||
}
|
||||
197
src/main/java/lua/VM.java
Normal file
197
src/main/java/lua/VM.java
Normal file
@@ -0,0 +1,197 @@
|
||||
package lua;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import lua.io.Closure;
|
||||
import lua.value.LNil;
|
||||
import lua.value.LString;
|
||||
import lua.value.LValue;
|
||||
|
||||
public interface VM {
|
||||
|
||||
// ================ interfaces for performing calls
|
||||
|
||||
/** Push an argument or return value onto the stack
|
||||
*/
|
||||
public void push( LValue value );
|
||||
|
||||
/** Push an int argument or return value onto the stack
|
||||
*/
|
||||
public void push( int value );
|
||||
|
||||
/** Push a double argument or return value onto the stack
|
||||
*/
|
||||
public void push( double value );
|
||||
|
||||
/** Push a boolean argument or return value onto the stack
|
||||
*/
|
||||
public void push( boolean value );
|
||||
|
||||
/** Push a String argument or return value onto the stack
|
||||
*/
|
||||
public void push( String value );
|
||||
|
||||
/**
|
||||
* Create a call frame for a call that has been set up on
|
||||
* the stack. The first value on the stack must be a Closure,
|
||||
* and subsequent values are arguments to the closure.
|
||||
*/
|
||||
public void prepStackCall();
|
||||
|
||||
/**
|
||||
* Execute bytecodes until the current call completes
|
||||
* or the vm yields.
|
||||
*/
|
||||
public void execute();
|
||||
|
||||
/**
|
||||
* Put the closure on the stack with arguments,
|
||||
* then perform the call. Leave return values
|
||||
* on the stack for later querying.
|
||||
*
|
||||
* @param c
|
||||
* @param values
|
||||
*/
|
||||
public void doCall(Closure c, LValue[] values);
|
||||
|
||||
/**
|
||||
* Set the number of results that are expected from the function being called.
|
||||
* (This should be called before prepStackCall)
|
||||
*/
|
||||
public void setExpectedResultCount( int nresults );
|
||||
|
||||
/**
|
||||
* Returns the number of results that are expected by the calling function,
|
||||
* or -1 if the calling function can accept a variable number of results.
|
||||
*/
|
||||
public int getExpectedResultCount();
|
||||
|
||||
/**
|
||||
* Adjust the stack to contain the expected number of results by adjusting
|
||||
* the top.
|
||||
*/
|
||||
public void adjustResults();
|
||||
|
||||
// ================ interfaces for getting arguments when called
|
||||
|
||||
/**
|
||||
* Get the number of argumnets supplied in the call.
|
||||
*/
|
||||
public int getArgCount();
|
||||
|
||||
/**
|
||||
* Get the index-th argument supplied, or NIL if fewer than index were supplied.
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public LValue getArg(int index);
|
||||
|
||||
/**
|
||||
* Get the index-th argument as an int value, or 0 if fewer than index arguments were supplied.
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public int getArgAsInt( int index );
|
||||
|
||||
/**
|
||||
* Get the index-th argument as a double value, or 0 if fewer than index arguments were supplied.
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public double getArgAsDouble( int index );
|
||||
|
||||
/**
|
||||
* Get the index-th argument as a boolean value, or false if fewer than index arguments were supplied.
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public boolean getArgAsBoolean( int index );
|
||||
|
||||
/**
|
||||
* Get the index-th argument as a String value, or "" if fewer than index arguments were supplied.
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public String getArgAsString( int index );
|
||||
|
||||
/**
|
||||
* Get the index-th argument as an LString value, or "" if fewer than index arguments were supplied.
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public LString getArgAsLuaString( int index );
|
||||
|
||||
/** Set top to base in preparation for pushing return values.
|
||||
* Can be used when returning no values.
|
||||
*
|
||||
* Once this is called, calls to getArg() are undefined.
|
||||
*
|
||||
* @see push() to push additional results once result is reset.
|
||||
*/
|
||||
public void setResult();
|
||||
|
||||
/** Convenience utility to set val to stack[base] and top to base + 1
|
||||
* in preparation for returning one value
|
||||
*
|
||||
* Once this is called, calls to getArg() are undefined.
|
||||
*
|
||||
* @param val value to provide as the only result.
|
||||
*/
|
||||
public void setResult(LValue val);
|
||||
|
||||
|
||||
/**
|
||||
* Set up an error result on the stack.
|
||||
* @param value the LValue to return as the first return value
|
||||
* @param message the String error message to supply
|
||||
*/
|
||||
public void setErrorResult(LValue value, String message);
|
||||
|
||||
// ====================== lua Java API =======================
|
||||
|
||||
/**
|
||||
* Raises an error. The message is pushed onto the stack and used as the error message.
|
||||
* It also adds at the beginning of the message the file name and the line number where
|
||||
* the error occurred, if this information is available.
|
||||
*
|
||||
* In the java implementation this throws a RuntimeException, possibly filling
|
||||
* line number information first.
|
||||
*/
|
||||
public void lua_error(String message);
|
||||
|
||||
/**
|
||||
* Run the method on the stack, propagating any error that occurs.
|
||||
* @param nArgs number of arguments on the stack
|
||||
* @param nResults number of results on the stack
|
||||
*/
|
||||
public void lua_call(int nArgs, int nResults);
|
||||
|
||||
/**
|
||||
* Run the method on the stack in protected mode.
|
||||
* @param nArgs number of arguments on the stack
|
||||
* @param nResults number of results on the stack
|
||||
* @return 0 if successful, LUA_ERRMEM if no memory, LUA_ERRRUN for any other error
|
||||
*/
|
||||
public int lua_pcall(int nArgs, int nResults);
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param is InputStream providing the data to be loaded
|
||||
* @param chunkname Name of the chunk to be used in debugging
|
||||
* @return 0 if successful, LUA_ERRMEM if no memory, LUA_ERRSYNTAX for i/o or any other errors
|
||||
*/
|
||||
public int lua_load( InputStream is, String chunkname );
|
||||
|
||||
/**
|
||||
* Get a value on the stack, relative to the top or bottom
|
||||
* @param i index from bottom if 0 or greater, index from top if less than 0
|
||||
* @return
|
||||
*/
|
||||
public LValue lua_tolvalue(int i);
|
||||
|
||||
/**
|
||||
* Pop some number of items off the stack.
|
||||
*/
|
||||
public void lua_pop(int n);
|
||||
}
|
||||
27
src/main/java/lua/value/LNumber.java
Normal file
27
src/main/java/lua/value/LNumber.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package lua.value;
|
||||
|
||||
import lua.Lua;
|
||||
|
||||
abstract
|
||||
public class LNumber extends LValue {
|
||||
|
||||
public static final LString TYPE_NAME = new LString("number");
|
||||
|
||||
/** Compare for equivalence by using lua op comparator */
|
||||
public boolean equals(Object o) {
|
||||
if ( ! ( o instanceof LValue) )
|
||||
return false;
|
||||
LValue v = (LValue) o;
|
||||
return this.luaBinCmpUnknown(Lua.OP_EQ, v );
|
||||
}
|
||||
|
||||
public LString luaGetType() {
|
||||
return TYPE_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false by default for non-LNumbers, but subclasses of LNumber must
|
||||
* override.
|
||||
*/
|
||||
public abstract boolean isInteger();
|
||||
}
|
||||
182
src/main/java/lua/value/LValue.java
Normal file
182
src/main/java/lua/value/LValue.java
Normal file
@@ -0,0 +1,182 @@
|
||||
package lua.value;
|
||||
|
||||
import lua.Lua;
|
||||
import lua.VM;
|
||||
|
||||
abstract
|
||||
public class LValue {
|
||||
|
||||
/** Metatable tag for intercepting table gets */
|
||||
public static final LString TM_INDEX = new LString("__index");
|
||||
|
||||
/** Metatable tag for intercepting table sets */
|
||||
public static final LString TM_NEWINDEX = new LString("__newindex");
|
||||
|
||||
protected static LValue luaUnsupportedOperation() {
|
||||
throw new java.lang.RuntimeException( "not supported" );
|
||||
}
|
||||
|
||||
public String id() {
|
||||
return Integer.toHexString(hashCode());
|
||||
}
|
||||
|
||||
// test if value is true
|
||||
public boolean luaAsBoolean() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Return true if this value can be represented as an "int" */
|
||||
public boolean isInteger() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// perform a lua call, return true if the call is to a lua function, false
|
||||
// if it ran to completion.
|
||||
public boolean luaStackCall(VM vm) {
|
||||
vm.lua_error("attempt to call "+this);
|
||||
return false;
|
||||
}
|
||||
|
||||
// unsupported except for numbers
|
||||
public LValue luaBinOpUnknown(int opcode, LValue lhs) {
|
||||
return luaUnsupportedOperation();
|
||||
}
|
||||
|
||||
// unsupported except for numbers
|
||||
public LValue luaBinOpInteger(int opcode, int m_value) {
|
||||
return luaUnsupportedOperation();
|
||||
}
|
||||
|
||||
// unsupported except for numbers
|
||||
public LValue luaBinOpDouble(int opcode, double m_value) {
|
||||
return luaUnsupportedOperation();
|
||||
}
|
||||
|
||||
// unsupported except for numbers, strings, and == with various combinations of Nil, Boolean, etc.
|
||||
public boolean luaBinCmpUnknown(int opcode, LValue lhs) {
|
||||
if ( opcode == Lua.OP_EQ )
|
||||
return lhs == this;
|
||||
luaUnsupportedOperation();
|
||||
return false;
|
||||
}
|
||||
|
||||
// unsupported except for strings
|
||||
public boolean luaBinCmpString(int opcode, LString rhs) {
|
||||
if ( opcode == Lua.OP_EQ )
|
||||
return false;
|
||||
luaUnsupportedOperation();
|
||||
return false;
|
||||
}
|
||||
|
||||
// unsupported except for numbers
|
||||
public boolean luaBinCmpInteger(int opcode, int rhs) {
|
||||
if ( opcode == Lua.OP_EQ )
|
||||
return false;
|
||||
luaUnsupportedOperation();
|
||||
return false;
|
||||
}
|
||||
|
||||
// unsupported except for numbers
|
||||
public boolean luaBinCmpDouble(int opcode, double rhs) {
|
||||
if ( opcode == Lua.OP_EQ )
|
||||
return false;
|
||||
luaUnsupportedOperation();
|
||||
return false;
|
||||
}
|
||||
|
||||
/** set a value in a table
|
||||
* For non-tables, goes straight to the meta-table.
|
||||
* @param vm the calling vm
|
||||
* @param table the table to operate on
|
||||
* @param the key to set
|
||||
* @param the value to set
|
||||
*/
|
||||
public void luaSetTable(VM vm, LValue table, LValue key, LValue val) {
|
||||
LTable mt = luaGetMetatable();
|
||||
if ( mt != null ) {
|
||||
LValue event = mt.get( TM_NEWINDEX );
|
||||
if ( event != null && event != LNil.NIL ) {
|
||||
event.luaSetTable( vm, table, key, val );
|
||||
return;
|
||||
}
|
||||
}
|
||||
vm.push( LNil.NIL );
|
||||
}
|
||||
|
||||
/** Get a value from a table
|
||||
* @param vm the calling vm
|
||||
* @param table the table from which to get the value
|
||||
* @param key the key to look up
|
||||
*/
|
||||
public void luaGetTable(VM vm, LValue table, LValue key) {
|
||||
LTable mt = luaGetMetatable();
|
||||
if ( mt != null ) {
|
||||
LValue event = mt.get( TM_INDEX );
|
||||
if ( event != null && event != LNil.NIL ) {
|
||||
event.luaGetTable( vm, table, key );
|
||||
return;
|
||||
}
|
||||
}
|
||||
vm.push(LNil.NIL);
|
||||
}
|
||||
|
||||
/** Get the value as a String
|
||||
*/
|
||||
public abstract LString luaAsString();
|
||||
|
||||
/** Override standard toString with lua String conversion by default */
|
||||
public String toString() {
|
||||
return luaAsString().toJavaString();
|
||||
}
|
||||
|
||||
/** Return value as an integer */
|
||||
public int luaAsInt() {
|
||||
luaUnsupportedOperation();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Return value as a double */
|
||||
public double luaAsDouble() {
|
||||
return luaAsInt();
|
||||
}
|
||||
|
||||
/** Arithmetic negative */
|
||||
public LValue luaUnaryMinus() {
|
||||
return luaUnsupportedOperation();
|
||||
}
|
||||
|
||||
/** Built-in opcode LEN, for Strings and Tables */
|
||||
public LValue luaLength() {
|
||||
// TODO: call meta-method TM_LEN here
|
||||
return luaUnsupportedOperation();
|
||||
}
|
||||
|
||||
/** Valid for tables
|
||||
* @param isPairs true to iterate over non-integers as well */
|
||||
public LValue luaPairs(boolean isPairs) {
|
||||
return luaUnsupportedOperation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Valid for all types: get a metatable. Only tables and userdata can have a
|
||||
* different metatable per instance, though, other types are restricted to
|
||||
* one metatable per type.
|
||||
*
|
||||
* Since metatables on non-tables can only be set through Java and not Lua,
|
||||
* this function should be overridden for each value type as necessary.
|
||||
*
|
||||
* @return null if there is no meta-table
|
||||
*/
|
||||
public LTable luaGetMetatable() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Valid for tables */
|
||||
public void luaSetMetatable(LValue metatable) {
|
||||
luaUnsupportedOperation();
|
||||
}
|
||||
|
||||
/** Valid for all types: return the type of this value as an LString */
|
||||
public abstract LString luaGetType();
|
||||
|
||||
}
|
||||
@@ -1,3 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2007 LuaJ. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
package lua.debug;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
92
src/test/java/lua/io/LoadStateTest.java
Normal file
92
src/test/java/lua/io/LoadStateTest.java
Normal file
@@ -0,0 +1,92 @@
|
||||
package lua.io;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import lua.value.LDouble;
|
||||
import lua.value.LInteger;
|
||||
import lua.value.LNumber;
|
||||
|
||||
public class LoadStateTest extends TestCase {
|
||||
double[] DOUBLE_VALUES = {
|
||||
0.0,
|
||||
1.0,
|
||||
2.5,
|
||||
10.0,
|
||||
16.0,
|
||||
16.125,
|
||||
-1.0,
|
||||
2.0,
|
||||
-2.0,
|
||||
-10.0,
|
||||
-0.25,
|
||||
-25,
|
||||
Integer.MAX_VALUE,
|
||||
Integer.MAX_VALUE - 1,
|
||||
(double)Integer.MAX_VALUE + 1.0,
|
||||
Integer.MIN_VALUE,
|
||||
Integer.MIN_VALUE + 1,
|
||||
(double)Integer.MIN_VALUE - 1.0,
|
||||
Double.NEGATIVE_INFINITY,
|
||||
Double.POSITIVE_INFINITY,
|
||||
Double.MAX_VALUE,
|
||||
Double.MAX_VALUE
|
||||
};
|
||||
|
||||
public void testLongBitsToLuaNumber() {
|
||||
for ( int i = 0; i < DOUBLE_VALUES.length; ++i ) {
|
||||
double v = DOUBLE_VALUES[i];
|
||||
long bits = Double.doubleToLongBits( v );
|
||||
LNumber luaNumber = LoadState.longBitsToLuaNumber( bits );
|
||||
|
||||
assertEquals( v, luaNumber.luaAsDouble() );
|
||||
|
||||
if ( v != Integer.MIN_VALUE ) {
|
||||
// Special case of MIN_VALUE is probably not worth dealing with.
|
||||
// (Unlike zero, which is also a special case but much more common.)
|
||||
assertEquals( "Value "+v+" (at index "+i+") can be represented as integer but was not",
|
||||
luaNumber instanceof LInteger, v == (double)( (int) v ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private LNumber simpleBitsToLuaNumber( long bits ) {
|
||||
double value = Double.longBitsToDouble( bits );
|
||||
int valueAsInt = (int) value;
|
||||
|
||||
if ( value == (double) valueAsInt ) {
|
||||
return new LInteger( valueAsInt );
|
||||
} else {
|
||||
return new LDouble( value );
|
||||
}
|
||||
}
|
||||
|
||||
public void testLongBitsToLuaNumberSpeed() {
|
||||
long[] BITS = new long[ 500000 ];
|
||||
Random r = new Random();
|
||||
|
||||
for ( int i = 0; i < DOUBLE_VALUES.length; ++i ) {
|
||||
BITS[i] = Double.doubleToLongBits( DOUBLE_VALUES[i] );
|
||||
}
|
||||
for ( int i = DOUBLE_VALUES.length; i < BITS.length; i += 2 ) {
|
||||
BITS[i ] = r.nextLong();
|
||||
BITS[i+1] = Double.doubleToLongBits( r.nextDouble() );
|
||||
}
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
for ( int j = 0; j < BITS.length; ++j ) {
|
||||
LoadState.longBitsToLuaNumber( BITS[j] );
|
||||
}
|
||||
long endTime = System.currentTimeMillis();
|
||||
long complexConversionTime = endTime - startTime;
|
||||
|
||||
startTime = System.currentTimeMillis();
|
||||
for ( int j = 0; j < BITS.length; ++j ) {
|
||||
simpleBitsToLuaNumber( BITS[j] );
|
||||
}
|
||||
endTime = System.currentTimeMillis();
|
||||
long simpleConversionTime = endTime - startTime;
|
||||
|
||||
assertTrue( complexConversionTime < simpleConversionTime );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user