cant be fucked to split this one up
This commit is contained in:
parent
fe86123f0f
commit
34434965d2
12
src/assets/js/bootstrap.js
vendored
12
src/assets/js/bootstrap.js
vendored
@ -1,8 +1,8 @@
|
||||
(function (ts, env, libs) {
|
||||
var src = '', version = 0;
|
||||
var lib = libs.concat([
|
||||
'declare const exit: never;',
|
||||
'declare const go: any;',
|
||||
'declare function exit(): never;',
|
||||
'declare function go(): any;',
|
||||
'declare function getTsDeclarations(): string[];'
|
||||
]).join('');
|
||||
var libSnapshot = ts.ScriptSnapshot.fromString(lib);
|
||||
@ -64,6 +64,7 @@
|
||||
|
||||
if (!environments[env.id]) environments[env.id] = []
|
||||
declSnapshots = environments[env.id];
|
||||
debugger;
|
||||
var emit = service.getEmitOutput("/src.ts");
|
||||
|
||||
var diagnostics = []
|
||||
@ -97,11 +98,8 @@
|
||||
if (declaration !== '') declSnapshots.push(ts.ScriptSnapshot.fromString(declaration));
|
||||
return val;
|
||||
},
|
||||
mapChain: compiled.mapChain.concat(JSON.stringify({
|
||||
file: filename,
|
||||
sources: [filename],
|
||||
mappings: map.mappings,
|
||||
}))
|
||||
breakpoints: compiled.breakpoints,
|
||||
mapChain: compiled.mapChain.concat(map.mappings),
|
||||
};
|
||||
}
|
||||
|
||||
|
15
src/assets/js/lib.d.ts
vendored
15
src/assets/js/lib.d.ts
vendored
@ -19,12 +19,7 @@ type Extract<T, U> = T extends U ? T : never;
|
||||
type Record<KeyT extends string | number | symbol, ValT> = { [x in KeyT]: ValT }
|
||||
type ReplaceFunc = (match: string, ...args: any[]) => string;
|
||||
|
||||
type PromiseFulfillFunc<T> = (val: T) => void;
|
||||
type PromiseThenFunc<T, NextT> = (val: T) => NextT;
|
||||
type PromiseRejectFunc = (err: unknown) => void;
|
||||
type PromiseFunc<T> = (resolve: PromiseFulfillFunc<T>, reject: PromiseRejectFunc) => void;
|
||||
|
||||
type PromiseResult<T> ={ type: 'fulfilled'; value: T; } | { type: 'rejected'; reason: any; }
|
||||
type PromiseResult<T> = { type: 'fulfilled'; value: T; } | { type: 'rejected'; reason: any; }
|
||||
|
||||
// wippidy-wine, this code is now mine :D
|
||||
type Awaited<T> =
|
||||
@ -46,8 +41,7 @@ type IteratorReturnResult<TReturn> =
|
||||
type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;
|
||||
|
||||
interface Thenable<T> {
|
||||
then<NextT>(onFulfilled: PromiseThenFunc<T, NextT>, onRejected?: PromiseRejectFunc): Promise<Awaited<NextT>>;
|
||||
then(onFulfilled: undefined, onRejected?: PromiseRejectFunc): Promise<T>;
|
||||
then<NextT = void>(onFulfilled?: (val: T) => NextT, onRejected?: (err: any) => NextT): Promise<Awaited<NextT>>;
|
||||
}
|
||||
|
||||
interface RegExpResultIndices extends Array<[number, number]> {
|
||||
@ -465,13 +459,13 @@ interface SymbolConstructor {
|
||||
}
|
||||
|
||||
interface Promise<T> extends Thenable<T> {
|
||||
catch(func: PromiseRejectFunc): Promise<T>;
|
||||
catch<ResT = void>(func: (err: unknown) => ResT): Promise<ResT>;
|
||||
finally(func: () => void): Promise<T>;
|
||||
}
|
||||
interface PromiseConstructor {
|
||||
prototype: Promise<any>;
|
||||
|
||||
new <T>(func: PromiseFunc<T>): Promise<Awaited<T>>;
|
||||
new <T>(func: (res: (val: T) => void, rej: (err: unknown) => void) => void): Promise<Awaited<T>>;
|
||||
resolve<T>(val: T): Promise<Awaited<T>>;
|
||||
reject(val: any): Promise<never>;
|
||||
|
||||
@ -544,6 +538,7 @@ declare var Error: ErrorConstructor;
|
||||
declare var RangeError: RangeErrorConstructor;
|
||||
declare var TypeError: TypeErrorConstructor;
|
||||
declare var SyntaxError: SyntaxErrorConstructor;
|
||||
declare var self: typeof globalThis;
|
||||
|
||||
declare class Map<KeyT, ValueT> {
|
||||
public [Symbol.iterator](): IterableIterator<[KeyT, ValueT]>;
|
||||
|
170093
src/assets/js/ts.js
170093
src/assets/js/ts.js
File diff suppressed because one or more lines are too long
@ -1,4 +1,4 @@
|
||||
package me.topchetoeu.jscript.filesystem;
|
||||
package me.topchetoeu.jscript;
|
||||
|
||||
public class Buffer {
|
||||
private byte[] data;
|
@ -71,4 +71,23 @@ public class Location implements Comparable<Location> {
|
||||
this.start = start;
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
public static Location parse(String raw) {
|
||||
int i0 = -1, i1 = -1;
|
||||
for (var i = raw.length() - 1; i >= 0; i--) {
|
||||
if (raw.charAt(i) == ':') {
|
||||
if (i1 == -1) i1 = i;
|
||||
else if (i0 == -1) {
|
||||
i0 = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Location(
|
||||
Integer.parseInt(raw.substring(i0 + 1, i1)),
|
||||
Integer.parseInt(raw.substring(i1 + 1)),
|
||||
Filename.parse(raw.substring(0, i0))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ import me.topchetoeu.jscript.engine.Environment;
|
||||
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;
|
||||
@ -101,11 +103,11 @@ public class Main {
|
||||
private static void initEnv() {
|
||||
environment = Internals.apply(environment);
|
||||
|
||||
environment.global.define("exit", _ctx -> {
|
||||
environment.global.define(false, new NativeFunction("exit", (_ctx, th, args) -> {
|
||||
exited = true;
|
||||
throw new InterruptException();
|
||||
});
|
||||
environment.global.define("go", _ctx -> {
|
||||
}));
|
||||
environment.global.define(false, new NativeFunction("go", (_ctx, th, args) -> {
|
||||
try {
|
||||
var f = Path.of("do.js");
|
||||
var func = _ctx.compile(new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f)));
|
||||
@ -114,7 +116,7 @@ public class Main {
|
||||
catch (IOException e) {
|
||||
throw new EngineException("Couldn't open do.js");
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
environment.filesystem.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE));
|
||||
environment.filesystem.protocols.put("file", new PhysicalFilesystem(Path.of(".").toAbsolutePath()));
|
||||
@ -127,6 +129,7 @@ public class Main {
|
||||
private static void initTypescript() {
|
||||
try {
|
||||
var tsEnv = Internals.apply(new Environment(null, null, null));
|
||||
tsEnv.global.define(null, "module", false, new ObjectValue());
|
||||
var bsEnv = Internals.apply(new Environment(null, null, null));
|
||||
|
||||
engine.pushMsg(
|
||||
@ -140,7 +143,7 @@ public class Main {
|
||||
|
||||
engine.pushMsg(
|
||||
false, ctx,
|
||||
new Filename("jscript", "internals/bootstrap.js"), Reading.resourceToString("js/bootstrap.js"), null,
|
||||
new Filename("jscript", "bootstrap.js"), Reading.resourceToString("js/bootstrap.js"), null,
|
||||
tsEnv.global.get(ctx, "ts"), environment, new ArrayValue(null, Reading.resourceToString("js/lib.d.ts"))
|
||||
).await();
|
||||
}
|
||||
|
21
src/me/topchetoeu/jscript/compilation/CalculateResult.java
Normal file
21
src/me/topchetoeu/jscript/compilation/CalculateResult.java
Normal file
@ -0,0 +1,21 @@
|
||||
package me.topchetoeu.jscript.compilation;
|
||||
|
||||
import me.topchetoeu.jscript.engine.values.Values;
|
||||
|
||||
public final class CalculateResult {
|
||||
public final boolean exists;
|
||||
public final Object value;
|
||||
|
||||
public final boolean isTruthy() {
|
||||
return exists && Values.toBoolean(value);
|
||||
}
|
||||
|
||||
public CalculateResult(Object value) {
|
||||
this.exists = true;
|
||||
this.value = value;
|
||||
}
|
||||
public CalculateResult() {
|
||||
this.exists = false;
|
||||
this.value = null;
|
||||
}
|
||||
}
|
@ -1,28 +1,44 @@
|
||||
package me.topchetoeu.jscript.compilation;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
import java.util.Vector;
|
||||
|
||||
import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.engine.Environment;
|
||||
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
||||
|
||||
public class CompileTarget {
|
||||
public final Vector<Instruction> target = new Vector<>();
|
||||
public final Map<Long, FunctionBody> functions;
|
||||
public final TreeSet<Location> breakpoints;
|
||||
private final HashMap<Location, Instruction> bpToInstr = new HashMap<>();
|
||||
private BreakpointType queueType = BreakpointType.NONE;
|
||||
private Location queueLoc = null;
|
||||
|
||||
public Instruction add(Instruction instr) {
|
||||
target.add(instr);
|
||||
if (queueType != BreakpointType.NONE) setDebug(queueType);
|
||||
if (queueLoc != null) instr.locate(queueLoc);
|
||||
queueType = BreakpointType.NONE;
|
||||
queueLoc = null;
|
||||
return instr;
|
||||
}
|
||||
public Instruction set(int i, Instruction instr) {
|
||||
return target.set(i, instr);
|
||||
}
|
||||
public void setDebug(int i) {
|
||||
public void setDebug(int i, BreakpointType type) {
|
||||
var instr = target.get(i);
|
||||
instr.breakpoint = type;
|
||||
breakpoints.add(target.get(i).location);
|
||||
|
||||
var old = bpToInstr.put(instr.location, instr);
|
||||
if (old != null) old.breakpoint = BreakpointType.NONE;
|
||||
}
|
||||
public void setDebug() {
|
||||
setDebug(target.size() - 1);
|
||||
public void setDebug(BreakpointType type) {
|
||||
setDebug(target.size() - 1, type);
|
||||
}
|
||||
public Instruction get(int i) {
|
||||
return target.get(i);
|
||||
@ -33,8 +49,22 @@ public class CompileTarget {
|
||||
else return target.get(target.size() - 1).location;
|
||||
}
|
||||
|
||||
public void queueDebug(BreakpointType type) {
|
||||
queueType = type;
|
||||
}
|
||||
public void queueDebug(BreakpointType type, Location loc) {
|
||||
queueType = type;
|
||||
}
|
||||
|
||||
public Instruction[] array() { return target.toArray(Instruction[]::new); }
|
||||
|
||||
public FunctionBody body() {
|
||||
return functions.get(0l);
|
||||
}
|
||||
public CodeFunction func(Environment env) {
|
||||
return new CodeFunction(env, "", body());
|
||||
}
|
||||
|
||||
public CompileTarget(Map<Long, FunctionBody> functions, TreeSet<Location> breakpoints) {
|
||||
this.functions = functions;
|
||||
this.breakpoints = breakpoints;
|
||||
|
@ -1,63 +1,53 @@
|
||||
package me.topchetoeu.jscript.compilation;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.compilation.control.ContinueStatement;
|
||||
import me.topchetoeu.jscript.compilation.control.ReturnStatement;
|
||||
import me.topchetoeu.jscript.compilation.control.ThrowStatement;
|
||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
|
||||
public class CompoundStatement extends Statement {
|
||||
public final Statement[] statements;
|
||||
public final boolean separateFuncs;
|
||||
public Location end;
|
||||
|
||||
@Override public boolean pure() {
|
||||
for (var stm : statements) {
|
||||
if (!stm.pure()) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void declare(ScopeRecord varsScope) {
|
||||
for (var stm : statements) {
|
||||
stm.declare(varsScope);
|
||||
}
|
||||
for (var stm : statements) stm.declare(varsScope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
for (var stm : statements) {
|
||||
if (stm instanceof FunctionStatement) stm.compile(target, scope, false);
|
||||
public void compileWithDebug(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType type) {
|
||||
if (separateFuncs) for (var stm : statements) {
|
||||
if (stm instanceof FunctionStatement && ((FunctionStatement)stm).statement) {
|
||||
stm.compile(target, scope, false);
|
||||
}
|
||||
}
|
||||
|
||||
var polluted = false;
|
||||
|
||||
for (var i = 0; i < statements.length; i++) {
|
||||
var stm = statements[i];
|
||||
|
||||
if (stm instanceof FunctionStatement) continue;
|
||||
if (i != statements.length - 1) stm.compileWithDebug(target, scope, false);
|
||||
else stm.compileWithDebug(target, scope, pollute);
|
||||
if (separateFuncs && stm instanceof FunctionStatement) continue;
|
||||
if (i != statements.length - 1) stm.compileWithDebug(target, scope, false, BreakpointType.STEP_OVER);
|
||||
else stm.compileWithDebug(target, scope, polluted = pollute, BreakpointType.STEP_OVER);
|
||||
}
|
||||
|
||||
if (end != null) {
|
||||
target.add(Instruction.nop(end));
|
||||
target.setDebug();
|
||||
if (!polluted && pollute) {
|
||||
target.add(Instruction.loadValue(loc(), null));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement optimize() {
|
||||
var res = new Vector<Statement>(statements.length);
|
||||
|
||||
for (var i = 0; i < statements.length; i++) {
|
||||
var stm = statements[i].optimize();
|
||||
if (i < statements.length - 1 && stm.pure()) continue;
|
||||
res.add(stm);
|
||||
if (
|
||||
stm instanceof ContinueStatement ||
|
||||
stm instanceof ReturnStatement ||
|
||||
stm instanceof ThrowStatement ||
|
||||
stm instanceof ContinueStatement
|
||||
) break;
|
||||
}
|
||||
|
||||
if (res.size() == 1) return res.get(0);
|
||||
else return new CompoundStatement(loc(), res.toArray(Statement[]::new));
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
compileWithDebug(target, scope, pollute, BreakpointType.STEP_IN);
|
||||
}
|
||||
|
||||
public CompoundStatement setEnd(Location loc) {
|
||||
@ -65,8 +55,9 @@ public class CompoundStatement extends Statement {
|
||||
return this;
|
||||
}
|
||||
|
||||
public CompoundStatement(Location loc, Statement ...statements) {
|
||||
public CompoundStatement(Location loc, boolean separateFuncs, Statement ...statements) {
|
||||
super(loc);
|
||||
this.separateFuncs = separateFuncs;
|
||||
this.statements = statements;
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +0,0 @@
|
||||
package me.topchetoeu.jscript.compilation;
|
||||
|
||||
import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.compilation.values.ConstantStatement;
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
|
||||
public class DiscardStatement extends Statement {
|
||||
public final Statement value;
|
||||
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
value.compile(target, scope, false);
|
||||
|
||||
}
|
||||
@Override
|
||||
public Statement optimize() {
|
||||
if (value == null) return this;
|
||||
var val = value.optimize();
|
||||
if (val.pure()) return new ConstantStatement(loc(), null);
|
||||
else return new DiscardStatement(loc(), val);
|
||||
}
|
||||
|
||||
public DiscardStatement(Location loc, Statement val) {
|
||||
super(loc);
|
||||
this.value = val;
|
||||
}
|
||||
}
|
@ -10,7 +10,8 @@ public class Instruction {
|
||||
THROW,
|
||||
THROW_SYNTAX,
|
||||
DELETE,
|
||||
TRY,
|
||||
TRY_START,
|
||||
TRY_END,
|
||||
NOP,
|
||||
|
||||
CALL,
|
||||
@ -86,10 +87,29 @@ public class Instruction {
|
||||
// this(false);
|
||||
// }
|
||||
}
|
||||
public static enum BreakpointType {
|
||||
NONE,
|
||||
STEP_OVER,
|
||||
STEP_IN;
|
||||
|
||||
public boolean shouldStepIn() {
|
||||
return this != NONE;
|
||||
}
|
||||
public boolean shouldStepOver() {
|
||||
return this == STEP_OVER;
|
||||
}
|
||||
}
|
||||
|
||||
public final Type type;
|
||||
public final Object[] params;
|
||||
public Location location;
|
||||
public BreakpointType breakpoint = BreakpointType.NONE;
|
||||
|
||||
public Instruction setDbgData(Instruction other) {
|
||||
this.location = other.location;
|
||||
this.breakpoint = other.breakpoint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Instruction locate(Location loc) {
|
||||
this.location = loc;
|
||||
@ -129,8 +149,11 @@ public class Instruction {
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
public static Instruction tryInstr(Location loc, int n, int catchN, int finallyN) {
|
||||
return new Instruction(loc, Type.TRY, n, catchN, finallyN);
|
||||
public static Instruction tryStart(Location loc, int catchStart, int finallyStart, int end) {
|
||||
return new Instruction(loc, Type.TRY_START, catchStart, finallyStart, end);
|
||||
}
|
||||
public static Instruction tryEnd(Location loc) {
|
||||
return new Instruction(loc, Type.TRY_END);
|
||||
}
|
||||
public static Instruction throwInstr(Location loc) {
|
||||
return new Instruction(loc, Type.THROW);
|
||||
|
@ -1,6 +1,7 @@
|
||||
package me.topchetoeu.jscript.compilation;
|
||||
|
||||
import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
|
||||
public abstract class Statement {
|
||||
@ -9,12 +10,15 @@ public abstract class Statement {
|
||||
public boolean pure() { return false; }
|
||||
public abstract void compile(CompileTarget target, ScopeRecord scope, boolean pollute);
|
||||
public void declare(ScopeRecord varsScope) { }
|
||||
public Statement optimize() { return this; }
|
||||
|
||||
public void compileWithDebug(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
public void compileWithDebug(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType type) {
|
||||
int start = target.size();
|
||||
compile(target, scope, pollute);
|
||||
if (target.size() != start) target.setDebug(start);
|
||||
|
||||
if (target.size() != start) {
|
||||
target.get(start).locate(loc());
|
||||
target.setDebug(start, type);
|
||||
}
|
||||
}
|
||||
|
||||
public Location loc() { return _loc; }
|
||||
|
@ -0,0 +1,18 @@
|
||||
package me.topchetoeu.jscript.compilation;
|
||||
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
||||
|
||||
public class ThrowSyntaxStatement extends Statement {
|
||||
public final String name;
|
||||
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
target.add(Instruction.throwSyntax(loc(), name));
|
||||
}
|
||||
|
||||
public ThrowSyntaxStatement(SyntaxException e) {
|
||||
super(e.loc);
|
||||
this.name = e.msg;
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package me.topchetoeu.jscript.compilation;
|
||||
import java.util.List;
|
||||
|
||||
import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
|
||||
@ -32,18 +33,15 @@ public class VariableDeclareStatement extends Statement {
|
||||
for (var entry : values) {
|
||||
if (entry.name == null) continue;
|
||||
var key = scope.getKey(entry.name);
|
||||
int start = target.size();
|
||||
|
||||
if (key instanceof String) target.add(Instruction.makeVar(entry.location, (String)key));
|
||||
|
||||
if (entry.value != null) {
|
||||
FunctionStatement.compileWithName(entry.value, target, scope, true, entry.name);
|
||||
target.queueDebug(BreakpointType.STEP_OVER, entry.location);
|
||||
if (entry.value != null) FunctionStatement.compileWithName(entry.value, target, scope, true, entry.name);
|
||||
else target.add(Instruction.loadValue(entry.location, null));
|
||||
target.add(Instruction.storeVar(entry.location, key));
|
||||
}
|
||||
|
||||
if (target.size() != start) target.setDebug(start);
|
||||
}
|
||||
|
||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||
}
|
||||
|
||||
|
@ -2,12 +2,10 @@ package me.topchetoeu.jscript.compilation.control;
|
||||
|
||||
import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||
import me.topchetoeu.jscript.compilation.CompoundStatement;
|
||||
import me.topchetoeu.jscript.compilation.Instruction;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
import me.topchetoeu.jscript.compilation.values.ConstantStatement;
|
||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
import me.topchetoeu.jscript.engine.values.Values;
|
||||
|
||||
public class DoWhileStatement extends Statement {
|
||||
public final Statement condition, body;
|
||||
@ -20,56 +18,16 @@ public class DoWhileStatement extends Statement {
|
||||
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
if (condition instanceof ConstantStatement) {
|
||||
int start = target.size();
|
||||
body.compile(target, scope, false);
|
||||
int end = target.size();
|
||||
if (Values.toBoolean(((ConstantStatement)condition).value)) {
|
||||
WhileStatement.replaceBreaks(target, label, start, end, end + 1, end + 1);
|
||||
}
|
||||
else {
|
||||
target.add(Instruction.jmp(loc(), start - end));
|
||||
WhileStatement.replaceBreaks(target, label, start, end, start, end + 1);
|
||||
}
|
||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||
return;
|
||||
}
|
||||
|
||||
int start = target.size();
|
||||
body.compileWithDebug(target, scope, false);
|
||||
body.compileWithDebug(target, scope, false, BreakpointType.STEP_OVER);
|
||||
int mid = target.size();
|
||||
condition.compile(target, scope, true);
|
||||
condition.compileWithDebug(target, scope, true, BreakpointType.STEP_OVER);
|
||||
int end = target.size();
|
||||
|
||||
WhileStatement.replaceBreaks(target, label, start, mid - 1, mid, end + 1);
|
||||
target.add(Instruction.jmpIf(loc(), start - end));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement optimize() {
|
||||
var cond = condition.optimize();
|
||||
var b = body.optimize();
|
||||
|
||||
if (b instanceof CompoundStatement) {
|
||||
var comp = (CompoundStatement)b;
|
||||
if (comp.statements.length > 0) {
|
||||
var last = comp.statements[comp.statements.length - 1];
|
||||
if (last instanceof ContinueStatement) comp.statements[comp.statements.length - 1] = new CompoundStatement(loc());
|
||||
if (last instanceof BreakStatement) {
|
||||
comp.statements[comp.statements.length - 1] = new CompoundStatement(loc());
|
||||
return new CompoundStatement(loc());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (b instanceof ContinueStatement) {
|
||||
b = new CompoundStatement(loc());
|
||||
}
|
||||
else if (b instanceof BreakStatement) return new CompoundStatement(loc());
|
||||
|
||||
if (b.pure()) return new DoWhileStatement(loc(), label, cond, new CompoundStatement(loc()));
|
||||
else return new DoWhileStatement(loc(), label, cond, b);
|
||||
}
|
||||
|
||||
public DoWhileStatement(Location loc, String label, Statement condition, Statement body) {
|
||||
super(loc);
|
||||
this.label = label;
|
||||
|
@ -4,6 +4,7 @@ import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||
import me.topchetoeu.jscript.compilation.Instruction;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.engine.Operation;
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
|
||||
@ -32,7 +33,7 @@ public class ForInStatement extends Statement {
|
||||
target.add(Instruction.storeVar(loc(), scope.getKey(varName)));
|
||||
}
|
||||
|
||||
object.compileWithDebug(target, scope, true);
|
||||
object.compileWithDebug(target, scope, true, BreakpointType.STEP_OVER);
|
||||
target.add(Instruction.keys(loc(), true));
|
||||
|
||||
int start = target.size();
|
||||
@ -43,10 +44,11 @@ public class ForInStatement extends Statement {
|
||||
target.add(Instruction.nop(loc()));
|
||||
|
||||
target.add(Instruction.loadMember(varLocation, "value"));
|
||||
target.queueDebug(BreakpointType.STEP_OVER, object.loc());
|
||||
target.add(Instruction.storeVar(varLocation, key));
|
||||
target.setDebug();
|
||||
|
||||
body.compileWithDebug(target, scope, false);
|
||||
target.queueDebug(BreakpointType.STEP_OVER, body.loc());
|
||||
body.compile(target, scope, false);
|
||||
|
||||
int end = target.size();
|
||||
|
||||
@ -57,7 +59,6 @@ public class ForInStatement extends Statement {
|
||||
target.set(mid, Instruction.jmpIf(loc(), end - mid + 1));
|
||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||
target.get(first).locate(loc());
|
||||
target.setDebug(first);
|
||||
}
|
||||
|
||||
public ForInStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement varValue, Statement object, Statement body) {
|
||||
|
@ -2,12 +2,10 @@ package me.topchetoeu.jscript.compilation.control;
|
||||
|
||||
import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||
import me.topchetoeu.jscript.compilation.CompoundStatement;
|
||||
import me.topchetoeu.jscript.compilation.Instruction;
|
||||
import me.topchetoeu.jscript.compilation.values.ConstantStatement;
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
import me.topchetoeu.jscript.engine.values.Values;
|
||||
|
||||
public class ForStatement extends Statement {
|
||||
public final Statement declaration, assignment, condition, body;
|
||||
@ -20,29 +18,15 @@ public class ForStatement extends Statement {
|
||||
}
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
declaration.compile(target, scope, false);
|
||||
|
||||
if (condition instanceof ConstantStatement) {
|
||||
if (Values.toBoolean(((ConstantStatement)condition).value)) {
|
||||
int start = target.size();
|
||||
body.compile(target, scope, false);
|
||||
int mid = target.size();
|
||||
assignment.compileWithDebug(target, scope, false);
|
||||
int end = target.size();
|
||||
WhileStatement.replaceBreaks(target, label, start, mid, mid, end + 1);
|
||||
target.add(Instruction.jmp(loc(), start - target.size()));
|
||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||
}
|
||||
return;
|
||||
}
|
||||
declaration.compileWithDebug(target, scope, false, BreakpointType.STEP_OVER);
|
||||
|
||||
int start = target.size();
|
||||
condition.compile(target, scope, true);
|
||||
condition.compileWithDebug(target, scope, true, BreakpointType.STEP_OVER);
|
||||
int mid = target.size();
|
||||
target.add(Instruction.nop(null));
|
||||
body.compile(target, scope, false);
|
||||
body.compileWithDebug(target, scope, false, BreakpointType.STEP_OVER);
|
||||
int beforeAssign = target.size();
|
||||
assignment.compileWithDebug(target, scope, false);
|
||||
assignment.compileWithDebug(target, scope, false, BreakpointType.STEP_OVER);
|
||||
int end = target.size();
|
||||
|
||||
WhileStatement.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1);
|
||||
@ -51,28 +35,6 @@ public class ForStatement extends Statement {
|
||||
target.set(mid, Instruction.jmpIfNot(loc(), end - mid + 1));
|
||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||
}
|
||||
@Override
|
||||
public Statement optimize() {
|
||||
var decl = declaration.optimize();
|
||||
var asgn = assignment.optimize();
|
||||
var cond = condition.optimize();
|
||||
var b = body.optimize();
|
||||
|
||||
if (asgn.pure()) {
|
||||
if (decl.pure()) return new WhileStatement(loc(), label, cond, b).optimize();
|
||||
else return new CompoundStatement(loc(),
|
||||
decl, new WhileStatement(loc(), label, cond, b)
|
||||
).optimize();
|
||||
}
|
||||
|
||||
else if (b instanceof ContinueStatement) return new CompoundStatement(loc(),
|
||||
decl, new WhileStatement(loc(), label, cond, new CompoundStatement(loc(), b, asgn))
|
||||
);
|
||||
else if (b instanceof BreakStatement) return decl;
|
||||
|
||||
if (b.pure()) return new ForStatement(loc(), label, decl, cond, asgn, new CompoundStatement(null));
|
||||
else return new ForStatement(loc(), label, decl, cond, asgn, b);
|
||||
}
|
||||
|
||||
public ForStatement(Location loc, String label, Statement declaration, Statement condition, Statement assignment, Statement body) {
|
||||
super(loc);
|
||||
@ -82,14 +44,4 @@ public class ForStatement extends Statement {
|
||||
this.assignment = assignment;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
public static CompoundStatement ofFor(Location loc, String label, Statement declaration, Statement condition, Statement increment, Statement body) {
|
||||
return new CompoundStatement(loc,
|
||||
declaration,
|
||||
new WhileStatement(loc, label, condition, new CompoundStatement(loc,
|
||||
body,
|
||||
increment
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,10 @@ package me.topchetoeu.jscript.compilation.control;
|
||||
|
||||
import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||
import me.topchetoeu.jscript.compilation.CompoundStatement;
|
||||
import me.topchetoeu.jscript.compilation.DiscardStatement;
|
||||
import me.topchetoeu.jscript.compilation.Instruction;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
import me.topchetoeu.jscript.compilation.values.ConstantStatement;
|
||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
import me.topchetoeu.jscript.engine.values.Values;
|
||||
|
||||
public class IfStatement extends Statement {
|
||||
public final Statement condition, body, elseBody;
|
||||
@ -20,52 +17,32 @@ public class IfStatement extends Statement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
if (condition instanceof ConstantStatement) {
|
||||
if (Values.not(((ConstantStatement)condition).value)) {
|
||||
if (elseBody != null) elseBody.compileWithDebug(target, scope, pollute);
|
||||
}
|
||||
else {
|
||||
body.compileWithDebug(target, scope, pollute);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
condition.compile(target, scope, true);
|
||||
public void compileWithDebug(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType breakpoint) {
|
||||
condition.compileWithDebug(target, scope, true, breakpoint);
|
||||
|
||||
if (elseBody == null) {
|
||||
int i = target.size();
|
||||
target.add(Instruction.nop(null));
|
||||
body.compileWithDebug(target, scope, pollute);
|
||||
body.compileWithDebug(target, scope, pollute, breakpoint);
|
||||
int endI = target.size();
|
||||
target.set(i, Instruction.jmpIfNot(loc(), endI - i));
|
||||
}
|
||||
else {
|
||||
int start = target.size();
|
||||
target.add(Instruction.nop(null));
|
||||
body.compileWithDebug(target, scope, pollute);
|
||||
body.compileWithDebug(target, scope, pollute, breakpoint);
|
||||
target.add(Instruction.nop(null));
|
||||
int mid = target.size();
|
||||
elseBody.compileWithDebug(target, scope, pollute);
|
||||
elseBody.compileWithDebug(target, scope, pollute, breakpoint);
|
||||
int end = target.size();
|
||||
|
||||
target.set(start, Instruction.jmpIfNot(loc(), mid - start));
|
||||
target.set(mid - 1, Instruction.jmp(loc(), end - mid + 1));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement optimize() {
|
||||
var cond = condition.optimize();
|
||||
var b = body.optimize();
|
||||
var e = elseBody == null ? null : elseBody.optimize();
|
||||
|
||||
if (b.pure()) b = new CompoundStatement(null);
|
||||
if (e != null && e.pure()) e = null;
|
||||
|
||||
if (b.pure() && e == null) return new DiscardStatement(loc(), cond).optimize();
|
||||
else return new IfStatement(loc(), cond, b, e);
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
compileWithDebug(target, scope, pollute, BreakpointType.STEP_IN);
|
||||
}
|
||||
|
||||
public IfStatement(Location loc, Statement condition, Statement body, Statement elseBody) {
|
||||
|
@ -6,6 +6,7 @@ import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||
import me.topchetoeu.jscript.compilation.Instruction;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
||||
import me.topchetoeu.jscript.engine.Operation;
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
@ -36,7 +37,7 @@ public class SwitchStatement extends Statement {
|
||||
var caseToStatement = new HashMap<Integer, Integer>();
|
||||
var statementToIndex = new HashMap<Integer, Integer>();
|
||||
|
||||
value.compile(target, scope, true);
|
||||
value.compileWithDebug(target, scope, true, BreakpointType.STEP_OVER);
|
||||
|
||||
for (var ccase : cases) {
|
||||
target.add(Instruction.dup(loc()));
|
||||
@ -52,7 +53,7 @@ public class SwitchStatement extends Statement {
|
||||
|
||||
for (var stm : body) {
|
||||
statementToIndex.put(statementToIndex.size(), target.size());
|
||||
stm.compileWithDebug(target, scope, false);
|
||||
stm.compileWithDebug(target, scope, false, BreakpointType.STEP_OVER);
|
||||
}
|
||||
|
||||
int end = target.size();
|
||||
|
@ -25,27 +25,28 @@ public class TryStatement extends Statement {
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
target.add(Instruction.nop(null));
|
||||
|
||||
int start = target.size(), tryN, catchN = -1, finN = -1;
|
||||
int start = target.size(), catchStart = -1, finallyStart = -1;
|
||||
|
||||
tryBody.compile(target, scope, false);
|
||||
tryN = target.size() - start;
|
||||
target.add(Instruction.tryEnd(loc()));
|
||||
|
||||
if (catchBody != null) {
|
||||
int tmp = target.size();
|
||||
catchStart = target.size() - start;
|
||||
var local = scope instanceof GlobalScope ? scope.child() : (LocalScopeRecord)scope;
|
||||
local.define(name, true);
|
||||
catchBody.compile(target, scope, false);
|
||||
local.undefine();
|
||||
catchN = target.size() - tmp;
|
||||
target.add(Instruction.tryEnd(loc()));
|
||||
}
|
||||
|
||||
if (finallyBody != null) {
|
||||
int tmp = target.size();
|
||||
finallyStart = target.size() - start;
|
||||
finallyBody.compile(target, scope, false);
|
||||
finN = target.size() - tmp;
|
||||
target.add(Instruction.tryEnd(loc()));
|
||||
}
|
||||
|
||||
target.set(start - 1, Instruction.tryInstr(loc(), tryN, catchN, finN));
|
||||
|
||||
target.set(start - 1, Instruction.tryStart(loc(), catchStart, finallyStart, target.size() - start));
|
||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||
}
|
||||
|
||||
|
@ -3,13 +3,10 @@ package me.topchetoeu.jscript.compilation.control;
|
||||
import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||
import me.topchetoeu.jscript.compilation.CompoundStatement;
|
||||
import me.topchetoeu.jscript.compilation.DiscardStatement;
|
||||
import me.topchetoeu.jscript.compilation.Instruction;
|
||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
||||
import me.topchetoeu.jscript.compilation.values.ConstantStatement;
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
import me.topchetoeu.jscript.engine.values.Values;
|
||||
|
||||
public class WhileStatement extends Statement {
|
||||
public final Statement condition, body;
|
||||
@ -21,22 +18,11 @@ public class WhileStatement extends Statement {
|
||||
}
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
if (condition instanceof ConstantStatement) {
|
||||
if (Values.toBoolean(((ConstantStatement)condition).value)) {
|
||||
int start = target.size();
|
||||
body.compile(target, scope, false);
|
||||
int end = target.size();
|
||||
replaceBreaks(target, label, start, end, start, end + 1);
|
||||
target.add(Instruction.jmp(loc(), start - target.size()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int start = target.size();
|
||||
condition.compile(target, scope, true);
|
||||
int mid = target.size();
|
||||
target.add(Instruction.nop(null));
|
||||
body.compile(target, scope, false);
|
||||
body.compileWithDebug(target, scope, false, BreakpointType.STEP_OVER);
|
||||
|
||||
int end = target.size();
|
||||
|
||||
@ -46,19 +32,6 @@ public class WhileStatement extends Statement {
|
||||
target.set(mid, Instruction.jmpIfNot(loc(), end - mid + 1));
|
||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||
}
|
||||
@Override
|
||||
public Statement optimize() {
|
||||
var cond = condition.optimize();
|
||||
var b = body.optimize();
|
||||
|
||||
if (b instanceof ContinueStatement) {
|
||||
b = new CompoundStatement(loc());
|
||||
}
|
||||
else if (b instanceof BreakStatement) return new DiscardStatement(loc(), cond).optimize();
|
||||
|
||||
if (b.pure()) return new WhileStatement(loc(), label, cond, new CompoundStatement(null));
|
||||
else return new WhileStatement(loc(), label, cond, b);
|
||||
}
|
||||
|
||||
public WhileStatement(Location loc, String label, Statement condition, Statement body) {
|
||||
super(loc);
|
||||
@ -71,21 +44,11 @@ public class WhileStatement extends Statement {
|
||||
for (int i = start; i < end; i++) {
|
||||
var instr = target.get(i);
|
||||
if (instr.type == Type.NOP && instr.is(0, "cont") && (instr.get(1) == null || instr.is(1, label))) {
|
||||
target.set(i, Instruction.jmp(instr.location, continuePoint - i));
|
||||
target.set(i, Instruction.jmp(instr.location, continuePoint - i).setDbgData(target.get(i)));
|
||||
}
|
||||
if (instr.type == Type.NOP && instr.is(0, "break") && (instr.get(1) == null || instr.is(1, label))) {
|
||||
target.set(i, Instruction.jmp(instr.location, breakPoint - i));
|
||||
target.set(i, Instruction.jmp(instr.location, breakPoint - i).setDbgData(target.get(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// public static CompoundStatement ofFor(Location loc, String label, Statement declaration, Statement condition, Statement increment, Statement body) {
|
||||
// return new CompoundStatement(loc,
|
||||
// declaration,
|
||||
// new WhileStatement(loc, label, condition, new CompoundStatement(loc,
|
||||
// body,
|
||||
// increment
|
||||
// ))
|
||||
// );
|
||||
// }
|
||||
}
|
||||
|
@ -9,8 +9,13 @@ import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
public class ArrayStatement extends Statement {
|
||||
public final Statement[] statements;
|
||||
|
||||
@Override
|
||||
public boolean pure() { return true; }
|
||||
@Override public boolean pure() {
|
||||
for (var stm : statements) {
|
||||
if (!stm.pure()) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
|
@ -4,15 +4,18 @@ import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||
import me.topchetoeu.jscript.compilation.Instruction;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
|
||||
public class CallStatement extends Statement {
|
||||
public final Statement func;
|
||||
public final Statement[] args;
|
||||
public final boolean isNew;
|
||||
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
if (func instanceof IndexStatement) {
|
||||
public void compileWithDebug(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType type) {
|
||||
if (isNew) func.compile(target, scope, true);
|
||||
else if (func instanceof IndexStatement) {
|
||||
((IndexStatement)func).compile(target, scope, true, true);
|
||||
}
|
||||
else {
|
||||
@ -22,18 +25,26 @@ public class CallStatement extends Statement {
|
||||
|
||||
for (var arg : args) arg.compile(target, scope, true);
|
||||
|
||||
target.add(Instruction.call(loc(), args.length));
|
||||
target.setDebug();
|
||||
target.queueDebug(type);
|
||||
if (isNew) target.add(Instruction.callNew(loc(), args.length));
|
||||
else target.add(Instruction.call(loc(), args.length));
|
||||
|
||||
if (!pollute) target.add(Instruction.discard(loc()));
|
||||
}
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
compileWithDebug(target, scope, pollute, BreakpointType.STEP_IN);
|
||||
}
|
||||
|
||||
public CallStatement(Location loc, Statement func, Statement ...args) {
|
||||
public CallStatement(Location loc, boolean isNew, Statement func, Statement ...args) {
|
||||
super(loc);
|
||||
this.isNew = isNew;
|
||||
this.func = func;
|
||||
this.args = args;
|
||||
}
|
||||
public CallStatement(Location loc, Statement obj, Object key, Statement ...args) {
|
||||
public CallStatement(Location loc, boolean isNew, Statement obj, Object key, Statement ...args) {
|
||||
super(loc);
|
||||
this.isNew = isNew;
|
||||
this.func = new IndexStatement(loc, obj, new ConstantStatement(loc, key));
|
||||
this.args = args;
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
|
||||
public class CommaStatement extends Statement {
|
||||
public final Statement[] values;
|
||||
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
values[i].compileWithDebug(target, scope, i == values.length - 1 && pollute);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement optimize() {
|
||||
var res = new Vector<Statement>(values.length);
|
||||
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
var stm = values[i].optimize();
|
||||
if (i < values.length - 1 && stm.pure()) continue;
|
||||
res.add(stm);
|
||||
}
|
||||
|
||||
if (res.size() == 1) return res.get(0);
|
||||
else return new CommaStatement(loc(), res.toArray(Statement[]::new));
|
||||
}
|
||||
|
||||
public CommaStatement(Location loc, Statement ...args) {
|
||||
super(loc);
|
||||
this.values = args;
|
||||
}
|
||||
}
|
@ -9,8 +9,7 @@ import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
public class ConstantStatement extends Statement {
|
||||
public final Object value;
|
||||
|
||||
@Override
|
||||
public boolean pure() { return true; }
|
||||
@Override public boolean pure() { return true; }
|
||||
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
|
@ -1,30 +1,24 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||
import me.topchetoeu.jscript.compilation.Instruction;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
|
||||
public class VoidStatement extends Statement {
|
||||
public class DiscardStatement extends Statement {
|
||||
public final Statement value;
|
||||
|
||||
@Override public boolean pure() { return value.pure(); }
|
||||
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
if (value != null) value.compile(target, scope, false);
|
||||
value.compile(target, scope, false);
|
||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement optimize() {
|
||||
if (value == null) return this;
|
||||
var val = value.optimize();
|
||||
if (val.pure()) return new ConstantStatement(loc(), null);
|
||||
else return new VoidStatement(loc(), val);
|
||||
}
|
||||
|
||||
public VoidStatement(Location loc, Statement value) {
|
||||
public DiscardStatement(Location loc, Statement val) {
|
||||
super(loc);
|
||||
this.value = value;
|
||||
this.value = val;
|
||||
}
|
||||
}
|
@ -17,15 +17,15 @@ public class FunctionStatement extends Statement {
|
||||
public final String varName;
|
||||
public final String[] args;
|
||||
public final boolean statement;
|
||||
public final Location end;
|
||||
|
||||
private static Random rand = new Random();
|
||||
|
||||
@Override
|
||||
public boolean pure() { return varName == null; }
|
||||
@Override public boolean pure() { return varName == null && statement; }
|
||||
|
||||
@Override
|
||||
public void declare(ScopeRecord scope) {
|
||||
if (varName != null) scope.define(varName);
|
||||
if (varName != null && statement) scope.define(varName);
|
||||
}
|
||||
|
||||
public static void checkBreakAndCont(CompileTarget target, int start) {
|
||||
@ -41,7 +41,7 @@ public class FunctionStatement extends Statement {
|
||||
}
|
||||
}
|
||||
|
||||
protected long compileBody(CompileTarget target, ScopeRecord scope, boolean polute) {
|
||||
private long compileBody(CompileTarget target, ScopeRecord scope, boolean polute) {
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
for (var j = 0; j < i; j++) {
|
||||
if (args[i].equals(args[j])) {
|
||||
@ -71,11 +71,14 @@ public class FunctionStatement extends Statement {
|
||||
|
||||
body.declare(subscope);
|
||||
body.compile(subtarget, subscope, false);
|
||||
subtarget.add(Instruction.ret(subtarget.lastLoc(loc())));
|
||||
subtarget.add(Instruction.ret(end));
|
||||
checkBreakAndCont(subtarget, 0);
|
||||
|
||||
if (polute) target.add(Instruction.loadFunc(loc(), id, subscope.getCaptures()));
|
||||
target.functions.put(id, new FunctionBody(subscope.localsCount(), args.length, subtarget.array(), subscope.captures(), subscope.locals()));
|
||||
target.functions.put(id, new FunctionBody(
|
||||
subscope.localsCount(), args.length,
|
||||
subtarget.array(), subscope.captures(), subscope.locals()
|
||||
));
|
||||
|
||||
return id;
|
||||
}
|
||||
@ -106,9 +109,10 @@ public class FunctionStatement extends Statement {
|
||||
compile(target, scope, pollute, null);
|
||||
}
|
||||
|
||||
public FunctionStatement(Location loc, String varName, String[] args, boolean statement, CompoundStatement body) {
|
||||
public FunctionStatement(Location loc, Location end, String varName, String[] args, boolean statement, CompoundStatement body) {
|
||||
super(loc);
|
||||
|
||||
this.end = end;
|
||||
this.varName = varName;
|
||||
this.statement = statement;
|
||||
|
||||
|
@ -7,8 +7,7 @@ import me.topchetoeu.jscript.compilation.Instruction;
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
|
||||
public class GlobalThisStatement extends Statement {
|
||||
@Override
|
||||
public boolean pure() { return true; }
|
||||
@Override public boolean pure() { return true; }
|
||||
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
|
@ -4,6 +4,7 @@ import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||
import me.topchetoeu.jscript.compilation.Instruction;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.engine.Operation;
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
|
||||
@ -25,7 +26,7 @@ public class IndexAssignStatement extends Statement {
|
||||
target.add(Instruction.operation(loc(), operation));
|
||||
|
||||
target.add(Instruction.storeMember(loc(), pollute));
|
||||
target.setDebug();
|
||||
target.setDebug(BreakpointType.STEP_IN);
|
||||
}
|
||||
else {
|
||||
object.compile(target, scope, true);
|
||||
@ -33,7 +34,7 @@ public class IndexAssignStatement extends Statement {
|
||||
value.compile(target, scope, true);
|
||||
|
||||
target.add(Instruction.storeMember(loc(), pollute));
|
||||
target.setDebug();
|
||||
target.setDebug(BreakpointType.STEP_IN);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import me.topchetoeu.jscript.compilation.AssignableStatement;
|
||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||
import me.topchetoeu.jscript.compilation.Instruction;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.engine.Operation;
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
|
||||
@ -12,9 +13,6 @@ public class IndexStatement extends AssignableStatement {
|
||||
public final Statement object;
|
||||
public final Statement index;
|
||||
|
||||
@Override
|
||||
public boolean pure() { return true; }
|
||||
|
||||
@Override
|
||||
public Statement toAssign(Statement val, Operation operation) {
|
||||
return new IndexAssignStatement(loc(), object, index, val, operation);
|
||||
@ -24,13 +22,13 @@ public class IndexStatement extends AssignableStatement {
|
||||
if (dupObj) target.add(Instruction.dup(loc()));
|
||||
if (index instanceof ConstantStatement) {
|
||||
target.add(Instruction.loadMember(loc(), ((ConstantStatement)index).value));
|
||||
target.setDebug();
|
||||
target.setDebug(BreakpointType.STEP_IN);
|
||||
return;
|
||||
}
|
||||
|
||||
index.compile(target, scope, true);
|
||||
target.add(Instruction.loadMember(loc()));
|
||||
target.setDebug();
|
||||
target.setDebug(BreakpointType.STEP_IN);
|
||||
if (!pollute) target.add(Instruction.discard(loc()));
|
||||
}
|
||||
@Override
|
||||
|
@ -10,10 +10,7 @@ import me.topchetoeu.jscript.engine.values.Values;
|
||||
public class LazyAndStatement extends Statement {
|
||||
public final Statement first, second;
|
||||
|
||||
@Override
|
||||
public boolean pure() {
|
||||
return first.pure() && second.pure();
|
||||
}
|
||||
@Override public boolean pure() { return first.pure() && second.pure(); }
|
||||
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
|
@ -10,10 +10,7 @@ import me.topchetoeu.jscript.engine.values.Values;
|
||||
public class LazyOrStatement extends Statement {
|
||||
public final Statement first, second;
|
||||
|
||||
@Override
|
||||
public boolean pure() {
|
||||
return first.pure() && second.pure();
|
||||
}
|
||||
@Override public boolean pure() { return first.pure() && second.pure(); }
|
||||
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
|
@ -1,28 +0,0 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||
import me.topchetoeu.jscript.compilation.Instruction;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
|
||||
public class NewStatement extends Statement {
|
||||
public final Statement func;
|
||||
public final Statement[] args;
|
||||
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
func.compile(target, scope, true);
|
||||
|
||||
for (var arg : args) arg.compile(target, scope, true);
|
||||
|
||||
target.add(Instruction.callNew(loc(), args.length));
|
||||
target.setDebug();
|
||||
}
|
||||
|
||||
public NewStatement(Location loc, Statement func, Statement ...args) {
|
||||
super(loc);
|
||||
this.func = func;
|
||||
this.args = args;
|
||||
}
|
||||
}
|
@ -14,6 +14,14 @@ public class ObjectStatement extends Statement {
|
||||
public final Map<Object, FunctionStatement> getters;
|
||||
public final Map<Object, FunctionStatement> setters;
|
||||
|
||||
@Override public boolean pure() {
|
||||
for (var el : map.values()) {
|
||||
if (!el.pure()) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
target.add(Instruction.loadObj(loc()));
|
||||
|
@ -4,16 +4,21 @@ import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||
import me.topchetoeu.jscript.compilation.Instruction;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
import me.topchetoeu.jscript.compilation.control.ThrowStatement;
|
||||
import me.topchetoeu.jscript.engine.Operation;
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
import me.topchetoeu.jscript.engine.values.Values;
|
||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||
|
||||
public class OperationStatement extends Statement {
|
||||
public final Statement[] args;
|
||||
public final Operation operation;
|
||||
|
||||
@Override public boolean pure() {
|
||||
for (var el : args) {
|
||||
if (!el.pure()) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
for (var arg : args) {
|
||||
@ -24,39 +29,6 @@ public class OperationStatement extends Statement {
|
||||
else target.add(Instruction.discard(loc()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pure() {
|
||||
for (var arg : args) {
|
||||
if (!arg.pure()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement optimize() {
|
||||
var args = new Statement[this.args.length];
|
||||
var allConst = true;
|
||||
|
||||
for (var i = 0; i < this.args.length; i++) {
|
||||
args[i] = this.args[i].optimize();
|
||||
if (!(args[i] instanceof ConstantStatement)) allConst = false;
|
||||
}
|
||||
|
||||
if (allConst) {
|
||||
var vals = new Object[this.args.length];
|
||||
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
vals[i] = ((ConstantStatement)args[i]).value;
|
||||
}
|
||||
|
||||
try { return new ConstantStatement(loc(), Values.operation(null, operation, vals)); }
|
||||
catch (EngineException e) { return new ThrowStatement(loc(), new ConstantStatement(loc(), e.value)); }
|
||||
}
|
||||
|
||||
return new OperationStatement(loc(), operation, args);
|
||||
|
||||
}
|
||||
|
||||
public OperationStatement(Location loc, Operation operation, Statement ...args) {
|
||||
super(loc);
|
||||
this.operation = operation;
|
||||
|
@ -9,8 +9,8 @@ import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
public class RegexStatement extends Statement {
|
||||
public final String pattern, flags;
|
||||
|
||||
@Override
|
||||
public boolean pure() { return true; }
|
||||
// Not really pure, since a function is called, but can be ignored.
|
||||
@Override public boolean pure() { return true; }
|
||||
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
|
@ -5,13 +5,13 @@ import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||
import me.topchetoeu.jscript.compilation.Instruction;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
import me.topchetoeu.jscript.engine.values.Values;
|
||||
|
||||
public class TypeofStatement extends Statement {
|
||||
public final Statement value;
|
||||
|
||||
@Override
|
||||
public boolean pure() { return true; }
|
||||
// Not really pure, since a variable from the global scope could be accessed,
|
||||
// which could lead to code execution, that would get omitted
|
||||
@Override public boolean pure() { return true; }
|
||||
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
@ -26,23 +26,6 @@ public class TypeofStatement extends Statement {
|
||||
target.add(Instruction.typeof(loc()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement optimize() {
|
||||
var val = value.optimize();
|
||||
|
||||
if (val instanceof ConstantStatement) {
|
||||
return new ConstantStatement(loc(), Values.type(((ConstantStatement)val).value));
|
||||
}
|
||||
else if (
|
||||
val instanceof ObjectStatement ||
|
||||
val instanceof ArrayStatement ||
|
||||
val instanceof GlobalThisStatement
|
||||
) return new ConstantStatement(loc(), "object");
|
||||
else if(val instanceof FunctionStatement) return new ConstantStatement(loc(), "function");
|
||||
|
||||
return new TypeofStatement(loc(), val);
|
||||
}
|
||||
|
||||
public TypeofStatement(Location loc, Statement value) {
|
||||
super(loc);
|
||||
this.value = value;
|
||||
|
@ -12,6 +12,8 @@ public class VariableAssignStatement extends Statement {
|
||||
public final Statement value;
|
||||
public final Operation operation;
|
||||
|
||||
@Override public boolean pure() { return false; }
|
||||
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
var i = scope.getKey(name);
|
||||
|
@ -9,8 +9,7 @@ import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
public class VariableIndexStatement extends Statement {
|
||||
public final int index;
|
||||
|
||||
@Override
|
||||
public boolean pure() { return true; }
|
||||
@Override public boolean pure() { return true; }
|
||||
|
||||
@Override
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
|
@ -11,8 +11,7 @@ import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
public class VariableStatement extends AssignableStatement {
|
||||
public final String name;
|
||||
|
||||
@Override
|
||||
public boolean pure() { return true; }
|
||||
@Override public boolean pure() { return false; }
|
||||
|
||||
@Override
|
||||
public Statement toAssign(Statement val, Operation operation) {
|
||||
|
@ -1,10 +1,12 @@
|
||||
package me.topchetoeu.jscript.engine;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import me.topchetoeu.jscript.Filename;
|
||||
import me.topchetoeu.jscript.Location;
|
||||
@ -37,12 +39,30 @@ public class Context {
|
||||
var result = env.compile.call(this, null, raw, filename.toString(), env);
|
||||
|
||||
var function = (FunctionValue)Values.getMember(this, result, "function");
|
||||
if (!engine.debugging) return function;
|
||||
|
||||
var rawMapChain = ((ArrayValue)Values.getMember(this, result, "mapChain")).toArray();
|
||||
var breakpoints = new TreeSet<>(
|
||||
Arrays.stream(((ArrayValue)Values.getMember(this, result, "breakpoints")).toArray())
|
||||
.map(v -> Location.parse(Values.toString(this, v)))
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
var maps = new SourceMap[rawMapChain.length];
|
||||
for (var i = 0; i < maps.length; i++) maps[i] = SourceMap.parse((String)rawMapChain[i]);
|
||||
|
||||
for (var i = 0; i < maps.length; i++) maps[i] = SourceMap.parse(Values.toString(this, (String)rawMapChain[i]));
|
||||
|
||||
var map = SourceMap.chain(maps);
|
||||
|
||||
engine.onSource(filename, raw, new TreeSet<>(), map);
|
||||
if (map != null) {
|
||||
var newBreakpoints = new TreeSet<Location>();
|
||||
for (var bp : breakpoints) {
|
||||
bp = map.toCompiled(bp);
|
||||
if (bp != null) newBreakpoints.add(bp);
|
||||
}
|
||||
breakpoints = newBreakpoints;
|
||||
}
|
||||
|
||||
engine.onSource(filename, raw, breakpoints, map);
|
||||
|
||||
return function;
|
||||
}
|
||||
@ -52,6 +72,7 @@ public class Context {
|
||||
frames.add(frame);
|
||||
if (frames.size() > engine.maxStackFrames) throw EngineException.ofRange("Stack overflow!");
|
||||
pushEnv(frame.function.environment);
|
||||
engine.onFramePush(this, frame);
|
||||
}
|
||||
public boolean popFrame(CodeFrame frame) {
|
||||
if (frames.size() == 0) return false;
|
||||
|
@ -15,6 +15,7 @@ import me.topchetoeu.jscript.events.Awaitable;
|
||||
import me.topchetoeu.jscript.events.DataNotifier;
|
||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||
import me.topchetoeu.jscript.exceptions.InterruptException;
|
||||
import me.topchetoeu.jscript.mapping.SourceMap;
|
||||
|
||||
public class Engine implements DebugController {
|
||||
private class UncompiledFunction extends FunctionValue {
|
||||
@ -66,22 +67,25 @@ public class Engine implements DebugController {
|
||||
|
||||
private final HashMap<Filename, String> sources = new HashMap<>();
|
||||
private final HashMap<Filename, TreeSet<Location>> bpts = new HashMap<>();
|
||||
private final HashMap<Filename, SourceMap> maps = new HashMap<>();
|
||||
|
||||
private DebugController debugger;
|
||||
private Thread thread;
|
||||
private PriorityBlockingQueue<Task> tasks = new PriorityBlockingQueue<>();
|
||||
|
||||
public boolean attachDebugger(DebugController debugger) {
|
||||
public synchronized 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()));
|
||||
}
|
||||
for (var source : sources.entrySet()) debugger.onSource(
|
||||
source.getKey(), source.getValue(),
|
||||
bpts.get(source.getKey()),
|
||||
maps.get(source.getKey())
|
||||
);
|
||||
|
||||
this.debugger = debugger;
|
||||
return true;
|
||||
}
|
||||
public boolean detachDebugger() {
|
||||
public synchronized boolean detachDebugger() {
|
||||
if (!debugging || this.debugger == null) return false;
|
||||
this.debugger = null;
|
||||
return true;
|
||||
@ -122,7 +126,7 @@ public class Engine implements DebugController {
|
||||
public boolean inExecThread() {
|
||||
return Thread.currentThread() == thread;
|
||||
}
|
||||
public boolean isRunning() {
|
||||
public synchronized boolean isRunning() {
|
||||
return this.thread != null;
|
||||
}
|
||||
|
||||
@ -135,6 +139,10 @@ public class Engine implements DebugController {
|
||||
return pushMsg(micro, ctx, new UncompiledFunction(filename, raw), thisArg, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFramePush(Context ctx, CodeFrame frame) {
|
||||
if (debugging && debugger != null) debugger.onFramePush(ctx, frame);
|
||||
}
|
||||
@Override public void onFramePop(Context ctx, CodeFrame frame) {
|
||||
if (debugging && debugger != null) debugger.onFramePop(ctx, frame);
|
||||
}
|
||||
@ -142,11 +150,12 @@ public class Engine implements DebugController {
|
||||
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) {
|
||||
@Override public void onSource(Filename filename, String source, TreeSet<Location> breakpoints, SourceMap map) {
|
||||
if (!debugging) return;
|
||||
if (debugger != null) debugger.onSource(filename, source, breakpoints);
|
||||
if (debugger != null) debugger.onSource(filename, source, breakpoints, map);
|
||||
sources.put(filename, source);
|
||||
bpts.put(filename, breakpoints);
|
||||
maps.put(filename, map);
|
||||
}
|
||||
|
||||
public Engine(boolean debugging) {
|
||||
|
@ -1,9 +1,10 @@
|
||||
package me.topchetoeu.jscript.engine;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import me.topchetoeu.jscript.Filename;
|
||||
import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.engine.scope.GlobalScope;
|
||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||
@ -39,12 +40,25 @@ public class Environment implements PermissionsProvider {
|
||||
@Native public FunctionValue compile = new NativeFunction("compile", (ctx, thisArg, args) -> {
|
||||
var source = Values.toString(ctx, args[0]);
|
||||
var filename = Values.toString(ctx, args[1]);
|
||||
var isDebug = Values.toBoolean(args[2]);
|
||||
|
||||
var env = Values.wrapper(args[2], Environment.class);
|
||||
var res = new ObjectValue();
|
||||
|
||||
res.defineProperty(ctx, "function", Parsing.compile(Engine.functions, new TreeSet<>(), env, Filename.parse(filename), source));
|
||||
System.out.println(source);
|
||||
|
||||
var target = Parsing.compile(env, Filename.parse(filename), source);
|
||||
Engine.functions.putAll(target.functions);
|
||||
Engine.functions.remove(0l);
|
||||
|
||||
res.defineProperty(ctx, "function", target.func(env));
|
||||
res.defineProperty(ctx, "mapChain", new ArrayValue());
|
||||
|
||||
|
||||
if (isDebug) {
|
||||
res.defineProperty(ctx, "breakpoints", ArrayValue.of(ctx, target.breakpoints.stream().map(Location::toString).collect(Collectors.toList())));
|
||||
}
|
||||
|
||||
return res;
|
||||
});
|
||||
@Native public FunctionValue regexConstructor = new NativeFunction("RegExp", (ctx, thisArg, args) -> {
|
||||
@ -64,12 +78,7 @@ public class Environment implements PermissionsProvider {
|
||||
}
|
||||
|
||||
@Native public Symbol symbol(String name) {
|
||||
if (symbols.containsKey(name)) return symbols.get(name);
|
||||
else {
|
||||
var res = new Symbol(name);
|
||||
symbols.put(name, res);
|
||||
return res;
|
||||
}
|
||||
return getSymbol(name);
|
||||
}
|
||||
|
||||
@NativeGetter("global") public ObjectValue getGlobal() {
|
||||
@ -103,6 +112,15 @@ public class Environment implements PermissionsProvider {
|
||||
return new Context(engine).pushEnv(this);
|
||||
}
|
||||
|
||||
public static Symbol getSymbol(String name) {
|
||||
if (symbols.containsKey(name)) return symbols.get(name);
|
||||
else {
|
||||
var res = new Symbol(name);
|
||||
symbols.put(name, res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
public Environment(FunctionValue compile, WrappersProvider nativeConverter, GlobalScope global) {
|
||||
if (compile != null) this.compile = compile;
|
||||
if (nativeConverter == null) nativeConverter = new NativeWrapperProvider(this);
|
||||
|
@ -8,13 +8,17 @@ import me.topchetoeu.jscript.compilation.Instruction;
|
||||
import me.topchetoeu.jscript.engine.Context;
|
||||
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||
import me.topchetoeu.jscript.mapping.SourceMap;
|
||||
|
||||
public interface DebugController {
|
||||
/**
|
||||
* Called when a script has been loaded
|
||||
* @param breakpoints
|
||||
* @param filename The name of the source
|
||||
* @param source The name of the source
|
||||
* @param breakpoints A set of all the breakpointable locations in this source
|
||||
* @param map The source map associated with this file. null if this source map isn't mapped
|
||||
*/
|
||||
void onSource(Filename filename, String source, TreeSet<Location> breakpoints);
|
||||
void onSource(Filename filename, String source, TreeSet<Location> breakpoints, SourceMap map);
|
||||
|
||||
/**
|
||||
* Called immediatly before an instruction is executed, as well as after an instruction, if it has threw or returned.
|
||||
@ -30,6 +34,13 @@ public interface DebugController {
|
||||
*/
|
||||
boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught);
|
||||
|
||||
/**
|
||||
* Called immediatly before a frame has been pushed on the frame stack.
|
||||
* This function might pause in order to await debugging commands.
|
||||
* @param ctx The context of execution
|
||||
* @param frame The code frame which was pushed
|
||||
*/
|
||||
void onFramePush(Context ctx, CodeFrame frame);
|
||||
/**
|
||||
* Called immediatly after a frame has been popped out of the frame stack.
|
||||
* This function might pause in order to await debugging commands.
|
||||
|
@ -4,7 +4,6 @@ public interface DebugHandler {
|
||||
void enable(V8Message msg);
|
||||
void disable(V8Message msg);
|
||||
|
||||
void setBreakpoint(V8Message msg);
|
||||
void setBreakpointByUrl(V8Message msg);
|
||||
void removeBreakpoint(V8Message msg);
|
||||
void continueToLocation(V8Message msg);
|
||||
|
@ -75,7 +75,6 @@ public class DebugServer {
|
||||
debugger.enable(msg); continue;
|
||||
case "Debugger.disable": debugger.disable(msg); continue;
|
||||
|
||||
case "Debugger.setBreakpoint": debugger.setBreakpoint(msg); continue;
|
||||
case "Debugger.setBreakpointByUrl": debugger.setBreakpointByUrl(msg); continue;
|
||||
case "Debugger.removeBreakpoint": debugger.removeBreakpoint(msg); continue;
|
||||
case "Debugger.continueToLocation": debugger.continueToLocation(msg); continue;
|
||||
|
@ -4,7 +4,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
@ -30,16 +30,24 @@ 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.mapping.SourceMap;
|
||||
import me.topchetoeu.jscript.parsing.Parsing;
|
||||
|
||||
// very simple indeed
|
||||
public class SimpleDebugger implements Debugger {
|
||||
public static final Set<String> VSCODE_EMPTY = Set.of(
|
||||
"function(...runtimeArgs){\n let t = 1024; let e = null;\n if(e)try{let r=\"<<default preview>>\",i=e.call(this,r);if(i!==r)return String(i)}catch(r){return`<<indescribable>>${JSON.stringify([String(r),\"object\"])}`}if(typeof this==\"object\"&&this){let r;for(let i of[Symbol.for(\"debug.description\"),Symbol.for(\"nodejs.util.inspect.custom\")])try{r=this[i]();break}catch{}if(!r&&!String(this.toString).includes(\"[native code]\")&&(r=String(this)),r&&!r.startsWith(\"[object \"))return r.length>=t?r.slice(0,t)+\"\\u2026\":r}\n ;\n\n}",
|
||||
"function(...runtimeArgs){\n let t = 1024; let e = null;\n let r={},i=\"<<default preview>>\";if(typeof this!=\"object\"||!this)return r;for(let[n,s]of Object.entries(this)){if(e)try{let o=e.call(s,i);if(o!==i){r[n]=String(o);continue}}catch(o){r[n]=`<<indescribable>>${JSON.stringify([String(o),n])}`;continue}if(typeof s==\"object\"&&s){let o;for(let a of runtimeArgs[0])try{o=s[a]();break}catch{}!o&&!String(s.toString).includes(\"[native code]\")&&(o=String(s)),o&&!o.startsWith(\"[object \")&&(r[n]=o.length>=t?o.slice(0,t)+\"\\u2026\":o)}}return r\n ;\n\n}",
|
||||
"function(){let t={__proto__:this.__proto__\n},e=Object.getOwnPropertyNames(this);for(let r=0;r<e.length;++r){let i=e[r],n=i>>>0;if(String(n>>>0)===i&&n>>>0!==4294967295)continue;let s=Object.getOwnPropertyDescriptor(this,i);s&&Object.defineProperty(t,i,s)}return t}",
|
||||
"function(){return[Symbol.for(\"debug.description\"),Symbol.for(\"nodejs.util.inspect.custom\")]\n}"
|
||||
);
|
||||
public static final Set<String> VSCODE_SELF = Set.of(
|
||||
"function(t,e){let r={\n},i=t===-1?0:t,n=e===-1?this.length:t+e;for(let s=i;s<n&&s<this.length;++s){let o=Object.getOwnPropertyDescriptor(this,s);o&&Object.defineProperty(r,s,o)}return r}"
|
||||
);
|
||||
|
||||
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}";
|
||||
public static final String VSCODE_STRINGIFY_VAL = "function(...runtimeArgs){\n let t = 1024; let e = null;\n if(e)try{let r=\"<<default preview>>\",i=e.call(this,r);if(i!==r)return String(i)}catch(r){return`<<indescribable>>${JSON.stringify([String(r),\"object\"])}`}if(typeof this==\"object\"&&this){let r;for(let i of[Symbol.for(\"debug.description\"),Symbol.for(\"nodejs.util.inspect.custom\")])try{r=this[i]();break}catch{}if(!r&&!String(this.toString).includes(\"[native code]\")&&(r=String(this)),r&&!r.startsWith(\"[object \"))return r.length>=t?r.slice(0,t)+\"\\u2026\":r}\n ;\n\n}";
|
||||
public static final String VSCODE_STRINGIFY_PROPS = "function(...runtimeArgs){\n let t = 1024; let e = null;\n let r={},i=\"<<default preview>>\";if(typeof this!=\"object\"||!this)return r;for(let[n,s]of Object.entries(this)){if(e)try{let o=e.call(s,i);if(o!==i){r[n]=String(o);continue}}catch(o){r[n]=`<<indescribable>>${JSON.stringify([String(o),n])}`;continue}if(typeof s==\"object\"&&s){let o;for(let a of runtimeArgs[0])try{o=s[a]();break}catch{}!o&&!String(s.toString).includes(\"[native code]\")&&(o=String(s)),o&&!o.startsWith(\"[object \")&&(r[n]=o.length>=t?o.slice(0,t)+\"\\u2026\":o)}}return r\n ;\n\n}";
|
||||
public static final String VSCODE_SYMBOL_REQUEST = "function(){return[Symbol.for(\"debug.description\"),Symbol.for(\"nodejs.util.inspect.custom\")]\n}";
|
||||
public static final String VSCODE_SHALLOW_COPY = "function(){let t={__proto__:this.__proto__\n},e=Object.getOwnPropertyNames(this);for(let r=0;r<e.length;++r){let i=e[r],n=i>>>0;if(String(n>>>0)===i&&n>>>0!==4294967295)continue;let s=Object.getOwnPropertyDescriptor(this,i);s&&Object.defineProperty(t,i,s)}return t}";
|
||||
public static final String VSCODE_FLATTEN_ARRAY = "function(t,e){let r={\n},i=t===-1?0:t,n=e===-1?this.length:t+e;for(let s=i;s<n&&s<this.length;++s){let o=Object.getOwnPropertyDescriptor(this,s);o&&Object.defineProperty(r,s,o)}return r}";
|
||||
public static final String VSCODE_CALL = "function(t){return t.call(this)\n}";
|
||||
public static final String VSCODE_AUTOCOMPLETE = "function(t,e,r){let n=r?\"variable\":\"property\",i=(l,p,f)=>{if(p!==\"function\")return n;if(l===\"constructor\")return\"class\";let m=String(f);return m.startsWith(\"class \")||m.includes(\"[native code]\")&&/^[A-Z]/.test(l)?\"class\":r?\"function\":\"method\"\n},o=l=>{switch(typeof l){case\"number\":case\"boolean\":return`${l}`;case\"object\":return l===null?\"null\":l.constructor.name||\"object\";case\"function\":return`fn(${new Array(l.length).fill(\"?\").join(\", \")})`;default:return typeof l}},s=[],a=new Set,u=\"~\",c=t===void 0?this:t;for(;c!=null;c=c.__proto__){u+=\"~\";let l=Object.getOwnPropertyNames(c).filter(p=>p.startsWith(e)&&!p.match(/^\\d+$/));for(let p of l){if(a.has(p))continue;a.add(p);let f=Object.getOwnPropertyDescriptor(c,p),m=n,h;try{let H=c[p];m=i(p,typeof f?.value,H),h=o(H)}catch{}s.push({label:p,sortText:u+p.replace(/^_+/,H=>\"{\".repeat(H.length)),type:m,detail:h})}r=!1}return{result:s,isArray:this instanceof Array}}";
|
||||
|
||||
private static enum State {
|
||||
RESUMED,
|
||||
@ -59,12 +67,14 @@ public class SimpleDebugger implements Debugger {
|
||||
public final Filename filename;
|
||||
public final String source;
|
||||
public final TreeSet<Location> breakpoints;
|
||||
public final SourceMap map;
|
||||
|
||||
public Source(int id, Filename filename, String source, TreeSet<Location> breakpoints) {
|
||||
public Source(int id, Filename filename, String source, TreeSet<Location> breakpoints, SourceMap map) {
|
||||
this.id = id;
|
||||
this.filename = filename;
|
||||
this.source = source;
|
||||
this.breakpoints = breakpoints;
|
||||
this.map = map;
|
||||
}
|
||||
}
|
||||
private static class Breakpoint {
|
||||
@ -104,7 +114,7 @@ public class SimpleDebugger implements Debugger {
|
||||
public boolean debugData = false;
|
||||
|
||||
public void updateLoc(Location loc) {
|
||||
serialized.set("location", serializeLocation(loc));
|
||||
if (loc == null) return;
|
||||
this.location = loc;
|
||||
}
|
||||
|
||||
@ -112,7 +122,6 @@ public class SimpleDebugger implements Debugger {
|
||||
this.frame = frame;
|
||||
this.func = frame.function;
|
||||
this.id = id;
|
||||
this.location = frame.function.loc();
|
||||
|
||||
this.global = frame.function.environment.global.obj;
|
||||
this.local = frame.getLocalScope(ctx, true);
|
||||
@ -120,13 +129,11 @@ public class SimpleDebugger implements Debugger {
|
||||
this.local.setPrototype(ctx, capture);
|
||||
this.capture.setPrototype(ctx, global);
|
||||
this.valstack = frame.getValStackScope(ctx);
|
||||
|
||||
if (location != null) {
|
||||
debugData = true;
|
||||
|
||||
this.serialized = new JSONMap()
|
||||
.set("callFrameId", id + "")
|
||||
.set("functionName", func.name)
|
||||
.set("location", serializeLocation(location))
|
||||
.set("scopeChain", new JSONList()
|
||||
.add(new JSONMap().set("type", "local").set("name", "Local Scope").set("object", serializeObj(ctx, local)))
|
||||
.add(new JSONMap().set("type", "closure").set("name", "Closure").set("object", serializeObj(ctx, capture)))
|
||||
@ -135,6 +142,20 @@ public class SimpleDebugger implements Debugger {
|
||||
);
|
||||
}
|
||||
}
|
||||
private class ObjRef {
|
||||
public final ObjectValue obj;
|
||||
public final Context ctx;
|
||||
public final HashSet<String> heldGroups = new HashSet<>();
|
||||
public boolean held = true;
|
||||
|
||||
public boolean shouldRelease() {
|
||||
return !held && heldGroups.size() == 0;
|
||||
}
|
||||
|
||||
public ObjRef(Context ctx, ObjectValue obj) {
|
||||
this.ctx = ctx;
|
||||
this.obj = obj;
|
||||
}
|
||||
}
|
||||
|
||||
private static class RunResult {
|
||||
@ -150,7 +171,7 @@ public class SimpleDebugger implements Debugger {
|
||||
}
|
||||
|
||||
public boolean enabled = true;
|
||||
public CatchType execptionType = CatchType.ALL;
|
||||
public CatchType execptionType = CatchType.NONE;
|
||||
public State state = State.RESUMED;
|
||||
|
||||
public final WebSocket ws;
|
||||
@ -171,22 +192,64 @@ public class SimpleDebugger implements Debugger {
|
||||
private HashMap<Integer, Frame> idToFrame = new HashMap<>();
|
||||
private HashMap<CodeFrame, Frame> codeFrameToFrame = new HashMap<>();
|
||||
|
||||
private HashMap<Integer, ObjectValue> idToObject = new HashMap<>();
|
||||
private HashMap<Integer, ObjRef> idToObject = new HashMap<>();
|
||||
private HashMap<ObjectValue, Integer> objectToId = new HashMap<>();
|
||||
private HashMap<ObjectValue, Context> objectToCtx = new HashMap<>();
|
||||
private HashMap<String, ArrayList<ObjectValue>> objectGroups = new HashMap<>();
|
||||
private HashMap<String, ArrayList<ObjRef>> objectGroups = new HashMap<>();
|
||||
|
||||
private Notifier updateNotifier = new Notifier();
|
||||
private boolean pendingPause = false;
|
||||
|
||||
private int nextId = new Random().nextInt() & 0x7FFFFFFF;
|
||||
private Location prevLocation = null;
|
||||
private int nextId = 0;
|
||||
private Frame stepOutFrame = null, currFrame = null;
|
||||
private int stepOutPtr = 0;
|
||||
|
||||
private int nextId() {
|
||||
return nextId++ ^ 1630022591 /* big prime */;
|
||||
private boolean compare(String src, String target) {
|
||||
if (src.length() != target.length()) return false;
|
||||
var diff = 0;
|
||||
var all = 0;
|
||||
|
||||
for (var i = 0; i < src.length(); i++) {
|
||||
var a = src.charAt(i);
|
||||
var b = target.charAt(i);
|
||||
var letter = Parsing.isLetter(a) && Parsing.isLetter(b);
|
||||
|
||||
if (a != b) {
|
||||
if (letter) diff++;
|
||||
else return false;
|
||||
}
|
||||
|
||||
private void updateFrames(Context ctx) {
|
||||
if (letter) all++;
|
||||
}
|
||||
|
||||
return diff / (float)all < .5f;
|
||||
}
|
||||
private boolean compare(String src, Set<String> target) {
|
||||
for (var el : target) {
|
||||
if (compare(src, el)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private int nextId() {
|
||||
return nextId++;
|
||||
}
|
||||
|
||||
private Location toCompiled(Location location) {
|
||||
var id = filenameToId.get(location.filename());
|
||||
if (id == null) return location;
|
||||
var map = idToSource.get(id).map;
|
||||
if (map == null) return location;
|
||||
return map.toCompiled(location);
|
||||
}
|
||||
private Location toOriginal(Location location) {
|
||||
var id = filenameToId.get(location.filename());
|
||||
if (id == null) return location;
|
||||
var map = idToSource.get(id).map;
|
||||
if (map == null) return location;
|
||||
return map.toOriginal(location);
|
||||
}
|
||||
|
||||
private synchronized void updateFrames(Context ctx) {
|
||||
var frame = ctx.peekFrame();
|
||||
if (frame == null) return;
|
||||
|
||||
@ -205,7 +268,10 @@ public class SimpleDebugger implements Debugger {
|
||||
var frames = ctx.frames();
|
||||
|
||||
for (var i = frames.size() - 1; i >= 0; i--) {
|
||||
res.add(codeFrameToFrame.get(frames.get(i)).serialized);
|
||||
var frame = codeFrameToFrame.get(frames.get(i));
|
||||
if (frame.location == null) continue;
|
||||
frame.serialized.set("location", serializeLocation(frame.location));
|
||||
if (frame.location != null) res.add(frame.serialized);
|
||||
}
|
||||
|
||||
return res;
|
||||
@ -245,13 +311,13 @@ public class SimpleDebugger implements Debugger {
|
||||
if (objectToId.containsKey(obj)) return objectToId.get(obj);
|
||||
else {
|
||||
int id = nextId();
|
||||
var ref = new ObjRef(ctx, obj);
|
||||
objectToId.put(obj, id);
|
||||
if (ctx != null) objectToCtx.put(obj, ctx);
|
||||
idToObject.put(id, obj);
|
||||
idToObject.put(id, ref);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
private JSONMap serializeObj(Context ctx, Object val) {
|
||||
private JSONMap serializeObj(Context ctx, Object val, boolean byValue) {
|
||||
val = Values.normalize(null, val);
|
||||
|
||||
if (val == Values.NULL) {
|
||||
@ -272,7 +338,7 @@ public class SimpleDebugger implements Debugger {
|
||||
if (obj instanceof FunctionValue) type = "function";
|
||||
if (obj instanceof ArrayValue) subtype = "array";
|
||||
|
||||
try { className = Values.toString(ctx, Values.getMember(ctx, obj.getMember(ctx, "constructor"), "name")); }
|
||||
try { className = Values.toString(ctx, Values.getMemberPath(ctx, obj, "constructor", "name")); }
|
||||
catch (Exception e) { }
|
||||
|
||||
var res = new JSONMap()
|
||||
@ -282,9 +348,28 @@ public class SimpleDebugger implements Debugger {
|
||||
if (subtype != null) res.set("subtype", subtype);
|
||||
if (className != null) {
|
||||
res.set("className", className);
|
||||
res.set("description", "{ " + className + " }");
|
||||
}
|
||||
|
||||
if (obj instanceof ArrayValue) res.set("description", "Array(" + ((ArrayValue)obj).size() + ")");
|
||||
else if (obj instanceof FunctionValue) res.set("description", obj.toString());
|
||||
else {
|
||||
var defaultToString = false;
|
||||
|
||||
try {
|
||||
defaultToString =
|
||||
Values.getMember(ctx, obj, "toString") ==
|
||||
Values.getMember(ctx, ctx.environment().proto("object"), "toString");
|
||||
}
|
||||
catch (Exception e) { }
|
||||
|
||||
try { res.set("description", className + (defaultToString ? "" : " { " + Values.toString(ctx, obj) + " }")); }
|
||||
catch (EngineException e) { res.set("description", className); }
|
||||
}
|
||||
|
||||
|
||||
if (byValue) try { res.put("value", JSON.fromJs(ctx, obj)); }
|
||||
catch (Exception e) { }
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -308,33 +393,44 @@ public class SimpleDebugger implements Debugger {
|
||||
|
||||
throw new IllegalArgumentException("Unexpected JS object.");
|
||||
}
|
||||
private void setObjectGroup(String name, Object val) {
|
||||
private JSONMap serializeObj(Context ctx, Object val) {
|
||||
return serializeObj(ctx, val, false);
|
||||
}
|
||||
private void addObjectGroup(String name, Object val) {
|
||||
if (val instanceof ObjectValue) {
|
||||
var obj = (ObjectValue)val;
|
||||
var id = objectToId.getOrDefault(obj, -1);
|
||||
if (id < 0) return;
|
||||
|
||||
if (objectGroups.containsKey(name)) objectGroups.get(name).add(obj);
|
||||
else objectGroups.put(name, new ArrayList<>(List.of(obj)));
|
||||
var ref = idToObject.get(id);
|
||||
|
||||
if (objectGroups.containsKey(name)) objectGroups.get(name).add(ref);
|
||||
else objectGroups.put(name, new ArrayList<>(List.of(ref)));
|
||||
|
||||
ref.heldGroups.add(name);
|
||||
}
|
||||
}
|
||||
private void releaseGroup(String name) {
|
||||
var objs = objectGroups.remove(name);
|
||||
|
||||
if (objs != null) {
|
||||
for (var obj : objs) {
|
||||
var id = objectToId.remove(obj);
|
||||
if (objs != null) for (var obj : objs) {
|
||||
if (obj.heldGroups.remove(name) && obj.shouldRelease()) {
|
||||
var id = objectToId.remove(obj.obj);
|
||||
if (id != null) idToObject.remove(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
private Object deserializeArgument(JSONMap val) {
|
||||
if (val.isString("objectId")) return idToObject.get(Integer.parseInt(val.string("objectId")));
|
||||
if (val.isString("objectId")) return idToObject.get(Integer.parseInt(val.string("objectId"))).obj;
|
||||
else if (val.isString("unserializableValue")) switch (val.string("unserializableValue")) {
|
||||
case "NaN": return Double.NaN;
|
||||
case "-Infinity": return Double.NEGATIVE_INFINITY;
|
||||
case "Infinity": return Double.POSITIVE_INFINITY;
|
||||
case "-0": return -0.;
|
||||
}
|
||||
return JSON.toJs(val.get("value"));
|
||||
var res = val.get("value");
|
||||
if (res == null) return null;
|
||||
else return JSON.toJs(res);
|
||||
}
|
||||
|
||||
private JSONMap serializeException(Context ctx, EngineException err) {
|
||||
@ -350,7 +446,6 @@ public class SimpleDebugger implements Debugger {
|
||||
var res = new JSONMap()
|
||||
.set("exceptionId", nextId())
|
||||
.set("exception", serializeObj(ctx, err.value))
|
||||
.set("exception", serializeObj(ctx, err.value))
|
||||
.set("text", text);
|
||||
|
||||
return res;
|
||||
@ -398,16 +493,11 @@ public class SimpleDebugger implements Debugger {
|
||||
}
|
||||
|
||||
private RunResult run(Frame codeFrame, String code) {
|
||||
if (codeFrame == null) return new RunResult(null, code, new EngineException("Invalid code frame!"));
|
||||
var engine = new Engine(false);
|
||||
var env = codeFrame.func.environment.fork();
|
||||
|
||||
ObjectValue global = env.global.obj,
|
||||
capture = codeFrame.frame.getCaptureScope(null, enabled),
|
||||
local = codeFrame.frame.getLocalScope(null, enabled);
|
||||
|
||||
capture.setPrototype(null, global);
|
||||
local.setPrototype(null, capture);
|
||||
env.global = new GlobalScope(local);
|
||||
env.global = new GlobalScope(codeFrame.local);
|
||||
|
||||
var ctx = new Context(engine).pushEnv(env);
|
||||
var awaiter = engine.pushMsg(false, ctx, new Filename("jscript", "eval"), code, codeFrame.frame.thisArg, codeFrame.frame.args);
|
||||
@ -418,7 +508,80 @@ public class SimpleDebugger implements Debugger {
|
||||
catch (EngineException e) { return new RunResult(ctx, null, e); }
|
||||
}
|
||||
|
||||
@Override public void enable(V8Message msg) {
|
||||
private ObjectValue vscodeAutoSuggest(Context ctx, Object target, String query, boolean variable) {
|
||||
var res = new ArrayValue();
|
||||
var passed = new HashSet<String>();
|
||||
var tildas = "~";
|
||||
if (target == null) target = ctx.environment().getGlobal();
|
||||
|
||||
for (var proto = target; proto != null && proto != Values.NULL; proto = Values.getPrototype(ctx, proto)) {
|
||||
for (var el : Values.getMembers(ctx, proto, true, true)) {
|
||||
var strKey = Values.toString(ctx, el);
|
||||
if (passed.contains(strKey)) continue;
|
||||
passed.add(strKey);
|
||||
|
||||
var val = Values.getMember(ctx, Values.getMemberDescriptor(ctx, proto, el), "value");
|
||||
var desc = new ObjectValue();
|
||||
var sortText = "";
|
||||
if (strKey.startsWith(query)) sortText += "0@";
|
||||
else if (strKey.toLowerCase().startsWith(query.toLowerCase())) sortText += "1@";
|
||||
else if (strKey.contains(query)) sortText += "2@";
|
||||
else if (strKey.toLowerCase().contains(query.toLowerCase())) sortText += "3@";
|
||||
else sortText += "4@";
|
||||
sortText += tildas + strKey;
|
||||
|
||||
desc.defineProperty(ctx, "label", strKey);
|
||||
desc.defineProperty(ctx, "sortText", sortText);
|
||||
|
||||
if (val instanceof FunctionValue) {
|
||||
if (strKey.equals("constructor")) desc.defineProperty(ctx, "type", "name");
|
||||
else desc.defineProperty(ctx, "type", variable ? "function" : "method");
|
||||
}
|
||||
else desc.defineProperty(ctx, "type", variable ? "variable" : "property");
|
||||
|
||||
switch (Values.type(val)) {
|
||||
case "number":
|
||||
case "boolean":
|
||||
desc.defineProperty(ctx, "detail", Values.toString(ctx, val));
|
||||
break;
|
||||
case "object":
|
||||
if (val == Values.NULL) desc.defineProperty(ctx, "detail", "null");
|
||||
else try {
|
||||
desc.defineProperty(ctx, "detail", Values.getMemberPath(ctx, target, "constructor", "name"));
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
desc.defineProperty(ctx, "detail", "object");
|
||||
}
|
||||
break;
|
||||
case "function": {
|
||||
var type = "fn(";
|
||||
for (var i = 0; i < ((FunctionValue)val).length; i++) {
|
||||
if (i != 0) type += ",";
|
||||
type += "?";
|
||||
}
|
||||
type += ")";
|
||||
desc.defineProperty(ctx, "detail", type);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
desc.defineProperty(ctx, "type", Values.type(val));
|
||||
break;
|
||||
}
|
||||
|
||||
res.set(ctx, res.size(), desc);
|
||||
}
|
||||
|
||||
tildas += "~";
|
||||
variable = true;
|
||||
}
|
||||
|
||||
var resObj = new ObjectValue();
|
||||
resObj.defineProperty(ctx, "result", res);
|
||||
resObj.defineProperty(ctx, "isArray", target instanceof ArrayValue);
|
||||
return resObj;
|
||||
}
|
||||
|
||||
@Override public synchronized void enable(V8Message msg) {
|
||||
enabled = true;
|
||||
ws.send(msg.respond());
|
||||
|
||||
@ -427,17 +590,17 @@ public class SimpleDebugger implements Debugger {
|
||||
|
||||
updateNotifier.next();
|
||||
}
|
||||
@Override public void disable(V8Message msg) {
|
||||
@Override public synchronized void disable(V8Message msg) {
|
||||
enabled = false;
|
||||
ws.send(msg.respond());
|
||||
updateNotifier.next();
|
||||
}
|
||||
|
||||
@Override public void getScriptSource(V8Message msg) {
|
||||
@Override public synchronized void getScriptSource(V8Message msg) {
|
||||
int id = Integer.parseInt(msg.params.string("scriptId"));
|
||||
ws.send(msg.respond(new JSONMap().set("scriptSource", idToSource.get(id).source)));
|
||||
}
|
||||
@Override public void getPossibleBreakpoints(V8Message msg) {
|
||||
@Override public synchronized void getPossibleBreakpoints(V8Message msg) {
|
||||
var src = idToSource.get(Integer.parseInt(msg.params.map("start").string("scriptId")));
|
||||
var start = deserializeLocation(msg.params.get("start"), false);
|
||||
var end = msg.params.isMap("end") ? deserializeLocation(msg.params.get("end"), false) : null;
|
||||
@ -452,26 +615,16 @@ public class SimpleDebugger implements Debugger {
|
||||
ws.send(msg.respond(new JSONMap().set("locations", res)));
|
||||
}
|
||||
|
||||
@Override public void pause(V8Message msg) {
|
||||
@Override public synchronized void pause(V8Message msg) {
|
||||
pendingPause = true;
|
||||
ws.send(msg.respond());
|
||||
}
|
||||
|
||||
@Override public void resume(V8Message msg) {
|
||||
@Override public synchronized void resume(V8Message msg) {
|
||||
resume(State.RESUMED);
|
||||
ws.send(msg.respond(new JSONMap()));
|
||||
}
|
||||
|
||||
@Override public void setBreakpoint(V8Message msg) {
|
||||
// int id = nextId();
|
||||
// var loc = deserializeLocation(msg.params.get("location"), true);
|
||||
// var bpt = new Breakpoint(id, loc, null);
|
||||
// breakpoints.put(loc, bpt);
|
||||
// idToBrpt.put(id, bpt);
|
||||
// ws.send(msg.respond(new JSONMap()
|
||||
// .set("breakpointId", id)
|
||||
// .set("actualLocation", serializeLocation(loc))
|
||||
// ));
|
||||
}
|
||||
@Override public void setBreakpointByUrl(V8Message msg) {
|
||||
@Override public synchronized void setBreakpointByUrl(V8Message msg) {
|
||||
var line = (int)msg.params.number("lineNumber") + 1;
|
||||
var col = (int)msg.params.number("columnNumber", 0) + 1;
|
||||
var cond = msg.params.string("condition", "").trim();
|
||||
@ -506,7 +659,7 @@ public class SimpleDebugger implements Debugger {
|
||||
.set("locations", locs)
|
||||
));
|
||||
}
|
||||
@Override public void removeBreakpoint(V8Message msg) {
|
||||
@Override public synchronized void removeBreakpoint(V8Message msg) {
|
||||
var id = Integer.parseInt(msg.params.string("breakpointId"));
|
||||
|
||||
if (idToBptCand.containsKey(id)) {
|
||||
@ -523,7 +676,7 @@ public class SimpleDebugger implements Debugger {
|
||||
}
|
||||
ws.send(msg.respond());
|
||||
}
|
||||
@Override public void continueToLocation(V8Message msg) {
|
||||
@Override public synchronized void continueToLocation(V8Message msg) {
|
||||
var loc = deserializeLocation(msg.params.get("location"), true);
|
||||
|
||||
tmpBreakpts.add(loc);
|
||||
@ -532,39 +685,48 @@ public class SimpleDebugger implements Debugger {
|
||||
ws.send(msg.respond());
|
||||
}
|
||||
|
||||
@Override public void setPauseOnExceptions(V8Message msg) {
|
||||
ws.send(new V8Error("i dont wanna to"));
|
||||
@Override public synchronized void setPauseOnExceptions(V8Message msg) {
|
||||
switch (msg.params.string("state")) {
|
||||
case "none": execptionType = CatchType.NONE; break;
|
||||
case "all": execptionType = CatchType.ALL; break;
|
||||
case "uncaught": execptionType = CatchType.UNCAUGHT; break;
|
||||
default:
|
||||
ws.send(new V8Error("Invalid exception pause type."));
|
||||
return;
|
||||
}
|
||||
|
||||
@Override public void stepInto(V8Message msg) {
|
||||
ws.send(msg.respond());
|
||||
}
|
||||
|
||||
@Override public synchronized void stepInto(V8Message msg) {
|
||||
if (state == State.RESUMED) ws.send(new V8Error("Debugger is resumed."));
|
||||
else {
|
||||
prevLocation = currFrame.location;
|
||||
stepOutFrame = currFrame;
|
||||
stepOutPtr = currFrame.frame.codePtr;
|
||||
resume(State.STEPPING_IN);
|
||||
ws.send(msg.respond());
|
||||
}
|
||||
}
|
||||
@Override public void stepOut(V8Message msg) {
|
||||
@Override public synchronized void stepOut(V8Message msg) {
|
||||
if (state == State.RESUMED) ws.send(new V8Error("Debugger is resumed."));
|
||||
else {
|
||||
prevLocation = currFrame.location;
|
||||
stepOutFrame = currFrame;
|
||||
stepOutPtr = currFrame.frame.codePtr;
|
||||
resume(State.STEPPING_OUT);
|
||||
ws.send(msg.respond());
|
||||
}
|
||||
}
|
||||
@Override public void stepOver(V8Message msg) {
|
||||
@Override public synchronized void stepOver(V8Message msg) {
|
||||
if (state == State.RESUMED) ws.send(new V8Error("Debugger is resumed."));
|
||||
else {
|
||||
prevLocation = currFrame.location;
|
||||
stepOutFrame = currFrame;
|
||||
stepOutPtr = currFrame.frame.codePtr;
|
||||
resume(State.STEPPING_OVER);
|
||||
ws.send(msg.respond());
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void evaluateOnCallFrame(V8Message msg) {
|
||||
@Override public synchronized void evaluateOnCallFrame(V8Message msg) {
|
||||
var cfId = Integer.parseInt(msg.params.string("callFrameId"));
|
||||
var expr = msg.params.string("expression");
|
||||
var group = msg.params.string("objectGroup", null);
|
||||
@ -572,32 +734,37 @@ public class SimpleDebugger implements Debugger {
|
||||
var cf = idToFrame.get(cfId);
|
||||
var res = run(cf, expr);
|
||||
|
||||
if (group != null) setObjectGroup(group, res.result);
|
||||
if (group != null) addObjectGroup(group, res.result);
|
||||
|
||||
if (res.error != null) ws.send(msg.respond(new JSONMap().set("exceptionDetails", serializeObj(res.ctx, res.result))));
|
||||
|
||||
ws.send(msg.respond(new JSONMap().set("result", serializeObj(res.ctx, res.result))));
|
||||
if (res.error != null) ws.send(msg.respond(new JSONMap().set("exceptionDetails", serializeException(res.ctx, res.error))));
|
||||
else ws.send(msg.respond(new JSONMap().set("result", serializeObj(res.ctx, res.result))));
|
||||
}
|
||||
|
||||
@Override public void releaseObjectGroup(V8Message msg) {
|
||||
@Override public synchronized void releaseObjectGroup(V8Message msg) {
|
||||
var group = msg.params.string("objectGroup");
|
||||
releaseGroup(group);
|
||||
ws.send(msg.respond());
|
||||
}
|
||||
@Override
|
||||
public void releaseObject(V8Message msg) {
|
||||
@Override public synchronized void releaseObject(V8Message msg) {
|
||||
var id = Integer.parseInt(msg.params.string("objectId"));
|
||||
var obj = idToObject.remove(id);
|
||||
if (obj != null) objectToId.remove(obj);
|
||||
var ref = idToObject.get(id);
|
||||
ref.held = false;
|
||||
|
||||
if (ref.shouldRelease()) {
|
||||
objectToId.remove(ref.obj);
|
||||
idToObject.remove(id);
|
||||
}
|
||||
|
||||
ws.send(msg.respond());
|
||||
}
|
||||
@Override public void getProperties(V8Message msg) {
|
||||
var obj = idToObject.get(Integer.parseInt(msg.params.string("objectId")));
|
||||
@Override public synchronized void getProperties(V8Message msg) {
|
||||
var ref = idToObject.get(Integer.parseInt(msg.params.string("objectId")));
|
||||
var obj = ref.obj;
|
||||
|
||||
var res = new JSONList();
|
||||
var ctx = objectToCtx.get(obj);
|
||||
var ctx = ref.ctx;
|
||||
|
||||
if (obj != emptyObject) {
|
||||
if (obj != emptyObject && obj != null) {
|
||||
for (var key : obj.keys(true)) {
|
||||
var propDesc = new JSONMap();
|
||||
|
||||
@ -637,7 +804,7 @@ public class SimpleDebugger implements Debugger {
|
||||
|
||||
ws.send(msg.respond(new JSONMap().set("result", res)));
|
||||
}
|
||||
@Override public void callFunctionOn(V8Message msg) {
|
||||
@Override public synchronized void callFunctionOn(V8Message msg) {
|
||||
var src = msg.params.string("functionDeclaration");
|
||||
var args = msg.params
|
||||
.list("arguments", new JSONList())
|
||||
@ -645,9 +812,11 @@ public class SimpleDebugger implements Debugger {
|
||||
.map(v -> v.map())
|
||||
.map(this::deserializeArgument)
|
||||
.collect(Collectors.toList());
|
||||
var byValue = msg.params.bool("returnByValue", false);
|
||||
|
||||
var thisArg = idToObject.get(Integer.parseInt(msg.params.string("objectId")));
|
||||
var ctx = objectToCtx.get(thisArg);
|
||||
var thisArgRef = idToObject.get(Integer.parseInt(msg.params.string("objectId")));
|
||||
var thisArg = thisArgRef.obj;
|
||||
var ctx = thisArgRef.ctx;
|
||||
|
||||
while (true) {
|
||||
var start = src.lastIndexOf("//# sourceURL=");
|
||||
@ -658,49 +827,38 @@ public class SimpleDebugger implements Debugger {
|
||||
}
|
||||
|
||||
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;
|
||||
Object res = null;
|
||||
if (compare(src, VSCODE_EMPTY)) res = emptyObject;
|
||||
else if (compare(src, VSCODE_SELF)) res = thisArg;
|
||||
else if (compare(src, CHROME_GET_PROP_FUNC)) {
|
||||
res = thisArg;
|
||||
for (var el : JSON.parse(null, (String)args.get(0)).list()) res = Values.getMember(ctx, res, JSON.toJs(el));
|
||||
}
|
||||
case VSCODE_STRINGIFY_VAL:
|
||||
case VSCODE_STRINGIFY_PROPS:
|
||||
case VSCODE_SHALLOW_COPY:
|
||||
case VSCODE_SYMBOL_REQUEST:
|
||||
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_CALL: {
|
||||
else if (compare(src, 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:
|
||||
else if (compare(src, VSCODE_AUTOCOMPLETE)) {
|
||||
var target = args.get(0);
|
||||
if (target == null) target = thisArg;
|
||||
res = vscodeAutoSuggest(ctx, target, Values.toString(ctx, args.get(1)), Values.toBoolean(args.get(2)));
|
||||
}
|
||||
else {
|
||||
ws.send(new V8Error("Please use well-known functions with callFunctionOn"));
|
||||
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))));
|
||||
// }
|
||||
return;
|
||||
}
|
||||
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, res, byValue))));
|
||||
}
|
||||
catch (EngineException e) { ws.send(msg.respond(new JSONMap().set("exceptionDetails", serializeException(ctx, e)))); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runtimeEnable(V8Message msg) {
|
||||
@Override public synchronized void runtimeEnable(V8Message msg) {
|
||||
ws.send(msg.respond());
|
||||
}
|
||||
|
||||
@Override public void onSource(Filename filename, String source, TreeSet<Location> locations) {
|
||||
@Override public void onSource(Filename filename, String source, TreeSet<Location> locations, SourceMap map) {
|
||||
int id = nextId();
|
||||
var src = new Source(id, filename, source, locations);
|
||||
var src = new Source(id, filename, source, locations, map);
|
||||
|
||||
idToSource.put(id, src);
|
||||
filenameToId.put(filename, id);
|
||||
@ -720,87 +878,103 @@ public class SimpleDebugger implements Debugger {
|
||||
@Override public boolean onInstruction(Context ctx, CodeFrame cf, Instruction instruction, Object returnVal, EngineException error, boolean caught) {
|
||||
if (!enabled) return false;
|
||||
|
||||
updateFrames(ctx);
|
||||
var frame = codeFrameToFrame.get(cf);
|
||||
boolean isBreakpointable;
|
||||
Location loc;
|
||||
Frame frame;
|
||||
|
||||
synchronized (this) {
|
||||
frame = codeFrameToFrame.get(cf);
|
||||
|
||||
if (!frame.debugData) return false;
|
||||
|
||||
if (instruction.location != null) frame.updateLoc(instruction.location);
|
||||
var loc = frame.location;
|
||||
var isBreakpointable = loc != null && (
|
||||
idToSource.get(filenameToId.get(loc.filename())).breakpoints.contains(loc) ||
|
||||
returnVal != Runners.NO_RETURN
|
||||
);
|
||||
|
||||
// TODO: FIXXXX
|
||||
// if (error != null && !caught && StackData.frames(ctx).size() > 1) error = null;
|
||||
if (instruction.location != null) frame.updateLoc(toCompiled(instruction.location));
|
||||
loc = frame.location;
|
||||
isBreakpointable = loc != null && (instruction.breakpoint.shouldStepIn());
|
||||
|
||||
if (error != null && (execptionType == CatchType.ALL || execptionType == CatchType.UNCAUGHT && !caught)) {
|
||||
pauseException(ctx);
|
||||
}
|
||||
else if (loc != null && (state == State.STEPPING_IN || state == State.STEPPING_OVER) && returnVal != Runners.NO_RETURN && stepOutFrame == frame) {
|
||||
pauseDebug(ctx, null);
|
||||
}
|
||||
else if (isBreakpointable && locToBreakpoint.containsKey(loc)) {
|
||||
var bp = locToBreakpoint.get(loc);
|
||||
var ok = bp.condition == null ? true : Values.toBoolean(run(currFrame, bp.condition).result);
|
||||
if (ok) pauseDebug(ctx, locToBreakpoint.get(loc));
|
||||
}
|
||||
else if (isBreakpointable && tmpBreakpts.remove(loc)) pauseDebug(ctx, null);
|
||||
else if (isBreakpointable && pendingPause) {
|
||||
pauseDebug(ctx, null);
|
||||
pendingPause = false;
|
||||
}
|
||||
else if (instruction.type == Type.NOP && instruction.match("debug")) pauseDebug(ctx, null);
|
||||
}
|
||||
|
||||
|
||||
while (enabled) {
|
||||
synchronized (this) {
|
||||
switch (state) {
|
||||
case PAUSED_EXCEPTION:
|
||||
case PAUSED_NORMAL: break;
|
||||
|
||||
case STEPPING_OUT:
|
||||
case RESUMED: return false;
|
||||
|
||||
case STEPPING_IN:
|
||||
if (!prevLocation.equals(loc)) {
|
||||
if (isBreakpointable) pauseDebug(ctx, null);
|
||||
else if (returnVal != Runners.NO_RETURN) pauseDebug(ctx, null);
|
||||
else return false;
|
||||
}
|
||||
else return false;
|
||||
break;
|
||||
case STEPPING_OVER:
|
||||
if (stepOutFrame.frame == frame.frame) {
|
||||
if (isBreakpointable && (
|
||||
!loc.filename().equals(prevLocation.filename()) ||
|
||||
loc.line() != prevLocation.line()
|
||||
)) pauseDebug(ctx, null);
|
||||
else return false;
|
||||
if (returnVal != Runners.NO_RETURN || error != null) {
|
||||
state = State.STEPPING_OUT;
|
||||
continue;
|
||||
}
|
||||
else return false;
|
||||
else if (stepOutPtr != frame.frame.codePtr) {
|
||||
if (state == State.STEPPING_IN && instruction.breakpoint.shouldStepIn()) {
|
||||
pauseDebug(ctx, null);
|
||||
break;
|
||||
}
|
||||
else if (state == State.STEPPING_OVER && instruction.breakpoint.shouldStepOver()) {
|
||||
pauseDebug(ctx, null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
updateNotifier.await();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@Override public void onFramePush(Context ctx, CodeFrame frame) {
|
||||
var prevFrame = currFrame;
|
||||
updateFrames(ctx);
|
||||
|
||||
if (stepOutFrame != null && stepOutFrame.frame == prevFrame.frame && state == State.STEPPING_IN) {
|
||||
stepOutFrame = currFrame;
|
||||
}
|
||||
}
|
||||
@Override public void onFramePop(Context ctx, CodeFrame frame) {
|
||||
updateFrames(ctx);
|
||||
|
||||
try { idToFrame.remove(codeFrameToFrame.remove(frame).id); }
|
||||
catch (NullPointerException e) { }
|
||||
|
||||
if (ctx.frames().size() == 0) resume(State.RESUMED);
|
||||
else if (stepOutFrame != null && stepOutFrame.frame == frame &&
|
||||
(state == State.STEPPING_OUT || state == State.STEPPING_IN || state == State.STEPPING_OVER)
|
||||
) {
|
||||
// if (state == State.STEPPING_OVER) state = State.STEPPING_IN;
|
||||
// else {
|
||||
pauseDebug(ctx, null);
|
||||
updateNotifier.await();
|
||||
// }
|
||||
if (ctx.frames().size() == 0) {
|
||||
if (state == State.PAUSED_EXCEPTION || state == State.PAUSED_NORMAL) resume(State.RESUMED);
|
||||
}
|
||||
else if (stepOutFrame != null && stepOutFrame.frame == frame && state == State.STEPPING_OUT) {
|
||||
state = State.STEPPING_IN;
|
||||
stepOutFrame = currFrame;
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void connect() {
|
||||
@Override public synchronized void connect() {
|
||||
if (!target.attachDebugger(this)) {
|
||||
ws.send(new V8Error("A debugger is already attached to this engine."));
|
||||
}
|
||||
}
|
||||
@Override public void disconnect() {
|
||||
@Override public synchronized void disconnect() {
|
||||
target.detachDebugger();
|
||||
enabled = false;
|
||||
updateNotifier.next();
|
||||
|
@ -17,32 +17,72 @@ import me.topchetoeu.jscript.exceptions.EngineException;
|
||||
import me.topchetoeu.jscript.exceptions.InterruptException;
|
||||
|
||||
public class CodeFrame {
|
||||
public static enum TryState {
|
||||
TRY,
|
||||
CATCH,
|
||||
FINALLY,
|
||||
}
|
||||
|
||||
public static class TryCtx {
|
||||
public static final int STATE_TRY = 0;
|
||||
public static final int STATE_CATCH = 1;
|
||||
public static final int STATE_FINALLY_THREW = 2;
|
||||
public static final int STATE_FINALLY_RETURNED = 3;
|
||||
public static final int STATE_FINALLY_JUMPED = 4;
|
||||
public final int start, end, catchStart, finallyStart;
|
||||
public final int restoreStackPtr;
|
||||
public final TryState state;
|
||||
public final EngineException error;
|
||||
public final PendingResult result;
|
||||
|
||||
public final boolean hasCatch, hasFinally;
|
||||
public final int tryStart, catchStart, finallyStart, end;
|
||||
public int state;
|
||||
public Object retVal;
|
||||
public EngineException err;
|
||||
public int jumpPtr;
|
||||
public boolean hasCatch() { return catchStart >= 0; }
|
||||
public boolean hasFinally() { return finallyStart >= 0; }
|
||||
|
||||
public TryCtx(int tryStart, int tryN, int catchN, int finallyN) {
|
||||
hasCatch = catchN >= 0;
|
||||
hasFinally = finallyN >= 0;
|
||||
public boolean inBounds(int ptr) {
|
||||
return ptr >= start && ptr < end;
|
||||
}
|
||||
|
||||
if (catchN < 0) catchN = 0;
|
||||
if (finallyN < 0) finallyN = 0;
|
||||
public TryCtx _catch(EngineException e) {
|
||||
e.setCause(error);
|
||||
return new TryCtx(TryState.CATCH, e, result, restoreStackPtr, start, end, -1, finallyStart);
|
||||
}
|
||||
public TryCtx _finally(PendingResult res) {
|
||||
return new TryCtx(TryState.FINALLY, error, res, restoreStackPtr, start, end, -1, -1);
|
||||
}
|
||||
|
||||
this.tryStart = tryStart;
|
||||
this.catchStart = tryStart + tryN;
|
||||
this.finallyStart = catchStart + catchN;
|
||||
this.end = finallyStart + finallyN;
|
||||
this.jumpPtr = end;
|
||||
public TryCtx(TryState state, EngineException err, PendingResult res, int stackPtr, int start, int end, int catchStart, int finallyStart) {
|
||||
this.catchStart = catchStart;
|
||||
this.finallyStart = finallyStart;
|
||||
this.restoreStackPtr = stackPtr;
|
||||
this.result = res == null ? PendingResult.ofNone() : res;
|
||||
this.state = state;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.error = err;
|
||||
}
|
||||
}
|
||||
|
||||
private static class PendingResult {
|
||||
public final boolean isReturn, isJump, isThrow;
|
||||
public final Object value;
|
||||
public final EngineException error;
|
||||
public final int ptr;
|
||||
|
||||
private PendingResult(boolean isReturn, boolean isJump, boolean isThrow, Object value, EngineException error, int ptr) {
|
||||
this.isReturn = isReturn;
|
||||
this.isJump = isJump;
|
||||
this.isThrow = isThrow;
|
||||
this.value = value;
|
||||
this.error = error;
|
||||
this.ptr = ptr;
|
||||
}
|
||||
|
||||
public static PendingResult ofNone() {
|
||||
return new PendingResult(false, false, false, null, null, 0);
|
||||
}
|
||||
public static PendingResult ofReturn(Object value) {
|
||||
return new PendingResult(true, false, false, value, null, 0);
|
||||
}
|
||||
public static PendingResult ofThrow(EngineException error) {
|
||||
return new PendingResult(false, false, true, null, error, 0);
|
||||
}
|
||||
public static PendingResult ofJump(int codePtr) {
|
||||
return new PendingResult(false, true, false, null, null, codePtr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,11 +91,10 @@ public class CodeFrame {
|
||||
public final Object[] args;
|
||||
public final Stack<TryCtx> tryStack = new Stack<>();
|
||||
public final CodeFunction function;
|
||||
|
||||
public Object[] stack = new Object[32];
|
||||
public int stackPtr = 0;
|
||||
public int codePtr = 0;
|
||||
public boolean jumpFlag = false;
|
||||
public boolean jumpFlag = false, popTryFlag = false;
|
||||
private Location prevLoc = null;
|
||||
|
||||
public ObjectValue getLocalScope(Context ctx, boolean props) {
|
||||
@ -105,9 +144,9 @@ public class CodeFrame {
|
||||
};
|
||||
}
|
||||
|
||||
public void addTry(int n, int catchN, int finallyN) {
|
||||
var res = new TryCtx(codePtr + 1, n, catchN, finallyN);
|
||||
if (!tryStack.empty()) res.err = tryStack.peek().err;
|
||||
public void addTry(int start, int end, int catchStart, int finallyStart) {
|
||||
var err = tryStack.empty() ? null : tryStack.peek().error;
|
||||
var res = new TryCtx(TryState.TRY, err, null, stackPtr, start, end, catchStart, finallyStart);
|
||||
|
||||
tryStack.add(res);
|
||||
}
|
||||
@ -145,10 +184,6 @@ public class CodeFrame {
|
||||
stack[stackPtr++] = Values.normalize(ctx, val);
|
||||
}
|
||||
|
||||
private void setCause(Context ctx, EngineException err, EngineException cause) {
|
||||
err.setCause(cause);
|
||||
}
|
||||
|
||||
public Object next(Context ctx, Object value, Object returnValue, EngineException error) {
|
||||
if (value != Runners.NO_RETURN) push(ctx, value);
|
||||
|
||||
@ -166,7 +201,7 @@ public class CodeFrame {
|
||||
if (instr.location != null) prevLoc = instr.location;
|
||||
|
||||
try {
|
||||
this.jumpFlag = false;
|
||||
this.jumpFlag = this.popTryFlag = false;
|
||||
returnValue = Runners.exec(ctx, instr, this);
|
||||
}
|
||||
catch (EngineException e) {
|
||||
@ -177,111 +212,75 @@ public class CodeFrame {
|
||||
catch (EngineException e) { error = e; }
|
||||
}
|
||||
|
||||
while (!tryStack.empty()) {
|
||||
if (!tryStack.empty()) {
|
||||
var tryCtx = tryStack.peek();
|
||||
var newState = -1;
|
||||
var pendingResult = PendingResult.ofNone();
|
||||
boolean shouldCatch = false, shouldFinally = false, shouldExit = this.popTryFlag;
|
||||
|
||||
switch (tryCtx.state) {
|
||||
case TryCtx.STATE_TRY:
|
||||
if (error != null) {
|
||||
if (tryCtx.hasCatch) {
|
||||
tryCtx.err = error;
|
||||
newState = TryCtx.STATE_CATCH;
|
||||
if (tryCtx.state != TryState.FINALLY) {
|
||||
if (error != null && tryCtx.hasCatch()) shouldCatch = true;
|
||||
else if (error != null && tryCtx.hasFinally()) {
|
||||
pendingResult = PendingResult.ofThrow(error);
|
||||
shouldFinally = true;
|
||||
}
|
||||
else if (tryCtx.hasFinally) {
|
||||
tryCtx.err = error;
|
||||
newState = TryCtx.STATE_FINALLY_THREW;
|
||||
else if (returnValue != Runners.NO_RETURN && tryCtx.hasFinally()) {
|
||||
pendingResult = PendingResult.ofReturn(returnValue);
|
||||
shouldFinally = true;
|
||||
}
|
||||
break;
|
||||
else if (jumpFlag && !tryCtx.inBounds(codePtr) && tryCtx.hasFinally()) {
|
||||
pendingResult = PendingResult.ofJump(codePtr);
|
||||
shouldFinally = true;
|
||||
}
|
||||
else if (returnValue != Runners.NO_RETURN) {
|
||||
if (tryCtx.hasFinally) {
|
||||
tryCtx.retVal = returnValue;
|
||||
newState = TryCtx.STATE_FINALLY_RETURNED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (codePtr >= tryCtx.tryStart && codePtr < tryCtx.catchStart) return Runners.NO_RETURN;
|
||||
|
||||
if (tryCtx.hasFinally) {
|
||||
if (jumpFlag) tryCtx.jumpPtr = codePtr;
|
||||
else tryCtx.jumpPtr = tryCtx.end;
|
||||
newState = TryCtx.STATE_FINALLY_JUMPED;
|
||||
}
|
||||
else codePtr = tryCtx.end;
|
||||
break;
|
||||
case TryCtx.STATE_CATCH:
|
||||
if (error != null) {
|
||||
setCause(ctx, error, tryCtx.err);
|
||||
if (tryCtx.hasFinally) {
|
||||
tryCtx.err = error;
|
||||
newState = TryCtx.STATE_FINALLY_THREW;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (returnValue != Runners.NO_RETURN) {
|
||||
if (tryCtx.hasFinally) {
|
||||
tryCtx.retVal = returnValue;
|
||||
newState = TryCtx.STATE_FINALLY_RETURNED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (codePtr >= tryCtx.catchStart && codePtr < tryCtx.finallyStart) return Runners.NO_RETURN;
|
||||
|
||||
if (tryCtx.hasFinally) {
|
||||
if (jumpFlag) tryCtx.jumpPtr = codePtr;
|
||||
else tryCtx.jumpPtr = tryCtx.end;
|
||||
newState = TryCtx.STATE_FINALLY_JUMPED;
|
||||
}
|
||||
else codePtr = tryCtx.end;
|
||||
break;
|
||||
case TryCtx.STATE_FINALLY_THREW:
|
||||
if (error != null) setCause(ctx, error, tryCtx.err);
|
||||
else if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) error = tryCtx.err;
|
||||
else if (returnValue == Runners.NO_RETURN) return Runners.NO_RETURN;
|
||||
break;
|
||||
case TryCtx.STATE_FINALLY_RETURNED:
|
||||
if (error != null) setCause(ctx, error, tryCtx.err);
|
||||
if (returnValue == Runners.NO_RETURN) {
|
||||
if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) returnValue = tryCtx.retVal;
|
||||
else return Runners.NO_RETURN;
|
||||
}
|
||||
break;
|
||||
case TryCtx.STATE_FINALLY_JUMPED:
|
||||
if (error != null) setCause(ctx, error, tryCtx.err);
|
||||
else if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) {
|
||||
if (!jumpFlag) codePtr = tryCtx.jumpPtr;
|
||||
else codePtr = tryCtx.end;
|
||||
}
|
||||
else if (returnValue == Runners.NO_RETURN) return Runners.NO_RETURN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (tryCtx.state == TryCtx.STATE_CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
|
||||
if (tryCtx.hasCatch() && shouldCatch) {
|
||||
if (tryCtx.state != TryState.CATCH) scope.catchVars.add(new ValueVariable(false, error.value));
|
||||
|
||||
if (newState == -1) {
|
||||
var err = tryCtx.err;
|
||||
tryStack.pop();
|
||||
if (!tryStack.isEmpty()) tryStack.peek().err = err;
|
||||
continue;
|
||||
}
|
||||
|
||||
tryCtx.state = newState;
|
||||
switch (newState) {
|
||||
case TryCtx.STATE_CATCH:
|
||||
scope.catchVars.add(new ValueVariable(false, tryCtx.err.value));
|
||||
codePtr = tryCtx.catchStart;
|
||||
ctx.engine.onInstruction(ctx, this, function.body[codePtr], null, error, true);
|
||||
break;
|
||||
default:
|
||||
codePtr = tryCtx.finallyStart;
|
||||
stackPtr = tryCtx.restoreStackPtr;
|
||||
tryStack.pop();
|
||||
tryStack.push(tryCtx._catch(error));
|
||||
error = null;
|
||||
returnValue = Runners.NO_RETURN;
|
||||
}
|
||||
else if (tryCtx.hasFinally() && shouldFinally) {
|
||||
if (tryCtx.state == TryState.CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
|
||||
|
||||
return Runners.NO_RETURN;
|
||||
codePtr = tryCtx.finallyStart;
|
||||
stackPtr = tryCtx.restoreStackPtr;
|
||||
tryStack.pop();
|
||||
tryStack.push(tryCtx._finally(pendingResult));
|
||||
error = null;
|
||||
returnValue = Runners.NO_RETURN;
|
||||
}
|
||||
else if (shouldExit) {
|
||||
if (tryCtx.state == TryState.CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
|
||||
|
||||
if (tryCtx.state != TryState.FINALLY && tryCtx.hasFinally()) {
|
||||
codePtr = tryCtx.finallyStart;
|
||||
stackPtr = tryCtx.restoreStackPtr;
|
||||
tryStack.pop();
|
||||
tryStack.push(tryCtx._finally(null));
|
||||
}
|
||||
else {
|
||||
codePtr = tryCtx.end;
|
||||
if (tryCtx.result.isJump) codePtr = tryCtx.result.ptr;
|
||||
if (tryCtx.result.isReturn) returnValue = tryCtx.result.value;
|
||||
if (tryCtx.result.isThrow) error = tryCtx.result.error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (error != null) {
|
||||
ctx.engine.onInstruction(ctx, this, instr, null, error, false);
|
||||
var caught = false;
|
||||
|
||||
for (var frame : ctx.frames()) {
|
||||
for (var tryCtx : frame.tryStack) {
|
||||
if (tryCtx.state == TryState.TRY) caught = true;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.engine.onInstruction(ctx, this, instr, null, error, caught);
|
||||
throw error;
|
||||
}
|
||||
if (returnValue != Runners.NO_RETURN) {
|
||||
|
@ -97,27 +97,26 @@ public class Runners {
|
||||
obj.defineProperty(ctx, "value", el);
|
||||
frame.push(ctx, obj);
|
||||
}
|
||||
// var arr = new ObjectValue();
|
||||
|
||||
// var members = Values.getMembers(ctx, val, false, false);
|
||||
// Collections.reverse(members);
|
||||
// for (var el : members) {
|
||||
// if (el instanceof Symbol) continue;
|
||||
// arr.defineProperty(ctx, i++, el);
|
||||
// }
|
||||
|
||||
// arr.defineProperty(ctx, "length", i);
|
||||
|
||||
// frame.push(ctx, arr);
|
||||
frame.codePtr++;
|
||||
return NO_RETURN;
|
||||
}
|
||||
|
||||
public static Object execTry(Context ctx, Instruction instr, CodeFrame frame) {
|
||||
frame.addTry(instr.get(0), instr.get(1), instr.get(2));
|
||||
public static Object execTryStart(Context ctx, Instruction instr, CodeFrame frame) {
|
||||
int start = frame.codePtr + 1;
|
||||
int catchStart = (int)instr.get(0);
|
||||
int finallyStart = (int)instr.get(1);
|
||||
if (finallyStart >= 0) finallyStart += start;
|
||||
if (catchStart >= 0) catchStart += start;
|
||||
int end = (int)instr.get(2) + start;
|
||||
frame.addTry(start, end, catchStart, finallyStart);
|
||||
frame.codePtr++;
|
||||
return NO_RETURN;
|
||||
}
|
||||
public static Object execTryEnd(Context ctx, Instruction instr, CodeFrame frame) {
|
||||
frame.popTryFlag = true;
|
||||
return NO_RETURN;
|
||||
}
|
||||
|
||||
public static Object execDup(Context ctx, Instruction instr, CodeFrame frame) {
|
||||
int offset = instr.get(0), count = instr.get(1);
|
||||
@ -326,7 +325,8 @@ public class Runners {
|
||||
case THROW_SYNTAX: return execThrowSyntax(ctx, instr, frame);
|
||||
case CALL: return execCall(ctx, instr, frame);
|
||||
case CALL_NEW: return execCallNew(ctx, instr, frame);
|
||||
case TRY: return execTry(ctx, instr, frame);
|
||||
case TRY_START: return execTryStart(ctx, instr, frame);
|
||||
case TRY_END: return execTryEnd(ctx, instr, frame);
|
||||
|
||||
case DUP: return execDup(ctx, instr, frame);
|
||||
case MOVE: return execMove(ctx, instr, frame);
|
||||
|
@ -12,9 +12,6 @@ import me.topchetoeu.jscript.exceptions.EngineException;
|
||||
public class GlobalScope implements ScopeRecord {
|
||||
public final ObjectValue obj;
|
||||
|
||||
@Override
|
||||
public GlobalScope parent() { return null; }
|
||||
|
||||
public boolean has(Context ctx, String name) {
|
||||
return obj.hasMember(ctx, name, false);
|
||||
}
|
||||
@ -28,7 +25,7 @@ public class GlobalScope implements ScopeRecord {
|
||||
return new GlobalScope(obj);
|
||||
}
|
||||
public LocalScopeRecord child() {
|
||||
return new LocalScopeRecord(this);
|
||||
return new LocalScopeRecord();
|
||||
}
|
||||
|
||||
public Object define(String name) {
|
||||
|
@ -2,11 +2,8 @@ package me.topchetoeu.jscript.engine.scope;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import me.topchetoeu.jscript.engine.Context;
|
||||
|
||||
public class LocalScopeRecord implements ScopeRecord {
|
||||
public final LocalScopeRecord parent;
|
||||
public final GlobalScope global;
|
||||
|
||||
private final ArrayList<String> captures = new ArrayList<>();
|
||||
private final ArrayList<String> locals = new ArrayList<>();
|
||||
@ -18,11 +15,8 @@ public class LocalScopeRecord implements ScopeRecord {
|
||||
return locals.toArray(String[]::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalScopeRecord parent() { return parent; }
|
||||
|
||||
public LocalScopeRecord child() {
|
||||
return new LocalScopeRecord(this, global);
|
||||
return new LocalScopeRecord(this);
|
||||
}
|
||||
|
||||
public int localsCount() {
|
||||
@ -62,12 +56,6 @@ public class LocalScopeRecord implements ScopeRecord {
|
||||
|
||||
return name;
|
||||
}
|
||||
public boolean has(Context ctx, String name) {
|
||||
return
|
||||
global.has(ctx, name) ||
|
||||
locals.contains(name) ||
|
||||
parent != null && parent.has(ctx, name);
|
||||
}
|
||||
public Object define(String name, boolean force) {
|
||||
if (!force && locals.contains(name)) return locals.indexOf(name);
|
||||
locals.add(name);
|
||||
@ -80,12 +68,10 @@ public class LocalScopeRecord implements ScopeRecord {
|
||||
locals.remove(locals.size() - 1);
|
||||
}
|
||||
|
||||
public LocalScopeRecord(GlobalScope global) {
|
||||
public LocalScopeRecord() {
|
||||
this.parent = null;
|
||||
this.global = global;
|
||||
}
|
||||
public LocalScopeRecord(LocalScopeRecord parent, GlobalScope global) {
|
||||
public LocalScopeRecord(LocalScopeRecord parent) {
|
||||
this.parent = parent;
|
||||
this.global = global;
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,5 @@ package me.topchetoeu.jscript.engine.scope;
|
||||
public interface ScopeRecord {
|
||||
public Object getKey(String name);
|
||||
public Object define(String name);
|
||||
public ScopeRecord parent();
|
||||
public LocalScopeRecord child();
|
||||
}
|
||||
|
@ -22,6 +22,9 @@ public class ScopeValue extends ObjectValue {
|
||||
return true;
|
||||
}
|
||||
|
||||
var proto = getPrototype(ctx);
|
||||
if (proto != null && proto.hasField(ctx, key) && proto.setField(ctx, key, val)) return true;
|
||||
|
||||
return super.setField(ctx, key, val);
|
||||
}
|
||||
@Override
|
||||
|
@ -1,5 +1,7 @@
|
||||
package me.topchetoeu.jscript.filesystem;
|
||||
|
||||
import me.topchetoeu.jscript.Buffer;
|
||||
|
||||
public interface File {
|
||||
int read(byte[] buff);
|
||||
void write(byte[] buff);
|
||||
|
@ -1,5 +1,6 @@
|
||||
package me.topchetoeu.jscript.filesystem;
|
||||
|
||||
import me.topchetoeu.jscript.Buffer;
|
||||
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
||||
|
||||
public class MemoryFile implements File {
|
||||
|
@ -4,6 +4,7 @@ import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
import me.topchetoeu.jscript.Buffer;
|
||||
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
||||
|
||||
public class MemoryFilesystem implements Filesystem {
|
||||
|
@ -3,6 +3,7 @@ package me.topchetoeu.jscript.lib;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
import me.topchetoeu.jscript.Buffer;
|
||||
import me.topchetoeu.jscript.Reading;
|
||||
import me.topchetoeu.jscript.engine.Context;
|
||||
import me.topchetoeu.jscript.engine.DataKey;
|
||||
@ -11,7 +12,6 @@ import me.topchetoeu.jscript.engine.scope.GlobalScope;
|
||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||
import me.topchetoeu.jscript.engine.values.Values;
|
||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||
import me.topchetoeu.jscript.filesystem.Buffer;
|
||||
import me.topchetoeu.jscript.interop.Native;
|
||||
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||
import me.topchetoeu.jscript.parsing.Parsing;
|
||||
@ -179,25 +179,25 @@ public class Internals {
|
||||
glob.define(null, "Encoding", false, wp.getNamespace(EncodingLib.class));
|
||||
glob.define(null, "Filesystem", false, wp.getNamespace(FilesystemLib.class));
|
||||
|
||||
glob.define(null, "Date", false, wp.getConstr(DateLib.class));
|
||||
glob.define(null, "Object", false, wp.getConstr(ObjectLib.class));
|
||||
glob.define(null, "Function", false, wp.getConstr(FunctionLib.class));
|
||||
glob.define(null, "Array", false, wp.getConstr(ArrayLib.class));
|
||||
glob.define(false, wp.getConstr(DateLib.class));
|
||||
glob.define(false, wp.getConstr(ObjectLib.class));
|
||||
glob.define(false, wp.getConstr(FunctionLib.class));
|
||||
glob.define(false, wp.getConstr(ArrayLib.class));
|
||||
|
||||
glob.define(null, "Boolean", false, wp.getConstr(BooleanLib.class));
|
||||
glob.define(null, "Number", false, wp.getConstr(NumberLib.class));
|
||||
glob.define(null, "String", false, wp.getConstr(StringLib.class));
|
||||
glob.define(null, "Symbol", false, wp.getConstr(SymbolLib.class));
|
||||
glob.define(false, wp.getConstr(BooleanLib.class));
|
||||
glob.define(false, wp.getConstr(NumberLib.class));
|
||||
glob.define(false, wp.getConstr(StringLib.class));
|
||||
glob.define(false, wp.getConstr(SymbolLib.class));
|
||||
|
||||
glob.define(null, "Promise", false, wp.getConstr(PromiseLib.class));
|
||||
glob.define(null, "RegExp", false, wp.getConstr(RegExpLib.class));
|
||||
glob.define(null, "Map", false, wp.getConstr(MapLib.class));
|
||||
glob.define(null, "Set", false, wp.getConstr(SetLib.class));
|
||||
glob.define(false, wp.getConstr(PromiseLib.class));
|
||||
glob.define(false, wp.getConstr(RegExpLib.class));
|
||||
glob.define(false, wp.getConstr(MapLib.class));
|
||||
glob.define(false, wp.getConstr(SetLib.class));
|
||||
|
||||
glob.define(null, "Error", false, wp.getConstr(ErrorLib.class));
|
||||
glob.define(null, "SyntaxError", false, wp.getConstr(SyntaxErrorLib.class));
|
||||
glob.define(null, "TypeError", false, wp.getConstr(TypeErrorLib.class));
|
||||
glob.define(null, "RangeError", false, wp.getConstr(RangeErrorLib.class));
|
||||
glob.define(false, wp.getConstr(ErrorLib.class));
|
||||
glob.define(false, wp.getConstr(SyntaxErrorLib.class));
|
||||
glob.define(false, wp.getConstr(TypeErrorLib.class));
|
||||
glob.define(false, wp.getConstr(RangeErrorLib.class));
|
||||
|
||||
env.setProto("object", wp.getProto(ObjectLib.class));
|
||||
env.setProto("function", wp.getProto(FunctionLib.class));
|
||||
|
@ -32,18 +32,18 @@ import me.topchetoeu.jscript.interop.NativeGetter;
|
||||
}
|
||||
|
||||
@Native public ObjectValue entries(Context ctx) {
|
||||
var res = map.entrySet().stream().map(v -> {
|
||||
return new ArrayValue(ctx, v.getKey(), v.getValue());
|
||||
}).collect(Collectors.toList());
|
||||
return Values.toJSIterator(ctx, res.iterator());
|
||||
return ArrayValue.of(ctx, map
|
||||
.entrySet()
|
||||
.stream()
|
||||
.map(v -> new ArrayValue(ctx, v.getKey(), v.getValue()))
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
}
|
||||
@Native public ObjectValue keys(Context ctx) {
|
||||
var res = new ArrayList<>(map.keySet());
|
||||
return Values.toJSIterator(ctx, res.iterator());
|
||||
return ArrayValue.of(ctx, map.keySet());
|
||||
}
|
||||
@Native public ObjectValue values(Context ctx) {
|
||||
var res = new ArrayList<>(map.values());
|
||||
return Values.toJSIterator(ctx, res.iterator());
|
||||
return ArrayValue.of(ctx, map.values());
|
||||
}
|
||||
|
||||
@Native public Object get(Object key) {
|
||||
|
@ -154,20 +154,20 @@ import me.topchetoeu.jscript.interop.Native;
|
||||
* Thread safe - you can call this from anywhere
|
||||
* HOWEVER, it's strongly recommended to use this only in javascript
|
||||
*/
|
||||
@Native(thisArg=true) public static Object then(Context ctx, Object thisArg, Object _onFulfill, Object _onReject) {
|
||||
@Native(thisArg=true) public static Object then(Context ctx, Object _thisArg, Object _onFulfill, Object _onReject) {
|
||||
var onFulfill = _onFulfill instanceof FunctionValue ? ((FunctionValue)_onFulfill) : null;
|
||||
var onReject = _onReject instanceof FunctionValue ? ((FunctionValue)_onReject) : null;
|
||||
|
||||
var res = new PromiseLib();
|
||||
|
||||
var fulfill = onFulfill == null ? new NativeFunction((_ctx, _thisArg, _args) -> _args.length > 0 ? _args[0] : null) : (FunctionValue)onFulfill;
|
||||
var reject = onReject == null ? new NativeFunction((_ctx, _thisArg, _args) -> {
|
||||
var fulfill = onFulfill == null ? new NativeFunction((_ctx, _0, _args) -> _args.length > 0 ? _args[0] : null) : (FunctionValue)onFulfill;
|
||||
var reject = onReject == null ? new NativeFunction((_ctx, _0, _args) -> {
|
||||
throw new EngineException(_args.length > 0 ? _args[0] : null);
|
||||
}) : (FunctionValue)onReject;
|
||||
|
||||
if (thisArg instanceof NativeWrapper && ((NativeWrapper)thisArg).wrapped instanceof PromiseLib) {
|
||||
thisArg = ((NativeWrapper)thisArg).wrapped;
|
||||
}
|
||||
var thisArg = _thisArg instanceof NativeWrapper && ((NativeWrapper)_thisArg).wrapped instanceof PromiseLib ?
|
||||
((NativeWrapper)_thisArg).wrapped :
|
||||
_thisArg;
|
||||
|
||||
var fulfillHandle = new NativeFunction(null, (_ctx, th, a) -> {
|
||||
try { res.fulfill(ctx, Values.convert(ctx, fulfill.call(ctx, null, a[0]), Object.class)); }
|
||||
@ -177,6 +177,7 @@ import me.topchetoeu.jscript.interop.Native;
|
||||
var rejectHandle = new NativeFunction(null, (_ctx, th, a) -> {
|
||||
try { res.fulfill(ctx, reject.call(ctx, null, a[0])); }
|
||||
catch (EngineException err) { res.reject(ctx, err.value); }
|
||||
if (thisArg instanceof PromiseLib) ((PromiseLib)thisArg).handled = true;
|
||||
return null;
|
||||
});
|
||||
|
||||
|
@ -21,16 +21,13 @@ import me.topchetoeu.jscript.interop.NativeGetter;
|
||||
}
|
||||
|
||||
@Native public ObjectValue entries(Context ctx) {
|
||||
var res = set.stream().map(v -> new ArrayValue(ctx, v, v)).collect(Collectors.toList());
|
||||
return Values.toJSIterator(ctx, res.iterator());
|
||||
return ArrayValue.of(ctx, set.stream().map(v -> new ArrayValue(ctx, v, v)).collect(Collectors.toList()));
|
||||
}
|
||||
@Native public ObjectValue keys(Context ctx) {
|
||||
var res = new ArrayList<>(set);
|
||||
return Values.toJSIterator(ctx, res.iterator());
|
||||
return ArrayValue.of(ctx, set);
|
||||
}
|
||||
@Native public ObjectValue values(Context ctx) {
|
||||
var res = new ArrayList<>(set);
|
||||
return Values.toJSIterator(ctx, res.iterator());
|
||||
return ArrayValue.of(ctx, set);
|
||||
}
|
||||
|
||||
@Native public Object add(Object key) {
|
||||
|
@ -1,97 +1,108 @@
|
||||
package me.topchetoeu.jscript.mapping;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import me.topchetoeu.jscript.Filename;
|
||||
import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.json.JSON;
|
||||
|
||||
public class SourceMap implements LocationMap {
|
||||
private final TreeMap<Location, Location> orgToSrc = new TreeMap<>();
|
||||
private final TreeMap<Location, Location> srcToOrg = new TreeMap<>();
|
||||
public class SourceMap {
|
||||
private final TreeMap<Long, Long> origToComp = new TreeMap<>();
|
||||
private final TreeMap<Long, Long> compToOrig = new TreeMap<>();
|
||||
|
||||
public Location toSource(Location loc) {
|
||||
return convert(loc, orgToSrc);
|
||||
public Location toCompiled(Location loc) { return convert(loc, origToComp); }
|
||||
public Location toOriginal(Location loc) { return convert(loc, compToOrig); }
|
||||
|
||||
private void add(long orig, long comp) {
|
||||
var a = origToComp.remove(orig);
|
||||
var b = compToOrig.remove(comp);
|
||||
|
||||
if (b != null) origToComp.remove(b);
|
||||
if (a != null) compToOrig.remove(a);
|
||||
|
||||
origToComp.put(orig, comp);
|
||||
compToOrig.put(comp, orig);
|
||||
}
|
||||
|
||||
public Location toOriginal(Location loc) {
|
||||
return convert(loc, srcToOrg);
|
||||
public SourceMap apply(SourceMap map) {
|
||||
var res = new SourceMap();
|
||||
|
||||
for (var el : new ArrayList<>(origToComp.entrySet())) {
|
||||
var mapped = convert(el.getValue(), map.origToComp);
|
||||
add(el.getKey(), mapped);
|
||||
}
|
||||
for (var el : new ArrayList<>(compToOrig.entrySet())) {
|
||||
var mapped = convert(el.getKey(), map.compToOrig);
|
||||
add(el.getValue(), mapped);
|
||||
}
|
||||
|
||||
public static Location convert(Location loc, TreeMap<Location, Location> map) {
|
||||
if (map.containsKey(loc)) return loc;
|
||||
|
||||
var srcA = map.floorKey(loc);
|
||||
return srcA == null ? loc : srcA;
|
||||
}
|
||||
|
||||
public void chain(LocationMap map) {
|
||||
for (var key : orgToSrc.keySet()) {
|
||||
orgToSrc.put(key, map.toSource(key));
|
||||
}
|
||||
for (var key : srcToOrg.keySet()) {
|
||||
srcToOrg.put(map.toOriginal(key), key);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public SourceMap clone() {
|
||||
var res = new SourceMap();
|
||||
res.orgToSrc.putAll(this.orgToSrc);
|
||||
res.srcToOrg.putAll(this.srcToOrg);
|
||||
res.origToComp.putAll(this.origToComp);
|
||||
res.compToOrig.putAll(this.compToOrig);
|
||||
return res;
|
||||
}
|
||||
|
||||
public void split(Map<Filename, TreeMap<Location, Location>> maps) {
|
||||
for (var el : orgToSrc.entrySet()) {
|
||||
var map = maps.get(el.getKey().filename());
|
||||
if (map == null) maps.put(el.getKey().filename(), map = new TreeMap<>());
|
||||
map.put(el.getKey(), el.getValue());
|
||||
|
||||
map = maps.get(el.getValue().filename());
|
||||
if (map == null) maps.put(el.getValue().filename(), map = new TreeMap<>());
|
||||
map.put(el.getValue(), el.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
public static SourceMap parse(String raw) {
|
||||
var mapping = VLQ.decodeMapping(raw);
|
||||
var res = new SourceMap();
|
||||
var json = JSON.parse(null, raw).map();
|
||||
var sources = json.list("sources").stream().map(v -> v.string()).toArray(String[]::new);
|
||||
var dstFilename = Filename.parse(json.string("file"));
|
||||
var mapping = VLQ.decodeMapping(json.string("mappings"));
|
||||
|
||||
var srcI = 0;
|
||||
var srcRow = 0;
|
||||
var srcCol = 0;
|
||||
var compRow = 0l;
|
||||
var compCol = 0l;
|
||||
|
||||
for (var dstRow = 0; dstRow < mapping.length; dstRow++) {
|
||||
var dstCol = 0;
|
||||
for (var origRow = 0; origRow < mapping.length; origRow++) {
|
||||
var origCol = 0;
|
||||
|
||||
for (var rawSeg : mapping[dstRow]) {
|
||||
dstCol += rawSeg.length > 0 ? rawSeg[0] : 0;
|
||||
srcI += rawSeg.length > 1 ? rawSeg[1] : 0;
|
||||
srcRow += rawSeg.length > 2 ? rawSeg[2] : 0;
|
||||
srcCol += rawSeg.length > 3 ? rawSeg[3] : 0;
|
||||
for (var rawSeg : mapping[origRow]) {
|
||||
if (rawSeg.length > 1 && rawSeg[1] != 0) throw new IllegalArgumentException("Source mapping is to more than one files.");
|
||||
origCol += rawSeg.length > 0 ? rawSeg[0] : 0;
|
||||
compRow += rawSeg.length > 2 ? rawSeg[2] : 0;
|
||||
compCol += rawSeg.length > 3 ? rawSeg[3] : 0;
|
||||
|
||||
var src = new Location(srcRow + 1, srcCol + 1, Filename.parse(sources[srcI]));
|
||||
var dst = new Location(dstRow + 1, dstCol + 1, dstFilename);
|
||||
var compPacked = ((long)compRow << 32) | compCol;
|
||||
var origPacked = ((long)origRow << 32) | origCol;
|
||||
|
||||
res.orgToSrc.put(src, dst);
|
||||
res.srcToOrg.put(dst, src);
|
||||
res.add(origPacked, compPacked);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
public static List<String> getSources(String raw) {
|
||||
var json = JSON.parse(null, raw).map();
|
||||
return json
|
||||
.list("sourcesContent")
|
||||
.stream()
|
||||
.map(v -> v.string())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static SourceMap chain(SourceMap ...maps) {
|
||||
if (maps.length == 0) return null;
|
||||
var res = maps[0];
|
||||
|
||||
for (var i = 1; i < maps.length; i++) res.chain(maps[i]);
|
||||
for (var i = 1; i < maps.length; i++) res = res.apply(maps[i]);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static Long convert(long packed, TreeMap<Long, Long> map) {
|
||||
if (map.containsKey(packed)) return map.get(packed);
|
||||
var key = map.floorKey(packed);
|
||||
if (key == null) return null;
|
||||
else return map.get(key);
|
||||
}
|
||||
|
||||
private static Location convert(Location loc, TreeMap<Long, Long> map) {
|
||||
var packed = ((loc.line() - 1l) << 32) | (loc.start() - 1);
|
||||
var resPacked = convert(packed, map);
|
||||
|
||||
if (resPacked == null) return null;
|
||||
else return new Location((int)(resPacked >> 32) + 1, (int)(resPacked & 0xFFFF) + 1, loc.filename());
|
||||
}
|
||||
}
|
||||
|
@ -17,8 +17,7 @@ import me.topchetoeu.jscript.compilation.control.SwitchStatement.SwitchCase;
|
||||
import me.topchetoeu.jscript.compilation.values.*;
|
||||
import me.topchetoeu.jscript.engine.Environment;
|
||||
import me.topchetoeu.jscript.engine.Operation;
|
||||
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
||||
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
||||
import me.topchetoeu.jscript.engine.scope.LocalScopeRecord;
|
||||
import me.topchetoeu.jscript.engine.values.Values;
|
||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
||||
import me.topchetoeu.jscript.parsing.ParseRes.State;
|
||||
@ -806,9 +805,11 @@ public class Parsing {
|
||||
if (!res.isSuccess()) return ParseRes.error(loc, "Expected a compound statement for property accessor.", res);
|
||||
n += res.n;
|
||||
|
||||
var end = getLoc(filename, tokens, i + n - 1);
|
||||
|
||||
return ParseRes.res(new ObjProp(
|
||||
name, access,
|
||||
new FunctionStatement(loc, access + " " + name.toString(), argsRes.result.toArray(String[]::new), false, res.result)
|
||||
new FunctionStatement(loc, end, access + " " + name.toString(), argsRes.result.toArray(String[]::new), false, res.result)
|
||||
), n);
|
||||
}
|
||||
public static ParseRes<ObjectStatement> parseObject(Filename filename, List<Token> tokens, int i) {
|
||||
@ -868,7 +869,7 @@ public class Parsing {
|
||||
|
||||
return ParseRes.res(new ObjectStatement(loc, values, getters, setters), n);
|
||||
}
|
||||
public static ParseRes<NewStatement> parseNew(Filename filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<CallStatement> parseNew(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
var n = 0;
|
||||
if (!isIdentifier(tokens, i + n++, "new")) return ParseRes.failed();
|
||||
@ -879,10 +880,10 @@ public class Parsing {
|
||||
var callRes = parseCall(filename, tokens, i + n, valRes.result, 0);
|
||||
n += callRes.n;
|
||||
if (callRes.isError()) return callRes.transform();
|
||||
else if (callRes.isFailed()) return ParseRes.res(new NewStatement(loc, valRes.result), n);
|
||||
else if (callRes.isFailed()) return ParseRes.res(new CallStatement(loc, false, valRes.result), n);
|
||||
var call = (CallStatement)callRes.result;
|
||||
|
||||
return ParseRes.res(new NewStatement(loc, call.func, call.args), n);
|
||||
return ParseRes.res(new CallStatement(loc, true, call.func, call.args), n);
|
||||
}
|
||||
public static ParseRes<TypeofStatement> parseTypeof(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
@ -895,7 +896,7 @@ public class Parsing {
|
||||
|
||||
return ParseRes.res(new TypeofStatement(loc, valRes.result), n);
|
||||
}
|
||||
public static ParseRes<VoidStatement> parseVoid(Filename filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<DiscardStatement> parseVoid(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
var n = 0;
|
||||
if (!isIdentifier(tokens, i + n++, "void")) return ParseRes.failed();
|
||||
@ -904,7 +905,7 @@ public class Parsing {
|
||||
if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'void' keyword.", valRes);
|
||||
n += valRes.n;
|
||||
|
||||
return ParseRes.res(new VoidStatement(loc, valRes.result), n);
|
||||
return ParseRes.res(new DiscardStatement(loc, valRes.result), n);
|
||||
}
|
||||
public static ParseRes<? extends Statement> parseDelete(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
@ -965,8 +966,9 @@ public class Parsing {
|
||||
|
||||
var res = parseCompound(filename, tokens, i + n);
|
||||
n += res.n;
|
||||
var end = getLoc(filename, tokens, i + n - 1);
|
||||
|
||||
if (res.isSuccess()) return ParseRes.res(new FunctionStatement(loc, name, args.toArray(String[]::new), statement, res.result), n);
|
||||
if (res.isSuccess()) return ParseRes.res(new FunctionStatement(loc, end, name, args.toArray(String[]::new), statement, res.result), n);
|
||||
else return ParseRes.error(loc, "Expected a compound statement for function.", res);
|
||||
}
|
||||
|
||||
@ -1186,7 +1188,7 @@ public class Parsing {
|
||||
else return ParseRes.failed();
|
||||
}
|
||||
|
||||
return ParseRes.res(new CallStatement(loc, prev, args.toArray(Statement[]::new)), n);
|
||||
return ParseRes.res(new CallStatement(loc, false, prev, args.toArray(Statement[]::new)), n);
|
||||
}
|
||||
public static ParseRes<ChangeStatement> parsePostfixChange(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
@ -1232,7 +1234,7 @@ public class Parsing {
|
||||
|
||||
return ParseRes.res(new OperationStatement(loc, Operation.IN, prev, valRes.result), n);
|
||||
}
|
||||
public static ParseRes<CommaStatement> parseComma(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
public static ParseRes<CompoundStatement> parseComma(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
var n = 0;
|
||||
|
||||
@ -1243,7 +1245,7 @@ public class Parsing {
|
||||
if (!res.isSuccess()) return ParseRes.error(loc, "Expected a value after the comma.", res);
|
||||
n += res.n;
|
||||
|
||||
return ParseRes.res(new CommaStatement(loc, prev, res.result), n);
|
||||
return ParseRes.res(new CompoundStatement(loc, false, prev, res.result), n);
|
||||
}
|
||||
public static ParseRes<IfStatement> parseTernary(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
@ -1509,7 +1511,7 @@ public class Parsing {
|
||||
statements.add(res.result);
|
||||
}
|
||||
|
||||
return ParseRes.res(new CompoundStatement(loc, statements.toArray(Statement[]::new)).setEnd(getLoc(filename, tokens, i + n - 1)), n);
|
||||
return ParseRes.res(new CompoundStatement(loc, true, statements.toArray(Statement[]::new)).setEnd(getLoc(filename, tokens, i + n - 1)), n);
|
||||
}
|
||||
public static ParseRes<String> parseLabel(List<Token> tokens, int i) {
|
||||
int n = 0;
|
||||
@ -1690,7 +1692,7 @@ public class Parsing {
|
||||
|
||||
if (isOperator(tokens, i + n, Operator.SEMICOLON)) {
|
||||
n++;
|
||||
decl = new CompoundStatement(loc);
|
||||
decl = new CompoundStatement(loc, false);
|
||||
}
|
||||
else {
|
||||
var declRes = ParseRes.any(
|
||||
@ -1716,7 +1718,7 @@ public class Parsing {
|
||||
|
||||
if (isOperator(tokens, i + n, Operator.PAREN_CLOSE)) {
|
||||
n++;
|
||||
inc = new CompoundStatement(loc);
|
||||
inc = new CompoundStatement(loc, false);
|
||||
}
|
||||
else {
|
||||
var incRes = parseValue(filename, tokens, i + n, 0);
|
||||
@ -1829,7 +1831,7 @@ public class Parsing {
|
||||
}
|
||||
|
||||
public static ParseRes<? extends Statement> parseStatement(Filename filename, List<Token> tokens, int i) {
|
||||
if (isOperator(tokens, i, Operator.SEMICOLON)) return ParseRes.res(new CompoundStatement(getLoc(filename, tokens, i)), 1);
|
||||
if (isOperator(tokens, i, Operator.SEMICOLON)) return ParseRes.res(new CompoundStatement(getLoc(filename, tokens, i), false), 1);
|
||||
if (isIdentifier(tokens, i, "with")) return ParseRes.error(getLoc(filename, tokens, i), "'with' statements are not allowed.");
|
||||
return ParseRes.any(
|
||||
parseVariableDeclare(filename, tokens, i),
|
||||
@ -1872,38 +1874,30 @@ public class Parsing {
|
||||
return list.toArray(Statement[]::new);
|
||||
}
|
||||
|
||||
public static CodeFunction compile(HashMap<Long, FunctionBody> funcs, TreeSet<Location> breakpoints, Environment environment, Statement ...statements) {
|
||||
var target = environment.global.globalChild();
|
||||
var subscope = target.child();
|
||||
var res = new CompileTarget(funcs, breakpoints);
|
||||
var body = new CompoundStatement(null, statements);
|
||||
if (body instanceof CompoundStatement) body = (CompoundStatement)body;
|
||||
else body = new CompoundStatement(null, new Statement[] { body });
|
||||
public static CompileTarget compile(Environment environment, Statement ...statements) {
|
||||
var subscope = new LocalScopeRecord();
|
||||
var target = new CompileTarget(new HashMap<>(), new TreeSet<>());
|
||||
var stm = new CompoundStatement(null, true, statements);
|
||||
|
||||
subscope.define("this");
|
||||
subscope.define("arguments");
|
||||
|
||||
body.declare(target);
|
||||
|
||||
try {
|
||||
body.compile(res, subscope, true);
|
||||
FunctionStatement.checkBreakAndCont(res, 0);
|
||||
stm.compile(target, subscope, true);
|
||||
FunctionStatement.checkBreakAndCont(target, 0);
|
||||
}
|
||||
catch (SyntaxException e) {
|
||||
res.target.clear();
|
||||
res.add(Instruction.throwSyntax(e.loc, e));
|
||||
target.target.clear();
|
||||
target.add(Instruction.throwSyntax(e.loc, e));
|
||||
}
|
||||
|
||||
res.add(Instruction.ret(body.loc()));
|
||||
target.add(Instruction.ret(stm.loc()));
|
||||
target.functions.put(0l, new FunctionBody(subscope.localsCount(), 0, target.array(), subscope.captures(), subscope.locals()));
|
||||
|
||||
return new CodeFunction(environment, "", new FunctionBody(subscope.localsCount(), 0, res.array(), subscope.captures(), subscope.locals()), new ValueVariable[0]);
|
||||
}
|
||||
public static CodeFunction compile(HashMap<Long, FunctionBody> funcs, TreeSet<Location> breakpoints, Environment environment, Filename filename, String raw) {
|
||||
try {
|
||||
return compile(funcs, breakpoints, environment, parse(filename, raw));
|
||||
}
|
||||
catch (SyntaxException e) {
|
||||
return new CodeFunction(environment, null, new FunctionBody(Instruction.throwSyntax(e.loc, e)));
|
||||
return target;
|
||||
}
|
||||
public static CompileTarget compile(Environment environment, Filename filename, String raw) {
|
||||
try { return compile(environment, parse(filename, raw)); }
|
||||
catch (SyntaxException e) { return compile(environment, new ThrowSyntaxStatement(e)); }
|
||||
}
|
||||
}
|
||||
|
80
test.txt
Normal file
80
test.txt
Normal file
@ -0,0 +1,80 @@
|
||||
const { spawn } = require('child_process');
|
||||
const fs = require('fs/promises');
|
||||
const pt = require('path');
|
||||
const { argv, exit } = require('process');
|
||||
|
||||
const conf = {
|
||||
name: "java-jscript",
|
||||
author: "TopchetoEU",
|
||||
javahome: "",
|
||||
version: argv[3]
|
||||
};
|
||||
|
||||
if (conf.version.startsWith('refs/tags/')) conf.version = conf.version.substring(10);
|
||||
if (conf.version.startsWith('v')) conf.version = conf.version.substring(1);
|
||||
|
||||
async function* find(src, dst, wildcard) {
|
||||
const stat = await fs.stat(src);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
for (const el of await fs.readdir(src)) {
|
||||
for await (const res of find(pt.join(src, el), dst ? pt.join(dst, el) : undefined, wildcard)) yield res;
|
||||
}
|
||||
}
|
||||
else if (stat.isFile() && wildcard(src)) yield dst ? { src, dst } : src;
|
||||
}
|
||||
async function copy(src, dst, wildcard) {
|
||||
const promises = [];
|
||||
|
||||
for await (const el of find(src, dst, wildcard)) {
|
||||
promises.push((async () => {
|
||||
await fs.mkdir(pt.dirname(el.dst), { recursive: true });
|
||||
await fs.copyFile(el.src, el.dst);
|
||||
})());
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
function run(cmd, ...args) {
|
||||
return new Promise((res, rej) => {
|
||||
const proc = spawn(cmd, args, { stdio: 'inherit' });
|
||||
proc.once('exit', code => {
|
||||
if (code === 0) res(code);
|
||||
else rej(new Error(`Process ${cmd} exited with code ${code}.`));
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
async function compileJava() {
|
||||
try {
|
||||
await fs.writeFile('Metadata.java', (await fs.readFile('src/me/topchetoeu/jscript/Metadata.java')).toString()
|
||||
.replace('${VERSION}', conf.version)
|
||||
.replace('${NAME}', conf.name)
|
||||
.replace('${AUTHOR}', conf.author)
|
||||
);
|
||||
const args = ['--release', '11', ];
|
||||
if (argv[2] === 'debug') args.push('-g');
|
||||
args.push('-d', 'dst/classes', 'Metadata.java');
|
||||
|
||||
for await (const path of find('src', undefined, v => v.endsWith('.java') && !v.endsWith('Metadata.java'))) args.push(path);
|
||||
await run(conf.javahome + 'javac', ...args);
|
||||
}
|
||||
finally {
|
||||
await fs.rm('Metadata.java');
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
try { await fs.rm('dst', { recursive: true }); } catch {}
|
||||
await copy('src', 'dst/classes', v => !v.endsWith('.java'));
|
||||
await compileJava();
|
||||
await run('jar', '-c', '-f', 'dst/jscript.jar', '-e', 'me.topchetoeu.jscript.Main', '-C', 'dst/classes', '.');
|
||||
}
|
||||
catch (e) {
|
||||
if (argv[2] === 'debug') throw e;
|
||||
console.log(e.toString());
|
||||
exit(-1);
|
||||
}
|
||||
})();
|
Loading…
Reference in New Issue
Block a user