Compare commits

..

3 Commits

Author SHA1 Message Date
fd8b0b7cbe fix: source map decoding 2025-05-22 14:20:29 +03:00
a4c09b6cd6 various debugging-related fixes 2025-05-22 14:19:53 +03:00
892408d9dd feat: some much needed REPL improvements 2025-05-22 14:18:49 +03:00
8 changed files with 424 additions and 377 deletions

View File

@@ -1,168 +1,168 @@
package me.topchetoeu.j2s.compilation; package me.topchetoeu.j2s.compilation;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Stack; import java.util.Stack;
import java.util.function.Function; import java.util.function.Function;
import me.topchetoeu.j2s.common.Environment; import me.topchetoeu.j2s.common.Environment;
import me.topchetoeu.j2s.common.FunctionBody; import me.topchetoeu.j2s.common.FunctionBody;
import me.topchetoeu.j2s.common.Instruction; import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Key; import me.topchetoeu.j2s.common.Key;
import me.topchetoeu.j2s.common.Location; import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.Instruction.BreakpointType; import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.compilation.CompilationFunctionMap.FunctionMapBuilder; import me.topchetoeu.j2s.compilation.CompilationFunctionMap.FunctionMapBuilder;
import me.topchetoeu.j2s.compilation.control.TryNode; import me.topchetoeu.j2s.compilation.control.TryNode;
import me.topchetoeu.j2s.compilation.scope.FunctionScope; import me.topchetoeu.j2s.compilation.scope.FunctionScope;
import me.topchetoeu.j2s.compilation.scope.Variable; import me.topchetoeu.j2s.compilation.scope.Variable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
public final class CompileResult { public final class CompileResult {
public static final Key<Void> DEBUG_LOG = new Key<>(); public static final Key<Void> DEBUG_LOG = new Key<>();
private FunctionBody body; private FunctionBody body;
public final List<Instruction> instructions; public final List<Instruction> instructions;
public final List<CompileResult> children; public final List<CompileResult> children;
public final Map<FunctionNode, CompileResult> childrenMap = new HashMap<>(); public final Map<FunctionNode, CompileResult> childrenMap = new HashMap<>();
public final Map<FunctionNode, Integer> childrenIndices = new HashMap<>(); public final Map<FunctionNode, Integer> childrenIndices = new HashMap<>();
public final FunctionMapBuilder map; public final FunctionMapBuilder map;
public final Environment env; public final Environment env;
public int length; public int length;
public final FunctionScope scope; public final FunctionScope scope;
public final Map<TryNode, Variable> catchBindings = new HashMap<>(); public final Map<TryNode, Variable> catchBindings = new HashMap<>();
public int temp() { public int temp() {
instructions.add(null); instructions.add(null);
return instructions.size() - 1; return instructions.size() - 1;
} }
public CompileResult add(Instruction instr) { public CompileResult add(Instruction instr) {
instructions.add(instr); instructions.add(instr);
return this; return this;
} }
public CompileResult set(int i, Instruction instr) { public CompileResult set(int i, Instruction instr) {
instructions.set(i, instr); instructions.set(i, instr);
return this; return this;
} }
public int size() { return instructions.size(); } public int size() { return instructions.size(); }
public void setDebug(Location loc, BreakpointType type) { public void setDebug(Location loc, BreakpointType type) {
map.setDebug(loc, type); map.setDebug(loc, type);
} }
public void setLocation(int i, Location loc) { public void setLocation(int i, Location loc) {
map.setLocation(i, loc); map.setLocation(i, loc);
} }
public void setLocationAndDebug(int i, Location loc, BreakpointType type) { public void setLocationAndDebug(int i, Location loc, BreakpointType type) {
map.setLocationAndDebug(i, loc, type); map.setLocationAndDebug(i, loc, type);
} }
public void setDebug(BreakpointType type) { public void setDebug(BreakpointType type) {
setDebug(map.last(), type); setDebug(map.last(), type);
} }
public void setLocation(Location type) { public void setLocation(Location type) {
setLocation(instructions.size() - 1, type); setLocation(instructions.size() - 1, type);
} }
public void setLocationAndDebug(Location loc, BreakpointType type) { public void setLocationAndDebug(Location loc, BreakpointType type) {
setLocationAndDebug(instructions.size() - 1, loc, type); setLocationAndDebug(instructions.size() - 1, loc, type);
} }
public Iterable<CompileResult> all() { public Iterable<CompileResult> all() {
var stack = new Stack<CompileResult>(); var stack = new Stack<CompileResult>();
stack.push(this); stack.push(this);
return () -> new Iterator<CompileResult>() { return () -> new Iterator<CompileResult>() {
@Override public CompileResult next() { @Override public CompileResult next() {
if (stack.empty()) return null; if (stack.empty()) return null;
else { else {
var res = stack.pop(); var res = stack.pop();
for (var child : res.children) { for (var child : res.children) {
stack.push(child); stack.push(child);
} }
return res; return res;
} }
} }
@Override public boolean hasNext() { @Override public boolean hasNext() {
return !stack.empty(); return !stack.empty();
} }
}; };
} }
public CompileResult addChild(FunctionNode node, CompileResult res) { public CompileResult addChild(FunctionNode node, CompileResult res) {
this.children.add(res); this.children.add(res);
this.childrenMap.put(node, res); this.childrenMap.put(node, res);
this.childrenIndices.put(node, this.children.size() - 1); this.childrenIndices.put(node, this.children.size() - 1);
return res; return res;
} }
public Instruction[] instructions() { public Instruction[] instructions() {
return instructions.toArray(new Instruction[0]); return instructions.toArray(new Instruction[0]);
} }
public CompilationFunctionMap map(Function<Location, Location> mapper) { public CompilationFunctionMap map(Function<Location, Location> mapper) {
return map.map(mapper).build(scope.localNames(), scope.capturableNames(), scope.captureNames()); return map.map(mapper).build(scope.localNames(), scope.capturableNames(), scope.captureNames());
} }
public CompilationFunctionMap map() { public CompilationFunctionMap map() {
return map.build(scope.localNames(), scope.capturableNames(), scope.captureNames()); return map.build(scope.localNames(), scope.capturableNames(), scope.captureNames());
} }
public FunctionBody body() { public FunctionBody body() {
if (body != null) return body; if (body != null) return body;
var builtChildren = new FunctionBody[children.size()]; var builtChildren = new FunctionBody[children.size()];
for (var i = 0; i < children.size(); i++) builtChildren[i] = children.get(i).body(); for (var i = 0; i < children.size(); i++) builtChildren[i] = children.get(i).body();
var instrRes = instructions(); var instrRes = instructions();
if (env.has(DEBUG_LOG)) { if (env.has(DEBUG_LOG)) {
System.out.println("================= BODY ================="); System.out.println("================= BODY =================");
System.out.println("LOCALS: " + scope.localsCount()); System.out.println("LOCALS: " + scope.localsCount());
System.out.println("CAPTURABLES: " + scope.capturablesCount()); System.out.println("CAPTURABLES: " + scope.capturablesCount());
System.out.println("CAPTURES: " + scope.capturesCount()); System.out.println("CAPTURES: " + scope.capturesCount());
for (var instr : instrRes) System.out.println(instr); for (var instr : instrRes) System.out.println(instr);
} }
return body = new FunctionBody( return body = new FunctionBody(
scope.localsCount(), scope.capturablesCount(), scope.capturesCount(), scope.localsCount(), scope.capturablesCount(), scope.capturesCount(),
length, instrRes, builtChildren length, instrRes, builtChildren
); );
} }
public CompileResult subtarget() { public CompileResult subtarget() {
return new CompileResult(env, new FunctionScope(scope), this); return new CompileResult(env, new FunctionScope(scope), this);
} }
public CompileResult setEnvironment(Environment env) { public CompileResult setEnvironment(Environment env) {
return new CompileResult(env, scope, this); return new CompileResult(env, scope, this);
} }
/** /**
* Returns a compile result with a child of the environment that relates to the given key. * Returns a compile result with a child of the environment that relates to the given key.
* In essence, this is used to create a compile result which is back at the root environment of the compilation * In essence, this is used to create a compile result which is back at the root environment of the compilation
*/ */
public CompileResult rootEnvironment(Key<Environment> env) { public CompileResult rootEnvironment(Key<Environment> env) {
return new CompileResult(this.env.get(env).child(), scope, this); return new CompileResult(this.env.get(env).child(), scope, this);
} }
public CompileResult subEnvironment() { public CompileResult subEnvironment() {
return new CompileResult(env.child(), scope, this); return new CompileResult(env.child(), scope, this);
} }
public CompileResult(Environment env, FunctionScope scope, int length) { public CompileResult(Environment env, FunctionScope scope, int length) {
this.scope = scope; this.scope = scope;
this.instructions = new ArrayList<>(); this.instructions = new ArrayList<>();
this.children = new LinkedList<>(); this.children = new LinkedList<>();
this.map = CompilationFunctionMap.builder(); this.map = CompilationFunctionMap.builder();
this.env = env; this.env = env;
this.length = length; this.length = length;
} }
private CompileResult(Environment env, FunctionScope scope, CompileResult parent) { private CompileResult(Environment env, FunctionScope scope, CompileResult parent) {
this.scope = scope; this.scope = scope;
this.instructions = parent.instructions; this.instructions = parent.instructions;
this.children = parent.children; this.children = parent.children;
this.map = parent.map; this.map = parent.map;
this.env = env; this.env = env;
} }
} }

View File

@@ -1,112 +1,114 @@
package me.topchetoeu.j2s.compilation; package me.topchetoeu.j2s.compilation;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import me.topchetoeu.j2s.common.Instruction; import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location; import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.Instruction.BreakpointType; import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.compilation.parsing.ParseRes; import me.topchetoeu.j2s.compilation.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing; import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source; import me.topchetoeu.j2s.compilation.parsing.Source;
public class CompoundNode extends Node { public class CompoundNode extends Node {
public final Node[] statements; public final Node[] statements;
public Location end; public Location end;
@Override public void resolve(CompileResult target) { @Override public void resolve(CompileResult target) {
for (var stm : statements) stm.resolve(target); for (var stm : statements) stm.resolve(target);
} }
@Override public void compileFunctions(CompileResult target) { @Override public void compileFunctions(CompileResult target) {
for (var stm : statements) stm.compileFunctions(target); for (var stm : statements) stm.compileFunctions(target);
} }
public void compile(CompileResult target, boolean pollute, BreakpointType type) { public void compile(CompileResult target, boolean pollute, BreakpointType type) {
List<Node> statements = new ArrayList<Node>(); List<Node> statements = new ArrayList<Node>();
for (var stm : this.statements) { for (var stm : this.statements) {
if (stm instanceof FunctionStatementNode func) { if (stm instanceof FunctionStatementNode func) {
func.compile(target, false); func.compile(target, false);
} }
else statements.add(stm); else statements.add(stm);
} }
var polluted = false; var polluted = false;
for (var i = 0; i < statements.size(); i++) { for (var i = 0; i < statements.size(); i++) {
var stm = statements.get(i); var stm = statements.get(i);
if (i != statements.size() - 1) stm.compile(target, false, BreakpointType.STEP_OVER); if (i != statements.size() - 1) stm.compileStatement(target, false, BreakpointType.STEP_OVER);
else stm.compile(target, polluted = pollute, BreakpointType.STEP_OVER); else stm.compileStatement(target, polluted = pollute, BreakpointType.STEP_OVER);
}
target.setDebug(type);
if (!polluted && pollute) { }
target.add(Instruction.pushUndefined());
} if (!polluted && pollute) {
} target.add(Instruction.pushUndefined());
}
public CompoundNode setEnd(Location loc) { }
this.end = loc;
return this; public CompoundNode setEnd(Location loc) {
} this.end = loc;
return this;
public CompoundNode(Location loc, Node ...statements) { }
super(loc);
this.statements = statements; public CompoundNode(Location loc, Node ...statements) {
} super(loc);
this.statements = statements;
public static ParseRes<CompoundNode> parseComma(Source src, int i, Node prev, int precedence) { }
if (precedence > 1) return ParseRes.failed();
public static ParseRes<CompoundNode> parseComma(Source src, int i, Node prev, int precedence) {
var n = Parsing.skipEmpty(src, i); if (precedence > 1) return ParseRes.failed();
var loc = src.loc(i + n);
var n = Parsing.skipEmpty(src, i);
if (!src.is(i + n, ",")) return ParseRes.failed(); var loc = src.loc(i + n);
n++;
if (!src.is(i + n, ",")) return ParseRes.failed();
var curr = JavaScript.parseExpression(src, i + n, 2); n++;
if (!curr.isSuccess()) return curr.chainError(src.loc(i + n), "Expected a value after the comma");
n += curr.n; var curr = JavaScript.parseExpression(src, i + n, 2);
if (!curr.isSuccess()) return curr.chainError(src.loc(i + n), "Expected a value after the comma");
if (prev instanceof CompoundNode comp) { n += curr.n;
var children = new ArrayList<Node>();
children.addAll(Arrays.asList(comp.statements)); if (prev instanceof CompoundNode comp) {
children.add(curr.result); var children = new ArrayList<Node>();
children.addAll(Arrays.asList(comp.statements));
return ParseRes.res(new CompoundNode(loc, children.toArray(new Node[0])), n); children.add(curr.result);
}
else return ParseRes.res(new CompoundNode(loc, prev, curr.result), n); return ParseRes.res(new CompoundNode(loc, children.toArray(new Node[0])), n);
} }
public static ParseRes<CompoundNode> parse(Source src, int i) { else return ParseRes.res(new CompoundNode(loc, prev, curr.result), n);
var n = Parsing.skipEmpty(src, i); }
var loc = src.loc(i + n); public static ParseRes<CompoundNode> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
if (!src.is(i + n, "{")) return ParseRes.failed(); var loc = src.loc(i + n);
n++;
if (!src.is(i + n, "{")) return ParseRes.failed();
var statements = new ArrayList<Node>(); n++;
while (true) { var statements = new ArrayList<Node>();
n += Parsing.skipEmpty(src, i + n);
while (true) {
if (src.is(i + n, "}")) { n += Parsing.skipEmpty(src, i + n);
n++;
break; if (src.is(i + n, "}")) {
} n++;
if (src.is(i + n, ";")) { break;
n++; }
continue; if (src.is(i + n, ";")) {
} n++;
continue;
var res = JavaScript.parseStatement(src, i + n); }
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a statement");
n += res.n; var res = JavaScript.parseStatement(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a statement");
statements.add(res.result); n += res.n;
}
statements.add(res.result);
return ParseRes.res(new CompoundNode(loc, statements.toArray(new Node[0])).setEnd(src.loc(i + n - 1)), n); }
}
} return ParseRes.res(new CompoundNode(loc, statements.toArray(new Node[0])).setEnd(src.loc(i + n - 1)), n);
}
}

View File

@@ -1,28 +1,33 @@
package me.topchetoeu.j2s.compilation; package me.topchetoeu.j2s.compilation;
import me.topchetoeu.j2s.common.Location; import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.Instruction.BreakpointType; import me.topchetoeu.j2s.common.Instruction.BreakpointType;
public abstract class Node { public abstract class Node {
private Location loc; private Location loc;
public void resolve(CompileResult target) {} public void resolve(CompileResult target) {}
public void compile(CompileResult target, boolean pollute, BreakpointType type) { public void compile(CompileResult target, boolean pollute, BreakpointType type) {
int start = target.size(); compileStatement(target, pollute, type);
compile(target, pollute); }
if (target.size() != start) target.setLocationAndDebug(start, loc(), type); public void compileStatement(CompileResult target, boolean pollute, BreakpointType type) {
} int start = target.size();
public void compile(CompileResult target, boolean pollute) { compile(target, pollute);
compile(target, pollute, BreakpointType.NONE); if (target.size() != start) {
} target.setLocationAndDebug(start, loc(), type);
}
public abstract void compileFunctions(CompileResult target); }
public void compile(CompileResult target, boolean pollute) {
public Location loc() { return loc; } compile(target, pollute, BreakpointType.NONE);
public void setLoc(Location loc) { this.loc = loc; } }
protected Node(Location loc) { public abstract void compileFunctions(CompileResult target);
this.loc = loc;
} public Location loc() { return loc; }
} public void setLoc(Location loc) { this.loc = loc; }
protected Node(Location loc) {
this.loc = loc;
}
}

View File

@@ -423,7 +423,7 @@ public class SimpleDebugger implements Debugger {
desc.append("..."); desc.append("...");
break; break;
} }
if (arr.has(i)) { if (arr.has(i)) {
try { try {
var curr = arr.get(i); var curr = arr.get(i);
@@ -442,7 +442,7 @@ public class SimpleDebugger implements Debugger {
desc.append("<empty>"); desc.append("<empty>");
} }
} }
desc.append("]"); desc.append("]");
} }
@@ -814,7 +814,6 @@ public class SimpleDebugger implements Debugger {
var cond = msg.params.string("condition", "").trim(); var cond = msg.params.string("condition", "").trim();
if (cond.equals("")) cond = null; if (cond.equals("")) cond = null;
if (cond != null) cond = "(" + cond + ")";
Pattern regex; Pattern regex;
@@ -1064,7 +1063,7 @@ public class SimpleDebugger implements Debugger {
} }
mappings.put(body, map); mappings.put(body, map);
} }
private boolean instructionLock; private boolean instructionLock;
@Override public boolean onInstruction(Environment env, Frame cf, Instruction instruction, Value returnVal, EngineException error, boolean caught) { @Override public boolean onInstruction(Environment env, Frame cf, Instruction instruction, Value returnVal, EngineException error, boolean caught) {
@@ -1077,16 +1076,16 @@ public class SimpleDebugger implements Debugger {
Location loc; Location loc;
DebugFrame frame; DebugFrame frame;
BreakpointType bptType; BreakpointType bptType;
frame = getFrame(cf); frame = getFrame(cf);
var map = DebugHandler.get(env).getMapOrEmpty(env, frame.frame.function); var map = DebugHandler.get(env).getMapOrEmpty(env, frame.frame.function);
frame.updateLoc(map.toLocation(frame.frame.codePtr)); frame.updateLoc(map.toLocation(frame.frame.codePtr));
loc = frame.location; loc = frame.location;
bptType = map.getBreakpoint(frame.frame.codePtr); bptType = map.getBreakpoint(frame.frame.codePtr);
isBreakpointable = loc != null && (bptType.shouldStepIn()); isBreakpointable = loc != null && (bptType.shouldStepIn());
if (error != null && (execptionType == CatchType.ALL || execptionType == CatchType.UNCAUGHT && !caught)) { if (error != null && (execptionType == CatchType.ALL || execptionType == CatchType.UNCAUGHT && !caught)) {
pauseException(env, error); pauseException(env, error);
} }
@@ -1114,19 +1113,19 @@ public class SimpleDebugger implements Debugger {
instruction.params.length == 1 && instruction.params.length == 1 &&
instruction.get(0).equals("debug") instruction.get(0).equals("debug")
) pauseDebug(env, null); ) pauseDebug(env, null);
synchronized (this) { synchronized (this) {
} }
while (enabled) { while (enabled) {
synchronized (this) { synchronized (this) {
switch (state) { switch (state) {
case PAUSED_EXCEPTION: case PAUSED_EXCEPTION:
case PAUSED_NORMAL: break; case PAUSED_NORMAL: break;
case STEPPING_OUT: case STEPPING_OUT:
case RESUMED: return false; case RESUMED: return false;
case STEPPING_IN: case STEPPING_IN:
case STEPPING_OVER: case STEPPING_OVER:
if (stepOutFrame.frame == frame.frame) { if (stepOutFrame.frame == frame.frame) {
@@ -1135,7 +1134,7 @@ public class SimpleDebugger implements Debugger {
continue; continue;
} }
else if (stepOutPtr != frame.frame.codePtr) { else if (stepOutPtr != frame.frame.codePtr) {
if (state == State.STEPPING_IN && bptType.shouldStepIn()) { if (state == State.STEPPING_IN && bptType.shouldStepIn()) {
pauseDebug(env, null); pauseDebug(env, null);
break; break;
@@ -1149,7 +1148,7 @@ public class SimpleDebugger implements Debugger {
return false; return false;
} }
} }
try { try {
synchronized (updateNotifier) { synchronized (updateNotifier) {
updateNotifier.wait(); updateNotifier.wait();

View File

@@ -1,10 +1,11 @@
const map: number[] = []; function decodeBase64(val: number) {
let j = 0; if (val >= 65 && val <= 90) return val - 65;
else if (val >= 97 && val <= 122) return val - 97 + 26;
for (let i = 65; i <= 90; i++) map[i] = j++; else if (val >= 48 && val <= 57) return val - 48 + 52;
for (let i = 97; i <= 122; i++) map[i] = j++; else if (val == 43) return 62;
map[43] = j++; else if (val == 47) return 63;
map[47] = j++; else throw "Invalid Base64 char";
}
export function decodeVLQ(val: string): number[][][] { export function decodeVLQ(val: string): number[][][] {
const lines: number[][][] = []; const lines: number[][][] = [];
@@ -30,14 +31,14 @@ export function decodeVLQ(val: string): number[][][] {
for (let i = 0; i < el.length;) { for (let i = 0; i < el.length;) {
let sign = 1; let sign = 1;
let curr = map[el.charCodeAt(i++)]; let curr = decodeBase64(el.charCodeAt(i++));
let cont = (curr & 0x20) === 0x20; let cont = (curr & 0x20) === 0x20;
if ((curr & 1) === 1) sign = -1; if ((curr & 1) === 1) sign = -1;
let res = (curr & 0b11110) >> 1; let res = (curr & 0b11110) >> 1;
let n = 4; let n = 4;
for (; i < el.length && cont;) { for (; i < el.length && cont;) {
curr = map[el.charCodeAt(i++)]; curr = decodeBase64(el.charCodeAt(i++));
cont = (curr & 0x20) == 0x20; cont = (curr & 0x20) == 0x20;
res |= (curr & 0b11111) << n; res |= (curr & 0b11111) << n;
n += 5; n += 5;
@@ -99,7 +100,7 @@ export class VLQSourceMap {
while (true) { while (true) {
const done = b - a <= 1; const done = b - a <= 1;
const mid = (a + b) >> 1; const mid = (a + b) >> 1;
const el = line[mid]; const el = line[mid];
@@ -134,7 +135,6 @@ export class VLQSourceMap {
let originalRow = 0; let originalRow = 0;
let originalCol = 0; let originalCol = 0;
let originalFile = 0; let originalFile = 0;
const lastCols = new Set<number>();
for (let compiledRow = 0; compiledRow < mapping.length; compiledRow++) { for (let compiledRow = 0; compiledRow < mapping.length; compiledRow++) {
const line: [start: number, dst: Location][] = file[compiledRow] = []; const line: [start: number, dst: Location][] = file[compiledRow] = [];
@@ -149,10 +149,7 @@ export class VLQSourceMap {
originalRow += rawSeg.length > 2 ? rawSeg[2] : 0; originalRow += rawSeg.length > 2 ? rawSeg[2] : 0;
originalCol += rawSeg.length > 3 ? rawSeg[3] : 0; originalCol += rawSeg.length > 3 ? rawSeg[3] : 0;
if (!lastCols.has(compiledCol)) { line[line.length] = [compiledCol, [filenames[originalFile], originalRow, originalCol]];
line[line.length] = [compiledCol, [filenames[originalFile], originalRow, originalCol]];
}
lastCols.add(compiledCol);
} }
line.sort((a, b) => a[0] - b[0]); line.sort((a, b) => a[0] - b[0]);

View File

@@ -7,6 +7,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@@ -41,55 +42,61 @@ public class SimpleRepl {
static Key<InputStream> STDIN = new Key<>(); static Key<InputStream> STDIN = new Key<>();
static int j = 0; static int j = 0;
static String[] args; static String[] files;
static boolean inspect = false;
static int inspectPort = 9229;
private static void reader() { private static void reader() {
try { try {
server = new DebugServer(); if (inspect) {
debugTask = server.start(new InetSocketAddress("127.0.0.1", 9229), true); server = new DebugServer();
server.targets.put("default", (socket, req) -> new SimpleDebugger(socket) debugTask = server.start(new InetSocketAddress("127.0.0.1", inspectPort), true);
.attach((SimpleDebugHandler)DebugHandler.get(environment)) server.targets.put("default", (socket, req) -> new SimpleDebugger(socket)
); .attach((SimpleDebugHandler)DebugHandler.get(environment))
);
System.out.println("Debug server started at localhost:" + inspectPort);
}
System.out.println(String.format("Running %s v%s by %s", Metadata.name(), Metadata.version(), Metadata.author())); System.out.println(String.format("Running %s v%s by %s", Metadata.name(), Metadata.version(), Metadata.author()));
for (var arg : args) { if (files.length > 0) {
var file = new File(arg); for (var arg : files) {
var raw = Reading.streamToString(new FileInputStream(file)); var file = new File(arg);
var raw = Reading.streamToString(new FileInputStream(file));
try {
try { try {
var res = engine.pushMsg( try {
false, environment, var res = engine.pushMsg(
Filename.fromFile(file), raw, null false, environment,
).get(); Filename.fromFile(file), raw, null
).get();
System.err.println(res.toReadable(environment)); System.err.println(res.toReadable(environment));
}
catch (ExecutionException e) { throw e.getCause(); }
} }
catch (ExecutionException e) { throw e.getCause(); } catch (EngineException | SyntaxException e) { System.err.println(Value.errorToReadable(environment, e, null)); }
} }
catch (EngineException | SyntaxException e) { System.err.println(Value.errorToReadable(environment, e, null)); }
} }
else {
for (var i = 0; ; i++) {
var raw = Reading.readline();
for (var i = 0; ; i++) { if (raw == null) break;
var raw = Reading.readline();
if (raw == null) break;
try {
try { try {
var res = engine.pushMsg( try {
false, environment, var res = engine.pushMsg(
new Filename(Metadata.name(), "repl/" + i + ".js"), raw, false, environment,
Value.UNDEFINED new Filename(Metadata.name(), "repl/" + i + ".js"), raw,
).get(); Value.UNDEFINED
System.err.println(res.toReadable(environment)); ).get();
System.err.println(res.toReadable(environment));
}
catch (ExecutionException e) { throw e.getCause(); }
} }
catch (ExecutionException e) { throw e.getCause(); } catch (EngineException | SyntaxException e) { System.err.println(Value.errorToReadable(environment, e, null)); }
} }
catch (EngineException | SyntaxException e) { System.err.println(Value.errorToReadable(environment, e, null)); }
} }
} }
catch (EngineException | SyntaxException e) { System.err.println(Value.errorToReadable(environment, e, null)); } catch (EngineException | SyntaxException e) { System.err.println(Value.errorToReadable(environment, e, null)); }
@@ -104,11 +111,30 @@ public class SimpleRepl {
} }
} }
private static Environment createESEnv() { private static Environment createESEnv(String compiler) {
var env = StdLib.apply(null); var env = StdLib.apply(null);
env.add(EventLoop.KEY, engine); env.add(EventLoop.KEY, engine);
env.add(DebugHandler.KEY, new SimpleDebugHandler()); env.add(DebugHandler.KEY, new SimpleDebugHandler());
env.add(Compiler.KEY, Compilers.chainTranspilers(Compilers.jsCompiler(), env, Compilers::babelCompiler));
switch (compiler) {
case "typescript":
case "ts":
env.add(Compiler.KEY, Compilers.chainTranspilers(Compilers.jsCompiler(), env, Compilers::typescriptCompiler));
break;
case "coffeescript":
case "cs":
env.add(Compiler.KEY, Compilers.chainTranspilers(Compilers.jsCompiler(), env, Compilers::babelCompiler, Compilers::coffeescriptCompiler));
break;
case "babel":
case "es6":
case "esnext":
env.add(Compiler.KEY, Compilers.chainTranspilers(Compilers.jsCompiler(), env, Compilers::babelCompiler));
break;
default:
case "js":
env.add(Compiler.KEY, Compilers.jsCompiler());
break;
}
var glob = Value.global(env); var glob = Value.global(env);
@@ -137,10 +163,29 @@ public class SimpleRepl {
} }
public static void main(String args[]) throws InterruptedException { public static void main(String args[]) throws InterruptedException {
SimpleRepl.args = args; var compiler = "js";
var files = new ArrayList<String>();
for (String arg : args) {
if (arg.startsWith("--lang=")) {
compiler = arg.substring(7);
}
else if (arg.equals("--inspect")) {
inspect = true;
}
else if (arg.startsWith("--inspect=")) {
inspect = true;
inspectPort = Integer.parseInt(arg.substring(10));
}
else {
files.add(arg);
}
}
SimpleRepl.files = files.toArray(new String[0]);
var reader = new Thread(SimpleRepl::reader); var reader = new Thread(SimpleRepl::reader);
environment = createESEnv(); environment = createESEnv(compiler);
initEngine(); initEngine();
@@ -150,6 +195,6 @@ public class SimpleRepl {
reader.join(); reader.join();
engineTask.interrupt(); engineTask.interrupt();
debugTask.interrupt(); if (debugTask != null) debugTask.interrupt();
} }
} }

View File

@@ -93,7 +93,7 @@ public interface DebugHandler {
} }
public default FunctionMap getMapOrEmpty(Environment env, FunctionValue func) { public default FunctionMap getMapOrEmpty(Environment env, FunctionValue func) {
if (func instanceof CodeFunction codeFunc) return getMapOrEmpty(env, codeFunc.body); if (func instanceof CodeFunction codeFunc) return getMapOrEmpty(env, codeFunc.body);
else return null; else return FunctionMap.EMPTY;
} }
public static DebugHandler get(Environment exts) { public static DebugHandler get(Environment exts) {

View File

@@ -195,7 +195,6 @@ public abstract class ArrayLikeValue extends ObjectValue {
res.add(" " + line); res.add(" " + line);
} }
} }
res.set(res.size() - 1, res.getLast().substring(0, res.getLast().length() - 1));
res.add("]"); res.add("]");
return res; return res;