Improve debug lib consistency with lua 5.2

This commit is contained in:
James Roseborough
2012-09-15 16:11:20 +00:00
parent 6ae66f6fce
commit e2646669d6
6 changed files with 287 additions and 268 deletions

View File

@@ -192,15 +192,15 @@ public class LuaClosure extends LuaFunction {
// TODO: use linked list.
UpValue[] openups = p.p.length>0? new UpValue[stack.length]: null;
// debug wants args to this function
if (DebugLib.DEBUG_ENABLED && globals != null)
globals.running_thread.callstack.onCall( this, varargs, stack );
// allow for debug hooks
if (globals != null && globals.debuglib != null)
globals.debuglib.onCall( this, varargs, stack );
// process instructions
try {
while ( true ) {
if (DebugLib.DEBUG_ENABLED && globals != null)
globals.running_thread.callstack.onInstruction( pc, v, top );
if (globals != null && globals.debuglib != null)
globals.debuglib.onInstruction( pc, v, top );
// pull out instruction
i = code[pc++];
@@ -507,12 +507,12 @@ public class LuaClosure extends LuaFunction {
processErrorHooks(le, p, pc);
throw le;
} finally {
if (DebugLib.DEBUG_ENABLED && globals != null)
globals.running_thread.callstack.onReturn();
if ( openups != null )
for ( int u=openups.length; --u>=0; )
if ( openups[u] != null )
openups[u].close();
if (globals != null && globals.debuglib != null)
globals.debuglib.onReturn();
}
}
@@ -538,8 +538,8 @@ public class LuaClosure extends LuaFunction {
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.running_thread.callstack.traceback(le.level);
if (globals != null && globals.debuglib != null)
le.traceback += globals.debuglib.traceback(le.level);
}
private UpValue findupval(LuaValue[] stack, short idx, UpValue[] openups) {
@@ -562,21 +562,4 @@ public class LuaClosure extends LuaFunction {
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.running_thread.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;
}
}

View File

@@ -48,7 +48,7 @@ public class LuaError extends RuntimeException {
public String getMessage() {
return traceback != null? traceback:
(fileline != null? fileline: "?:-1") + " " + super.getMessage();
(fileline != null? fileline + " ": "") + super.getMessage();
}
/** Construct LuaError when a program exception occurs.

View File

@@ -84,21 +84,14 @@ public class LuaThread extends LuaValue {
public final State state;
public final CallStack callstack = new CallStack();
public static final int MAX_CALLSTACK = 256;
/** 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;
/** Thread-local used by DebugLib to store debugging state.
* This is ano opaque value that should not be modified by applications. */
public Object callstack;
public final Globals globals;
@@ -239,179 +232,5 @@ public class LuaThread extends LuaValue {
}
}
}
public static class CallStack {
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
*/
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);
}
/**
* Method to signal the end of a call
* @see DebugLib
*/
public final void onReturn() {
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
* @see DebugLib
*/
public final int getCallstackDepth() {
return calls;
}
/**
* Get the function at a particular level of the stack.
* @param level # of levels back from the top of the stack.
* @return LuaFunction, or null if beyond the stack limits.
*/
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:" );
CallFrame c = getCallFrame(level);
if (c != null) {
sb.append("\n\t");
sb.append( c.sourceline() );
sb.append( " in " );
while ( (c = getCallFrame(++level)) != null ) {
sb.append( c.tracename() );
sb.append( "\n\t" );
sb.append( c.sourceline() );
sb.append( " in " );
}
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 static class CallFrame {
public LuaFunction f;
public int pc;
int top;
Varargs v;
LuaValue[] stack;
boolean tail;
public void set(LuaClosure function, Varargs varargs, LuaValue[] stack) {
this.f = function;
this.v = varargs;
this.stack = stack;
this.tail = false;
}
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 (f.checkclosure().p.code[pc] == Lua.OP_TAILCALL)
this.tail = true;
if (DebugLib.TRACE)
Print.printState(f.checkclosure(), pc, stack, top, v);
}
public Varargs getLocal(int i) {
LuaString name = getlocalname(i);
if ( name != null )
return varargsOf( name, stack[i-1] );
else
return NIL;
}
public Varargs setLocal(int i, LuaValue value) {
LuaString name = getlocalname(i);
if ( name != null ) {
stack[i-1] = value;
return name;
} else {
return NIL;
}
}
public int currentline() {
if ( !f.isclosure() ) return -1;
int[] li = f.checkclosure().p.lineinfo;
return li==null || pc<0 || pc>=li.length? -1: li[pc];
}
public String sourceline() {
if ( !f.isclosure() ) return f.tojstring();
String s = f.checkclosure().p.source.tojstring();
int line = currentline();
return (s.startsWith("@")||s.startsWith("=")? s.substring(1): s) + ":" + line;
}
public String tracename() {
LuaString[] kind = DebugLib.getfuncname(this);
if ( kind == null )
return "function ?";
return "function "+kind[0].tojstring();
}
public LuaString getlocalname(int index) {
if ( !f.isclosure() ) return null;
return f.checkclosure().p.getlocalname(index, pc);
}
public String tojstring() {
return tracename()+" "+sourceline();
}
public boolean istailcall() {
return tail;
}
}
}

View File

@@ -208,11 +208,13 @@ public class BaseLib extends OneArgFunction implements ResourceFinder {
final class pcall extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaValue func = args.checkvalue(1);
globals.running_thread.callstack.onCall(this);
if (globals != null && globals.debuglib != null)
globals.debuglib.onCall(this);
try {
return pcall(func,args.subargs(2),null);
} finally {
globals.running_thread.callstack.onReturn();
if (globals != null && globals.debuglib != null)
globals.debuglib.onReturn();
}
}
}
@@ -350,11 +352,13 @@ public class BaseLib extends OneArgFunction implements ResourceFinder {
// "xpcall", // (f, err) -> result1, ...
final class xpcall extends VarArgFunction {
public Varargs invoke(Varargs args) {
globals.running_thread.callstack.onCall(this);
if (globals != null && globals.debuglib != null)
globals.debuglib.onCall(this);
try {
return pcall(args.arg1(),NONE,args.checkvalue(2));
return pcall(args.arg1(),args.subargs(3),args.checkvalue(2));
} finally {
globals.running_thread.callstack.onReturn();
if (globals != null && globals.debuglib != null)
globals.debuglib.onReturn();
}
}
}

View File

@@ -26,21 +26,17 @@ import org.luaj.vm2.LoadState.LuaCompiler;
import org.luaj.vm2.Lua;
import org.luaj.vm2.LuaBoolean;
import org.luaj.vm2.LuaClosure;
import org.luaj.vm2.LuaError;
import org.luaj.vm2.LuaFunction;
import org.luaj.vm2.LuaNil;
import org.luaj.vm2.LuaNumber;
import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaThread;
import org.luaj.vm2.LuaThread.CallFrame;
import org.luaj.vm2.LuaUserdata;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Print;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.Varargs;
import org.luaj.vm2.lib.jme.JmePlatform;
import org.luaj.vm2.lib.jse.JsePlatform;
import org.luaj.vm2.luajc.LuaJC;
/**
* Subclass of {@link LibFunction} which implements the lua standard {@code debug}
@@ -73,10 +69,6 @@ import org.luaj.vm2.luajc.LuaJC;
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;
private static final LuaString LUA = valueOf("Lua");
private static final LuaString JAVA = valueOf("Java");
@@ -89,8 +81,7 @@ public class DebugLib extends OneArgFunction {
private static final LuaString CALL = valueOf("call");
private static final LuaString LINE = valueOf("line");
private static final LuaString COUNT = valueOf("count");
private static final LuaString RETURN = valueOf("return");
private static final LuaString TAILRETURN = valueOf("tail return");
private static final LuaString RETURN = valueOf("return");
private static final LuaString CONSTANT = valueOf("constant");
private static final LuaString FOR_ITERATOR = valueOf("for iterator");
private static final LuaString METAMETHOD = valueOf("metamethod");
@@ -112,10 +103,18 @@ public class DebugLib extends OneArgFunction {
Globals globals;
LuaValue hookfunc;
boolean hookline;
boolean hookcall;
boolean hookrtrn;
int hookcount;
boolean inhook;
int lastline;
int bytecodes;
public LuaTable call(LuaValue env) {
globals = env.checkglobals();
globals.debuglib = this;
DEBUG_ENABLED = true;
LuaTable debug = new LuaTable();
debug.set("debug", new debug());
debug.set("gethook", new gethook());
@@ -148,11 +147,10 @@ public class DebugLib extends OneArgFunction {
// 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));
hookfunc,
valueOf((hookcall?"c":"")+(hookline?"l":"")+(hookrtrn?"r":"")),
valueOf(hookcount));
}
}
@@ -163,10 +161,10 @@ public class DebugLib extends OneArgFunction {
LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running_thread;
LuaValue func = args.arg(a++);
String what = args.optjstring(a++, "flnStu");
LuaThread.CallStack callstack = thread.callstack;
DebugLib.CallStack callstack = callstack(thread);
// find the stack info
LuaThread.CallFrame frame;
DebugLib.CallFrame frame;
if ( func.isnumber() ) {
frame = callstack.getCallFrame(func.toint());
if (frame == null)
@@ -220,7 +218,7 @@ public class DebugLib extends OneArgFunction {
if (what.indexOf('L') >= 0) {
LuaTable lines = new LuaTable();
info.set(ACTIVELINES, lines);
CallFrame cf;
DebugLib.CallFrame cf;
for (int l = 1; (cf=callstack.getCallFrame(l)) != null; ++l)
if (cf.f == func)
lines.insert(-1, valueOf(cf.currentline()));
@@ -239,7 +237,7 @@ public class DebugLib extends OneArgFunction {
LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running_thread;
int level = args.checkint(a++);
int local = args.checkint(a++);
return thread.callstack.getCallFrame(level).getLocal(local);
return callstack(thread).getCallFrame(level).getLocal(local);
}
}
@@ -297,11 +295,11 @@ public class DebugLib extends OneArgFunction {
case 'l': line=true; break;
case 'r': rtrn=true; break;
}
thread.hookfunc = func;
thread.hookcall = call;
thread.hookline = line;
thread.hookcount = count;
thread.hookrtrn = rtrn;
hookfunc = func;
hookcall = call;
hookline = line;
hookcount = count;
hookrtrn = rtrn;
return NONE;
}
}
@@ -314,7 +312,7 @@ public class DebugLib extends OneArgFunction {
int level = args.checkint(a++);
int local = args.checkint(a++);
LuaValue value = args.arg(a++);
return thread.callstack.getCallFrame(level).setLocal(local, value);
return callstack(thread).getCallFrame(level).setLocal(local, value);
}
}
@@ -372,7 +370,7 @@ public class DebugLib extends OneArgFunction {
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 = thread.callstack.traceback(level);
String tb = callstack(thread).traceback(level);
return valueOf(message!=null? message+"\n"+tb: tb);
}
}
@@ -403,6 +401,223 @@ public class DebugLib extends OneArgFunction {
}
}
public void onCall(LuaFunction f) {
if (inhook) return;
callstack().onCall(f);
if (hookcall && hookfunc != null)
callHook(CALL, NIL);
}
public void onCall(LuaClosure c, Varargs varargs, LuaValue[] stack) {
if (inhook) return;
callstack().onCall(c, varargs, stack);
if (hookcall && hookfunc != null)
callHook(CALL, NIL);
}
public void onInstruction(int pc, Varargs v, int top) {
if (inhook) return;
callstack().onInstruction(pc, v, top);
if (hookfunc == null) return;
if (hookcount > 0)
if (++bytecodes % hookcount == 0)
callHook(COUNT, NIL);
if (hookline) {
int newline = callstack().currentline();
if ( newline != lastline ) {
lastline = newline;
callHook(LINE, LuaValue.valueOf(newline));
}
}
}
public void onReturn() {
if (inhook) return;
callstack().onReturn();
if (hookcall && hookfunc != null)
callHook(RETURN, NIL);
}
public String traceback(int level) {
return callstack().traceback(level);
}
void callHook(LuaValue type, LuaValue arg) {
inhook = true;
try {
hookfunc.call(type, arg);
} catch (Exception e) {
e.printStackTrace();
} finally {
inhook = false;
}
}
CallStack callstack() {
return callstack(globals.running_thread);
}
CallStack callstack(LuaThread t) {
if (t.callstack == null)
t.callstack = new CallStack();
return (CallStack) t.callstack;
}
public static class CallStack {
final static CallFrame[] EMPTY = {};
CallFrame[] frame = EMPTY;
int calls = 0;
CallStack() {}
int currentline() {
return calls > 0? frame[calls-1].currentline(): -1;
}
private CallFrame pushcall() {
if (calls >= frame.length) {
int n = Math.max(4, frame.length * 3 / 2);
CallFrame[] f = new CallFrame[n];
System.arraycopy(frame, 0, f, 0, frame.length);
for (int i = frame.length; i < n; ++i)
f[i] = new CallFrame();
frame = f;
}
return frame[calls++];
}
final void onCall(LuaFunction function) {
pushcall().set(function);
}
final void onCall(LuaClosure function, Varargs varargs, LuaValue[] stack) {
pushcall().set(function, varargs, stack);
}
final void onReturn() {
if (calls > 0)
frame[--calls].reset();
}
final void onInstruction(int pc, Varargs v, int top) {
frame[calls-1].instr(pc, v, top);
}
/**
* Get the traceback starting at a specific level.
* @param level
* @return String containing the traceback.
*/
String traceback(int level) {
StringBuffer sb = new StringBuffer();
sb.append( "stack traceback:" );
DebugLib.CallFrame c = getCallFrame(level);
if (c != null) {
sb.append("\n\t");
sb.append( c.sourceline() );
sb.append( " in " );
while ( (c = getCallFrame(++level)) != null ) {
sb.append( c.tracename() );
sb.append( "\n\t" );
sb.append( c.sourceline() );
sb.append( " in " );
}
sb.append( "main chunk" );
}
return sb.toString();
}
DebugLib.CallFrame getCallFrame(int level) {
if (level < 1 || level >= calls)
return null;
return frame[calls-level];
}
DebugLib.CallFrame findCallFrame(LuaValue func) {
for (int i = 1; i <= calls; ++i)
if (frame[calls-i].f == func)
return frame[i];
return null;
}
}
static class CallFrame {
LuaFunction f;
int pc;
int top;
Varargs v;
LuaValue[] stack;
boolean tail;
void set(LuaClosure function, Varargs varargs, LuaValue[] stack) {
this.f = function;
this.v = varargs;
this.stack = stack;
this.tail = false;
}
void set(LuaFunction function) {
this.f = function;
}
void reset() {
this.f = null;
this.v = null;
this.stack = null;
}
void instr(int pc, Varargs v, int top) {
this.pc = pc;
this.v = v;
this.top = top;
if (f.checkclosure().p.code[pc] == Lua.OP_TAILCALL)
this.tail = true;
if (TRACE)
Print.printState(f.checkclosure(), pc, stack, top, v);
}
Varargs getLocal(int i) {
LuaString name = getlocalname(i);
if ( name != null )
return varargsOf( name, stack[i-1] );
else
return NIL;
}
Varargs setLocal(int i, LuaValue value) {
LuaString name = getlocalname(i);
if ( name != null ) {
stack[i-1] = value;
return name;
} else {
return NIL;
}
}
int currentline() {
if ( !f.isclosure() ) return -1;
int[] li = f.checkclosure().p.lineinfo;
return li==null || pc<0 || pc>=li.length? -1: li[pc];
}
String sourceline() {
if ( !f.isclosure() ) return f.tojstring();
String s = f.checkclosure().p.source.tojstring();
int line = currentline();
return (s.startsWith("@")||s.startsWith("=")? s.substring(1): s) + ":" + line;
}
String tracename() {
LuaString[] kind = getfuncname(this);
if ( kind == null )
return "function ?";
return "function "+kind[0].tojstring();
}
LuaString getlocalname(int index) {
if ( !f.isclosure() ) return null;
return f.checkclosure().p.getlocalname(index, pc);
}
String tojstring() {
return tracename()+" "+sourceline();
}
boolean istailcall() {
return tail;
}
}
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 )
@@ -426,7 +641,7 @@ public class DebugLib extends OneArgFunction {
if (!x) throw new RuntimeException("lua_assert failed");
}
public static LuaString[] getfuncname(CallFrame frame) {
public static LuaString[] getfuncname(DebugLib.CallFrame frame) {
if (!frame.f.isclosure())
return new LuaString[] { frame.f.strvalue(), JAVA };
Prototype p = frame.f.checkclosure().p;
@@ -572,6 +787,4 @@ public class DebugLib extends OneArgFunction {
}
return setreg;
}
}

View File

@@ -41,11 +41,10 @@ public class OrphanedThreadTest extends TestCase {
WeakReference luathr_ref;
LuaValue function;
WeakReference func_ref;
LuaValue env;
protected void setUp() throws Exception {
LuaThread.thread_orphan_check_interval = 5;
env = JsePlatform.standardGlobals();
globals = JsePlatform.standardGlobals();
}
protected void tearDown() {
@@ -53,17 +52,17 @@ public class OrphanedThreadTest extends TestCase {
}
public void testCollectOrphanedNormalThread() throws Exception {
function = new NormalFunction();
function = new NormalFunction(globals);
doTest(LuaValue.TRUE, LuaValue.ZERO);
}
public void testCollectOrphanedEarlyCompletionThread() throws Exception {
function = new EarlyCompletionFunction();
function = new EarlyCompletionFunction(globals);
doTest(LuaValue.TRUE, LuaValue.ZERO);
}
public void testCollectOrphanedAbnormalThread() throws Exception {
function = new AbnormalFunction();
function = new AbnormalFunction(globals);
doTest(LuaValue.FALSE, LuaValue.valueOf("abnormal condition"));
}
@@ -76,7 +75,7 @@ public class OrphanedThreadTest extends TestCase {
"print('leakage in closure.3, arg is '..arg)\n" +
"return 'done'\n";
LuaC.install();
function = LoadState.load(new ByteArrayInputStream(script.getBytes()), "script", "bt", env);
function = LoadState.load(new ByteArrayInputStream(script.getBytes()), "script", "bt", globals);
doTest(LuaValue.TRUE, LuaValue.ZERO);
}
@@ -92,7 +91,7 @@ public class OrphanedThreadTest extends TestCase {
"end\n" +
"print( 'pcall-closre.result:', pcall( f, ... ) )\n";
LuaC.install();
function = LoadState.load(new ByteArrayInputStream(script.getBytes()), "script", "bt", env);
function = LoadState.load(new ByteArrayInputStream(script.getBytes()), "script", "bt", globals);
doTest(LuaValue.TRUE, LuaValue.ZERO);
}
@@ -109,12 +108,11 @@ public class OrphanedThreadTest extends TestCase {
"end\n" +
"load(f)()\n";
LuaC.install();
function = LoadState.load(new ByteArrayInputStream(script.getBytes()), "script", "bt", env);
function = LoadState.load(new ByteArrayInputStream(script.getBytes()), "script", "bt", globals);
doTest(LuaValue.TRUE, LuaValue.ONE);
}
private void doTest(LuaValue status2, LuaValue value2) throws Exception {
globals = JsePlatform.standardGlobals();
luathread = new LuaThread(globals, function);
luathr_ref = new WeakReference(luathread);
func_ref = new WeakReference(function);
@@ -144,7 +142,11 @@ public class OrphanedThreadTest extends TestCase {
}
class NormalFunction extends OneArgFunction {
static class NormalFunction extends OneArgFunction {
final Globals globals;
public NormalFunction(Globals globals) {
this.globals = globals;
}
public LuaValue call(LuaValue arg) {
System.out.println("in normal.1, arg is "+arg);
arg = globals.yield(ONE).arg1();
@@ -155,7 +157,11 @@ public class OrphanedThreadTest extends TestCase {
}
}
class EarlyCompletionFunction extends OneArgFunction {
static class EarlyCompletionFunction extends OneArgFunction {
final Globals globals;
public EarlyCompletionFunction(Globals globals) {
this.globals = globals;
}
public LuaValue call(LuaValue arg) {
System.out.println("in early.1, arg is "+arg);
arg = globals.yield(ONE).arg1();
@@ -164,17 +170,11 @@ public class OrphanedThreadTest extends TestCase {
}
}
class AbnormalFunction extends OneArgFunction {
public LuaValue call(LuaValue arg) {
System.out.println("in abnormal.1, arg is "+arg);
arg = globals.yield(ONE).arg1();
System.out.println("in abnormal.2, arg is "+arg);
error("abnormal condition");
return ZERO;
}
}
class ClosureFunction extends OneArgFunction {
static class AbnormalFunction extends OneArgFunction {
final Globals globals;
public AbnormalFunction(Globals globals) {
this.globals = globals;
}
public LuaValue call(LuaValue arg) {
System.out.println("in abnormal.1, arg is "+arg);
arg = globals.yield(ONE).arg1();