Improve error handling, introduce lua stack trace processing.

This commit is contained in:
James Roseborough
2007-09-18 22:55:22 +00:00
parent a55504639e
commit 32e1fedba5
7 changed files with 155 additions and 76 deletions

View File

@@ -153,7 +153,7 @@ public class LuaCompat extends LFunction {
vm.setResult(); vm.setResult();
} break; } break;
case LOADFILE: case LOADFILE:
vm.setResult( loadfile(vm, vm.getArg(0)) ); loadfile(vm, vm.getArgAsString(0));
break; break;
case TONUMBER: case TONUMBER:
vm.setResult( toNumber( vm ) ); vm.setResult( toNumber( vm ) );
@@ -178,13 +178,12 @@ public class LuaCompat extends LFunction {
vm.setResult(); vm.setResult();
break; break;
case DOFILE: case DOFILE:
dofile(vm, vm.getArg(0)); return dofile(vm, vm.getArgAsString(0));
break;
case LOADSTRING: case LOADSTRING:
vm.setResult( loadstring(vm, vm.getArg(0), vm.getArgAsString(1)) ); loadstring(vm, vm.getArg(0), vm.getArgAsString(1));
break; break;
case LOAD: case LOAD:
vm.setResult( load(vm, vm.getArg(0), vm.getArgAsString(1)) ); load(vm, vm.getArg(0), vm.getArgAsString(1));
break; break;
case TOSTRING: case TOSTRING:
vm.setResult( tostring(vm, vm.getArg(0)) ); vm.setResult( tostring(vm, vm.getArg(0)) );
@@ -383,60 +382,63 @@ public class LuaCompat extends LFunction {
} }
} }
// closes the input stream // return true if laoded, false if error put onto the stack
private static LValue inputStreamToClosureThenClose(VM vm, InputStream is, String chunkname ) { private static boolean loadis(VM vm, InputStream is, String chunkname ) {
try { try {
Proto p = LoadState.undump(vm, is, chunkname); if ( 0 != vm.lua_load(is, chunkname) ) {
return new Closure( (StackState) vm, p); vm.setErrorResult( LNil.NIL, "cannot load "+chunkname+": "+vm.getArgAsString(0) );
} catch (IOException e) { return false;
e.printStackTrace(); } else {
return true;
}
} finally { } finally {
closeSafely( is ); closeSafely( is );
} }
return LNil.NIL;
} }
private LValue loadfile( VM vm, LValue fileName ) { // return true if loaded, false if error put onto stack
public static boolean loadfile( VM vm, String fileName ) {
InputStream is; InputStream is;
String script; String script;
if ( fileName != null ) { if ( ! "".equals(fileName) ) {
script = fileName.luaAsString().toJavaString(); script = fileName;
is = getClass().getResourceAsStream( "/"+script ); is = vm.getClass().getResourceAsStream( "/"+script );
if ( is == null ) {
vm.setErrorResult( LNil.NIL, "cannot open "+fileName+": No such file or directory" );
return false;
}
} else { } else {
is = STDIN; is = STDIN;
script = "-"; script = "-";
} }
if ( is != null ) // use vm to load the script
return inputStreamToClosureThenClose( vm, is, script ); return loadis( vm, is, script );
return LNil.NIL;
} }
private LValue dofile( VM vm, LValue fileName ) { // return true if call placed on the stack & ready to execute, false otherwise
LValue chunk = loadfile( vm, fileName ); private boolean dofile( VM vm, String fileName ) {
if ( chunk != LNil.NIL ) { if ( loadfile( vm, fileName ) ) {
vm.doCall((Closure) chunk, null); vm.prepStackCall(); // TODO: is this necessary?
return true;
} else { } else {
vm.setResult(); return false;
vm.push("file not found: "+fileName);
vm.lua_error();
} }
return null;
} }
private LValue loadstring(VM vm, LValue string, String chunkname) { // return true if loaded, false if error put onto stack
InputStream is = string.luaAsString().toInputStream(); private boolean loadstring(VM vm, LValue string, String chunkname) {
return inputStreamToClosureThenClose( vm, is, chunkname ); return loadis( vm,
string.luaAsString().toInputStream(),
("".equals(chunkname)? "(string)": chunkname) );
} }
// TODO: convert this to stream? // return true if loaded, false if error put onto stack
private LValue load(VM vm, LValue chunkPartLoader, String chunkname) { private boolean load(VM vm, LValue chunkPartLoader, String chunkname) {
if ( ! (chunkPartLoader instanceof Closure) ) { if ( ! (chunkPartLoader instanceof Closure) ) {
vm.setResult(); vm.lua_error("not a closure: "+chunkPartLoader);
vm.push("not a function: "+chunkPartLoader);
vm.lua_error();
} }
// load all the parts // load all the parts
@@ -444,22 +446,29 @@ public class LuaCompat extends LFunction {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
try { try {
while ( true ) { while ( true ) {
vm.doCall( c, null ); vm.setResult(c);
LValue r = vm.getArg(0); if ( 0 != vm.lua_pcall(0, 1) ) {
if ( ! (r instanceof LString) ) vm.setErrorResult(LNil.NIL, vm.getArgAsString(0));
return false;
}
LValue v = vm.getArg(0);
if ( v == LNil.NIL )
break; break;
LString s = (LString) r; LString s = v.luaAsString();
s.write(baos, 0, s.length()); s.write(baos, 0, s.length());
} }
} catch ( Exception e ) {
e.printStackTrace(); // load the chunk
return loadis( vm,
new ByteArrayInputStream( baos.toByteArray() ),
("".equals(chunkname)? "=(load)": chunkname) );
} catch (IOException ioe) {
vm.setErrorResult(LNil.NIL, ioe.getMessage());
return false;
} finally { } finally {
closeSafely( baos ); closeSafely( baos );
} }
// load from the byte array
InputStream is = new ByteArrayInputStream( baos.toByteArray() );
return inputStreamToClosureThenClose( vm, is, chunkname );
} }
private LValue tostring(VM vm, LValue arg) { private LValue tostring(VM vm, LValue arg) {
@@ -468,9 +477,7 @@ public class LuaCompat extends LFunction {
private static LTable toTable(VM vm, LValue list) { private static LTable toTable(VM vm, LValue list) {
if ( ! (list instanceof LTable) ) { if ( ! (list instanceof LTable) ) {
vm.setResult(); vm.lua_error("not a list: "+list);
vm.push("not a list: "+list);
vm.lua_error();
return null; return null;
} }
return (LTable) list; return (LTable) list;

View File

@@ -58,8 +58,7 @@ public class StrLib {
* TODO: port dumping code as optional add-on * TODO: port dumping code as optional add-on
*/ */
static void dump( VM vm ) { static void dump( VM vm ) {
vm.setResult( new LString("not supported") ); vm.lua_error("dump() not supported");
vm.lua_error();
} }
/** /**
@@ -397,7 +396,7 @@ public class StrLib {
} else { } else {
int l = clen[i]; int l = clen[i];
if ( l == CAP_UNFINISHED ) { if ( l == CAP_UNFINISHED ) {
vm.luaL_error( "unfinished capture" ); vm.lua_error( "unfinished capture" );
} }
if ( l == CAP_POSITION ) { if ( l == CAP_POSITION ) {
vm.push( new LInteger( cinit[i] + 1 ) ); vm.push( new LInteger( cinit[i] + 1 ) );
@@ -411,7 +410,7 @@ public class StrLib {
private int check_capture( int l ) { private int check_capture( int l ) {
l -= '1'; l -= '1';
if ( l < 0 || l >= level || this.clen[l] == CAP_UNFINISHED ) { if ( l < 0 || l >= level || this.clen[l] == CAP_UNFINISHED ) {
vm.luaL_error("invalid capture index"); vm.lua_error("invalid capture index");
} }
return l; return l;
} }
@@ -421,8 +420,7 @@ public class StrLib {
for ( level--; level >= 0; level-- ) for ( level--; level >= 0; level-- )
if ( clen[level] == CAP_UNFINISHED ) if ( clen[level] == CAP_UNFINISHED )
return level; return level;
vm.push("invalid pattern capture"); vm.lua_error("invalid pattern capture");
vm.lua_error();
return 0; return 0;
} }
@@ -430,8 +428,7 @@ public class StrLib {
switch ( p.luaByte( poffset++ ) ) { switch ( p.luaByte( poffset++ ) ) {
case L_ESC: case L_ESC:
if ( poffset == p.length() ) { if ( poffset == p.length() ) {
vm.push( "malformed pattern (ends with %)" ); vm.lua_error( "malformed pattern (ends with %)" );
vm.lua_error();
} }
return poffset + 1; return poffset + 1;
@@ -439,8 +436,7 @@ public class StrLib {
if ( p.luaByte( poffset ) == '^' ) poffset++; if ( p.luaByte( poffset ) == '^' ) poffset++;
do { do {
if ( poffset == p.length() ) { if ( poffset == p.length() ) {
vm.push( "malformed pattern (missing ])" ); vm.lua_error( "malformed pattern (missing ])" );
vm.lua_error();
} }
if ( p.luaByte( poffset++ ) == L_ESC && poffset != p.length() ) if ( p.luaByte( poffset++ ) == L_ESC && poffset != p.length() )
poffset++; poffset++;
@@ -534,8 +530,7 @@ public class StrLib {
case 'f': { case 'f': {
poffset += 2; poffset += 2;
if ( p.luaByte( poffset ) != '[' ) { if ( p.luaByte( poffset ) != '[' ) {
vm.push("Missing [ after %f in pattern"); vm.lua_error("Missing [ after %f in pattern");
vm.lua_error();
} }
int ep = classend( poffset ); int ep = classend( poffset );
int previous = ( soffset == 0 ) ? -1 : s.luaByte( soffset - 1 ); int previous = ( soffset == 0 ) ? -1 : s.luaByte( soffset - 1 );
@@ -615,8 +610,7 @@ public class StrLib {
int res; int res;
int level = this.level; int level = this.level;
if ( level >= MAX_CAPTURES ) { if ( level >= MAX_CAPTURES ) {
vm.push( "too many captures" ); vm.lua_error( "too many captures" );
vm.lua_error();
} }
cinit[ level ] = soff; cinit[ level ] = soff;
clen[ level ] = what; clen[ level ] = what;
@@ -648,8 +642,7 @@ public class StrLib {
int matchbalance( int soff, int poff ) { int matchbalance( int soff, int poff ) {
final int plen = p.length(); final int plen = p.length();
if ( poff == plen || poff + 1 == plen ) { if ( poff == plen || poff + 1 == plen ) {
vm.push( "unbalanced pattern" ); vm.lua_error( "unbalanced pattern" );
vm.lua_error();
} }
if ( s.luaByte( soff ) != p.luaByte( poff ) ) if ( s.luaByte( soff ) != p.luaByte( poff ) )
return -1; return -1;

View File

@@ -6,6 +6,7 @@ package lua;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PrintStream; import java.io.PrintStream;
import lua.value.LBoolean;
import lua.value.LFunction; import lua.value.LFunction;
import lua.value.LNil; import lua.value.LNil;
import lua.value.LTable; import lua.value.LTable;
@@ -71,8 +72,13 @@ final class Builtin extends LFunction {
vm.setResult( vm.getArg(0).luaGetType() ); vm.setResult( vm.getArg(0).luaGetType() );
break; break;
case PCALL: case PCALL:
// TODO: implement pcall if ( 0 != vm.lua_pcall( vm.getArgCount()-1, Lua.LUA_MULTRET ) ) {
vm.setResult( LNil.NIL ); LValue v = vm.getArg(0);
vm.setResult( LBoolean.FALSE );
vm.push( v );
} else {
vm.setResult( LBoolean.TRUE );
}
break; break;
default: default:
luaUnsupportedOperation(); luaUnsupportedOperation();

View File

@@ -1,6 +1,9 @@
package lua; package lua;
import java.io.InputStream;
import lua.io.Closure; import lua.io.Closure;
import lua.value.LNil;
import lua.value.LString; import lua.value.LString;
import lua.value.LValue; import lua.value.LValue;
@@ -142,15 +145,38 @@ public interface VM {
/** /**
* Generates a Lua error. The error message(which can actually be a Lua value of any type) * Set up an error result on the stack.
* must be on the top of the stack. * @param value the LValue to return as the first return value
* @param message the String error message to supply
*/ */
public void lua_error(); public void setErrorResult(LValue value, String message);
// ====================== lua Java API =======================
/** /**
* Raises an error. The message is pushed onto the stack and used as the error message. * Raises an error. The message is pushed onto the stack and used as the error message.
* It also adds at the beginning of the message the file name and the line number where * It also adds at the beginning of the message the file name and the line number where
* the error occurred, if this information is available. * the error occurred, if this information is available.
*
* In the java implementation this throws a RuntimeException, possibly filling
* line number information first.
*/ */
public void luaL_error(String message); public void lua_error(String message);
/**
* Run the method on the stack in protected mode.
* @param nArgs number of arguments on the stack
* @param nResults number of results on the stack
* @return 0 if successful, LUA_ERRMEM if no memory, LUA_ERRRUN for any other error
*/
public int lua_pcall(int nArgs, int nResults);
/**
*
* @param is InputStream providing the data to be loaded
* @param chunkname Name of the chunk to be used in debugging
* @return 0 if successful, LUA_ERRMEM if no memory, LUA_ERRSYNTAX for i/o or any other errors
*/
public int lua_load( InputStream is, String chunkname );
} }

View File

@@ -5,11 +5,15 @@ import java.util.Map;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import lua.CallInfo; import lua.CallInfo;
import lua.Print;
import lua.StackState; import lua.StackState;
import lua.io.LocVars; import lua.io.LocVars;
import lua.io.Proto;
public class DebugStackState extends StackState implements DebugRequestListener { public class DebugStackState extends StackState implements DebugRequestListener {
private static final boolean DEBUG = false;
public Map<Integer,Boolean> breakpoints = new HashMap<Integer,Boolean>(); public Map<Integer,Boolean> breakpoints = new HashMap<Integer,Boolean>();
private boolean exiting = false; private boolean exiting = false;
private boolean suspended = false; private boolean suspended = false;
@@ -19,6 +23,47 @@ public class DebugStackState extends StackState implements DebugRequestListener
public DebugStackState() { public DebugStackState() {
} }
private String getFileLine(int cindex) {
String func = "?";
String line = "?";
String source = "?";
if ( cindex >= 0 ) {
CallInfo call = this.calls[cindex];
Proto p = call.closure.p;
if ( p != null && p.source != null )
source = p.source.toJavaString();
if ( p.lineinfo != null && p.lineinfo.length > call.pc )
line = String.valueOf( p.lineinfo[call.pc] );
// TODO: reverse lookup on function name ????
func = call.closure.luaAsString().toJavaString();
}
return source+":"+line+"("+func+")";
}
// override and fill in line number info
public void lua_error(String message) {
super.lua_error( getFileLine(cc)+": "+message );
}
private void printLuaTrace() {
System.out.println( "Lua location: "+getFileLine(cc) );
for ( int cindex=cc-1; cindex>=0; cindex-- )
System.out.println( "\tin "+getFileLine( cindex ) );
}
// intercept exceptions and fill in line numbers
public void exec() {
try {
super.exec();
} catch ( Exception t ) {
t.printStackTrace();
printLuaTrace();
System.out.flush();
}
}
// debug hooks // debug hooks
public void debugHooks( int pc ) { public void debugHooks( int pc ) {
if ( exiting ) if ( exiting )

View File

@@ -196,6 +196,8 @@ public class LoadState {
Proto f = new Proto(); Proto f = new Proto();
// this.L.push(f); // this.L.push(f);
f.source = loadString(); f.source = loadString();
if ( f.source == null )
f.source = p;
f.linedefined = loadInt(); f.linedefined = loadInt();
f.lastlinedefined = loadInt(); f.lastlinedefined = loadInt();
f.nups = loadByte(); f.nups = loadByte();

View File

@@ -33,7 +33,7 @@ public class LValue {
// perform a lua call, return true if the call is to a lua function, false // perform a lua call, return true if the call is to a lua function, false
// if it ran to completion. // if it ran to completion.
public boolean luaStackCall(VM vm) { public boolean luaStackCall(VM vm) {
luaUnsupportedOperation(); vm.lua_error("attempt to call "+this);
return false; return false;
} }