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* 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
@@ -71,6 +72,7 @@ import org.luaj.vm2.lib.TableLib;
*
* The standard globals will contain all standard libraries in their JME flavors:
*
* A simple example of initializing globals and using them from Java is:
*
* Once globals are created, a simple way to load and run a script is:
*
* although {@code require} could also be used:
@@ -58,6 +60,7 @@ import org.luaj.vm2.lib.TableLib;
*
* The standard globals will contain all standard libraries plus {@code luajava}:
* {@code
- * LuaValue _G = JmePlatform.standardGlobals();
+ * Globals _G = JmePlatform.standardGlobals();
* _G.get("print").call(LuaValue.valueOf("hello, world"));
* }
*
+ *
{@code
- * LuaValue _G = JsePlatform.standardGlobals();
+ * Globals _G = JsePlatform.standardGlobals();
* _G.get("print").call(LuaValue.valueOf("hello, world"));
* }
* {@code
- * LoadState.load( new FileInputStream("main.lua"), "main.lua", _G ).call();
+ * _G.load( new FileInputStream("main.lua"), "main.lua" ).call();
* }
*
+ *