Integrate typescript #8

Merged
TopchetoEU merged 16 commits from TopchetoEU/tests into master 2023-11-05 18:32:42 +00:00
8 changed files with 278 additions and 242 deletions
Showing only changes of commit ed1009ab69 - Show all commits

View File

@ -8,16 +8,17 @@ import java.nio.file.Path;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Engine;
import me.topchetoeu.jscript.engine.Environment;
import me.topchetoeu.jscript.engine.StackData;
import me.topchetoeu.jscript.engine.debug.DebugServer;
import me.topchetoeu.jscript.engine.debug.SimpleDebugger;
import me.topchetoeu.jscript.engine.values.ArrayValue;
import me.topchetoeu.jscript.engine.values.NativeFunction;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.events.Observer;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.exceptions.InterruptException;
import me.topchetoeu.jscript.exceptions.SyntaxException;
import me.topchetoeu.jscript.exceptions.UncheckedException;
import me.topchetoeu.jscript.lib.Internals;
public class Main {
@ -44,16 +45,15 @@ public class Main {
public static void main(String args[]) {
System.out.println(String.format("Running %s v%s by %s", Metadata.NAME, Metadata.VERSION, Metadata.AUTHOR));
engine = new Engine();
engine = new Engine(true);
env = new Environment(null, null, null);
var exited = new boolean[1];
var server = new DebugServer();
server.targets.put("target", (ws, req) -> SimpleDebugger.get(ws, engine));
server.targets.put("target", (ws, req) -> new SimpleDebugger(ws, engine));
engineTask = engine.start();
debugTask = server.start(new InetSocketAddress("127.0.0.1", 9229), true);
// server.awaitConnection();
engine.pushMsg(false, null, new NativeFunction((ctx, thisArg, _a) -> {
new Internals().apply(env);
@ -72,34 +72,54 @@ public class Main {
throw new EngineException("Couldn't open do.js");
}
});
// TODO: make better API
env.global.define(true, new NativeFunction("include", (_ctx, th, __args) -> {
try {
var currFilename = StackData.peekFrame(_ctx).function.loc().filename();
var loc = Path.of("").toAbsolutePath();
if (currFilename.protocol.equals("file")) loc = Path.of(currFilename.path).getParent();
var path = loc.resolve(Path.of(__args.length >= 1 ? Values.toString(_ctx, __args[0]) : ""));
var src = Files.readString(path);
var func = _ctx.compile(Filename.fromFile(path.toFile()), src);
var callArgs = new ArrayValue();
if (__args.length >= 2 && __args[1] instanceof ArrayValue) callArgs = (ArrayValue)__args[1];
return func.call(_ctx, null, callArgs);
}
catch (IOException e) { throw EngineException.ofError("IOError", "Couldn't open file."); }
}));
return null;
}), null).await();
try {
var ts = engine.pushMsg(
false, new Context(engine).pushEnv(env),
new Filename("file", "/mnt/data/repos/java-jscript/src/me/topchetoeu/jscript/js/ts.js"),
var tsEnv = env.child();
tsEnv.global.define(null, "module", false, new ObjectValue());
engine.pushMsg(
false, new Context(engine).pushEnv(tsEnv),
new Filename("jscript", "ts.js"),
Reading.resourceToString("js/ts.js"), null
).await();
System.out.println("Loaded typescript!");
engine.pushMsg(
false, new Context(engine).pushEnv(env.child()),
new Filename("jscript", "internals/bootstrap.js"), Reading.resourceToString("js/bootstrap.js"), null,
ts, env, new ArrayValue(null, Reading.resourceToString("js/lib.d.ts"))
tsEnv.global.obj, env, new ArrayValue(null, Reading.resourceToString("js/lib.d.ts"))
).await();
}
catch (EngineException e) {
Values.printError(e, "(while initializing TS)");
System.out.println("engine reported stack trace:");
for (var el : e.stackTrace) {
System.out.println(el);
}
}
var reader = new Thread(() -> {
try {
for (var arg : args) {
try {
var file = Path.of(arg);
var raw = Files.readString(file);
valuePrinter.next(engine.pushMsg(false, new Context(engine).pushEnv(env), Filename.fromFile(file.toFile()), raw, null).await());
}
catch (EngineException e) { Values.printError(e, ""); }
}
for (var i = 0; ; i++) {
try {
var raw = Reading.read();
@ -110,7 +130,7 @@ public class Main {
catch (EngineException e) { Values.printError(e, ""); }
}
}
catch (IOException e) { return; }
catch (IOException e) { exited[0] = true; }
catch (SyntaxException ex) {
if (exited[0]) return;
System.out.println("Syntax error:" + ex.msg);
@ -121,7 +141,6 @@ public class Main {
ex.printStackTrace();
}
}
catch (Throwable e) { throw new UncheckedException(e); }
if (exited[0]) debugTask.interrupt();
});
reader.setDaemon(true);

View File

@ -6,7 +6,6 @@ import java.util.TreeSet;
import me.topchetoeu.jscript.Filename;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.engine.values.NativeFunction;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.parsing.Parsing;
@ -40,10 +39,9 @@ public class Context {
}
else source = Values.toString(this, transpiled);
var debugger = StackData.getDebugger(this);
var breakpoints = new TreeSet<Location>();
FunctionValue res = Parsing.compile(engine.functions, breakpoints, environment(), filename, source);
if (debugger != null) debugger.onSource(filename, source, breakpoints);
engine.onSource(filename, source, breakpoints);
if (runner != null) res = (FunctionValue)runner.call(this, null, res);

View File

@ -1,16 +1,22 @@
package me.topchetoeu.jscript.engine;
import java.util.HashMap;
import java.util.TreeSet;
import java.util.concurrent.LinkedBlockingDeque;
import me.topchetoeu.jscript.Filename;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.FunctionBody;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.engine.debug.DebugController;
import me.topchetoeu.jscript.engine.frame.CodeFrame;
import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.events.Awaitable;
import me.topchetoeu.jscript.events.DataNotifier;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.exceptions.InterruptException;
public class Engine {
public class Engine implements DebugController {
private class UncompiledFunction extends FunctionValue {
public final Filename filename;
public final String raw;
@ -51,8 +57,42 @@ public class Engine {
private LinkedBlockingDeque<Task> microTasks = new LinkedBlockingDeque<>();
public final int id = ++nextId;
public final Data data = new Data().set(StackData.MAX_FRAMES, 200);
public final HashMap<Long, FunctionBody> functions = new HashMap<>();
public final Data data = new Data().set(StackData.MAX_FRAMES, 10000);
public final boolean debugging;
private final HashMap<Filename, String> sources = new HashMap<>();
private final HashMap<Filename, TreeSet<Location>> bpts = new HashMap<>();
private DebugController debugger;
public boolean attachDebugger(DebugController debugger) {
if (!debugging || this.debugger != null) return false;
for (var source : sources.entrySet()) {
debugger.onSource(source.getKey(), source.getValue(), bpts.get(source.getKey()));
}
this.debugger = debugger;
return true;
}
public boolean detachDebugger() {
if (!debugging || this.debugger == null) return false;
this.debugger = null;
return true;
}
@Override public void onFramePop(Context ctx, CodeFrame frame) {
if (debugging && debugger != null) debugger.onFramePop(ctx, frame);
}
@Override public boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) {
if (debugging && debugger != null) return debugger.onInstruction(ctx, frame, instruction, returnVal, error, caught);
else return false;
}
@Override public void onSource(Filename filename, String source, TreeSet<Location> breakpoints) {
if (!debugging) return;
if (debugger != null) debugger.onSource(filename, source, breakpoints);
sources.put(filename, source);
bpts.put(filename, breakpoints);
}
private void runTask(Task task) {
try {
@ -108,4 +148,8 @@ public class Engine {
public Awaitable<Object> pushMsg(boolean micro, Context ctx, Filename filename, String raw, Object thisArg, Object ...args) {
return pushMsg(micro, ctx, new UncompiledFunction(filename, raw), thisArg, args);
}
public Engine(boolean debugging) {
this.debugging = debugging;
}
}

View File

@ -40,8 +40,7 @@ public class Environment {
}
@Native public Symbol symbol(String name) {
if (symbols.containsKey(name))
return symbols.get(name);
if (symbols.containsKey(name)) return symbols.get(name);
else {
var res = new Symbol(name);
symbols.put(name, res);

View File

@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.engine.debug.Debugger;
import me.topchetoeu.jscript.engine.frame.CodeFrame;
import me.topchetoeu.jscript.exceptions.EngineException;
@ -25,8 +26,7 @@ public class StackData {
if (frames.get(frames.size() - 1) != frame) return false;
frames.remove(frames.size() - 1);
ctx.popEnv();
var dbg = getDebugger(ctx);
if (dbg != null) dbg.onFramePop(ctx, frame);
ctx.engine.onFramePop(ctx, frame);
return true;
}
public static CodeFrame peekFrame(Context ctx) {
@ -45,7 +45,11 @@ public class StackData {
for (var i = frames.size() - 1; i >= 0; i--) {
var el = frames.get(i);
var name = el.function.name;
var loc = el.function.loc();
Location loc = null;
for (var j = el.codePtr; j >= 0 && loc == null; j--) loc = el.function.body[j].location;
if (loc == null) loc = el.function.loc();
var trace = "";
if (loc != null) trace += "at " + loc.toString() + " ";
@ -58,8 +62,4 @@ public class StackData {
return res;
}
public static Debugger getDebugger(Context ctx) {
return ctx.data.get(DEBUGGER);
}
}

View File

@ -37,7 +37,4 @@ public interface DebugController {
* @param frame The code frame which was popped out
*/
void onFramePop(Context ctx, CodeFrame frame);
void connect();
void disconnect();
}

View File

@ -1,5 +1,6 @@
package me.topchetoeu.jscript.engine.debug;
public interface Debugger extends DebugHandler, DebugController {
void connect();
void disconnect();
}

View File

@ -31,12 +31,6 @@ import me.topchetoeu.jscript.json.JSON;
import me.topchetoeu.jscript.json.JSONElement;
import me.topchetoeu.jscript.json.JSONList;
import me.topchetoeu.jscript.json.JSONMap;
import me.topchetoeu.jscript.lib.DateLib;
import me.topchetoeu.jscript.lib.MapLib;
import me.topchetoeu.jscript.lib.PromiseLib;
import me.topchetoeu.jscript.lib.RegExpLib;
import me.topchetoeu.jscript.lib.SetLib;
import me.topchetoeu.jscript.lib.GeneratorLib.Generator;
public class SimpleDebugger implements Debugger {
public static final String CHROME_GET_PROP_FUNC = "function s(e){let t=this;const n=JSON.parse(e);for(let e=0,i=n.length;e<i;++e)t=t[n[e]];return t}";
@ -403,7 +397,7 @@ public class SimpleDebugger implements Debugger {
}
private RunResult run(Frame codeFrame, String code) {
var engine = new Engine();
var engine = new Engine(false);
var env = codeFrame.func.environment.fork();
ObjectValue global = env.global.obj,
@ -415,7 +409,7 @@ public class SimpleDebugger implements Debugger {
env.global = new GlobalScope(local);
var ctx = new Context(engine).pushEnv(env);
var awaiter = engine.pushMsg(false, ctx, new Filename("temp", "exec"), "(" + code + ")", codeFrame.frame.thisArg, codeFrame.frame.args);
var awaiter = engine.pushMsg(false, ctx, new Filename("jscript", "eval"), code, codeFrame.frame.thisArg, codeFrame.frame.args);
engine.run(true);
@ -594,57 +588,46 @@ public class SimpleDebugger implements Debugger {
}
@Override public void getProperties(V8Message msg) {
var obj = idToObject.get(Integer.parseInt(msg.params.string("objectId")));
var own = msg.params.bool("ownProperties");
var accessorPropertiesOnly = msg.params.bool("accessorPropertiesOnly", false);
var currOwn = true;
var res = new JSONList();
var ctx = objectToCtx.get(obj);
while (obj != emptyObject && obj != null) {
var ctx = objectToCtx.get(obj);
for (var key : obj.keys(true)) {
var propDesc = new JSONMap();
for (var key : obj.keys(true)) {
var propDesc = new JSONMap();
if (obj.properties.containsKey(key)) {
var prop = obj.properties.get(key);
if (obj.properties.containsKey(key)) {
var prop = obj.properties.get(key);
propDesc.set("name", Values.toString(ctx, key));
if (prop.getter != null) propDesc.set("get", serializeObj(ctx, prop.getter));
if (prop.setter != null) propDesc.set("set", serializeObj(ctx, prop.setter));
propDesc.set("enumerable", obj.memberEnumerable(key));
propDesc.set("configurable", obj.memberConfigurable(key));
propDesc.set("isOwn", currOwn);
res.add(propDesc);
}
else {
propDesc.set("name", Values.toString(ctx, key));
propDesc.set("value", serializeObj(ctx, obj.getMember(ctx, key)));
propDesc.set("writable", obj.memberWritable(key));
propDesc.set("enumerable", obj.memberEnumerable(key));
propDesc.set("configurable", obj.memberConfigurable(key));
propDesc.set("isOwn", currOwn);
res.add(propDesc);
}
propDesc.set("name", Values.toString(ctx, key));
if (prop.getter != null) propDesc.set("get", serializeObj(ctx, prop.getter));
if (prop.setter != null) propDesc.set("set", serializeObj(ctx, prop.setter));
propDesc.set("enumerable", obj.memberEnumerable(key));
propDesc.set("configurable", obj.memberConfigurable(key));
propDesc.set("isOwn", true);
res.add(propDesc);
}
obj = obj.getPrototype(ctx);
if (currOwn) {
var protoDesc = new JSONMap();
protoDesc.set("name", "__proto__");
protoDesc.set("value", serializeObj(ctx, obj == null ? Values.NULL : obj));
protoDesc.set("writable", true);
protoDesc.set("enumerable", false);
protoDesc.set("configurable", false);
protoDesc.set("isOwn", currOwn);
res.add(protoDesc);
else {
propDesc.set("name", Values.toString(ctx, key));
propDesc.set("value", serializeObj(ctx, obj.getMember(ctx, key)));
propDesc.set("writable", obj.memberWritable(key));
propDesc.set("enumerable", obj.memberEnumerable(key));
propDesc.set("configurable", obj.memberConfigurable(key));
propDesc.set("isOwn", true);
res.add(propDesc);
}
currOwn = false;
if (true) break;
}
var proto = obj.getPrototype(ctx);
var protoDesc = new JSONMap();
protoDesc.set("name", "__proto__");
protoDesc.set("value", serializeObj(ctx, proto == null ? Values.NULL : proto));
protoDesc.set("writable", true);
protoDesc.set("enumerable", false);
protoDesc.set("configurable", false);
protoDesc.set("isOwn", true);
res.add(protoDesc);
ws.send(msg.respond(new JSONMap().set("result", res)));
}
@Override public void callFunctionOn(V8Message msg) {
@ -667,36 +650,40 @@ public class SimpleDebugger implements Debugger {
else src = src.substring(0, start) + src.substring(end + 1);
}
switch (src) {
case CHROME_GET_PROP_FUNC: {
var path = JSON.parse(new Filename("tmp", "json"), (String)args.get(0)).list();
Object res = thisArg;
for (var el : path) res = Values.getMember(ctx, res, JSON.toJs(el));
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, res))));
return;
try {
switch (src) {
case CHROME_GET_PROP_FUNC: {
var path = JSON.parse(null, (String)args.get(0)).list();
Object res = thisArg;
for (var el : path) res = Values.getMember(ctx, res, JSON.toJs(el));
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, res))));
return;
}
case VSCODE_STRINGIFY_VAL:
case VSCODE_STRINGIFY_PROPS:
case VSCODE_SHALLOW_COPY:
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, emptyObject))));
break;
case VSCODE_FLATTEN_ARRAY: {
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, thisArg))));
break;
}
case VSCODE_SYMBOL_REQUEST:
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, new ArrayValue(ctx)))));
break;
case VSCODE_CALL: {
var func = (FunctionValue)(args.size() < 1 ? null : args.get(0));
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, func.call(ctx, thisArg)))));
break;
}
default: {
var res = ctx.compile(new Filename("jscript", "eval"), src).call(ctx);
if (res instanceof FunctionValue) ((FunctionValue)res).call(ctx, thisArg, args);
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, res))));
}
}
case VSCODE_STRINGIFY_VAL:
case VSCODE_STRINGIFY_PROPS:
case VSCODE_SHALLOW_COPY:
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, emptyObject))));
break;
case VSCODE_FLATTEN_ARRAY: {
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, thisArg))));
break;
}
case VSCODE_SYMBOL_REQUEST:
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, new ArrayValue(ctx)))));
break;
case VSCODE_CALL: {
var func = (FunctionValue)(args.size() < 1 ? null : args.get(0));
try { ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, func.call(ctx, thisArg))))); }
catch (EngineException e) { ws.send(msg.respond(new JSONMap().set("exceptionDetails", serializeException(ctx, e)))); }
break;
}
default:
ws.send(new V8Error("A non well-known function was used with callFunctionOn."));
break;
}
catch (EngineException e) { ws.send(msg.respond(new JSONMap().set("exceptionDetails", serializeException(ctx, e)))); }
}
@Override
@ -708,8 +695,8 @@ public class SimpleDebugger implements Debugger {
int id = nextId();
var src = new Source(id, filename, source, locations);
filenameToId.put(filename, id);
idToSource.put(id, src);
filenameToId.put(filename, id);
for (var bpcd : idToBptCand.values()) {
if (!bpcd.pattern.matcher(filename.toString()).matches()) continue;
@ -801,27 +788,18 @@ public class SimpleDebugger implements Debugger {
}
@Override public void connect() {
target.data.set(StackData.DEBUGGER, this);
if (!target.attachDebugger(this)) {
ws.send(new V8Error("A debugger is already attached to this engine."));
}
}
@Override public void disconnect() {
target.data.remove(StackData.DEBUGGER);
target.detachDebugger();
enabled = false;
updateNotifier.next();
}
private SimpleDebugger(WebSocket ws, Engine target) {
public SimpleDebugger(WebSocket ws, Engine target) {
this.ws = ws;
this.target = target;
}
public static SimpleDebugger get(WebSocket ws, Engine target) {
if (target.data.has(StackData.DEBUGGER)) {
ws.send(new V8Error("A debugger is already attached to this engine."));
return null;
}
else {
var res = new SimpleDebugger(ws, target);
return res;
}
}
}