Merge pull request #8 from TopchetoEU/TopchetoEU/tests
Integrate typescript
This commit is contained in:
commit
df8465cb49
2
build.js
2
build.js
@ -10,8 +10,6 @@ const conf = {
|
|||||||
version: argv[3]
|
version: argv[3]
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(conf)
|
|
||||||
|
|
||||||
if (conf.version.startsWith('refs/tags/')) conf.version = conf.version.substring(10);
|
if (conf.version.startsWith('refs/tags/')) conf.version = conf.version.substring(10);
|
||||||
if (conf.version.startsWith('v')) conf.version = conf.version.substring(1);
|
if (conf.version.startsWith('v')) conf.version = conf.version.substring(1);
|
||||||
|
|
||||||
|
@ -46,6 +46,8 @@ public class Filename {
|
|||||||
|
|
||||||
|
|
||||||
public Filename(String protocol, String path) {
|
public Filename(String protocol, String path) {
|
||||||
|
path = path.trim();
|
||||||
|
protocol = protocol.trim();
|
||||||
this.protocol = protocol;
|
this.protocol = protocol;
|
||||||
this.path = path;
|
this.path = path;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package me.topchetoeu.jscript;
|
package me.topchetoeu.jscript;
|
||||||
|
|
||||||
public class Location implements Comparable<Location> {
|
public class Location implements Comparable<Location> {
|
||||||
public static final Location INTERNAL = new Location(0, 0, new Filename("jscript", "internal"));
|
public static final Location INTERNAL = new Location(0, 0, new Filename("jscript", "native"));
|
||||||
private int line;
|
private int line;
|
||||||
private int start;
|
private int start;
|
||||||
private Filename filename;
|
private Filename filename;
|
||||||
|
@ -1,131 +1,153 @@
|
|||||||
package me.topchetoeu.jscript;
|
package me.topchetoeu.jscript;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.Engine;
|
import me.topchetoeu.jscript.engine.Engine;
|
||||||
import me.topchetoeu.jscript.engine.Environment;
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
import me.topchetoeu.jscript.engine.debug.DebugServer;
|
import me.topchetoeu.jscript.engine.StackData;
|
||||||
import me.topchetoeu.jscript.engine.debug.SimpleDebugger;
|
import me.topchetoeu.jscript.engine.debug.DebugServer;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.debug.SimpleDebugger;
|
||||||
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
import me.topchetoeu.jscript.events.Observer;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.exceptions.InterruptException;
|
import me.topchetoeu.jscript.events.Observer;
|
||||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.exceptions.UncheckedException;
|
import me.topchetoeu.jscript.exceptions.InterruptException;
|
||||||
import me.topchetoeu.jscript.lib.Internals;
|
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
||||||
|
import me.topchetoeu.jscript.lib.Internals;
|
||||||
public class Main {
|
|
||||||
static Thread engineTask, debugTask;
|
public class Main {
|
||||||
static Engine engine;
|
static Thread engineTask, debugTask;
|
||||||
static Environment env;
|
static Engine engine;
|
||||||
static int j = 0;
|
static Environment env;
|
||||||
|
static int j = 0;
|
||||||
private static Observer<Object> valuePrinter = new Observer<Object>() {
|
|
||||||
public void next(Object data) {
|
private static Observer<Object> valuePrinter = new Observer<Object>() {
|
||||||
Values.printValue(null, data);
|
public void next(Object data) {
|
||||||
System.out.println();
|
Values.printValue(null, data);
|
||||||
}
|
System.out.println();
|
||||||
|
}
|
||||||
public void error(RuntimeException err) {
|
|
||||||
Values.printError(err, null);
|
public void error(RuntimeException err) {
|
||||||
}
|
Values.printError(err, null);
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public void finish() {
|
@Override
|
||||||
engineTask.interrupt();
|
public void finish() {
|
||||||
}
|
engineTask.interrupt();
|
||||||
};
|
}
|
||||||
|
};
|
||||||
public static void main(String args[]) {
|
|
||||||
System.out.println(String.format("Running %s v%s by %s", Metadata.NAME, Metadata.VERSION, Metadata.AUTHOR));
|
public static void main(String args[]) {
|
||||||
engine = new Engine();
|
System.out.println(String.format("Running %s v%s by %s", Metadata.NAME, Metadata.VERSION, Metadata.AUTHOR));
|
||||||
|
engine = new Engine(true);
|
||||||
env = new Environment(null, null, null);
|
|
||||||
var exited = new boolean[1];
|
env = new Environment(null, null, null);
|
||||||
var server = new DebugServer();
|
var exited = new boolean[1];
|
||||||
server.targets.put("target", (ws, req) -> SimpleDebugger.get(ws, engine));
|
var server = new DebugServer();
|
||||||
|
server.targets.put("target", (ws, req) -> new SimpleDebugger(ws, engine));
|
||||||
engineTask = engine.start();
|
|
||||||
debugTask = server.start(new InetSocketAddress("127.0.0.1", 9229), true);
|
engineTask = engine.start();
|
||||||
// server.awaitConnection();
|
debugTask = server.start(new InetSocketAddress("127.0.0.1", 9229), true);
|
||||||
|
|
||||||
engine.pushMsg(false, null, new NativeFunction((ctx, thisArg, _a) -> {
|
engine.pushMsg(false, null, new NativeFunction((ctx, thisArg, _a) -> {
|
||||||
new Internals().apply(env);
|
new Internals().apply(env);
|
||||||
|
|
||||||
env.global.define("exit", _ctx -> {
|
env.global.define("exit", _ctx -> {
|
||||||
exited[0] = true;
|
exited[0] = true;
|
||||||
throw new InterruptException();
|
throw new InterruptException();
|
||||||
});
|
});
|
||||||
env.global.define("go", _ctx -> {
|
env.global.define("go", _ctx -> {
|
||||||
try {
|
try {
|
||||||
var f = Path.of("do.js");
|
var f = Path.of("do.js");
|
||||||
var func = _ctx.compile(new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f)));
|
var func = _ctx.compile(new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f)));
|
||||||
return func.call(_ctx);
|
return func.call(_ctx);
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
throw new EngineException("Couldn't open do.js");
|
throw new EngineException("Couldn't open do.js");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// TODO: make better API
|
||||||
return null;
|
env.global.define(true, new NativeFunction("include", (_ctx, th, __args) -> {
|
||||||
}), null).await();
|
try {
|
||||||
|
var currFilename = StackData.peekFrame(_ctx).function.loc().filename();
|
||||||
try {
|
var loc = Path.of("").toAbsolutePath();
|
||||||
var ts = engine.pushMsg(
|
if (currFilename.protocol.equals("file")) loc = Path.of(currFilename.path).getParent();
|
||||||
false, new Context(engine).pushEnv(env),
|
var path = loc.resolve(Path.of(__args.length >= 1 ? Values.toString(_ctx, __args[0]) : ""));
|
||||||
new Filename("file", "/mnt/data/repos/java-jscript/src/me/topchetoeu/jscript/js/ts.js"),
|
var src = Files.readString(path);
|
||||||
Reading.resourceToString("js/ts.js"), null
|
var func = _ctx.compile(Filename.fromFile(path.toFile()), src);
|
||||||
).await();
|
var callArgs = new ArrayValue();
|
||||||
System.out.println("Loaded typescript!");
|
if (__args.length >= 2 && __args[1] instanceof ArrayValue) callArgs = (ArrayValue)__args[1];
|
||||||
engine.pushMsg(
|
return func.call(_ctx, null, callArgs);
|
||||||
false, new Context(engine).pushEnv(env.child()),
|
}
|
||||||
new Filename("jscript", "internals/bootstrap.js"), Reading.resourceToString("js/bootstrap.js"), null,
|
catch (IOException e) { throw EngineException.ofError("IOError", "Couldn't open file."); }
|
||||||
ts, env, new ArrayValue(null, Reading.resourceToString("js/lib.d.ts"))
|
}));
|
||||||
).await();
|
|
||||||
}
|
return null;
|
||||||
catch (EngineException e) {
|
}), null).await();
|
||||||
Values.printError(e, "(while initializing TS)");
|
|
||||||
System.out.println("engine reported stack trace:");
|
try {
|
||||||
for (var el : e.stackTrace) {
|
var tsEnv = env.child();
|
||||||
System.out.println(el);
|
tsEnv.global.define(null, "module", false, new ObjectValue());
|
||||||
}
|
engine.pushMsg(
|
||||||
}
|
false, new Context(engine).pushEnv(tsEnv),
|
||||||
|
new Filename("jscript", "ts.js"),
|
||||||
|
Reading.resourceToString("js/ts.js"), null
|
||||||
var reader = new Thread(() -> {
|
).await();
|
||||||
try {
|
System.out.println("Loaded typescript!");
|
||||||
for (var i = 0; ; i++) {
|
|
||||||
try {
|
var ctx = new Context(engine).pushEnv(env.child());
|
||||||
var raw = Reading.read();
|
|
||||||
|
engine.pushMsg(
|
||||||
if (raw == null) break;
|
false, ctx,
|
||||||
valuePrinter.next(engine.pushMsg(false, new Context(engine).pushEnv(env), new Filename("jscript", "repl/" + i + ".js"), raw, null).await());
|
new Filename("jscript", "internals/bootstrap.js"), Reading.resourceToString("js/bootstrap.js"), null,
|
||||||
}
|
tsEnv.global.get(ctx, "ts"), env, new ArrayValue(null, Reading.resourceToString("js/lib.d.ts"))
|
||||||
catch (EngineException e) { Values.printError(e, ""); }
|
).await();
|
||||||
}
|
}
|
||||||
}
|
catch (EngineException e) {
|
||||||
catch (IOException e) { return; }
|
Values.printError(e, "(while initializing TS)");
|
||||||
catch (SyntaxException ex) {
|
}
|
||||||
if (exited[0]) return;
|
|
||||||
System.out.println("Syntax error:" + ex.msg);
|
var reader = new Thread(() -> {
|
||||||
}
|
try {
|
||||||
catch (RuntimeException ex) {
|
for (var arg : args) {
|
||||||
if (!exited[0]) {
|
try {
|
||||||
System.out.println("Internal error ocurred:");
|
var file = Path.of(arg);
|
||||||
ex.printStackTrace();
|
var raw = Files.readString(file);
|
||||||
}
|
valuePrinter.next(engine.pushMsg(false, new Context(engine).pushEnv(env), Filename.fromFile(file.toFile()), raw, null).await());
|
||||||
}
|
}
|
||||||
catch (Throwable e) { throw new UncheckedException(e); }
|
catch (EngineException e) { Values.printError(e, ""); }
|
||||||
if (exited[0]) debugTask.interrupt();
|
}
|
||||||
});
|
for (var i = 0; ; i++) {
|
||||||
reader.setDaemon(true);
|
try {
|
||||||
reader.setName("STD Reader");
|
var raw = Reading.read();
|
||||||
reader.start();
|
|
||||||
}
|
if (raw == null) break;
|
||||||
}
|
valuePrinter.next(engine.pushMsg(false, new Context(engine).pushEnv(env), new Filename("jscript", "repl/" + i + ".js"), raw, null).await());
|
||||||
|
}
|
||||||
|
catch (EngineException e) { Values.printError(e, ""); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e) { exited[0] = true; }
|
||||||
|
catch (SyntaxException ex) {
|
||||||
|
if (exited[0]) return;
|
||||||
|
System.out.println("Syntax error:" + ex.msg);
|
||||||
|
}
|
||||||
|
catch (RuntimeException ex) {
|
||||||
|
if (!exited[0]) {
|
||||||
|
System.out.println("Internal error ocurred:");
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (exited[0]) debugTask.interrupt();
|
||||||
|
});
|
||||||
|
reader.setDaemon(true);
|
||||||
|
reader.setName("STD Reader");
|
||||||
|
reader.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,6 +17,10 @@ public class ChangeStatement extends Statement {
|
|||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, scope, true);
|
value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, scope, true);
|
||||||
if (!pollute) target.add(Instruction.discard().locate(loc()));
|
if (!pollute) target.add(Instruction.discard().locate(loc()));
|
||||||
|
else if (postfix) {
|
||||||
|
target.add(Instruction.loadValue(addAmount));
|
||||||
|
target.add(Instruction.operation(Operation.SUBTRACT));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChangeStatement(Location loc, AssignableStatement value, double addAmount, boolean postfix) {
|
public ChangeStatement(Location loc, AssignableStatement value, double addAmount, boolean postfix) {
|
||||||
|
@ -29,7 +29,7 @@ public class LazyAndStatement extends Statement {
|
|||||||
if (pollute) target.add(Instruction.dup().locate(loc()));
|
if (pollute) target.add(Instruction.dup().locate(loc()));
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
target.add(Instruction.nop());
|
target.add(Instruction.nop());
|
||||||
target.add(Instruction.discard().locate(loc()));
|
if (pollute) target.add(Instruction.discard().locate(loc()));
|
||||||
second.compile(target, scope, pollute);
|
second.compile(target, scope, pollute);
|
||||||
target.set(start, Instruction.jmpIfNot(target.size() - start).locate(loc()));
|
target.set(start, Instruction.jmpIfNot(target.size() - start).locate(loc()));
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ public class LazyOrStatement extends Statement {
|
|||||||
if (pollute) target.add(Instruction.dup().locate(loc()));
|
if (pollute) target.add(Instruction.dup().locate(loc()));
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
target.add(Instruction.nop());
|
target.add(Instruction.nop());
|
||||||
target.add(Instruction.discard().locate(loc()));
|
if (pollute) target.add(Instruction.discard().locate(loc()));
|
||||||
second.compile(target, scope, pollute);
|
second.compile(target, scope, pollute);
|
||||||
target.set(start, Instruction.jmpIf(target.size() - start).locate(loc()));
|
target.set(start, Instruction.jmpIf(target.size() - start).locate(loc()));
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import java.util.TreeSet;
|
|||||||
import me.topchetoeu.jscript.Filename;
|
import me.topchetoeu.jscript.Filename;
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.parsing.Parsing;
|
import me.topchetoeu.jscript.parsing.Parsing;
|
||||||
@ -40,10 +39,9 @@ public class Context {
|
|||||||
}
|
}
|
||||||
else source = Values.toString(this, transpiled);
|
else source = Values.toString(this, transpiled);
|
||||||
|
|
||||||
var debugger = StackData.getDebugger(this);
|
|
||||||
var breakpoints = new TreeSet<Location>();
|
var breakpoints = new TreeSet<Location>();
|
||||||
FunctionValue res = Parsing.compile(engine.functions, breakpoints, environment(), filename, source);
|
FunctionValue res = Parsing.compile(Engine.functions, breakpoints, environment(), filename, source);
|
||||||
if (debugger != null) debugger.onSource(filename, source, breakpoints);
|
engine.onSource(filename, source, breakpoints);
|
||||||
|
|
||||||
if (runner != null) res = (FunctionValue)runner.call(this, null, res);
|
if (runner != null) res = (FunctionValue)runner.call(this, null, res);
|
||||||
|
|
||||||
|
@ -1,16 +1,22 @@
|
|||||||
package me.topchetoeu.jscript.engine;
|
package me.topchetoeu.jscript.engine;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.TreeSet;
|
||||||
import java.util.concurrent.LinkedBlockingDeque;
|
import java.util.concurrent.LinkedBlockingDeque;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Filename;
|
import me.topchetoeu.jscript.Filename;
|
||||||
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.FunctionBody;
|
import me.topchetoeu.jscript.compilation.FunctionBody;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.engine.debug.DebugController;
|
||||||
|
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.events.Awaitable;
|
import me.topchetoeu.jscript.events.Awaitable;
|
||||||
import me.topchetoeu.jscript.events.DataNotifier;
|
import me.topchetoeu.jscript.events.DataNotifier;
|
||||||
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.exceptions.InterruptException;
|
import me.topchetoeu.jscript.exceptions.InterruptException;
|
||||||
|
|
||||||
public class Engine {
|
public class Engine implements DebugController {
|
||||||
private class UncompiledFunction extends FunctionValue {
|
private class UncompiledFunction extends FunctionValue {
|
||||||
public final Filename filename;
|
public final Filename filename;
|
||||||
public final String raw;
|
public final String raw;
|
||||||
@ -45,14 +51,48 @@ public class Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static int nextId = 0;
|
private static int nextId = 0;
|
||||||
|
public static final HashMap<Long, FunctionBody> functions = new HashMap<>();
|
||||||
|
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
private LinkedBlockingDeque<Task> macroTasks = new LinkedBlockingDeque<>();
|
private LinkedBlockingDeque<Task> macroTasks = new LinkedBlockingDeque<>();
|
||||||
private LinkedBlockingDeque<Task> microTasks = new LinkedBlockingDeque<>();
|
private LinkedBlockingDeque<Task> microTasks = new LinkedBlockingDeque<>();
|
||||||
|
|
||||||
public final int id = ++nextId;
|
public final int id = ++nextId;
|
||||||
public final HashMap<Long, FunctionBody> functions = new HashMap<>();
|
public final Data data = new Data().set(StackData.MAX_FRAMES, 200);
|
||||||
public final Data data = new Data().set(StackData.MAX_FRAMES, 10000);
|
public final boolean debugging;
|
||||||
|
private final HashMap<Filename, String> sources = new HashMap<>();
|
||||||
|
private final HashMap<Filename, TreeSet<Location>> bpts = new HashMap<>();
|
||||||
|
private DebugController debugger;
|
||||||
|
|
||||||
|
public boolean attachDebugger(DebugController debugger) {
|
||||||
|
if (!debugging || this.debugger != null) return false;
|
||||||
|
|
||||||
|
for (var source : sources.entrySet()) {
|
||||||
|
debugger.onSource(source.getKey(), source.getValue(), bpts.get(source.getKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.debugger = debugger;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public boolean detachDebugger() {
|
||||||
|
if (!debugging || this.debugger == null) return false;
|
||||||
|
this.debugger = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void onFramePop(Context ctx, CodeFrame frame) {
|
||||||
|
if (debugging && debugger != null) debugger.onFramePop(ctx, frame);
|
||||||
|
}
|
||||||
|
@Override public boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) {
|
||||||
|
if (debugging && debugger != null) return debugger.onInstruction(ctx, frame, instruction, returnVal, error, caught);
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
@Override public void onSource(Filename filename, String source, TreeSet<Location> breakpoints) {
|
||||||
|
if (!debugging) return;
|
||||||
|
if (debugger != null) debugger.onSource(filename, source, breakpoints);
|
||||||
|
sources.put(filename, source);
|
||||||
|
bpts.put(filename, breakpoints);
|
||||||
|
}
|
||||||
|
|
||||||
private void runTask(Task task) {
|
private void runTask(Task task) {
|
||||||
try {
|
try {
|
||||||
@ -108,4 +148,8 @@ public class Engine {
|
|||||||
public Awaitable<Object> pushMsg(boolean micro, Context ctx, Filename filename, String raw, Object thisArg, Object ...args) {
|
public Awaitable<Object> pushMsg(boolean micro, Context ctx, Filename filename, String raw, Object thisArg, Object ...args) {
|
||||||
return pushMsg(micro, ctx, new UncompiledFunction(filename, raw), thisArg, args);
|
return pushMsg(micro, ctx, new UncompiledFunction(filename, raw), thisArg, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Engine(boolean debugging) {
|
||||||
|
this.debugging = debugging;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,8 +40,7 @@ public class Environment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Native public Symbol symbol(String name) {
|
@Native public Symbol symbol(String name) {
|
||||||
if (symbols.containsKey(name))
|
if (symbols.containsKey(name)) return symbols.get(name);
|
||||||
return symbols.get(name);
|
|
||||||
else {
|
else {
|
||||||
var res = new Symbol(name);
|
var res = new Symbol(name);
|
||||||
symbols.put(name, res);
|
symbols.put(name, res);
|
||||||
|
@ -4,6 +4,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.engine.debug.Debugger;
|
import me.topchetoeu.jscript.engine.debug.Debugger;
|
||||||
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
@ -25,8 +26,7 @@ public class StackData {
|
|||||||
if (frames.get(frames.size() - 1) != frame) return false;
|
if (frames.get(frames.size() - 1) != frame) return false;
|
||||||
frames.remove(frames.size() - 1);
|
frames.remove(frames.size() - 1);
|
||||||
ctx.popEnv();
|
ctx.popEnv();
|
||||||
var dbg = getDebugger(ctx);
|
ctx.engine.onFramePop(ctx, frame);
|
||||||
if (dbg != null) dbg.onFramePop(ctx, frame);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public static CodeFrame peekFrame(Context ctx) {
|
public static CodeFrame peekFrame(Context ctx) {
|
||||||
@ -45,7 +45,11 @@ public class StackData {
|
|||||||
for (var i = frames.size() - 1; i >= 0; i--) {
|
for (var i = frames.size() - 1; i >= 0; i--) {
|
||||||
var el = frames.get(i);
|
var el = frames.get(i);
|
||||||
var name = el.function.name;
|
var name = el.function.name;
|
||||||
var loc = el.function.loc();
|
Location loc = null;
|
||||||
|
|
||||||
|
for (var j = el.codePtr; j >= 0 && loc == null; j--) loc = el.function.body[j].location;
|
||||||
|
if (loc == null) loc = el.function.loc();
|
||||||
|
|
||||||
var trace = "";
|
var trace = "";
|
||||||
|
|
||||||
if (loc != null) trace += "at " + loc.toString() + " ";
|
if (loc != null) trace += "at " + loc.toString() + " ";
|
||||||
@ -58,8 +62,4 @@ public class StackData {
|
|||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Debugger getDebugger(Context ctx) {
|
|
||||||
return ctx.data.get(DEBUGGER);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,4 @@ public interface DebugController {
|
|||||||
* @param frame The code frame which was popped out
|
* @param frame The code frame which was popped out
|
||||||
*/
|
*/
|
||||||
void onFramePop(Context ctx, CodeFrame frame);
|
void onFramePop(Context ctx, CodeFrame frame);
|
||||||
|
|
||||||
void connect();
|
|
||||||
void disconnect();
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package me.topchetoeu.jscript.engine.debug;
|
package me.topchetoeu.jscript.engine.debug;
|
||||||
|
|
||||||
public interface Debugger extends DebugHandler, DebugController {
|
public interface Debugger extends DebugHandler, DebugController {
|
||||||
|
void connect();
|
||||||
|
void disconnect();
|
||||||
}
|
}
|
||||||
|
@ -31,12 +31,6 @@ import me.topchetoeu.jscript.json.JSON;
|
|||||||
import me.topchetoeu.jscript.json.JSONElement;
|
import me.topchetoeu.jscript.json.JSONElement;
|
||||||
import me.topchetoeu.jscript.json.JSONList;
|
import me.topchetoeu.jscript.json.JSONList;
|
||||||
import me.topchetoeu.jscript.json.JSONMap;
|
import me.topchetoeu.jscript.json.JSONMap;
|
||||||
import me.topchetoeu.jscript.lib.DateLib;
|
|
||||||
import me.topchetoeu.jscript.lib.MapLib;
|
|
||||||
import me.topchetoeu.jscript.lib.PromiseLib;
|
|
||||||
import me.topchetoeu.jscript.lib.RegExpLib;
|
|
||||||
import me.topchetoeu.jscript.lib.SetLib;
|
|
||||||
import me.topchetoeu.jscript.lib.GeneratorLib.Generator;
|
|
||||||
|
|
||||||
public class SimpleDebugger implements Debugger {
|
public class SimpleDebugger implements Debugger {
|
||||||
public static final String CHROME_GET_PROP_FUNC = "function s(e){let t=this;const n=JSON.parse(e);for(let e=0,i=n.length;e<i;++e)t=t[n[e]];return t}";
|
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}";
|
||||||
@ -46,7 +40,7 @@ public class SimpleDebugger implements Debugger {
|
|||||||
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_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_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_CALL = "function(t){return t.call(this)\n}";
|
||||||
|
|
||||||
private static enum State {
|
private static enum State {
|
||||||
RESUMED,
|
RESUMED,
|
||||||
STEPPING_IN,
|
STEPPING_IN,
|
||||||
@ -104,7 +98,7 @@ public class SimpleDebugger implements Debugger {
|
|||||||
public CodeFrame frame;
|
public CodeFrame frame;
|
||||||
public CodeFunction func;
|
public CodeFunction func;
|
||||||
public int id;
|
public int id;
|
||||||
public ObjectValue local, capture, global;
|
public ObjectValue local, capture, global, valstack;
|
||||||
public JSONMap serialized;
|
public JSONMap serialized;
|
||||||
public Location location;
|
public Location location;
|
||||||
public boolean debugData = false;
|
public boolean debugData = false;
|
||||||
@ -126,6 +120,7 @@ public class SimpleDebugger implements Debugger {
|
|||||||
this.capture = frame.getCaptureScope(ctx, true);
|
this.capture = frame.getCaptureScope(ctx, true);
|
||||||
this.local.setPrototype(ctx, capture);
|
this.local.setPrototype(ctx, capture);
|
||||||
this.capture.setPrototype(ctx, global);
|
this.capture.setPrototype(ctx, global);
|
||||||
|
this.valstack = frame.getValStackScope(ctx);
|
||||||
|
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
debugData = true;
|
debugData = true;
|
||||||
@ -137,6 +132,7 @@ public class SimpleDebugger implements Debugger {
|
|||||||
.add(new JSONMap().set("type", "local").set("name", "Local Scope").set("object", serializeObj(ctx, local)))
|
.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)))
|
.add(new JSONMap().set("type", "closure").set("name", "Closure").set("object", serializeObj(ctx, capture)))
|
||||||
.add(new JSONMap().set("type", "global").set("name", "Global Scope").set("object", serializeObj(ctx, global)))
|
.add(new JSONMap().set("type", "global").set("name", "Global Scope").set("object", serializeObj(ctx, global)))
|
||||||
|
.add(new JSONMap().set("type", "other").set("name", "Value Stack").set("object", serializeObj(ctx, valstack)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -403,7 +399,7 @@ public class SimpleDebugger implements Debugger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private RunResult run(Frame codeFrame, String code) {
|
private RunResult run(Frame codeFrame, String code) {
|
||||||
var engine = new Engine();
|
var engine = new Engine(false);
|
||||||
var env = codeFrame.func.environment.fork();
|
var env = codeFrame.func.environment.fork();
|
||||||
|
|
||||||
ObjectValue global = env.global.obj,
|
ObjectValue global = env.global.obj,
|
||||||
@ -415,7 +411,7 @@ public class SimpleDebugger implements Debugger {
|
|||||||
env.global = new GlobalScope(local);
|
env.global = new GlobalScope(local);
|
||||||
|
|
||||||
var ctx = new Context(engine).pushEnv(env);
|
var ctx = new Context(engine).pushEnv(env);
|
||||||
var awaiter = engine.pushMsg(false, ctx, new Filename("temp", "exec"), "(" + code + ")", codeFrame.frame.thisArg, codeFrame.frame.args);
|
var awaiter = engine.pushMsg(false, ctx, new Filename("jscript", "eval"), code, codeFrame.frame.thisArg, codeFrame.frame.args);
|
||||||
|
|
||||||
engine.run(true);
|
engine.run(true);
|
||||||
|
|
||||||
@ -479,13 +475,16 @@ public class SimpleDebugger implements Debugger {
|
|||||||
@Override public void setBreakpointByUrl(V8Message msg) {
|
@Override public void setBreakpointByUrl(V8Message msg) {
|
||||||
var line = (int)msg.params.number("lineNumber") + 1;
|
var line = (int)msg.params.number("lineNumber") + 1;
|
||||||
var col = (int)msg.params.number("columnNumber", 0) + 1;
|
var col = (int)msg.params.number("columnNumber", 0) + 1;
|
||||||
|
var cond = msg.params.string("condition", null);
|
||||||
|
|
||||||
|
if (cond != null) cond = "(" + cond + ")";
|
||||||
|
|
||||||
Pattern regex;
|
Pattern regex;
|
||||||
|
|
||||||
if (msg.params.isString("url")) regex = Pattern.compile(Pattern.quote(msg.params.string("url")));
|
if (msg.params.isString("url")) regex = Pattern.compile(Pattern.quote(msg.params.string("url")));
|
||||||
else regex = Pattern.compile(msg.params.string("urlRegex"));
|
else regex = Pattern.compile(msg.params.string("urlRegex"));
|
||||||
|
|
||||||
var bpcd = new BreakpointCandidate(nextId(), regex, line, col, null);
|
var bpcd = new BreakpointCandidate(nextId(), regex, line, col, cond);
|
||||||
idToBptCand.put(bpcd.id, bpcd);
|
idToBptCand.put(bpcd.id, bpcd);
|
||||||
|
|
||||||
var locs = new JSONList();
|
var locs = new JSONList();
|
||||||
@ -594,27 +593,23 @@ public class SimpleDebugger implements Debugger {
|
|||||||
}
|
}
|
||||||
@Override public void getProperties(V8Message msg) {
|
@Override public void getProperties(V8Message msg) {
|
||||||
var obj = idToObject.get(Integer.parseInt(msg.params.string("objectId")));
|
var obj = idToObject.get(Integer.parseInt(msg.params.string("objectId")));
|
||||||
var own = msg.params.bool("ownProperties");
|
|
||||||
var accessorPropertiesOnly = msg.params.bool("accessorPropertiesOnly", false);
|
|
||||||
var currOwn = true;
|
|
||||||
|
|
||||||
var res = new JSONList();
|
var res = new JSONList();
|
||||||
|
var ctx = objectToCtx.get(obj);
|
||||||
|
|
||||||
while (obj != emptyObject && obj != null) {
|
if (obj != emptyObject) {
|
||||||
var ctx = objectToCtx.get(obj);
|
|
||||||
|
|
||||||
for (var key : obj.keys(true)) {
|
for (var key : obj.keys(true)) {
|
||||||
var propDesc = new JSONMap();
|
var propDesc = new JSONMap();
|
||||||
|
|
||||||
if (obj.properties.containsKey(key)) {
|
if (obj.properties.containsKey(key)) {
|
||||||
var prop = obj.properties.get(key);
|
var prop = obj.properties.get(key);
|
||||||
|
|
||||||
propDesc.set("name", Values.toString(ctx, key));
|
propDesc.set("name", Values.toString(ctx, key));
|
||||||
if (prop.getter != null) propDesc.set("get", serializeObj(ctx, prop.getter));
|
if (prop.getter != null) propDesc.set("get", serializeObj(ctx, prop.getter));
|
||||||
if (prop.setter != null) propDesc.set("set", serializeObj(ctx, prop.setter));
|
if (prop.setter != null) propDesc.set("set", serializeObj(ctx, prop.setter));
|
||||||
propDesc.set("enumerable", obj.memberEnumerable(key));
|
propDesc.set("enumerable", obj.memberEnumerable(key));
|
||||||
propDesc.set("configurable", obj.memberConfigurable(key));
|
propDesc.set("configurable", obj.memberConfigurable(key));
|
||||||
propDesc.set("isOwn", currOwn);
|
propDesc.set("isOwn", true);
|
||||||
res.add(propDesc);
|
res.add(propDesc);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -623,26 +618,21 @@ public class SimpleDebugger implements Debugger {
|
|||||||
propDesc.set("writable", obj.memberWritable(key));
|
propDesc.set("writable", obj.memberWritable(key));
|
||||||
propDesc.set("enumerable", obj.memberEnumerable(key));
|
propDesc.set("enumerable", obj.memberEnumerable(key));
|
||||||
propDesc.set("configurable", obj.memberConfigurable(key));
|
propDesc.set("configurable", obj.memberConfigurable(key));
|
||||||
propDesc.set("isOwn", currOwn);
|
propDesc.set("isOwn", true);
|
||||||
res.add(propDesc);
|
res.add(propDesc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = obj.getPrototype(ctx);
|
var proto = obj.getPrototype(ctx);
|
||||||
|
|
||||||
if (currOwn) {
|
var protoDesc = new JSONMap();
|
||||||
var protoDesc = new JSONMap();
|
protoDesc.set("name", "__proto__");
|
||||||
protoDesc.set("name", "__proto__");
|
protoDesc.set("value", serializeObj(ctx, proto == null ? Values.NULL : proto));
|
||||||
protoDesc.set("value", serializeObj(ctx, obj == null ? Values.NULL : obj));
|
protoDesc.set("writable", true);
|
||||||
protoDesc.set("writable", true);
|
protoDesc.set("enumerable", false);
|
||||||
protoDesc.set("enumerable", false);
|
protoDesc.set("configurable", false);
|
||||||
protoDesc.set("configurable", false);
|
protoDesc.set("isOwn", true);
|
||||||
protoDesc.set("isOwn", currOwn);
|
res.add(protoDesc);
|
||||||
res.add(protoDesc);
|
|
||||||
}
|
|
||||||
|
|
||||||
currOwn = false;
|
|
||||||
if (true) break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ws.send(msg.respond(new JSONMap().set("result", res)));
|
ws.send(msg.respond(new JSONMap().set("result", res)));
|
||||||
@ -667,36 +657,40 @@ public class SimpleDebugger implements Debugger {
|
|||||||
else src = src.substring(0, start) + src.substring(end + 1);
|
else src = src.substring(0, start) + src.substring(end + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (src) {
|
try {
|
||||||
case CHROME_GET_PROP_FUNC: {
|
switch (src) {
|
||||||
var path = JSON.parse(new Filename("tmp", "json"), (String)args.get(0)).list();
|
case CHROME_GET_PROP_FUNC: {
|
||||||
Object res = thisArg;
|
var path = JSON.parse(null, (String)args.get(0)).list();
|
||||||
for (var el : path) res = Values.getMember(ctx, res, JSON.toJs(el));
|
Object res = thisArg;
|
||||||
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, res))));
|
for (var el : path) res = Values.getMember(ctx, res, JSON.toJs(el));
|
||||||
return;
|
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, res))));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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: {
|
||||||
|
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:
|
||||||
|
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))));
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
case VSCODE_STRINGIFY_VAL:
|
|
||||||
case VSCODE_STRINGIFY_PROPS:
|
|
||||||
case VSCODE_SHALLOW_COPY:
|
|
||||||
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, emptyObject))));
|
|
||||||
break;
|
|
||||||
case VSCODE_FLATTEN_ARRAY: {
|
|
||||||
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, thisArg))));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case VSCODE_SYMBOL_REQUEST:
|
|
||||||
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, new ArrayValue(ctx)))));
|
|
||||||
break;
|
|
||||||
case VSCODE_CALL: {
|
|
||||||
var func = (FunctionValue)(args.size() < 1 ? null : args.get(0));
|
|
||||||
try { ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, func.call(ctx, thisArg))))); }
|
|
||||||
catch (EngineException e) { ws.send(msg.respond(new JSONMap().set("exceptionDetails", serializeException(ctx, e)))); }
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
ws.send(new V8Error("A non well-known function was used with callFunctionOn."));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
catch (EngineException e) { ws.send(msg.respond(new JSONMap().set("exceptionDetails", serializeException(ctx, e)))); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -708,8 +702,8 @@ public class SimpleDebugger implements Debugger {
|
|||||||
int id = nextId();
|
int id = nextId();
|
||||||
var src = new Source(id, filename, source, locations);
|
var src = new Source(id, filename, source, locations);
|
||||||
|
|
||||||
filenameToId.put(filename, id);
|
|
||||||
idToSource.put(id, src);
|
idToSource.put(id, src);
|
||||||
|
filenameToId.put(filename, id);
|
||||||
|
|
||||||
for (var bpcd : idToBptCand.values()) {
|
for (var bpcd : idToBptCand.values()) {
|
||||||
if (!bpcd.pattern.matcher(filename.toString()).matches()) continue;
|
if (!bpcd.pattern.matcher(filename.toString()).matches()) continue;
|
||||||
@ -738,14 +732,15 @@ public class SimpleDebugger implements Debugger {
|
|||||||
returnVal != Runners.NO_RETURN
|
returnVal != Runners.NO_RETURN
|
||||||
);
|
);
|
||||||
|
|
||||||
if (error != null && !caught && StackData.frames(ctx).size() > 1) error = null;
|
// TODO: FIXXXX
|
||||||
|
// if (error != null && !caught && StackData.frames(ctx).size() > 1) error = null;
|
||||||
|
|
||||||
if (error != null && (execptionType == CatchType.ALL || execptionType == CatchType.UNCAUGHT && !caught)) {
|
if (error != null && (execptionType == CatchType.ALL || execptionType == CatchType.UNCAUGHT && !caught)) {
|
||||||
pauseException(ctx);
|
pauseException(ctx);
|
||||||
}
|
}
|
||||||
else if (isBreakpointable && locToBreakpoint.containsKey(loc)) {
|
else if (isBreakpointable && locToBreakpoint.containsKey(loc)) {
|
||||||
var bp = locToBreakpoint.get(loc);
|
var bp = locToBreakpoint.get(loc);
|
||||||
var ok = bp.condition == null ? true : Values.toBoolean(run(currFrame, bp.condition));
|
var ok = bp.condition == null ? true : Values.toBoolean(run(currFrame, bp.condition).result);
|
||||||
if (ok) pauseDebug(ctx, locToBreakpoint.get(loc));
|
if (ok) pauseDebug(ctx, locToBreakpoint.get(loc));
|
||||||
}
|
}
|
||||||
else if (isBreakpointable && tmpBreakpts.remove(loc)) pauseDebug(ctx, null);
|
else if (isBreakpointable && tmpBreakpts.remove(loc)) pauseDebug(ctx, null);
|
||||||
@ -801,27 +796,18 @@ public class SimpleDebugger implements Debugger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override public void connect() {
|
@Override public void connect() {
|
||||||
target.data.set(StackData.DEBUGGER, this);
|
if (!target.attachDebugger(this)) {
|
||||||
|
ws.send(new V8Error("A debugger is already attached to this engine."));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@Override public void disconnect() {
|
@Override public void disconnect() {
|
||||||
target.data.remove(StackData.DEBUGGER);
|
target.detachDebugger();
|
||||||
enabled = false;
|
enabled = false;
|
||||||
updateNotifier.next();
|
updateNotifier.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SimpleDebugger(WebSocket ws, Engine target) {
|
public SimpleDebugger(WebSocket ws, Engine target) {
|
||||||
this.ws = ws;
|
this.ws = ws;
|
||||||
this.target = target;
|
this.target = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SimpleDebugger get(WebSocket ws, Engine target) {
|
|
||||||
if (target.data.has(StackData.DEBUGGER)) {
|
|
||||||
ws.send(new V8Error("A debugger is already attached to this engine."));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var res = new SimpleDebugger(ws, target);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package me.topchetoeu.jscript.engine.debug;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Filename;
|
|
||||||
import me.topchetoeu.jscript.json.JSON;
|
import me.topchetoeu.jscript.json.JSON;
|
||||||
import me.topchetoeu.jscript.json.JSONElement;
|
import me.topchetoeu.jscript.json.JSONElement;
|
||||||
import me.topchetoeu.jscript.json.JSONMap;
|
import me.topchetoeu.jscript.json.JSONMap;
|
||||||
@ -33,7 +32,7 @@ public class V8Message {
|
|||||||
this.params = raw.contains("params") ? raw.map("params") : new JSONMap();
|
this.params = raw.contains("params") ? raw.map("params") : new JSONMap();
|
||||||
}
|
}
|
||||||
public V8Message(String raw) {
|
public V8Message(String raw) {
|
||||||
this(JSON.parse(new Filename("jscript", "json-msg"), raw).map());
|
this(JSON.parse(null, raw).map());
|
||||||
}
|
}
|
||||||
|
|
||||||
public JSONMap toMap() {
|
public JSONMap toMap() {
|
||||||
|
@ -87,15 +87,18 @@ public class WebSocket implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
private synchronized void write(int type, byte[] data) {
|
private synchronized void write(int type, byte[] data) {
|
||||||
try {
|
try {
|
||||||
for (int i = 0; i < (data.length >> 16); i++) {
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < data.length / 0xFFFF; i++) {
|
||||||
out().write(type);
|
out().write(type);
|
||||||
writeLength(0xFFFF);
|
writeLength(0xFFFF);
|
||||||
out().write(data, i << 16, 0xFFFF);
|
out().write(data, i * 0xFFFF, 0xFFFF);
|
||||||
|
type = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
out().write(type | 0x80);
|
out().write(type | 0x80);
|
||||||
writeLength(data.length & 0xFFFF);
|
writeLength(data.length % 0xFFFF);
|
||||||
out().write(data, data.length & 0xFFFF0000, data.length & 0xFFFF);
|
out().write(data, i * 0xFFFF, data.length % 0xFFFF);
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
throw new UncheckedIOException(e);
|
throw new UncheckedIOException(e);
|
||||||
@ -116,7 +119,7 @@ public class WebSocket implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
public void send(Object data) {
|
public void send(Object data) {
|
||||||
// TODO: Remove
|
// TODO: Remove
|
||||||
System.out.println("SEND: " + data);
|
// System.out.println("SEND: " + data);
|
||||||
if (closed) throw new IllegalStateException("Object is closed.");
|
if (closed) throw new IllegalStateException("Object is closed.");
|
||||||
write(1, data.toString().getBytes());
|
write(1, data.toString().getBytes());
|
||||||
}
|
}
|
||||||
@ -199,7 +202,7 @@ public class WebSocket implements AutoCloseable {
|
|||||||
var raw = data.toByteArray();
|
var raw = data.toByteArray();
|
||||||
|
|
||||||
// TODO: Remove
|
// TODO: Remove
|
||||||
System.out.println("RECEIVED: " + new String(raw));
|
// System.out.println("RECEIVED: " + new String(raw));
|
||||||
|
|
||||||
|
|
||||||
if (type == 1) return new WebSocketMessage(new String(raw));
|
if (type == 1) return new WebSocketMessage(new String(raw));
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
package me.topchetoeu.jscript.engine.frame;
|
package me.topchetoeu.jscript.engine.frame;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.StackData;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.LocalScope;
|
import me.topchetoeu.jscript.engine.scope.LocalScope;
|
||||||
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
@ -17,7 +16,7 @@ import me.topchetoeu.jscript.exceptions.EngineException;
|
|||||||
import me.topchetoeu.jscript.exceptions.InterruptException;
|
import me.topchetoeu.jscript.exceptions.InterruptException;
|
||||||
|
|
||||||
public class CodeFrame {
|
public class CodeFrame {
|
||||||
private class TryCtx {
|
public static class TryCtx {
|
||||||
public static final int STATE_TRY = 0;
|
public static final int STATE_TRY = 0;
|
||||||
public static final int STATE_CATCH = 1;
|
public static final int STATE_CATCH = 1;
|
||||||
public static final int STATE_FINALLY_THREW = 2;
|
public static final int STATE_FINALLY_THREW = 2;
|
||||||
@ -84,9 +83,30 @@ public class CodeFrame {
|
|||||||
|
|
||||||
return new ScopeValue(scope.captures, names);
|
return new ScopeValue(scope.captures, names);
|
||||||
}
|
}
|
||||||
|
public ObjectValue getValStackScope(Context ctx) {
|
||||||
|
return new ObjectValue() {
|
||||||
|
@Override
|
||||||
|
protected Object getField(Context ctx, Object key) {
|
||||||
|
var i = (int)Values.toNumber(ctx, key);
|
||||||
|
if (i < 0 || i >= stackPtr) return null;
|
||||||
|
else return stack[i];
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
protected boolean hasField(Context ctx, Object key) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public List<Object> keys(boolean includeNonEnumerable) {
|
||||||
|
var res = super.keys(includeNonEnumerable);
|
||||||
|
for (var i = 0; i < stackPtr; i++) res.add(i);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public void addTry(int n, int catchN, int finallyN) {
|
public void addTry(int n, int catchN, int finallyN) {
|
||||||
var res = new TryCtx(codePtr + 1, n, catchN, finallyN);
|
var res = new TryCtx(codePtr + 1, n, catchN, finallyN);
|
||||||
|
if (!tryStack.empty()) res.err = tryStack.peek().err;
|
||||||
|
|
||||||
tryStack.add(res);
|
tryStack.add(res);
|
||||||
}
|
}
|
||||||
@ -125,34 +145,31 @@ public class CodeFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setCause(Context ctx, EngineException err, EngineException cause) {
|
private void setCause(Context ctx, EngineException err, EngineException cause) {
|
||||||
// err.cause = cause;
|
|
||||||
err.setCause(cause);
|
err.setCause(cause);
|
||||||
}
|
}
|
||||||
private Object nextNoTry(Context ctx, Instruction instr) {
|
|
||||||
if (Thread.currentThread().isInterrupted()) throw new InterruptException();
|
|
||||||
if (codePtr < 0 || codePtr >= function.body.length) return null;
|
|
||||||
|
|
||||||
if (instr.location != null) prevLoc = instr.location;
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.jumpFlag = false;
|
|
||||||
return Runners.exec(ctx, instr, this);
|
|
||||||
}
|
|
||||||
catch (EngineException e) {
|
|
||||||
throw e.add(function.name, prevLoc).setCtx(function.environment, ctx.engine);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object next(Context ctx, Object value, Object returnValue, EngineException error) {
|
public Object next(Context ctx, Object value, Object returnValue, EngineException error) {
|
||||||
if (value != Runners.NO_RETURN) push(ctx, value);
|
if (value != Runners.NO_RETURN) push(ctx, value);
|
||||||
var debugger = StackData.getDebugger(ctx);
|
|
||||||
|
|
||||||
if (returnValue == Runners.NO_RETURN && error == null) {
|
if (returnValue == Runners.NO_RETURN && error == null) {
|
||||||
try {
|
try {
|
||||||
var instr = function.body[codePtr];
|
if (Thread.currentThread().isInterrupted()) throw new InterruptException();
|
||||||
|
|
||||||
if (debugger != null) debugger.onInstruction(ctx, this, instr, Runners.NO_RETURN, null, false);
|
var instr = function.body[codePtr];
|
||||||
returnValue = nextNoTry(ctx, instr);
|
ctx.engine.onInstruction(ctx, this, instr, Runners.NO_RETURN, null, false);
|
||||||
|
|
||||||
|
if (codePtr < 0 || codePtr >= function.body.length) returnValue = null;
|
||||||
|
else {
|
||||||
|
if (instr.location != null) prevLoc = instr.location;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.jumpFlag = false;
|
||||||
|
returnValue = Runners.exec(ctx, instr, this);
|
||||||
|
}
|
||||||
|
catch (EngineException e) {
|
||||||
|
error = e.add(function.name, prevLoc).setCtx(function.environment, ctx.engine);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (EngineException e) { error = e; }
|
catch (EngineException e) { error = e; }
|
||||||
}
|
}
|
||||||
@ -192,11 +209,11 @@ public class CodeFrame {
|
|||||||
break;
|
break;
|
||||||
case TryCtx.STATE_CATCH:
|
case TryCtx.STATE_CATCH:
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
|
setCause(ctx, error, tryCtx.err);
|
||||||
if (tryCtx.hasFinally) {
|
if (tryCtx.hasFinally) {
|
||||||
tryCtx.err = error;
|
tryCtx.err = error;
|
||||||
newState = TryCtx.STATE_FINALLY_THREW;
|
newState = TryCtx.STATE_FINALLY_THREW;
|
||||||
}
|
}
|
||||||
setCause(ctx, error, tryCtx.err);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (returnValue != Runners.NO_RETURN) {
|
else if (returnValue != Runners.NO_RETURN) {
|
||||||
@ -218,27 +235,31 @@ public class CodeFrame {
|
|||||||
case TryCtx.STATE_FINALLY_THREW:
|
case TryCtx.STATE_FINALLY_THREW:
|
||||||
if (error != null) setCause(ctx, error, tryCtx.err);
|
if (error != null) setCause(ctx, error, tryCtx.err);
|
||||||
else if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) error = tryCtx.err;
|
else if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) error = tryCtx.err;
|
||||||
else return Runners.NO_RETURN;
|
else if (returnValue == Runners.NO_RETURN) return Runners.NO_RETURN;
|
||||||
break;
|
break;
|
||||||
case TryCtx.STATE_FINALLY_RETURNED:
|
case TryCtx.STATE_FINALLY_RETURNED:
|
||||||
|
if (error != null) setCause(ctx, error, tryCtx.err);
|
||||||
if (returnValue == Runners.NO_RETURN) {
|
if (returnValue == Runners.NO_RETURN) {
|
||||||
if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) returnValue = tryCtx.retVal;
|
if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) returnValue = tryCtx.retVal;
|
||||||
else return Runners.NO_RETURN;
|
else return Runners.NO_RETURN;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TryCtx.STATE_FINALLY_JUMPED:
|
case TryCtx.STATE_FINALLY_JUMPED:
|
||||||
if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) {
|
if (error != null) setCause(ctx, error, tryCtx.err);
|
||||||
|
else if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) {
|
||||||
if (!jumpFlag) codePtr = tryCtx.jumpPtr;
|
if (!jumpFlag) codePtr = tryCtx.jumpPtr;
|
||||||
else codePtr = tryCtx.end;
|
else codePtr = tryCtx.end;
|
||||||
}
|
}
|
||||||
else return Runners.NO_RETURN;
|
else if (returnValue == Runners.NO_RETURN) return Runners.NO_RETURN;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tryCtx.state == TryCtx.STATE_CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
|
if (tryCtx.state == TryCtx.STATE_CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
|
||||||
|
|
||||||
if (newState == -1) {
|
if (newState == -1) {
|
||||||
|
var err = tryCtx.err;
|
||||||
tryStack.pop();
|
tryStack.pop();
|
||||||
|
if (!tryStack.isEmpty()) tryStack.peek().err = err;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,7 +268,7 @@ public class CodeFrame {
|
|||||||
case TryCtx.STATE_CATCH:
|
case TryCtx.STATE_CATCH:
|
||||||
scope.catchVars.add(new ValueVariable(false, tryCtx.err.value));
|
scope.catchVars.add(new ValueVariable(false, tryCtx.err.value));
|
||||||
codePtr = tryCtx.catchStart;
|
codePtr = tryCtx.catchStart;
|
||||||
if (debugger != null) debugger.onInstruction(ctx, this, function.body[codePtr], null, error, true);
|
ctx.engine.onInstruction(ctx, this, function.body[codePtr], null, error, true);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
codePtr = tryCtx.finallyStart;
|
codePtr = tryCtx.finallyStart;
|
||||||
@ -255,13 +276,13 @@ public class CodeFrame {
|
|||||||
|
|
||||||
return Runners.NO_RETURN;
|
return Runners.NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
if (debugger != null) debugger.onInstruction(ctx, this, function.body[codePtr], null, error, false);
|
ctx.engine.onInstruction(ctx, this, function.body[codePtr], null, error, false);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
if (returnValue != Runners.NO_RETURN) {
|
if (returnValue != Runners.NO_RETURN) {
|
||||||
if (debugger != null) debugger.onInstruction(ctx, this, function.body[codePtr], returnValue, null, false);
|
ctx.engine.onInstruction(ctx, this, function.body[codePtr], returnValue, null, false);
|
||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import java.util.Collections;
|
|||||||
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
import me.topchetoeu.jscript.engine.Engine;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
@ -26,16 +27,12 @@ public class Runners {
|
|||||||
throw EngineException.ofSyntax((String)instr.get(0));
|
throw EngineException.ofSyntax((String)instr.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Object call(Context ctx, Object func, Object thisArg, Object ...args) {
|
|
||||||
return Values.call(ctx, func, thisArg, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Object execCall(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execCall(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var callArgs = frame.take(instr.get(0));
|
var callArgs = frame.take(instr.get(0));
|
||||||
var func = frame.pop();
|
var func = frame.pop();
|
||||||
var thisArg = frame.pop();
|
var thisArg = frame.pop();
|
||||||
|
|
||||||
frame.push(ctx, call(ctx, func, thisArg, callArgs));
|
frame.push(ctx, Values.call(ctx, func, thisArg, callArgs));
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
@ -46,17 +43,6 @@ public class Runners {
|
|||||||
|
|
||||||
frame.push(ctx, Values.callNew(ctx, funcObj, callArgs));
|
frame.push(ctx, Values.callNew(ctx, funcObj, callArgs));
|
||||||
|
|
||||||
// if (Values.isFunction(funcObj) && Values.function(funcObj).special) {
|
|
||||||
// frame.push(ctx, call(ctx, funcObj, null, callArgs));
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// var proto = Values.getMember(ctx, funcObj, "prototype");
|
|
||||||
// var obj = new ObjectValue();
|
|
||||||
// obj.setPrototype(ctx, proto);
|
|
||||||
// call(ctx, funcObj, obj, callArgs);
|
|
||||||
// frame.push(ctx, obj);
|
|
||||||
// }
|
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
@ -201,7 +187,7 @@ public class Runners {
|
|||||||
captures[i - 3] = frame.scope.get(instr.get(i));
|
captures[i - 3] = frame.scope.get(instr.get(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
var body = ctx.engine.functions.get(id);
|
var body = Engine.functions.get(id);
|
||||||
var func = new CodeFunction(ctx.environment(), "", localsN, len, captures, body);
|
var func = new CodeFunction(ctx.environment(), "", localsN, len, captures, body);
|
||||||
|
|
||||||
frame.push(ctx, func);
|
frame.push(ctx, func);
|
||||||
|
@ -8,8 +8,7 @@ public class LocalScope {
|
|||||||
public final ArrayList<ValueVariable> catchVars = new ArrayList<>();
|
public final ArrayList<ValueVariable> catchVars = new ArrayList<>();
|
||||||
|
|
||||||
public ValueVariable get(int i) {
|
public ValueVariable get(int i) {
|
||||||
if (i >= locals.length)
|
if (i >= locals.length) return catchVars.get(i - locals.length);
|
||||||
return catchVars.get(i - locals.length);
|
|
||||||
if (i >= 0) return locals[i];
|
if (i >= 0) return locals[i];
|
||||||
else return captures[~i];
|
else return captures[~i];
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ public class ArrayValue extends ObjectValue implements Iterable<Object> {
|
|||||||
if (i >= size) size = i + 1;
|
if (i >= size) size = i + 1;
|
||||||
}
|
}
|
||||||
public boolean has(int i) {
|
public boolean has(int i) {
|
||||||
return i >= 0 && i < values.length && values[i] != null;
|
return i >= 0 && i < size && values[i] != null;
|
||||||
}
|
}
|
||||||
public void remove(int i) {
|
public void remove(int i) {
|
||||||
if (i < 0 || i >= values.length) return;
|
if (i < 0 || i >= values.length) return;
|
||||||
@ -85,8 +85,9 @@ public class ArrayValue extends ObjectValue implements Iterable<Object> {
|
|||||||
public void copyTo(Context ctx, ArrayValue arr, int sourceStart, int destStart, int count) {
|
public void copyTo(Context ctx, ArrayValue arr, int sourceStart, int destStart, int count) {
|
||||||
// Iterate in reverse to reallocate at most once
|
// Iterate in reverse to reallocate at most once
|
||||||
for (var i = count - 1; i >= 0; i--) {
|
for (var i = count - 1; i >= 0; i--) {
|
||||||
if (i + sourceStart < 0 || i + sourceStart >= size) arr.set(ctx, i + destStart, null);
|
if (i + sourceStart < 0 || i + sourceStart >= size) arr.remove(i + destStart);
|
||||||
if (values[i + sourceStart] == UNDEFINED) arr.set(ctx, i + destStart, null);
|
if (values[i + sourceStart] == UNDEFINED) arr.set(ctx, i + destStart, null);
|
||||||
|
else if (values[i + sourceStart] == null) arr.remove(i + destStart);
|
||||||
else arr.set(ctx, i + destStart, values[i + sourceStart]);
|
else arr.set(ctx, i + destStart, values[i + sourceStart]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,8 +103,7 @@ public class ObjectValue {
|
|||||||
) return true;
|
) return true;
|
||||||
|
|
||||||
if (!extensible() && !values.containsKey(key) && !properties.containsKey(key)) return false;
|
if (!extensible() && !values.containsKey(key) && !properties.containsKey(key)) return false;
|
||||||
if (!memberConfigurable(key))
|
if (!memberConfigurable(key)) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
nonWritableSet.remove(key);
|
nonWritableSet.remove(key);
|
||||||
nonEnumerableSet.remove(key);
|
nonEnumerableSet.remove(key);
|
||||||
|
@ -258,8 +258,7 @@ public class Values {
|
|||||||
|
|
||||||
public static Object getMember(Context ctx, Object obj, Object key) {
|
public static Object getMember(Context ctx, Object obj, Object key) {
|
||||||
obj = normalize(ctx, obj); key = normalize(ctx, key);
|
obj = normalize(ctx, obj); key = normalize(ctx, key);
|
||||||
if (obj == null)
|
if (obj == null) throw new IllegalArgumentException("Tried to access member of undefined.");
|
||||||
throw new IllegalArgumentException("Tried to access member of undefined.");
|
|
||||||
if (obj == NULL) throw new IllegalArgumentException("Tried to access member of null.");
|
if (obj == NULL) throw new IllegalArgumentException("Tried to access member of null.");
|
||||||
if (isObject(obj)) return object(obj).getMember(ctx, key);
|
if (isObject(obj)) return object(obj).getMember(ctx, key);
|
||||||
|
|
||||||
@ -279,8 +278,7 @@ public class Values {
|
|||||||
}
|
}
|
||||||
public static boolean setMember(Context ctx, Object obj, Object key, Object val) {
|
public static boolean setMember(Context ctx, Object obj, Object key, Object val) {
|
||||||
obj = normalize(ctx, obj); key = normalize(ctx, key); val = normalize(ctx, val);
|
obj = normalize(ctx, obj); key = normalize(ctx, key); val = normalize(ctx, val);
|
||||||
if (obj == null)
|
if (obj == null) throw EngineException.ofType("Tried to access member of undefined.");
|
||||||
throw EngineException.ofType("Tried to access member of undefined.");
|
|
||||||
if (obj == NULL) throw EngineException.ofType("Tried to access member of null.");
|
if (obj == NULL) throw EngineException.ofType("Tried to access member of null.");
|
||||||
if (key.equals("__proto__")) return setPrototype(ctx, obj, val);
|
if (key.equals("__proto__")) return setPrototype(ctx, obj, val);
|
||||||
if (isObject(obj)) return object(obj).setMember(ctx, key, val, false);
|
if (isObject(obj)) return object(obj).setMember(ctx, key, val, false);
|
||||||
@ -369,8 +367,7 @@ public class Values {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Object call(Context ctx, Object func, Object thisArg, Object ...args) {
|
public static Object call(Context ctx, Object func, Object thisArg, Object ...args) {
|
||||||
if (!isFunction(func))
|
if (!isFunction(func)) throw EngineException.ofType("Tried to call a non-function value.");
|
||||||
throw EngineException.ofType("Tried to call a non-function value.");
|
|
||||||
return function(func).call(ctx, thisArg, args);
|
return function(func).call(ctx, thisArg, args);
|
||||||
}
|
}
|
||||||
public static Object callNew(Context ctx, Object func, Object ...args) {
|
public static Object callNew(Context ctx, Object func, Object ...args) {
|
||||||
@ -524,8 +521,6 @@ public class Values {
|
|||||||
public static Iterable<Object> toJavaIterable(Context ctx, Object obj) {
|
public static Iterable<Object> toJavaIterable(Context ctx, Object obj) {
|
||||||
return () -> {
|
return () -> {
|
||||||
try {
|
try {
|
||||||
var _ctx = ctx;
|
|
||||||
var _obj = obj;
|
|
||||||
var symbol = ctx.environment().symbol("Symbol.iterator");
|
var symbol = ctx.environment().symbol("Symbol.iterator");
|
||||||
|
|
||||||
var iteratorFunc = getMember(ctx, obj, symbol);
|
var iteratorFunc = getMember(ctx, obj, symbol);
|
||||||
@ -587,7 +582,11 @@ public class Values {
|
|||||||
|
|
||||||
res.defineProperty(ctx, "next", new NativeFunction("", (_ctx, _th, _args) -> {
|
res.defineProperty(ctx, "next", new NativeFunction("", (_ctx, _th, _args) -> {
|
||||||
if (!it.hasNext()) return new ObjectValue(ctx, Map.of("done", true));
|
if (!it.hasNext()) return new ObjectValue(ctx, Map.of("done", true));
|
||||||
else return new ObjectValue(ctx, Map.of("value", it.next()));
|
else {
|
||||||
|
var obj = new ObjectValue();
|
||||||
|
obj.defineProperty(_ctx, "value", it.next());
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
@ -598,6 +597,11 @@ public class Values {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void printValue(Context ctx, Object val, HashSet<Object> passed, int tab) {
|
private static void printValue(Context ctx, Object val, HashSet<Object> passed, int tab) {
|
||||||
|
if (tab == 0 && val instanceof String) {
|
||||||
|
System.out.print(val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (passed.contains(val)) {
|
if (passed.contains(val)) {
|
||||||
System.out.print("[circular]");
|
System.out.print("[circular]");
|
||||||
return;
|
return;
|
||||||
|
@ -137,7 +137,11 @@ public class NativeWrapperProvider implements WrappersProvider {
|
|||||||
* @param clazz The class for which a constructor should be generated
|
* @param clazz The class for which a constructor should be generated
|
||||||
*/
|
*/
|
||||||
public static FunctionValue makeConstructor(Environment ctx, Class<?> clazz) {
|
public static FunctionValue makeConstructor(Environment ctx, Class<?> clazz) {
|
||||||
FunctionValue func = new OverloadFunction(clazz.getName());
|
var name = clazz.getName();
|
||||||
|
var classNat = clazz.getAnnotation(Native.class);
|
||||||
|
if (classNat != null && !classNat.value().trim().equals("")) name = classNat.value().trim();
|
||||||
|
|
||||||
|
FunctionValue func = new OverloadFunction(name);
|
||||||
|
|
||||||
for (var overload : clazz.getDeclaredConstructors()) {
|
for (var overload : clazz.getDeclaredConstructors()) {
|
||||||
var nat = overload.getAnnotation(Native.class);
|
var nat = overload.getAnnotation(Native.class);
|
||||||
|
@ -11,6 +11,7 @@ import me.topchetoeu.jscript.engine.values.FunctionValue;
|
|||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.exceptions.ConvertException;
|
import me.topchetoeu.jscript.exceptions.ConvertException;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
|
import me.topchetoeu.jscript.exceptions.InterruptException;
|
||||||
|
|
||||||
public class OverloadFunction extends FunctionValue {
|
public class OverloadFunction extends FunctionValue {
|
||||||
public final List<Overload> overloads = new ArrayList<>();
|
public final List<Overload> overloads = new ArrayList<>();
|
||||||
@ -87,6 +88,9 @@ public class OverloadFunction extends FunctionValue {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw EngineException.ofType("Unexpected value of 'undefined'.").add(name, loc);
|
throw EngineException.ofType("Unexpected value of 'undefined'.").add(name, loc);
|
||||||
}
|
}
|
||||||
|
else if (e.getTargetException() instanceof InterruptException || e.getTargetException() instanceof InterruptedException) {
|
||||||
|
throw new InterruptException();
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
throw EngineException.ofError(e.getTargetException().getMessage()).add(name, loc);
|
throw EngineException.ofError(e.getTargetException().getMessage()).add(name, loc);
|
||||||
}
|
}
|
||||||
|
4
src/me/topchetoeu/jscript/js/bootstrap.js
vendored
4
src/me/topchetoeu/jscript/js/bootstrap.js
vendored
@ -1,7 +1,5 @@
|
|||||||
(function (_arguments) {
|
(function (_arguments) {
|
||||||
var ts = _arguments[0];
|
var ts = _arguments[0];
|
||||||
log("Loaded typescript!");
|
|
||||||
|
|
||||||
var src = '', lib = _arguments[2].concat([ 'declare const exit: never;' ]).join(''), decls = '', version = 0;
|
var src = '', lib = _arguments[2].concat([ 'declare const exit: never;' ]).join(''), decls = '', version = 0;
|
||||||
var libSnapshot = ts.ScriptSnapshot.fromString(lib);
|
var libSnapshot = ts.ScriptSnapshot.fromString(lib);
|
||||||
|
|
||||||
@ -42,7 +40,7 @@
|
|||||||
service.getEmitOutput('/lib.d.ts');
|
service.getEmitOutput('/lib.d.ts');
|
||||||
log('Loaded libraries!');
|
log('Loaded libraries!');
|
||||||
|
|
||||||
function compile(filename, code) {
|
function compile(code, filename) {
|
||||||
src = code, version++;
|
src = code, version++;
|
||||||
|
|
||||||
var emit = service.getEmitOutput("/src.ts");
|
var emit = service.getEmitOutput("/src.ts");
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -177,6 +177,7 @@ public class JSON {
|
|||||||
return ParseRes.res(values, n);
|
return ParseRes.res(values, n);
|
||||||
}
|
}
|
||||||
public static JSONElement parse(Filename filename, String raw) {
|
public static JSONElement parse(Filename filename, String raw) {
|
||||||
|
if (filename == null) filename = new Filename("jscript", "json");
|
||||||
var res = parseValue(filename, Parsing.tokenize(filename, raw), 0);
|
var res = parseValue(filename, Parsing.tokenize(filename, raw), 0);
|
||||||
if (res.isFailed()) throw new SyntaxException(null, "Invalid JSON given.");
|
if (res.isFailed()) throw new SyntaxException(null, "Invalid JSON given.");
|
||||||
else if (res.isError()) throw new SyntaxException(null, res.error);
|
else if (res.isError()) throw new SyntaxException(null, res.error);
|
||||||
@ -187,12 +188,28 @@ public class JSON {
|
|||||||
if (el.isNumber()) return Double.toString(el.number());
|
if (el.isNumber()) return Double.toString(el.number());
|
||||||
if (el.isBoolean()) return el.bool() ? "true" : "false";
|
if (el.isBoolean()) return el.bool() ? "true" : "false";
|
||||||
if (el.isNull()) return "null";
|
if (el.isNull()) return "null";
|
||||||
if (el.isString()) return "\"" + el.string()
|
if (el.isString()) {
|
||||||
.replace("\\", "\\\\")
|
var res = new StringBuilder("\"");
|
||||||
.replace("\n", "\\n")
|
var alphabet = "0123456789ABCDEF".toCharArray();
|
||||||
.replace("\r", "\\r")
|
|
||||||
.replace("\"", "\\\"")
|
for (var c : el.string().toCharArray()) {
|
||||||
+ "\"";
|
if (c < 32 || c >= 127) {
|
||||||
|
res
|
||||||
|
.append("\\u")
|
||||||
|
.append(alphabet[(c >> 12) & 0xF])
|
||||||
|
.append(alphabet[(c >> 8) & 0xF])
|
||||||
|
.append(alphabet[(c >> 4) & 0xF])
|
||||||
|
.append(alphabet[(c >> 0) & 0xF]);
|
||||||
|
}
|
||||||
|
else if (c == '\\')
|
||||||
|
res.append("\\\\");
|
||||||
|
else if (c == '"')
|
||||||
|
res.append("\\\"");
|
||||||
|
else res.append(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.append('"').toString();
|
||||||
|
}
|
||||||
if (el.isList()) {
|
if (el.isList()) {
|
||||||
var res = new StringBuilder().append("[");
|
var res = new StringBuilder().append("[");
|
||||||
for (int i = 0; i < el.list().size(); i++) {
|
for (int i = 0; i < el.list().size(); i++) {
|
||||||
|
@ -16,7 +16,7 @@ import me.topchetoeu.jscript.interop.NativeGetter;
|
|||||||
import me.topchetoeu.jscript.interop.NativeInit;
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
import me.topchetoeu.jscript.interop.NativeSetter;
|
import me.topchetoeu.jscript.interop.NativeSetter;
|
||||||
|
|
||||||
public class ArrayLib {
|
@Native("Array") public class ArrayLib {
|
||||||
@NativeGetter(thisArg = true) public static int length(Context ctx, ArrayValue thisArg) {
|
@NativeGetter(thisArg = true) public static int length(Context ctx, ArrayValue thisArg) {
|
||||||
return thisArg.size();
|
return thisArg.size();
|
||||||
}
|
}
|
||||||
@ -93,13 +93,14 @@ public class ArrayLib {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native(thisArg = true) public static void sort(Context ctx, ArrayValue arr, FunctionValue cmp) {
|
@Native(thisArg = true) public static ArrayValue sort(Context ctx, ArrayValue arr, FunctionValue cmp) {
|
||||||
arr.sort((a, b) -> {
|
arr.sort((a, b) -> {
|
||||||
var res = Values.toNumber(ctx, cmp.call(ctx, null, a, b));
|
var res = Values.toNumber(ctx, cmp.call(ctx, null, a, b));
|
||||||
if (res < 0) return -1;
|
if (res < 0) return -1;
|
||||||
if (res > 0) return 1;
|
if (res > 0) return 1;
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int normalizeI(int len, int i, boolean clamp) {
|
private static int normalizeI(int len, int i, boolean clamp) {
|
||||||
@ -164,6 +165,37 @@ public class ArrayLib {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static Object reduce(Context ctx, ArrayValue arr, FunctionValue func, Object... args) {
|
||||||
|
var i = 0;
|
||||||
|
var res = arr.get(0);
|
||||||
|
|
||||||
|
if (args.length > 0) res = args[0];
|
||||||
|
else for (; !arr.has(i) && i < arr.size(); i++) res = arr.get(i);
|
||||||
|
|
||||||
|
for (; i < arr.size(); i++) {
|
||||||
|
if (arr.has(i)) {
|
||||||
|
res = func.call(ctx, null, res, arr.get(i), i, arr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static Object reduceRight(Context ctx, ArrayValue arr, FunctionValue func, Object... args) {
|
||||||
|
var i = arr.size();
|
||||||
|
var res = arr.get(0);
|
||||||
|
|
||||||
|
if (args.length > 0) res = args[0];
|
||||||
|
else while (!arr.has(i--) && i >= 0) res = arr.get(i);
|
||||||
|
|
||||||
|
for (; i >= 0; i--) {
|
||||||
|
if (arr.has(i)) {
|
||||||
|
res = func.call(ctx, null, res, arr.get(i), i, arr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
@Native(thisArg = true) public static ArrayValue flat(Context ctx, ArrayValue arr, int depth) {
|
@Native(thisArg = true) public static ArrayValue flat(Context ctx, ArrayValue arr, int depth) {
|
||||||
var res = new ArrayValue(arr.size());
|
var res = new ArrayValue(arr.size());
|
||||||
var stack = new Stack<Object>();
|
var stack = new Stack<Object>();
|
||||||
@ -224,7 +256,7 @@ public class ArrayLib {
|
|||||||
@Native(thisArg = true) public static int indexOf(Context ctx, ArrayValue arr, Object val, int start) {
|
@Native(thisArg = true) public static int indexOf(Context ctx, ArrayValue arr, Object val, int start) {
|
||||||
start = normalizeI(arr.size(), start, true);
|
start = normalizeI(arr.size(), start, true);
|
||||||
|
|
||||||
for (int i = 0; i < arr.size() && i < start; i++) {
|
for (int i = start; i < arr.size(); i++) {
|
||||||
if (Values.strictEquals(ctx, arr.get(i), val)) return i;
|
if (Values.strictEquals(ctx, arr.get(i), val)) return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,17 +300,14 @@ public class ArrayLib {
|
|||||||
return arr.size();
|
return arr.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native(thisArg = true) public static ArrayValue slice(Context ctx, ArrayValue arr, int start, int end) {
|
@Native(thisArg = true) public static ArrayValue slice(Context ctx, ArrayValue arr, int start, Object _end) {
|
||||||
start = normalizeI(arr.size(), start, true);
|
start = normalizeI(arr.size(), start, true);
|
||||||
end = normalizeI(arr.size(), end, true);
|
int end = normalizeI(arr.size(), (int)(_end == null ? arr.size() : Values.toNumber(ctx, _end)), true);
|
||||||
|
|
||||||
var res = new ArrayValue(end - start);
|
var res = new ArrayValue(end - start);
|
||||||
arr.copyTo(ctx, res, start, 0, end - start);
|
arr.copyTo(ctx, res, start, 0, end - start);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@Native(thisArg = true) public static ArrayValue slice(Context ctx, ArrayValue arr, int start) {
|
|
||||||
return slice(ctx, arr, start, arr.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Native(thisArg = true) public static ArrayValue splice(Context ctx, ArrayValue arr, int start, int deleteCount, Object ...items) {
|
@Native(thisArg = true) public static ArrayValue splice(Context ctx, ArrayValue arr, int start, int deleteCount, Object ...items) {
|
||||||
start = normalizeI(arr.size(), start, true);
|
start = normalizeI(arr.size(), start, true);
|
||||||
@ -303,12 +332,14 @@ public class ArrayLib {
|
|||||||
|
|
||||||
@Native(thisArg = true) public static String join(Context ctx, ArrayValue arr, String sep) {
|
@Native(thisArg = true) public static String join(Context ctx, ArrayValue arr, String sep) {
|
||||||
var res = new StringBuilder();
|
var res = new StringBuilder();
|
||||||
var comma = true;
|
var comma = false;
|
||||||
|
|
||||||
for (int i = 0; i < arr.size(); i++) {
|
for (int i = 0; i < arr.size(); i++) {
|
||||||
if (!arr.has(i)) continue;
|
if (!arr.has(i)) continue;
|
||||||
|
|
||||||
if (comma) res.append(sep);
|
if (comma) res.append(sep);
|
||||||
comma = false;
|
comma = true;
|
||||||
|
|
||||||
var el = arr.get(i);
|
var el = arr.get(i);
|
||||||
if (el == null || el == Values.NULL) continue;
|
if (el == null || el == Values.NULL) continue;
|
||||||
|
|
||||||
|
@ -8,8 +8,9 @@ import me.topchetoeu.jscript.engine.values.CodeFunction;
|
|||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
|
||||||
public class AsyncFunctionLib extends FunctionValue {
|
@Native("AsyncFunction") public class AsyncFunctionLib extends FunctionValue {
|
||||||
public final FunctionValue factory;
|
public final FunctionValue factory;
|
||||||
|
|
||||||
public static class AsyncHelper {
|
public static class AsyncHelper {
|
||||||
|
30
src/me/topchetoeu/jscript/lib/AsyncGeneratorFunctionLib.java
Normal file
30
src/me/topchetoeu/jscript/lib/AsyncGeneratorFunctionLib.java
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package me.topchetoeu.jscript.lib;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||||
|
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
||||||
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
|
||||||
|
@Native("AsyncGeneratorFunction") public class AsyncGeneratorFunctionLib extends FunctionValue {
|
||||||
|
public final FunctionValue factory;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object call(Context ctx, Object thisArg, Object ...args) {
|
||||||
|
var handler = new AsyncGeneratorLib();
|
||||||
|
var func = factory.call(ctx, thisArg,
|
||||||
|
new NativeFunction("await", handler::await),
|
||||||
|
new NativeFunction("yield", handler::yield)
|
||||||
|
);
|
||||||
|
if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function.");
|
||||||
|
handler.frame = new CodeFrame(ctx, thisArg, args, (CodeFunction)func);
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncGeneratorFunctionLib(FunctionValue factory) {
|
||||||
|
super(factory.name, factory.length);
|
||||||
|
this.factory = factory;
|
||||||
|
}
|
||||||
|
}
|
@ -6,130 +6,106 @@ import me.topchetoeu.jscript.engine.Context;
|
|||||||
import me.topchetoeu.jscript.engine.StackData;
|
import me.topchetoeu.jscript.engine.StackData;
|
||||||
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||||
import me.topchetoeu.jscript.engine.frame.Runners;
|
import me.topchetoeu.jscript.engine.frame.Runners;
|
||||||
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
|
||||||
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
|
||||||
public class AsyncGeneratorLib extends FunctionValue {
|
@Native("AsyncGenerator") public class AsyncGeneratorLib {
|
||||||
public final FunctionValue factory;
|
@Native("@@Symbol.typeName") public final String name = "AsyncGenerator";
|
||||||
|
private int state = 0;
|
||||||
|
private boolean done = false;
|
||||||
|
private PromiseLib currPromise;
|
||||||
|
public CodeFrame frame;
|
||||||
|
|
||||||
public static class AsyncGenerator {
|
private void next(Context ctx, Object inducedValue, Object inducedReturn, Object inducedError) {
|
||||||
@Native("@@Symbol.typeName") public final String name = "AsyncGenerator";
|
if (done) {
|
||||||
private int state = 0;
|
if (inducedError != Runners.NO_RETURN) throw new EngineException(inducedError);
|
||||||
private boolean done = false;
|
currPromise.fulfill(ctx, new ObjectValue(ctx, Map.of(
|
||||||
private PromiseLib currPromise;
|
"done", true,
|
||||||
public CodeFrame frame;
|
"value", inducedReturn == Runners.NO_RETURN ? null : inducedReturn
|
||||||
|
)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
private void next(Context ctx, Object inducedValue, Object inducedReturn, Object inducedError) {
|
Object res = null;
|
||||||
if (done) {
|
StackData.pushFrame(ctx, frame);
|
||||||
if (inducedError != Runners.NO_RETURN)
|
state = 0;
|
||||||
throw new EngineException(inducedError);
|
|
||||||
currPromise.fulfill(ctx, new ObjectValue(ctx, Map.of(
|
|
||||||
"done", true,
|
|
||||||
"value", inducedReturn == Runners.NO_RETURN ? null : inducedReturn
|
|
||||||
)));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object res = null;
|
while (state == 0) {
|
||||||
StackData.pushFrame(ctx, frame);
|
try {
|
||||||
state = 0;
|
res = frame.next(ctx, inducedValue, inducedReturn, inducedError == Runners.NO_RETURN ? null : new EngineException(inducedError));
|
||||||
|
inducedValue = inducedReturn = inducedError = Runners.NO_RETURN;
|
||||||
while (state == 0) {
|
if (res != Runners.NO_RETURN) {
|
||||||
try {
|
var obj = new ObjectValue();
|
||||||
res = frame.next(ctx, inducedValue, inducedReturn, inducedError == Runners.NO_RETURN ? null : new EngineException(inducedError));
|
obj.defineProperty(ctx, "done", true);
|
||||||
inducedValue = inducedReturn = inducedError = Runners.NO_RETURN;
|
obj.defineProperty(ctx, "value", res);
|
||||||
if (res != Runners.NO_RETURN) {
|
currPromise.fulfill(ctx, obj);
|
||||||
var obj = new ObjectValue();
|
|
||||||
obj.defineProperty(ctx, "done", true);
|
|
||||||
obj.defineProperty(ctx, "value", res);
|
|
||||||
currPromise.fulfill(ctx, obj);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (EngineException e) {
|
|
||||||
currPromise.reject(ctx, e.value);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (EngineException e) {
|
||||||
StackData.popFrame(ctx, frame);
|
currPromise.reject(ctx, e.value);
|
||||||
|
break;
|
||||||
if (state == 1) {
|
|
||||||
PromiseLib.then(ctx, frame.pop(), new NativeFunction(this::fulfill), new NativeFunction(this::reject));
|
|
||||||
}
|
|
||||||
else if (state == 2) {
|
|
||||||
var obj = new ObjectValue();
|
|
||||||
obj.defineProperty(ctx, "done", false);
|
|
||||||
obj.defineProperty(ctx, "value", frame.pop());
|
|
||||||
currPromise.fulfill(ctx, obj);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
StackData.popFrame(ctx, frame);
|
||||||
public String toString() {
|
|
||||||
if (done) return "Generator [closed]";
|
|
||||||
if (state != 0) return "Generator [suspended]";
|
|
||||||
return "Generator [running]";
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object fulfill(Context ctx, Object thisArg, Object ...args) {
|
if (state == 1) {
|
||||||
next(ctx, args.length > 0 ? args[0] : null, Runners.NO_RETURN, Runners.NO_RETURN);
|
PromiseLib.then(ctx, frame.pop(), new NativeFunction(this::fulfill), new NativeFunction(this::reject));
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
public Object reject(Context ctx, Object thisArg, Object ...args) {
|
else if (state == 2) {
|
||||||
next(ctx, Runners.NO_RETURN, args.length > 0 ? args[0] : null, Runners.NO_RETURN);
|
var obj = new ObjectValue();
|
||||||
return null;
|
obj.defineProperty(ctx, "done", false);
|
||||||
}
|
obj.defineProperty(ctx, "value", frame.pop());
|
||||||
|
currPromise.fulfill(ctx, obj);
|
||||||
@Native
|
|
||||||
public PromiseLib next(Context ctx, Object ...args) {
|
|
||||||
this.currPromise = new PromiseLib();
|
|
||||||
if (args.length == 0) next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, Runners.NO_RETURN);
|
|
||||||
else next(ctx, args[0], Runners.NO_RETURN, Runners.NO_RETURN);
|
|
||||||
return this.currPromise;
|
|
||||||
}
|
|
||||||
@Native("throw")
|
|
||||||
public PromiseLib _throw(Context ctx, Object error) {
|
|
||||||
this.currPromise = new PromiseLib();
|
|
||||||
next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, error);
|
|
||||||
return this.currPromise;
|
|
||||||
}
|
|
||||||
@Native("return")
|
|
||||||
public PromiseLib _return(Context ctx, Object value) {
|
|
||||||
this.currPromise = new PromiseLib();
|
|
||||||
next(ctx, Runners.NO_RETURN, value, Runners.NO_RETURN);
|
|
||||||
return this.currPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Object await(Context ctx, Object thisArg, Object[] args) {
|
|
||||||
this.state = 1;
|
|
||||||
return args.length > 0 ? args[0] : null;
|
|
||||||
}
|
|
||||||
public Object yield(Context ctx, Object thisArg, Object[] args) {
|
|
||||||
this.state = 2;
|
|
||||||
return args.length > 0 ? args[0] : null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object call(Context ctx, Object thisArg, Object ...args) {
|
public String toString() {
|
||||||
var handler = new AsyncGenerator();
|
if (done) return "Generator [closed]";
|
||||||
var func = factory.call(ctx, thisArg,
|
if (state != 0) return "Generator [suspended]";
|
||||||
new NativeFunction("await", handler::await),
|
return "Generator [running]";
|
||||||
new NativeFunction("yield", handler::yield)
|
|
||||||
);
|
|
||||||
if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function.");
|
|
||||||
handler.frame = new CodeFrame(ctx, thisArg, args, (CodeFunction)func);
|
|
||||||
return handler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AsyncGeneratorLib(FunctionValue factory) {
|
public Object fulfill(Context ctx, Object thisArg, Object ...args) {
|
||||||
super(factory.name, factory.length);
|
next(ctx, args.length > 0 ? args[0] : null, Runners.NO_RETURN, Runners.NO_RETURN);
|
||||||
this.factory = factory;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
public Object reject(Context ctx, Object thisArg, Object ...args) {
|
||||||
|
next(ctx, Runners.NO_RETURN, args.length > 0 ? args[0] : null, Runners.NO_RETURN);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native
|
||||||
|
public PromiseLib next(Context ctx, Object ...args) {
|
||||||
|
this.currPromise = new PromiseLib();
|
||||||
|
if (args.length == 0) next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, Runners.NO_RETURN);
|
||||||
|
else next(ctx, args[0], Runners.NO_RETURN, Runners.NO_RETURN);
|
||||||
|
return this.currPromise;
|
||||||
|
}
|
||||||
|
@Native("throw")
|
||||||
|
public PromiseLib _throw(Context ctx, Object error) {
|
||||||
|
this.currPromise = new PromiseLib();
|
||||||
|
next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, error);
|
||||||
|
return this.currPromise;
|
||||||
|
}
|
||||||
|
@Native("return")
|
||||||
|
public PromiseLib _return(Context ctx, Object value) {
|
||||||
|
this.currPromise = new PromiseLib();
|
||||||
|
next(ctx, Runners.NO_RETURN, value, Runners.NO_RETURN);
|
||||||
|
return this.currPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Object await(Context ctx, Object thisArg, Object[] args) {
|
||||||
|
this.state = 1;
|
||||||
|
return args.length > 0 ? args[0] : null;
|
||||||
|
}
|
||||||
|
public Object yield(Context ctx, Object thisArg, Object[] args) {
|
||||||
|
this.state = 2;
|
||||||
|
return args.length > 0 ? args[0] : null;
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,7 @@ import me.topchetoeu.jscript.interop.Native;
|
|||||||
import me.topchetoeu.jscript.interop.NativeConstructor;
|
import me.topchetoeu.jscript.interop.NativeConstructor;
|
||||||
import me.topchetoeu.jscript.interop.NativeInit;
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
public class BooleanLib {
|
@Native("Boolean") public class BooleanLib {
|
||||||
public static final BooleanLib TRUE = new BooleanLib(true);
|
public static final BooleanLib TRUE = new BooleanLib(true);
|
||||||
public static final BooleanLib FALSE = new BooleanLib(false);
|
public static final BooleanLib FALSE = new BooleanLib(false);
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import java.util.TimeZone;
|
|||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
|
||||||
public class DateLib {
|
@Native("Date") public class DateLib {
|
||||||
private Calendar normal;
|
private Calendar normal;
|
||||||
private Calendar utc;
|
private Calendar utc;
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import me.topchetoeu.jscript.interop.Native;
|
|||||||
import me.topchetoeu.jscript.interop.NativeConstructor;
|
import me.topchetoeu.jscript.interop.NativeConstructor;
|
||||||
import me.topchetoeu.jscript.interop.NativeInit;
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
public class ErrorLib {
|
@Native("Error") public class ErrorLib {
|
||||||
private static String toString(Context ctx, boolean rethrown, Object cause, Object name, Object message, ArrayValue stack) {
|
private static String toString(Context ctx, boolean rethrown, Object cause, Object name, Object message, ArrayValue stack) {
|
||||||
if (name == null) name = "";
|
if (name == null) name = "";
|
||||||
else name = Values.toString(ctx, name).trim();
|
else name = Values.toString(ctx, name).trim();
|
||||||
@ -23,13 +23,6 @@ public class ErrorLib {
|
|||||||
if (!message.equals("") && !name.equals("")) res.append(": ");
|
if (!message.equals("") && !name.equals("")) res.append(": ");
|
||||||
if (!message.equals("")) res.append(message);
|
if (!message.equals("")) res.append(message);
|
||||||
|
|
||||||
if (stack != null) {
|
|
||||||
for (var el : stack) {
|
|
||||||
var str = Values.toString(ctx, el).trim();
|
|
||||||
if (!str.equals("")) res.append("\n ").append(el);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cause instanceof ObjectValue) {
|
if (cause instanceof ObjectValue) {
|
||||||
if (rethrown) res.append("\n (rethrown)");
|
if (rethrown) res.append("\n (rethrown)");
|
||||||
else res.append("\nCaused by ").append(toString(ctx, cause));
|
else res.append("\nCaused by ").append(toString(ctx, cause));
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package me.topchetoeu.jscript.lib;
|
package me.topchetoeu.jscript.lib;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.Environment;
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
@ -11,7 +13,11 @@ import me.topchetoeu.jscript.interop.InitType;
|
|||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
import me.topchetoeu.jscript.interop.NativeInit;
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
public class FunctionLib {
|
@Native("Function") public class FunctionLib {
|
||||||
|
@Native(thisArg = true) public static Object location(Context ctx, FunctionValue func) {
|
||||||
|
if (func instanceof CodeFunction) return ((CodeFunction)func).loc().toString();
|
||||||
|
else return Location.INTERNAL.toString();
|
||||||
|
}
|
||||||
@Native(thisArg = true) public static Object apply(Context ctx, FunctionValue func, Object thisArg, ArrayValue args) {
|
@Native(thisArg = true) public static Object apply(Context ctx, FunctionValue func, Object thisArg, ArrayValue args) {
|
||||||
return func.call(ctx, thisArg, args.toArray());
|
return func.call(ctx, thisArg, args.toArray());
|
||||||
}
|
}
|
||||||
@ -44,10 +50,10 @@ public class FunctionLib {
|
|||||||
return new AsyncFunctionLib(func);
|
return new AsyncFunctionLib(func);
|
||||||
}
|
}
|
||||||
@Native public static FunctionValue asyncGenerator(FunctionValue func) {
|
@Native public static FunctionValue asyncGenerator(FunctionValue func) {
|
||||||
return new AsyncGeneratorLib(func);
|
return new AsyncGeneratorFunctionLib(func);
|
||||||
}
|
}
|
||||||
@Native public static FunctionValue generator(FunctionValue func) {
|
@Native public static FunctionValue generator(FunctionValue func) {
|
||||||
return new GeneratorLib(func);
|
return new GeneratorFunctionLib(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NativeInit(InitType.PROTOTYPE) public static void init(Environment env, ObjectValue target) {
|
@NativeInit(InitType.PROTOTYPE) public static void init(Environment env, ObjectValue target) {
|
||||||
|
27
src/me/topchetoeu/jscript/lib/GeneratorFunctionLib.java
Normal file
27
src/me/topchetoeu/jscript/lib/GeneratorFunctionLib.java
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package me.topchetoeu.jscript.lib;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||||
|
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
||||||
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
|
||||||
|
@Native("GeneratorFunction") public class GeneratorFunctionLib extends FunctionValue {
|
||||||
|
public final FunctionValue factory;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object call(Context ctx, Object thisArg, Object ...args) {
|
||||||
|
var handler = new GeneratorLib();
|
||||||
|
var func = factory.call(ctx, thisArg, new NativeFunction("yield", handler::yield));
|
||||||
|
if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function.");
|
||||||
|
handler.frame = new CodeFrame(ctx, thisArg, args, (CodeFunction)func);
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeneratorFunctionLib(FunctionValue factory) {
|
||||||
|
super(factory.name, factory.length);
|
||||||
|
this.factory = factory;
|
||||||
|
}
|
||||||
|
}
|
@ -4,99 +4,78 @@ import me.topchetoeu.jscript.engine.Context;
|
|||||||
import me.topchetoeu.jscript.engine.StackData;
|
import me.topchetoeu.jscript.engine.StackData;
|
||||||
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||||
import me.topchetoeu.jscript.engine.frame.Runners;
|
import me.topchetoeu.jscript.engine.frame.Runners;
|
||||||
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
|
||||||
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
|
||||||
public class GeneratorLib extends FunctionValue {
|
@Native("Generator") public class GeneratorLib {
|
||||||
public final FunctionValue factory;
|
private boolean yielding = true;
|
||||||
|
private boolean done = false;
|
||||||
|
public CodeFrame frame;
|
||||||
|
|
||||||
public static class Generator {
|
@Native("@@Symbol.typeName") public final String name = "Generator";
|
||||||
private boolean yielding = true;
|
|
||||||
private boolean done = false;
|
|
||||||
public CodeFrame frame;
|
|
||||||
|
|
||||||
@Native("@@Symbol.typeName") public final String name = "Generator";
|
private ObjectValue next(Context ctx, Object inducedValue, Object inducedReturn, Object inducedError) {
|
||||||
|
if (done) {
|
||||||
|
if (inducedError != Runners.NO_RETURN) throw new EngineException(inducedError);
|
||||||
|
var res = new ObjectValue();
|
||||||
|
res.defineProperty(ctx, "done", true);
|
||||||
|
res.defineProperty(ctx, "value", inducedReturn == Runners.NO_RETURN ? null : inducedReturn);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
private ObjectValue next(Context ctx, Object inducedValue, Object inducedReturn, Object inducedError) {
|
Object res = null;
|
||||||
if (done) {
|
StackData.pushFrame(ctx, frame);
|
||||||
if (inducedError != Runners.NO_RETURN) throw new EngineException(inducedError);
|
yielding = false;
|
||||||
var res = new ObjectValue();
|
|
||||||
res.defineProperty(ctx, "done", true);
|
|
||||||
res.defineProperty(ctx, "value", inducedReturn == Runners.NO_RETURN ? null : inducedReturn);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object res = null;
|
while (!yielding) {
|
||||||
StackData.pushFrame(ctx, frame);
|
try {
|
||||||
yielding = false;
|
res = frame.next(ctx, inducedValue, inducedReturn, inducedError == Runners.NO_RETURN ? null : new EngineException(inducedError));
|
||||||
|
inducedReturn = inducedError = Runners.NO_RETURN;
|
||||||
while (!yielding) {
|
if (res != Runners.NO_RETURN) {
|
||||||
try {
|
|
||||||
res = frame.next(ctx, inducedValue, inducedReturn, inducedError == Runners.NO_RETURN ? null : new EngineException(inducedError));
|
|
||||||
inducedReturn = inducedError = Runners.NO_RETURN;
|
|
||||||
if (res != Runners.NO_RETURN) {
|
|
||||||
done = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (EngineException e) {
|
|
||||||
done = true;
|
done = true;
|
||||||
throw e;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (EngineException e) {
|
||||||
StackData.popFrame(ctx, frame);
|
done = true;
|
||||||
if (done) frame = null;
|
throw e;
|
||||||
else res = frame.pop();
|
}
|
||||||
|
|
||||||
var obj = new ObjectValue();
|
|
||||||
obj.defineProperty(ctx, "done", done);
|
|
||||||
obj.defineProperty(ctx, "value", res);
|
|
||||||
return obj;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native
|
StackData.popFrame(ctx, frame);
|
||||||
public ObjectValue next(Context ctx, Object ...args) {
|
if (done) frame = null;
|
||||||
if (args.length == 0) return next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, Runners.NO_RETURN);
|
else res = frame.pop();
|
||||||
else return next(ctx, args[0], Runners.NO_RETURN, Runners.NO_RETURN);
|
|
||||||
}
|
|
||||||
@Native("throw")
|
|
||||||
public ObjectValue _throw(Context ctx, Object error) {
|
|
||||||
return next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, error);
|
|
||||||
}
|
|
||||||
@Native("return")
|
|
||||||
public ObjectValue _return(Context ctx, Object value) {
|
|
||||||
return next(ctx, Runners.NO_RETURN, value, Runners.NO_RETURN);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
var obj = new ObjectValue();
|
||||||
public String toString() {
|
obj.defineProperty(ctx, "done", done);
|
||||||
if (done) return "Generator [closed]";
|
obj.defineProperty(ctx, "value", res);
|
||||||
if (yielding) return "Generator [suspended]";
|
return obj;
|
||||||
return "Generator [running]";
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public Object yield(Context ctx, Object thisArg, Object[] args) {
|
@Native
|
||||||
this.yielding = true;
|
public ObjectValue next(Context ctx, Object ...args) {
|
||||||
return args.length > 0 ? args[0] : null;
|
if (args.length == 0) return next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, Runners.NO_RETURN);
|
||||||
}
|
else return next(ctx, args[0], Runners.NO_RETURN, Runners.NO_RETURN);
|
||||||
|
}
|
||||||
|
@Native("throw")
|
||||||
|
public ObjectValue _throw(Context ctx, Object error) {
|
||||||
|
return next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, error);
|
||||||
|
}
|
||||||
|
@Native("return")
|
||||||
|
public ObjectValue _return(Context ctx, Object value) {
|
||||||
|
return next(ctx, Runners.NO_RETURN, value, Runners.NO_RETURN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object call(Context ctx, Object thisArg, Object ...args) {
|
public String toString() {
|
||||||
var handler = new Generator();
|
if (done) return "Generator [closed]";
|
||||||
var func = factory.call(ctx, thisArg, new NativeFunction("yield", handler::yield));
|
if (yielding) return "Generator [suspended]";
|
||||||
if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function.");
|
return "Generator [running]";
|
||||||
handler.frame = new CodeFrame(ctx, thisArg, args, (CodeFunction)func);
|
|
||||||
return handler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public GeneratorLib(FunctionValue factory) {
|
public Object yield(Context ctx, Object thisArg, Object[] args) {
|
||||||
super(factory.name, factory.length);
|
this.yielding = true;
|
||||||
this.factory = factory;
|
return args.length > 0 ? args[0] : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,6 +11,7 @@ import me.topchetoeu.jscript.engine.scope.GlobalScope;
|
|||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
|
|
||||||
public class Internals {
|
public class Internals {
|
||||||
private static final DataKey<HashMap<Integer, Thread>> THREADS = new DataKey<>();
|
private static final DataKey<HashMap<Integer, Thread>> THREADS = new DataKey<>();
|
||||||
@ -20,6 +21,7 @@ public class Internals {
|
|||||||
@Native public static void log(Context ctx, Object ...args) {
|
@Native public static void log(Context ctx, Object ...args) {
|
||||||
for (var arg : args) {
|
for (var arg : args) {
|
||||||
Values.printValue(ctx, arg);
|
Values.printValue(ctx, arg);
|
||||||
|
System.out.print(" ");
|
||||||
}
|
}
|
||||||
System.out.println();
|
System.out.println();
|
||||||
}
|
}
|
||||||
@ -101,6 +103,13 @@ public class Internals {
|
|||||||
return NumberLib.isInfinite(ctx, val);
|
return NumberLib.isInfinite(ctx, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NativeGetter public static double NaN(Context ctx) {
|
||||||
|
return Double.NaN;
|
||||||
|
}
|
||||||
|
@NativeGetter public static double Infinity(Context ctx) {
|
||||||
|
return Double.POSITIVE_INFINITY;
|
||||||
|
}
|
||||||
|
|
||||||
public void apply(Environment env) {
|
public void apply(Environment env) {
|
||||||
var wp = env.wrappers;
|
var wp = env.wrappers;
|
||||||
var glob = env.global = new GlobalScope(wp.getNamespace(Internals.class));
|
var glob = env.global = new GlobalScope(wp.getNamespace(Internals.class));
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
package me.topchetoeu.jscript.lib;
|
package me.topchetoeu.jscript.lib;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Filename;
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
import me.topchetoeu.jscript.json.JSON;
|
import me.topchetoeu.jscript.json.JSON;
|
||||||
|
|
||||||
public class JSONLib {
|
@Native("JSON") public class JSONLib {
|
||||||
@Native
|
@Native
|
||||||
public static Object parse(Context ctx, String val) {
|
public static Object parse(Context ctx, String val) {
|
||||||
try {
|
try {
|
||||||
return JSON.toJs(JSON.parse(new Filename("jscript", "json"), val));
|
return JSON.toJs(JSON.parse(null, val));
|
||||||
}
|
}
|
||||||
catch (SyntaxException e) { throw EngineException.ofSyntax(e.msg); }
|
catch (SyntaxException e) { throw EngineException.ofSyntax(e.msg); }
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import me.topchetoeu.jscript.engine.values.Values;
|
|||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
import me.topchetoeu.jscript.interop.NativeGetter;
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
|
|
||||||
public class MapLib {
|
@Native("Map") public class MapLib {
|
||||||
private LinkedHashMap<Object, Object> map = new LinkedHashMap<>();
|
private LinkedHashMap<Object, Object> map = new LinkedHashMap<>();
|
||||||
|
|
||||||
@Native("@@Symbol.typeName") public final String name = "Map";
|
@Native("@@Symbol.typeName") public final String name = "Map";
|
||||||
@ -64,7 +64,7 @@ public class MapLib {
|
|||||||
@Native public void forEach(Context ctx, FunctionValue func, Object thisArg) {
|
@Native public void forEach(Context ctx, FunctionValue func, Object thisArg) {
|
||||||
var keys = new ArrayList<>(map.keySet());
|
var keys = new ArrayList<>(map.keySet());
|
||||||
|
|
||||||
for (var el : keys) func.call(ctx, thisArg, el, map.get(el), this);
|
for (var el : keys) func.call(ctx, thisArg, map.get(el), el,this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public MapLib(Context ctx, Object iterable) {
|
@Native public MapLib(Context ctx, Object iterable) {
|
||||||
|
@ -2,7 +2,7 @@ package me.topchetoeu.jscript.lib;
|
|||||||
|
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
|
||||||
public class MathLib {
|
@Native("Math") public class MathLib {
|
||||||
@Native public static final double E = Math.E;
|
@Native public static final double E = Math.E;
|
||||||
@Native public static final double PI = Math.PI;
|
@Native public static final double PI = Math.PI;
|
||||||
@Native public static final double SQRT2 = Math.sqrt(2);
|
@Native public static final double SQRT2 = Math.sqrt(2);
|
||||||
|
@ -9,7 +9,7 @@ import me.topchetoeu.jscript.interop.Native;
|
|||||||
import me.topchetoeu.jscript.interop.NativeConstructor;
|
import me.topchetoeu.jscript.interop.NativeConstructor;
|
||||||
import me.topchetoeu.jscript.interop.NativeInit;
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
public class NumberLib {
|
@Native("Number") public class NumberLib {
|
||||||
@Native public static final double EPSILON = java.lang.Math.ulp(1.0);
|
@Native public static final double EPSILON = java.lang.Math.ulp(1.0);
|
||||||
@Native public static final double MAX_SAFE_INTEGER = 9007199254740991.;
|
@Native public static final double MAX_SAFE_INTEGER = 9007199254740991.;
|
||||||
@Native public static final double MIN_SAFE_INTEGER = -MAX_SAFE_INTEGER;
|
@Native public static final double MIN_SAFE_INTEGER = -MAX_SAFE_INTEGER;
|
||||||
|
@ -13,7 +13,7 @@ import me.topchetoeu.jscript.interop.Native;
|
|||||||
import me.topchetoeu.jscript.interop.NativeConstructor;
|
import me.topchetoeu.jscript.interop.NativeConstructor;
|
||||||
import me.topchetoeu.jscript.interop.NativeInit;
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
public class ObjectLib {
|
@Native("Object") public class ObjectLib {
|
||||||
@Native public static ObjectValue assign(Context ctx, ObjectValue dst, Object... src) {
|
@Native public static ObjectValue assign(Context ctx, ObjectValue dst, Object... src) {
|
||||||
for (var obj : src) {
|
for (var obj : src) {
|
||||||
for (var key : Values.getMembers(ctx, obj, true, true)) {
|
for (var key : Values.getMembers(ctx, obj, true, true)) {
|
||||||
|
@ -18,7 +18,7 @@ import me.topchetoeu.jscript.interop.InitType;
|
|||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
import me.topchetoeu.jscript.interop.NativeInit;
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
public class PromiseLib {
|
@Native("Promise") public class PromiseLib {
|
||||||
private static class Handle {
|
private static class Handle {
|
||||||
public final Context ctx;
|
public final Context ctx;
|
||||||
public final FunctionValue fulfilled;
|
public final FunctionValue fulfilled;
|
||||||
|
@ -4,10 +4,11 @@ import me.topchetoeu.jscript.engine.Context;
|
|||||||
import me.topchetoeu.jscript.engine.Environment;
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.interop.InitType;
|
import me.topchetoeu.jscript.interop.InitType;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
import me.topchetoeu.jscript.interop.NativeConstructor;
|
import me.topchetoeu.jscript.interop.NativeConstructor;
|
||||||
import me.topchetoeu.jscript.interop.NativeInit;
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
public class RangeErrorLib extends ErrorLib {
|
@Native("RangeError") public class RangeErrorLib extends ErrorLib {
|
||||||
@NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) {
|
@NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) {
|
||||||
var target = ErrorLib.constructor(ctx, thisArg, message);
|
var target = ErrorLib.constructor(ctx, thisArg, message);
|
||||||
target.defineProperty(ctx, "name", "RangeError");
|
target.defineProperty(ctx, "name", "RangeError");
|
||||||
|
@ -6,13 +6,14 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.NativeWrapper;
|
import me.topchetoeu.jscript.engine.values.NativeWrapper;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
import me.topchetoeu.jscript.interop.NativeGetter;
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
|
|
||||||
public class RegExpLib {
|
@Native("RegExp") public class RegExpLib {
|
||||||
// I used Regex to analyze Regex
|
// I used Regex to analyze Regex
|
||||||
private static final Pattern NAMED_PATTERN = Pattern.compile("\\(\\?<([^=!].*?)>", Pattern.DOTALL);
|
private static final Pattern NAMED_PATTERN = Pattern.compile("\\(\\?<([^=!].*?)>", Pattern.DOTALL);
|
||||||
private static final Pattern ESCAPE_PATTERN = Pattern.compile("[/\\-\\\\^$*+?.()|\\[\\]{}]");
|
private static final Pattern ESCAPE_PATTERN = Pattern.compile("[/\\-\\\\^$*+?.()|\\[\\]{}]");
|
||||||
@ -81,7 +82,6 @@ public class RegExpLib {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Native public Object exec(String str) {
|
@Native public Object exec(String str) {
|
||||||
var matcher = pattern.matcher(str);
|
var matcher = pattern.matcher(str);
|
||||||
if (lastI > str.length() || !matcher.find(lastI) || sticky && matcher.start() != lastI) {
|
if (lastI > str.length() || !matcher.find(lastI) || sticky && matcher.start() != lastI) {
|
||||||
@ -133,7 +133,7 @@ public class RegExpLib {
|
|||||||
return "/" + source + "/" + flags();
|
return "/" + source + "/" + flags();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native("@@Symvol.match") public Object match(Context ctx, String target) {
|
@Native("@@Symbol.match") public Object match(Context ctx, String target) {
|
||||||
if (this.global) {
|
if (this.global) {
|
||||||
var res = new ArrayValue();
|
var res = new ArrayValue();
|
||||||
Object val;
|
Object val;
|
||||||
@ -150,7 +150,7 @@ public class RegExpLib {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native("@@Symvol.matchAll") public Object matchAll(Context ctx, String target) {
|
@Native("@@Symbol.matchAll") public Object matchAll(Context ctx, String target) {
|
||||||
var pattern = new RegExpLib(this.source, this.flags() + "g");
|
var pattern = new RegExpLib(this.source, this.flags() + "g");
|
||||||
|
|
||||||
return Values.fromJavaIterator(ctx, new Iterator<Object>() {
|
return Values.fromJavaIterator(ctx, new Iterator<Object>() {
|
||||||
@ -171,8 +171,8 @@ public class RegExpLib {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native("@@Symvol.split") public ArrayValue split(Context ctx, String target, Object limit, boolean sensible) {
|
@Native("@@Symbol.split") public ArrayValue split(Context ctx, String target, Object limit, boolean sensible) {
|
||||||
var pattern = new RegExpLib(this.source, this.flags() + "g");
|
var pattern = new RegExpLib(this.source, this.flags() + "g");
|
||||||
Object match;
|
Object match;
|
||||||
int lastEnd = 0;
|
int lastEnd = 0;
|
||||||
@ -214,29 +214,41 @@ public class RegExpLib {
|
|||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
// [Symbol.replace](target, replacement) {
|
|
||||||
// const pattern = new this.constructor(this, this.flags + "d") as RegExp;
|
@Native("@@Symbol.replace") public String replace(Context ctx, String target, Object replacement) {
|
||||||
// let match: RegExpResult | null;
|
var pattern = new RegExpLib(this.source, this.flags() + "d");
|
||||||
// let lastEnd = 0;
|
Object match;
|
||||||
// const res: string[] = [];
|
var lastEnd = 0;
|
||||||
// // log(pattern.toString());
|
var res = new StringBuilder();
|
||||||
// while ((match = pattern.exec(target)) !== null) {
|
|
||||||
// const indices = match.indices![0];
|
while ((match = pattern.exec(target)) != Values.NULL) {
|
||||||
// res.push(target.substring(lastEnd, indices[0]));
|
var indices = (ArrayValue)((ArrayValue)Values.getMember(ctx, match, "indices")).get(0);
|
||||||
// if (replacement instanceof Function) {
|
var arrMatch = (ArrayValue)match;
|
||||||
// res.push(replacement(target.substring(indices[0], indices[1]), ...match.slice(1), indices[0], target));
|
|
||||||
// }
|
var start = ((Number)indices.get(0)).intValue();
|
||||||
// else {
|
var end = ((Number)indices.get(1)).intValue();
|
||||||
// res.push(replacement);
|
|
||||||
// }
|
res.append(target.substring(lastEnd, start));
|
||||||
// lastEnd = indices[1];
|
if (replacement instanceof FunctionValue) {
|
||||||
// if (!pattern.global) break;
|
var args = new Object[arrMatch.size() + 2];
|
||||||
// }
|
args[0] = target.substring(start, end);
|
||||||
// if (lastEnd < target.length) {
|
arrMatch.copyTo(args, 1, 1, arrMatch.size() - 1);
|
||||||
// res.push(target.substring(lastEnd));
|
args[args.length - 2] = start;
|
||||||
// }
|
args[args.length - 1] = target;
|
||||||
// return res.join('');
|
res.append(Values.toString(ctx, ((FunctionValue)replacement).call(ctx, null, args)));
|
||||||
// },
|
}
|
||||||
|
else {
|
||||||
|
res.append(Values.toString(ctx, replacement));
|
||||||
|
}
|
||||||
|
lastEnd = end;
|
||||||
|
if (!pattern.global) break;
|
||||||
|
}
|
||||||
|
if (lastEnd < target.length()) {
|
||||||
|
res.append(target.substring(lastEnd));
|
||||||
|
}
|
||||||
|
return res.toString();
|
||||||
|
}
|
||||||
|
|
||||||
// [Symbol.search](target, reverse, start) {
|
// [Symbol.search](target, reverse, start) {
|
||||||
// const pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp;
|
// const pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp;
|
||||||
// if (!reverse) {
|
// if (!reverse) {
|
||||||
@ -258,6 +270,7 @@ public class RegExpLib {
|
|||||||
// else return -1;
|
// else return -1;
|
||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
|
|
||||||
@Native public RegExpLib(Context ctx, Object pattern, Object flags) {
|
@Native public RegExpLib(Context ctx, Object pattern, Object flags) {
|
||||||
this(cleanupPattern(ctx, pattern), cleanupFlags(ctx, flags));
|
this(cleanupPattern(ctx, pattern), cleanupFlags(ctx, flags));
|
||||||
}
|
}
|
||||||
@ -276,6 +289,7 @@ public class RegExpLib {
|
|||||||
if (flags.contains("s")) this.flags |= Pattern.DOTALL;
|
if (flags.contains("s")) this.flags |= Pattern.DOTALL;
|
||||||
if (flags.contains("u")) this.flags |= Pattern.UNICODE_CHARACTER_CLASS;
|
if (flags.contains("u")) this.flags |= Pattern.UNICODE_CHARACTER_CLASS;
|
||||||
|
|
||||||
|
if (pattern.equals("{(\\d+)}")) pattern = "\\{([0-9]+)\\}";
|
||||||
this.pattern = Pattern.compile(pattern.replace("\\d", "[0-9]"), this.flags);
|
this.pattern = Pattern.compile(pattern.replace("\\d", "[0-9]"), this.flags);
|
||||||
|
|
||||||
var matcher = NAMED_PATTERN.matcher(source);
|
var matcher = NAMED_PATTERN.matcher(source);
|
||||||
|
@ -12,7 +12,7 @@ import me.topchetoeu.jscript.engine.values.Values;
|
|||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
import me.topchetoeu.jscript.interop.NativeGetter;
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
|
|
||||||
public class SetLib {
|
@Native("Set") public class SetLib {
|
||||||
private LinkedHashSet<Object> set = new LinkedHashSet<>();
|
private LinkedHashSet<Object> set = new LinkedHashSet<>();
|
||||||
|
|
||||||
@Native("@@Symbol.typeName") public final String name = "Set";
|
@Native("@@Symbol.typeName") public final String name = "Set";
|
||||||
|
@ -16,7 +16,7 @@ import me.topchetoeu.jscript.interop.NativeGetter;
|
|||||||
import me.topchetoeu.jscript.interop.NativeInit;
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
// TODO: implement index wrapping properly
|
// TODO: implement index wrapping properly
|
||||||
public class StringLib {
|
@Native("String") public class StringLib {
|
||||||
public final String value;
|
public final String value;
|
||||||
|
|
||||||
private static String passThis(Context ctx, String funcName, Object val) {
|
private static String passThis(Context ctx, String funcName, Object val) {
|
||||||
@ -111,7 +111,7 @@ public class StringLib {
|
|||||||
return lastIndexOf(ctx, passThis(ctx, "includes", thisArg), term, pos) >= 0;
|
return lastIndexOf(ctx, passThis(ctx, "includes", thisArg), term, pos) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native(thisArg = true) public static String replace(Context ctx, Object thisArg, Object term, String replacement) {
|
@Native(thisArg = true) public static String replace(Context ctx, Object thisArg, Object term, Object replacement) {
|
||||||
var val = passThis(ctx, "replace", thisArg);
|
var val = passThis(ctx, "replace", thisArg);
|
||||||
|
|
||||||
if (term != null && term != Values.NULL && !(term instanceof String)) {
|
if (term != null && term != Values.NULL && !(term instanceof String)) {
|
||||||
@ -121,9 +121,9 @@ public class StringLib {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return val.replaceFirst(Pattern.quote(Values.toString(ctx, term)), replacement);
|
return val.replaceFirst(Pattern.quote(Values.toString(ctx, term)), Values.toString(ctx, replacement));
|
||||||
}
|
}
|
||||||
@Native(thisArg = true) public static String replaceAll(Context ctx, Object thisArg, Object term, String replacement) {
|
@Native(thisArg = true) public static String replaceAll(Context ctx, Object thisArg, Object term, Object replacement) {
|
||||||
var val = passThis(ctx, "replaceAll", thisArg);
|
var val = passThis(ctx, "replaceAll", thisArg);
|
||||||
|
|
||||||
if (term != null && term != Values.NULL && !(term instanceof String)) {
|
if (term != null && term != Values.NULL && !(term instanceof String)) {
|
||||||
@ -133,7 +133,7 @@ public class StringLib {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return val.replaceFirst(Pattern.quote(Values.toString(ctx, term)), replacement);
|
return val.replaceFirst(Pattern.quote(Values.toString(ctx, term)), Values.toString(ctx, replacement));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native(thisArg = true) public static ArrayValue match(Context ctx, Object thisArg, Object term, String replacement) {
|
@Native(thisArg = true) public static ArrayValue match(Context ctx, Object thisArg, Object term, String replacement) {
|
||||||
|
@ -15,7 +15,7 @@ import me.topchetoeu.jscript.interop.NativeConstructor;
|
|||||||
import me.topchetoeu.jscript.interop.NativeGetter;
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
import me.topchetoeu.jscript.interop.NativeInit;
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
public class SymbolLib {
|
@Native("Symbol") public class SymbolLib {
|
||||||
private static final Map<String, Symbol> symbols = new HashMap<>();
|
private static final Map<String, Symbol> symbols = new HashMap<>();
|
||||||
|
|
||||||
@NativeGetter public static Symbol typeName(Context ctx) { return ctx.environment().symbol("Symbol.typeName"); }
|
@NativeGetter public static Symbol typeName(Context ctx) { return ctx.environment().symbol("Symbol.typeName"); }
|
||||||
|
@ -4,10 +4,11 @@ import me.topchetoeu.jscript.engine.Context;
|
|||||||
import me.topchetoeu.jscript.engine.Environment;
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.interop.InitType;
|
import me.topchetoeu.jscript.interop.InitType;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
import me.topchetoeu.jscript.interop.NativeConstructor;
|
import me.topchetoeu.jscript.interop.NativeConstructor;
|
||||||
import me.topchetoeu.jscript.interop.NativeInit;
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
public class SyntaxErrorLib extends ErrorLib {
|
@Native("SyntaxError") public class SyntaxErrorLib extends ErrorLib {
|
||||||
@NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) {
|
@NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) {
|
||||||
var target = ErrorLib.constructor(ctx, thisArg, message);
|
var target = ErrorLib.constructor(ctx, thisArg, message);
|
||||||
target.defineProperty(ctx, "name", "SyntaxError");
|
target.defineProperty(ctx, "name", "SyntaxError");
|
||||||
|
@ -4,10 +4,11 @@ import me.topchetoeu.jscript.engine.Context;
|
|||||||
import me.topchetoeu.jscript.engine.Environment;
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.interop.InitType;
|
import me.topchetoeu.jscript.interop.InitType;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
import me.topchetoeu.jscript.interop.NativeConstructor;
|
import me.topchetoeu.jscript.interop.NativeConstructor;
|
||||||
import me.topchetoeu.jscript.interop.NativeInit;
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
public class TypeErrorLib extends ErrorLib {
|
@Native("TypeError") public class TypeErrorLib extends ErrorLib {
|
||||||
@NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) {
|
@NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) {
|
||||||
var target = ErrorLib.constructor(ctx, thisArg, message);
|
var target = ErrorLib.constructor(ctx, thisArg, message);
|
||||||
target.defineProperty(ctx, "name", "TypeError");
|
target.defineProperty(ctx, "name", "TypeError");
|
||||||
|
@ -82,8 +82,6 @@ public class Parsing {
|
|||||||
// Although ES5 allow these, we will comply to ES6 here
|
// Although ES5 allow these, we will comply to ES6 here
|
||||||
reserved.add("const");
|
reserved.add("const");
|
||||||
reserved.add("let");
|
reserved.add("let");
|
||||||
reserved.add("async");
|
|
||||||
reserved.add("super");
|
|
||||||
// These are allowed too, however our parser considers them keywords
|
// These are allowed too, however our parser considers them keywords
|
||||||
reserved.add("undefined");
|
reserved.add("undefined");
|
||||||
reserved.add("arguments");
|
reserved.add("arguments");
|
||||||
@ -259,7 +257,7 @@ public class Parsing {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CURR_LITERAL:
|
case CURR_LITERAL:
|
||||||
if (isAlphanumeric(c) || c == '_') {
|
if (isAlphanumeric(c) || c == '_' || c == '$') {
|
||||||
currToken.append(c);
|
currToken.append(c);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1060,7 +1058,6 @@ public class Parsing {
|
|||||||
|
|
||||||
if (!checkVarName(literal.result)) {
|
if (!checkVarName(literal.result)) {
|
||||||
if (literal.result.equals("await")) return ParseRes.error(loc, "'await' expressions are not supported.");
|
if (literal.result.equals("await")) return ParseRes.error(loc, "'await' expressions are not supported.");
|
||||||
if (literal.result.equals("async")) return ParseRes.error(loc, "'async' is not supported.");
|
|
||||||
if (literal.result.equals("const")) return ParseRes.error(loc, "'const' declarations are not supported.");
|
if (literal.result.equals("const")) return ParseRes.error(loc, "'const' declarations are not supported.");
|
||||||
if (literal.result.equals("let")) return ParseRes.error(loc, "'let' declarations are not supported.");
|
if (literal.result.equals("let")) return ParseRes.error(loc, "'let' declarations are not supported.");
|
||||||
return ParseRes.error(loc, String.format("Unexpected identifier '%s'.", literal.result));
|
return ParseRes.error(loc, String.format("Unexpected identifier '%s'.", literal.result));
|
||||||
@ -1137,7 +1134,9 @@ public class Parsing {
|
|||||||
var op = opRes.result;
|
var op = opRes.result;
|
||||||
if (!op.isAssign()) return ParseRes.failed();
|
if (!op.isAssign()) return ParseRes.failed();
|
||||||
|
|
||||||
if (!(prev instanceof AssignableStatement)) return ParseRes.error(loc, "Invalid expression on left hand side of assign operator.");
|
if (!(prev instanceof AssignableStatement)) {
|
||||||
|
return ParseRes.error(loc, "Invalid expression on left hand side of assign operator.");
|
||||||
|
}
|
||||||
|
|
||||||
var res = parseValue(filename, tokens, i + n, 2);
|
var res = parseValue(filename, tokens, i + n, 2);
|
||||||
if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected value after assignment operator '%s'.", op.value), res);
|
if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected value after assignment operator '%s'.", op.value), res);
|
||||||
@ -1411,8 +1410,7 @@ public class Parsing {
|
|||||||
|
|
||||||
var valRes = parseValue(filename, tokens, i + n, 0);
|
var valRes = parseValue(filename, tokens, i + n, 0);
|
||||||
n += valRes.n;
|
n += valRes.n;
|
||||||
if (valRes.isError())
|
if (valRes.isError()) return ParseRes.error(loc, "Expected a return value.", valRes);
|
||||||
return ParseRes.error(loc, "Expected a return value.", valRes);
|
|
||||||
|
|
||||||
var res = ParseRes.res(new ReturnStatement(loc, valRes.result), n);
|
var res = ParseRes.res(new ReturnStatement(loc, valRes.result), n);
|
||||||
|
|
||||||
@ -1534,8 +1532,7 @@ public class Parsing {
|
|||||||
if (!condRes.isSuccess()) return ParseRes.error(loc, "Expected an if condition.", condRes);
|
if (!condRes.isSuccess()) return ParseRes.error(loc, "Expected an if condition.", condRes);
|
||||||
n += condRes.n;
|
n += condRes.n;
|
||||||
|
|
||||||
if (!isOperator(tokens, i + n++, Operator.PAREN_CLOSE))
|
if (!isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after if condition.");
|
||||||
return ParseRes.error(loc, "Expected a closing paren after if condition.");
|
|
||||||
|
|
||||||
var res = parseStatement(filename, tokens, i + n);
|
var res = parseStatement(filename, tokens, i + n);
|
||||||
if (!res.isSuccess()) return ParseRes.error(loc, "Expected an if body.", res);
|
if (!res.isSuccess()) return ParseRes.error(loc, "Expected an if body.", res);
|
||||||
@ -1702,8 +1699,7 @@ public class Parsing {
|
|||||||
parseVariableDeclare(filename, tokens, i + n),
|
parseVariableDeclare(filename, tokens, i + n),
|
||||||
parseValueStatement(filename, tokens, i + n)
|
parseValueStatement(filename, tokens, i + n)
|
||||||
);
|
);
|
||||||
if (!declRes.isSuccess())
|
if (!declRes.isSuccess()) return ParseRes.error(loc, "Expected a declaration or an expression.", declRes);
|
||||||
return ParseRes.error(loc, "Expected a declaration or an expression.", declRes);
|
|
||||||
n += declRes.n;
|
n += declRes.n;
|
||||||
decl = declRes.result;
|
decl = declRes.result;
|
||||||
}
|
}
|
||||||
|
10
tests/arithmetics/counters.js
Normal file
10
tests/arithmetics/counters.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
return new UnitTest('counters')
|
||||||
|
.add('postfix increment', function () { var i = 10; i++ === 10; })
|
||||||
|
.add('postfix decrement', function () { var i = 10; i-- === 10; })
|
||||||
|
.add('prefix decrement', function () { var i = 10; --i === 9; })
|
||||||
|
.add('prefix increment', function () { var i = 10; ++i === 11; })
|
||||||
|
.add('ostfix increment of non-number', function () { var i = 'hi mom'; isNaN(i++); })
|
||||||
|
.add('ostfix decrement of non-number', function () { var i = 'hi mom'; isNaN(i--); })
|
||||||
|
.add('prefix increment of non-number', function () { var i = 'hi mom'; isNaN(++i); })
|
||||||
|
.add('prefix decrement of non-number', function () { var i = 'hi mom'; isNaN(--i); })
|
||||||
|
.add('postfix increment of convertible to number', function () { var i = '10'; i++; i === 11; })
|
2
tests/arithmetics/index.js
Normal file
2
tests/arithmetics/index.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
return new UnitTest('Arithmetics')
|
||||||
|
.add(include('counters.js'))
|
4
tests/array/concat.js
Normal file
4
tests/array/concat.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
return new UnitTest('concat', function() { return typeof Array.prototype.concat === 'function'; })
|
||||||
|
.add('two arrays', function() { return match([1, 2, 3], [1].concat([2], [3])) })
|
||||||
|
.add('simple spread', function() { return match([1, 2, 3, 4, 5], [1].concat([2], 3, [4, 5])) })
|
||||||
|
.add('sparse concat', function() { return match([1,, 2,,, 3,,, 4, 5], [1,,2].concat([,,3,,,4], 5)) })
|
22
tests/array/index.js
Normal file
22
tests/array/index.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
function runIterator(arr, method, func, n) {
|
||||||
|
var res = [];
|
||||||
|
var j = 1;
|
||||||
|
var args = [ function() {
|
||||||
|
var pushed = [];
|
||||||
|
for (var i = 0; i < n; i++) pushed[i] = arguments[i];
|
||||||
|
res[j++] = pushed;
|
||||||
|
return func.apply(this, arguments);
|
||||||
|
} ];
|
||||||
|
|
||||||
|
for (var i = 4; i < arguments.length; i++) args[i - 3] = arguments[i];
|
||||||
|
|
||||||
|
res[0] = method.apply(arr, args);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new UnitTest('Array', function() { []; })
|
||||||
|
.add(include('length.js'))
|
||||||
|
.add(include('reduce.js'))
|
||||||
|
.add(include('sparse.js'))
|
||||||
|
.add(include('concat.js'))
|
26
tests/array/length.js
Normal file
26
tests/array/length.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
return new UnitTest('length & capacity', function() { return 'length' in Array.prototype; })
|
||||||
|
.add('empty literal', function() { return [].length === 0 })
|
||||||
|
.add('filled literal', function() { return [1, 2, 3].length === 3 })
|
||||||
|
.add('set length', function() {
|
||||||
|
var a = [];
|
||||||
|
a.length = 10;
|
||||||
|
return a.length === 10;
|
||||||
|
})
|
||||||
|
.add('length after set', function() {
|
||||||
|
var a = [];
|
||||||
|
a [5]= 5;
|
||||||
|
return a.length === 6;
|
||||||
|
})
|
||||||
|
.add('length after set (big', function() {
|
||||||
|
var a = [1, 2];
|
||||||
|
a [5000]= 5;
|
||||||
|
return a.length === 5001;
|
||||||
|
})
|
||||||
|
.add('expand test', function() {
|
||||||
|
var a = [];
|
||||||
|
for (var i = 0; i < 1000; i++) {
|
||||||
|
a[i] = i * 50;
|
||||||
|
if (a[i] !== i * 50) return false;
|
||||||
|
}
|
||||||
|
return a.length === 1000;
|
||||||
|
})
|
44
tests/array/reduce.js
Normal file
44
tests/array/reduce.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
var res = [];
|
||||||
|
|
||||||
|
return new UnitTest('reduceRight', function () { return typeof Array.prototype.reduceRight === 'function' })
|
||||||
|
.add('empty function', function () {match(
|
||||||
|
[ undefined, [4, 3, 2], [undefined, 2, 1], [undefined, 1, 0], ],
|
||||||
|
runIterator([1, 2, 3, 4], Array.prototype.reduceRight, function() { }, 3), 1
|
||||||
|
)})
|
||||||
|
.add('adder', function () {match(
|
||||||
|
[ 10, [4, 3, 2], [7, 2, 1], [9, 1, 0], ],
|
||||||
|
runIterator([1, 2, 3, 4], Array.prototype.reduceRight, function(a, b) { return a + b; }, 3), 1
|
||||||
|
)})
|
||||||
|
.add('sparse array', function () {match(
|
||||||
|
[ 10, [4, 3, 11], [7, 2, 7], [9, 1, 3], ],
|
||||||
|
runIterator([,,,1,,,, 2,,,, 3,,,, 4,,,,], Array.prototype.reduceRight, function(a, b) { return a + b }, 3), 1
|
||||||
|
)})
|
||||||
|
.add('sparse array with one element', function () {match(
|
||||||
|
[ 1 ],
|
||||||
|
runIterator([,,,1,,,,], Array.prototype.reduceRight, function(v) { return v; }, 3), 1
|
||||||
|
)})
|
||||||
|
.add('sparse array with no elements', function () {match(
|
||||||
|
[ undefined ],
|
||||||
|
runIterator([,,,,,,,], Array.prototype.reduceRight, function(v) { return v; }, 3), 1
|
||||||
|
)})
|
||||||
|
|
||||||
|
.add('initial value and empty function', function () {match(
|
||||||
|
[ undefined, [0, 4, 3], [undefined, 3, 2], [undefined, 2, 1], [undefined, 1, 0] ],
|
||||||
|
runIterator([1, 2, 3, 4], Array.prototype.reduceRight, function() { }, 3, 0), 1
|
||||||
|
)})
|
||||||
|
.add('initial value and adder', function () {match(
|
||||||
|
[ 15, [5, 4, 3], [9, 3, 2], [12, 2, 1], [14, 1, 0] ],
|
||||||
|
runIterator([1, 2, 3, 4], Array.prototype.reduceRight, function(a, b) { return a + b; }, 3, 5), 1
|
||||||
|
)})
|
||||||
|
.add('initial value, sparce array and adder', function () {match(
|
||||||
|
[ 15, [5, 4, 15], [9, 3, 11], [12, 2, 7], [14, 1, 3] ],
|
||||||
|
runIterator([,,,1,,,, 2,,,, 3,,,, 4,,,,], Array.prototype.reduceRight, function(a, b) { return a + b; }, 3, 5), 1
|
||||||
|
)})
|
||||||
|
.add('initial value and sparse array with one element', function () {match(
|
||||||
|
[ 6, [5, 1, 3] ],
|
||||||
|
runIterator([,,,1,,,,], Array.prototype.reduceRight, function(a, b) { return a + b; }, 3, 5), 1
|
||||||
|
)})
|
||||||
|
.add('initial value and sparse array with no elements', function () {match(
|
||||||
|
[ 5 ],
|
||||||
|
runIterator([,,,,,,,], Array.prototype.reduceRight, function(v) { return v; }, 3, 5), 1
|
||||||
|
)});
|
4
tests/array/sort.js
Normal file
4
tests/array/sort.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
return new UnitTest('concat', function() { return typeof Array.prototype.concat === 'function'; })
|
||||||
|
.add('two arrays', function() { return match([1, 2, 3], [1].concat([2], [3])) })
|
||||||
|
.add('simple spread', function() { return match([1, 2, 3, 4, 5], [1].concat([2], 3, [4, 5])) })
|
||||||
|
.add('sparse concat', function() { return match([1,, 2,,, 3,,, 4, 5], [1,,2].concat([,,3,,,4], 5)) })
|
5
tests/array/sparse.js
Normal file
5
tests/array/sparse.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
return new UnitTest('sparse', function() { return !(0 in [,,]) })
|
||||||
|
.add('empty in start', function() { var a = [,1]; return !(0 in a) && (1 in a); })
|
||||||
|
.add('empty in middle', function() { var a = [1,,2]; return !(1 in a) && (2 in a) && (0 in a); })
|
||||||
|
.add('empty in end', function() { var a = [1,,]; return !(1 in a) && (0 in a); })
|
||||||
|
.add('trailing comma', function() { var a = [1,]; return a.length === 1; })
|
70
tests/index.js
Normal file
70
tests/index.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
function assert(cond, msg, locDepth) {
|
||||||
|
if (locDepth < 0 || locDepth === undefined) locDepth = 0;
|
||||||
|
if (!cond) {
|
||||||
|
log('Assert failed', (typeof locDepth === 'string' ? locDepth : Error().stack[locDepth + 1]) + ': ', msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function assertMatch(expected, actual, depth, msg) {
|
||||||
|
if (!match(expected, actual, depth)) {
|
||||||
|
log('Assert failed', Error().stack[1] + ': ', msg);
|
||||||
|
log('Expected:', expected);
|
||||||
|
log('Actual:', actual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function match(expected, actual, depth) {
|
||||||
|
if (!Array.isArray(expected) || !Array.isArray(actual)) return expected === actual;
|
||||||
|
else if (expected.length !== actual.length) return false;
|
||||||
|
else if (depth === undefined || depth < 0) depth = 0;
|
||||||
|
|
||||||
|
for (var i = 0; i < expected.length; i++) {
|
||||||
|
if (!(i in expected) || !(i in actual)) return !(i in expected) && !(i in actual);
|
||||||
|
|
||||||
|
if (
|
||||||
|
expected[i] === actual[i] ||
|
||||||
|
depth > 0 &&
|
||||||
|
Array.isArray(expected) &&
|
||||||
|
Array.isArray(actual) &&
|
||||||
|
match(expected[i], actual[i], depth - 1)
|
||||||
|
) continue;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @class */ function UnitTest(msg, exec) {
|
||||||
|
this.name = msg;
|
||||||
|
this.exec = exec;
|
||||||
|
this.subtests = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
UnitTest.prototype.run = function(path) {
|
||||||
|
if (path === undefined) path = [];
|
||||||
|
|
||||||
|
path.push(this.name);
|
||||||
|
|
||||||
|
if (typeof this.exec === 'function') {
|
||||||
|
var res = true, err = 'exec() returned false.';
|
||||||
|
try {
|
||||||
|
if (this.exec() === false) res = false;
|
||||||
|
}
|
||||||
|
catch (e) { res = false; err = e; }
|
||||||
|
assert(res, path.join('/') + ': ' + err, this.exec.location());
|
||||||
|
}
|
||||||
|
for (var i = 0; i < this.subtests.length; i++) {
|
||||||
|
this.subtests[i].run(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
path.pop();
|
||||||
|
}
|
||||||
|
UnitTest.prototype.add = function(test, exec) {
|
||||||
|
if (test instanceof UnitTest) this.subtests.push(test);
|
||||||
|
else this.subtests.push(new UnitTest(test, exec));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
include('arithmetics/index.js').run();
|
||||||
|
include('array/index.js').run();
|
||||||
|
|
||||||
|
log('Tests complete.');
|
Loading…
Reference in New Issue
Block a user