diff --git a/src/debug/org/luaj/debug/DebugLuaState.java b/src/debug/org/luaj/debug/DebugLuaState.java index 50338b3c..d219cfa1 100644 --- a/src/debug/org/luaj/debug/DebugLuaState.java +++ b/src/debug/org/luaj/debug/DebugLuaState.java @@ -91,10 +91,10 @@ public class DebugLuaState extends LuaState implements DebugRequestListener { if (debugSupport != null) setDebugSupport(debugSupport); else - System.out.println("Warning: DebugNetSupportBase is not implemented."); + System.out.println("Warning: DebugNetSupport is missing. Cannot communicate with a debugging client."); } 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()); + System.out.println("Warning: cannot communicate with a debugging client due to error: " + e.getMessage()); } } diff --git a/src/debug/org/luaj/debug/DebugMessageType.java b/src/debug/org/luaj/debug/DebugMessageType.java index 0b3a7287..3abadd0a 100644 --- a/src/debug/org/luaj/debug/DebugMessageType.java +++ b/src/debug/org/luaj/debug/DebugMessageType.java @@ -66,6 +66,7 @@ public class DebugMessageType extends EnumType { public static final DebugMessageType disconnected = new DebugMessageType("disconnected", 34); public static final DebugMessageType sessionId = new DebugMessageType("sessionId", 35); public static final DebugMessageType debugServiceDown = new DebugMessageType("debugServiceDown", 36); + public static final DebugMessageType outputRedirect = new DebugMessageType("outputRedirect", 37); protected static DebugMessageType[] ENUMS = new DebugMessageType[] { start, @@ -104,7 +105,8 @@ public class DebugMessageType extends EnumType { clientRequestGlobalReply, disconnected, sessionId, - debugServiceDown + debugServiceDown, + outputRedirect }; protected DebugMessageType(String name, int ordinal) { diff --git a/src/debug/org/luaj/debug/RedirectOutputStream.java b/src/debug/org/luaj/debug/RedirectOutputStream.java new file mode 100644 index 00000000..7f104ab5 --- /dev/null +++ b/src/debug/org/luaj/debug/RedirectOutputStream.java @@ -0,0 +1,57 @@ +package org.luaj.debug; + +import java.io.IOException; +import java.io.OutputStream; + +import org.luaj.debug.event.DebugEventListener; +import org.luaj.debug.event.DebugEventOutputRedirect; + +public class RedirectOutputStream extends OutputStream { + + protected DebugEventListener listener; + protected int count; + protected byte[] buffer; + + public RedirectOutputStream(DebugEventListener listener) { + this(listener, 1024); + } + + public RedirectOutputStream(DebugEventListener listener, int count) { + this.listener = listener; + this.count = 0; + this.buffer = new byte[count]; + } + + public void close() throws IOException { + flushBuffer(); + } + + public synchronized void flush() throws IOException { + flushBuffer(); + } + + public void write(byte[] b, int off, int len) throws IOException { + super.write(b, off, len); + flushBuffer(); + } + + public void write(byte[] b) throws IOException { + super.write(b); + flushBuffer(); + } + + public synchronized void write(int b) throws IOException { + if (count >= buffer.length) { + flushBuffer(); + } + buffer[count++] = (byte)b; + } + + protected synchronized void flushBuffer(){ + if (count > 0) { + String msg = new String(buffer, 0, this.count); + listener.notifyDebugEvent(new DebugEventOutputRedirect(msg)); + count = 0; + } + } +} diff --git a/src/debug/org/luaj/debug/SerializationHelper.java b/src/debug/org/luaj/debug/SerializationHelper.java index 8cb80144..93e7cf64 100644 --- a/src/debug/org/luaj/debug/SerializationHelper.java +++ b/src/debug/org/luaj/debug/SerializationHelper.java @@ -8,6 +8,7 @@ import java.io.IOException; import org.luaj.debug.event.DebugEventBreakpoint; import org.luaj.debug.event.DebugEventError; +import org.luaj.debug.event.DebugEventOutputRedirect; import org.luaj.debug.request.DebugRequestDisconnect; import org.luaj.debug.request.DebugRequestLineBreakpointToggle; import org.luaj.debug.request.DebugRequestStack; @@ -59,6 +60,7 @@ public class SerializationHelper { static final int SERIAL_TYPE_DebugResponseVariables = 12; static final int SERIAL_TYPE_DebugResponseStack = 13; static final int SERIAL_TYPE_DebugResponseSession = 14; + static final int SERIAL_TYPE_DebugEventOutputRedirect = 15; public static void serialize(Serializable object, DataOutputStream dout) throws IOException { @@ -93,6 +95,9 @@ public class SerializationHelper { } else if (object instanceof DebugEventError) { dout.writeInt(SERIAL_TYPE_DebugEventError); DebugEventError.serialize(dout, (DebugEventError) object); + } else if (object instanceof DebugEventOutputRedirect) { + dout.writeInt(SERIAL_TYPE_DebugEventOutputRedirect); + DebugEventOutputRedirect.serialize(dout, (DebugEventOutputRedirect) object); } else if (object instanceof DebugResponseStack) { dout.writeInt(SERIAL_TYPE_DebugResponseStack); DebugResponseStack.serialize(dout, (DebugResponseStack) object); @@ -152,6 +157,9 @@ public class SerializationHelper { case SERIAL_TYPE_DebugEventError: object = DebugEventError.deserialize(din); break; + case SERIAL_TYPE_DebugEventOutputRedirect: + object = DebugEventOutputRedirect.deserialize(din); + break; case SERIAL_TYPE_DebugResponseCallgraph: object = DebugResponseCallgraph.deserialize(din); break; diff --git a/src/debug/org/luaj/debug/event/DebugEventOutputRedirect.java b/src/debug/org/luaj/debug/event/DebugEventOutputRedirect.java new file mode 100644 index 00000000..5a18557c --- /dev/null +++ b/src/debug/org/luaj/debug/event/DebugEventOutputRedirect.java @@ -0,0 +1,40 @@ +package org.luaj.debug.event; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.luaj.debug.DebugMessage; +import org.luaj.debug.DebugMessageType; + +public class DebugEventOutputRedirect extends DebugMessage { + protected String data; + + public DebugEventOutputRedirect(String data) { + super(DebugMessageType.outputRedirect); + this.data = data; + } + + public String getOutput() { + return this.data; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + public String toString() { + return type.toString() + ": " + this.data; + } + + public static void serialize(DataOutputStream out, DebugEventOutputRedirect event) + throws IOException { + out.writeUTF(event.getOutput()); + } + + public static DebugMessage deserialize(DataInputStream in) throws IOException { + String data = in.readUTF(); + return new DebugEventOutputRedirect(data); + } +} diff --git a/src/debug/org/luaj/debug/j2se/J2sePlatform.java b/src/debug/org/luaj/debug/j2se/J2sePlatform.java new file mode 100644 index 00000000..f2ece929 --- /dev/null +++ b/src/debug/org/luaj/debug/j2se/J2sePlatform.java @@ -0,0 +1,37 @@ +package org.luaj.debug.j2se; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; + +import org.luaj.debug.net.j2se.DebugSupportImpl; +import org.luaj.vm.DebugNetSupport; +import org.luaj.vm.Platform; + +public class J2sePlatform extends 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) { + return System.getProperty(propertyName); + } + + /** + * Provides a J2SE DebugSupport instance. + */ + public DebugNetSupport getDebugSupport() throws IOException { + int port = getDebugPort(); + DebugSupportImpl debugSupport = new DebugSupportImpl(port); + return debugSupport; + } +} diff --git a/src/debug/org/luaj/debug/j2se/StandardLuaJVM.java b/src/debug/org/luaj/debug/j2se/StandardLuaJVM.java index 6736f96c..0f70ee6a 100644 --- a/src/debug/org/luaj/debug/j2se/StandardLuaJVM.java +++ b/src/debug/org/luaj/debug/j2se/StandardLuaJVM.java @@ -36,6 +36,7 @@ import org.luaj.vm.LValue; import org.luaj.vm.LoadState; import org.luaj.vm.LuaErrorException; import org.luaj.vm.LuaState; +import org.luaj.vm.Platform; /** * StandardLuaJVM executes a lua program in normal run mode or debug mode. @@ -170,7 +171,9 @@ public class StandardLuaJVM { public void run() { try { - // new lua debug state + // new lua debug state + Platform.setInstance(new J2sePlatform()); + state = LuaState.newState(); init(state); diff --git a/src/debug/org/luaj/debug/net/j2me/ClientConnectionTask.java b/src/debug/org/luaj/debug/net/j2me/ClientConnectionTask.java index bdb34e18..ad266c27 100644 --- a/src/debug/org/luaj/debug/net/j2me/ClientConnectionTask.java +++ b/src/debug/org/luaj/debug/net/j2me/ClientConnectionTask.java @@ -10,9 +10,11 @@ import javax.microedition.io.SocketConnection; import org.luaj.debug.DebugMessage; import org.luaj.debug.DebugMessageType; +import org.luaj.debug.RedirectOutputStream; import org.luaj.debug.SerializationHelper; import org.luaj.debug.event.DebugEventListener; import org.luaj.debug.response.DebugResponseSession; +import org.luaj.lib.BaseLib; public class ClientConnectionTask implements Runnable, DebugEventListener { private static final boolean TRACE = true; //(null != System.getProperty("TRACE")); @@ -26,6 +28,8 @@ public class ClientConnectionTask implements Runnable, DebugEventListener { protected DataInputStream inStream; protected DataOutputStream outStream; + protected RedirectOutputStream redirectOutputStream; + public ClientConnectionTask(String host, int port, DebugSupportImpl debugSupport) { this.host = host; this.port = port; @@ -59,6 +63,10 @@ public class ClientConnectionTask implements Runnable, DebugEventListener { if ( TRACE ) System.out.println("LuaJ debug server connected to " + host + ":" + port); + // to redirect the print to the debug client console + this.redirectOutputStream = new RedirectOutputStream(this); + BaseLib.redirectOutput( redirectOutputStream ); + // loop for incoming requests while ( !isDisconnected() ) { byte[] data = null; @@ -85,6 +93,13 @@ public class ClientConnectionTask implements Runnable, DebugEventListener { debugSupport.disconnect(1); } finally { + try { + redirectOutputStream.close(); + } catch (IOException ignore) {} + + // restore the print output + BaseLib.restoreStandardOutput(); + dispose(); } } diff --git a/src/debug/org/luaj/debug/net/j2se/ClientConnectionTask.java b/src/debug/org/luaj/debug/net/j2se/ClientConnectionTask.java index ed1761e3..194128b7 100644 --- a/src/debug/org/luaj/debug/net/j2se/ClientConnectionTask.java +++ b/src/debug/org/luaj/debug/net/j2se/ClientConnectionTask.java @@ -8,9 +8,11 @@ import java.net.Socket; import org.luaj.debug.DebugMessage; import org.luaj.debug.DebugMessageType; +import org.luaj.debug.RedirectOutputStream; import org.luaj.debug.SerializationHelper; import org.luaj.debug.event.DebugEventListener; import org.luaj.debug.response.DebugResponseSession; +import org.luaj.lib.BaseLib; public class ClientConnectionTask implements Runnable, DebugEventListener { private static final boolean TRACE = (null != System.getProperty("TRACE")); @@ -23,6 +25,7 @@ public class ClientConnectionTask implements Runnable, DebugEventListener { protected DataOutputStream eventWriter; protected DebugSupportImpl debugSupport; protected boolean isDisposed = false; + protected RedirectOutputStream redirectOutputStream; public ClientConnectionTask(DebugSupportImpl debugSupport, Socket socket) throws IOException { @@ -60,6 +63,10 @@ public class ClientConnectionTask implements Runnable, DebugEventListener { public void run() { try { + // to redirect the print to the debug client console + this.redirectOutputStream = new RedirectOutputStream(this); + BaseLib.redirectOutput( redirectOutputStream ); + // loop for incoming requests while (!isDisconnected()) { byte[] data = null; @@ -87,6 +94,13 @@ public class ClientConnectionTask implements Runnable, DebugEventListener { debugSupport.disconnect(getSessionId()); } finally { + try { + redirectOutputStream.close(); + } catch (IOException ignore) {} + + // restore the print output + BaseLib.restoreStandardOutput(); + dispose(); } } diff --git a/src/test/java/org/luaj/debug/DebugEventTest.java b/src/test/java/org/luaj/debug/DebugEventTest.java index 3b55d69b..fc4c9ce3 100644 --- a/src/test/java/org/luaj/debug/DebugEventTest.java +++ b/src/test/java/org/luaj/debug/DebugEventTest.java @@ -5,6 +5,7 @@ import java.io.IOException; import junit.framework.TestCase; import org.luaj.debug.event.DebugEventBreakpoint; +import org.luaj.debug.event.DebugEventOutputRedirect; public class DebugEventTest extends TestCase { public void testDebugEventSerialization() { @@ -34,4 +35,17 @@ public class DebugEventTest extends TestCase { fail(e.getMessage()); } } + + public void testDebugEventOutputRedirectSerialization() { + try { + String msg = "This is a testing message"; + DebugEventOutputRedirect redirectEvent = new DebugEventOutputRedirect(msg); + byte[] data = SerializationHelper.serialize(redirectEvent); + DebugEventOutputRedirect redirectEventOut = (DebugEventOutputRedirect) + SerializationHelper.deserialize(data); + assertEquals(msg, redirectEventOut.getOutput()); + } catch (IOException e) { + fail(e.getMessage()); + } + } } diff --git a/src/test/java/org/luaj/debug/RedirectOutputStreamTest.java b/src/test/java/org/luaj/debug/RedirectOutputStreamTest.java new file mode 100644 index 00000000..be279e0b --- /dev/null +++ b/src/test/java/org/luaj/debug/RedirectOutputStreamTest.java @@ -0,0 +1,24 @@ +package org.luaj.debug; + +import java.io.PrintWriter; + +import junit.framework.TestCase; + +import org.luaj.debug.event.DebugEventListener; + +public class RedirectOutputStreamTest extends TestCase { + public void testRedirectOutputStream() { + RedirectOutputStream redirectOut = new RedirectOutputStream( + new DebugEventListener(){ + public void notifyDebugEvent(DebugMessage event) { + assertEquals(event.toString(), "outputRedirect: true\r\na"); + } + + }); + PrintWriter writer = new PrintWriter(redirectOut); + writer.print(true); + writer.println(); + writer.print('a'); + writer.close(); + } +}