diff --git a/.classpath b/.classpath index fc39bf58..1af1087a 100644 --- a/.classpath +++ b/.classpath @@ -6,5 +6,6 @@ + diff --git a/build.xml b/build.xml index 11145062..c17a9eee 100644 --- a/build.xml +++ b/build.xml @@ -13,8 +13,9 @@ - - + + + diff --git a/src/main/java/lua/debug/DebugStackState.java b/src/main/java/lua/debug/DebugStackState.java index 4d3d486a..66af7c4a 100644 --- a/src/main/java/lua/debug/DebugStackState.java +++ b/src/main/java/lua/debug/DebugStackState.java @@ -21,7 +21,10 @@ ******************************************************************************/ package lua.debug; +import java.io.IOException; import java.util.Hashtable; +import java.util.Timer; +import java.util.TimerTask; import java.util.Vector; import lua.CallInfo; @@ -30,7 +33,7 @@ import lua.StackState; import lua.addon.compile.LexState; import lua.debug.event.DebugEvent; import lua.debug.event.DebugEventBreakpoint; -import lua.debug.event.DebugEventListener; +import lua.debug.event.DebugEventType; import lua.debug.request.DebugRequest; import lua.debug.request.DebugRequestLineBreakpointToggle; import lua.debug.request.DebugRequestListener; @@ -40,6 +43,7 @@ import lua.debug.response.DebugResponse; import lua.debug.response.DebugResponseCallgraph; import lua.debug.response.DebugResponseSimple; import lua.debug.response.DebugResponseStack; +import lua.io.Closure; import lua.io.LocVars; import lua.io.Proto; import lua.value.LTable; @@ -54,34 +58,24 @@ public class DebugStackState extends StackState implements DebugRequestListener protected boolean suspended = false; protected boolean stepping = false; protected int lastline = -1; - protected Vector debugEventListeners = new Vector(); + protected DebugSupport debugSupport = null; + + public DebugStackState() {} - public DebugStackState() { + public void setDebugSupport(DebugSupport debugSupport) throws IOException { + if (debugSupport == null) { + throw new IllegalArgumentException("DebugSupport cannot be null"); + } + + this.debugSupport = debugSupport; + debugSupport.setDebugStackState(this); + debugSupport.start(); } protected void debugAssert(boolean b) { if ( ! b ) error( "assert failure" ); } - - public void addDebugEventListener(DebugEventListener listener) { - if (!debugEventListeners.contains(listener)) { - debugEventListeners.addElement(listener); - } - } - - public void removeDebugEventListener(DebugEventListener listener) { - if (debugEventListeners.contains(listener)) { - debugEventListeners.removeElement(listener); - } - } - - protected void notifyDebugEventListeners(DebugEvent event) { - for (int i = 0; debugEventListeners != null && i < debugEventListeners.size(); i++) { - DebugEventListener listener = (DebugEventListener)debugEventListeners.elementAt(i); - listener.notifyDebugEvent(event); - } - } private String getFileLine(int cindex) { String func = "?"; @@ -157,10 +151,12 @@ public class DebugStackState extends StackState implements DebugRequestListener Proto p = calls[cc].closure.p; String source = DebugUtils.getSourceFileName(p.source); if ( breakpoints.containsKey(constructBreakpointKey(source, line))){ - if(DebugUtils.IS_DEBUG) + if(DebugUtils.IS_DEBUG) { DebugUtils.println("hitting breakpoint " + constructBreakpointKey(source, line)); - notifyDebugEventListeners( - new DebugEventBreakpoint(source, line)); + } + if (debugSupport != null) { + debugSupport.notifyDebugEvent(new DebugEventBreakpoint(source, line)); + } suspended = true; } else { return; @@ -195,16 +191,28 @@ public class DebugStackState extends StackState implements DebugRequestListener // ------------------ commands coming from the debugger ------------------- public DebugResponse handleRequest(DebugRequest request) { + if (this.debugSupport == null) { + throw new IllegalStateException("DebugStackState is not equiped with DebugSupport."); + } + DebugUtils.println("DebugStackState is handling request: " + request.toString()); - DebugRequestType requestType = request.getType(); - if (DebugRequestType.suspend == requestType) { - suspend(); + DebugRequestType requestType = request.getType(); + if (DebugRequestType.start == requestType) { + DebugEvent event = new DebugEvent(DebugEventType.started); + debugSupport.notifyDebugEvent(event); + return DebugResponseSimple.SUCCESS; + } else if (DebugRequestType.exit == requestType) { + stop(); + return DebugResponseSimple.SUCCESS; + } else if (DebugRequestType.suspend == requestType) { + suspend(); + DebugEvent event = new DebugEvent(DebugEventType.suspendedByClient); + debugSupport.notifyDebugEvent(event); return DebugResponseSimple.SUCCESS; } else if (DebugRequestType.resume == requestType) { - resume(); - return DebugResponseSimple.SUCCESS; - } else if (DebugRequestType.exit == requestType) { - exit(); + resume(); + DebugEvent event = new DebugEvent(DebugEventType.resumedByClient); + debugSupport.notifyDebugEvent(event); return DebugResponseSimple.SUCCESS; } else if (DebugRequestType.lineBreakpointSet == requestType) { DebugRequestLineBreakpointToggle setBreakpointRequest @@ -252,7 +260,25 @@ public class DebugStackState extends StackState implements DebugRequestListener this.notify(); } } - + + public void stop() { + if (this.debugSupport == null) { + throw new IllegalStateException("DebugStackState is not equiped with DebugSupport."); + } + + DebugEvent event = new DebugEvent(DebugEventType.terminated); + debugSupport.notifyDebugEvent(event); + + new Timer().schedule(new TimerTask() { + public void run () { + debugSupport.stop(); + debugSupport = null; + } + }, 300); + + exit(); + } + /** * terminate the execution */ diff --git a/src/main/java/lua/debug/DebugSupport.java b/src/main/java/lua/debug/DebugSupport.java new file mode 100644 index 00000000..8903a8f7 --- /dev/null +++ b/src/main/java/lua/debug/DebugSupport.java @@ -0,0 +1,198 @@ +package lua.debug; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.IOException; + +import lua.debug.event.DebugEvent; +import lua.debug.event.DebugEventListener; +import lua.debug.request.DebugRequest; +import lua.debug.request.DebugRequestListener; +import lua.debug.response.DebugResponse; + +public class DebugSupport implements DebugRequestListener, DebugEventListener { + protected static final int UNKNOWN = 0; + protected static final int RUNNING = 1; + protected static final int STOPPED = 2; + + protected DebugStackState vm; + protected int requestPort; + protected int eventPort; + protected Thread requestWatcherThread; + protected int state = UNKNOWN; + + protected DataInputStream requestReader; + protected DataOutputStream requestWriter; + protected DataOutputStream eventWriter; + + public DebugSupport(int requestPort, + int eventPort) { + if (requestPort == -1) { + throw new IllegalArgumentException("requestPort is invalid"); + } + + if (eventPort == -1) { + throw new IllegalArgumentException("eventPort is invalid"); + } + + this.requestPort = requestPort; + this.eventPort = eventPort; + } + + public void setDebugStackState(DebugStackState vm) { + this.vm = vm; + } + + protected void releaseServer() { + DebugUtils.println("shutting down the debug server..."); + if (requestReader != null) { + try { + requestReader.close(); + } catch (IOException e) {} + } + + if (requestWriter != null) { + try { + requestWriter.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + if (eventWriter != null) { + try { + eventWriter.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public synchronized boolean isStarted() { + return (state == RUNNING || state == STOPPED); + } + + /* (non-Javadoc) + * @see lua.debug.j2se.DebugSupport#start() + */ + public synchronized void start() throws IOException { + if (this.vm == null) { + throw new IllegalStateException("DebugStackState is not set. Please call setDebugStackState first."); + } + + this.requestWatcherThread = new Thread(new Runnable() { + public void run() { + if (getState() != STOPPED) { + handleRequest(); + } else { + releaseServer(); + } + } + }); + this.requestWatcherThread.start(); + this.state = RUNNING; + + System.out.println("LuaJ debug server is started on ports: " + requestPort + ", " + eventPort); + } + + protected synchronized int getState() { + return this.state; + } + + /* (non-Javadoc) + * @see lua.debug.j2se.DebugSupport#stop() + */ + public synchronized void stop() { + DebugUtils.println("stopping the debug support..."); + this.state = STOPPED; + } + + protected void handleRequest() { + try { + while (getState() != STOPPED) { + int size = requestReader.readInt(); + byte[] data = new byte[size]; + requestReader.readFully(data); + DebugRequest request + = (DebugRequest) SerializationHelper.deserialize(data); + DebugUtils.println("SERVER receives request: " + request.toString()); + + DebugResponse response = handleRequest(request); + data = SerializationHelper.serialize(response); + requestWriter.writeInt(data.length); + requestWriter.write(data); + requestWriter.flush(); + DebugUtils.println("SERVER sends response: " + response); + } + + if (getState() == STOPPED) { + cleanup(); + } + } catch (EOFException e) { + cleanup(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void cleanup() { + DebugUtils.println("SERVER terminated..."); + releaseServer(); + System.exit(0); + } + + /** + * This method provides the second communication channel with the debugging + * client. The server can send events via this channel to notify the client + * about debug events (see below) asynchronously. + * + * The following events can be fired: + * 1. started -- the vm is started and ready to receive debugging requests + * (guaranteed to be the first event sent) + * 2. terminated -- the vm is terminated (guaranteed to be the last event sent) + * 3. suspended client|step|breakpoint N + * -- the vm is suspended by client, due to a stepping request or + * the breakpoint at line N is hit + * 4. resumed client|step + * -- the vm resumes execution by client or step + * + * @param event + */ + protected void sendEvent(DebugEvent event) { + DebugUtils.println("SERVER sending event: " + event.toString()); + try { + byte[] data = SerializationHelper.serialize(event); + eventWriter.writeInt(data.length); + eventWriter.write(data); + eventWriter.flush(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /* (non-Javadoc) + * @see lua.debug.DebugEventListener#notifyDebugEvent(lua.debug.DebugEvent) + */ + /* (non-Javadoc) + * @see lua.debug.j2se.DebugSupport#notifyDebugEvent(lua.debug.event.DebugEvent) + */ + public void notifyDebugEvent(DebugEvent event) { + sendEvent(event); + } + + /* (non-Javadoc) + * @see lua.debug.DebugRequestListener#handleRequest(java.lang.String) + */ + /* (non-Javadoc) + * @see lua.debug.j2se.DebugSupport#handleRequest(lua.debug.request.DebugRequest) + */ + public DebugResponse handleRequest(DebugRequest request) { + if (DebugUtils.IS_DEBUG) { + DebugUtils.println("handling request: " + request.toString()); + } + + DebugResponse response = vm.handleRequest(request); + return response; + } +} \ No newline at end of file diff --git a/src/main/java/lua/debug/j2me/DebugSupportImpl.java b/src/main/java/lua/debug/j2me/DebugSupportImpl.java new file mode 100644 index 00000000..e7b92a0c --- /dev/null +++ b/src/main/java/lua/debug/j2me/DebugSupportImpl.java @@ -0,0 +1,98 @@ +package lua.debug.j2me; + +import java.io.IOException; + +import javax.microedition.io.Connector; +import javax.microedition.io.ServerSocketConnection; +import javax.microedition.io.SocketConnection; + +import lua.debug.DebugSupport; +import lua.debug.DebugUtils; +import lua.debug.event.DebugEvent; + +public class DebugSupportImpl extends DebugSupport { + protected ServerSocketConnection requestServerConnection; + protected SocketConnection requestSocketConnection; + + protected ServerSocketConnection eventServerConnection; + protected SocketConnection eventSocketConnection; + + public DebugSupportImpl(int requestPort, + int eventPort) { + super(requestPort, eventPort); + } + + /* (non-Javadoc) + * @see lua.debug.j2se.DebugSupport#start() + */ + public synchronized void start() throws IOException { + System.out.println("Starting the sockets...."); + // Set up the request socket and request input + output streams + this.requestServerConnection + = (ServerSocketConnection)Connector.open("socket://:" + this.requestPort); + this.requestSocketConnection = + (SocketConnection) requestServerConnection.acceptAndOpen(); + requestSocketConnection.setSocketOption(SocketConnection.DELAY, 0); + requestSocketConnection.setSocketOption(SocketConnection.LINGER, 0); + requestSocketConnection.setSocketOption(SocketConnection.KEEPALIVE, 1); + requestSocketConnection.setSocketOption(SocketConnection.RCVBUF, 1024); + requestSocketConnection.setSocketOption(SocketConnection.SNDBUF, 1024); + this.requestReader = requestSocketConnection.openDataInputStream(); + this.requestWriter = requestSocketConnection.openDataOutputStream(); + + // Set up the event socket and event output stream + this.eventServerConnection + = (ServerSocketConnection)Connector.open("socket://:" + this.eventPort); + this.eventSocketConnection + = (SocketConnection) eventServerConnection.acceptAndOpen(); + eventSocketConnection.setSocketOption(SocketConnection.DELAY, 0); + eventSocketConnection.setSocketOption(SocketConnection.LINGER, 0); + eventSocketConnection.setSocketOption(SocketConnection.KEEPALIVE, 1); + eventSocketConnection.setSocketOption(SocketConnection.RCVBUF, 1024); + eventSocketConnection.setSocketOption(SocketConnection.SNDBUF, 1024); + this.eventWriter = eventSocketConnection.openDataOutputStream();; + + System.out.println("Lua debug server is started on ports: " + requestPort + ", " + eventPort); + super.start(); + } + + protected void releaseServer() { + super.releaseServer(); + + if (requestSocketConnection != null) { + try { + requestSocketConnection.close(); + } catch (IOException e) {} + } + + if (requestServerConnection != null) { + try { + requestServerConnection.close(); + } catch (IOException e) {} + } + + if (eventSocketConnection != null) { + try { + eventSocketConnection.close(); + } catch (IOException e) {} + } + + if (eventServerConnection != null){ + try { + eventServerConnection.close(); + } catch (IOException e) {} + } + } + + protected void handleRequest() { + synchronized (requestSocketConnection) { + super.handleRequest(); + } + } + + protected void sendEvent(DebugEvent event) { + synchronized (eventSocketConnection) { + super.sendEvent(event); + } + } +} diff --git a/src/main/java/lua/debug/j2se/DebugSupport.java b/src/main/java/lua/debug/j2se/DebugSupport.java deleted file mode 100644 index daafd58e..00000000 --- a/src/main/java/lua/debug/j2se/DebugSupport.java +++ /dev/null @@ -1,238 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2007 LuaJ. All rights reserved. -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. -******************************************************************************/ -package lua.debug.j2se; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.EOFException; -import java.io.IOException; -import java.net.ServerSocket; -import java.net.Socket; - -import lua.debug.DebugUtils; -import lua.debug.SerializationHelper; -import lua.debug.event.DebugEvent; -import lua.debug.event.DebugEventListener; -import lua.debug.request.DebugRequest; -import lua.debug.request.DebugRequestListener; -import lua.debug.response.DebugResponse; - -public class DebugSupport implements DebugRequestListener, DebugEventListener { - protected static final int UNKNOWN = 0; - protected static final int RUNNING = 1; - protected static final int STOPPED = 2; - - protected DebugRequestListener vm; - protected int requestPort; - protected int eventPort; - protected Thread requestWatcherThread; - protected int state = UNKNOWN; - - protected ServerSocket requestSocket; - protected Socket clientRequestSocket; - protected DataInputStream requestReader; - protected DataOutputStream requestWriter; - - protected ServerSocket eventSocket; - protected Socket clientEventSocket; - protected DataOutputStream eventWriter; - - public DebugSupport(DebugRequestListener vm, - int requestPort, - int eventPort) { - this.vm = vm; - this.requestPort = requestPort; - this.eventPort = eventPort; - } - - protected void releaseServer() { - DebugUtils.println("shutting down the debug server..."); - if (requestReader != null) { - try { - requestReader.close(); - } catch (IOException e) {} - } - - if (requestWriter != null) { - try { - requestWriter.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - if (clientRequestSocket != null) { - try { - clientRequestSocket.close(); - } catch (IOException e) {} - } - - if (requestSocket != null) { - try { - requestSocket.close(); - } catch (IOException e) {} - } - - if (eventWriter != null) { - try { - eventWriter.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - if (clientEventSocket != null) { - try { - clientEventSocket.close(); - } catch (IOException e) {} - } - - if (eventSocket != null){ - try { - eventSocket.close(); - } catch (IOException e) {} - } - } - - public synchronized void start() throws IOException { - this.requestSocket = new ServerSocket(requestPort); - this.clientRequestSocket = requestSocket.accept(); - this.requestReader - = new DataInputStream(clientRequestSocket.getInputStream()); - this.requestWriter - = new DataOutputStream(clientRequestSocket.getOutputStream()); - - this.eventSocket = new ServerSocket(eventPort); - this.clientEventSocket = eventSocket.accept(); - this.eventWriter - = new DataOutputStream(clientEventSocket.getOutputStream()); - - this.requestWatcherThread = new Thread(new Runnable() { - public void run() { - if (getState() != STOPPED) { - handleRequest(); - } else { - releaseServer(); - } - } - }); - this.requestWatcherThread.start(); - this.state = RUNNING; - } - - protected synchronized int getState() { - return this.state; - } - - public synchronized void stop() { - this.state = STOPPED; - } - - protected void handleRequest() { - synchronized (clientRequestSocket) { - try { - while (getState() != STOPPED) { - int size = requestReader.readInt(); - byte[] data = new byte[size]; - requestReader.readFully(data); - DebugRequest request - = (DebugRequest) SerializationHelper.deserialize(data); - DebugUtils.println("SERVER receives request: " + request.toString()); - - DebugResponse response = handleRequest(request); - data = SerializationHelper.serialize(response); - requestWriter.writeInt(data.length); - requestWriter.write(data); - requestWriter.flush(); - DebugUtils.println("SERVER sends response: " + response); - } - - if (getState() == STOPPED) { - cleanup(); - } - } catch (EOFException e) { - cleanup(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - /** - * - */ - private void cleanup() { - DebugUtils.println("SERVER terminated..."); - releaseServer(); - System.exit(0); - } - - /** - * This method provides the second communication channel with the debugging - * client. The server can send events via this channel to notify the client - * about debug events (see below) asynchonously. - * - * The following events can be fired: - * 1. started -- the vm is started and ready to receive debugging requests - * (guaranteed to be the first event sent) - * 2. terminated -- the vm is terminated (guaranteed to be the last event sent) - * 3. suspended client|step|breakpoint N - * -- the vm is suspended by client, due to a stepping request or - * the breakpoint at line N is hit - * 4. resumed client|step - * -- the vm resumes execution by client or step - * - * @param event - */ - protected void sendEvent(DebugEvent event) { - DebugUtils.println("SERVER sending event: " + event.toString()); - synchronized (eventSocket) { - try { - byte[] data = SerializationHelper.serialize(event); - eventWriter.writeInt(data.length); - eventWriter.write(data); - eventWriter.flush(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - /* (non-Javadoc) - * @see lua.debug.DebugEventListener#notifyDebugEvent(lua.debug.DebugEvent) - */ - public void notifyDebugEvent(DebugEvent event) { - sendEvent(event); - } - - /* (non-Javadoc) - * @see lua.debug.DebugRequestListener#handleRequest(java.lang.String) - */ - public DebugResponse handleRequest(DebugRequest request) { - if (DebugUtils.IS_DEBUG) { - DebugUtils.println("handling request: " + request.toString()); - } - - DebugResponse response = vm.handleRequest(request); - return response; - } -} diff --git a/src/main/java/lua/debug/j2se/DebugSupportImpl.java b/src/main/java/lua/debug/j2se/DebugSupportImpl.java new file mode 100644 index 00000000..283c2a84 --- /dev/null +++ b/src/main/java/lua/debug/j2se/DebugSupportImpl.java @@ -0,0 +1,120 @@ +/******************************************************************************* +* Copyright (c) 2007 LuaJ. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package lua.debug.j2se; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; + +import lua.debug.DebugSupport; +import lua.debug.DebugUtils; +import lua.debug.event.DebugEvent; + +public class DebugSupportImpl extends DebugSupport { + protected ServerSocket requestSocket; + protected Socket clientRequestSocket; + + protected ServerSocket eventSocket; + protected Socket clientEventSocket; + + public DebugSupportImpl(int requestPort, int eventPort) { + super(requestPort, eventPort); + } + + /* (non-Javadoc) + * @see lua.debug.j2se.DebugSupport#start() + */ + public synchronized void start() throws IOException { + this.requestSocket = new ServerSocket(requestPort); + this.clientRequestSocket = requestSocket.accept(); + this.requestReader + = new DataInputStream(clientRequestSocket.getInputStream()); + this.requestWriter + = new DataOutputStream(clientRequestSocket.getOutputStream()); + + this.eventSocket = new ServerSocket(eventPort); + this.clientEventSocket = eventSocket.accept(); + this.eventWriter + = new DataOutputStream(clientEventSocket.getOutputStream()); + + super.start(); + } + + protected void releaseServer() { + super.releaseServer(); + + if (clientRequestSocket != null) { + try { + clientRequestSocket.close(); + } catch (IOException e) {} + } + + if (requestSocket != null) { + try { + requestSocket.close(); + } catch (IOException e) {} + } + + if (clientEventSocket != null) { + try { + clientEventSocket.close(); + } catch (IOException e) {} + } + + if (eventSocket != null){ + try { + eventSocket.close(); + } catch (IOException e) {} + } + } + + protected void handleRequest() { + synchronized (clientRequestSocket) { + super.handleRequest(); + } + } + + /** + * This method provides the second communication channel with the debugging + * client. The server can send events via this channel to notify the client + * about debug events (see below) asynchronously. + * + * The following events can be fired: + * 1. started -- the vm is started and ready to receive debugging requests + * (guaranteed to be the first event sent) + * 2. terminated -- the vm is terminated (guaranteed to be the last event sent) + * 3. suspended client|step|breakpoint N + * -- the vm is suspended by client, due to a stepping request or + * the breakpoint at line N is hit + * 4. resumed client|step + * -- the vm resumes execution by client or step + * + * @param event + */ + protected void sendEvent(DebugEvent event) { + synchronized (eventSocket) { + super.sendEvent(event); + } + } +} diff --git a/src/main/java/lua/debug/j2se/StandardLuaJVM.java b/src/main/java/lua/debug/j2se/StandardLuaJVM.java index cfc0904c..9e5cea61 100644 --- a/src/main/java/lua/debug/j2se/StandardLuaJVM.java +++ b/src/main/java/lua/debug/j2se/StandardLuaJVM.java @@ -25,24 +25,14 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.util.Timer; -import java.util.TimerTask; import lua.GlobalState; import lua.StackState; import lua.addon.luacompat.LuaCompat; import lua.addon.luajava.LuaJava; import lua.debug.DebugStackState; +import lua.debug.DebugSupport; import lua.debug.DebugUtils; -import lua.debug.event.DebugEvent; -import lua.debug.event.DebugEventType; -import lua.debug.request.DebugRequest; -import lua.debug.request.DebugRequestListener; -import lua.debug.request.DebugRequestType; -import lua.debug.response.DebugResponse; -import lua.debug.response.DebugResponseSimple; import lua.io.Closure; import lua.io.LoadState; import lua.io.Proto; @@ -55,9 +45,8 @@ import lua.value.LValue; * @author: Shu Lei * @version: 1.0 */ -public class StandardLuaJVM implements DebugRequestListener { +public class StandardLuaJVM { protected boolean isDebugMode = false; - protected DebugSupport debugSupport; protected int requestPort; protected int eventPort; protected String script; @@ -125,19 +114,15 @@ public class StandardLuaJVM implements DebugRequestListener { throw new ParseException("script is missing."); } - try { - this.script = URLDecoder.decode(args[0], "UTF-8"); - DebugUtils.println("Lua script to run: " + this.script); - int scriptArgsLength = args.length - 1; - if (scriptArgsLength > 0) { - this.scriptArgs = new String[scriptArgsLength]; - for (int i = 1; i < args.length; i++) { - this.scriptArgs[i - 1] = URLDecoder.decode(args[i], "UTF-8"); - } - } - } catch (UnsupportedEncodingException e) { - throw new ParseException("Malformed program argument strings: " + e.getMessage()); - } + this.script = args[0]; + DebugUtils.println("Lua script to run: " + this.script); + int scriptArgsLength = args.length - 1; + if (scriptArgsLength > 0) { + this.scriptArgs = new String[scriptArgsLength]; + for (int i = 1; i < args.length; i++) { + this.scriptArgs[i - 1] = args[i]; + } + } } // end of command line parsing utilities @@ -184,7 +169,7 @@ public class StandardLuaJVM implements DebugRequestListener { LuaCompat.install(); } - public void doRun() throws IOException { + protected void doRun() throws IOException { init(); // new lua state @@ -208,10 +193,13 @@ public class StandardLuaJVM implements DebugRequestListener { state.doCall(c, vargs); } - private void doDebug() throws IOException { + protected void doDebug() throws IOException { DebugUtils.println("setting up LuaJava and debug stack state..."); init(); + // new lua debug state + state = new DebugStackState(); + // load the Lua file DebugUtils.println("loading Lua script '" + getScript() + "'"); InputStream is = new FileInputStream(new File(getScript())); @@ -219,80 +207,26 @@ public class StandardLuaJVM implements DebugRequestListener { // set up debug support if the file is successfully loaded DebugUtils.println("start debugging..."); - this.debugSupport = new DebugSupport(this, getRequestPort(), getEventPort()); - DebugUtils.println("created client request socket connection..."); - debugSupport.start(); - - // new lua debug state - state = new DebugStackState(); - getDebugState().addDebugEventListener(debugSupport); + DebugSupport debugSupport + = new DebugSupportImpl(getRequestPort(), getEventPort()); + getDebugState().setDebugSupport(debugSupport); getDebugState().suspend(); // create closure and execute final Closure c = new Closure(state, p); - - new Thread(new Runnable() { - public void run() { - 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]); - } - - getDebugState().doCall(c, vargs); - stop(); - } - }).start(); - - debugSupport.notifyDebugEvent(new DebugEvent(DebugEventType.started)); + 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]); + } + getDebugState().doCall(c, vargs); + getDebugState().stop(); } private DebugStackState getDebugState() { return (DebugStackState)state; } - - /* (non-Javadoc) - * @see lua.debug.DebugRequestListener#handleRequest(java.lang.String) - */ - public DebugResponse handleRequest(DebugRequest request) { - if (!isDebug()) { - throw new UnsupportedOperationException("Must be in debug mode to handle the debug requests"); - } - - DebugRequestType requestType = request.getType(); - if (DebugRequestType.suspend == requestType) { - DebugResponse status = getDebugState().handleRequest(request); - DebugEvent event = new DebugEvent(DebugEventType.suspendedByClient); - debugSupport.notifyDebugEvent(event); - return status; - } else if (DebugRequestType.resume == requestType) { - DebugResponse status = getDebugState().handleRequest(request); - DebugEvent event = new DebugEvent(DebugEventType.resumedByClient); - debugSupport.notifyDebugEvent(event); - return status; - } else if (DebugRequestType.exit == requestType) { - stop(); - return DebugResponseSimple.SUCCESS; - } else { - return getDebugState().handleRequest(request); - } - } - - protected void stop() { - if (this.debugSupport != null) { - DebugEvent event = new DebugEvent(DebugEventType.terminated); - debugSupport.notifyDebugEvent(event); - Timer timer = new Timer("DebugServerDeathThread"); - timer.schedule(new TimerTask() { - public void run() { - debugSupport.stop(); - debugSupport = null; - } - }, 300); - } - getDebugState().exit(); - } /** * Parses the command line arguments and executes/debugs the lua program. diff --git a/src/main/java/lua/debug/request/DebugRequestType.java b/src/main/java/lua/debug/request/DebugRequestType.java index f6ad5e62..49980ee1 100644 --- a/src/main/java/lua/debug/request/DebugRequestType.java +++ b/src/main/java/lua/debug/request/DebugRequestType.java @@ -38,7 +38,8 @@ public class DebugRequestType extends EnumType { public static final DebugRequestType callgraph = new DebugRequestType("callgraph", 7); public static final DebugRequestType stack = new DebugRequestType("stack", 8); public static final DebugRequestType step = new DebugRequestType("step", 9); - + public static final DebugRequestType start = new DebugRequestType("start", 10); + protected static final DebugRequestType[] ENUMS = new DebugRequestType[] { suspend, resume, @@ -49,7 +50,8 @@ public class DebugRequestType extends EnumType { watchpointClear, callgraph, stack, - step + step, + start }; public DebugRequestType(String name, int ordinal) {