Improve error reporting.

This commit is contained in:
James Roseborough
2007-12-06 15:29:49 +00:00
parent 8f581c8f07
commit 644896c467
16 changed files with 208 additions and 80 deletions

View File

@@ -364,7 +364,7 @@ public class BaseLib extends LFunction {
break; break;
} }
default: default:
luaUnsupportedOperation(); throw new RuntimeException( "bad id: "+id );
} }
return false; return false;
} }

View File

@@ -126,7 +126,7 @@ public class CoroutineLib extends LFunction {
if ( vm.toboolean(1) ) if ( vm.toboolean(1) )
vm.remove(1); vm.remove(1);
else else
vm.error( vm.tostring(2), 0 ); vm.error( vm.tostring(2) );
return false; return false;
} }
} }

View File

@@ -113,7 +113,7 @@ public class MathLib extends LFunction {
setResult( vm, LInteger.valueOf( (int) Math.floor( vm.tonumber(2) ) ) ); setResult( vm, LInteger.valueOf( (int) Math.floor( vm.tonumber(2) ) ) );
break; break;
default: default:
luaUnsupportedOperation(); throw new RuntimeException( "bad id: "+id );
} }
return false; return false;
} }

View File

@@ -144,7 +144,7 @@ public class PackageLib extends LFunction {
break; break;
} }
default: default:
luaUnsupportedOperation(); throw new RuntimeException( "bad id: "+id );
} }
return false; return false;
} }
@@ -182,7 +182,7 @@ public class PackageLib extends LFunction {
/* try global variable (and create one if it does not exist) */ /* try global variable (and create one if it does not exist) */
module = findtable( vm._G, modname ); module = findtable( vm._G, modname );
if ( module == null ) if ( module == null )
vm.error( "name conflict for module '"+modname+"'", 2 ); vm.error( "name conflict for module '"+modname+"'" );
LOADED.luaSetTable(vm, LOADED, modname, module); LOADED.luaSetTable(vm, LOADED, modname, module);
} else { } else {
module = (LTable) value; module = (LTable) value;

View File

@@ -135,7 +135,7 @@ public class StringLib extends LFunction {
break; break;
default: default:
luaUnsupportedOperation(); vm.error( "bad id" );
} }
return false; return false;
} }

View File

@@ -175,7 +175,7 @@ public class TableLib extends LFunction {
} }
default: default:
luaUnsupportedOperation(); throw new RuntimeException( "bad id" );
} }
return false; return false;
} }

View File

@@ -77,25 +77,27 @@ public class LDouble extends LNumber {
case Lua.OP_MUL: return new LDouble( lhs * rhs ); case Lua.OP_MUL: return new LDouble( lhs * rhs );
case Lua.OP_DIV: return new LDouble( lhs / rhs ); case Lua.OP_DIV: return new LDouble( lhs / rhs );
case Lua.OP_MOD: return new LDouble( lhs - Math.floor(lhs/rhs) * rhs ); case Lua.OP_MOD: return new LDouble( lhs - Math.floor(lhs/rhs) * rhs );
// case Lua.OP_POW: return new LDouble( dpow(lhs, rhs) ); case Lua.OP_POW: throw new LuaErrorException("math.pow() not implemented for doubles");
} }
return luaUnsupportedOperation(); throw new RuntimeException("bad opcode");
} }
/* /* warning: NOT TESTED
public static double dpow(double a, double b) { public static double dpow(double a, double b) {
if ( b < 0 ) if ( b < 0 )
return 1 / dpow( a, -b ); return 1 / dpow( a, -b );
int p = 1; double p = 1;
int whole = (int) b; int whole = (int) b;
for ( double v=a; whole > 0; whole>>=1, v=v*v ) for ( double v=a; whole > 0; whole>>=1, v*=v )
if ( (whole & 1) != 0 ) if ( (whole & 1) != 0 )
p *= v; p *= v;
int frac = (int) (0x10000 * b); if ( (b -= whole) > 0 ) {
for ( ; (frac&0xffff)!=0; frac<<=1 ) { int frac = (int) (0x10000 * b);
a = Math.sqrt(a); for ( ; (frac&0xffff)!=0; frac<<=1 ) {
if ( (frac & 0x8000) != 0 ) a = Math.sqrt(a);
p *= a; if ( (frac & 0x8000) != 0 )
p *= a;
}
} }
return p; return p;
} }
@@ -132,8 +134,7 @@ public class LDouble extends LNumber {
case Lua.OP_LT: return lhs < rhs; case Lua.OP_LT: return lhs < rhs;
case Lua.OP_LE: return lhs <= rhs; case Lua.OP_LE: return lhs <= rhs;
} }
luaUnsupportedOperation(); throw new RuntimeException("bad opcode");
return false;
} }
/** Arithmetic negative */ /** Arithmetic negative */

View File

@@ -85,7 +85,7 @@ public class LInteger extends LNumber {
case Lua.OP_MOD: return LInteger.valueOf( m_value - ((int) Math.floor(m_value/(double)rhs)) * rhs ); case Lua.OP_MOD: return LInteger.valueOf( m_value - ((int) Math.floor(m_value/(double)rhs)) * rhs );
case Lua.OP_POW: return LInteger.valueOf( ipow(m_value, rhs) ); case Lua.OP_POW: return LInteger.valueOf( ipow(m_value, rhs) );
} }
return luaUnsupportedOperation(); throw new RuntimeException("bad opcode");
} }
private static int ipow(int v, int rhs) { private static int ipow(int v, int rhs) {
@@ -113,8 +113,7 @@ public class LInteger extends LNumber {
case Lua.OP_LT: return m_value < rhs; case Lua.OP_LT: return m_value < rhs;
case Lua.OP_LE: return m_value <= rhs; case Lua.OP_LE: return m_value <= rhs;
} }
luaUnsupportedOperation(); throw new RuntimeException("bad opcode");
return false;
} }
// unsupported except for numbers // unsupported except for numbers

View File

@@ -315,8 +315,7 @@ public class LString extends LValue {
case Lua.OP_LT: return compareTo(rhs) < 0; case Lua.OP_LT: return compareTo(rhs) < 0;
case Lua.OP_LE: return compareTo(rhs) <= 0; case Lua.OP_LE: return compareTo(rhs) <= 0;
} }
luaUnsupportedOperation(); throw new RuntimeException("bad opcode");
return false;
} }
public LValue luaBinOpDouble( int opcode, double m_value ) { public LValue luaBinOpDouble( int opcode, double m_value ) {

View File

@@ -31,14 +31,21 @@ public class LValue {
/** Metatable tag for intercepting table sets */ /** Metatable tag for intercepting table sets */
public static final LString TM_NEWINDEX = new LString("__newindex"); public static final LString TM_NEWINDEX = new LString("__newindex");
protected static LValue luaUnsupportedOperation() { protected void conversionError(String target) {
throw new LuaErrorException( "not supported" );
}
protected void luaConversionError(String target) {
throw new LuaErrorException( "bad conversion: "+luaGetTypeName()+" to "+target ); throw new LuaErrorException( "bad conversion: "+luaGetTypeName()+" to "+target );
} }
private static LValue arithmeticError( Object type ) {
throw new LuaErrorException( "attempt to perform arithmetic on ? (a "+type+" value)" );
}
protected static LValue compareError( Object typea, Object typeb ) {
throw new LuaErrorException( "attempt to compare "+typea+" with "+typeb );
}
private void indexError(LuaState vm, LValue nontable) {
vm.error( "attempt to index ? (a "+nontable.luaGetTypeName()+" value)", 2 );
}
public String id() { public String id() {
return Integer.toHexString(hashCode()); return Integer.toHexString(hashCode());
@@ -58,24 +65,24 @@ public class LValue {
// unsupported except for numbers // unsupported except for numbers
public LValue luaBinOpUnknown(int opcode, LValue lhs) { public LValue luaBinOpUnknown(int opcode, LValue lhs) {
return luaUnsupportedOperation(); return arithmeticError(lhs.luaGetTypeName());
} }
// unsupported except for numbers // unsupported except for numbers
public LValue luaBinOpInteger(int opcode, int m_value) { public LValue luaBinOpInteger(int opcode, int m_value) {
return luaUnsupportedOperation(); return arithmeticError("number");
} }
// unsupported except for numbers // unsupported except for numbers
public LValue luaBinOpDouble(int opcode, double m_value) { public LValue luaBinOpDouble(int opcode, double m_value) {
return luaUnsupportedOperation(); return arithmeticError("number");
} }
// unsupported except for numbers, strings, and == with various combinations of Nil, Boolean, etc. // unsupported except for numbers, strings, and == with various combinations of Nil, Boolean, etc.
public boolean luaBinCmpUnknown(int opcode, LValue lhs) { public boolean luaBinCmpUnknown(int opcode, LValue lhs) {
if ( opcode == Lua.OP_EQ ) if ( opcode == Lua.OP_EQ )
return lhs == this; return lhs == this;
luaUnsupportedOperation(); compareError(lhs.luaGetTypeName(), luaGetTypeName());
return false; return false;
} }
@@ -83,7 +90,7 @@ public class LValue {
public boolean luaBinCmpString(int opcode, LString rhs) { public boolean luaBinCmpString(int opcode, LString rhs) {
if ( opcode == Lua.OP_EQ ) if ( opcode == Lua.OP_EQ )
return false; return false;
luaUnsupportedOperation(); compareError(luaGetTypeName(), "string");
return false; return false;
} }
@@ -91,7 +98,7 @@ public class LValue {
public boolean luaBinCmpInteger(int opcode, int rhs) { public boolean luaBinCmpInteger(int opcode, int rhs) {
if ( opcode == Lua.OP_EQ ) if ( opcode == Lua.OP_EQ )
return false; return false;
luaUnsupportedOperation(); compareError(luaGetTypeName(), "number");
return false; return false;
} }
@@ -99,7 +106,7 @@ public class LValue {
public boolean luaBinCmpDouble(int opcode, double rhs) { public boolean luaBinCmpDouble(int opcode, double rhs) {
if ( opcode == Lua.OP_EQ ) if ( opcode == Lua.OP_EQ )
return false; return false;
luaUnsupportedOperation(); compareError(luaGetTypeName(), "number");
return false; return false;
} }
@@ -119,7 +126,7 @@ public class LValue {
return; return;
} }
} }
vm.pushnil(); indexError( vm, table );
} }
/** Get a value from a table /** Get a value from a table
@@ -136,7 +143,7 @@ public class LValue {
return; return;
} }
} }
vm.error( "attempt to index ? (a "+table.luaGetTypeName()+" value)", 2 ); indexError( vm, table );
} }
/** Get the value as a LString /** Get the value as a LString
@@ -152,20 +159,12 @@ public class LValue {
/** Arithmetic negative */ /** Arithmetic negative */
public LValue luaUnaryMinus() { public LValue luaUnaryMinus() {
return luaUnsupportedOperation(); return arithmeticError(luaGetTypeName());
} }
/** Built-in opcode LEN, for Strings and Tables */ /** Built-in opcode LEN, for Strings and Tables */
public int luaLength() { public int luaLength() {
// TODO: call meta-method TM_LEN here throw new LuaErrorException( "attempt to get length of ? (a "+luaGetTypeName()+" value)" );
luaUnsupportedOperation();
return 0;
}
/** Valid for tables
* @param isPairs true to iterate over non-integers as well */
public LValue luaPairs(boolean isPairs) {
return luaUnsupportedOperation();
} }
/** /**
@@ -184,7 +183,7 @@ public class LValue {
/** Valid for tables */ /** Valid for tables */
public void luaSetMetatable(LValue metatable) { public void luaSetMetatable(LValue metatable) {
luaUnsupportedOperation(); throw new LuaErrorException( "bad argument #1 to 'setmetatable' (table expected, got "+metatable.luaGetTypeName()+")");
} }
/** Valid for all types: return the int value identifying the type of this value */ /** Valid for all types: return the int value identifying the type of this value */
@@ -229,7 +228,7 @@ public class LValue {
/** Return value as an integer */ /** Return value as an integer */
public int toJavaInt() { public int toJavaInt() {
luaConversionError("number"); conversionError("number");
return 0; return 0;
} }
@@ -245,55 +244,55 @@ public class LValue {
/** Convert to a Boolean value */ /** Convert to a Boolean value */
public Boolean toJavaBoxedBoolean() { public Boolean toJavaBoxedBoolean() {
luaConversionError("Boolean"); conversionError("Boolean");
return null; return null;
} }
/** Convert to a Byte value */ /** Convert to a Byte value */
public Byte toJavaBoxedByte() { public Byte toJavaBoxedByte() {
luaConversionError("Byte"); conversionError("Byte");
return null; return null;
} }
/** Convert to a boxed Character value */ /** Convert to a boxed Character value */
public Character toJavaBoxedCharacter() { public Character toJavaBoxedCharacter() {
luaConversionError("Character"); conversionError("Character");
return null; return null;
} }
/** Convert to a boxed Double value */ /** Convert to a boxed Double value */
public Double toJavaBoxedDouble() { public Double toJavaBoxedDouble() {
luaConversionError("Double"); conversionError("Double");
return null; return null;
} }
/** Convert to a boxed Float value */ /** Convert to a boxed Float value */
public Float toJavaBoxedFloat() { public Float toJavaBoxedFloat() {
luaConversionError("Float"); conversionError("Float");
return null; return null;
} }
/** Convert to a boxed Integer value */ /** Convert to a boxed Integer value */
public Integer toJavaBoxedInteger() { public Integer toJavaBoxedInteger() {
luaConversionError("Integer"); conversionError("Integer");
return null; return null;
} }
/** Convert to a boxed Long value */ /** Convert to a boxed Long value */
public Long toJavaBoxedLong() { public Long toJavaBoxedLong() {
luaConversionError("Long"); conversionError("Long");
return null; return null;
} }
/** Convert to a boxed Short value */ /** Convert to a boxed Short value */
public Short toJavaBoxedShort() { public Short toJavaBoxedShort() {
luaConversionError("Short"); conversionError("Short");
return null; return null;
} }
/** Convert to a Java Object iff this is a LUserData value */ /** Convert to a Java Object iff this is a LUserData value */
public Object toJavaInstance() { public Object toJavaInstance() {
luaConversionError("instance"); conversionError("instance");
return null; return null;
} }

View File

@@ -36,7 +36,15 @@ public class LuaErrorException extends RuntimeException {
* Construct a LuaErrorException with the default message. * Construct a LuaErrorException with the default message.
*/ */
public LuaErrorException() { public LuaErrorException() {
super(DEFAULT_MESSAGE); this(DEFAULT_MESSAGE);
}
/**
* Construct a LuaErrorException in response to a Throwable that was caught
* and with the default message.
*/
public LuaErrorException(Throwable cause) {
this(DEFAULT_MESSAGE+": "+cause);
} }
/** /**
@@ -45,14 +53,35 @@ public class LuaErrorException extends RuntimeException {
* @param message message to supply * @param message message to supply
*/ */
public LuaErrorException(String message) { public LuaErrorException(String message) {
super(message); this(null, message, 1);
} }
/** /**
* Construct a LuaErrorException in response to a Throwable that was caught * Construct the message around a specific vm and with a particular level of debug info
* and with the default message. * @param vm
* @param message
* @param level
*/ */
public LuaErrorException(Throwable cause) { public LuaErrorException(LuaState vm, String message, int level) {
super(DEFAULT_MESSAGE+": "+cause); super( addLineInfo( vm, message, level ) );
}
/**
* Append line info as per level
* @param vm
* @param message
* @param level
* @return
*/
private static String addLineInfo(LuaState vm, String message, int level) {
if ( level < 1 )
return message;
if ( vm == null ) {
if ( LThread.running != null )
vm = LThread.running.threadVm;
else
vm = LuaState.mainState;
}
return vm.getFileLine(vm.cc + 1 - level) + ": " + message;
} }
} }

View File

@@ -96,7 +96,7 @@ public class LuaState extends Lua {
protected Stack upvals = new Stack(); protected Stack upvals = new Stack();
protected LFunction panic; protected LFunction panic;
public static LTable DEFAULT_GLOBALS; static LuaState mainState;
public LTable _G; public LTable _G;
// main debug hook, overridden by DebugStackState // main debug hook, overridden by DebugStackState
@@ -121,8 +121,9 @@ public class LuaState extends Lua {
* @deprecated As of version 0.10, replaced by {@link #newState()} * @deprecated As of version 0.10, replaced by {@link #newState()}
*/ */
public LuaState() { public LuaState() {
_G = DEFAULT_GLOBALS = new LTable(); _G = new LTable();
_G.put("_G", _G); _G.put("_G", _G);
mainState = this;
} }
/** /**
@@ -1004,13 +1005,13 @@ public class LuaState extends Lua {
/** /**
* Get the file line number info for a particular call frame. * Get the file line number info for a particular call frame.
* @param cindex * @param cindex index into call stack
* @return * @return
*/ */
protected String getFileLine(int cindex) { protected String getFileLine(int cindex) {
String source = "?"; String source = "?";
String line = ""; String line = "";
if (cindex >= 0) { if (cindex >= 0 && cindex <= cc) {
CallInfo call = this.calls[cindex]; CallInfo call = this.calls[cindex];
LPrototype p = call.closure.p; LPrototype p = call.closure.p;
if (p != null && p.source != null) if (p != null && p.source != null)
@@ -1041,18 +1042,14 @@ public class LuaState extends Lua {
* after filling line number information first when level > 0. * after filling line number information first when level > 0.
*/ */
public void error(String message, int level) { public void error(String message, int level) {
if ( level > 1 ) throw new LuaErrorException( this, message, level );
message = getFileLine(cc + 2 - level) + ": " + message;
throw new LuaErrorException( message );
} }
/** /**
* Raises an error with the default level. * Raises an error with the default level.
*
* In the java implementation this calls error(message,1)
*/ */
public void error(String message) { public void error(String message) {
error( message, 1 ); throw new LuaErrorException( this, message, 1 );
} }
/** /**
@@ -1065,9 +1062,7 @@ public class LuaState extends Lua {
* *
*/ */
public void error() { public void error() {
String message = tostring(-1); throw new LuaErrorException( this, tostring(-1), 0);
// pop(1);
error( message );
} }
/** /**

View File

@@ -109,7 +109,7 @@ public final class LuajavaLib extends LFunction {
} }
break; break;
default: default:
luaUnsupportedOperation(); throw new LuaErrorException("not yet supported: "+this);
} }
return false; return false;
} }

View File

@@ -70,6 +70,10 @@ public class LuaJTest extends TestCase {
runTest( "compare" ); runTest( "compare" );
} }
public void testErrors() throws IOException, InterruptedException {
runTest( "errors" );
}
public void testMathLib() throws IOException, InterruptedException { public void testMathLib() throws IOException, InterruptedException {
runTest( "mathlib" ); runTest( "mathlib" );
} }
@@ -129,7 +133,7 @@ public class LuaJTest extends TestCase {
public void testUpvalues2() throws IOException, InterruptedException { public void testUpvalues2() throws IOException, InterruptedException {
runTest( "upvalues2" ); runTest( "upvalues2" );
} }
//*/
private void runTest( String testName ) throws IOException, InterruptedException { private void runTest( String testName ) throws IOException, InterruptedException {
// new lua state // new lua state

88
src/test/res/errors.lua Normal file
View File

@@ -0,0 +1,88 @@
-- object ids
require 'ids'
ids = {}
-- test of common types of errors
local function c(f,...) return f(...) end
local function b(...) return c(...) end
local function a(...) return pcall(b,...) end
s = 'some string'
local t = {}
-- error message tests
print( 'a(error)', a(error) )
print( 'a(error,"msg")', a(error,"msg") )
print( 'a(error,"msg",0)', a(error,"msg",0) )
print( 'a(error,"msg",1)', a(error,"msg",1) )
print( 'a(error,"msg",2)', a(error,"msg",2) )
print( 'a(error,"msg",3)', a(error,"msg",3) )
print( 'a(error,"msg",4)', a(error,"msg",4) )
print( 'a(error,"msg",5)', a(error,"msg",5) )
print( 'a(error,"msg",6)', a(error,"msg",6) )
-- call errors
print( 'a(nil())', a(function() return n() end) )
print( 'a(t()) ', a(function() return t() end) )
print( 'a(s()) ', a(function() return s() end) )
print( 'a(true())', a(function() local b = true; return b() end) )
-- arithmetic errors
print( 'a(nil+1)', a(function() return nil+1 end) )
print( 'a(a+1) ', a(function() return a+1 end) )
print( 'a(s+1) ', a(function() return s+1 end) )
print( 'a(true+1)', a(function() local b = true; return b+1 end) )
-- table errors
print( 'a(nil.x)', a(function() return n.x end) )
print( 'a(a.x) ', a(function() return a.x end) )
print( 'a(s.x) ', a(function() return s.x end) )
print( 'a(true.x)', a(function() local b = true; return b.x end) )
print( 'a(nil.x=5)', a(function() n.x=5 end) )
print( 'a(a.x=5) ', a(function() a.x=5 end) )
print( 'a(s.x=5) ', a(function() s.x=5 end) )
print( 'a(true.x=5)', a(function() local b = true; b.x=5 end) )
-- len operator
print( 'a(#nil) ', a(function() return #n end) )
print( 'a(#t) ', a(function() return #t end) )
print( 'a(#s) ', a(function() return #s end) )
print( 'a(#a) ', a(function() return #a end) )
print( 'a(#true)', a(function() local b = true; return #b end) )
-- comparison errors
print( 'a(nil>1)', a(function() return nil>1 end) )
print( 'a(a>1) ', a(function() return a>1 end) )
print( 'a(s>1) ', a(function() return s>1 end) )
print( 'a(true>1)', a(function() local b = true; return b>1 end) )
-- unary minus errors
print( 'a(-nil)', a(function() return -n end) )
print( 'a(-a) ', a(function() return -a end) )
print( 'a(-s) ', a(function() return -s end) )
print( 'a(-true)', a(function() local b = true; return -b end) )
-- pairs
print( 'a(pairs(nil))', a(function() return id(pairs(nil,{})) end) )
print( 'a(pairs(a)) ', a(function() return id(pairs(a,{})) end) )
print( 'a(pairs(s)) ', a(function() return id(pairs(s,{})) end) )
print( 'a(pairs(t)) ', a(function() return id(pairs(t,{})) end) )
print( 'a(pairs(true))', a(function() local b = true; return id(pairs(b,{})) end) )
-- setmetatable
function sm(...)
return id(setmetatable(...))
end
print( 'a(setmetatable(nil))', a(function() return sm(nil,{}) end) )
print( 'a(setmetatable(a)) ', a(function() return sm(a,{}) end) )
print( 'a(setmetatable(s)) ', a(function() return sm(s,{}) end) )
print( 'a(setmetatable(true))', a(function() local b = true; return sm(b,{}) end) )
print( 'a(setmetatable(t)) ', a(function() return sm(t,{}) end) )
print( 'a(setmetatable(t*)) ', a(function() return sm(t,{__metatable={}}) end) )
print( 'a(setmetatable(t)) ', a(function() return sm(t,{}) end) )
t = {}
print( 'a(setmetatable(t)) ', a(function() return sm(t,{}) end) )
print( 'a(setmetatable(t*)) ', a(function() return sm(t,{__metatable='some string'}) end) )
print( 'a(setmetatable(t)) ', a(function() return sm(t,{}) end) )
-- bad args to error!
print( 'error("msg","arg")', a(function() error('some message', 'some bad arg') end) )

14
src/test/res/ids.lua Normal file
View File

@@ -0,0 +1,14 @@
-- utility to give tables and functions uniform ids for testing
ids = {}
function id(obj)
if not obj or type(obj) == 'number' or type(obj) == 'string' or type(obj) == 'boolean' then
return obj
end
local v = ids[obj]
if v then
return v
end
table.insert(ids,obj)
ids[obj] = type(obj)..'.'..tostring(#ids)
return ids[obj]
end