From 644896c46755d9c16c6aa4fcdc02cd1740a9683d Mon Sep 17 00:00:00 2001 From: James Roseborough Date: Thu, 6 Dec 2007 15:29:49 +0000 Subject: [PATCH] Improve error reporting. --- src/core/org/luaj/lib/BaseLib.java | 2 +- src/core/org/luaj/lib/CoroutineLib.java | 2 +- src/core/org/luaj/lib/MathLib.java | 2 +- src/core/org/luaj/lib/PackageLib.java | 4 +- src/core/org/luaj/lib/StringLib.java | 2 +- src/core/org/luaj/lib/TableLib.java | 2 +- src/core/org/luaj/vm/LDouble.java | 25 +++--- src/core/org/luaj/vm/LInteger.java | 5 +- src/core/org/luaj/vm/LString.java | 3 +- src/core/org/luaj/vm/LValue.java | 69 ++++++++-------- src/core/org/luaj/vm/LuaErrorException.java | 41 ++++++++-- src/core/org/luaj/vm/LuaState.java | 21 ++--- src/j2se/org/luaj/lib/j2se/LuajavaLib.java | 2 +- src/test/java/org/luaj/vm/LuaJTest.java | 6 +- src/test/res/errors.lua | 88 +++++++++++++++++++++ src/test/res/ids.lua | 14 ++++ 16 files changed, 208 insertions(+), 80 deletions(-) create mode 100644 src/test/res/errors.lua create mode 100644 src/test/res/ids.lua diff --git a/src/core/org/luaj/lib/BaseLib.java b/src/core/org/luaj/lib/BaseLib.java index 7ff8e119..260ff843 100644 --- a/src/core/org/luaj/lib/BaseLib.java +++ b/src/core/org/luaj/lib/BaseLib.java @@ -364,7 +364,7 @@ public class BaseLib extends LFunction { break; } default: - luaUnsupportedOperation(); + throw new RuntimeException( "bad id: "+id ); } return false; } diff --git a/src/core/org/luaj/lib/CoroutineLib.java b/src/core/org/luaj/lib/CoroutineLib.java index 9edd4876..b1f8346b 100644 --- a/src/core/org/luaj/lib/CoroutineLib.java +++ b/src/core/org/luaj/lib/CoroutineLib.java @@ -126,7 +126,7 @@ public class CoroutineLib extends LFunction { if ( vm.toboolean(1) ) vm.remove(1); else - vm.error( vm.tostring(2), 0 ); + vm.error( vm.tostring(2) ); return false; } } diff --git a/src/core/org/luaj/lib/MathLib.java b/src/core/org/luaj/lib/MathLib.java index 17c615a3..4bffc896 100644 --- a/src/core/org/luaj/lib/MathLib.java +++ b/src/core/org/luaj/lib/MathLib.java @@ -113,7 +113,7 @@ public class MathLib extends LFunction { setResult( vm, LInteger.valueOf( (int) Math.floor( vm.tonumber(2) ) ) ); break; default: - luaUnsupportedOperation(); + throw new RuntimeException( "bad id: "+id ); } return false; } diff --git a/src/core/org/luaj/lib/PackageLib.java b/src/core/org/luaj/lib/PackageLib.java index 7519e1b9..94070e2a 100644 --- a/src/core/org/luaj/lib/PackageLib.java +++ b/src/core/org/luaj/lib/PackageLib.java @@ -144,7 +144,7 @@ public class PackageLib extends LFunction { break; } default: - luaUnsupportedOperation(); + throw new RuntimeException( "bad id: "+id ); } return false; } @@ -182,7 +182,7 @@ public class PackageLib extends LFunction { /* try global variable (and create one if it does not exist) */ module = findtable( vm._G, modname ); if ( module == null ) - vm.error( "name conflict for module '"+modname+"'", 2 ); + vm.error( "name conflict for module '"+modname+"'" ); LOADED.luaSetTable(vm, LOADED, modname, module); } else { module = (LTable) value; diff --git a/src/core/org/luaj/lib/StringLib.java b/src/core/org/luaj/lib/StringLib.java index 9abc5014..1cfbe7c1 100644 --- a/src/core/org/luaj/lib/StringLib.java +++ b/src/core/org/luaj/lib/StringLib.java @@ -135,7 +135,7 @@ public class StringLib extends LFunction { break; default: - luaUnsupportedOperation(); + vm.error( "bad id" ); } return false; } diff --git a/src/core/org/luaj/lib/TableLib.java b/src/core/org/luaj/lib/TableLib.java index 22909674..1470675c 100644 --- a/src/core/org/luaj/lib/TableLib.java +++ b/src/core/org/luaj/lib/TableLib.java @@ -175,7 +175,7 @@ public class TableLib extends LFunction { } default: - luaUnsupportedOperation(); + throw new RuntimeException( "bad id" ); } return false; } diff --git a/src/core/org/luaj/vm/LDouble.java b/src/core/org/luaj/vm/LDouble.java index df28b63f..22ae9a19 100644 --- a/src/core/org/luaj/vm/LDouble.java +++ b/src/core/org/luaj/vm/LDouble.java @@ -77,25 +77,27 @@ public class LDouble extends LNumber { case Lua.OP_MUL: 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_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) { if ( b < 0 ) return 1 / dpow( a, -b ); - int p = 1; + double p = 1; 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 ) p *= v; - int frac = (int) (0x10000 * b); - for ( ; (frac&0xffff)!=0; frac<<=1 ) { - a = Math.sqrt(a); - if ( (frac & 0x8000) != 0 ) - p *= a; + if ( (b -= whole) > 0 ) { + int frac = (int) (0x10000 * b); + for ( ; (frac&0xffff)!=0; frac<<=1 ) { + a = Math.sqrt(a); + if ( (frac & 0x8000) != 0 ) + p *= a; + } } return p; } @@ -132,8 +134,7 @@ public class LDouble extends LNumber { case Lua.OP_LT: return lhs < rhs; case Lua.OP_LE: return lhs <= rhs; } - luaUnsupportedOperation(); - return false; + throw new RuntimeException("bad opcode"); } /** Arithmetic negative */ diff --git a/src/core/org/luaj/vm/LInteger.java b/src/core/org/luaj/vm/LInteger.java index 23f6e470..44410757 100644 --- a/src/core/org/luaj/vm/LInteger.java +++ b/src/core/org/luaj/vm/LInteger.java @@ -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_POW: return LInteger.valueOf( ipow(m_value, rhs) ); } - return luaUnsupportedOperation(); + throw new RuntimeException("bad opcode"); } 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_LE: return m_value <= rhs; } - luaUnsupportedOperation(); - return false; + throw new RuntimeException("bad opcode"); } // unsupported except for numbers diff --git a/src/core/org/luaj/vm/LString.java b/src/core/org/luaj/vm/LString.java index aa6b72cb..5f70685a 100644 --- a/src/core/org/luaj/vm/LString.java +++ b/src/core/org/luaj/vm/LString.java @@ -315,8 +315,7 @@ public class LString extends LValue { case Lua.OP_LT: return compareTo(rhs) < 0; case Lua.OP_LE: return compareTo(rhs) <= 0; } - luaUnsupportedOperation(); - return false; + throw new RuntimeException("bad opcode"); } public LValue luaBinOpDouble( int opcode, double m_value ) { diff --git a/src/core/org/luaj/vm/LValue.java b/src/core/org/luaj/vm/LValue.java index 40aed512..bb1fa380 100644 --- a/src/core/org/luaj/vm/LValue.java +++ b/src/core/org/luaj/vm/LValue.java @@ -31,14 +31,21 @@ public class LValue { /** Metatable tag for intercepting table sets */ public static final LString TM_NEWINDEX = new LString("__newindex"); - protected static LValue luaUnsupportedOperation() { - throw new LuaErrorException( "not supported" ); - } - - protected void luaConversionError(String target) { + protected void conversionError(String 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() { return Integer.toHexString(hashCode()); @@ -58,24 +65,24 @@ public class LValue { // unsupported except for numbers public LValue luaBinOpUnknown(int opcode, LValue lhs) { - return luaUnsupportedOperation(); + return arithmeticError(lhs.luaGetTypeName()); } // unsupported except for numbers public LValue luaBinOpInteger(int opcode, int m_value) { - return luaUnsupportedOperation(); + return arithmeticError("number"); } // unsupported except for numbers 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. public boolean luaBinCmpUnknown(int opcode, LValue lhs) { if ( opcode == Lua.OP_EQ ) return lhs == this; - luaUnsupportedOperation(); + compareError(lhs.luaGetTypeName(), luaGetTypeName()); return false; } @@ -83,7 +90,7 @@ public class LValue { public boolean luaBinCmpString(int opcode, LString rhs) { if ( opcode == Lua.OP_EQ ) return false; - luaUnsupportedOperation(); + compareError(luaGetTypeName(), "string"); return false; } @@ -91,7 +98,7 @@ public class LValue { public boolean luaBinCmpInteger(int opcode, int rhs) { if ( opcode == Lua.OP_EQ ) return false; - luaUnsupportedOperation(); + compareError(luaGetTypeName(), "number"); return false; } @@ -99,7 +106,7 @@ public class LValue { public boolean luaBinCmpDouble(int opcode, double rhs) { if ( opcode == Lua.OP_EQ ) return false; - luaUnsupportedOperation(); + compareError(luaGetTypeName(), "number"); return false; } @@ -119,7 +126,7 @@ public class LValue { return; } } - vm.pushnil(); + indexError( vm, table ); } /** Get a value from a table @@ -136,7 +143,7 @@ public class LValue { return; } } - vm.error( "attempt to index ? (a "+table.luaGetTypeName()+" value)", 2 ); + indexError( vm, table ); } /** Get the value as a LString @@ -152,20 +159,12 @@ public class LValue { /** Arithmetic negative */ public LValue luaUnaryMinus() { - return luaUnsupportedOperation(); + return arithmeticError(luaGetTypeName()); } /** Built-in opcode LEN, for Strings and Tables */ public int luaLength() { - // TODO: call meta-method TM_LEN here - luaUnsupportedOperation(); - return 0; - } - - /** Valid for tables - * @param isPairs true to iterate over non-integers as well */ - public LValue luaPairs(boolean isPairs) { - return luaUnsupportedOperation(); + throw new LuaErrorException( "attempt to get length of ? (a "+luaGetTypeName()+" value)" ); } /** @@ -184,7 +183,7 @@ public class LValue { /** Valid for tables */ 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 */ @@ -229,7 +228,7 @@ public class LValue { /** Return value as an integer */ public int toJavaInt() { - luaConversionError("number"); + conversionError("number"); return 0; } @@ -245,55 +244,55 @@ public class LValue { /** Convert to a Boolean value */ public Boolean toJavaBoxedBoolean() { - luaConversionError("Boolean"); + conversionError("Boolean"); return null; } /** Convert to a Byte value */ public Byte toJavaBoxedByte() { - luaConversionError("Byte"); + conversionError("Byte"); return null; } /** Convert to a boxed Character value */ public Character toJavaBoxedCharacter() { - luaConversionError("Character"); + conversionError("Character"); return null; } /** Convert to a boxed Double value */ public Double toJavaBoxedDouble() { - luaConversionError("Double"); + conversionError("Double"); return null; } /** Convert to a boxed Float value */ public Float toJavaBoxedFloat() { - luaConversionError("Float"); + conversionError("Float"); return null; } /** Convert to a boxed Integer value */ public Integer toJavaBoxedInteger() { - luaConversionError("Integer"); + conversionError("Integer"); return null; } /** Convert to a boxed Long value */ public Long toJavaBoxedLong() { - luaConversionError("Long"); + conversionError("Long"); return null; } /** Convert to a boxed Short value */ public Short toJavaBoxedShort() { - luaConversionError("Short"); + conversionError("Short"); return null; } /** Convert to a Java Object iff this is a LUserData value */ public Object toJavaInstance() { - luaConversionError("instance"); + conversionError("instance"); return null; } diff --git a/src/core/org/luaj/vm/LuaErrorException.java b/src/core/org/luaj/vm/LuaErrorException.java index 39078582..59be3a18 100644 --- a/src/core/org/luaj/vm/LuaErrorException.java +++ b/src/core/org/luaj/vm/LuaErrorException.java @@ -36,7 +36,15 @@ public class LuaErrorException extends RuntimeException { * Construct a LuaErrorException with the default message. */ 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 */ public LuaErrorException(String message) { - super(message); + this(null, message, 1); } /** - * Construct a LuaErrorException in response to a Throwable that was caught - * and with the default message. + * Construct the message around a specific vm and with a particular level of debug info + * @param vm + * @param message + * @param level */ - public LuaErrorException(Throwable cause) { - super(DEFAULT_MESSAGE+": "+cause); + public LuaErrorException(LuaState vm, String message, int level) { + 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; } } diff --git a/src/core/org/luaj/vm/LuaState.java b/src/core/org/luaj/vm/LuaState.java index a06cbcab..01d5e9af 100644 --- a/src/core/org/luaj/vm/LuaState.java +++ b/src/core/org/luaj/vm/LuaState.java @@ -96,7 +96,7 @@ public class LuaState extends Lua { protected Stack upvals = new Stack(); protected LFunction panic; - public static LTable DEFAULT_GLOBALS; + static LuaState mainState; public LTable _G; // 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()} */ public LuaState() { - _G = DEFAULT_GLOBALS = new LTable(); + _G = new LTable(); _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. - * @param cindex + * @param cindex index into call stack * @return */ protected String getFileLine(int cindex) { String source = "?"; String line = ""; - if (cindex >= 0) { + if (cindex >= 0 && cindex <= cc) { CallInfo call = this.calls[cindex]; LPrototype p = call.closure.p; if (p != null && p.source != null) @@ -1041,18 +1042,14 @@ public class LuaState extends Lua { * after filling line number information first when level > 0. */ public void error(String message, int level) { - if ( level > 1 ) - message = getFileLine(cc + 2 - level) + ": " + message; - throw new LuaErrorException( message ); + throw new LuaErrorException( this, message, level ); } /** * Raises an error with the default level. - * - * In the java implementation this calls error(message,1) */ 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() { - String message = tostring(-1); - // pop(1); - error( message ); + throw new LuaErrorException( this, tostring(-1), 0); } /** diff --git a/src/j2se/org/luaj/lib/j2se/LuajavaLib.java b/src/j2se/org/luaj/lib/j2se/LuajavaLib.java index eb4ff232..47ce3404 100644 --- a/src/j2se/org/luaj/lib/j2se/LuajavaLib.java +++ b/src/j2se/org/luaj/lib/j2se/LuajavaLib.java @@ -109,7 +109,7 @@ public final class LuajavaLib extends LFunction { } break; default: - luaUnsupportedOperation(); + throw new LuaErrorException("not yet supported: "+this); } return false; } diff --git a/src/test/java/org/luaj/vm/LuaJTest.java b/src/test/java/org/luaj/vm/LuaJTest.java index d548179d..452538e0 100644 --- a/src/test/java/org/luaj/vm/LuaJTest.java +++ b/src/test/java/org/luaj/vm/LuaJTest.java @@ -70,6 +70,10 @@ public class LuaJTest extends TestCase { runTest( "compare" ); } + public void testErrors() throws IOException, InterruptedException { + runTest( "errors" ); + } + public void testMathLib() throws IOException, InterruptedException { runTest( "mathlib" ); } @@ -129,7 +133,7 @@ public class LuaJTest extends TestCase { public void testUpvalues2() throws IOException, InterruptedException { runTest( "upvalues2" ); } - +//*/ private void runTest( String testName ) throws IOException, InterruptedException { // new lua state diff --git a/src/test/res/errors.lua b/src/test/res/errors.lua new file mode 100644 index 00000000..a4eee00d --- /dev/null +++ b/src/test/res/errors.lua @@ -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) ) diff --git a/src/test/res/ids.lua b/src/test/res/ids.lua new file mode 100644 index 00000000..58075d0d --- /dev/null +++ b/src/test/res/ids.lua @@ -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