diff --git a/src/core/org/luaj/vm/LuaState.java b/src/core/org/luaj/vm/LuaState.java index bbb56289..58cf1e7e 100644 --- a/src/core/org/luaj/vm/LuaState.java +++ b/src/core/org/luaj/vm/LuaState.java @@ -70,6 +70,9 @@ import org.luaj.lib.TableLib; */ public class LuaState extends Lua { + public static final String PROPERTY_LUAJ_DEBUG = "Luaj-Debug"; + protected static final String DEBUG_CLASS_NAME = "org.luaj.debug.DebugLuaState"; + /* thread status; 0 is OK */ private static final int LUA_YIELD = 1; private static final int LUA_ERRRUN = 2; @@ -109,6 +112,8 @@ public class LuaState extends Lua { * does all memory allocation for this state through this function. The * second argument, ud, is an opaque pointer that Lua simply * passes to the allocator in every call. + * + * @deprecated As of version 0.10, replaced by {@link #newState()} */ public LuaState() { _G = new LTable(); @@ -119,6 +124,38 @@ public class LuaState extends Lua { _G = globals; } + /** + * Factory method to return an instance of LuaState. If debug property is + * present, it will create a DebugLuaState instance. + * @return + */ + public static LuaState newState() { + String isDebugStr + = Platform.getInstance().getProperty(PROPERTY_LUAJ_DEBUG, "false"); + boolean isDebug = Boolean.parseBoolean(isDebugStr); + + LuaState vm = null; + if ( isDebug ) { + try { + vm = (LuaState) Class.forName( DEBUG_CLASS_NAME ).newInstance(); + } catch (Exception e) { + System.out.println("Warning: no debug support, " + e ); + } + } + + if ( vm == null ) + vm = new LuaState(); + + vm.init(); + + return vm; + } + + /** + * Performs the initialization. + */ + public void init() {} + /** * Install the standard set of libraries used by most implementations: * BaseLib, CoroutineLib, MathLib, PackageLib, TableLib, StringLib diff --git a/src/core/org/luaj/vm/Platform.java b/src/core/org/luaj/vm/Platform.java index 426886b8..06cc25be 100644 --- a/src/core/org/luaj/vm/Platform.java +++ b/src/core/org/luaj/vm/Platform.java @@ -21,10 +21,15 @@ ******************************************************************************/ package org.luaj.vm; +import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; +import org.luaj.debug.DebugLuaState; +import org.luaj.debug.net.DebugSupport; +import org.luaj.debug.net.j2se.DebugSupportImpl; + /** * Singleton to manage platform-specific behaviors. * @@ -41,18 +46,40 @@ abstract public class Platform { * InputStreamReader class. */ public static Platform getInstance() { - if ( instance == null ) { - instance = new Platform() { - public Reader createReader(InputStream inputStream) { - return new InputStreamReader(inputStream); - } - public InputStream openFile(String fileName) { - return getClass().getResourceAsStream("/"+fileName); - } - }; - } - return instance; - } + if (instance == null) { + instance = new Platform() { + public Reader createReader(InputStream inputStream) { + return new InputStreamReader(inputStream); + } + + public InputStream openFile(String fileName) { + return getClass().getResourceAsStream("/" + fileName); + } + + /** + * Assumes J2SE platform, return the corresponding system property + */ + public String getProperty(String propertyName, + String defaultValue) { + return System.getProperty(propertyName, defaultValue); + } + + /** + * Provides a J2SE DebugSupport instance. + */ + public DebugSupport getDebugSupport() throws IOException { + String portStr = getProperty(DebugLuaState.PROPERTY_LUAJ_DEBUG_PORT, "-1"); + try { + int port = Integer.parseInt(portStr); + return new DebugSupportImpl(port); + } catch (NumberFormatException e) { + throw new IOException("Bad port number: " + portStr); + } + } + }; + } + return instance; + } /** * Set the Platform instance. @@ -77,4 +104,18 @@ abstract public class Platform { * @return Reader instance to use for character input */ abstract public Reader createReader( InputStream inputStream ); + + /** + * Returns the value for the given platform property. + * @param propertyName Property name + * @param defaultValue Default property value + * @return Property value + */ + abstract public String getProperty(String propertyName, String defaultValue); + + /** + * Returns an platform dependent DebugSupport instance. + * @return an plaform dependent DebugSupport instance. + */ + abstract public DebugSupport getDebugSupport() throws IOException; } diff --git a/src/debug/org/luaj/debug/DebugLuaState.java b/src/debug/org/luaj/debug/DebugLuaState.java index 8150e070..1d12984f 100644 --- a/src/debug/org/luaj/debug/DebugLuaState.java +++ b/src/debug/org/luaj/debug/DebugLuaState.java @@ -45,9 +45,14 @@ import org.luaj.vm.LocVars; import org.luaj.vm.Lua; import org.luaj.vm.LuaErrorException; import org.luaj.vm.LuaState; +import org.luaj.vm.Platform; public class DebugLuaState extends LuaState implements DebugRequestListener { + public static final String PROPERTY_LUAJ_DEBUG_SUSPEND_AT_START = "Luaj-Debug-SuspendAtStart"; + public static final String PROPERTY_LUAJ_DEBUG_HOST = "Luaj-Debug-Host"; + public static final String PROPERTY_LUAJ_DEBUG_PORT = "Luaj-Debug-Port"; + private static final boolean TRACE = (null != System.getProperty("TRACE")); // stepping constants and stepping state @@ -68,9 +73,35 @@ public class DebugLuaState extends LuaState implements DebugRequestListener { protected DebugSupport debugSupport; protected LuaErrorException lastError; + /** + * Creates an instance of DebugLuaState. + * + * @deprecated As of version 0.10, replaced by {@link #LuaState.newState()} + */ public DebugLuaState() {} - public void setDebugSupport(DebugSupport debugSupport) + public void init() { + Platform platform = Platform.getInstance(); + + // set if the vm should be suspended at start + String suspendOnStartStr = platform.getProperty(PROPERTY_LUAJ_DEBUG_SUSPEND_AT_START, "false"); + boolean bSuspendOnStart = Boolean.parseBoolean(suspendOnStartStr); + setSuspendAtStart(bSuspendOnStart); + + // set up the debug networking support + try { + DebugSupport debugSupport = platform.getDebugSupport(); + if (debugSupport != null) + setDebugSupport(debugSupport); + else + System.out.println("Warning: DebugSupport is not implemented."); + } catch (IOException e) { + // no debug client can talk to VM, but VM can continue functioning + System.out.println("Warning: no debug client support due to error: " + e.getMessage()); + } + } + + protected void setDebugSupport(DebugSupport debugSupport) throws IOException { if (debugSupport == null) { throw new IllegalArgumentException("DebugSupport cannot be null"); @@ -394,16 +425,17 @@ public class DebugLuaState extends LuaState implements DebugRequestListener { synchronized (this) { if (exiting) return; - if (this.debugSupport == null) { - throw new IllegalStateException( - "DebugSupport must be defined."); + if (this.debugSupport != null) { + DebugMessage event = new DebugMessage(DebugMessageType.terminated); + debugSupport.notifyDebugEvent(event); } - DebugMessage event = new DebugMessage(DebugMessageType.terminated); - debugSupport.notifyDebugEvent(event); exit(); - debugSupport.stop(); - debugSupport = null; + + if (this.debugSupport != null) { + debugSupport.stop(); + debugSupport = null; + } } } diff --git a/src/debug/org/luaj/debug/j2se/StandardLuaJVM.java b/src/debug/org/luaj/debug/j2se/StandardLuaJVM.java index 2d1a0f60..6736f96c 100644 --- a/src/debug/org/luaj/debug/j2se/StandardLuaJVM.java +++ b/src/debug/org/luaj/debug/j2se/StandardLuaJVM.java @@ -28,8 +28,6 @@ import java.io.InputStream; import org.luaj.compiler.LuaC; import org.luaj.debug.DebugLuaState; -import org.luaj.debug.net.DebugSupport; -import org.luaj.debug.net.j2se.DebugSupportImpl; import org.luaj.lib.j2se.LuajavaLib; import org.luaj.vm.LClosure; import org.luaj.vm.LPrototype; @@ -81,8 +79,10 @@ public class StandardLuaJVM { if (args.length < 2) { throw new ParseException("Invalid command line arguments."); } - + this.isDebugMode = true; + System.setProperty(LuaState.PROPERTY_LUAJ_DEBUG, "true"); + String debugOptions = args[0]; debugOptions = debugOptions.substring(2); // remove '-D' String[] options = debugOptions.split(","); @@ -91,6 +91,7 @@ public class StandardLuaJVM { String portString = options[i].substring(CMD_LINE_DEBUG_OPTION_PORT.length()); try { this.debugPort = Integer.parseInt(portString); + System.setProperty(DebugLuaState.PROPERTY_LUAJ_DEBUG_PORT, String.valueOf(debugPort)); if (this.debugPort <= 0) { throw new ParseException( "Invalid debug port: it must be greater than zero."); @@ -105,12 +106,13 @@ public class StandardLuaJVM { !suspendOnStartStr.equalsIgnoreCase("false")) { throw new ParseException("invalid debug flag: suspendOnStart"); } - this.bSuspendOnStart = Boolean.valueOf(suspendOnStartStr).booleanValue(); + this.bSuspendOnStart = Boolean.parseBoolean(suspendOnStartStr); + System.setProperty(DebugLuaState.PROPERTY_LUAJ_DEBUG_SUSPEND_AT_START, suspendOnStartStr); } else { throw new ParseException("Invalid command line argument: " + debugOptions); } } - + if (this.debugPort == -1) { throw new ParseException("Invalid command line: debug port is missing"); } @@ -168,10 +170,28 @@ public class StandardLuaJVM { public void run() { try { - if (isDebug()) { - doDebug(); - } else { - doRun(); + // new lua debug state + state = LuaState.newState(); + init(state); + + // load the Lua file + InputStream is = new FileInputStream(new File(getScript())); + LPrototype p = LoadState.undump(state, is, getScript()); + + // create closure and execute + final LClosure c = new LClosure(p, state._G); + String[] args = getScriptArgs(); + int numOfScriptArgs = (args != null ? args.length : 0); + LValue[] vargs = new LValue[numOfScriptArgs]; + for (int i = 0; i < numOfScriptArgs; i++) { + vargs[i] = new LString(args[i]); + } + try { + state.doCall(c, vargs); + } finally { + if (state instanceof DebugLuaState) { + ((DebugLuaState)state).stop(); + } } } catch (LuaErrorException e) { System.err.println("Error: " + e.getMessage()); @@ -194,62 +214,6 @@ public class StandardLuaJVM { LuaC.install(); } - protected void doRun() throws IOException { - - // new lua state - state = new LuaState(); - init(state); - - // convert args to lua - String[] scriptArgs = getScriptArgs(); - int numOfScriptArgs = (scriptArgs == null) ? 0 : scriptArgs.length; - LValue[] vargs = new LValue[numOfScriptArgs]; - for (int i = 0; i < numOfScriptArgs; i++) { - vargs[i] = new LString(getScriptArgs()[i]); - } - - // load the Lua file - InputStream is = new FileInputStream(new File(getScript())); - LPrototype p = LoadState.undump(state, is, getScript()); - - // create closure and execute - LClosure c = new LClosure(state, p); - state.doCall(c, vargs); - } - - protected void doDebug() throws IOException { - // new lua debug state - state = new DebugLuaState(); - init(state); - - // load the Lua file - InputStream is = new FileInputStream(new File(getScript())); - LPrototype p = LoadState.undump(state, is, getScript()); - - // set up debug support if the file is successfully loaded - DebugSupport debugSupport = new DebugSupportImpl(getDebugPort()); - getDebugState().setSuspendAtStart(getSuspendOnStart()); - getDebugState().setDebugSupport(debugSupport); - - // create closure and execute - final LClosure c = new LClosure(p, state._G); - String[] args = getScriptArgs(); - int numOfScriptArgs = (args != null ? args.length : 0); - LValue[] vargs = new LValue[numOfScriptArgs]; - for (int i = 0; i < numOfScriptArgs; i++) { - vargs[i] = new LString(args[i]); - } - try { - getDebugState().doCall(c, vargs); - } finally { - getDebugState().stop(); - } - } - - private DebugLuaState getDebugState() { - return (DebugLuaState) state; - } - /** * Parses the command line arguments and executes/debugs the lua program. * @param args -- command line arguments: diff --git a/src/test/java/org/luaj/debug/j2se/LuaJVMTest.java b/src/test/java/org/luaj/debug/j2se/LuaJVMTest.java index 4735410f..c0978691 100644 --- a/src/test/java/org/luaj/debug/j2se/LuaJVMTest.java +++ b/src/test/java/org/luaj/debug/j2se/LuaJVMTest.java @@ -21,14 +21,15 @@ ******************************************************************************/ package org.luaj.debug.j2se; -import java.io.IOException; import java.net.URL; - -import org.luaj.debug.j2se.StandardLuaJVM; -import org.luaj.debug.j2se.StandardLuaJVM.ParseException; +import java.util.Properties; import junit.framework.TestCase; +import org.luaj.debug.DebugLuaState; +import org.luaj.debug.j2se.StandardLuaJVM.ParseException; +import org.luaj.vm.LuaState; + /** * Sanity test for StandardLuaJVM. */ @@ -88,7 +89,7 @@ public class LuaJVMTest extends TestCase { vm = new StandardLuaJVM(); try { vm.parse(args); - assertFalse(1044 == vm.getDebugPort()); + assertTrue(1044 == vm.getDebugPort()); assertFalse(true == vm.getSuspendOnStart()); assertEquals("dummy.lua", vm.getScript()); } catch (ParseException e) { @@ -133,7 +134,18 @@ public class LuaJVMTest extends TestCase { } catch (ParseException e) { fail("Should never reach this line."); } - + + args = new String[] { "-Dport=1044,suspendOnStart=True", "dummy.lua" }; + vm = new StandardLuaJVM(); + try { + vm.parse(args); + assertEquals(1044, vm.getDebugPort()); + assertEquals(true, vm.getSuspendOnStart()); + assertEquals("dummy.lua", vm.getScript()); + } catch (ParseException e) { + fail("Should never reach this line."); + } + args = new String[] { "-DsuspendOnStart=true,port=1044", "dummy.lua" }; vm = new StandardLuaJVM(); try { @@ -169,13 +181,37 @@ public class LuaJVMTest extends TestCase { } public void testRun() { + Properties props = System.getProperties(); + props.remove(LuaState.PROPERTY_LUAJ_DEBUG); + props.remove(DebugLuaState.PROPERTY_LUAJ_DEBUG_HOST); + props.remove(DebugLuaState.PROPERTY_LUAJ_DEBUG_PORT); + props.remove(DebugLuaState.PROPERTY_LUAJ_DEBUG_SUSPEND_AT_START); + System.setProperties(props); + String[] tests = new String[] { "autoload", "boolean", "calls", "coercions", "compare", "math", "mathlib", "metatables", "select", "setlist", "swingapp", "test1", "test2", "test3", "test4", "test5", "test6", "test7", "type", "upvalues", - // "strlib" + "strlib" }; + doRun(tests); + } + public void testDebugRun() { + Properties props = System.getProperties(); + props.setProperty(LuaState.PROPERTY_LUAJ_DEBUG, "true"); + props.setProperty(DebugLuaState.PROPERTY_LUAJ_DEBUG_PORT, "1999"); + System.setProperties(props); + + String[] tests = new String[] { "boolean", "calls", + "coercions", "compare", "math", "mathlib", "metatables", + "select", "setlist", "swingapp", "test1", "test2", "test3", + "test4", "test5", "test6", "test7", "type", "upvalues" + }; + doRun(tests); + } + + private void doRun(String[] tests) { for (int i = 0; i < tests.length; i++) { String test = tests[i]; System.out.println("==> running test: " + test + ".lua"); @@ -185,7 +221,7 @@ public class LuaJVMTest extends TestCase { System.out.println(); } } - + protected void doTestRun(String testName) { String[] args = new String[1]; URL filePath = getClass().getResource("/" + testName);