move debugging to lib
This commit is contained in:
parent
3c4d05abd4
commit
4352550ae9
@ -1,4 +1,4 @@
|
|||||||
package me.topchetoeu.j2s.repl.debug;
|
package me.topchetoeu.j2s.lib.debug;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UncheckedIOException;
|
import java.io.UncheckedIOException;
|
||||||
@ -15,7 +15,7 @@ import me.topchetoeu.j2s.common.SyntaxException;
|
|||||||
import me.topchetoeu.j2s.compilation.json.JSON;
|
import me.topchetoeu.j2s.compilation.json.JSON;
|
||||||
import me.topchetoeu.j2s.compilation.json.JSONList;
|
import me.topchetoeu.j2s.compilation.json.JSONList;
|
||||||
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
||||||
import me.topchetoeu.j2s.repl.debug.WebSocketMessage.Type;
|
import me.topchetoeu.j2s.lib.debug.WebSocketMessage.Type;
|
||||||
|
|
||||||
public class DebugServer {
|
public class DebugServer {
|
||||||
public static String browserDisplayName = Metadata.name() + "/" + Metadata.version();
|
public static String browserDisplayName = Metadata.name() + "/" + Metadata.version();
|
37
lib/src/main/java/me/topchetoeu/j2s/lib/debug/Debugger.java
Normal file
37
lib/src/main/java/me/topchetoeu/j2s/lib/debug/Debugger.java
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package me.topchetoeu.j2s.lib.debug;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import me.topchetoeu.j2s.runtime.debug.DebugHandler;
|
||||||
|
|
||||||
|
public interface Debugger extends DebugHandler {
|
||||||
|
void close();
|
||||||
|
|
||||||
|
void enable(V8Message msg) throws IOException;
|
||||||
|
void disable(V8Message msg) throws IOException;
|
||||||
|
|
||||||
|
void setBreakpointByUrl(V8Message msg) throws IOException;
|
||||||
|
void removeBreakpoint(V8Message msg) throws IOException;
|
||||||
|
void continueToLocation(V8Message msg) throws IOException;
|
||||||
|
|
||||||
|
void getScriptSource(V8Message msg) throws IOException;
|
||||||
|
void getPossibleBreakpoints(V8Message msg) throws IOException;
|
||||||
|
|
||||||
|
void resume(V8Message msg) throws IOException;
|
||||||
|
void pause(V8Message msg) throws IOException;
|
||||||
|
|
||||||
|
void stepInto(V8Message msg) throws IOException;
|
||||||
|
void stepOut(V8Message msg) throws IOException;
|
||||||
|
void stepOver(V8Message msg) throws IOException;
|
||||||
|
|
||||||
|
void setPauseOnExceptions(V8Message msg) throws IOException;
|
||||||
|
|
||||||
|
void evaluateOnCallFrame(V8Message msg) throws IOException;
|
||||||
|
|
||||||
|
void getProperties(V8Message msg) throws IOException;
|
||||||
|
void releaseObjectGroup(V8Message msg) throws IOException;
|
||||||
|
void releaseObject(V8Message msg) throws IOException;
|
||||||
|
void callFunctionOn(V8Message msg) throws IOException;
|
||||||
|
|
||||||
|
void runtimeEnable(V8Message msg) throws IOException;
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package me.topchetoeu.j2s.lib.debug;
|
||||||
|
|
||||||
|
public interface DebuggerProvider {
|
||||||
|
Debugger getDebugger(WebSocket socket, HttpRequest req);
|
||||||
|
}
|
100
lib/src/main/java/me/topchetoeu/j2s/lib/debug/HttpRequest.java
Normal file
100
lib/src/main/java/me/topchetoeu/j2s/lib/debug/HttpRequest.java
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package me.topchetoeu.j2s.lib.debug;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.IllegalFormatException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import me.topchetoeu.j2s.common.Reading;
|
||||||
|
|
||||||
|
public class HttpRequest {
|
||||||
|
public final String method;
|
||||||
|
public final String path;
|
||||||
|
public final Map<String, String> headers;
|
||||||
|
public final OutputStream out;
|
||||||
|
|
||||||
|
public void writeCode(int code, String name) {
|
||||||
|
try { out.write(("HTTP/1.1 " + code + " " + name + "\r\n").getBytes()); }
|
||||||
|
catch (IOException e) { }
|
||||||
|
}
|
||||||
|
public void writeHeader(String name, String value) {
|
||||||
|
try { out.write((name + ": " + value + "\r\n").getBytes()); }
|
||||||
|
catch (IOException e) { }
|
||||||
|
}
|
||||||
|
public void writeLastHeader(String name, String value) {
|
||||||
|
try { out.write((name + ": " + value + "\r\n\r\n").getBytes()); }
|
||||||
|
catch (IOException e) { }
|
||||||
|
}
|
||||||
|
public void writeHeadersEnd() {
|
||||||
|
try { out.write("\n".getBytes()); }
|
||||||
|
catch (IOException e) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeResponse(int code, String name, String type, byte[] data) {
|
||||||
|
writeCode(code, name);
|
||||||
|
writeHeader("Content-Type", type);
|
||||||
|
writeLastHeader("Content-Length", data.length + "");
|
||||||
|
try {
|
||||||
|
out.write(data);
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
catch (IOException e) { }
|
||||||
|
}
|
||||||
|
public void writeResponse(int code, String name, String type, InputStream data) {
|
||||||
|
writeResponse(code, name, type, Reading.streamToBytes(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpRequest(String method, String path, Map<String, String> headers, OutputStream out) {
|
||||||
|
this.method = method;
|
||||||
|
this.path = path;
|
||||||
|
this.headers = headers;
|
||||||
|
this.out = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We dont need no http library
|
||||||
|
public static HttpRequest read(Socket socket) {
|
||||||
|
try {
|
||||||
|
var str = socket.getInputStream();
|
||||||
|
var lines = new BufferedReader(new InputStreamReader(str));
|
||||||
|
var line = lines.readLine();
|
||||||
|
var i1 = line.indexOf(" ");
|
||||||
|
var i2 = line.indexOf(" ", i1 + 1);
|
||||||
|
|
||||||
|
if (i1 < 0 || i2 < 0) {
|
||||||
|
socket.close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var method = line.substring(0, i1).trim().toUpperCase();
|
||||||
|
var path = line.substring(i1 + 1, i2).trim();
|
||||||
|
var headers = new HashMap<String, String>();
|
||||||
|
|
||||||
|
while (!(line = lines.readLine()).isEmpty()) {
|
||||||
|
var i = line.indexOf(":");
|
||||||
|
if (i < 0) continue;
|
||||||
|
var name = line.substring(0, i).trim().toLowerCase();
|
||||||
|
var value = line.substring(i + 1).trim();
|
||||||
|
|
||||||
|
if (name.length() == 0) continue;
|
||||||
|
headers.put(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headers.containsKey("content-length")) {
|
||||||
|
try {
|
||||||
|
var i = Integer.parseInt(headers.get("content-length"));
|
||||||
|
str.skip(i);
|
||||||
|
}
|
||||||
|
catch (IllegalFormatException e) { /* ¯\_(ツ)_/¯ */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HttpRequest(method, path, headers, socket.getOutputStream());
|
||||||
|
}
|
||||||
|
catch (IOException | NullPointerException e) { return null; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package me.topchetoeu.j2s.repl.debug;
|
package me.topchetoeu.j2s.lib.debug;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
@ -1,4 +1,4 @@
|
|||||||
package me.topchetoeu.j2s.repl.debug;
|
package me.topchetoeu.j2s.lib.debug;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
@ -69,14 +69,8 @@ public class SimpleDebugHandler implements DebugHandler {
|
|||||||
if (debugger != null) debugger.onFunctionLoad(func, map);
|
if (debugger != null) debugger.onFunctionLoad(func, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SimpleDebugHandler(boolean enabled) {
|
|
||||||
if (enabled) {
|
|
||||||
sources = new HashMap<>();
|
|
||||||
maps = new WeakHashMap<>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public SimpleDebugHandler() {
|
public SimpleDebugHandler() {
|
||||||
this(true);
|
sources = new HashMap<>();
|
||||||
|
maps = new WeakHashMap<>();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package me.topchetoeu.j2s.repl.debug;
|
package me.topchetoeu.j2s.lib.debug;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
@ -1,4 +1,4 @@
|
|||||||
package me.topchetoeu.j2s.repl.debug;
|
package me.topchetoeu.j2s.lib.debug;
|
||||||
|
|
||||||
import me.topchetoeu.j2s.common.Environment;
|
import me.topchetoeu.j2s.common.Environment;
|
||||||
import me.topchetoeu.j2s.runtime.Frame;
|
import me.topchetoeu.j2s.runtime.Frame;
|
19
lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Error.java
Normal file
19
lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Error.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package me.topchetoeu.j2s.lib.debug;
|
||||||
|
|
||||||
|
import me.topchetoeu.j2s.compilation.json.JSON;
|
||||||
|
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
||||||
|
|
||||||
|
public class V8Error {
|
||||||
|
public final String message;
|
||||||
|
|
||||||
|
public V8Error(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return JSON.stringify(new JSONMap().set("error", new JSONMap()
|
||||||
|
.set("message", message)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
22
lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Event.java
Normal file
22
lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Event.java
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package me.topchetoeu.j2s.lib.debug;
|
||||||
|
|
||||||
|
import me.topchetoeu.j2s.compilation.json.JSON;
|
||||||
|
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
||||||
|
|
||||||
|
public class V8Event {
|
||||||
|
public final String name;
|
||||||
|
public final JSONMap params;
|
||||||
|
|
||||||
|
public V8Event(String name, JSONMap params) {
|
||||||
|
this.name = name;
|
||||||
|
this.params = params;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return JSON.stringify(new JSONMap()
|
||||||
|
.set("method", name)
|
||||||
|
.set("params", params)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
50
lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Message.java
Normal file
50
lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Message.java
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package me.topchetoeu.j2s.lib.debug;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import me.topchetoeu.j2s.compilation.json.JSON;
|
||||||
|
import me.topchetoeu.j2s.compilation.json.JSONElement;
|
||||||
|
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
||||||
|
|
||||||
|
public class V8Message {
|
||||||
|
public final String name;
|
||||||
|
public final int id;
|
||||||
|
public final JSONMap params;
|
||||||
|
|
||||||
|
public V8Message(String name, int id, Map<String, JSONElement> params) {
|
||||||
|
this.name = name;
|
||||||
|
this.params = new JSONMap(params);
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
public V8Result respond(JSONMap result) {
|
||||||
|
return new V8Result(id, result);
|
||||||
|
}
|
||||||
|
public V8Result respond() {
|
||||||
|
return new V8Result(id, new JSONMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
public V8Message(JSONMap raw) {
|
||||||
|
if (!raw.isNumber("id")) throw new IllegalArgumentException("Expected number property 'id'.");
|
||||||
|
if (!raw.isString("method")) throw new IllegalArgumentException("Expected string property 'method'.");
|
||||||
|
|
||||||
|
this.name = raw.string("method");
|
||||||
|
this.id = (int)raw.number("id");
|
||||||
|
this.params = raw.contains("params") ? raw.map("params") : new JSONMap();
|
||||||
|
}
|
||||||
|
public V8Message(String raw) {
|
||||||
|
this(JSON.parse(null, raw).map());
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSONMap toMap() {
|
||||||
|
var res = new JSONMap();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return JSON.stringify(new JSONMap()
|
||||||
|
.set("method", name)
|
||||||
|
.set("params", params)
|
||||||
|
.set("id", id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
22
lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Result.java
Normal file
22
lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Result.java
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package me.topchetoeu.j2s.lib.debug;
|
||||||
|
|
||||||
|
import me.topchetoeu.j2s.compilation.json.JSON;
|
||||||
|
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
||||||
|
|
||||||
|
public class V8Result {
|
||||||
|
public final int id;
|
||||||
|
public final JSONMap result;
|
||||||
|
|
||||||
|
public V8Result(int id, JSONMap result) {
|
||||||
|
this.id = id;
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return JSON.stringify(new JSONMap()
|
||||||
|
.set("id", id)
|
||||||
|
.set("result", result)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
186
lib/src/main/java/me/topchetoeu/j2s/lib/debug/WebSocket.java
Normal file
186
lib/src/main/java/me/topchetoeu/j2s/lib/debug/WebSocket.java
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
package me.topchetoeu.j2s.lib.debug;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
import me.topchetoeu.j2s.lib.debug.WebSocketMessage.Type;
|
||||||
|
|
||||||
|
public class WebSocket implements AutoCloseable {
|
||||||
|
public long maxLength = 1 << 20;
|
||||||
|
|
||||||
|
private Socket socket;
|
||||||
|
private boolean closed = false;
|
||||||
|
|
||||||
|
private OutputStream out() throws IOException {
|
||||||
|
return socket.getOutputStream();
|
||||||
|
}
|
||||||
|
private InputStream in() throws IOException {
|
||||||
|
return socket.getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
private long readLen(int byteLen) throws IOException {
|
||||||
|
long res = 0;
|
||||||
|
|
||||||
|
if (byteLen == 126) {
|
||||||
|
res |= in().read() << 8;
|
||||||
|
res |= in().read();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
else if (byteLen == 127) {
|
||||||
|
res |= in().read() << 56;
|
||||||
|
res |= in().read() << 48;
|
||||||
|
res |= in().read() << 40;
|
||||||
|
res |= in().read() << 32;
|
||||||
|
res |= in().read() << 24;
|
||||||
|
res |= in().read() << 16;
|
||||||
|
res |= in().read() << 8;
|
||||||
|
res |= in().read();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
else return byteLen;
|
||||||
|
}
|
||||||
|
private byte[] readMask(boolean has) throws IOException {
|
||||||
|
if (has) {
|
||||||
|
return new byte[] {
|
||||||
|
(byte)in().read(),
|
||||||
|
(byte)in().read(),
|
||||||
|
(byte)in().read(),
|
||||||
|
(byte)in().read()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else return new byte[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeLength(int len) throws IOException {
|
||||||
|
if (len < 126) {
|
||||||
|
out().write((int)len);
|
||||||
|
}
|
||||||
|
else if (len <= 0xFFFF) {
|
||||||
|
out().write(126);
|
||||||
|
out().write((int)(len >> 8) & 0xFF);
|
||||||
|
out().write((int)len & 0xFF);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
out().write(127);
|
||||||
|
out().write(0);
|
||||||
|
out().write(0);
|
||||||
|
out().write(0);
|
||||||
|
out().write(0);
|
||||||
|
out().write((len >> 24) & 0xFF);
|
||||||
|
out().write((len >> 16) & 0xFF);
|
||||||
|
out().write((len >> 8) & 0xFF);
|
||||||
|
out().write(len & 0xFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private synchronized void write(int type, byte[] data) throws IOException {
|
||||||
|
out().write(type | 0x80);
|
||||||
|
writeLength(data.length);
|
||||||
|
out().write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(String data) throws IOException {
|
||||||
|
if (closed) throw new IllegalStateException("Websocket is closed.");
|
||||||
|
write(1, data.getBytes());
|
||||||
|
}
|
||||||
|
public void send(byte[] data) throws IOException {
|
||||||
|
if (closed) throw new IllegalStateException("Websocket is closed.");
|
||||||
|
write(2, data);
|
||||||
|
}
|
||||||
|
public void send(WebSocketMessage msg) throws IOException {
|
||||||
|
if (msg.type == Type.Binary) send(msg.binaryData());
|
||||||
|
else send(msg.textData());
|
||||||
|
}
|
||||||
|
public void send(Object data) throws IOException {
|
||||||
|
if (closed) throw new IllegalStateException("Websocket is closed.");
|
||||||
|
write(1, data.toString().getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close(String reason) {
|
||||||
|
if (socket != null) {
|
||||||
|
try {
|
||||||
|
write(8, reason.getBytes());
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
catch (Throwable e) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
socket = null;
|
||||||
|
closed = true;
|
||||||
|
}
|
||||||
|
public void close() {
|
||||||
|
close("");
|
||||||
|
}
|
||||||
|
|
||||||
|
private WebSocketMessage fail(String reason) {
|
||||||
|
System.out.println("WebSocket Error: " + reason);
|
||||||
|
close(reason);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] readData() throws IOException {
|
||||||
|
var maskLen = in().read();
|
||||||
|
var hasMask = (maskLen & 0x80) != 0;
|
||||||
|
var len = (int)readLen(maskLen & 0x7F);
|
||||||
|
var mask = readMask(hasMask);
|
||||||
|
|
||||||
|
if (len > maxLength) fail("WebSocket Error: client exceeded configured max message size");
|
||||||
|
else {
|
||||||
|
var buff = new byte[len];
|
||||||
|
|
||||||
|
if (in().read(buff) < len) fail("WebSocket Error: payload too short");
|
||||||
|
else {
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
buff[i] ^= mask[(int)(i % 4)];
|
||||||
|
}
|
||||||
|
return buff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebSocketMessage receive() throws IOException {
|
||||||
|
var data = new ByteArrayOutputStream();
|
||||||
|
var type = 0;
|
||||||
|
|
||||||
|
while (socket != null && !closed) {
|
||||||
|
var finId = in().read();
|
||||||
|
if (finId < 0) break;
|
||||||
|
var fin = (finId & 0x80) != 0;
|
||||||
|
int id = finId & 0x0F;
|
||||||
|
|
||||||
|
if (id == 0x8) { close(); return null; }
|
||||||
|
if (id >= 0x8) {
|
||||||
|
if (!fin) return fail("WebSocket Error: client-sent control frame was fragmented");
|
||||||
|
if (id == 0x9) write(0xA, data.toByteArray());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == 0) type = id;
|
||||||
|
if (type == 0) return fail("WebSocket Error: client used opcode 0x00 for first fragment");
|
||||||
|
|
||||||
|
var buff = readData();
|
||||||
|
if (buff == null) break;
|
||||||
|
|
||||||
|
if (data.size() + buff.length > maxLength) return fail("WebSocket Error: client exceeded configured max message size");
|
||||||
|
data.write(buff);
|
||||||
|
|
||||||
|
if (!fin) continue;
|
||||||
|
var raw = data.toByteArray();
|
||||||
|
|
||||||
|
if (type == 1) {
|
||||||
|
return new WebSocketMessage(new String(raw));
|
||||||
|
}
|
||||||
|
else return new WebSocketMessage(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebSocket(Socket socket) {
|
||||||
|
this.socket = socket;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package me.topchetoeu.j2s.lib.debug;
|
||||||
|
|
||||||
|
public class WebSocketMessage {
|
||||||
|
public static enum Type {
|
||||||
|
Text,
|
||||||
|
Binary,
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Type type;
|
||||||
|
private final Object data;
|
||||||
|
|
||||||
|
public final String textData() {
|
||||||
|
if (type != Type.Text) throw new IllegalStateException("Message is not text.");
|
||||||
|
return (String)data;
|
||||||
|
}
|
||||||
|
public final byte[] binaryData() {
|
||||||
|
if (type != Type.Binary) throw new IllegalStateException("Message is not binary.");
|
||||||
|
return (byte[])data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebSocketMessage(String data) {
|
||||||
|
this.type = Type.Text;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
public WebSocketMessage(byte[] data) {
|
||||||
|
this.type = Type.Binary;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
}
|
@ -1,19 +0,0 @@
|
|||||||
package me.topchetoeu.j2s.repl;
|
|
||||||
|
|
||||||
import me.topchetoeu.j2s.compilation.json.JSON;
|
|
||||||
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
|
||||||
|
|
||||||
public class V8Error {
|
|
||||||
public final String message;
|
|
||||||
|
|
||||||
public V8Error(String message) {
|
|
||||||
this.message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return JSON.stringify(new JSONMap().set("error", new JSONMap()
|
|
||||||
.set("message", message)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
package me.topchetoeu.j2s.repl.debug;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import me.topchetoeu.j2s.runtime.debug.DebugHandler;
|
|
||||||
|
|
||||||
public interface Debugger extends DebugHandler {
|
|
||||||
void close();
|
|
||||||
|
|
||||||
void enable(V8Message msg) throws IOException;
|
|
||||||
void disable(V8Message msg) throws IOException;
|
|
||||||
|
|
||||||
void setBreakpointByUrl(V8Message msg) throws IOException;
|
|
||||||
void removeBreakpoint(V8Message msg) throws IOException;
|
|
||||||
void continueToLocation(V8Message msg) throws IOException;
|
|
||||||
|
|
||||||
void getScriptSource(V8Message msg) throws IOException;
|
|
||||||
void getPossibleBreakpoints(V8Message msg) throws IOException;
|
|
||||||
|
|
||||||
void resume(V8Message msg) throws IOException;
|
|
||||||
void pause(V8Message msg) throws IOException;
|
|
||||||
|
|
||||||
void stepInto(V8Message msg) throws IOException;
|
|
||||||
void stepOut(V8Message msg) throws IOException;
|
|
||||||
void stepOver(V8Message msg) throws IOException;
|
|
||||||
|
|
||||||
void setPauseOnExceptions(V8Message msg) throws IOException;
|
|
||||||
|
|
||||||
void evaluateOnCallFrame(V8Message msg) throws IOException;
|
|
||||||
|
|
||||||
void getProperties(V8Message msg) throws IOException;
|
|
||||||
void releaseObjectGroup(V8Message msg) throws IOException;
|
|
||||||
void releaseObject(V8Message msg) throws IOException;
|
|
||||||
void callFunctionOn(V8Message msg) throws IOException;
|
|
||||||
|
|
||||||
void runtimeEnable(V8Message msg) throws IOException;
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package me.topchetoeu.j2s.repl.debug;
|
|
||||||
|
|
||||||
public interface DebuggerProvider {
|
|
||||||
Debugger getDebugger(WebSocket socket, HttpRequest req);
|
|
||||||
}
|
|
@ -1,101 +0,0 @@
|
|||||||
package me.topchetoeu.j2s.repl.debug;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.IllegalFormatException;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import me.topchetoeu.j2s.common.Reading;
|
|
||||||
|
|
||||||
public class HttpRequest {
|
|
||||||
public final String method;
|
|
||||||
public final String path;
|
|
||||||
public final Map<String, String> headers;
|
|
||||||
public final OutputStream out;
|
|
||||||
|
|
||||||
|
|
||||||
public void writeCode(int code, String name) {
|
|
||||||
try { out.write(("HTTP/1.1 " + code + " " + name + "\r\n").getBytes()); }
|
|
||||||
catch (IOException e) { }
|
|
||||||
}
|
|
||||||
public void writeHeader(String name, String value) {
|
|
||||||
try { out.write((name + ": " + value + "\r\n").getBytes()); }
|
|
||||||
catch (IOException e) { }
|
|
||||||
}
|
|
||||||
public void writeLastHeader(String name, String value) {
|
|
||||||
try { out.write((name + ": " + value + "\r\n\r\n").getBytes()); }
|
|
||||||
catch (IOException e) { }
|
|
||||||
}
|
|
||||||
public void writeHeadersEnd() {
|
|
||||||
try { out.write("\n".getBytes()); }
|
|
||||||
catch (IOException e) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeResponse(int code, String name, String type, byte[] data) {
|
|
||||||
writeCode(code, name);
|
|
||||||
writeHeader("Content-Type", type);
|
|
||||||
writeLastHeader("Content-Length", data.length + "");
|
|
||||||
try {
|
|
||||||
out.write(data);
|
|
||||||
out.close();
|
|
||||||
}
|
|
||||||
catch (IOException e) { }
|
|
||||||
}
|
|
||||||
public void writeResponse(int code, String name, String type, InputStream data) {
|
|
||||||
writeResponse(code, name, type, Reading.streamToBytes(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpRequest(String method, String path, Map<String, String> headers, OutputStream out) {
|
|
||||||
this.method = method;
|
|
||||||
this.path = path;
|
|
||||||
this.headers = headers;
|
|
||||||
this.out = out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We dont need no http library
|
|
||||||
public static HttpRequest read(Socket socket) {
|
|
||||||
try {
|
|
||||||
var str = socket.getInputStream();
|
|
||||||
var lines = new BufferedReader(new InputStreamReader(str));
|
|
||||||
var line = lines.readLine();
|
|
||||||
var i1 = line.indexOf(" ");
|
|
||||||
var i2 = line.indexOf(" ", i1 + 1);
|
|
||||||
|
|
||||||
if (i1 < 0 || i2 < 0) {
|
|
||||||
socket.close();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var method = line.substring(0, i1).trim().toUpperCase();
|
|
||||||
var path = line.substring(i1 + 1, i2).trim();
|
|
||||||
var headers = new HashMap<String, String>();
|
|
||||||
|
|
||||||
while (!(line = lines.readLine()).isEmpty()) {
|
|
||||||
var i = line.indexOf(":");
|
|
||||||
if (i < 0) continue;
|
|
||||||
var name = line.substring(0, i).trim().toLowerCase();
|
|
||||||
var value = line.substring(i + 1).trim();
|
|
||||||
|
|
||||||
if (name.length() == 0) continue;
|
|
||||||
headers.put(name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (headers.containsKey("content-length")) {
|
|
||||||
try {
|
|
||||||
var i = Integer.parseInt(headers.get("content-length"));
|
|
||||||
str.skip(i);
|
|
||||||
}
|
|
||||||
catch (IllegalFormatException e) { /* ¯\_(ツ)_/¯ */ }
|
|
||||||
}
|
|
||||||
|
|
||||||
return new HttpRequest(method, path, headers, socket.getOutputStream());
|
|
||||||
}
|
|
||||||
catch (IOException | NullPointerException e) { return null; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
|||||||
package me.topchetoeu.j2s.repl.debug;
|
|
||||||
|
|
||||||
import me.topchetoeu.j2s.compilation.json.JSON;
|
|
||||||
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
|
||||||
|
|
||||||
public class V8Error {
|
|
||||||
public final String message;
|
|
||||||
|
|
||||||
public V8Error(String message) {
|
|
||||||
this.message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return JSON.stringify(new JSONMap().set("error", new JSONMap()
|
|
||||||
.set("message", message)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package me.topchetoeu.j2s.repl.debug;
|
|
||||||
|
|
||||||
import me.topchetoeu.j2s.compilation.json.JSON;
|
|
||||||
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
|
||||||
|
|
||||||
public class V8Event {
|
|
||||||
public final String name;
|
|
||||||
public final JSONMap params;
|
|
||||||
|
|
||||||
public V8Event(String name, JSONMap params) {
|
|
||||||
this.name = name;
|
|
||||||
this.params = params;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return JSON.stringify(new JSONMap()
|
|
||||||
.set("method", name)
|
|
||||||
.set("params", params)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
package me.topchetoeu.j2s.repl.debug;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import me.topchetoeu.j2s.compilation.json.JSON;
|
|
||||||
import me.topchetoeu.j2s.compilation.json.JSONElement;
|
|
||||||
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
|
||||||
|
|
||||||
public class V8Message {
|
|
||||||
public final String name;
|
|
||||||
public final int id;
|
|
||||||
public final JSONMap params;
|
|
||||||
|
|
||||||
public V8Message(String name, int id, Map<String, JSONElement> params) {
|
|
||||||
this.name = name;
|
|
||||||
this.params = new JSONMap(params);
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
public V8Result respond(JSONMap result) {
|
|
||||||
return new V8Result(id, result);
|
|
||||||
}
|
|
||||||
public V8Result respond() {
|
|
||||||
return new V8Result(id, new JSONMap());
|
|
||||||
}
|
|
||||||
|
|
||||||
public V8Message(JSONMap raw) {
|
|
||||||
if (!raw.isNumber("id")) throw new IllegalArgumentException("Expected number property 'id'.");
|
|
||||||
if (!raw.isString("method")) throw new IllegalArgumentException("Expected string property 'method'.");
|
|
||||||
|
|
||||||
this.name = raw.string("method");
|
|
||||||
this.id = (int)raw.number("id");
|
|
||||||
this.params = raw.contains("params") ? raw.map("params") : new JSONMap();
|
|
||||||
}
|
|
||||||
public V8Message(String raw) {
|
|
||||||
this(JSON.parse(null, raw).map());
|
|
||||||
}
|
|
||||||
|
|
||||||
public JSONMap toMap() {
|
|
||||||
var res = new JSONMap();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return JSON.stringify(new JSONMap()
|
|
||||||
.set("method", name)
|
|
||||||
.set("params", params)
|
|
||||||
.set("id", id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package me.topchetoeu.j2s.repl.debug;
|
|
||||||
|
|
||||||
import me.topchetoeu.j2s.compilation.json.JSON;
|
|
||||||
import me.topchetoeu.j2s.compilation.json.JSONMap;
|
|
||||||
|
|
||||||
public class V8Result {
|
|
||||||
public final int id;
|
|
||||||
public final JSONMap result;
|
|
||||||
|
|
||||||
public V8Result(int id, JSONMap result) {
|
|
||||||
this.id = id;
|
|
||||||
this.result = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return JSON.stringify(new JSONMap()
|
|
||||||
.set("id", id)
|
|
||||||
.set("result", result)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,186 +0,0 @@
|
|||||||
package me.topchetoeu.j2s.repl.debug;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.Socket;
|
|
||||||
|
|
||||||
import me.topchetoeu.j2s.repl.debug.WebSocketMessage.Type;
|
|
||||||
|
|
||||||
public class WebSocket implements AutoCloseable {
|
|
||||||
public long maxLength = 1 << 20;
|
|
||||||
|
|
||||||
private Socket socket;
|
|
||||||
private boolean closed = false;
|
|
||||||
|
|
||||||
private OutputStream out() throws IOException {
|
|
||||||
return socket.getOutputStream();
|
|
||||||
}
|
|
||||||
private InputStream in() throws IOException {
|
|
||||||
return socket.getInputStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
private long readLen(int byteLen) throws IOException {
|
|
||||||
long res = 0;
|
|
||||||
|
|
||||||
if (byteLen == 126) {
|
|
||||||
res |= in().read() << 8;
|
|
||||||
res |= in().read();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
else if (byteLen == 127) {
|
|
||||||
res |= in().read() << 56;
|
|
||||||
res |= in().read() << 48;
|
|
||||||
res |= in().read() << 40;
|
|
||||||
res |= in().read() << 32;
|
|
||||||
res |= in().read() << 24;
|
|
||||||
res |= in().read() << 16;
|
|
||||||
res |= in().read() << 8;
|
|
||||||
res |= in().read();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
else return byteLen;
|
|
||||||
}
|
|
||||||
private byte[] readMask(boolean has) throws IOException {
|
|
||||||
if (has) {
|
|
||||||
return new byte[] {
|
|
||||||
(byte)in().read(),
|
|
||||||
(byte)in().read(),
|
|
||||||
(byte)in().read(),
|
|
||||||
(byte)in().read()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else return new byte[4];
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeLength(int len) throws IOException {
|
|
||||||
if (len < 126) {
|
|
||||||
out().write((int)len);
|
|
||||||
}
|
|
||||||
else if (len <= 0xFFFF) {
|
|
||||||
out().write(126);
|
|
||||||
out().write((int)(len >> 8) & 0xFF);
|
|
||||||
out().write((int)len & 0xFF);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
out().write(127);
|
|
||||||
out().write(0);
|
|
||||||
out().write(0);
|
|
||||||
out().write(0);
|
|
||||||
out().write(0);
|
|
||||||
out().write((len >> 24) & 0xFF);
|
|
||||||
out().write((len >> 16) & 0xFF);
|
|
||||||
out().write((len >> 8) & 0xFF);
|
|
||||||
out().write(len & 0xFF);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private synchronized void write(int type, byte[] data) throws IOException {
|
|
||||||
out().write(type | 0x80);
|
|
||||||
writeLength(data.length);
|
|
||||||
out().write(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void send(String data) throws IOException {
|
|
||||||
if (closed) throw new IllegalStateException("Websocket is closed.");
|
|
||||||
write(1, data.getBytes());
|
|
||||||
}
|
|
||||||
public void send(byte[] data) throws IOException {
|
|
||||||
if (closed) throw new IllegalStateException("Websocket is closed.");
|
|
||||||
write(2, data);
|
|
||||||
}
|
|
||||||
public void send(WebSocketMessage msg) throws IOException {
|
|
||||||
if (msg.type == Type.Binary) send(msg.binaryData());
|
|
||||||
else send(msg.textData());
|
|
||||||
}
|
|
||||||
public void send(Object data) throws IOException {
|
|
||||||
if (closed) throw new IllegalStateException("Websocket is closed.");
|
|
||||||
write(1, data.toString().getBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close(String reason) {
|
|
||||||
if (socket != null) {
|
|
||||||
try {
|
|
||||||
write(8, reason.getBytes());
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
catch (Throwable e) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
socket = null;
|
|
||||||
closed = true;
|
|
||||||
}
|
|
||||||
public void close() {
|
|
||||||
close("");
|
|
||||||
}
|
|
||||||
|
|
||||||
private WebSocketMessage fail(String reason) {
|
|
||||||
System.out.println("WebSocket Error: " + reason);
|
|
||||||
close(reason);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] readData() throws IOException {
|
|
||||||
var maskLen = in().read();
|
|
||||||
var hasMask = (maskLen & 0x80) != 0;
|
|
||||||
var len = (int)readLen(maskLen & 0x7F);
|
|
||||||
var mask = readMask(hasMask);
|
|
||||||
|
|
||||||
if (len > maxLength) fail("WebSocket Error: client exceeded configured max message size");
|
|
||||||
else {
|
|
||||||
var buff = new byte[len];
|
|
||||||
|
|
||||||
if (in().read(buff) < len) fail("WebSocket Error: payload too short");
|
|
||||||
else {
|
|
||||||
for (int i = 0; i < len; i++) {
|
|
||||||
buff[i] ^= mask[(int)(i % 4)];
|
|
||||||
}
|
|
||||||
return buff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WebSocketMessage receive() throws IOException {
|
|
||||||
var data = new ByteArrayOutputStream();
|
|
||||||
var type = 0;
|
|
||||||
|
|
||||||
while (socket != null && !closed) {
|
|
||||||
var finId = in().read();
|
|
||||||
if (finId < 0) break;
|
|
||||||
var fin = (finId & 0x80) != 0;
|
|
||||||
int id = finId & 0x0F;
|
|
||||||
|
|
||||||
if (id == 0x8) { close(); return null; }
|
|
||||||
if (id >= 0x8) {
|
|
||||||
if (!fin) return fail("WebSocket Error: client-sent control frame was fragmented");
|
|
||||||
if (id == 0x9) write(0xA, data.toByteArray());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == 0) type = id;
|
|
||||||
if (type == 0) return fail("WebSocket Error: client used opcode 0x00 for first fragment");
|
|
||||||
|
|
||||||
var buff = readData();
|
|
||||||
if (buff == null) break;
|
|
||||||
|
|
||||||
if (data.size() + buff.length > maxLength) return fail("WebSocket Error: client exceeded configured max message size");
|
|
||||||
data.write(buff);
|
|
||||||
|
|
||||||
if (!fin) continue;
|
|
||||||
var raw = data.toByteArray();
|
|
||||||
|
|
||||||
if (type == 1) {
|
|
||||||
return new WebSocketMessage(new String(raw));
|
|
||||||
}
|
|
||||||
else return new WebSocketMessage(raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WebSocket(Socket socket) {
|
|
||||||
this.socket = socket;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
package me.topchetoeu.j2s.repl.debug;
|
|
||||||
|
|
||||||
public class WebSocketMessage {
|
|
||||||
public static enum Type {
|
|
||||||
Text,
|
|
||||||
Binary,
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Type type;
|
|
||||||
private final Object data;
|
|
||||||
|
|
||||||
public final String textData() {
|
|
||||||
if (type != Type.Text) throw new IllegalStateException("Message is not text.");
|
|
||||||
return (String)data;
|
|
||||||
}
|
|
||||||
public final byte[] binaryData() {
|
|
||||||
if (type != Type.Binary) throw new IllegalStateException("Message is not binary.");
|
|
||||||
return (byte[])data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WebSocketMessage(String data) {
|
|
||||||
this.type = Type.Text;
|
|
||||||
this.data = data;
|
|
||||||
}
|
|
||||||
public WebSocketMessage(byte[] data) {
|
|
||||||
this.type = Type.Binary;
|
|
||||||
this.data = data;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user