diff --git a/src/core/org/luaj/vm2/Globals.java b/src/core/org/luaj/vm2/Globals.java new file mode 100644 index 00000000..9c96d6b2 --- /dev/null +++ b/src/core/org/luaj/vm2/Globals.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2012 Luaj.org. 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.vm2; + +import java.io.InputStream; +import java.io.PrintStream; + +import org.luaj.vm2.LoadState.LuaCompiler; +import org.luaj.vm2.lib.BaseLib; +import org.luaj.vm2.lib.DebugLib; +import org.luaj.vm2.lib.ResourceFinder; +import org.luaj.vm2.lib.jme.JmePlatform; +import org.luaj.vm2.lib.jse.JsePlatform; + +/** + * Global environment used by luaj. + *

+ * Contains the global variables referenced by lua libraries such as stdin and stdout, + * the resrouce finder which is used tolook up files in a platform independent way, + * the installed lua compiler, the math library in use, debugging calls stack, and so on. + *

+ * In a multithreded server environment, each server thread should create one Globals instance, + * which will be logically distance and not interfere with each other, but share certain + * static immutable resources such as class data and string data. + *

+ * Typically, this is constructed indirectly by a call to + * {@link JsePlatform.standardGlobasl()} or {@link JmePlatform.standardGlobals()}, + * and then used to load lua scripts for execution as in the following example. + *

 {@code
+ * Globals _G = JsePlatform.standardGlobals();
+ * _G.compiler.load( new ByteArrayInputStream("print 'hello'".getBytes()), "main.lua", _G ).call();
+ * } 
+ * @see LuaCompiler + * @see JsePlatform + * @see JmePlatform + * @see LuaValue + * + */ +public class Globals extends LuaTable { + + public InputStream STDIN = null; + public PrintStream STDOUT = System.out; + public PrintStream STDERR = System.err; + + public ResourceFinder FINDER; + + public LuaCompiler compiler = null; + + public LuaThread.CallStack callstack = new LuaThread.CallStack(); + + public BaseLib baselib; + + public LuaValue errorfunc; + + public LuaThread running_thread = new LuaThread(this); + + public DebugLib debuglib; + + public Globals checkglobals() { + return this; + } + + public Varargs loadFile(String filename) { + return baselib.loadFile(filename, "bt", this); + } + + public Varargs yield(Varargs args) { + if (running_thread == null || running_thread.isMainThread()) + throw new LuaError("cannot yield main thread"); + final LuaThread.State s = running_thread.state; + return s.lua_yield(args); + } + +} diff --git a/src/core/org/luaj/vm2/LuaClosure.java b/src/core/org/luaj/vm2/LuaClosure.java index 6ee03729..e4541782 100644 --- a/src/core/org/luaj/vm2/LuaClosure.java +++ b/src/core/org/luaj/vm2/LuaClosure.java @@ -21,8 +21,6 @@ ******************************************************************************/ package org.luaj.vm2; -import org.luaj.vm2.LoadState.LuaCompiler; -import org.luaj.vm2.compiler.LuaC; import org.luaj.vm2.lib.DebugLib; /** @@ -92,14 +90,7 @@ public class LuaClosure extends LuaFunction { public UpValue[] upValues; - /** Create a closure around a Prototype with the default global environment. - * If the prototype has upvalues, the environment will be written into the first upvalue. - * @param p the Prototype to construct this Closure for. - * @param env the environment to associate with the closure. - */ - public LuaClosure(Prototype p) { - this(p, LuaValue._G); - } + final Globals globals; /** Create a closure around a Prototype with a specific environment. * If the prototype has upvalues, the environment will be written into the first upvalue. @@ -114,6 +105,7 @@ public class LuaClosure extends LuaFunction { this.upValues = new UpValue[p.upvalues.length]; this.upValues[0] = new UpValue(new LuaValue[] {env}, 0); } + globals = env instanceof Globals? (Globals) env: null; } public boolean isclosure() { @@ -184,7 +176,6 @@ public class LuaClosure extends LuaFunction { return execute(stack,p.is_vararg!=0? varargs.subargs(p.numparams+1): NONE); } - protected Varargs execute( LuaValue[] stack, Varargs varargs ) { // loop through instructions int i,a,b,c,pc=0,top=0; @@ -194,18 +185,17 @@ public class LuaClosure extends LuaFunction { LuaValue[] k = p.k; // upvalues are only possible when closures create closures - UpValue[] openups = p.p.length>0? new UpValue[stack.length]: null; + UpValue[] openups = p.p.length>0? new UpValue[p.p.length]: null; // debug wants args to this function - if (DebugLib.DEBUG_ENABLED) - DebugLib.debugSetupCall(varargs, stack); + if (globals != null) + globals.callstack.onCall( this, varargs, stack ); // process instructions - LuaThread.CallStack cs = LuaThread.onCall( this ); try { while ( true ) { - if (DebugLib.DEBUG_ENABLED) - DebugLib.debugBytecode(pc, v, top); + if (DebugLib.DEBUG_ENABLED && globals != null) + globals.callstack.onInstruction( pc, v, top ); // pull out instruction i = code[pc++]; @@ -473,7 +463,7 @@ public class LuaClosure extends LuaFunction { case Lua.OP_CLOSURE: /* A Bx R(A):= closure(KPROTO[Bx]) */ { Prototype newp = p.p[i>>>14]; - LuaClosure ncl = new LuaClosure(newp, null); + LuaClosure ncl = new LuaClosure(newp, globals); Upvaldesc[] uv = newp.upvalues; for ( int j=0, nup=uv.length; j=0; ) if ( openups[u] != null ) @@ -516,6 +511,32 @@ public class LuaClosure extends LuaFunction { } } + /** + * Run the error hook if there is one + * @param msg the message to use in error hook processing. + * */ + String errorHook(String msg) { + if (globals == null || globals.errorfunc == null) + return msg; + LuaValue errfunc = globals.errorfunc; + globals.errorfunc = null; + try { + return errfunc.call( LuaValue.valueOf(msg) ).tojstring(); + } catch ( Throwable t ) { + return "error in error handling"; + } finally { + globals.errorfunc = errfunc; + } + } + + private void processErrorHooks(LuaError le, Prototype p, int pc) { + le.fileline = (p.source != null? p.source.tojstring(): "?") + ":" + + (p.lineinfo != null && pc >= 0 && pc < p.lineinfo.length? String.valueOf(p.lineinfo[pc]): "?"); + le.traceback = errorHook(le.getMessage()); + if (DebugLib.DEBUG_ENABLED && globals != null && globals.debuglib != null) + le.traceback += globals.callstack.traceback(le.level); + } + private UpValue findupval(LuaValue[] stack, short idx, UpValue[] openups) { final int n = openups.length; for (int i = 0; i < n; ++i) @@ -535,4 +556,22 @@ public class LuaClosure extends LuaFunction { protected void setUpvalue(int i, LuaValue v) { upValues[i].setValue(v); } + + + /** + * Add file and line info to a message at a particular level + * @param message the String message to use + * @param level where to supply line info from in call stack + * */ + private String getFileLineMessage( Exception e, int pc ) { + String msg = e.getMessage(); + if ( msg == null ) return null; + if ( globals == null ) return msg; + LuaFunction f = globals.callstack.getFunction(1); + if ( ! (f instanceof LuaClosure) ) return msg; + LuaClosure c = (LuaClosure) f; + LuaString file = c.p.source != null ? c.p.source: valueOf("?"); + String line = c.p.lineinfo != null && pc < c.p.lineinfo.length? String.valueOf(c.p.lineinfo[pc]): "?"; + return file.tojstring() + ": " + line; + } } diff --git a/src/core/org/luaj/vm2/LuaError.java b/src/core/org/luaj/vm2/LuaError.java index fd9c62e4..149303c3 100644 --- a/src/core/org/luaj/vm2/LuaError.java +++ b/src/core/org/luaj/vm2/LuaError.java @@ -38,39 +38,28 @@ import org.luaj.vm2.lib.DebugLib; public class LuaError extends RuntimeException { private static final long serialVersionUID = 1L; - private String traceback; + protected int level; + + protected String fileline; + + protected String traceback; + + protected Throwable cause; - /** - * Run the error hook if there is one - * @param msg the message to use in error hook processing. - * */ - private static String errorHook(String msg) { - LuaThread thread = LuaThread.getRunning(); - if ( thread.err != null ) { - LuaValue errfunc = thread.err; - thread.err = null; - try { - return errfunc.call( LuaValue.valueOf(msg) ).tojstring(); - } catch ( Throwable t ) { - return "error in error handling"; - } finally { - thread.err = errfunc; - } - } - return msg; + public String getMessage() { + return (fileline != null? fileline + " ": "") + + (traceback != null? traceback: super.getMessage()); } - - private Throwable cause; - + /** Construct LuaError when a program exception occurs. *

* All errors generated from lua code should throw LuaError(String) instead. * @param cause the Throwable that caused the error, if known. */ public LuaError(Throwable cause) { - super( errorHook( addFileLine( "vm error: "+cause ) ) ); + super( "vm error: "+cause ); this.cause = cause; - this.traceback = DebugLib.traceback(1); + this.level = 1; } /** @@ -79,8 +68,8 @@ public class LuaError extends RuntimeException { * @param message message to supply */ public LuaError(String message) { - super( errorHook( addFileLine( message ) ) ); - this.traceback = DebugLib.traceback(1); + super( message ); + this.level = 1; } /** @@ -89,37 +78,10 @@ public class LuaError extends RuntimeException { * @param level where to supply line info from in call stack */ public LuaError(String message, int level) { - super( errorHook( addFileLine( message, level ) ) ); - this.traceback = DebugLib.traceback(1); + super( message ); + this.level = level; } - /** - * Add file and line info to a message at a particular level - * @param message the String message to use - * @param level where to supply line info from in call stack - * */ - private static String addFileLine( String message, int level ) { - if ( message == null ) return null; - if ( level == 0 ) return message; - String fileline = DebugLib.fileline(level-1); - return fileline!=null? fileline+": "+message: message; - } - - /** Add file and line info for the nearest enclosing closure - * @param message the String message to use - * */ - private static String addFileLine( String message ) { - if ( message == null ) return null; - String fileline = DebugLib.fileline(); - return fileline!=null? fileline+": "+message: message; - } - - /** Print the message and stack trace */ - public void printStackTrace() { - System.out.println( toString() ); - if ( traceback != null ) - System.out.println( traceback ); - } /** * Get the cause, if any. diff --git a/src/core/org/luaj/vm2/LuaThread.java b/src/core/org/luaj/vm2/LuaThread.java index 9ed032da..d66c6db2 100644 --- a/src/core/org/luaj/vm2/LuaThread.java +++ b/src/core/org/luaj/vm2/LuaThread.java @@ -24,6 +24,7 @@ package org.luaj.vm2; import java.lang.ref.WeakReference; +import org.luaj.vm2.LuaThread.CallFrame; import org.luaj.vm2.lib.DebugLib; /** @@ -60,7 +61,7 @@ import org.luaj.vm2.lib.DebugLib; * @see CoroutineLib */ public class LuaThread extends LuaValue { - + public static LuaValue s_metatable; public static int coroutine_count = 0; @@ -84,37 +85,39 @@ public class LuaThread extends LuaValue { public final State state; - /** Field to hold state of error condition during debug hook function calls. */ - public LuaValue err; - final CallStack callstack = new CallStack(); public static final int MAX_CALLSTACK = 256; - private static final LuaThread main_thread = new LuaThread(); - - // state of running thread including call stack - private static LuaThread running_thread = main_thread; - /** Interval to check for LuaThread dereferencing. */ public static int GC_INTERVAL = 30000; /** Thread-local used by DebugLib to store debugging state. */ public Object debugState; + public LuaValue hookfunc; + public boolean hookline; + public boolean hookcall; + public boolean hookrtrn; + public int hookcount; + + public final Globals globals; + /** Private constructor for main thread only */ - private LuaThread() { - state = new State(this, null); + public LuaThread(Globals globals) { + state = new State(globals, this, null); state.status = STATUS_RUNNING; + this.globals = globals; } /** * Create a LuaThread around a function and environment * @param func The function to execute */ - public LuaThread(LuaValue func) { + public LuaThread(Globals globals, LuaValue func) { LuaValue.assert_(func != null, "function cannot be null"); - state = new State(this, func); + state = new State(globals, this, func); + this.globals = globals; } public int type() { @@ -145,71 +148,20 @@ public class LuaThread extends LuaValue { return STATUS_NAMES[state.status]; } - /** - * Get the currently running thread. - * @return {@link LuaThread} that is currenly running - */ - public static LuaThread getRunning() { - return running_thread; - } - - /** - * Callback used at the beginning of a call to prepare for possible getfenv/setfenv calls - * @param function Function being called - * @return CallStack which is used to signal the return or a tail-call recursion - * @see DebugLib - */ - public static final CallStack onCall(LuaFunction function) { - CallStack cs = running_thread.callstack; - cs.onCall(function); - return cs; + public boolean isMainThread() { + return this.state.function == null; } - /** - * Get the function called as a specific location on the stack. - * @param level 1 for the function calling this one, 2 for the next one. - * @return LuaFunction on the call stack, or null if outside of range of active stack - */ - public static final LuaFunction getCallstackFunction(int level) { - return running_thread.callstack.getFunction(level); - } - - /** - * Replace the error function of the currently running thread. - * @param errfunc the new error function to use. - * @return the previous error function. - */ - public static LuaValue setErrorFunc(LuaValue errfunc) { - LuaValue prev = running_thread.err; - running_thread.err = errfunc; - return prev; - } - - /** Yield the current thread with arguments - * - * @param args The arguments to send as return values to {@link #resume(Varargs)} - * @return {@link Varargs} provided as arguments to {@link #resume(Varargs)} - */ - public static Varargs yield(Varargs args) { - State s = running_thread.state; - if (s.function == null) - throw new LuaError("cannot yield main thread"); - return s.lua_yield(args); - } - - /** Start or resume this thread - * - * @param args The arguments to send as return values to {@link #yield(Varargs)} - * @return {@link Varargs} provided as arguments to {@link #yield(Varargs)} - */ public Varargs resume(Varargs args) { - if (this.state.status > STATUS_SUSPENDED) + final LuaThread.State s = this.state; + if (s.status > LuaThread.STATUS_SUSPENDED) return LuaValue.varargsOf(LuaValue.FALSE, - LuaValue.valueOf("cannot resume "+LuaThread.STATUS_NAMES[this.state.status]+" coroutine")); - return state.lua_resume(this, args); + LuaValue.valueOf("cannot resume "+(s.status==LuaThread.STATUS_DEAD? "dead": "non-suspended")+" coroutine")); + return s.lua_resume(this, args); } public static class State implements Runnable { + private final Globals globals; final WeakReference lua_thread; public final LuaValue function; Varargs args = LuaValue.NONE; @@ -217,7 +169,8 @@ public class LuaThread extends LuaValue { String error = null; public int status = LuaThread.STATUS_INITIAL; - State(LuaThread lua_thread, LuaValue function) { + State(Globals globals, LuaThread lua_thread, LuaValue function) { + this.globals = globals; this.lua_thread = new WeakReference(lua_thread); this.function = function; } @@ -236,9 +189,9 @@ public class LuaThread extends LuaValue { } public synchronized Varargs lua_resume(LuaThread new_thread, Varargs args) { - LuaThread previous_thread = LuaThread.running_thread; + LuaThread previous_thread = globals.running_thread; try { - LuaThread.running_thread = new_thread; + globals.running_thread = new_thread; this.args = args; if (this.status == STATUS_INITIAL) { this.status = STATUS_RUNNING; @@ -246,7 +199,8 @@ public class LuaThread extends LuaValue { } else { this.notify(); } - previous_thread.state.status = STATUS_NORMAL; + if (previous_thread != null) + previous_thread.state.status = STATUS_NORMAL; this.status = STATUS_RUNNING; this.wait(); return (this.error != null? @@ -255,11 +209,12 @@ public class LuaThread extends LuaValue { } catch (InterruptedException ie) { throw new OrphanedThread(); } finally { - running_thread = previous_thread; - running_thread.state.status =STATUS_RUNNING; this.args = LuaValue.NONE; this.result = LuaValue.NONE; this.error = null; + globals.running_thread = previous_thread; + if (previous_thread != null) + globals.running_thread.state.status =STATUS_RUNNING; } } @@ -287,17 +242,30 @@ public class LuaThread extends LuaValue { } public static class CallStack { - final LuaFunction[] functions = new LuaFunction[MAX_CALLSTACK]; - int calls = 0; + final CallFrame[] frame = new CallFrame[MAX_CALLSTACK]; + int calls = 0; + + CallStack() { + for (int i = 0; i < MAX_CALLSTACK; ++i) + frame[i] = new CallFrame(); + } /** * Method to indicate the start of a call + * @param stack + * @param varargs * @see DebugLib */ - final void onCall(LuaFunction function) { - functions[calls++] = function; - if (DebugLib.DEBUG_ENABLED) - DebugLib.debugOnCall(running_thread, calls, function); + public final void onCall(LuaFunction function) { + frame[calls++].set(function); +// if (DebugLib.DEBUG_ENABLED) +// DebugLib.debugOnCall(globals.running_thread, calls, function); + } + + public final void onCall(LuaClosure function, Varargs varargs, LuaValue[] stack) { + frame[calls++].set(function, varargs, stack); +// if (DebugLib.DEBUG_ENABLED) +// DebugLib.debugOnCall(globals.running_thread, calls, function); } /** @@ -305,11 +273,15 @@ public class LuaThread extends LuaValue { * @see DebugLib */ public final void onReturn() { - functions[--calls] = null; - if (DebugLib.DEBUG_ENABLED) - DebugLib.debugOnReturn(running_thread, calls); + frame[--calls].reset(); +// if (DebugLib.DEBUG_ENABLED) +// DebugLib.debugOnReturn(running_thread, calls); } + public final void onInstruction(int pc, Varargs v, int top) { + frame[calls-1].instr(pc, v, top); + } + /** * Get number of calls in stack * @return number of calls in current call stack @@ -324,12 +296,103 @@ public class LuaThread extends LuaValue { * @param level # of levels back from the top of the stack. * @return LuaFunction, or null if beyond the stack limits. */ - LuaFunction getFunction(int level) { - return level>0 && level<=calls? functions[calls-level]: null; + public LuaFunction getFunction(int level) { + return level>0 && level<=calls? frame[calls-level].f: null; } + + /** + * Get the traceback starting at a specific level. + * @param level + * @return String containing the traceback. + */ + public String traceback(int level) { + StringBuffer sb = new StringBuffer(); + sb.append( "stack traceback:" ); + for (LuaFunction f = null; (f = getFunction(level)) != null; ++level) { + sb.append( "\n\t" ); + sb.append( f.tostring() ); + sb.append( "main chunk" ); + } + return sb.toString(); + } + + public CallFrame getCallFrame(int level) { + if (level < 1 || level >= calls) + return null; + return frame[calls-level]; + } + + public CallFrame findCallFrame(LuaValue func) { + for (int i = 1; i <= calls; ++i) + if (frame[calls-i].f == func) + return frame[i]; + return null; + } + } - public boolean isMainThread() { - return this.state.function == null; + public static class CallFrame { + public LuaFunction f; + int pc, top; + Varargs v; + LuaValue[] stack; + public void set(LuaClosure function, Varargs varargs, LuaValue[] stack) { + this.f = function; + this.v = varargs; + this.stack = stack; + } + public void print() { + // TODO Auto-generated method stub + + } + public void set(LuaFunction function) { + this.f = function; + } + public void reset() { + this.f = null; + this.v = null; + this.stack = null; + } + public void instr(int pc, Varargs v, int top) { + this.pc = pc; + this.v = v; + this.top = top; + if (DebugLib.DEBUG_ENABLED && DebugLib.TRACE & f.isclosure()) + Print.printState(f.checkclosure(), pc, stack, top, v); + } + public int getLine() { + if (!f.isclosure()) + return 0; + LuaClosure c = (LuaClosure) f; + Prototype p = c.p; + if (p.lineinfo == null || pc < 0 || pc >= p.lineinfo[pc]) + return 0; + return p.lineinfo[pc]; + } + public Varargs getLocal(int i) { + if (!f.isclosure()) + return NONE; + LuaClosure c = (LuaClosure) f; + Prototype p = c.p; + if (i < 1 || i > stack.length) + if (p.locvars != null && p.locvars.length >= i) + return varargsOf(stack[i-1], p.locvars[i-1].varname); + else + return stack[i-1]; + return NONE; + } + public Varargs setLocal(int i, LuaValue value) { + if (!f.isclosure()) + return NONE; + LuaClosure c = (LuaClosure) f; + Prototype p = c.p; + if (i < 1 || i > stack.length) + return NONE; + stack[i] = value; + if (p.locvars != null && p.locvars.length >= i) + return p.locvars[i-1].varname; + return NONE; + } } + } diff --git a/src/core/org/luaj/vm2/LuaUserdata.java b/src/core/org/luaj/vm2/LuaUserdata.java index cf2b7b28..300980a1 100644 --- a/src/core/org/luaj/vm2/LuaUserdata.java +++ b/src/core/org/luaj/vm2/LuaUserdata.java @@ -24,7 +24,7 @@ package org.luaj.vm2; public class LuaUserdata extends LuaValue { - public final Object m_instance; + public Object m_instance; public LuaValue m_metatable; public LuaUserdata(Object obj) { diff --git a/src/core/org/luaj/vm2/LuaValue.java b/src/core/org/luaj/vm2/LuaValue.java index 8b6d433d..8cec3de4 100644 --- a/src/core/org/luaj/vm2/LuaValue.java +++ b/src/core/org/luaj/vm2/LuaValue.java @@ -109,11 +109,6 @@ package org.luaj.vm2; */ abstract public class LuaValue extends Varargs { - /** The default global environment. This must be set before lua can be used. - * Typically, it is set as a side effect of calling one of - * JsePlatform.standardGlobals() or similar functions. - */ - public static LuaValue _G; /** Type enumeration constant for lua numbers that are ints, for compatibility with lua 5.1 number patch only */ public static final int TINT = (-2); @@ -877,7 +872,16 @@ public class LuaValue extends Varargs { * @see #checkclosure() */ public LuaValue checkfunction() { argerror("function"); return null; } - + + + /** Check that the value is a Globals instance, or throw {@link LuaError} if not + *

+ * {@link Globals} are a special {@link LuaTable} that establish the default global environment. + * @return {@code this} if if an instance fof {@Globals} + * @throws LuaError if not a {@link Globals} instance. + */ + public Globals checkglobals() { argerror("globals"); return null; } + /** Check that the value is numeric, and convert and cast value to int, or throw {@link LuaError} if not numeric *

* Values that are {@link LuaNumber} will be cast to int and may lose precision. @@ -1354,13 +1358,14 @@ public class LuaValue extends Varargs { public Varargs inext(LuaValue index) { return typerror("table"); } /** - * Load a library instance by setting its environment to the global environment {@code LuaValue._G} + * Load a library instance by setting its environment to the calling object, * and calling it, which should iniitalize the library instance and - * install itself into this instance. + * install itself into this instance. The calling object should be the + * {@link Globals} environment to associate wtih the library. * @param library The callable {@link LuaValue} to load into {@code this} * @return {@link LuaValue._G} containing the result of the initialization call. */ - public LuaValue load(LuaValue library) { return load(library, _G); } + public LuaValue load(LuaValue library) { return load(library, this); } /** * Load a library instance by setting its environment to {@code env} diff --git a/src/core/org/luaj/vm2/lib/BaseLib.java b/src/core/org/luaj/vm2/lib/BaseLib.java index 75a1d299..cb2a9ee9 100644 --- a/src/core/org/luaj/vm2/lib/BaseLib.java +++ b/src/core/org/luaj/vm2/lib/BaseLib.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; +import org.luaj.vm2.Globals; import org.luaj.vm2.LoadState; import org.luaj.vm2.Lua; import org.luaj.vm2.LuaError; @@ -73,36 +74,21 @@ import org.luaj.vm2.Varargs; */ public class BaseLib extends OneArgFunction implements ResourceFinder { - public static BaseLib instance; - - public InputStream STDIN = null; - public PrintStream STDOUT = System.out; - public PrintStream STDERR = System.err; - - /** - * Singleton file opener for this Java ClassLoader realm. - * - * Unless set or changed elsewhere, will be set by the BaseLib that is created. - */ - public static ResourceFinder FINDER; - - /** - * Construct a base libarary instance. - */ - public BaseLib() { - instance = this; - } + Globals globals; public LuaValue call(LuaValue env) { + globals = env.checkglobals(); + globals.FINDER = this; + globals.baselib = this; env.set( "_G", env ); env.set( "_VERSION", Lua._VERSION ); env.set("assert", new _assert()); env.set("collectgarbage", new collectgarbage()); - env.set("dofile", new dofile(this)); + env.set("dofile", new dofile()); env.set("error", new error()); env.set("getmetatable", new getmetatable()); env.set("load", new load()); - env.set("loadfile", new loadfile(this)); + env.set("loadfile", new loadfile()); env.set("pcall", new pcall()); env.set("print", new print(this)); env.set("rawequal", new rawequal()); @@ -121,9 +107,6 @@ public class BaseLib extends OneArgFunction implements ResourceFinder { env.set("pairs", new pairs(next)); env.set("ipairs", new ipairs()); - // set the default resource finder if not set already - if ( FINDER == null ) - FINDER = this; return env; } @@ -132,8 +115,7 @@ public class BaseLib extends OneArgFunction implements ResourceFinder { * Tries to open the file as a resource, which can work for JSE and JME. */ public InputStream findResource(String filename) { - Class c = getClass(); - return c.getResourceAsStream(filename.startsWith("/")? filename: "/"+filename); + return getClass().getResourceAsStream(filename.startsWith("/")? filename: "/"+filename); } @@ -168,17 +150,13 @@ public class BaseLib extends OneArgFunction implements ResourceFinder { } // "dofile", // ( filename ) -> result1, ... - static final class dofile extends VarArgFunction { - final BaseLib baselib; - dofile(BaseLib baselib) { - this.baselib = baselib; - } + final class dofile extends VarArgFunction { public Varargs invoke(Varargs args) { args.argcheck(args.isstring(1) || args.isnil(1), 1, "filename must be string or nil"); String filename = args.isstring(1)? args.tojstring(1): null; Varargs v = filename == null? - BaseLib.loadStream( baselib.STDIN, "=stdin", "bt",LuaValue._G ): - BaseLib.loadFile( args.checkjstring(1), "bt",LuaValue._G ); + loadStream( globals.STDIN, "=stdin", "bt", globals ): + loadFile( args.checkjstring(1), "bt", globals ); return v.isnil(1)? error(v.tojstring(2)): v.arg1().invoke(); } } @@ -201,63 +179,59 @@ public class BaseLib extends OneArgFunction implements ResourceFinder { } } // "load", // ( ld [, source [, mode [, env]]] ) -> chunk | nil, msg - static final class load extends VarArgFunction { + final class load extends VarArgFunction { public Varargs invoke(Varargs args) { LuaValue ld = args.arg1(); args.argcheck(ld.isstring() || ld.isfunction(), 1, "ld must be string or function"); String source = args.optjstring(2, ld.isstring()? ld.tojstring(): "=(load)"); String mode = args.optjstring(3, "bt"); - LuaValue env = args.optvalue(4,LuaValue._G); - return BaseLib.loadStream(ld.isstring()? ld.strvalue().toInputStream(): + LuaValue env = args.optvalue(4, globals); + return loadStream(ld.isstring()? ld.strvalue().toInputStream(): new StringInputStream(ld.checkfunction()), source, mode, env); } } // "loadfile", // ( [filename [, mode [, env]]] ) -> chunk | nil, msg - static final class loadfile extends VarArgFunction { - final BaseLib baselib; - loadfile(BaseLib baselib) { - this.baselib = baselib; - } + final class loadfile extends VarArgFunction { public Varargs invoke(Varargs args) { args.argcheck(args.isstring(1) || args.isnil(1), 1, "filename must be string or nil"); String filename = args.isstring(1)? args.tojstring(1): null; String mode = args.optjstring(2, "bt"); - LuaValue env = args.optvalue(3,LuaValue._G); + LuaValue env = args.optvalue(3, globals); return filename == null? - BaseLib.loadStream( baselib.STDIN, "=stdin", mode, env ): - BaseLib.loadFile( filename, mode, env ); + loadStream( globals.STDIN, "=stdin", mode, env ): + loadFile( filename, mode, env ); } } // "pcall", // (f, arg1, ...) -> status, result1, ... - static final class pcall extends VarArgFunction { + final class pcall extends VarArgFunction { public Varargs invoke(Varargs args) { LuaValue func = args.checkvalue(1); - LuaThread.CallStack cs = LuaThread.onCall(this); + globals.callstack.onCall(this); try { return pcall(func,args.subargs(2),null); } finally { - cs.onReturn(); + globals.callstack.onReturn(); } } } // "print", // (...) -> void - static final class print extends VarArgFunction { + final class print extends VarArgFunction { final BaseLib baselib; print(BaseLib baselib) { this.baselib = baselib; } public Varargs invoke(Varargs args) { - LuaValue tostring =LuaValue._G.get("tostring"); + LuaValue tostring = globals.get("tostring"); for ( int i=1, n=args.narg(); i<=n; i++ ) { - if ( i>1 ) baselib.STDOUT.write( '\t' ); + if ( i>1 ) globals.STDOUT.write( '\t' ); LuaString s = tostring.call( args.arg(i) ).strvalue(); int z = s.indexOf((byte)0, 0); - baselib.STDOUT.write( s.m_bytes, s.m_offset, z>=0? z: s.m_length ); + globals.STDOUT.write( s.m_bytes, s.m_offset, z>=0? z: s.m_length ); } - baselib.STDOUT.println(); + globals.STDOUT.println(); return NONE; } } @@ -374,13 +348,13 @@ public class BaseLib extends OneArgFunction implements ResourceFinder { } // "xpcall", // (f, err) -> result1, ... - static final class xpcall extends VarArgFunction { + final class xpcall extends VarArgFunction { public Varargs invoke(Varargs args) { - LuaThread.CallStack cs = LuaThread.onCall(this); + globals.callstack.onCall(this); try { return pcall(args.arg1(),NONE,args.checkvalue(2)); } finally { - cs.onReturn(); + globals.callstack.onReturn(); } } } @@ -418,18 +392,19 @@ public class BaseLib extends OneArgFunction implements ResourceFinder { } } - public static Varargs pcall(LuaValue func, Varargs args, LuaValue errfunc) { - LuaValue olderr = LuaThread.setErrorFunc(errfunc); + public Varargs pcall(LuaValue func, Varargs args, LuaValue errorfunc) { try { - Varargs result = varargsOf(LuaValue.TRUE, func.invoke(args)); - LuaThread.setErrorFunc(olderr); - return result; + LuaValue olderr = globals.errorfunc; + globals.errorfunc = errorfunc; + try { + return varargsOf(LuaValue.TRUE, func.invoke(args)); + } finally { + globals.errorfunc = olderr; + } } catch ( LuaError le ) { - LuaThread.setErrorFunc(olderr); String m = le.getMessage(); return varargsOf(FALSE, m!=null? valueOf(m): NIL); } catch ( Exception e ) { - LuaThread.setErrorFunc(olderr); String m = e.getMessage(); return varargsOf(FALSE, valueOf(m!=null? m: e.toString())); } @@ -441,8 +416,8 @@ public class BaseLib extends OneArgFunction implements ResourceFinder { * @param mode * @return Varargs containing chunk, or NIL,error-text on error */ - public static Varargs loadFile(String filename, String mode, LuaValue env) { - InputStream is = FINDER.findResource(filename); + public Varargs loadFile(String filename, String mode, LuaValue env) { + InputStream is = globals.FINDER.findResource(filename); if ( is == null ) return varargsOf(NIL, valueOf("cannot open "+filename+": No such file or directory")); try { @@ -456,7 +431,7 @@ public class BaseLib extends OneArgFunction implements ResourceFinder { } } - public static Varargs loadStream(InputStream is, String chunkname, String mode, LuaValue env) { + public Varargs loadStream(InputStream is, String chunkname, String mode, LuaValue env) { try { if ( is == null ) return varargsOf(NIL, valueOf("not found: "+chunkname)); diff --git a/src/core/org/luaj/vm2/lib/Bit32Lib.java b/src/core/org/luaj/vm2/lib/Bit32Lib.java index 264d4229..a3279ed0 100644 --- a/src/core/org/luaj/vm2/lib/Bit32Lib.java +++ b/src/core/org/luaj/vm2/lib/Bit32Lib.java @@ -42,7 +42,7 @@ public class Bit32Lib extends OneArgFunction { "arshift", "lrotate", "lshift", "rrotate", "rshift" }); env.set("bit32", t); - PackageLib.instance.LOADED.set("bit32", t); + env.get("package").get("loaded").set("bit32", t); return t; } diff --git a/src/core/org/luaj/vm2/lib/CoroutineLib.java b/src/core/org/luaj/vm2/lib/CoroutineLib.java index 32e399a4..d7249d51 100644 --- a/src/core/org/luaj/vm2/lib/CoroutineLib.java +++ b/src/core/org/luaj/vm2/lib/CoroutineLib.java @@ -21,6 +21,7 @@ ******************************************************************************/ package org.luaj.vm2.lib; +import org.luaj.vm2.Globals; import org.luaj.vm2.LuaError; import org.luaj.vm2.LuaTable; import org.luaj.vm2.LuaThread; @@ -61,7 +62,10 @@ public class CoroutineLib extends OneArgFunction { static int coroutine_count = 0; + Globals globals; + public LuaValue call(LuaValue env) { + globals = env.checkglobals(); LuaTable coroutine = new LuaTable(); coroutine.set("create", new create()); coroutine.set("resume", new resume()); @@ -70,27 +74,26 @@ public class CoroutineLib extends OneArgFunction { coroutine.set("yield", new yield()); coroutine.set("wrap", new wrap()); env.set("coroutine", coroutine); - env.get("package").get("loaded").set("coroutine", TRUE); + env.get("package").get("loaded").set("coroutine", coroutine); return coroutine; } final class create extends LibFunction { public LuaValue call(LuaValue f) { - return new LuaThread(f.checkfunction()); + return new LuaThread(globals, f.checkfunction()); } } final class resume extends VarArgFunction { public Varargs invoke(Varargs args) { - if (!(args.arg1() instanceof LuaThread)) argerror(1, "thread"); - final LuaThread t = (LuaThread) args.arg1(); - return resume( t, args.subargs(2) ); + final LuaThread t = args.checkthread(1); + return t.resume( args.subargs(2) ); } } final class running extends VarArgFunction { public Varargs invoke(Varargs args) { - final LuaThread r = LuaThread.getRunning(); + final LuaThread r = globals.running_thread; return varargsOf(r, valueOf(r.isMainThread())); } } @@ -104,14 +107,14 @@ public class CoroutineLib extends OneArgFunction { final class yield extends VarArgFunction { public Varargs invoke(Varargs args) { - return yield( args ); + return globals.yield( args ); } } final class wrap extends LibFunction { public LuaValue call(LuaValue f) { final LuaValue func = f.checkfunction(); - final LuaThread thread = new LuaThread(func); + final LuaThread thread = new LuaThread(globals, func); return new wrapper(thread); } } @@ -122,7 +125,7 @@ public class CoroutineLib extends OneArgFunction { this.luathread = luathread; } public Varargs invoke(Varargs args) { - final Varargs result = resume(luathread, args); + final Varargs result = luathread.resume(args); if ( result.arg1().toboolean() ) { return result.subargs(2); } else { @@ -130,19 +133,4 @@ public class CoroutineLib extends OneArgFunction { } } } - - Varargs yield(Varargs args) { - final LuaThread.State s = LuaThread.getRunning().state; - if (s.function == null) - throw new LuaError("cannot yield main thread"); - return s.lua_yield(args); - } - - Varargs resume(LuaThread t, Varargs args) { - final LuaThread.State s = t.state; - if (s.status > LuaThread.STATUS_SUSPENDED) - return LuaValue.varargsOf(LuaValue.FALSE, - LuaValue.valueOf("cannot resume "+(s.status==LuaThread.STATUS_DEAD? "dead": "non-suspended")+" coroutine")); - return s.lua_resume(t, args); - } } diff --git a/src/core/org/luaj/vm2/lib/DebugLib.java b/src/core/org/luaj/vm2/lib/DebugLib.java index f07a9d93..aaaf52a0 100644 --- a/src/core/org/luaj/vm2/lib/DebugLib.java +++ b/src/core/org/luaj/vm2/lib/DebugLib.java @@ -23,6 +23,7 @@ package org.luaj.vm2.lib; import java.lang.ref.WeakReference; +import org.luaj.vm2.Globals; import org.luaj.vm2.Lua; import org.luaj.vm2.LuaBoolean; import org.luaj.vm2.LuaClosure; @@ -33,6 +34,7 @@ import org.luaj.vm2.LuaNumber; import org.luaj.vm2.LuaString; import org.luaj.vm2.LuaTable; import org.luaj.vm2.LuaThread; +import org.luaj.vm2.LuaUserdata; import org.luaj.vm2.LuaValue; import org.luaj.vm2.Print; import org.luaj.vm2.Prototype; @@ -66,45 +68,13 @@ import org.luaj.vm2.Varargs; * @see JmePlatform * @see http://www.lua.org/manual/5.1/manual.html#5.9 */ -public class DebugLib extends VarArgFunction { +public class DebugLib extends OneArgFunction { public static final boolean CALLS = (null != System.getProperty("CALLS")); public static final boolean TRACE = (null != System.getProperty("TRACE")); // leave this unset to allow obfuscators to // remove it in production builds public static boolean DEBUG_ENABLED; - - static final String[] NAMES = { - "debug", - "gethook", - "getinfo", - "getlocal", - "getmetatable", - "getregistry", - "getupvalue", - "sethook", - "setlocal", - "setmetatable", - "setupvalue", - "traceback", - }; - - private static final int INIT = 0; - private static final int DEBUG = 1; - private static final int GETHOOK = 2; - private static final int GETINFO = 3; - private static final int GETLOCAL = 4; - private static final int GETMETATABLE = 5; - private static final int GETREGISTRY = 6; - private static final int GETUPVALUE = 7; - private static final int SETHOOK = 8; - private static final int SETLOCAL = 9; - private static final int SETMETATABLE = 10; - private static final int SETUPVALUE = 11; - private static final int TRACEBACK = 12; - - /* maximum stack for a Lua function */ - private static final int MAXSTACK = 250; private static final LuaString LUA = valueOf("Lua"); private static final LuaString JAVA = valueOf("Java"); @@ -133,456 +103,225 @@ public class DebugLib extends VarArgFunction { private static final LuaString CURRENTLINE = valueOf("currentline"); private static final LuaString ACTIVELINES = valueOf("activelines"); - public DebugLib() { - } + Globals globals; - private LuaTable init(LuaValue env) { + public LuaTable call(LuaValue env) { + globals = env.checkglobals(); + globals.debuglib = this; DEBUG_ENABLED = true; - LuaTable t = new LuaTable(); - bind(t, DebugLib.class, NAMES, DEBUG); - env.set("debug", t); - PackageLib.instance.LOADED.set("debug", t); - return t; + LuaTable debug = new LuaTable(); + env.set("debug", new debug()); + env.set("gethook", new gethook()); + env.set("sethook", new sethook()); + env.get("package").get("loaded").set("debug", debug); + return debug; } - - public Varargs invoke(Varargs args) { - switch ( opcode ) { - case INIT: return init(args.arg1()); - case DEBUG: return _debug(args); - case GETHOOK: return _gethook(args); - case GETINFO: return _getinfo(args,this); - case GETLOCAL: return _getlocal(args); - case GETMETATABLE: return _getmetatable(args); - case GETREGISTRY: return _getregistry(args); - case GETUPVALUE: return _getupvalue(args); - case SETHOOK: return _sethook(args); - case SETLOCAL: return _setlocal(args); - case SETMETATABLE: return _setmetatable(args); - case SETUPVALUE: return _setupvalue(args); - case TRACEBACK: return _traceback(args); - default: return NONE; + + // debug.debug() + static final class debug extends ZeroArgFunction { + public LuaValue call() { + return NONE; } } - // ------------------------ Debug Info management -------------------------- - // - // when DEBUG_ENABLED is set to true, these functions will be called - // by Closure instances as they process bytecodes. - // - // Each thread will get a DebugState attached to it by the debug library - // which will track function calls, hook functions, etc. - // - static class DebugInfo { - LuaValue func; - LuaClosure closure; - LuaValue[] stack; - Varargs varargs, extras; - int pc, top; - - private DebugInfo() { - func = NIL; + // debug.gethook ([thread]) + final class gethook extends VarArgFunction { + public Varargs invoke(Varargs args) { + LuaThread t = args.optthread(1, globals.running_thread); + return varargsOf( + t.hookfunc, + valueOf((t.hookcall?"c":"")+(t.hookline?"l":"")+(t.hookrtrn?"r":"")), + valueOf(t.hookcount)); } - private DebugInfo(LuaValue func) { - pc = -1; - setfunction( func ); + } + + // debug.getinfo ([thread,] f [, what]) + final class getinfo extends VarArgFunction { + public Varargs invoke(Varargs args) { + int a=1; + LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running_thread; + LuaValue func = args.optfunction(a++, null); + String what = args.optjstring(a++,""); + return NONE; } - void setargs(Varargs varargs, LuaValue[] stack) { - this.varargs = varargs; - this.stack = stack; + } + + // debug.getlocal ([thread,] f, local) + final class getlocal extends VarArgFunction { + public Varargs invoke(Varargs args) { + int a=1; + LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running_thread; + LuaValue func = args.optfunction(a++, null); + LuaValue local = args.checkvalue(a++); + return thread.globals.callstack.findCallFrame(func).getLocal(local.toint()); } - void setfunction( LuaValue func ) { - this.func = func; - this.closure = (func instanceof LuaClosure? (LuaClosure) func: null); + } + + // debug.getmetatable (value) + final class getmetatable extends LibFunction { + public LuaValue call(LuaValue v) { + LuaValue mt = v.getmetatable(); + return mt != null? mt: NIL; } - void clear() { - func = NIL; - closure = null; - stack = null; - varargs = extras = null; - pc = top = 0; + } + + // debug.getregistry () + final class getregistry extends ZeroArgFunction { + public LuaValue call() { + return globals; } - public void bytecode(int pc, Varargs extras, int top) { - this.pc = pc; - this.top = top; - this.extras = extras; + } + + // debug.getupvalue (f, up) + static final class getupvalue extends VarArgFunction { + public Varargs invoke(Varargs args) { + LuaValue func = args.checkfunction(1); + int up = args.checkint(2); + if ( func instanceof LuaClosure ) { + LuaClosure c = (LuaClosure) func; + LuaString name = findupvalue(c, up); + if ( name != null ) { + return varargsOf(name, c.upValues[up-1].getValue() ); + } + } + return NIL; } - public int currentline() { - if ( closure == null ) return -1; - int[] li = closure.p.lineinfo; - return li==null || pc<0 || pc>=li.length? -1: li[pc]; - } - public LuaString[] getfunckind() { - if ( closure == null || pc<0 ) return null; - int stackpos = (closure.p.code[pc] >> 6) & 0xff; - return getobjname(this, pc, stackpos); - } - public String sourceline() { - if ( closure == null ) return func.tojstring(); - String s = closure.p.source.tojstring(); - int line = currentline(); - return (s.startsWith("@")||s.startsWith("=")? s.substring(1): s) + ":" + line; - } - public String tracename() { - // if ( func != null ) - // return func.tojstring(); - LuaString[] kind = getfunckind(); - if ( kind == null ) - return "function ?"; - return "function "+kind[0].tojstring(); - } - public LuaString getlocalname(int index) { - if ( closure == null ) return null; - return closure.p.getlocalname(index, pc); - } - public String tojstring() { - return tracename()+" "+sourceline(); + } + + // debug.getuservalue (u) + static final class getuservalue extends LibFunction { + public LuaValue call(LuaValue u) { + return u.isuserdata()? u: NIL; } } - /** DebugState is associated with a Thread */ - static class DebugState { - private final WeakReference thread_ref; - private int debugCalls = 0; - private DebugInfo[] debugInfo = new DebugInfo[LuaThread.MAX_CALLSTACK+1]; - private LuaValue hookfunc; - private boolean hookcall,hookline,hookrtrn,inhook; - private int hookcount,hookcodes; - private int line; - DebugState(LuaThread thread) { - this.thread_ref = new WeakReference(thread); + + // debug.sethook ([thread,] hook, mask [, count]) + final class sethook extends VarArgFunction { + public Varargs call(Varargs args) { + int a=1; + LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running_thread; + LuaValue func = args.optfunction(a++, null); + String str = args.optjstring(a++,""); + int count = args.optint(a++,0); + boolean call=false,line=false,rtrn=false; + for ( int i=0; i calls ) - debugInfo[--debugCalls].clear(); - } - void callHookFunc(DebugState ds, LuaString type, LuaValue arg) { - if ( inhook || hookfunc == null ) - return; - inhook = true; + } + + // debug.setmetatable (value, table) + final class setmetatable extends VarArgFunction { + public Varargs invoke(Varargs args) { + LuaValue object = args.arg(1); try { - int n = debugCalls; - ds.nextInfo().setargs( arg, null ); - ds.pushInfo(n+1).setfunction(hookfunc); - try { - hookfunc.call(type,arg); - } finally { - ds.popInfo(n); - } - } catch ( Exception e ) { - e.printStackTrace(); - } finally { - inhook = false; - } - } - public void sethook(LuaValue func, boolean call, boolean line, boolean rtrn, int count) { - this.hookcount = count; - this.hookcall = call; - this.hookline = line; - this.hookrtrn = rtrn; - this.hookfunc = func; - } - DebugInfo getDebugInfo() { - try { - return debugInfo[debugCalls-1]; - } catch ( Exception e ) { - if ( debugCalls <= 0 ) - return debugInfo[debugCalls++] = new DebugInfo(); - return null; - } - } - DebugInfo getDebugInfo(int level) { - return level < 0 || level >= debugCalls? null: debugInfo[debugCalls-level-1]; - } - public DebugInfo findDebugInfo(LuaValue func) { - for ( int i=debugCalls; --i>=0; ) { - if ( debugInfo[i].func == func ) { - return debugInfo[i]; - } - } - return new DebugInfo(func); - } - public String tojstring() { - LuaThread thread = (LuaThread) thread_ref.get(); - return thread != null? DebugLib.traceback(thread, 0): "orphaned thread"; - } - } - - static DebugState getDebugState( LuaThread thread ) { - if ( thread.debugState == null ) - thread.debugState = new DebugState(thread); - return (DebugState) thread.debugState; - } - - static DebugState getDebugState() { - return getDebugState( LuaThread.getRunning() ); - } - - /** Called by Closures to set up stack and arguments to next call */ - public static void debugSetupCall(Varargs args, LuaValue[] stack) { - DebugState ds = getDebugState(); - if ( ds.inhook ) - return; - ds.nextInfo().setargs( args, stack ); - } - - /** Called by Closures and recursing java functions on entry - * @param thread the thread for the call - * @param calls the number of calls in the call stack - * @param func the function called - */ - public static void debugOnCall(LuaThread thread, int calls, LuaFunction func) { - DebugState ds = getDebugState(); - if ( ds.inhook ) - return; - DebugInfo di = ds.pushInfo(calls); - di.setfunction( func ); - if(CALLS)System.out.println("calling "+func); - if ( ds.hookcall ) - ds.callHookFunc( ds, CALL, LuaValue.NIL ); - } - - /** Called by Closures and recursing java functions on return - * @param thread the thread for the call - * @param calls the number of calls in the call stack - */ - public static void debugOnReturn(LuaThread thread, int calls) { - DebugState ds = getDebugState(thread); - if ( ds.inhook ) - return; - if(CALLS)System.out.println("returning"); - try { - if ( ds.hookrtrn ) - ds.callHookFunc( ds, RETURN, LuaValue.NIL ); - } finally { - getDebugState().popInfo(calls); - } - } - - /** Called by Closures on bytecode execution */ - public static void debugBytecode( int pc, Varargs extras, int top ) { - DebugState ds = getDebugState(); - if ( ds.inhook ) - return; - DebugInfo di = ds.getDebugInfo(); - if(TRACE)Print.printState(di.closure, pc, di.stack, top, di.varargs); - di.bytecode( pc, extras, top ); - if ( ds.hookcount > 0 ) { - if ( ++ds.hookcodes >= ds.hookcount ) { - ds.hookcodes = 0; - ds.callHookFunc( ds, COUNT, LuaValue.NIL ); - } - } - if ( ds.hookline ) { - int newline = di.currentline(); - if ( newline != ds.line ) { - int c = di.closure.p.code[pc]; - if ( (c&0x3f) != Lua.OP_JMP || ((c>>>14)-0x1ffff) >= 0 ) { - ds.line = newline; - ds.callHookFunc( ds, LINE, LuaValue.valueOf(newline) ); + LuaValue mt = args.opttable(2, null); + switch ( object.type() ) { + case TNIL: LuaNil.s_metatable = mt; break; + case TNUMBER: LuaNumber.s_metatable = mt; break; + case TBOOLEAN: LuaBoolean.s_metatable = mt; break; + case TSTRING: LuaString.s_metatable = mt; break; + case TFUNCTION: LuaFunction.s_metatable = mt; break; + case TTHREAD: LuaThread.s_metatable = mt; break; + default: object.setmetatable( mt ); } + return LuaValue.TRUE; + } catch ( LuaError e ) { + return varargsOf(FALSE, valueOf(e.toString())); } } } - // ------------------- library function implementations ----------------- - - // j2se subclass may wish to override and provide actual console here. - // j2me platform has not System.in to provide console. - static Varargs _debug(Varargs args) { - return NONE; - } - - static Varargs _gethook(Varargs args) { - int a=1; - LuaThread thread = args.isthread(a)? args.checkthread(a++): LuaThread.getRunning(); - DebugState ds = getDebugState(thread); - return varargsOf( - ds.hookfunc, - valueOf((ds.hookcall?"c":"")+(ds.hookline?"l":"")+(ds.hookrtrn?"r":"")), - valueOf(ds.hookcount)); - } - - static Varargs _sethook(Varargs args) { - int a=1; - LuaThread thread = args.isthread(a)? args.checkthread(a++): LuaThread.getRunning(); - LuaValue func = args.optfunction(a++, null); - String str = args.optjstring(a++,""); - int count = args.optint(a++,0); - boolean call=false,line=false,rtrn=false; - for ( int i=0; i0? - ds.getDebugInfo(level-1): - new DebugInfo( level0func ); - } else { - di = ds.findDebugInfo( func.checkfunction() ); - } - if ( di == null ) - return NIL; - - // start a table - LuaTable info = new LuaTable(); - LuaClosure c = di.closure; - for (int i = 0, j = what.length(); i < j; i++) { - switch (what.charAt(i)) { - case 'S': { - if ( c != null ) { - Prototype p = c.p; - info.set(WHAT, LUA); - info.set(SOURCE, p.source); - info.set(SHORT_SRC, valueOf(sourceshort(p))); - info.set(LINEDEFINED, valueOf(p.linedefined)); - info.set(LASTLINEDEFINED, valueOf(p.lastlinedefined)); - } else { - String shortName = di.func.tojstring(); - LuaString name = LuaString.valueOf("[Java] "+shortName); - info.set(WHAT, JAVA); - info.set(SOURCE, name); - info.set(SHORT_SRC, valueOf(shortName)); - info.set(LINEDEFINED, LuaValue.MINUSONE); - info.set(LASTLINEDEFINED, LuaValue.MINUSONE); - } - break; - } - case 'l': { - int line = di.currentline(); - info.set( CURRENTLINE, valueOf(line) ); - break; - } - case 'u': { - info.set(NUPS, valueOf(c!=null? c.p.upvalues.length: 0)); - break; - } - case 'n': { - LuaString[] kind = di.getfunckind(); - info.set(NAME, kind!=null? kind[0]: QMARK); - info.set(NAMEWHAT, kind!=null? kind[1]: EMPTYSTRING); - break; - } - case 'f': { - info.set( FUNC, di.func ); - break; - } - case 'L': { - LuaTable lines = new LuaTable(); - info.set(ACTIVELINES, lines); -// if ( di.luainfo != null ) { -// int line = di.luainfo.currentline(); -// if ( line >= 0 ) -// lines.set(1, IntValue.valueOf(line)); -// } - break; + // debug.setupvalue (f, up, value) + final class setupvalue extends VarArgFunction { + public Varargs invoke(Varargs args) { + LuaValue func = args.checkfunction(1); + int up = args.checkint(2); + LuaValue value = args.arg(3); + if ( func instanceof LuaClosure ) { + LuaClosure c = (LuaClosure) func; + LuaString name = findupvalue(c, up); + if ( name != null ) { + c.upValues[up-1].setValue(value); + return name; } } - } - return info; - } - - public static String sourceshort(Prototype p) { - String name = p.source.tojstring(); - if ( name.startsWith("@") || name.startsWith("=") ) - name = name.substring(1); - else if ( name.startsWith("\033") ) - name = "binary string"; - return name; - } - - static Varargs _getlocal(Varargs args) { - int a=1; - LuaThread thread = args.isthread(a)? args.checkthread(a++): LuaThread.getRunning(); - int level = args.checkint(a++); - int local = args.checkint(a++); - - DebugState ds = getDebugState(thread); - DebugInfo di = ds.getDebugInfo(level-1); - LuaString name = (di!=null? di.getlocalname(local): null); - if ( name != null ) { - LuaValue value = di.stack[local-1]; - return varargsOf( name, value ); - } else { return NIL; } } - static Varargs _setlocal(Varargs args) { - int a=1; - LuaThread thread = args.isthread(a)? args.checkthread(a++): LuaThread.getRunning(); - int level = args.checkint(a++); - int local = args.checkint(a++); - LuaValue value = args.arg(a++); - - DebugState ds = getDebugState(thread); - DebugInfo di = ds.getDebugInfo(level-1); - LuaString name = (di!=null? di.getlocalname(local): null); - if ( name != null ) { - di.stack[local-1] = value; - return name; - } else { + // debug.setuservalue (udata, value) + final class setuservalue extends VarArgFunction { + public Varargs invoke(Varargs args) { + Object o = args.checkuserdata(1); + LuaValue v = args.checkvalue(2); + LuaUserdata u = (LuaUserdata)args.arg1(); + u.m_instance = v.checkuserdata(); + u.m_metatable = v.getmetatable(); + return NONE; + } + } + + // debug.traceback ([thread,] [message [, level]]) + final class traceback extends VarArgFunction { + public Varargs invoke(Varargs args) { + int a=1; + LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running_thread; + String message = args.optjstring(a++, null); + int level = args.optint(a++,1); + String tb = globals.callstack.traceback(level-1); + return valueOf(message!=null? message+"\n"+tb: tb); + } + } + + // debug.upvalueid (f, n) + final class upvalueid extends VarArgFunction { + public Varargs invoke(Varargs args) { + LuaValue func = args.checkfunction(1); + int up = args.checkint(2); + if ( func instanceof LuaClosure ) { + LuaClosure c = (LuaClosure) func; + if ( c.upValues != null && up > 0 && up <= c.upValues.length ) { + return userdataOf(c.upValues[up-1].hashCode()); + } + } return NIL; } } - static LuaValue _getmetatable(Varargs args) { - LuaValue object = args.arg(1); - LuaValue mt = object.getmetatable(); - return mt!=null? mt: NIL; - } - - static Varargs _setmetatable(Varargs args) { - LuaValue object = args.arg(1); - try { - LuaValue mt = args.opttable(2, null); - switch ( object.type() ) { - case TNIL: LuaNil.s_metatable = mt; break; - case TNUMBER: LuaNumber.s_metatable = mt; break; - case TBOOLEAN: LuaBoolean.s_metatable = mt; break; - case TSTRING: LuaString.s_metatable = mt; break; - case TFUNCTION: LuaFunction.s_metatable = mt; break; - case TTHREAD: LuaThread.s_metatable = mt; break; - default: object.setmetatable( mt ); - } - return LuaValue.TRUE; - } catch ( LuaError e ) { - return varargsOf(FALSE, valueOf(e.toString())); + // debug.upvaluejoin (f1, n1, f2, n2) + final class upvaluejoin extends VarArgFunction { + public Varargs invoke(Varargs args) { + LuaValue f1 = args.checkfunction(1); + int n1 = args.checkint(2); + LuaValue f2 = args.checkfunction(3); + int n2 = args.checkint(4); + return NONE; } } - static Varargs _getregistry(Varargs args) { - return new LuaTable(); - } - static LuaString findupvalue(LuaClosure c, int up) { if ( c.upValues != null && up > 0 && up <= c.upValues.length ) { if ( c.p.upvalues != null && up <= c.p.upvalues.length ) @@ -593,105 +332,93 @@ public class DebugLib extends VarArgFunction { return null; } - static Varargs _getupvalue(Varargs args) { - LuaValue func = args.checkfunction(1); - int up = args.checkint(2); - if ( func instanceof LuaClosure ) { - LuaClosure c = (LuaClosure) func; - LuaString name = findupvalue(c, up); - if ( name != null ) { - return varargsOf(name, c.upValues[up-1].getValue() ); - } - } - return NIL; - } - - static LuaValue _setupvalue(Varargs args) { - LuaValue func = args.checkfunction(1); - int up = args.checkint(2); - LuaValue value = args.arg(3); - if ( func instanceof LuaClosure ) { - LuaClosure c = (LuaClosure) func; - LuaString name = findupvalue(c, up); - if ( name != null ) { - c.upValues[up-1].setValue(value); - return name; - } - } - return NIL; - } - - static LuaValue _traceback(Varargs args) { + + // ------------------- library function implementations ----------------- + protected Varargs _getinfo(Varargs args, LuaValue level0func) { int a=1; - LuaThread thread = args.isthread(a)? args.checkthread(a++): LuaThread.getRunning(); - String message = args.optjstring(a++, null); - int level = args.optint(a++,1); - String tb = DebugLib.traceback(thread, level-1); - return valueOf(message!=null? message+"\n"+tb: tb); - } - - // =================== public utilities ==================== - - /** - * Get a traceback as a string for the current thread - */ - public static String traceback(int level) { - return traceback(LuaThread.getRunning(), level); - } - - /** - * Get a traceback for a particular thread. - * @param thread LuaThread to provide stack trace for - * @param level 0-based level to start reporting on - * @return String containing the stack trace. - */ - public static String traceback(LuaThread thread, int level) { - StringBuffer sb = new StringBuffer(); - DebugState ds = getDebugState(thread); - sb.append( "stack traceback:" ); - DebugInfo di = ds.getDebugInfo(level); - if ( di != null ) { - sb.append( "\n\t" ); - sb.append( di.sourceline() ); - sb.append( " in " ); - while ( (di = ds.getDebugInfo(++level)) != null ) { - sb.append( di.tracename() ); - sb.append( "\n\t" ); - sb.append( di.sourceline() ); - sb.append( " in " ); + LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running_thread; + LuaValue func = args.arg(a++); + String what = args.optjstring(a++, "nSluf"); + LuaThread.CallStack callstack = thread.globals.callstack; + + // find the stack info + LuaThread.CallFrame frame; + if ( func.isnumber() ) { + frame = callstack.getCallFrame(func.toint()); + } else { + frame = callstack.findCallFrame(func); + } + if (frame == null) + return NIL; + + // start a table + LuaTable info = new LuaTable(); + LuaClosure c = frame.f.isclosure()? (LuaClosure) frame.f: null; + for (int i = 0, j = what.length(); i < j; i++) { + switch (what.charAt(i)) { + case 'S': { + if ( c != null ) { + Prototype p = c.p; + info.set(WHAT, LUA); + info.set(SOURCE, p.source); + info.set(SHORT_SRC, valueOf(sourceshort(p))); + info.set(LINEDEFINED, valueOf(p.linedefined)); + info.set(LASTLINEDEFINED, valueOf(p.lastlinedefined)); + } else { + String shortName = frame.f.tojstring(); + LuaString name = LuaString.valueOf("[Java] "+shortName); + info.set(WHAT, JAVA); + info.set(SOURCE, name); + info.set(SHORT_SRC, valueOf(shortName)); + info.set(LINEDEFINED, LuaValue.MINUSONE); + info.set(LASTLINEDEFINED, LuaValue.MINUSONE); + } + break; + } + case 'l': { + int line = frame.getLine(); + info.set( CURRENTLINE, valueOf(line) ); + break; + } + case 'u': { + info.set(NUPS, valueOf(c!=null? c.p.upvalues.length: 0)); + break; + } + case 'n': { + LuaString[] kind = getfunckind(frame.f); + info.set(NAME, kind!=null? kind[0]: QMARK); + info.set(NAMEWHAT, kind!=null? kind[1]: EMPTYSTRING); + break; + } + case 'f': { + info.set( FUNC, frame.f ); + break; + } + /* + case 'L': { + LuaTable lines = new LuaTable(); + info.set(ACTIVELINES, lines); + if ( c !+ null && c.luainfo != null ) { + int line = c.luainfo.currentline(); + if ( line >= 0 ) + lines.set(1, IntValue.valueOf(line)); + } + break; + } + */ } - sb.append( "main chunk" ); } - return sb.toString(); + return info; } - /** - * Get file and line for the nearest calling closure. - * @return String identifying the file and line of the nearest lua closure, - * or the function name of the Java call if no closure is being called. - */ - public static String fileline() { - DebugState ds = getDebugState(LuaThread.getRunning()); - DebugInfo di; - for ( int i=0, n=ds.debugCalls; i * A simple example of initializing globals and using them from Java is: *

 {@code
- * LuaValue _G = JmePlatform.standardGlobals();
+ * Globals _G = JmePlatform.standardGlobals();
  * _G.get("print").call(LuaValue.valueOf("hello, world"));
  * } 
*

@@ -71,6 +72,7 @@ import org.luaj.vm2.lib.TableLib; *

* The standard globals will contain all standard libraries in their JME flavors: *