org.luaj.vm.LuaState Maven / Gradle / Ivy
Show all versions of luaj-j2me Show documentation
/*******************************************************************************
* 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 org.luaj.vm;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Stack;
import org.luaj.lib.BaseLib;
import org.luaj.lib.CoroutineLib;
import org.luaj.lib.MathLib;
import org.luaj.lib.PackageLib;
import org.luaj.lib.StringLib;
import org.luaj.lib.TableLib;
/**
*
* LuaState
*
*
* typedef struct LuaState;
*
*
*
* Opaque structure that keeps the whole state of a Lua interpreter. The Lua
* library is fully reentrant: it has no global variables. All information about
* a state is kept in this structure.
*
*
*
* Here we list all functions and types from the C API in alphabetical
* order. Each function has an indicator like this: [-o, +p,
* x]
*
*
* The first field, o
, is how many elements the function pops
* from the stack. The second field, p
, is how many elements the
* function pushes onto the stack. (Any function always pushes its results after
* popping its arguments.) A field in the form x|y
means the
* function may push (or pop) x
or y
elements,
* depending on the situation; an interrogation mark '?
' means
* that we cannot know how many elements the function pops/pushes by looking
* only at its arguments (e.g., they may depend on what is on the stack). The
* third field, x
, tells whether the function may throw errors: '-
'
* means the function never throws any error; 'm
' means the
* function may throw an error only due to not enough memory; 'e
'
* means the function may throw other kinds of errors; 'v
'
* means the function may throw an error on purpose.
*
*
*/
public class LuaState extends Lua {
/* thread status; 0 is OK */
public static final int LUA_YIELD = 1;
public static final int LUA_ERRRUN = 2;
public static final int LUA_ERRSYNTAX = 3;
public static final int LUA_ERRMEM = 4;
public static final int LUA_ERRERR = 5;
private static final int LUA_MINSTACK = 20;
private static final int LUA_MINCALLS = 10;
private static final int MAXTAGLOOP = 100;
// hook function values
private static final int LUA_HOOKCALL = 0;
private static final int LUA_HOOKRET = 1;
private static final int LUA_HOOKLINE = 2;
private static final int LUA_HOOKCOUNT = 3;
private static final int LUA_HOOKTAILRET = 4;
public static final int LUA_MASKCALL = (1 << LUA_HOOKCALL);
public static final int LUA_MASKRET = (1 << LUA_HOOKRET);
public static final int LUA_MASKLINE = (1 << LUA_HOOKLINE);
public int base = 0;
public int top = 0;
protected int nresults = -1;
public LValue[] stack = new LValue[LUA_MINSTACK];
public int cc = -1;
public CallInfo[] calls;
protected Stack upvals = new Stack();
protected LValue errfunc;
static LuaState mainState;
public LTable _G;
// debug hooks - these MUST NOT be initialized,
// so that a later obfuscation step can decide to remove them.
private boolean hooksenabled,inhook;
private int hookmask;
private int hookcount;
private LFunction hookfunc;
private int hookincr;
private int hookline,hookcc;
protected void debugHooks(int pc) {}
protected void debugAssert(boolean b) {}
// ------------------- constructors ---------------------
/**
* Creates a new, independent LuaState instance. [-0, +0,
* -]
*
*
* Returns NULL
if cannot create the state (due to lack of
* memory). The argument f
is the allocator function; Lua
* does all memory allocation for this state through this function. The
* second argument, ud
, is an opaque pointer that Lua simply
* passes to the allocator in every call.
*/
protected LuaState() {
this ( new LTable() );
mainState = this;
}
/**
* Create a LuaState with a specific global environment. Used by LThread.
*
* @param globals the LTable to use as globals for this LuaState
*/
LuaState(LTable globals) {
_G = globals;
calls = new CallInfo[LUA_MINCALLS];
for ( int i=0; i= calls.length ) {
CallInfo[] newcalls = new CallInfo[ calls.length * 2 ];
System.arraycopy( calls, 0, newcalls, 0, cc+1 );
for ( int i=calls.length, n=newcalls.length; i=cb; )
exec();
}
/**
* Put the closure on the stack with arguments,
* then perform the call. Leave return values
* on the stack for later querying.
*
* @param c
* @param values
*/
public void doCall( LClosure c, LValue[] args ) {
settop(0);
pushlvalue( c );
for ( int i=0, n=(args!=null? args.length: 0); i=0? calls[cc].base: 0);
}
/**
* Invoke a LFunction being called via prepStackCall()
* @param javaFunction
*/
public void invokeJavaFunction(LFunction javaFunction) {
++base;
// call hook
if ( hooksenabled && !inhook ) {
debugCallHooks( );
}
int nactual = javaFunction.invoke(this);
// call hook
if ( hooksenabled && !inhook ) {
debugReturnHooks( );
}
if (nactual < 0)
nactual = top - base;
System.arraycopy(stack, top-nactual, stack, --base, nactual);
luaV_settop_fillabove( base+nactual );
}
// ================== error processing =================
/**
* Calls a function. [-(nargs + 1), +nresults, e]
*
*
*
* To call a function you must use the following protocol: first, the
* function to be called is pushed onto the stack; then, the arguments to
* the function are pushed in direct order; that is, the first argument is
* pushed first. Finally you call lua_call
;
* nargs
is the number of arguments that you pushed onto the
* stack. All arguments and the function value are popped from the stack
* when the function is called. The function results are pushed onto the
* stack when the function returns. The number of results is adjusted to
* nresults
, unless nresults
is LUA_MULTRET
. In this case,
* all results from the function are pushed. Lua takes care that
* the returned values fit into the stack space. The function results are
* pushed onto the stack in direct order (the first result is pushed first),
* so that after the call the last result is on the top of the stack.
*
*
*
* Any error inside the called function is propagated upwards (with a
* longjmp
).
*
*
*
* The following example shows how the host program may do the equivalent to
* this Lua code:
*
*
* a = f("how", t.x, 14)
*
*
*
* Here it is in C:
*
*
* lua_getfield(L, LUA_GLOBALSINDEX, "f"); // function to be called
* lua_pushstring(L, "how"); // 1st argument
* lua_getfield(L, LUA_GLOBALSINDEX, "t"); // table to be indexed
* lua_getfield(L, -1, "x"); // push result of t.x (2nd arg)
* lua_remove(L, -2); // remove 't' from the stack
* lua_pushinteger(L, 14); // 3rd argument
* lua_call(L, 3, 1); // call 'f' with 3 arguments and 1 result
* lua_setfield(L, LUA_GLOBALSINDEX, "a"); // set global 'a'
*
*
*
* Note that the code above is "balanced": at its end, the stack is back to
* its original configuration. This is considered good programming practice.
*/
public void call( int nargs, int nreturns ) {
// save stack state
int oldbase = base;
int oldcc = cc;
try {
// rb is base of new call frame
int rb = this.base = top - 1 - nargs;
// make or set up the call
this.nresults = nreturns;
if (this.stack[base].luaStackCall(this)) {
// call hook
if ( hooksenabled && ! inhook ) {
debugCallHooks( );
}
// call was set up on the stack,
// we still have to execute it
execute();
}
// adjustTop only for case when call was completed
// and number of args > 0. If call completed but
// c == 0, leave top to point to end of results
if (nreturns >= 0)
luaV_adjusttop(rb + nreturns);
} finally {
this.base = oldbase;
while ( this.cc > oldcc )
calls[cc--].closure = null;
}
}
/**
* Calls a function in protected mode. [-(nargs + 1),
* +(nresults|1), -]
*
*
*
* Both nargs
and nresults
have the same
* meaning as in lua_call
. If there
* are no errors during the call, lua_pcall
* behaves exactly like lua_call
.
* However, if there is any error, lua_pcall
* catches it, pushes a single value on the stack (the error message), and
* returns an error code. Like lua_call
,
* lua_pcall
always removes the
* function and its arguments from the stack.
*
*
* The lua_pcall
function returns
* 0 in case of success or one of the following error codes (defined in
* lua.h
):
*
*
*
* LUA_ERRRUN
: a
* runtime error.
*
* LUA_ERRMEM
:
* memory allocation error. For such errors, Lua does not call the error
* handler function.
*
* LUA_ERRERR
:
* error while running the error handler function.
*
*
*/
public int pcall( int nargs, int nreturns ) {
// save stack state
int oldtop = top;
int oldbase = base;
int oldcc = cc;
try {
// rb is base of new call frame
int rb = this.base = top - 1 - nargs;
// make or set up the call
this.nresults = nreturns;
if (this.stack[base].luaStackCall(this)) {
// call hook
if ( hooksenabled ) {
debugCallHooks( );
}
// call was set up on the stack,
// we still have to execute it
execute();
}
// adjustTop only for case when call was completed
// and number of args > 0. If call completed but
// c == 0, leave top to point to end of results
if (nreturns >= 0)
luaV_adjusttop(rb + nreturns);
// restore base
this.base = oldbase;
return 0;
} catch ( Throwable t ) {
this.base = oldbase;
while ( this.cc > oldcc )
calls[cc--].closure = null;
closeUpVals(oldtop); /* close eventual pending closures */
String s = t.getMessage();
resettop();
if ( s != null )
pushstring( s );
else
pushnil();
return (t instanceof OutOfMemoryError? LUA_ERRMEM: LUA_ERRRUN);
}
}
/**
* Calls a function in protected mode with an error function.
* [-(nargs + 1), +(nresults|1), -]
*
*
*
* Both nargs
and nresults
have the same
* meaning as in lua_call
. If there
* are no errors during the call, lua_xpcall
* behaves exactly like lua_call
.
* However, if there is any error, lua_xpcall
* catches it, pushes a single value on the stack (the error message), and
* tries to call the supplied error function.
*
*
In case of runtime errors, this function
* will be called with the error message and its return value will be the
* message returned on the stack by lua_pcall
.
*
*
* Typically, the error handler function is used to add more debug
* information to the error message, such as a stack traceback. Such
* information cannot be gathered after the return of lua_pcall
,
* since by then the stack has unwound.
*
*
* The return values for
* lua_xpcall
are the same as those for
* lua_pcall
*/
public int xpcall( int nargs, int nreturns, LValue errfunc ) {
LValue preverrfunc = this.errfunc;
try {
this.errfunc = errfunc;
return pcall( nargs, nreturns );
} finally {
this.errfunc = preverrfunc;
}
}
/**
* Loads a Lua chunk. [-0, +1, -]
*
*
* If there are no errors, lua_load
* pushes the compiled chunk as a Lua function on top of the stack.
* Otherwise, it pushes an error message. The return values of lua_load
are:
*
*
*
* - 0: no errors;
*
* LUA_ERRSYNTAX
:
* syntax error during pre-compilation;
*
* LUA_ERRMEM
:
* memory allocation error.
*
*
*
*
* This function only loads a chunk; it does not run it.
*
*
*
* lua_load
automatically detects
* whether the chunk is text or binary, and loads it accordingly (see
* program luac
).
*
*
*
* The lua_load
function uses a
* user-supplied reader
function to read the chunk (see lua_Reader
). The
* data
argument is an opaque value passed to the reader
* function.
*
*
*
* The chunkname
argument gives a name to the chunk, which is
* used for error messages and in debug information (see §3.8).
*/
public int load( InputStream is, String chunkname ) {
try {
LPrototype p = LoadState.undump(this, is, chunkname );
pushlvalue( p.newClosure( _G ) );
return 0;
} catch ( Throwable t ) {
pushstring( t.getMessage() );
return (t instanceof OutOfMemoryError? LUA_ERRMEM: LUA_ERRSYNTAX);
}
}
// ================ execute instructions
private LValue RKBC(LValue[] k, int bc) {
return LuaState.ISK(bc) ?
k[LuaState.INDEXK(bc)]:
stack[base + bc];
}
private LValue GETARG_RKB(LValue[] k, int i) {
return RKBC(k, GETARG_B(i));
}
private LValue GETARG_RKC(LValue[] k, int i) {
return RKBC(k, GETARG_C(i));
}
private final void stackClear(int startIndex, int endIndex) {
for (; startIndex < endIndex; startIndex++) {
stack[startIndex] = LNil.NIL;
}
}
/** execute instructions up to a yield, return, or call */
public void exec() {
if ( cc < 0 )
return;
int i, a, b, c, o, n, cb;
LValue rkb, rkc, nvarargs, key, val;
LValue i0, step, idx, limit, init, table;
boolean back, body;
LPrototype proto;
LClosure newClosure;
// reload values from the current call frame
// into local variables
CallInfo ci = calls[cc];
LClosure cl = ci.closure;
LPrototype p = cl.p;
int[] code = p.code;
LValue[] k = p.k;
this.base = ci.base;
// loop until a return instruction is processed,
// or the vm yields
while (true) {
debugAssert( ci == calls[cc] );
// sync up top
ci.top = top;
// print out next bytecode
// Print.printState(this, base, top, base+cl.p.maxstacksize, cl, ci.pc);
// allow debug hooks a chance to operate
debugHooks( ci.pc );
// advance program counter
i = code[ci.pc++];
// call hooks after pc is advanced
if ( hooksenabled && ! inhook ) {
debugBytecodeHooks( ci.pc-1 );
}
// get opcode and first arg
o = (i >> POS_OP) & MAX_OP;
a = (i >> POS_A) & MAXARG_A;
switch (o) {
case LuaState.OP_MOVE: {
b = LuaState.GETARG_B(i);
this.stack[base + a] = this.stack[base + b];
continue;
}
case LuaState.OP_LOADK: {
b = LuaState.GETARG_Bx(i);
this.stack[base + a] = k[b];
continue;
}
case LuaState.OP_LOADBOOL: {
b = LuaState.GETARG_B(i);
c = LuaState.GETARG_C(i);
this.stack[base + a] = (b != 0 ? LBoolean.TRUE : LBoolean.FALSE);
if (c != 0)
ci.pc++; /* skip next instruction (if C) */
continue;
}
case LuaState.OP_LOADNIL: {
b = LuaState.GETARG_B(i);
do {
this.stack[base + b] = LNil.NIL;
} while ((--b) >= a);
continue;
}
case LuaState.OP_GETUPVAL: {
b = LuaState.GETARG_B(i);
this.stack[base + a] = cl.upVals[b].getValue();
continue;
}
case LuaState.OP_GETGLOBAL: {
b = LuaState.GETARG_Bx(i);
key = k[b];
table = cl.env;
val = this.luaV_gettable(table, key);
this.stack[base + a] = val;
continue;
}
case LuaState.OP_GETTABLE: {
b = LuaState.GETARG_B(i);
key = GETARG_RKC(k, i);
table = this.stack[base + b];
val = this.luaV_gettable(table, key);
this.stack[base + a] = val;
continue;
}
case LuaState.OP_SETGLOBAL: {
b = LuaState.GETARG_Bx(i);
key = k[b];
val = this.stack[base + a];
table = cl.env;
this.luaV_settable(table, key, val);
continue;
}
case LuaState.OP_SETUPVAL: {
b = LuaState.GETARG_B(i);
cl.upVals[b].setValue( this.stack[base + a] );
continue;
}
case LuaState.OP_SETTABLE: {
key = GETARG_RKB(k, i);
val = GETARG_RKC(k, i);
table = this.stack[base + a];
this.luaV_settable(table, key, val);
continue;
}
case LuaState.OP_NEWTABLE: {
b = LuaState.GETARG_B(i);
c = LuaState.GETARG_C(i);
this.stack[base + a] = new LTable(b, c);
continue;
}
case LuaState.OP_SELF: {
rkb = stack[base + GETARG_B(i)];
rkc = GETARG_RKC(k, i);
val = this.luaV_gettable(rkb, rkc);
this.stack[base + a] = val;
this.stack[base + a + 1] = rkb;
continue;
}
case LuaState.OP_ADD:
case LuaState.OP_SUB:
case LuaState.OP_MUL:
case LuaState.OP_DIV:
case LuaState.OP_MOD:
case LuaState.OP_POW: {
rkb = GETARG_RKB(k, i);
rkc = GETARG_RKC(k, i);
this.stack[base + a] = rkc.luaBinOpUnknown(o, rkb);
continue;
}
case LuaState.OP_UNM: {
rkb = GETARG_RKB(k, i);
this.stack[base + a] = rkb.luaUnaryMinus();
continue;
}
case LuaState.OP_NOT: {
rkb = GETARG_RKB(k, i);
this.stack[base + a] = (!rkb.toJavaBoolean() ? LBoolean.TRUE
: LBoolean.FALSE);
continue;
}
case LuaState.OP_LEN: {
rkb = GETARG_RKB(k, i);
this.stack[base + a] = LInteger.valueOf( rkb.luaLength() );
continue;
}
case LuaState.OP_CONCAT: {
b = LuaState.GETARG_B(i);
c = LuaState.GETARG_C(i);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int j = b, l = 0; j <= c; j++, l++) {
this.stack[base + j].luaConcatTo( baos );
}
this.stack[base + a] = new LString( baos.toByteArray() );
continue;
}
case LuaState.OP_JMP: {
ci.pc += LuaState.GETARG_sBx(i);
continue;
}
case LuaState.OP_EQ:
case LuaState.OP_LT:
case LuaState.OP_LE: {
rkb = GETARG_RKB(k, i);
rkc = GETARG_RKC(k, i);
boolean test = rkc.luaBinCmpUnknown(o, rkb);
if (test == (a == 0))
ci.pc++;
continue;
}
case LuaState.OP_TEST: {
c = LuaState.GETARG_C(i);
if (this.stack[base + a].toJavaBoolean() != (c != 0))
ci.pc++;
continue;
}
case LuaState.OP_TESTSET: {
rkb = stack[base + GETARG_B(i)];
c = LuaState.GETARG_C(i);
if (rkb.toJavaBoolean() != (c != 0))
ci.pc++;
else
this.stack[base + a] = rkb;
continue;
}
case LuaState.OP_CALL: {
// ra is base of new call frame
this.base += a;
// number of args
b = LuaState.GETARG_B(i);
if (b != 0) // else use previous instruction set top
luaV_settop_fillabove( base + b );
// number of return values we need
n = LuaState.GETARG_C(i) - 1;
// make or set up the call
this.nresults = n;
if (this.stack[base].luaStackCall(this)) {
// call hook
if ( hooksenabled && ! inhook ) {
debugCallHooks( );
}
return;
}
// adjustTop only for case when call was completed
// and number of args > 0. If call completed but
// c == 0, leave top to point to end of results
if (n >= 0)
luaV_adjusttop(base + n);
// restore base
base = ci.base;
continue;
}
case LuaState.OP_TAILCALL: {
// return hook
if ( hooksenabled && ! inhook ) {
debugTailReturnHooks( );
}
// close up values
closeUpVals(base);
// copy down the frame before calling!
// number of args (including the function)
b = LuaState.GETARG_B(i);
if (b != 0) // else use previous instruction set top
luaV_settop_fillabove( base + a + b );
else
b = top - (base + a);
// number of return values we need
n = ci.nresults;
// copy call + all args, discard current frame
System.arraycopy(stack, base + a, stack, ci.resultbase, b);
this.base = ci.resultbase;
luaV_settop_fillabove( base + b );
this.nresults = n;
calls[cc--].closure = null;
// make or set up the call
try {
if (this.stack[base].luaStackCall(this)) {
// call hook
if ( hooksenabled && ! inhook ) {
debugCallHooks( );
}
return;
}
} catch (LuaErrorException e) {
// in case of lua error, we need to restore cc so that
// the debug can get the correct location where the error
// occured.
cc++;
throw e;
}
// adjustTop only for case when call was completed
// and number of args > 0. If call completed but
// c == 0, leave top to point to end of results
if (n >= 0)
luaV_adjusttop(base + n);
// force restore of base, etc.
return;
}
case LuaState.OP_RETURN: {
// return hook
if ( hooksenabled && ! inhook ) {
debugReturnHooks( );
}
// close up values
closeUpVals( base );
// number of return vals to return
b = LuaState.GETARG_B(i) - 1;
if (b >= 0) // else use previous instruction set top
luaV_settop_fillabove( base + a + b );
else
b = top - (base + a);
// number to copy down
System.arraycopy(stack, base + a, stack, ci.resultbase, b);
debugAssert( ci.resultbase + b <= top );
luaV_settop_fillabove( ci.resultbase + b );
// adjust results to what caller expected
if (ci.nresults >= 0)
luaV_adjusttop(ci.resultbase + ci.nresults);
// pop the call stack
calls[cc--].closure = null;
// force a reload of the calling context
return;
}
case LuaState.OP_FORLOOP: {
i0 = this.stack[base + a];
step = this.stack[base + a + 2];
idx = step.luaBinOpUnknown(Lua.OP_ADD, i0);
limit = this.stack[base + a + 1];
back = step.luaBinCmpInteger(Lua.OP_LT, 0);
body = (back ? idx.luaBinCmpUnknown(Lua.OP_LE, limit) : limit
.luaBinCmpUnknown(Lua.OP_LE, idx));
if (body) {
this.stack[base + a] = idx;
this.stack[base + a + 3] = idx;
ci.pc += LuaState.GETARG_sBx(i);
}
continue;
}
case LuaState.OP_FORPREP: {
init = this.stack[base + a].luaToNumber();
limit = this.stack[base + a + 1].luaToNumber();
step = this.stack[base + a + 2].luaToNumber();
if ( init.isNil() ) error("'for' initial value must be a number");
if ( limit.isNil() ) error("'for' limit must be a number");
if ( step.isNil() ) error("'for' step must be a number");
this.stack[base + a] = step.luaBinOpUnknown(Lua.OP_SUB, init);
this.stack[base + a + 1] = limit;
this.stack[base + a + 2] = step;
b = LuaState.GETARG_sBx(i);
ci.pc += b;
continue;
}
case LuaState.OP_TFORLOOP: {
cb = base + a + 3; /* call base */
base = cb;
System.arraycopy(this.stack, cb-3, this.stack, cb, 3);
luaV_settop_fillabove( cb + 3 );
// call the iterator
c = LuaState.GETARG_C(i);
this.nresults = c;
if (this.stack[cb].luaStackCall(this))
execute();
base = ci.base;
luaV_adjusttop( cb + c );
// test for continuation
if (!this.stack[cb].isNil() ) { // continue?
this.stack[cb-1] = this.stack[cb]; // save control variable
} else {
ci.pc++; // skip over jump
}
continue;
}
case LuaState.OP_SETLIST: {
b = LuaState.GETARG_B(i);
c = LuaState.GETARG_C(i);
int listBase = base + a;
if (b == 0) {
b = top - listBase - 1;
}
if (c == 0) {
c = code[ci.pc++];
}
int offset = (c-1) * LFIELDS_PER_FLUSH;
LTable tbl = (LTable) this.stack[base + a];
tbl.arrayPresize( offset + b );
for (int j=1; j<=b; j++) {
tbl.put(offset+j, stack[listBase + j]);
}
continue;
}
case LuaState.OP_CLOSE: {
closeUpVals( base + a ); // close upvals higher in the stack than position a
continue;
}
case LuaState.OP_CLOSURE: {
b = LuaState.GETARG_Bx(i);
proto = cl.p.p[b];
newClosure = proto.newClosure(cl.env);
for (int j = 0; j < newClosure.upVals.length; j++, ci.pc++) {
i = code[ci.pc];
o = LuaState.GET_OPCODE(i);
b = LuaState.GETARG_B(i);
if (o == LuaState.OP_GETUPVAL) {
newClosure.upVals[j] = cl.upVals[b];
} else if (o == LuaState.OP_MOVE) {
newClosure.upVals[j] = findUpVal( base + b );
} else {
throw new java.lang.IllegalArgumentException(
"bad opcode: " + o);
}
}
this.stack[base + a] = newClosure;
continue;
}
case LuaState.OP_VARARG: {
// figure out how many args to copy
b = LuaState.GETARG_B(i) - 1;
nvarargs = this.stack[base - 1];
n = nvarargs.toJavaInt();
if (b == LuaState.LUA_MULTRET) {
b = n; // use entire varargs supplied
luaV_settop_fillabove( base + a + b );
}
// copy args up to call stack area
checkstack(a+b);
for (int j = 0; j < b; j++)
this.stack[base + a + j] = (j < n ?
this.stack[base - n + j - 1]
: LNil.NIL);
continue;
}
default: break;
}
}
}
public UpVal findUpVal( int target ) {
UpVal up;
int i;
for ( i = this.upvals.size() - 1; i >= 0; --i ) {
up = (UpVal) this.upvals.elementAt( i );
if ( up.state == this && up.position == target ) {
return up;
} else if ( up.position < target ) {
break;
}
}
up = new UpVal( this, target );
this.upvals.insertElementAt( up, i + 1 );
return up;
}
public void closeUpVals( int limit ) {
while ( !upvals.empty() && ( (UpVal) this.upvals.lastElement() ).close( limit ) ) {
this.upvals.pop();
}
}
public CallInfo getStackFrame(int callStackDepth) {
return calls[cc-callStackDepth];
}
private void indexError(LValue nontable) {
error( "attempt to index ? (a "+nontable.luaGetTypeName()+" value)" );
}
public static LValue luaV_getmetafield(LValue t, LString tag) {
LTable mt = t.luaGetMetatable();
if ( mt == null )
return null;
LValue h = mt.get(tag);
return h.isNil()? null: h;
}
/** Get a key from a table using full metatable processing */
public LValue luaV_gettable(LValue table, LValue key) {
LValue h=LNil.NIL,t=table;
for ( int loop=0; loop newTop)
this.stack[--top] = LNil.NIL;
}
/** Move top down, filling from above */
private void luaV_settop_fillabove(int newTop) {
while (top > newTop)
this.stack[--top] = LNil.NIL;
top = newTop;
}
//===============================================================
// Lua Java API
//===============================================================
/** @deprecated: use LPrototype.source or LPrototype.sourceshort() instead */
public String getSourceFileName(LString s) {
return getSourceFileName(s.toJavaString());
}
/** @deprecated: use LPrototype.source or LPrototype.sourceshort() instead */
protected String getSourceFileName(String s) {
return LoadState.getSourceName(s);
}
/**
* Get the file line number info for a particular call frame.
* @param cindex index into call stack, or -1 to get first lua location
* @return
*/
protected String getFileLine(int level) {
LClosure c = null;
for (int j=cc; j>=0; --j) {
CallInfo ci = calls[j];
LFunction f = ci.currentfunc(this);
if ( f != null && (!f.isClosure() || f!=c) ) {
if ( level != -1 && (level--) <= 0 ) {
return "[Java]: "+f.toString();
}
}
c = ci.closure;
if ( (level--) <= 0 ) {
return c.p.sourceshort()+":"+ci.currentline();
}
}
return "";
}
/**
* 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 LuaErrorException
* after filling line number information first when level > 0.
*/
public void error(String message, int level) {
throw new LuaErrorException( this, message, level );
}
/**
* Raises an error with the default level.
*/
public void error(String message) {
throw new LuaErrorException( this, message, -1 );
}
/**
*
* Ensures that there are at least extra
free stack slots in
* the stack. [-0, +0, -]
*
*
* It returns false if it cannot grow the stack to that size. This function
* never shrinks the stack; if the stack is already larger than the new
* size, it is left unchanged.
*
*/
public void checkstack(int extra) {
if ( top + extra >= stack.length ) {
int n = Math.max( top + extra + LUA_MINSTACK, stack.length * 2 );
LValue[] s = new LValue[n];
System.arraycopy(stack, 0, s, 0, stack.length);
stack = s;
}
}
/**
* Dereference a tables field. [-0, +1, e]
*
*
* Pushes onto the stack the value t[k]
, where
* t
is the value at the given valid index. As in Lua, this
* function may trigger a metamethod for the "index" event (see §2.8).
*
*/
public void getfield(int index, LString k) {
pushlvalue( this.luaV_gettable(topointer(index), k) );
}
/**
* Look up a global value. [-0, +1, e]
*
*
* Pushes onto the stack the value of the global name
. It is
* defined as a macro:
*
*
* #define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, s)
*
*
*/
public void getglobal(String s) {
pushlvalue( this.luaV_gettable(_G, new LString(s)) );
}
/**
* Get a value's metatable. [-0, +(0|1), -]
*
*
* Pushes onto the stack the metatable of the value at the given acceptable
* index. If the index is not valid, or if the value does not have a
* metatable, the function returns false and pushes nothing on the stack.
*
* @return true if the metatable was pushed onto the stack, false otherwise
*/
public boolean getmetatable(int index) {
LTable mt = topointer(index).luaGetMetatable();
if ( mt != null ) {
pushlvalue( mt );
return true;
}
return false;
}
/**
* Insert the top item somewhere in the stack. [-1, +1,
* -]
*
*
* Moves the top element into the given valid index, shifting up the
* elements above this index to open space. Cannot be called with a
* pseudo-index, because a pseudo-index is not an actual stack position.
*
*/
public void insert(int index) {
int ai = index2adr(index);
LValue v = stack[top-1];
System.arraycopy(stack, ai, stack, ai+1, top-ai-1);
stack[ai] = v;
}
/**
* Test if a value is boolean. [-0, +0, -]
*
*
* Returns 1 if the value at the given acceptable index has type boolean,
* and 0 otherwise.
*
*/
public boolean isboolean(int index) {
return type(index) == Lua.LUA_TBOOLEAN;
}
/**
* Test if a value is a function. [-0, +0, -]
*
*
* Returns true if the value at the given acceptable index is a function
* (either C or Lua), and false otherwise.
*
*/
public boolean isfunction(int index) {
return type(index) == Lua.LUA_TFUNCTION;
}
/**
* Test if a value is nil [-0, +0, -]
*
*
* Returns 1 if the value at the given acceptable index is nil, and
* 0 otherwise.
*/
public boolean isnil(int index) {
return topointer(index).isNil();
}
/**
* Test if a value is nil or not valid [-0, +0,
* -]
*
*
* Returns 1 if the the given acceptable index is not valid (that is, it
* refers to an element outside the current stack) or if the value at this
* index is nil, and 0 otherwise.
*/
public boolean isnoneornil(int index) {
Object v = topointer(index);
return v == null || v == LNil.NIL;
}
/**
* Test if a value is a number [-0, +0, -]
*
*
* Returns 1 if the value at the given acceptable index is a number or a
* string convertible to a number, and 0 otherwise.
*/
public boolean isnumber(int index) {
return ! tolnumber(index).isNil();
}
/**
* Convert a value to an LNumber[-0, +0, -]
*
*
* Returns an LNumber if the value at the given acceptable index is a number or a
* string convertible to a number, and LNil.NIL otherwise.
*/
public LValue tolnumber(int index) {
return topointer(index).luaToNumber();
}
/**
* Test if a value is a string [-0, +0, m]
*
*
* Returns 1 if the value at the given acceptable index is a string or a
* number (which is always convertible to a string), and 0 otherwise.
*/
public boolean isstring(int index) {
return topointer(index).isString();
}
/**
* Test if a value is a table [-0, +0, -]
*
*
* Returns 1 if the value at the given acceptable index is a table, and
* 0 otherwise.
*/
public boolean istable(int index) {
return topointer(index).isTable();
}
/**
* Test if a value is a thread [-0, +0, -]
*
*
* Returns 1 if the value at the given acceptable index is a thread, and
* 0 otherwise.
*/
public boolean isthread(int index) {
return type(index) == Lua.LUA_TTHREAD;
}
/**
* Test if a value is a userdata [-0, +0, -]
*
*
* Returns 1 if the value at the given acceptable index is a userdata
* (either full or light), and 0 otherwise.
*/
public boolean isuserdata(int index) {
return type(index) == Lua.LUA_TUSERDATA;
}
/**
* Pops n
elements from the stack. [-n,
* +0, -]
*/
public void pop(int n) {
for ( int i=0; i[-0, +1,
* m]
*/
public void pushlvalue(LValue value) {
if ( value == null )
throw new java.lang.IllegalArgumentException("stack values cannot be null");
try {
stack[top] = value;
} catch ( java.lang.ArrayIndexOutOfBoundsException aiobe ) {
checkstack( LUA_MINSTACK );
stack[top] = value;
} finally {
++top;
}
}
/**
* Pushes a boolean value with value b
onto the stack. [-0, +1, -]
*
*/
public void pushboolean(boolean b) {
pushlvalue( LBoolean.valueOf(b) );
}
/**
* Pushes a number with value n
onto the stack. [-0, +1, -]
*/
public void pushinteger(int n) {
pushlvalue( LInteger.valueOf(n) );
}
/**
* Pushes a function onto the stack. [-0, +1,
* m]
*
*
* This function receives an LFunction and pushes onto the
* stack a Lua value of type function
that, when called,
* invokes the corresponding function.
*
*
*
* Any function to be registered in Lua must follow the correct protocol to
* receive its parameters and return its results
* @see LFunction
*/
public void pushfunction(LFunction f) {
pushlvalue( f );
}
/**
* Push an LString onto the stack. [-0, +1,
* m]
*/
public void pushlstring(LString s) {
pushlvalue(s);
}
/**
* Push string bytes onto the stack as a string. [-0, +1,
* m]
*
* Pushes the string pointed to by s
with size
* len
onto the stack. Lua makes (or reuses) an internal copy
* of the given string, so the memory at s
can be freed or
* reused immediately after the function returns. The string can contain
* embedded zeros.
*/
public void pushlstring(byte[] bytes, int offset, int length) {
pushlvalue(new LString(bytes, offset, length));
}
/**
* Push string bytes onto the stack as a string. [-0, +1,
* m]
*
* Pushes the bytes in byteArray onto the stack as a lua string.
*/
public void pushlstring(byte[] byteArray) {
pushlstring(byteArray, 0, byteArray.length);
}
/**
* Pushes a nil value onto the stack. [-0, +1, -]
*
*/
public void pushnil() {
pushlvalue(LNil.NIL);
}
/**
* Pushes a number with value d
onto the stack. [-0, +1, -]
*
*/
public void pushnumber(double d) {
pushlvalue(LDouble.numberOf(d));
}
/**
* Push a String onto the stack. [-0, +1, m]
*
*
* Pushes the String s
onto the stack. Lua makes (or reuses)
* an internal copy of the given string, so the memory at s
* can be freed or reused immediately after the function returns. The string
* cannot contain embedded zeros; it is assumed to end at the first zero.
*/
public void pushstring(String s) {
if ( s == null )
pushnil();
else
pushlstring( LString.valueOf(s) );
}
/**
* Push a value from the stack onto the stack. [-0, +1,
* -]
*
*
* Pushes a copy of the element at the given valid index onto the stack.
*/
public void pushvalue(int index) {
pushlvalue(topointer(index));
}
/**
* Do a integer-key table get without metadata calls. [-0, +1, -]
*
*
* Pushes onto the stack the value t[n]
, where
* t
is the value at the given valid index. The access is
* raw; that is, it does not invoke metamethods.
* @deprecated should get the table and do a raw get instead
*/
public void rawgeti(int index, int n) {
pushlvalue( totable(index).get(n) );
}
/**
* Remove an element from the stack. [-1, +0, -]
*
*
* Removes the element at the given valid index, shifting down the elements
* above this index to fill the gap. Cannot be called with a pseudo-index,
* because a pseudo-index is not an actual stack position.
*/
public void remove(int index) {
int ai = index2adr(index);
System.arraycopy(stack, ai+1, stack, ai, top-ai-1);
poplvalue();
}
/**
* Replace an element on the stack. [-1, +0, -]
*
*
* Moves the top element into the given position (and pops it), without
* shifting any element (therefore replacing the value at the given
* position).
*/
public void replace(int index) {
int ai = index2adr(index);
stack[ai] = poplvalue();
}
/**
* Set the value of a table field. [-1, +0, e]
*
*
* Does the equivalent to t[k] = v
, where t
* is the value at the given valid index and v
is the value
* at the top of the stack.
*
*
*
* This function pops the value from the stack. As in Lua, this function may
* trigger a metamethod for the "newindex" event (see §2.8).
*/
public void setfield(int index, LString k) {
LTable t = totable(index);
this.luaV_settable(t, k, poplvalue());
}
/**
* Set the value of a global variable. [-1, +0,
* e]
*
*
* Pops a value from the stack and sets it as the new value of global
* name
. It is defined as a macro:
*
*
* #define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, s)
*
*
*/
public void setglobal(String name) {
this.luaV_settable(_G, new LString(name), poplvalue());
}
/**
* Get a value as a boolean. [-0, +0, -]
*
*
* Converts the Lua value at the given acceptable index to a C boolean
* value (0 or 1). Like all tests in Lua, lua_toboolean
returns 1 for
* any Lua value different from false and nil; otherwise it
* returns 0. It also returns 0 when called with a non-valid index. (If you
* want to accept only actual boolean values, use lua_isboolean
* to test the value's type.)
*
*/
public boolean toboolean(int index) {
return topointer(index).toJavaBoolean();
}
/**
* Get a value as an int. [-0, +0, -]
*
*
* Converts the Lua value at the given acceptable index to the signed
* integral type lua_Integer
.
* The Lua value must be a number or a string convertible to a number (see
* §2.2.1); otherwise, lua_tointeger
* returns 0.
*
*
*
* If the number is not an integer, it is truncated in some non-specified
* way.
*/
public int tointeger(int index) {
LValue v = tolnumber(index);
return v.isNil()? 0: v.toJavaInt();
}
/**
* Get a value as a LFunction.
*
* tofunction
*
* [-0, +0, -]
*
*
* LFunction tofunction (lua_State *L, int index);
*
*
*
* Converts a value at the given acceptable index to a C function. That
* value must be a function; otherwise, returns NULL
.
*/
public LValue tofunction(int index) {
LValue v = topointer(index);
return v.isFunction()? v: LNil.NIL;
}
/**
* Gets the value of a string as byte array. [-0, +0,
* m]
*
*
* Converts the Lua value at the given acceptable index to a C string.
* If len
is not NULL
, it also sets
* *len
with the string length. The Lua value must be a
* string or a number; otherwise, the function returns NULL
.
* If the value is a number, then lua_tolstring
* also changes the actual value in the stack to a string. (This
* change confuses lua_next
when lua_tolstring
is applied to
* keys during a table traversal.)
*
*
*
* lua_tolstring
returns a
* fully aligned pointer to a string inside the Lua state. This string
* always has a zero ('\0
') after its last character (as
* in C), but may contain other zeros in its body. Because Lua has
* garbage collection, there is no guarantee that the pointer returned by lua_tolstring
will be valid
* after the corresponding value is removed from the stack.
*/
public LString tolstring(int index) {
return topointer(index).luaAsString();
}
/**
* Convert a value to a double. [-0, +0, -]
*
*
* Converts the Lua value at the given acceptable index to the C type
* lua_Number
(see lua_Number
). The Lua value must
* be a number or a string convertible to a number (see §2.2.1); otherwise, lua_tonumber
* returns 0.
*
*/
public double tonumber(int index) {
LValue v = tolnumber(index);
return v.isNil()? 0: v.toJavaDouble();
}
/**
* Returns the index of the top element in the stack. [-0, +0, -]
*
*
* Because indices start at 1, this result is equal to the number of
* elements in the stack (and so 0 means an empty stack).
*/
public int gettop() {
return top - base;
}
/**
* Set the top of the stack. [-?, +?, -]
*
*
* Accepts any acceptable index, or 0, and sets the stack top to this
* index. If the new top is larger than the old one, then the new elements
* are filled with nil. If index
is 0, then all
* stack elements are removed.
*/
public void settop(int nt) {
int ant = nt>=0? base+nt: top+nt;
if ( ant < base )
throw new IllegalArgumentException("index out of bounds: "+ant );
luaV_adjusttop(ant);
}
/**
* Set the top to the base. Equivalent to settop(0)
*/
public void resettop() {
luaV_settop_fillabove( base );
}
private int index2adr(int index) {
// TODO: upvalues? globals? environment?
int ai = index>0? base+index-1: top+index;
if ( ai < base )
throw new IllegalArgumentException("index out of bounds: "+ai );
return ai;
}
/**
* Get the raw Object at a stack location. [-0, +0,
* -]
*
*
* Converts the value at the given acceptable index to a generic
* C pointer (void*
). The value may be a userdata, a
* table, a thread, or a function; otherwise, lua_topointer
* returns NULL
. Different objects will give different
* pointers. There is no way to convert the pointer back to its original
* value.
*
*
*
* Typically this function is used only for debug information.
*/
public LValue topointer(int index) {
int ai = index2adr(index);
if ( ai >= top )
return LNil.NIL;
return stack[ai];
}
/**
* Get a stack value as a String. [-0, +0, m]
*
*
* Equivalent to lua_tolstring
* with len
equal to NULL
.
*/
public String tostring(int index) {
return topointer(index).toJavaString();
}
/**
* Get a value from the stack as a lua table. [-0, +0, -]
*
*
* Converts the value at the given acceptable index to a Lua table
* This value must be a table otherwise, the function returns NIL
.
*/
public LTable totable(int index) {
return (LTable) topointer(index);
}
/**
* Get the Object from a userdata value. [-0, +0,
* -]
*
*
* If the value at the given acceptable index is a full userdata, returns
* its block address. If the value is a light userdata, returns its pointer.
* Otherwise, returns NULL
.
*
*/
public Object touserdata(int index) {
LValue v = topointer(index);
if ( v.luaGetType() != Lua.LUA_TUSERDATA )
return null;
return ((LUserData)v).m_instance;
}
/**
* Get the type of a value. [-0, +0, -]
*
*
* Returns the type of the value in the given acceptable index, or
* LUA_TNONE
for a non-valid index (that is, an index to an
* "empty" stack position). The types returned by lua_type
* are coded by the following constants defined in lua.h
:
* LUA_TNIL
, LUA_TNUMBER
,
* LUA_TBOOLEAN
, LUA_TSTRING
,
* LUA_TTABLE
, LUA_TFUNCTION
,
* LUA_TUSERDATA
, LUA_TTHREAD
, and
* LUA_TLIGHTUSERDATA
.
*/
public int type(int index) {
return topointer(index).luaGetType();
}
/**
* Get the type name for a value. [-0, +0, -]
*
*
* Returns the name of the type encoded by the value tp
,
* which must be one the values returned by lua_type
.
*/
public String typename(int index) {
return topointer(index).luaGetTypeName().toJavaString();
}
/**
* Exchange values between threads. [-?, +?, -]
*
*
* Exchange values between different threads of the same global
* state.
*
*
*
* This function pops n
values from the stack
* from
, and pushes them onto the stack to
.
*/
public void xmove(LuaState to, int n) {
if ( n > 0 ) {
to.checkstack(n);
LuaState ss = (LuaState)to;
ss.checkstack(n);
System.arraycopy(stack, top-n, ss.stack, ss.top, n);
ss.top += n;
}
}
// ============================= conversion to and from Java boxed types ====================
/**
* Push a Java Boolean value, or nil if the value is null.
* @param b Boolean value to convert, or null to to nil.
*/
public void pushboolean(Boolean b) {
if ( b == null )
pushnil();
else
pushboolean( b.booleanValue() );
}
/**
* Push a Java Byte value, or nil if the value is null.
* @param b Byte value to convert, or null to to nil.
*/
public void pushinteger(Byte b) {
if ( b == null )
pushnil();
else
pushinteger( b.byteValue() );
}
/**
* Push a Java Character value, or nil if the value is null.
* @param c Character value to convert, or null to to nil.
*/
public void pushinteger(Character c) {
if ( c == null )
pushnil();
else
pushinteger( c.charValue() );
}
/**
* Push a Java Double as a double, or nil if the value is null.
* @param d Double value to convert, or null to to nil.
*/
public void pushnumber(Double d) {
if ( d == null )
pushnil();
else
pushnumber( d.doubleValue() );
}
/**
* Push a Java Float value, or nil if the value is null.
* @param f Float value to convert, or null to to nil.
*/
public void pushnumber(Float f) {
if ( f == null )
pushnil();
else
pushnumber( f.doubleValue() );
}
/**
* Push a Java Integer value, or nil if the value is null.
* @param i Integer value to convert, or null to to nil.
*/
public void pushinteger(Integer i) {
if ( i == null )
pushnil();
else
pushinteger( i.intValue() );
}
/**
* Push a Java Short value, or nil if the value is null.
* @param s Short value to convert, or null to to nil.
*/
public void pushinteger(Short s) {
if ( s == null )
pushnil();
else
pushinteger( s.shortValue() );
}
/**
* Push a Java Long value, or nil if the value is null.
* @param l Long value to convert, or null to to nil.
*/
public void pushnumber(Long l) {
if ( l == null )
pushnil();
else
pushnumber( l.doubleValue() );
}
/**
* Push a Java Object as userdata, or nil if the value is null.
* @param o Object value to push, or null to to nil.
*/
public void pushuserdata( Object o ) {
if ( o == null )
pushnil();
else
pushlvalue( new LUserData(o) );
}
/**
* Convert a value to a Java Boolean value, or null if the value is nil.
* @param index index of the parameter to convert.
* @return Boolean value at the index, or null if the value was not a boolean.
*/
public Boolean toboxedboolean(int index) {
return topointer(index).toJavaBoxedBoolean();
}
/**
* Convert a value to a Java Byte value, or null if the value is not a number.
* @param index index of the parameter to convert.
* @return Byte value at the index, or null if the value was not a number.
*/
public Byte toboxedbyte(int index) {
return topointer(index).toJavaBoxedByte();
}
/**
* Convert a value to a Java Double value, or null if the value is not a number.
* @param index index of the parameter to convert.
* @return Double value at the index, or null if the value was not a number.
*/
public Double toboxeddouble(int index) {
return topointer(index).toJavaBoxedDouble();
}
/**
* Convert a value to a Java Float value, or null if the value is not a number.
* @param index index of the parameter to convert.
* @return Float value at the index, or null if the value was not a boolean.
*/
public Float toboxedfloat(int index) {
return topointer(index).toJavaBoxedFloat();
}
/**
* Convert a value to a Java Integer value, or null if the value is not a number.
* @param index index of the parameter to convert.
* @return Integer value at the index, or null if the value was not a number.
*/
public Integer toboxedinteger(int index) {
return topointer(index).toJavaBoxedInteger();
}
/**
* Convert a value to a Java Long value, or null if the value is nil.
* @param index index of the parameter to convert.
* @return Long value at the index, or null if the value was not a number.
*/
public Long toboxedlong(int index) {
return topointer(index).toJavaBoxedLong();
}
// ================= Error Reporting Functions =================
/**
* Report an error with an argument.
*
* @param narg Stack index of the bad argument
* @param extramsg String to include in error message
*/
public void argerror(int narg, String extramsg) {
error("bad argument #" + (narg) + " (" + extramsg + ")");
}
/**
* Conditionally report an error with an argument.
*
* @param cond boolean condition that generates an error when false
* @param narg Stack index of the bad argument
* @param extramsg String to include in error message
*/
public void argcheck(boolean cond, int narg, String extramsg) {
if ( ! cond )
argerror(narg,extramsg);
}
/**
* Report a type error.
*
* @param narg Stack index of the bad argument
* @param typename Name of the type that was expected, such as "string"
*/
public void typerror(int narg, String typename) {
argerror(narg, typename + " expected, got " + typename(narg));
}
/**
* Report a type error.
*
* @param narg Stack index of the bad argument
* @param typenum Constant value specifying the type of argument that was expected (i.e. LUA_TSTRING).
*/
public void typerror(int narg, int typenum) {
typerror(narg, TYPE_NAMES[typenum]);
}
/**
* Checks whether the function has an argument of any type (including nil)
* at position narg
.
* @param narg the argument number
* @return the value at the index
* @throws LuaErrorException if there is no argument at position narg
*/
public LValue checkany(int narg) {
if ( gettop() < narg )
argerror(narg, "value expected");
return topointer(narg);
}
/**
* Checks whether the function argument narg
is a function and
* returns this function.
* @see LFunction
* @param narg the argument number
* @throws LuaErrorException if the value is not a function
* @return LFunction value if the argument is a function
*/
public LFunction checkfunction(int narg) {
return (LFunction) checktype(narg, Lua.LUA_TFUNCTION);
}
/**
* Checks whether the function argument narg
is a thread and
* returns this thread.
* @see LThread
* @param narg the argument number
* @throws LuaErrorException if the value is not a thread
* @return LThread value if the argument is a thread
*/
public LThread checkthread(int narg) {
return (LThread) checktype(narg, Lua.LUA_TTHREAD);
}
/**
* Checks whether the function argument narg
is a number and
* returns this number cast to an int
.
* @param narg the argument number
* @throws LuaErrorException if the number cannot be converted to an int
* @return int value if the argument is an int or can be converted to one
*/
public int checkint(int narg) {
LValue v = tolnumber(narg);
if ( v.isNil() )
typerror(narg, Lua.LUA_TNUMBER);
return v.toJavaInt();
}
/**
* Checks whether the function argument narg
is a number and
* returns this number cast to a LInteger
.
* @see LInteger
* @param narg the argument number
* @throws LuaErrorException if the value cannot be converted to an int
* @return LInteger value if the argument is an int or can be converted to one
*/
public LInteger checkinteger(int narg) {
return LInteger.valueOf(checkint(narg));
}
/**
* Checks whether the function argument narg
is a number and
* returns this number cast to a long
.
* @param narg the argument number
* @throws LuaErrorException if the value cannot be converted to a long
* @return long value if the argument is a number or can be converted to long
*/
public long checklong(int narg) {
return checknumber(narg).toJavaLong();
}
/**
* Checks whether the function argument narg
is a number and
* returns this number cast to a double
.
* @param narg the argument number
* @throws LuaErrorException if the value cannot be converted to a double
* @return long value if the argument is a number or can be converted to double
*/
public double checkdouble(int narg) {
return checknumber(narg).toJavaDouble();
}
/**
* Checks whether the function argument narg
is a number and
* returns this number.
* @see LNumber
* @param narg the argument number
* @throws LuaErrorException if the value cannot be converted to a number
* @return LNumber value if the argument is a number or can be converted to one
*/
public LNumber checknumber(int narg) {
LValue v = topointer(narg).luaToNumber();
if ( v.isNil() )
typerror(narg, Lua.LUA_TNUMBER);
return (LNumber) v;
}
/**
* Checks whether the function argument narg
is a string and
* returns this string as a lua string.
* @see LString
* @param narg the argument number
* @throws LuaErrorException if the value cannot be converted to a string
* @return LString value if the argument is a string or can be converted to one
*/
public LString checklstring(int narg) {
LValue v = topointer(narg);
if ( ! v.isString() )
typerror(narg, Lua.LUA_TSTRING);
return v.luaAsString();
}
/**
* Checks whether the function argument narg
is a string and
* returns this string as a Java String.
* @param narg the argument number
* @throws LuaErrorException if the value cannot be converted to a string
* @return String value if the argument is a string or can be converted to one
*/
public String checkstring(int narg) {
LValue v = topointer(narg);
if ( ! v.isString() )
typerror(narg, Lua.LUA_TSTRING);
return v.toJavaString();
}
/**
* Checks whether the function argument narg
is a table and
* returns this table.
* @see LTable
* @param narg the argument number
* @throws LuaErrorException if the value is not a table
* @return LTable value if the argument is a table
*/
public LTable checktable(int narg) {
return (LTable) checktype(narg, Lua.LUA_TTABLE);
}
/**
* Checks whether the function argument narg
has type
* t
and return it as an LValue.
* @param narg the argument number
* @param t the type number to check against
* @return the lua value
* @throws LuaErrorException if the value is not of type t
*/
public LValue checktype(int narg, int t) {
LValue v = topointer(narg);
if ( v.luaGetType() != t )
typerror(narg, t);
return v;
}
/**
* Check that the type of userdata on the stack matches the required type,
* and if so, return the Java Object the userdata value points to.
*
* @param ud
* Stack index of the argument to check
* @param expected
* Class that the userdata is expected to have an instance of.
*/
public Object checkudata(int ud, Class expected) {
Object p = touserdata(ud);
if (expected.isInstance(p)) {
return p;
}
typerror(ud, expected.getName());
return null;
}
/**
* If the function argument narg
is a number, returns this
* number cast to an int
. If this argument is absent or is
* nil, returns d
. Otherwise, raises an error.
* @param narg the argument number
* @param d the default value when the argument is nil or not supplied
* @throws LuaErrorException if the value cannot be converted to an int
* @return int value if the argument is an int, or d
*/
public int optint(int narg, int d) {
LNumber n = optnumber(narg,null);
return (n==null? d: n.toJavaInt());
}
/**
* If the function argument narg
is a number, returns this
* number cast to a lua_Integer
.
* If this argument is absent or is nil, returns d
.
* Otherwise, raises an error.
* @param narg the argument number
* @param d the default value when the argument is nil or not supplied
* @throws LuaErrorException if the value cannot be converted to an int
* @return int value if the argument is an int, or d
*/
public LInteger optinteger(int narg, int d) {
return LInteger.valueOf(optint(narg,d));
}
/**
* If the function argument narg
is a number, returns this
* number cast to a long
. If this argument is absent or is
* nil, returns d
. Otherwise, raises an error.
* @param narg the argument number
* @param d the default value when the argument is nil or not supplied
* @throws LuaErrorException if the value cannot be converted to an number
* @return int value if the argument is an number, or d
*/
public long optlong(int narg, long d) {
LNumber n = optnumber(narg,null);
return (n==null? d: n.toJavaLong());
}
/**
* If the function argument narg
is a number, returns this
* number. If this argument is absent or is nil, returns
* d
. Otherwise, raises an error.
* @param narg the argument number
* @param d the default value when the argument is nil or not supplied
* @throws LuaErrorException if the value cannot be converted to an number
* @return int value if the argument is an number, or d
*/
public LNumber optnumber(int narg, LNumber d) {
LValue v = topointer(narg);
if ( v.isNil() )
return d;
v = v.luaToNumber();
if ( v.isNil() )
typerror(narg, Lua.LUA_TNUMBER);
return (LNumber) v;
}
/**
* If the function argument narg
is a string, returns this
* string. If this argument is absent or is nil, returns
* d
. Otherwise, raises an error.
*/
public LString optlstring(int narg, LString d) {
LValue v = topointer(narg);
if ( v.isNil() )
return d;
if ( ! v.isString() )
typerror(narg, Lua.LUA_TSTRING);
return v.luaAsString();
}
/**
* If the function argument narg
is a string, returns this
* string. If this argument is absent or is nil, returns
* d
. Otherwise, raises an error.
*/
public String optstring(int narg, String d) {
LValue v = topointer(narg);
if ( v.isNil() )
return d;
if ( ! v.isString() )
typerror(narg, Lua.LUA_TSTRING);
return v.toJavaString();
}
/**
* Method to indicate a vm internal error has occurred. Generally, this is
* not recoverable, so we convert to a lua error during production so that
* the code may recover.
*/
public static void vmerror(String description) {
throw new LuaErrorException( "internal error: "+description );
}
/**
* Call a function with no arguments and one return value.
* This may change values in the current stack frame.
* @param function
* @return
*/
public LValue call(LFunction function) {
pushlvalue(function);
call(0,1);
return poplvalue();
}
/**
* Call a function with one argument and one return value
* This may change values in the current stack frame.
* @param function
* @param arg0
* @return
*/
public LValue call(LFunction function, LValue arg0) {
pushlvalue(function);
pushlvalue(arg0);
call(1,1);
return poplvalue();
}
/**
* Call an index function with two arguments and one return value
* Values in the current stack frame will be preserved.
* @param function the __index function to call
* @param table the table on which the metadata operation is taking place
* @param key the key used in the index operation
* @return the value that results from the metatable operation
*/
public LValue luaV_call_index(LFunction function, LValue table, LValue key) {
int oldtop = top;
try {
if ( cc >= 0 ) {
int t = base + this.calls[cc].closure.p.maxstacksize;
if ( t > top )
top = t;
}
pushlvalue(function);
pushlvalue(table);
pushlvalue(key);
call(2,1);
return poplvalue();
} finally {
top = oldtop;
}
}
/**
* Call a newindex function with three arguments and one return value
* Values in the current stack frame will be preserved.
* @param function the __newindex function to call
* @param table the table on which the metadata operation is taking place
* @param key the key used in the newindex operation
* @param value the value beting set in the newindex operation
* @return the value that results from the metatable operation
*/
public LValue luaV_call_newindex(LFunction function, LValue table, LValue key, LValue value) {
int oldtop = top;
try {
if ( cc >= 0 ) {
int t = base + this.calls[cc].closure.p.maxstacksize;
if ( t > top )
top = t;
}
pushlvalue(function);
pushlvalue(table);
pushlvalue(key);
pushlvalue(value);
call(3,1);
return poplvalue();
} finally {
top = oldtop;
}
}
/**
* Call the error function, if any, to possibly modify the message
*/
public String luaV_call_errfunc(String message) {
// error function runs without hooks
if ( inhook || errfunc == null )
return message;
// run the error function
int oldtop = top;
try {
inhook = true;
if ( cc >= 0 ) {
int t = base + this.calls[cc].closure.p.maxstacksize;
if ( t > top )
top = t;
}
pushlvalue(errfunc);
pushstring(message);
call(1,1);
return poplvalue().toJavaString();
} catch ( Throwable t ) {
return "error in error handling: "+t.getMessage();
} finally {
top = oldtop;
inhook = false;
}
}
// ===========================================================================
// Debug hooks.
// These should be obfuscated out when sethook is never called
// cannot be called from the application.
//
/**
* Set the hook function.
*
* @param func LFunction to call on the hook event
* @param mask combination of LuaState.LUA_MASKLINE, LuaState.LUA_MASKCALL, and LuaState.LUA_MASKRET
* @param count 0, or number of bytecodes between count events.
*/
public void sethook( LFunction func, int mask, int count ) {
hooksenabled = (mask != 0 || count > 0);
hookfunc = func;
hookmask = mask;
hookcount = count;
}
/**
* Get the current hook function, if any.
*
* @return LFunction that is set as the current hook function, or null if not set.
*/
public LFunction gethook() {
return hookfunc;
}
/**
* Get the current hook count.
*
* @return current count, which is # of bytecodes between "count" hook calls
*/
public int gethookcount() {
return hookcount;
}
/**
* Get the current hook mask.
*
* @return current mask as a combination of
* LuaState.LUA_MASKLINE, LuaState.LUA_MASKCALL, and LuaState.LUA_MASKRET
*/
public int gethookmask() {
return hookmask;
}
// line number and count hooks
private void debugBytecodeHooks(int pc) {
if ( hookfunc != null ) {
if (hookcount != 0) {
if ( --hookincr <= 0 ) {
hookincr = hookcount;
debugInvokeHook(LUA_HOOKCOUNT, -1);
}
}
if ( (hookmask & LUA_MASKLINE) != 0 ) {
int line = calls[cc].currentline();
if ( (line != hookline || cc != hookcc) && line >= 0 ) {
hookline = line;
hookcc = cc;
debugInvokeHook(LUA_HOOKLINE, line);
}
}
}
}
private void debugCallHooks() {
if ( hookfunc != null && ((hookmask & LUA_MASKCALL) != 0) ) {
debugInvokeHook(LUA_HOOKCALL, calls[cc].currentline());
}
}
private void debugReturnHooks() {
if ( hookfunc != null && ((hookmask & LUA_MASKRET) != 0) ) {
debugInvokeHook(LUA_HOOKRET, calls[cc].currentline());
}
}
private void debugTailReturnHooks() {
if ( hookfunc != null && ((hookmask & LUA_MASKRET) != 0) ) {
debugInvokeHook(LUA_HOOKTAILRET, calls[cc].currentline());
}
}
private void debugInvokeHook(int mask, int line) {
if ( inhook )
return;
int oldtop = top;
int oldbase = base;
int oldcc = cc;
int oldnresults = nresults;
int beyond = (cc>=0? base+calls[cc].closure.p.maxstacksize: top);
if ( beyond < top )
beyond = top;
try {
inhook = true;
// adjust base and top to beyond call frame
top = base = beyond;
// push hook function and arguments
this.pushfunction(hookfunc);
switch ( mask ) {
case LUA_HOOKCOUNT: this.pushstring("count"); break;
case LUA_HOOKCALL: this.pushstring("call"); break;
case LUA_HOOKRET: this.pushstring("return"); break;
case LUA_HOOKTAILRET: this.pushstring("tail return"); break;
default:
this.pushstring("line");
this.pushinteger(line);
break;
}
// make or set up the call
this.nresults = 0;
if ( this.stack[base].luaStackCall(this) )
execute();
} catch ( Throwable t ) {
System.err.println("hook exception: "+t);
} finally {
luaV_settop_fillabove(beyond);
cc = oldcc;
base = oldbase;
top = oldtop;
nresults = oldnresults;
inhook = false;
}
}
}