Merge pull request #8 from TopchetoEU/TopchetoEU/tests

Integrate typescript
This commit is contained in:
TopchetoEU 2023-11-05 20:32:42 +02:00 committed by GitHub
commit df8465cb49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 931 additions and 86691 deletions

View File

@ -10,8 +10,6 @@ const conf = {
version: argv[3]
};
console.log(conf)
if (conf.version.startsWith('refs/tags/')) conf.version = conf.version.substring(10);
if (conf.version.startsWith('v')) conf.version = conf.version.substring(1);

View File

@ -46,6 +46,8 @@ public class Filename {
public Filename(String protocol, String path) {
path = path.trim();
protocol = protocol.trim();
this.protocol = protocol;
this.path = path;
}

View File

@ -1,7 +1,7 @@
package me.topchetoeu.jscript;
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 start;
private Filename filename;

View File

@ -1,131 +1,153 @@
package me.topchetoeu.jscript;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Engine;
import me.topchetoeu.jscript.engine.Environment;
import me.topchetoeu.jscript.engine.debug.DebugServer;
import me.topchetoeu.jscript.engine.debug.SimpleDebugger;
import me.topchetoeu.jscript.engine.values.ArrayValue;
import me.topchetoeu.jscript.engine.values.NativeFunction;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.events.Observer;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.exceptions.InterruptException;
import me.topchetoeu.jscript.exceptions.SyntaxException;
import me.topchetoeu.jscript.exceptions.UncheckedException;
import me.topchetoeu.jscript.lib.Internals;
public class Main {
static Thread engineTask, debugTask;
static Engine engine;
static Environment env;
static int j = 0;
private static Observer<Object> valuePrinter = new Observer<Object>() {
public void next(Object data) {
Values.printValue(null, data);
System.out.println();
}
public void error(RuntimeException err) {
Values.printError(err, null);
}
@Override
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));
engine = new Engine();
env = new Environment(null, null, null);
var exited = new boolean[1];
var server = new DebugServer();
server.targets.put("target", (ws, req) -> SimpleDebugger.get(ws, engine));
engineTask = engine.start();
debugTask = server.start(new InetSocketAddress("127.0.0.1", 9229), true);
// server.awaitConnection();
engine.pushMsg(false, null, new NativeFunction((ctx, thisArg, _a) -> {
new Internals().apply(env);
env.global.define("exit", _ctx -> {
exited[0] = true;
throw new InterruptException();
});
env.global.define("go", _ctx -> {
try {
var f = Path.of("do.js");
var func = _ctx.compile(new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f)));
return func.call(_ctx);
}
catch (IOException e) {
throw new EngineException("Couldn't open do.js");
}
});
return null;
}), null).await();
try {
var ts = engine.pushMsg(
false, new Context(engine).pushEnv(env),
new Filename("file", "/mnt/data/repos/java-jscript/src/me/topchetoeu/jscript/js/ts.js"),
Reading.resourceToString("js/ts.js"), null
).await();
System.out.println("Loaded typescript!");
engine.pushMsg(
false, new Context(engine).pushEnv(env.child()),
new Filename("jscript", "internals/bootstrap.js"), Reading.resourceToString("js/bootstrap.js"), null,
ts, env, new ArrayValue(null, Reading.resourceToString("js/lib.d.ts"))
).await();
}
catch (EngineException e) {
Values.printError(e, "(while initializing TS)");
System.out.println("engine reported stack trace:");
for (var el : e.stackTrace) {
System.out.println(el);
}
}
var reader = new Thread(() -> {
try {
for (var i = 0; ; i++) {
try {
var raw = Reading.read();
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) { return; }
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();
}
}
catch (Throwable e) { throw new UncheckedException(e); }
if (exited[0]) debugTask.interrupt();
});
reader.setDaemon(true);
reader.setName("STD Reader");
reader.start();
}
}
package me.topchetoeu.jscript;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Engine;
import me.topchetoeu.jscript.engine.Environment;
import me.topchetoeu.jscript.engine.StackData;
import me.topchetoeu.jscript.engine.debug.DebugServer;
import me.topchetoeu.jscript.engine.debug.SimpleDebugger;
import me.topchetoeu.jscript.engine.values.ArrayValue;
import me.topchetoeu.jscript.engine.values.NativeFunction;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.events.Observer;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.exceptions.InterruptException;
import me.topchetoeu.jscript.exceptions.SyntaxException;
import me.topchetoeu.jscript.lib.Internals;
public class Main {
static Thread engineTask, debugTask;
static Engine engine;
static Environment env;
static int j = 0;
private static Observer<Object> valuePrinter = new Observer<Object>() {
public void next(Object data) {
Values.printValue(null, data);
System.out.println();
}
public void error(RuntimeException err) {
Values.printError(err, null);
}
@Override
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));
engine = new Engine(true);
env = new Environment(null, null, null);
var exited = new boolean[1];
var server = new DebugServer();
server.targets.put("target", (ws, req) -> new SimpleDebugger(ws, engine));
engineTask = engine.start();
debugTask = server.start(new InetSocketAddress("127.0.0.1", 9229), true);
engine.pushMsg(false, null, new NativeFunction((ctx, thisArg, _a) -> {
new Internals().apply(env);
env.global.define("exit", _ctx -> {
exited[0] = true;
throw new InterruptException();
});
env.global.define("go", _ctx -> {
try {
var f = Path.of("do.js");
var func = _ctx.compile(new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f)));
return func.call(_ctx);
}
catch (IOException e) {
throw new EngineException("Couldn't open do.js");
}
});
// TODO: make better API
env.global.define(true, new NativeFunction("include", (_ctx, th, __args) -> {
try {
var currFilename = StackData.peekFrame(_ctx).function.loc().filename();
var loc = Path.of("").toAbsolutePath();
if (currFilename.protocol.equals("file")) loc = Path.of(currFilename.path).getParent();
var path = loc.resolve(Path.of(__args.length >= 1 ? Values.toString(_ctx, __args[0]) : ""));
var src = Files.readString(path);
var func = _ctx.compile(Filename.fromFile(path.toFile()), src);
var callArgs = new ArrayValue();
if (__args.length >= 2 && __args[1] instanceof ArrayValue) callArgs = (ArrayValue)__args[1];
return func.call(_ctx, null, callArgs);
}
catch (IOException e) { throw EngineException.ofError("IOError", "Couldn't open file."); }
}));
return null;
}), null).await();
try {
var tsEnv = env.child();
tsEnv.global.define(null, "module", false, new ObjectValue());
engine.pushMsg(
false, new Context(engine).pushEnv(tsEnv),
new Filename("jscript", "ts.js"),
Reading.resourceToString("js/ts.js"), null
).await();
System.out.println("Loaded typescript!");
var ctx = new Context(engine).pushEnv(env.child());
engine.pushMsg(
false, ctx,
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"))
).await();
}
catch (EngineException e) {
Values.printError(e, "(while initializing TS)");
}
var reader = new Thread(() -> {
try {
for (var arg : args) {
try {
var file = Path.of(arg);
var raw = Files.readString(file);
valuePrinter.next(engine.pushMsg(false, new Context(engine).pushEnv(env), Filename.fromFile(file.toFile()), raw, null).await());
}
catch (EngineException e) { Values.printError(e, ""); }
}
for (var i = 0; ; i++) {
try {
var raw = Reading.read();
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();
}
}

View File

@ -17,6 +17,10 @@ public class ChangeStatement extends Statement {
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, scope, true);
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) {

View File

@ -29,7 +29,7 @@ public class LazyAndStatement extends Statement {
if (pollute) target.add(Instruction.dup().locate(loc()));
int start = target.size();
target.add(Instruction.nop());
target.add(Instruction.discard().locate(loc()));
if (pollute) target.add(Instruction.discard().locate(loc()));
second.compile(target, scope, pollute);
target.set(start, Instruction.jmpIfNot(target.size() - start).locate(loc()));
}

View File

@ -29,7 +29,7 @@ public class LazyOrStatement extends Statement {
if (pollute) target.add(Instruction.dup().locate(loc()));
int start = target.size();
target.add(Instruction.nop());
target.add(Instruction.discard().locate(loc()));
if (pollute) target.add(Instruction.discard().locate(loc()));
second.compile(target, scope, pollute);
target.set(start, Instruction.jmpIf(target.size() - start).locate(loc()));
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -31,12 +31,6 @@ import me.topchetoeu.jscript.json.JSON;
import me.topchetoeu.jscript.json.JSONElement;
import me.topchetoeu.jscript.json.JSONList;
import me.topchetoeu.jscript.json.JSONMap;
import me.topchetoeu.jscript.lib.DateLib;
import me.topchetoeu.jscript.lib.MapLib;
import me.topchetoeu.jscript.lib.PromiseLib;
import me.topchetoeu.jscript.lib.RegExpLib;
import me.topchetoeu.jscript.lib.SetLib;
import me.topchetoeu.jscript.lib.GeneratorLib.Generator;
public class SimpleDebugger implements Debugger {
public static final String CHROME_GET_PROP_FUNC = "function s(e){let t=this;const n=JSON.parse(e);for(let e=0,i=n.length;e<i;++e)t=t[n[e]];return t}";
@ -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_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}";
private static enum State {
RESUMED,
STEPPING_IN,
@ -104,7 +98,7 @@ public class SimpleDebugger implements Debugger {
public CodeFrame frame;
public CodeFunction func;
public int id;
public ObjectValue local, capture, global;
public ObjectValue local, capture, global, valstack;
public JSONMap serialized;
public Location location;
public boolean debugData = false;
@ -126,6 +120,7 @@ public class SimpleDebugger implements Debugger {
this.capture = frame.getCaptureScope(ctx, true);
this.local.setPrototype(ctx, capture);
this.capture.setPrototype(ctx, global);
this.valstack = frame.getValStackScope(ctx);
if (location != null) {
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", "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", "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) {
var engine = new Engine();
var engine = new Engine(false);
var env = codeFrame.func.environment.fork();
ObjectValue global = env.global.obj,
@ -415,7 +411,7 @@ public class SimpleDebugger implements Debugger {
env.global = new GlobalScope(local);
var ctx = new Context(engine).pushEnv(env);
var awaiter = engine.pushMsg(false, ctx, new Filename("temp", "exec"), "(" + code + ")", codeFrame.frame.thisArg, codeFrame.frame.args);
var awaiter = engine.pushMsg(false, ctx, new Filename("jscript", "eval"), code, codeFrame.frame.thisArg, codeFrame.frame.args);
engine.run(true);
@ -479,13 +475,16 @@ public class SimpleDebugger implements Debugger {
@Override public void setBreakpointByUrl(V8Message msg) {
var line = (int)msg.params.number("lineNumber") + 1;
var col = (int)msg.params.number("columnNumber", 0) + 1;
var cond = msg.params.string("condition", null);
if (cond != null) cond = "(" + cond + ")";
Pattern regex;
if (msg.params.isString("url")) regex = Pattern.compile(Pattern.quote(msg.params.string("url")));
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);
var locs = new JSONList();
@ -594,27 +593,23 @@ public class SimpleDebugger implements Debugger {
}
@Override public void getProperties(V8Message msg) {
var obj = idToObject.get(Integer.parseInt(msg.params.string("objectId")));
var own = msg.params.bool("ownProperties");
var accessorPropertiesOnly = msg.params.bool("accessorPropertiesOnly", false);
var currOwn = true;
var res = new JSONList();
var ctx = objectToCtx.get(obj);
while (obj != emptyObject && obj != null) {
var ctx = objectToCtx.get(obj);
if (obj != emptyObject) {
for (var key : obj.keys(true)) {
var propDesc = new JSONMap();
if (obj.properties.containsKey(key)) {
var prop = obj.properties.get(key);
propDesc.set("name", Values.toString(ctx, key));
if (prop.getter != null) propDesc.set("get", serializeObj(ctx, prop.getter));
if (prop.setter != null) propDesc.set("set", serializeObj(ctx, prop.setter));
propDesc.set("enumerable", obj.memberEnumerable(key));
propDesc.set("configurable", obj.memberConfigurable(key));
propDesc.set("isOwn", currOwn);
propDesc.set("isOwn", true);
res.add(propDesc);
}
else {
@ -623,26 +618,21 @@ public class SimpleDebugger implements Debugger {
propDesc.set("writable", obj.memberWritable(key));
propDesc.set("enumerable", obj.memberEnumerable(key));
propDesc.set("configurable", obj.memberConfigurable(key));
propDesc.set("isOwn", currOwn);
propDesc.set("isOwn", true);
res.add(propDesc);
}
}
obj = obj.getPrototype(ctx);
if (currOwn) {
var protoDesc = new JSONMap();
protoDesc.set("name", "__proto__");
protoDesc.set("value", serializeObj(ctx, obj == null ? Values.NULL : obj));
protoDesc.set("writable", true);
protoDesc.set("enumerable", false);
protoDesc.set("configurable", false);
protoDesc.set("isOwn", currOwn);
res.add(protoDesc);
}
currOwn = false;
if (true) break;
var proto = obj.getPrototype(ctx);
var protoDesc = new JSONMap();
protoDesc.set("name", "__proto__");
protoDesc.set("value", serializeObj(ctx, proto == null ? Values.NULL : proto));
protoDesc.set("writable", true);
protoDesc.set("enumerable", false);
protoDesc.set("configurable", false);
protoDesc.set("isOwn", true);
res.add(protoDesc);
}
ws.send(msg.respond(new JSONMap().set("result", res)));
@ -667,36 +657,40 @@ public class SimpleDebugger implements Debugger {
else src = src.substring(0, start) + src.substring(end + 1);
}
switch (src) {
case CHROME_GET_PROP_FUNC: {
var path = JSON.parse(new Filename("tmp", "json"), (String)args.get(0)).list();
Object res = thisArg;
for (var el : path) res = Values.getMember(ctx, res, JSON.toJs(el));
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, res))));
return;
try {
switch (src) {
case CHROME_GET_PROP_FUNC: {
var path = JSON.parse(null, (String)args.get(0)).list();
Object res = thisArg;
for (var el : path) res = Values.getMember(ctx, res, JSON.toJs(el));
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, res))));
return;
}
case VSCODE_STRINGIFY_VAL:
case VSCODE_STRINGIFY_PROPS:
case VSCODE_SHALLOW_COPY:
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
@ -708,8 +702,8 @@ public class SimpleDebugger implements Debugger {
int id = nextId();
var src = new Source(id, filename, source, locations);
filenameToId.put(filename, id);
idToSource.put(id, src);
filenameToId.put(filename, id);
for (var bpcd : idToBptCand.values()) {
if (!bpcd.pattern.matcher(filename.toString()).matches()) continue;
@ -738,14 +732,15 @@ public class SimpleDebugger implements Debugger {
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)) {
pauseException(ctx);
}
else if (isBreakpointable && locToBreakpoint.containsKey(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));
}
else if (isBreakpointable && tmpBreakpts.remove(loc)) pauseDebug(ctx, null);
@ -801,27 +796,18 @@ public class SimpleDebugger implements Debugger {
}
@Override public void connect() {
target.data.set(StackData.DEBUGGER, this);
if (!target.attachDebugger(this)) {
ws.send(new V8Error("A debugger is already attached to this engine."));
}
}
@Override public void disconnect() {
target.data.remove(StackData.DEBUGGER);
target.detachDebugger();
enabled = false;
updateNotifier.next();
}
private SimpleDebugger(WebSocket ws, Engine target) {
public SimpleDebugger(WebSocket ws, Engine target) {
this.ws = ws;
this.target = target;
}
public static SimpleDebugger get(WebSocket ws, Engine target) {
if (target.data.has(StackData.DEBUGGER)) {
ws.send(new V8Error("A debugger is already attached to this engine."));
return null;
}
else {
var res = new SimpleDebugger(ws, target);
return res;
}
}
}

View File

@ -2,7 +2,6 @@ package me.topchetoeu.jscript.engine.debug;
import java.util.Map;
import me.topchetoeu.jscript.Filename;
import me.topchetoeu.jscript.json.JSON;
import me.topchetoeu.jscript.json.JSONElement;
import me.topchetoeu.jscript.json.JSONMap;
@ -33,7 +32,7 @@ public class V8Message {
this.params = raw.contains("params") ? raw.map("params") : new JSONMap();
}
public V8Message(String raw) {
this(JSON.parse(new Filename("jscript", "json-msg"), raw).map());
this(JSON.parse(null, raw).map());
}
public JSONMap toMap() {

View File

@ -87,15 +87,18 @@ public class WebSocket implements AutoCloseable {
}
private synchronized void write(int type, byte[] data) {
try {
for (int i = 0; i < (data.length >> 16); i++) {
int i;
for (i = 0; i < data.length / 0xFFFF; i++) {
out().write(type);
writeLength(0xFFFF);
out().write(data, i << 16, 0xFFFF);
out().write(data, i * 0xFFFF, 0xFFFF);
type = 0;
}
out().write(type | 0x80);
writeLength(data.length & 0xFFFF);
out().write(data, data.length & 0xFFFF0000, data.length & 0xFFFF);
writeLength(data.length % 0xFFFF);
out().write(data, i * 0xFFFF, data.length % 0xFFFF);
}
catch (IOException e) {
throw new UncheckedIOException(e);
@ -116,7 +119,7 @@ public class WebSocket implements AutoCloseable {
}
public void send(Object data) {
// TODO: Remove
System.out.println("SEND: " + data);
// System.out.println("SEND: " + data);
if (closed) throw new IllegalStateException("Object is closed.");
write(1, data.toString().getBytes());
}
@ -199,7 +202,7 @@ public class WebSocket implements AutoCloseable {
var raw = data.toByteArray();
// 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));

View File

@ -1,11 +1,10 @@
package me.topchetoeu.jscript.engine.frame;
import java.util.List;
import java.util.Stack;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction;
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.ValueVariable;
import me.topchetoeu.jscript.engine.values.ArrayValue;
@ -17,7 +16,7 @@ import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.exceptions.InterruptException;
public class CodeFrame {
private class TryCtx {
public static class TryCtx {
public static final int STATE_TRY = 0;
public static final int STATE_CATCH = 1;
public static final int STATE_FINALLY_THREW = 2;
@ -84,9 +83,30 @@ public class CodeFrame {
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) {
var res = new TryCtx(codePtr + 1, n, catchN, finallyN);
if (!tryStack.empty()) res.err = tryStack.peek().err;
tryStack.add(res);
}
@ -125,34 +145,31 @@ public class CodeFrame {
}
private void setCause(Context ctx, EngineException err, EngineException cause) {
// err.cause = 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) {
if (value != Runners.NO_RETURN) push(ctx, value);
var debugger = StackData.getDebugger(ctx);
if (returnValue == Runners.NO_RETURN && error == null) {
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);
returnValue = nextNoTry(ctx, instr);
var instr = function.body[codePtr];
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; }
}
@ -192,11 +209,11 @@ public class CodeFrame {
break;
case TryCtx.STATE_CATCH:
if (error != null) {
setCause(ctx, error, tryCtx.err);
if (tryCtx.hasFinally) {
tryCtx.err = error;
newState = TryCtx.STATE_FINALLY_THREW;
}
setCause(ctx, error, tryCtx.err);
break;
}
else if (returnValue != Runners.NO_RETURN) {
@ -218,27 +235,31 @@ public class CodeFrame {
case TryCtx.STATE_FINALLY_THREW:
if (error != null) setCause(ctx, error, tryCtx.err);
else if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) error = tryCtx.err;
else return Runners.NO_RETURN;
else if (returnValue == Runners.NO_RETURN) return Runners.NO_RETURN;
break;
case TryCtx.STATE_FINALLY_RETURNED:
if (error != null) setCause(ctx, error, tryCtx.err);
if (returnValue == Runners.NO_RETURN) {
if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) returnValue = tryCtx.retVal;
else return Runners.NO_RETURN;
}
break;
case TryCtx.STATE_FINALLY_JUMPED:
if (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;
else codePtr = tryCtx.end;
}
else return Runners.NO_RETURN;
else if (returnValue == Runners.NO_RETURN) return Runners.NO_RETURN;
break;
}
if (tryCtx.state == TryCtx.STATE_CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
if (newState == -1) {
var err = tryCtx.err;
tryStack.pop();
if (!tryStack.isEmpty()) tryStack.peek().err = err;
continue;
}
@ -247,7 +268,7 @@ public class CodeFrame {
case TryCtx.STATE_CATCH:
scope.catchVars.add(new ValueVariable(false, tryCtx.err.value));
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;
default:
codePtr = tryCtx.finallyStart;
@ -255,13 +276,13 @@ public class CodeFrame {
return Runners.NO_RETURN;
}
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;
}
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;
}

View File

@ -4,6 +4,7 @@ import java.util.Collections;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Engine;
import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ValueVariable;
import me.topchetoeu.jscript.engine.values.ArrayValue;
@ -26,16 +27,12 @@ public class Runners {
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) {
var callArgs = frame.take(instr.get(0));
var func = 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++;
return NO_RETURN;
@ -46,17 +43,6 @@ public class Runners {
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++;
return NO_RETURN;
}
@ -201,7 +187,7 @@ public class Runners {
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);
frame.push(ctx, func);

View File

@ -8,8 +8,7 @@ public class LocalScope {
public final ArrayList<ValueVariable> catchVars = new ArrayList<>();
public ValueVariable get(int i) {
if (i >= locals.length)
return catchVars.get(i - locals.length);
if (i >= locals.length) return catchVars.get(i - locals.length);
if (i >= 0) return locals[i];
else return captures[~i];
}

View File

@ -52,7 +52,7 @@ public class ArrayValue extends ObjectValue implements Iterable<Object> {
if (i >= size) size = i + 1;
}
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) {
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) {
// Iterate in reverse to reallocate at most once
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);
else if (values[i + sourceStart] == null) arr.remove(i + destStart);
else arr.set(ctx, i + destStart, values[i + sourceStart]);
}
}

View File

@ -103,8 +103,7 @@ public class ObjectValue {
) return true;
if (!extensible() && !values.containsKey(key) && !properties.containsKey(key)) return false;
if (!memberConfigurable(key))
return false;
if (!memberConfigurable(key)) return false;
nonWritableSet.remove(key);
nonEnumerableSet.remove(key);

View File

@ -258,8 +258,7 @@ public class Values {
public static Object getMember(Context ctx, Object obj, Object key) {
obj = normalize(ctx, obj); key = normalize(ctx, key);
if (obj == null)
throw new IllegalArgumentException("Tried to access member of undefined.");
if (obj == null) throw new IllegalArgumentException("Tried to access member of undefined.");
if (obj == NULL) throw new IllegalArgumentException("Tried to access member of null.");
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) {
obj = normalize(ctx, obj); key = normalize(ctx, key); val = normalize(ctx, val);
if (obj == null)
throw EngineException.ofType("Tried to access member of undefined.");
if (obj == null) throw EngineException.ofType("Tried to access member of undefined.");
if (obj == NULL) throw EngineException.ofType("Tried to access member of null.");
if (key.equals("__proto__")) return setPrototype(ctx, obj, val);
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) {
if (!isFunction(func))
throw EngineException.ofType("Tried to call a non-function value.");
if (!isFunction(func)) throw EngineException.ofType("Tried to call a non-function value.");
return function(func).call(ctx, thisArg, 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) {
return () -> {
try {
var _ctx = ctx;
var _obj = obj;
var symbol = ctx.environment().symbol("Symbol.iterator");
var iteratorFunc = getMember(ctx, obj, symbol);
@ -587,7 +582,11 @@ public class Values {
res.defineProperty(ctx, "next", new NativeFunction("", (_ctx, _th, _args) -> {
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;
@ -598,6 +597,11 @@ public class Values {
}
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)) {
System.out.print("[circular]");
return;

View File

@ -137,7 +137,11 @@ public class NativeWrapperProvider implements WrappersProvider {
* @param clazz The class for which a constructor should be generated
*/
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()) {
var nat = overload.getAnnotation(Native.class);

View File

@ -11,6 +11,7 @@ import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.exceptions.ConvertException;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.exceptions.InterruptException;
public class OverloadFunction extends FunctionValue {
public final List<Overload> overloads = new ArrayList<>();
@ -87,6 +88,9 @@ public class OverloadFunction extends FunctionValue {
e.printStackTrace();
throw EngineException.ofType("Unexpected value of 'undefined'.").add(name, loc);
}
else if (e.getTargetException() instanceof InterruptException || e.getTargetException() instanceof InterruptedException) {
throw new InterruptException();
}
else {
throw EngineException.ofError(e.getTargetException().getMessage()).add(name, loc);
}

View File

@ -1,7 +1,5 @@
(function (_arguments) {
var ts = _arguments[0];
log("Loaded typescript!");
var src = '', lib = _arguments[2].concat([ 'declare const exit: never;' ]).join(''), decls = '', version = 0;
var libSnapshot = ts.ScriptSnapshot.fromString(lib);
@ -42,7 +40,7 @@
service.getEmitOutput('/lib.d.ts');
log('Loaded libraries!');
function compile(filename, code) {
function compile(code, filename) {
src = code, version++;
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

View File

@ -177,6 +177,7 @@ public class JSON {
return ParseRes.res(values, n);
}
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);
if (res.isFailed()) throw new SyntaxException(null, "Invalid JSON given.");
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.isBoolean()) return el.bool() ? "true" : "false";
if (el.isNull()) return "null";
if (el.isString()) return "\"" + el.string()
.replace("\\", "\\\\")
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\"", "\\\"")
+ "\"";
if (el.isString()) {
var res = new StringBuilder("\"");
var alphabet = "0123456789ABCDEF".toCharArray();
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()) {
var res = new StringBuilder().append("[");
for (int i = 0; i < el.list().size(); i++) {

View File

@ -16,7 +16,7 @@ import me.topchetoeu.jscript.interop.NativeGetter;
import me.topchetoeu.jscript.interop.NativeInit;
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) {
return thisArg.size();
}
@ -93,13 +93,14 @@ public class ArrayLib {
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) -> {
var res = Values.toNumber(ctx, cmp.call(ctx, null, a, b));
if (res < 0) return -1;
if (res > 0) return 1;
return 0;
});
return arr;
}
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) {
var res = new ArrayValue(arr.size());
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) {
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;
}
@ -268,17 +300,14 @@ public class ArrayLib {
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);
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);
arr.copyTo(ctx, res, start, 0, end - start);
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) {
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) {
var res = new StringBuilder();
var comma = true;
var comma = false;
for (int i = 0; i < arr.size(); i++) {
if (!arr.has(i)) continue;
if (comma) res.append(sep);
comma = false;
comma = true;
var el = arr.get(i);
if (el == null || el == Values.NULL) continue;

View File

@ -8,8 +8,9 @@ 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;
public class AsyncFunctionLib extends FunctionValue {
@Native("AsyncFunction") public class AsyncFunctionLib extends FunctionValue {
public final FunctionValue factory;
public static class AsyncHelper {

View 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;
}
}

View File

@ -6,130 +6,106 @@ import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.StackData;
import me.topchetoeu.jscript.engine.frame.CodeFrame;
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.exceptions.EngineException;
import me.topchetoeu.jscript.interop.Native;
public class AsyncGeneratorLib extends FunctionValue {
public final FunctionValue factory;
@Native("AsyncGenerator") public class AsyncGeneratorLib {
@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 {
@Native("@@Symbol.typeName") public final String name = "AsyncGenerator";
private int state = 0;
private boolean done = false;
private PromiseLib currPromise;
public CodeFrame frame;
private void next(Context ctx, Object inducedValue, Object inducedReturn, Object inducedError) {
if (done) {
if (inducedError != Runners.NO_RETURN) throw new EngineException(inducedError);
currPromise.fulfill(ctx, new ObjectValue(ctx, Map.of(
"done", true,
"value", inducedReturn == Runners.NO_RETURN ? null : inducedReturn
)));
return;
}
private void next(Context ctx, Object inducedValue, Object inducedReturn, Object inducedError) {
if (done) {
if (inducedError != Runners.NO_RETURN)
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;
StackData.pushFrame(ctx, frame);
state = 0;
Object res = null;
StackData.pushFrame(ctx, frame);
state = 0;
while (state == 0) {
try {
res = frame.next(ctx, inducedValue, inducedReturn, inducedError == Runners.NO_RETURN ? null : new EngineException(inducedError));
inducedValue = inducedReturn = inducedError = Runners.NO_RETURN;
if (res != Runners.NO_RETURN) {
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);
while (state == 0) {
try {
res = frame.next(ctx, inducedValue, inducedReturn, inducedError == Runners.NO_RETURN ? null : new EngineException(inducedError));
inducedValue = inducedReturn = inducedError = Runners.NO_RETURN;
if (res != Runners.NO_RETURN) {
var obj = new ObjectValue();
obj.defineProperty(ctx, "done", true);
obj.defineProperty(ctx, "value", res);
currPromise.fulfill(ctx, obj);
break;
}
}
StackData.popFrame(ctx, frame);
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);
catch (EngineException e) {
currPromise.reject(ctx, e.value);
break;
}
}
@Override
public String toString() {
if (done) return "Generator [closed]";
if (state != 0) return "Generator [suspended]";
return "Generator [running]";
}
StackData.popFrame(ctx, frame);
public Object fulfill(Context ctx, Object thisArg, Object ...args) {
next(ctx, args.length > 0 ? args[0] : null, Runners.NO_RETURN, Runners.NO_RETURN);
return null;
if (state == 1) {
PromiseLib.then(ctx, frame.pop(), new NativeFunction(this::fulfill), new NativeFunction(this::reject));
}
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;
else if (state == 2) {
var obj = new ObjectValue();
obj.defineProperty(ctx, "done", false);
obj.defineProperty(ctx, "value", frame.pop());
currPromise.fulfill(ctx, obj);
}
}
@Override
public Object call(Context ctx, Object thisArg, Object ...args) {
var handler = new AsyncGenerator();
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 String toString() {
if (done) return "Generator [closed]";
if (state != 0) return "Generator [suspended]";
return "Generator [running]";
}
public AsyncGeneratorLib(FunctionValue factory) {
super(factory.name, factory.length);
this.factory = factory;
public Object fulfill(Context ctx, Object thisArg, Object ...args) {
next(ctx, args.length > 0 ? args[0] : null, Runners.NO_RETURN, Runners.NO_RETURN);
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;
}
}

View File

@ -9,7 +9,7 @@ import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeConstructor;
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 FALSE = new BooleanLib(false);

View File

@ -6,7 +6,7 @@ import java.util.TimeZone;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.interop.Native;
public class DateLib {
@Native("Date") public class DateLib {
private Calendar normal;
private Calendar utc;

View File

@ -11,7 +11,7 @@ import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeConstructor;
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) {
if (name == null) name = "";
else name = Values.toString(ctx, name).trim();
@ -23,13 +23,6 @@ public class ErrorLib {
if (!message.equals("") && !name.equals("")) res.append(": ");
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 (rethrown) res.append("\n (rethrown)");
else res.append("\nCaused by ").append(toString(ctx, cause));

View File

@ -1,8 +1,10 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Environment;
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.NativeFunction;
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.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) {
return func.call(ctx, thisArg, args.toArray());
}
@ -44,10 +50,10 @@ public class FunctionLib {
return new AsyncFunctionLib(func);
}
@Native public static FunctionValue asyncGenerator(FunctionValue func) {
return new AsyncGeneratorLib(func);
return new AsyncGeneratorFunctionLib(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) {

View 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;
}
}

View File

@ -4,99 +4,78 @@ import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.StackData;
import me.topchetoeu.jscript.engine.frame.CodeFrame;
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.exceptions.EngineException;
import me.topchetoeu.jscript.interop.Native;
public class GeneratorLib extends FunctionValue {
public final FunctionValue factory;
@Native("Generator") public class GeneratorLib {
private boolean yielding = true;
private boolean done = false;
public CodeFrame frame;
public static class Generator {
private boolean yielding = true;
private boolean done = false;
public CodeFrame frame;
@Native("@@Symbol.typeName") public final String name = "Generator";
@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) {
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;
}
Object res = null;
StackData.pushFrame(ctx, frame);
yielding = false;
Object res = null;
StackData.pushFrame(ctx, frame);
yielding = false;
while (!yielding) {
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) {
while (!yielding) {
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;
throw e;
break;
}
}
StackData.popFrame(ctx, frame);
if (done) frame = null;
else res = frame.pop();
var obj = new ObjectValue();
obj.defineProperty(ctx, "done", done);
obj.defineProperty(ctx, "value", res);
return obj;
catch (EngineException e) {
done = true;
throw e;
}
}
@Native
public ObjectValue next(Context ctx, Object ...args) {
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);
}
StackData.popFrame(ctx, frame);
if (done) frame = null;
else res = frame.pop();
@Override
public String toString() {
if (done) return "Generator [closed]";
if (yielding) return "Generator [suspended]";
return "Generator [running]";
}
var obj = new ObjectValue();
obj.defineProperty(ctx, "done", done);
obj.defineProperty(ctx, "value", res);
return obj;
}
public Object yield(Context ctx, Object thisArg, Object[] args) {
this.yielding = true;
return args.length > 0 ? args[0] : null;
}
@Native
public ObjectValue next(Context ctx, Object ...args) {
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
public Object call(Context ctx, Object thisArg, Object ...args) {
var handler = new Generator();
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 String toString() {
if (done) return "Generator [closed]";
if (yielding) return "Generator [suspended]";
return "Generator [running]";
}
public GeneratorLib(FunctionValue factory) {
super(factory.name, factory.length);
this.factory = factory;
public Object yield(Context ctx, Object thisArg, Object[] args) {
this.yielding = true;
return args.length > 0 ? args[0] : null;
}
}
}

View File

@ -11,6 +11,7 @@ import me.topchetoeu.jscript.engine.scope.GlobalScope;
import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeGetter;
public class Internals {
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) {
for (var arg : args) {
Values.printValue(ctx, arg);
System.out.print(" ");
}
System.out.println();
}
@ -101,6 +103,13 @@ public class Internals {
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) {
var wp = env.wrappers;
var glob = env.global = new GlobalScope(wp.getNamespace(Internals.class));

View File

@ -1,17 +1,16 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.Filename;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.exceptions.SyntaxException;
import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.json.JSON;
public class JSONLib {
@Native("JSON") public class JSONLib {
@Native
public static Object parse(Context ctx, String val) {
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); }
}

View File

@ -12,7 +12,7 @@ import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeGetter;
public class MapLib {
@Native("Map") public class MapLib {
private LinkedHashMap<Object, Object> map = new LinkedHashMap<>();
@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) {
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) {

View File

@ -2,7 +2,7 @@ package me.topchetoeu.jscript.lib;
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 PI = Math.PI;
@Native public static final double SQRT2 = Math.sqrt(2);

View File

@ -9,7 +9,7 @@ import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeConstructor;
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 MAX_SAFE_INTEGER = 9007199254740991.;
@Native public static final double MIN_SAFE_INTEGER = -MAX_SAFE_INTEGER;

View File

@ -13,7 +13,7 @@ import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeConstructor;
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) {
for (var obj : src) {
for (var key : Values.getMembers(ctx, obj, true, true)) {

View File

@ -18,7 +18,7 @@ import me.topchetoeu.jscript.interop.InitType;
import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeInit;
public class PromiseLib {
@Native("Promise") public class PromiseLib {
private static class Handle {
public final Context ctx;
public final FunctionValue fulfilled;

View File

@ -4,10 +4,11 @@ import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Environment;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.interop.InitType;
import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeConstructor;
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) {
var target = ErrorLib.constructor(ctx, thisArg, message);
target.defineProperty(ctx, "name", "RangeError");

View File

@ -6,13 +6,14 @@ import java.util.regex.Pattern;
import me.topchetoeu.jscript.engine.Context;
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.ObjectValue;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeGetter;
public class RegExpLib {
@Native("RegExp") public class RegExpLib {
// I used Regex to analyze Regex
private static final Pattern NAMED_PATTERN = Pattern.compile("\\(\\?<([^=!].*?)>", Pattern.DOTALL);
private static final Pattern ESCAPE_PATTERN = Pattern.compile("[/\\-\\\\^$*+?.()|\\[\\]{}]");
@ -81,7 +82,6 @@ public class RegExpLib {
return res;
}
@Native public Object exec(String str) {
var matcher = pattern.matcher(str);
if (lastI > str.length() || !matcher.find(lastI) || sticky && matcher.start() != lastI) {
@ -133,7 +133,7 @@ public class RegExpLib {
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) {
var res = new ArrayValue();
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");
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");
Object match;
int lastEnd = 0;
@ -214,29 +214,41 @@ public class RegExpLib {
}
return res;
}
// [Symbol.replace](target, replacement) {
// const pattern = new this.constructor(this, this.flags + "d") as RegExp;
// let match: RegExpResult | null;
// let lastEnd = 0;
// const res: string[] = [];
// // log(pattern.toString());
// while ((match = pattern.exec(target)) !== null) {
// const indices = match.indices![0];
// res.push(target.substring(lastEnd, indices[0]));
// if (replacement instanceof Function) {
// res.push(replacement(target.substring(indices[0], indices[1]), ...match.slice(1), indices[0], target));
// }
// else {
// res.push(replacement);
// }
// lastEnd = indices[1];
// if (!pattern.global) break;
// }
// if (lastEnd < target.length) {
// res.push(target.substring(lastEnd));
// }
// return res.join('');
// },
@Native("@@Symbol.replace") public String replace(Context ctx, String target, Object replacement) {
var pattern = new RegExpLib(this.source, this.flags() + "d");
Object match;
var lastEnd = 0;
var res = new StringBuilder();
while ((match = pattern.exec(target)) != Values.NULL) {
var indices = (ArrayValue)((ArrayValue)Values.getMember(ctx, match, "indices")).get(0);
var arrMatch = (ArrayValue)match;
var start = ((Number)indices.get(0)).intValue();
var end = ((Number)indices.get(1)).intValue();
res.append(target.substring(lastEnd, start));
if (replacement instanceof FunctionValue) {
var args = new Object[arrMatch.size() + 2];
args[0] = target.substring(start, end);
arrMatch.copyTo(args, 1, 1, arrMatch.size() - 1);
args[args.length - 2] = start;
args[args.length - 1] = target;
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) {
// const pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp;
// if (!reverse) {
@ -258,6 +270,7 @@ public class RegExpLib {
// else return -1;
// }
// },
@Native public RegExpLib(Context ctx, Object pattern, Object 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("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);
var matcher = NAMED_PATTERN.matcher(source);

View File

@ -12,7 +12,7 @@ import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeGetter;
public class SetLib {
@Native("Set") public class SetLib {
private LinkedHashSet<Object> set = new LinkedHashSet<>();
@Native("@@Symbol.typeName") public final String name = "Set";

View File

@ -16,7 +16,7 @@ import me.topchetoeu.jscript.interop.NativeGetter;
import me.topchetoeu.jscript.interop.NativeInit;
// TODO: implement index wrapping properly
public class StringLib {
@Native("String") public class StringLib {
public final String value;
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;
}
@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);
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);
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) {

View File

@ -15,7 +15,7 @@ import me.topchetoeu.jscript.interop.NativeConstructor;
import me.topchetoeu.jscript.interop.NativeGetter;
import me.topchetoeu.jscript.interop.NativeInit;
public class SymbolLib {
@Native("Symbol") public class SymbolLib {
private static final Map<String, Symbol> symbols = new HashMap<>();
@NativeGetter public static Symbol typeName(Context ctx) { return ctx.environment().symbol("Symbol.typeName"); }

View File

@ -4,10 +4,11 @@ import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Environment;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.interop.InitType;
import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeConstructor;
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) {
var target = ErrorLib.constructor(ctx, thisArg, message);
target.defineProperty(ctx, "name", "SyntaxError");

View File

@ -4,10 +4,11 @@ import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Environment;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.interop.InitType;
import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeConstructor;
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) {
var target = ErrorLib.constructor(ctx, thisArg, message);
target.defineProperty(ctx, "name", "TypeError");

View File

@ -82,8 +82,6 @@ public class Parsing {
// Although ES5 allow these, we will comply to ES6 here
reserved.add("const");
reserved.add("let");
reserved.add("async");
reserved.add("super");
// These are allowed too, however our parser considers them keywords
reserved.add("undefined");
reserved.add("arguments");
@ -259,7 +257,7 @@ public class Parsing {
}
break;
case CURR_LITERAL:
if (isAlphanumeric(c) || c == '_') {
if (isAlphanumeric(c) || c == '_' || c == '$') {
currToken.append(c);
continue;
}
@ -1060,7 +1058,6 @@ public class Parsing {
if (!checkVarName(literal.result)) {
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("let")) return ParseRes.error(loc, "'let' declarations are not supported.");
return ParseRes.error(loc, String.format("Unexpected identifier '%s'.", literal.result));
@ -1137,7 +1134,9 @@ public class Parsing {
var op = opRes.result;
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);
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);
n += valRes.n;
if (valRes.isError())
return ParseRes.error(loc, "Expected a return value.", valRes);
if (valRes.isError()) return ParseRes.error(loc, "Expected a return value.", valRes);
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);
n += condRes.n;
if (!isOperator(tokens, i + n++, Operator.PAREN_CLOSE))
return ParseRes.error(loc, "Expected a closing paren after if condition.");
if (!isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after if condition.");
var res = parseStatement(filename, tokens, i + n);
if (!res.isSuccess()) return ParseRes.error(loc, "Expected an if body.", res);
@ -1702,8 +1699,7 @@ public class Parsing {
parseVariableDeclare(filename, tokens, i + n),
parseValueStatement(filename, tokens, i + n)
);
if (!declRes.isSuccess())
return ParseRes.error(loc, "Expected a declaration or an expression.", declRes);
if (!declRes.isSuccess()) return ParseRes.error(loc, "Expected a declaration or an expression.", declRes);
n += declRes.n;
decl = declRes.result;
}

View 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; })

View File

@ -0,0 +1,2 @@
return new UnitTest('Arithmetics')
.add(include('counters.js'))

4
tests/array/concat.js Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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.');