All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.luaj.vm.LuaState Maven / Gradle / Ivy

There is a newer version: 1.0.5
Show newest version
/*******************************************************************************
* 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; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy