Improve error handling, introduce lua stack trace processing.
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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 );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 )
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user