fix: forgor to delete this
This commit is contained in:
parent
292d08d5a6
commit
8c049ac08f
@ -1,154 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.engine.debug;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.ServerSocket;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.util.Base64;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Engine;
|
|
||||||
import me.topchetoeu.jscript.engine.debug.WebSocketMessage.Type;
|
|
||||||
import me.topchetoeu.jscript.engine.debug.handlers.DebuggerHandles;
|
|
||||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
|
||||||
|
|
||||||
public class DebugServer {
|
|
||||||
public static String browserDisplayName = "jscript";
|
|
||||||
public static String targetName = "target";
|
|
||||||
|
|
||||||
public final Engine engine;
|
|
||||||
|
|
||||||
private static void send(Socket socket, String val) throws IOException {
|
|
||||||
Http.writeResponse(socket.getOutputStream(), 200, "OK", "application/json", val.getBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
// SILENCE JAVA
|
|
||||||
private MessageDigest getDigestInstance() {
|
|
||||||
try {
|
|
||||||
return MessageDigest.getInstance("sha1");
|
|
||||||
}
|
|
||||||
catch (Throwable a) { return null; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Thread runAsync(Runnable func, String name) {
|
|
||||||
var res = new Thread(func);
|
|
||||||
res.setName(name);
|
|
||||||
res.start();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handle(WebSocket ws) throws InterruptedException, IOException {
|
|
||||||
WebSocketMessage raw;
|
|
||||||
|
|
||||||
while ((raw = ws.receive()) != null) {
|
|
||||||
if (raw.type != Type.Text) {
|
|
||||||
ws.send(new V8Error("Expected a text message."));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
V8Message msg;
|
|
||||||
|
|
||||||
try {
|
|
||||||
msg = new V8Message(raw.textData());
|
|
||||||
}
|
|
||||||
catch (SyntaxException e) {
|
|
||||||
ws.send(new V8Error(e.getMessage()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (msg.name) {
|
|
||||||
case "Debugger.enable": DebuggerHandles.enable(msg, engine, ws); continue;
|
|
||||||
case "Debugger.disable": DebuggerHandles.disable(msg, engine, ws); continue;
|
|
||||||
case "Debugger.stepInto": DebuggerHandles.stepInto(msg, engine, ws); continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void onWsConnect(HttpRequest req, Socket socket) throws IOException {
|
|
||||||
var key = req.headers.get("sec-websocket-key");
|
|
||||||
|
|
||||||
if (key == null) {
|
|
||||||
Http.writeResponse(
|
|
||||||
socket.getOutputStream(), 426, "Upgrade Required", "text/txt",
|
|
||||||
"Expected a WS upgrade".getBytes()
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var resKey = Base64.getEncoder().encodeToString(getDigestInstance().digest(
|
|
||||||
(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes()
|
|
||||||
));
|
|
||||||
|
|
||||||
Http.writeCode(socket.getOutputStream(), 101, "Switching Protocols");
|
|
||||||
Http.writeHeader(socket.getOutputStream(), "Connection", "Upgrade");
|
|
||||||
Http.writeHeader(socket.getOutputStream(), "Sec-WebSocket-Accept", resKey);
|
|
||||||
Http.writeLastHeader(socket.getOutputStream(), "Upgrade", "WebSocket");
|
|
||||||
|
|
||||||
var ws = new WebSocket(socket);
|
|
||||||
|
|
||||||
runAsync(() -> {
|
|
||||||
try {
|
|
||||||
handle(ws);
|
|
||||||
}
|
|
||||||
catch (InterruptedException e) { return; }
|
|
||||||
catch (IOException e) { e.printStackTrace(); }
|
|
||||||
finally { ws.close(); }
|
|
||||||
}, "Debug Server Message Reader");
|
|
||||||
runAsync(() -> {
|
|
||||||
try {
|
|
||||||
handle(ws);
|
|
||||||
}
|
|
||||||
catch (InterruptedException e) { return; }
|
|
||||||
catch (IOException e) { e.printStackTrace(); }
|
|
||||||
finally { ws.close(); }
|
|
||||||
}, "Debug Server Event Writer");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void open(InetSocketAddress address) throws IOException {
|
|
||||||
ServerSocket server = new ServerSocket();
|
|
||||||
server.bind(address);
|
|
||||||
|
|
||||||
try {
|
|
||||||
while (true) {
|
|
||||||
var socket = server.accept();
|
|
||||||
var req = Http.readRequest(socket.getInputStream());
|
|
||||||
|
|
||||||
switch (req.path) {
|
|
||||||
case "/json/version":
|
|
||||||
send(socket, "{\"Browser\":\"" + browserDisplayName + "\",\"Protocol-Version\":\"1.2\"}");
|
|
||||||
break;
|
|
||||||
case "/json/list":
|
|
||||||
case "/json":
|
|
||||||
var addr = "ws://" + address.getHostString() + ":" + address.getPort() + "/devtools/page/" + targetName;
|
|
||||||
send(socket, "{\"id\":\"" + browserDisplayName + "\",\"webSocketDebuggerUrl\":\"" + addr + "\"}");
|
|
||||||
break;
|
|
||||||
case "/json/new":
|
|
||||||
case "/json/activate":
|
|
||||||
case "/json/protocol":
|
|
||||||
case "/json/close":
|
|
||||||
case "/devtools/inspector.html":
|
|
||||||
Http.writeResponse(
|
|
||||||
socket.getOutputStream(),
|
|
||||||
501, "Not Implemented", "text/txt",
|
|
||||||
"This feature isn't (and won't be) implemented.".getBytes()
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (req.path.equals("/devtools/page/" + targetName)) onWsConnect(req, socket);
|
|
||||||
else {
|
|
||||||
Http.writeResponse(
|
|
||||||
socket.getOutputStream(),
|
|
||||||
404, "Not Found", "text/txt",
|
|
||||||
"Not found :/".getBytes()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally { server.close(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public DebugServer(Engine engine) {
|
|
||||||
this.engine = engine;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.engine.debug;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.engine.BreakpointData;
|
|
||||||
import me.topchetoeu.jscript.engine.DebugCommand;
|
|
||||||
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
|
||||||
import me.topchetoeu.jscript.events.Event;
|
|
||||||
|
|
||||||
public class DebugState {
|
|
||||||
private boolean paused = false;
|
|
||||||
|
|
||||||
public final HashSet<Location> breakpoints = new HashSet<>();
|
|
||||||
public final List<CodeFrame> frames = new ArrayList<>();
|
|
||||||
public final Map<String, String> sources = new HashMap<>();
|
|
||||||
|
|
||||||
public final Event<BreakpointData> breakpointNotifier = new Event<>();
|
|
||||||
public final Event<DebugCommand> commandNotifier = new Event<>();
|
|
||||||
public final Event<String> sourceAdded = new Event<>();
|
|
||||||
|
|
||||||
public DebugState pushFrame(CodeFrame frame) {
|
|
||||||
frames.add(frame);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
public DebugState popFrame() {
|
|
||||||
if (frames.size() > 0) frames.remove(frames.size() - 1);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DebugCommand pause(BreakpointData data) throws InterruptedException {
|
|
||||||
paused = true;
|
|
||||||
breakpointNotifier.next(data);
|
|
||||||
return commandNotifier.toAwaitable().await();
|
|
||||||
}
|
|
||||||
public void resume(DebugCommand command) {
|
|
||||||
paused = false;
|
|
||||||
commandNotifier.next(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
// public void addSource()?
|
|
||||||
|
|
||||||
public boolean paused() { return paused; }
|
|
||||||
|
|
||||||
public boolean isBreakpoint(Location loc) {
|
|
||||||
return breakpoints.contains(loc);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.engine.debug;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.IllegalFormatException;
|
|
||||||
|
|
||||||
// We dont need no http library
|
|
||||||
public class Http {
|
|
||||||
public static void writeCode(OutputStream str, int code, String name) throws IOException {
|
|
||||||
str.write(("HTTP/1.1 " + code + " " + name + "\r\n").getBytes());
|
|
||||||
}
|
|
||||||
public static void writeHeader(OutputStream str, String name, String value) throws IOException {
|
|
||||||
str.write((name + ": " + value + "\r\n").getBytes());
|
|
||||||
}
|
|
||||||
public static void writeLastHeader(OutputStream str, String name, String value) throws IOException {
|
|
||||||
str.write((name + ": " + value + "\r\n").getBytes());
|
|
||||||
writeHeadersEnd(str);
|
|
||||||
}
|
|
||||||
public static void writeHeadersEnd(OutputStream str) throws IOException {
|
|
||||||
str.write("\n".getBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void writeResponse(OutputStream str, int code, String name, String type, byte[] data) throws IOException {
|
|
||||||
writeCode(str, code, name);
|
|
||||||
writeHeader(str, "Content-Type", type);
|
|
||||||
writeLastHeader(str, "Content-Length", data.length + "");
|
|
||||||
str.write(data);
|
|
||||||
str.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HttpRequest readRequest(InputStream str) throws IOException {
|
|
||||||
var lines = new BufferedReader(new InputStreamReader(str));
|
|
||||||
var line = lines.readLine();
|
|
||||||
var i1 = line.indexOf(" ");
|
|
||||||
var i2 = line.lastIndexOf(" ");
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.engine.debug;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class HttpRequest {
|
|
||||||
public final String method;
|
|
||||||
public final String path;
|
|
||||||
public final Map<String, String> headers;
|
|
||||||
|
|
||||||
public HttpRequest(String method, String path, Map<String, String> headers) {
|
|
||||||
this.method = method;
|
|
||||||
this.path = path;
|
|
||||||
this.headers = headers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.engine.debug;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.json.JSON;
|
|
||||||
import me.topchetoeu.jscript.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.jscript.engine.debug;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.json.JSON;
|
|
||||||
import me.topchetoeu.jscript.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.jscript.engine.debug;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.json.JSON;
|
|
||||||
import me.topchetoeu.jscript.json.JSONElement;
|
|
||||||
import me.topchetoeu.jscript.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("json", 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.jscript.engine.debug;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.json.JSON;
|
|
||||||
import me.topchetoeu.jscript.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,185 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.engine.debug;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.Socket;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.debug.WebSocketMessage.Type;
|
|
||||||
|
|
||||||
public class WebSocket implements AutoCloseable {
|
|
||||||
public long maxLength = 2000000;
|
|
||||||
|
|
||||||
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(long 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((int)(len >> 56) & 0xFF);
|
|
||||||
out().write((int)(len >> 48) & 0xFF);
|
|
||||||
out().write((int)(len >> 40) & 0xFF);
|
|
||||||
out().write((int)(len >> 32) & 0xFF);
|
|
||||||
out().write((int)(len >> 24) & 0xFF);
|
|
||||||
out().write((int)(len >> 16) & 0xFF);
|
|
||||||
out().write((int)(len >> 8) & 0xFF);
|
|
||||||
out().write((int)len & 0xFF);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private synchronized void write(int type, byte[] data) throws IOException {
|
|
||||||
out().write(type | 0x80);
|
|
||||||
writeLength(data.length);
|
|
||||||
for (int i = 0; i < data.length; i++) {
|
|
||||||
out().write(data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void send(String data) throws IOException {
|
|
||||||
if (closed) throw new IllegalStateException("Object is closed.");
|
|
||||||
write(1, data.getBytes());
|
|
||||||
}
|
|
||||||
public void send(byte[] data) throws IOException {
|
|
||||||
if (closed) throw new IllegalStateException("Object 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("Object is closed.");
|
|
||||||
write(1, data.toString().getBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close(String reason) {
|
|
||||||
if (socket != null) {
|
|
||||||
try { write(8, reason.getBytes()); } catch (IOException e) { /* ¯\_(ツ)_/¯ */ }
|
|
||||||
try { socket.close(); } catch (IOException e) { e.printStackTrace(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
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 InterruptedException {
|
|
||||||
try {
|
|
||||||
var data = new ByteArrayOutputStream();
|
|
||||||
var type = 0;
|
|
||||||
|
|
||||||
while (socket != null && !closed) {
|
|
||||||
var finId = in().read();
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException e) { close(); }
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WebSocket(Socket socket) {
|
|
||||||
this.socket = socket;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.engine.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,29 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.engine.debug.handlers;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.DebugCommand;
|
|
||||||
import me.topchetoeu.jscript.engine.Engine;
|
|
||||||
import me.topchetoeu.jscript.engine.debug.V8Error;
|
|
||||||
import me.topchetoeu.jscript.engine.debug.V8Message;
|
|
||||||
import me.topchetoeu.jscript.engine.debug.WebSocket;
|
|
||||||
import me.topchetoeu.jscript.json.JSONMap;
|
|
||||||
|
|
||||||
public class DebuggerHandles {
|
|
||||||
public static void enable(V8Message msg, Engine engine, WebSocket ws) throws IOException {
|
|
||||||
if (engine.debugState == null) ws.send(new V8Error("Debugging is disabled for this engine."));
|
|
||||||
else ws.send(msg.respond(new JSONMap().set("debuggerId", 1)));
|
|
||||||
}
|
|
||||||
public static void disable(V8Message msg, Engine engine, WebSocket ws) throws IOException {
|
|
||||||
if (engine.debugState == null) ws.send(msg.respond());
|
|
||||||
else ws.send(new V8Error("Debugger may not be disabled."));
|
|
||||||
}
|
|
||||||
public static void stepInto(V8Message msg, Engine engine, WebSocket ws) throws IOException {
|
|
||||||
if (engine.debugState == null) ws.send(new V8Error("Debugging is disabled for this engine."));
|
|
||||||
else if (!engine.debugState.paused()) ws.send(new V8Error("Debugger is not paused."));
|
|
||||||
else {
|
|
||||||
engine.debugState.resume(DebugCommand.STEP_INTO);
|
|
||||||
ws.send(msg.respond());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.engine.modules;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.polyfills.PolyfillEngine;
|
|
||||||
|
|
||||||
public class FileModuleProvider implements ModuleProvider {
|
|
||||||
public File root;
|
|
||||||
public final boolean allowOutside;
|
|
||||||
|
|
||||||
private boolean checkInside(Path modFile) {
|
|
||||||
return modFile.toAbsolutePath().startsWith(root.toPath().toAbsolutePath());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Module getModule(File cwd, String name) {
|
|
||||||
var realName = getRealName(cwd, name);
|
|
||||||
if (realName == null) return null;
|
|
||||||
var path = Path.of(realName + ".js").normalize();
|
|
||||||
|
|
||||||
try {
|
|
||||||
var res = PolyfillEngine.streamToString(new FileInputStream(path.toFile()));
|
|
||||||
return new Module(realName, path.toString(), res);
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public String getRealName(File cwd, String name) {
|
|
||||||
var path = Path.of(".", Path.of(cwd.toString(), name).normalize().toString());
|
|
||||||
var fileName = path.getFileName().toString();
|
|
||||||
if (fileName == null) return null;
|
|
||||||
if (!fileName.equals("index") && path.toFile().isDirectory()) return getRealName(cwd, name + "/index");
|
|
||||||
path = Path.of(path.toString() + ".js");
|
|
||||||
if (!allowOutside && !checkInside(path)) return null;
|
|
||||||
if (!path.toFile().isFile() || !path.toFile().canRead()) return null;
|
|
||||||
var res = path.toString().replace('\\', '/');
|
|
||||||
var i = res.lastIndexOf('.');
|
|
||||||
return res.substring(0, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileModuleProvider(File root, boolean allowOutside) {
|
|
||||||
this.root = root.toPath().normalize().toFile();
|
|
||||||
this.allowOutside = allowOutside;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.engine.modules;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.CallContext;
|
|
||||||
import me.topchetoeu.jscript.engine.CallContext.DataKey;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.Variable;
|
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
|
||||||
import me.topchetoeu.jscript.interop.NativeGetter;
|
|
||||||
import me.topchetoeu.jscript.interop.NativeSetter;
|
|
||||||
|
|
||||||
public class Module {
|
|
||||||
public class ExportsVariable implements Variable {
|
|
||||||
@Override
|
|
||||||
public boolean readonly() { return false; }
|
|
||||||
@Override
|
|
||||||
public Object get(CallContext ctx) { return exports; }
|
|
||||||
@Override
|
|
||||||
public void set(CallContext ctx, Object val) { exports = val; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static DataKey<Module> KEY = new DataKey<>();
|
|
||||||
|
|
||||||
public final String filename;
|
|
||||||
public final String source;
|
|
||||||
public final String name;
|
|
||||||
private Object exports = new ObjectValue();
|
|
||||||
private boolean executing = false;
|
|
||||||
|
|
||||||
@NativeGetter("name")
|
|
||||||
public String name() { return name; }
|
|
||||||
@NativeGetter("exports")
|
|
||||||
public Object exports() { return exports; }
|
|
||||||
@NativeSetter("exports")
|
|
||||||
public void setExports(Object val) { exports = val; }
|
|
||||||
|
|
||||||
public void execute(CallContext ctx) throws InterruptedException {
|
|
||||||
if (executing) return;
|
|
||||||
|
|
||||||
executing = true;
|
|
||||||
var scope = ctx.engine().global().globalChild();
|
|
||||||
scope.define(null, "module", true, this);
|
|
||||||
scope.define("exports", new ExportsVariable());
|
|
||||||
|
|
||||||
var parent = new File(filename).getParentFile();
|
|
||||||
if (parent == null) parent = new File(".");
|
|
||||||
|
|
||||||
ctx.engine().compile(scope, filename, source).call(ctx.copy().setData(KEY, this), null);
|
|
||||||
executing = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Module(String name, String filename, String source) {
|
|
||||||
this.name = name;
|
|
||||||
this.filename = filename;
|
|
||||||
this.source = source;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,80 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.engine.modules;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.CallContext;
|
|
||||||
|
|
||||||
public class ModuleManager {
|
|
||||||
private final List<ModuleProvider> providers = new ArrayList<>();
|
|
||||||
private final HashMap<String, Module> cache = new HashMap<>();
|
|
||||||
public final FileModuleProvider files;
|
|
||||||
|
|
||||||
public void addProvider(ModuleProvider provider) {
|
|
||||||
this.providers.add(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isCached(File cwd, String name) {
|
|
||||||
name = name.replace("\\", "/");
|
|
||||||
|
|
||||||
// Absolute paths are forbidden
|
|
||||||
if (name.startsWith("/")) return false;
|
|
||||||
// Look for files if we have a relative path
|
|
||||||
if (name.startsWith("../") || name.startsWith("./")) {
|
|
||||||
var realName = files.getRealName(cwd, name);
|
|
||||||
if (cache.containsKey(realName)) return true;
|
|
||||||
else return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var provider : providers) {
|
|
||||||
var realName = provider.getRealName(cwd, name);
|
|
||||||
if (realName == null) continue;
|
|
||||||
if (cache.containsKey(realName)) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
public Module tryLoad(CallContext ctx, String name) throws InterruptedException {
|
|
||||||
name = name.replace('\\', '/');
|
|
||||||
|
|
||||||
var pcwd = Path.of(".");
|
|
||||||
|
|
||||||
if (ctx.hasData(Module.KEY)) {
|
|
||||||
pcwd = Path.of(((Module)ctx.getData(Module.KEY)).filename).getParent();
|
|
||||||
if (pcwd == null) pcwd = Path.of(".");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var cwd = pcwd.toFile();
|
|
||||||
|
|
||||||
if (name.startsWith("/")) return null;
|
|
||||||
if (name.startsWith("../") || name.startsWith("./")) {
|
|
||||||
var realName = files.getRealName(cwd, name);
|
|
||||||
if (realName == null) return null;
|
|
||||||
if (cache.containsKey(realName)) return cache.get(realName);
|
|
||||||
var mod = files.getModule(cwd, name);
|
|
||||||
cache.put(mod.name(), mod);
|
|
||||||
mod.execute(ctx);
|
|
||||||
return mod;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var provider : providers) {
|
|
||||||
var realName = provider.getRealName(cwd, name);
|
|
||||||
if (realName == null) continue;
|
|
||||||
if (cache.containsKey(realName)) return cache.get(realName);
|
|
||||||
var mod = provider.getModule(cwd, name);
|
|
||||||
cache.put(mod.name(), mod);
|
|
||||||
mod.execute(ctx);
|
|
||||||
return mod;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ModuleManager(File root) {
|
|
||||||
files = new FileModuleProvider(root, false);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.engine.modules;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public interface ModuleProvider {
|
|
||||||
Module getModule(File cwd, String name);
|
|
||||||
String getRealName(File cwd, String name);
|
|
||||||
default boolean hasModule(File cwd, String name) { return getRealName(cwd, name) != null; }
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user