ES6 Support Groundwork + Fixes #26

Merged
TopchetoEU merged 49 commits from ES6 into master 2024-09-05 14:26:07 +00:00
64 changed files with 1943 additions and 1241 deletions
Showing only changes of commit 3475e3a130 - Show all commits

View File

@ -9,7 +9,7 @@ import me.topchetoeu.jscript.compilation.parsing.Operator;
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.compilation.parsing.Token;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
@ -32,7 +32,7 @@ public class JSON {
if (val.isNull()) return Values.NULL;
return null;
}
private static JSONElement fromJs(Extensions ext, Object val, HashSet<Object> prev) {
private static JSONElement fromJs(Environment ext, Object val, HashSet<Object> prev) {
if (val instanceof Boolean) return JSONElement.bool((boolean)val);
if (val instanceof Number) return JSONElement.number(((Number)val).doubleValue());
if (val instanceof String) return JSONElement.string((String)val);
@ -70,7 +70,7 @@ public class JSON {
if (val == null) return null;
return null;
}
public static JSONElement fromJs(Extensions ext, Object val) {
public static JSONElement fromJs(Environment ext, Object val) {
return fromJs(ext, val, new HashSet<>());
}

View File

@ -699,7 +699,7 @@ public class Parsing {
var values = new ArrayList<Statement>();
// Java allows labels, so me uses labels
// Java allows labels, so labels were used
loop: while (true) {
if (isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) {
n++;

View File

@ -39,7 +39,7 @@ public class ArrayLib {
return __iterator(args);
}
@Expose public static ObjectValue __keys(Arguments args) {
return Values.toJSIterator(args.ctx, () -> new Iterator<Object>() {
return Values.toJSIterator(args.env, () -> new Iterator<Object>() {
private int i = 0;
@Override
@ -54,7 +54,7 @@ public class ArrayLib {
});
}
@Expose public static ObjectValue __entries(Arguments args) {
return Values.toJSIterator(args.ctx, () -> new Iterator<Object>() {
return Values.toJSIterator(args.env, () -> new Iterator<Object>() {
private int i = 0;
@Override
@ -64,18 +64,18 @@ public class ArrayLib {
@Override
public Object next() {
if (!hasNext()) return null;
return new ArrayValue(args.ctx, i, args.self(ArrayValue.class).get(i++));
return new ArrayValue(args.env, i, args.self(ArrayValue.class).get(i++));
}
});
}
@Expose(value = "@@Symbol.iterator")
public static ObjectValue __iterator(Arguments args) {
return Values.toJSIterator(args.ctx, args.self(ArrayValue.class));
return Values.toJSIterator(args.env, args.self(ArrayValue.class));
}
@Expose(value = "@@Symbol.asyncIterator")
public static ObjectValue __asyncIterator(Arguments args) {
return Values.toJSAsyncIterator(args.ctx, args.self(ArrayValue.class).iterator());
return Values.toJSAsyncIterator(args.env, args.self(ArrayValue.class).iterator());
}
@Expose public static ArrayValue __concat(Arguments args) {
@ -98,7 +98,7 @@ public class ArrayLib {
j += n;
}
else {
res.set(args.ctx, j++, arrs.get(i));
res.set(args.env, j++, arrs.get(i));
}
}
@ -113,7 +113,7 @@ public class ArrayLib {
});
arr.sort((a, b) -> {
var res = Values.toNumber(args.ctx, (cmp == null ? defaultCmp : cmp).call(args.ctx, null, a, b));
var res = Values.toNumber(args.env, (cmp == null ? defaultCmp : cmp).call(args.env, null, a, b));
if (res < 0) return -1;
if (res > 0) return 1;
return 0;
@ -127,7 +127,7 @@ public class ArrayLib {
var start = normalizeI(arr.size(), args.getInt(1, 0), true);
var end = normalizeI(arr.size(), args.getInt(2, arr.size()), true);
for (; start < end; start++) arr.set(args.ctx, start, val);
for (; start < end; start++) arr.set(args.env, start, val);
return arr;
}
@ -136,7 +136,7 @@ public class ArrayLib {
for (var i = 0; i < arr.size(); i++) {
if (arr.has(i) && !Values.toBoolean(Values.call(
args.ctx, args.get(0), args.get(1),
args.env, args.get(0), args.get(1),
arr.get(i), i, arr
))) return false;
}
@ -148,7 +148,7 @@ public class ArrayLib {
for (var i = 0; i < arr.size(); i++) {
if (arr.has(i) && Values.toBoolean(Values.call(
args.ctx, args.get(0), args.get(1),
args.env, args.get(0), args.get(1),
arr.get(i), i, arr
))) return true;
}
@ -161,9 +161,9 @@ public class ArrayLib {
for (int i = 0, j = 0; i < arr.size(); i++) {
if (arr.has(i) && Values.toBoolean(Values.call(
args.ctx, args.get(0), args.get(1),
args.env, args.get(0), args.get(1),
arr.get(i), i, arr
))) res.set(args.ctx, j++, arr.get(i));
))) res.set(args.env, j++, arr.get(i));
}
return res;
@ -174,7 +174,7 @@ public class ArrayLib {
res.setSize(arr.size());
for (int i = 0; i < arr.size(); i++) {
if (arr.has(i)) res.set(args.ctx, i, Values.call(args.ctx, args.get(0), args.get(1), arr.get(i), i, arr));
if (arr.has(i)) res.set(args.env, i, Values.call(args.env, args.get(0), args.get(1), arr.get(i), i, arr));
}
return res;
}
@ -184,7 +184,7 @@ public class ArrayLib {
var thisArg = args.get(1);
for (int i = 0; i < arr.size(); i++) {
if (arr.has(i)) func.call(args.ctx, thisArg, arr.get(i), i, arr);
if (arr.has(i)) func.call(args.env, thisArg, arr.get(i), i, arr);
}
}
@ -205,7 +205,7 @@ public class ArrayLib {
for (; i < arr.size(); i++) {
if (arr.has(i)) {
res = func.call(args.ctx, null, res, arr.get(i), i, arr);
res = func.call(args.env, null, res, arr.get(i), i, arr);
}
}
@ -226,7 +226,7 @@ public class ArrayLib {
for (; i >= 0; i--) {
if (arr.has(i)) {
res = func.call(args.ctx, null, res, arr.get(i), i, arr);
res = func.call(args.env, null, res, arr.get(i), i, arr);
}
}
@ -255,13 +255,13 @@ public class ArrayLib {
depths.push(d + 1);
}
}
else res.set(args.ctx, res.size(), el);
else res.set(args.env, res.size(), el);
}
return res;
}
@Expose public static ArrayValue __flatMap(Arguments args) {
return __flat(new Arguments(args.ctx, __map(args), 1));
return __flat(new Arguments(args.env, __map(args), 1));
}
@Expose public static Object __find(Arguments args) {
@ -269,7 +269,7 @@ public class ArrayLib {
for (int i = 0; i < arr.size(); i++) {
if (arr.has(i) && Values.toBoolean(Values.call(
args.ctx, args.get(0), args.get(1),
args.env, args.get(0), args.get(1),
arr.get(i), i, args.self
))) return arr.get(i);
}
@ -281,7 +281,7 @@ public class ArrayLib {
for (var i = arr.size() - 1; i >= 0; i--) {
if (arr.has(i) && Values.toBoolean(Values.call(
args.ctx, args.get(0), args.get(1),
args.env, args.get(0), args.get(1),
arr.get(i), i, args.self
))) return arr.get(i);
}
@ -294,7 +294,7 @@ public class ArrayLib {
for (int i = 0; i < arr.size(); i++) {
if (arr.has(i) && Values.toBoolean(Values.call(
args.ctx, args.get(0), args.get(1),
args.env, args.get(0), args.get(1),
arr.get(i), i, args.self
))) return i;
}
@ -306,7 +306,7 @@ public class ArrayLib {
for (var i = arr.size() - 1; i >= 0; i--) {
if (arr.has(i) && Values.toBoolean(Values.call(
args.ctx, args.get(0), args.get(1),
args.env, args.get(0), args.get(1),
arr.get(i), i, args.self
))) return i;
}
@ -320,7 +320,7 @@ public class ArrayLib {
var start = normalizeI(arr.size(), args.getInt(1), true);
for (int i = start; i < arr.size(); i++) {
if (Values.strictEquals(args.ctx, arr.get(i), val)) return i;
if (Values.strictEquals(args.env, arr.get(i), val)) return i;
}
return -1;
@ -331,7 +331,7 @@ public class ArrayLib {
var start = normalizeI(arr.size(), args.getInt(1), true);
for (int i = arr.size(); i >= start; i--) {
if (Values.strictEquals(args.ctx, arr.get(i), val)) return i;
if (Values.strictEquals(args.env, arr.get(i), val)) return i;
}
return -1;
@ -353,7 +353,7 @@ public class ArrayLib {
var arr = args.self(ArrayValue.class);
var values = args.args;
arr.copyFrom(args.ctx, values, 0, arr.size(), values.length);
arr.copyFrom(args.env, values, 0, arr.size(), values.length);
return arr.size();
}
@ -372,7 +372,7 @@ public class ArrayLib {
var values = args.slice(0).args;
arr.move(0, values.length, arr.size());
arr.copyFrom(args.ctx, values, 0, 0, values.length);
arr.copyFrom(args.env, values, 0, 0, values.length);
return arr.size();
}
@ -398,13 +398,13 @@ public class ArrayLib {
var res = new ArrayValue(deleteCount);
arr.copyTo(res, start, 0, deleteCount);
arr.move(start + deleteCount, start + items.length, arr.size() - start - deleteCount);
arr.copyFrom(args.ctx, items, 0, start, items.length);
arr.copyFrom(args.env, items, 0, start, items.length);
arr.setSize(size);
return res;
}
@Expose public static String __toString(Arguments args) {
return __join(new Arguments(args.ctx, args.self, ","));
return __join(new Arguments(args.env, args.self, ","));
}
@Expose public static String __join(Arguments args) {
@ -422,7 +422,7 @@ public class ArrayLib {
var el = arr.get(i);
if (el == null || el == Values.NULL) continue;
res.append(Values.toString(args.ctx, el));
res.append(Values.toString(args.env, el));
}
return res.toString();
@ -434,7 +434,7 @@ public class ArrayLib {
}
@Expose(target = ExposeTarget.STATIC)
public static ArrayValue __of(Arguments args) {
return new ArrayValue(args.ctx, args.slice(0).args);
return new ArrayValue(args.env, args.slice(0).args);
}
@ExposeConstructor public static ArrayValue __constructor(Arguments args) {
@ -448,7 +448,7 @@ public class ArrayLib {
else {
var val = args.args;
res = new ArrayValue(val.length);
res.copyFrom(args.ctx, val, 0, 0, val.length);
res.copyFrom(args.env, val, 0, 0, val.length);
}
return res;

View File

@ -1,9 +1,8 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.lib.PromiseLib.Handle;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.Frame;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.CodeFunction;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
@ -22,40 +21,43 @@ public class AsyncFunctionLib extends FunctionValue {
private boolean awaiting = false;
private void next(Context ctx, Object inducedValue, EngineException inducedError) {
private void next(Environment env, Object inducedValue, EngineException inducedError) {
Object res = null;
frame.onPush();
awaiting = false;
while (!awaiting) {
try {
res = frame.next(inducedValue, Values.NO_RETURN, inducedError);
if (inducedValue != Values.NO_RETURN) res = frame.next(inducedValue);
else if (inducedError != null) res = frame.induceError(inducedError);
else res = frame.next();
inducedValue = Values.NO_RETURN;
inducedError = null;
if (res != Values.NO_RETURN) {
promise.fulfill(ctx, res);
promise.fulfill(env, res);
break;
}
}
catch (EngineException e) {
promise.reject(ctx, e);
promise.reject(env, e);
break;
}
}
frame.onPop();
if (awaiting) {
PromiseLib.handle(ctx, frame.pop(), new Handle() {
PromiseLib.handle(env, frame.pop(), new Handle() {
@Override
public void onFulfil(Object val) {
next(ctx, val, null);
next(env, val, null);
}
@Override
public void onReject(EngineException err) {
next(ctx, Values.NO_RETURN, err);
next(env, Values.NO_RETURN, err);
}
}.defer(ctx));
}.defer(env));
}
}
@ -66,16 +68,15 @@ public class AsyncFunctionLib extends FunctionValue {
}
@Override
public Object call(Extensions ext, Object thisArg, Object ...args) {
public Object call(Environment env, Object thisArg, Object ...args) {
var handler = new AsyncHelper();
var ctx = Context.of(ext);
var newArgs = new Object[args.length + 1];
newArgs[0] = new NativeFunction("await", handler::await);
System.arraycopy(args, 0, newArgs, 1, args.length);
handler.frame = new Frame(ctx, thisArg, newArgs, (CodeFunction)func);
handler.next(ctx, Values.NO_RETURN, null);
handler.frame = new Frame(env, thisArg, newArgs, (CodeFunction)func);
handler.next(env, Values.NO_RETURN, null);
return handler.promise;
}

View File

@ -1,8 +1,7 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.Frame;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.CodeFunction;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
@ -14,7 +13,7 @@ public class AsyncGeneratorFunctionLib extends FunctionValue {
public final CodeFunction func;
@Override
public Object call(Extensions ext, Object thisArg, Object ...args) {
public Object call(Environment ext, Object thisArg, Object ...args) {
var handler = new AsyncGeneratorLib();
var newArgs = new Object[args.length + 2];
@ -22,13 +21,13 @@ public class AsyncGeneratorFunctionLib extends FunctionValue {
newArgs[1] = new NativeFunction("yield", handler::yield);
System.arraycopy(args, 0, newArgs, 2, args.length);
handler.frame = new Frame(Context.of(ext), thisArg, newArgs, func);
handler.frame = new Frame(ext, thisArg, newArgs, func);
return handler;
}
public AsyncGeneratorFunctionLib(CodeFunction func) {
super(func.name, func.length);
if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function.");
if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a code function.");
this.func = func;
}
}

View File

@ -3,8 +3,8 @@ package me.topchetoeu.jscript.lib;
import java.util.Map;
import me.topchetoeu.jscript.lib.PromiseLib.Handle;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Frame;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Values;
@ -19,10 +19,10 @@ public class AsyncGeneratorLib {
private PromiseLib currPromise;
public Frame frame;
private void next(Context ctx, Object inducedValue, Object inducedReturn, EngineException inducedError) {
private void next(Environment env, Object inducedValue, Object inducedReturn, EngineException inducedError) {
if (done) {
if (inducedError != null) throw inducedError;
currPromise.fulfill(ctx, new ObjectValue(ctx, Map.of(
currPromise.fulfill(env, new ObjectValue(env, Map.of(
"done", true,
"value", inducedReturn == Values.NO_RETURN ? null : inducedReturn
)));
@ -35,40 +35,44 @@ public class AsyncGeneratorLib {
frame.onPush();
while (state == 0) {
try {
res = frame.next(inducedValue, inducedReturn, inducedError);
if (inducedValue != Values.NO_RETURN) res = frame.next(inducedValue);
else if (inducedReturn != Values.NO_RETURN) res = frame.induceReturn(inducedValue);
else if (inducedError != null) res = frame.induceError(inducedError);
else res = frame.next();
inducedValue = inducedReturn = Values.NO_RETURN;
inducedError = null;
if (res != Values.NO_RETURN) {
var obj = new ObjectValue();
obj.defineProperty(ctx, "done", true);
obj.defineProperty(ctx, "value", res);
currPromise.fulfill(ctx, obj);
obj.defineProperty(env, "done", true);
obj.defineProperty(env, "value", res);
currPromise.fulfill(env, obj);
break;
}
}
catch (EngineException e) {
currPromise.reject(ctx, e);
currPromise.reject(env, e);
break;
}
}
frame.onPop();
if (state == 1) {
PromiseLib.handle(ctx, frame.pop(), new Handle() {
PromiseLib.handle(env, frame.pop(), new Handle() {
@Override public void onFulfil(Object val) {
next(ctx, val, Values.NO_RETURN, null);
next(env, val, Values.NO_RETURN, null);
}
@Override public void onReject(EngineException err) {
next(ctx, Values.NO_RETURN, Values.NO_RETURN, err);
next(env, Values.NO_RETURN, Values.NO_RETURN, err);
}
}.defer(ctx));
}.defer(env));
}
else if (state == 2) {
var obj = new ObjectValue();
obj.defineProperty(ctx, "done", false);
obj.defineProperty(ctx, "value", frame.pop());
currPromise.fulfill(ctx, obj);
obj.defineProperty(env, "done", false);
obj.defineProperty(env, "value", frame.pop());
currPromise.fulfill(env, obj);
}
}
@ -90,18 +94,18 @@ public class AsyncGeneratorLib {
@Expose public PromiseLib __next(Arguments args) {
this.currPromise = new PromiseLib();
if (args.has(0)) next(args.ctx, args.get(0), Values.NO_RETURN, null);
else next(args.ctx, Values.NO_RETURN, Values.NO_RETURN, null);
if (args.has(0)) next(args.env, args.get(0), Values.NO_RETURN, null);
else next(args.env, Values.NO_RETURN, Values.NO_RETURN, null);
return this.currPromise;
}
@Expose public PromiseLib __return(Arguments args) {
this.currPromise = new PromiseLib();
next(args.ctx, Values.NO_RETURN, args.get(0), null);
next(args.env, Values.NO_RETURN, args.get(0), null);
return this.currPromise;
}
@Expose public PromiseLib __throw(Arguments args) {
this.currPromise = new PromiseLib();
next(args.ctx, Values.NO_RETURN, Values.NO_RETURN, new EngineException(args.get(0)).setExtensions(args.ctx));
next(args.env, Values.NO_RETURN, Values.NO_RETURN, new EngineException(args.get(0)).setEnvironment(args.env));
return this.currPromise;
}
}

View File

@ -24,7 +24,7 @@ public class ConsoleLib {
for (var el : args.args) {
if (!first) res.append(" ");
first = false;
res.append(Values.toReadable(args.ctx, el).getBytes());
res.append(Values.toReadable(args.env, el).getBytes());
}
for (var line : res.toString().split("\n", -1)) {

View File

@ -66,7 +66,7 @@ public class EncodingLib {
public static String __decode(Arguments args) {
var raw = args.convert(0, ArrayList.class);
var res = new byte[raw.size()];
for (var i = 0; i < raw.size(); i++) res[i] = (byte)Values.toNumber(args.ctx, raw.get(i));
for (var i = 0; i < raw.size(); i++) res[i] = (byte)Values.toNumber(args.env, raw.get(i));
return new String(res);
}

View File

@ -1,6 +1,6 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.ConvertException;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Values;
@ -13,7 +13,7 @@ import me.topchetoeu.jscript.utils.interop.WrapperName;
@WrapperName("Error")
public class ErrorLib {
private static String toString(Context ctx, Object name, Object message) {
private static String toString(Environment ctx, Object name, Object message) {
if (name == null) name = "";
else name = Values.toString(ctx, name).trim();
if (message == null) message = "";
@ -30,9 +30,9 @@ public class ErrorLib {
@ExposeField public static final String __name = "Error";
@Expose public static String __toString(Arguments args) {
if (args.self instanceof ObjectValue) return toString(args.ctx,
Values.getMember(args.ctx, args.self, "name"),
Values.getMember(args.ctx, args.self, "message")
if (args.self instanceof ObjectValue) return toString(args.env,
Values.getMember(args.env, args.self, "name"),
Values.getMember(args.env, args.self, "message")
);
else return "[Invalid error]";
}
@ -47,7 +47,7 @@ public class ErrorLib {
catch (ConvertException e) {}
target.setPrototype(PlaceholderProto.ERROR);
target.defineProperty(args.ctx, "message", Values.toString(args.ctx, message));
target.defineProperty(args.env, "message", Values.toString(args.env, message));
return target;
}

View File

@ -13,7 +13,7 @@ public class FileLib {
public final File fd;
@Expose public PromiseLib __pointer(Arguments args) {
return PromiseLib.await(args.ctx, () -> {
return PromiseLib.await(args.env, () -> {
try {
return fd.seek(0, 1);
}
@ -21,7 +21,7 @@ public class FileLib {
});
}
@Expose public PromiseLib __length(Arguments args) {
return PromiseLib.await(args.ctx, () -> {
return PromiseLib.await(args.env, () -> {
try {
long curr = fd.seek(0, 1);
long res = fd.seek(0, 2);
@ -33,26 +33,26 @@ public class FileLib {
}
@Expose public PromiseLib __read(Arguments args) {
return PromiseLib.await(args.ctx, () -> {
return PromiseLib.await(args.env, () -> {
var n = args.getInt(0);
try {
var buff = new byte[n];
var res = new ArrayValue();
int resI = fd.read(buff);
for (var i = resI - 1; i >= 0; i--) res.set(args.ctx, i, (int)buff[i]);
for (var i = resI - 1; i >= 0; i--) res.set(args.env, i, (int)buff[i]);
return res;
}
catch (FilesystemException e) { throw e.toEngineException(); }
});
}
@Expose public PromiseLib __write(Arguments args) {
return PromiseLib.await(args.ctx, () -> {
return PromiseLib.await(args.env, () -> {
var val = args.convert(0, ArrayValue.class);
try {
var res = new byte[val.size()];
for (var i = 0; i < val.size(); i++) res[i] = (byte)Values.toNumber(args.ctx, val.get(i));
for (var i = 0; i < val.size(); i++) res[i] = (byte)Values.toNumber(args.env, val.get(i));
fd.write(res);
return null;
@ -61,13 +61,13 @@ public class FileLib {
});
}
@Expose public PromiseLib __close(Arguments args) {
return PromiseLib.await(args.ctx, () -> {
return PromiseLib.await(args.env, () -> {
fd.close();
return null;
});
}
@Expose public PromiseLib __seek(Arguments args) {
return PromiseLib.await(args.ctx, () -> {
return PromiseLib.await(args.env, () -> {
var ptr = args.getLong(0);
var whence = args.getInt(1);

View File

@ -4,7 +4,7 @@ import java.io.IOException;
import java.util.Iterator;
import java.util.Stack;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Values;
@ -31,21 +31,21 @@ public class FilesystemLib {
@ExposeField(target = ExposeTarget.STATIC)
public static final int __SEEK_END = 2;
private static Filesystem fs(Context ctx) {
var fs = Filesystem.get(ctx);
private static Filesystem fs(Environment env) {
var fs = Filesystem.get(env);
if (fs != null) return fs;
throw EngineException.ofError("Current environment doesn't have a file system.");
}
@Expose(target = ExposeTarget.STATIC)
public static String __normalize(Arguments args) {
return fs(args.ctx).normalize(args.convert(String.class));
return fs(args.env).normalize(args.convert(String.class));
}
@Expose(target = ExposeTarget.STATIC)
public static PromiseLib __open(Arguments args) {
return PromiseLib.await(args.ctx, () -> {
var fs = fs(args.ctx);
return PromiseLib.await(args.env, () -> {
var fs = fs(args.env);
var path = fs.normalize(args.getString(0));
var _mode = Mode.parse(args.getString(1));
@ -62,7 +62,7 @@ public class FilesystemLib {
@Expose(target = ExposeTarget.STATIC)
public static ObjectValue __ls(Arguments args) {
return Values.toJSAsyncIterator(args.ctx, new Iterator<>() {
return Values.toJSAsyncIterator(args.env, new Iterator<>() {
private boolean failed, done;
private File file;
private String nextLine;
@ -71,7 +71,7 @@ public class FilesystemLib {
if (done) return;
if (!failed) {
if (file == null) {
var fs = fs(args.ctx);
var fs = fs(args.env);
var path = fs.normalize(args.getString(0));
if (fs.stat(path).type != EntryType.FOLDER) {
@ -117,9 +117,9 @@ public class FilesystemLib {
}
@Expose(target = ExposeTarget.STATIC)
public static PromiseLib __mkdir(Arguments args) throws IOException {
return PromiseLib.await(args.ctx, () -> {
return PromiseLib.await(args.env, () -> {
try {
fs(args.ctx).create(args.getString(0), EntryType.FOLDER);
fs(args.env).create(args.getString(0), EntryType.FOLDER);
return null;
}
catch (FilesystemException e) { throw e.toEngineException(); }
@ -128,9 +128,9 @@ public class FilesystemLib {
}
@Expose(target = ExposeTarget.STATIC)
public static PromiseLib __mkfile(Arguments args) throws IOException {
return PromiseLib.await(args.ctx, () -> {
return PromiseLib.await(args.env, () -> {
try {
fs(args.ctx).create(args.getString(0), EntryType.FILE);
fs(args.env).create(args.getString(0), EntryType.FILE);
return null;
}
catch (FilesystemException e) { throw e.toEngineException(); }
@ -138,9 +138,9 @@ public class FilesystemLib {
}
@Expose(target = ExposeTarget.STATIC)
public static PromiseLib __rm(Arguments args) throws IOException {
return PromiseLib.await(args.ctx, () -> {
return PromiseLib.await(args.env, () -> {
try {
var fs = fs(args.ctx);
var fs = fs(args.env);
var path = fs.normalize(args.getString(0));
var recursive = args.getBoolean(1);
@ -169,15 +169,15 @@ public class FilesystemLib {
}
@Expose(target = ExposeTarget.STATIC)
public static PromiseLib __stat(Arguments args) throws IOException {
return PromiseLib.await(args.ctx, () -> {
return PromiseLib.await(args.env, () -> {
try {
var fs = fs(args.ctx);
var fs = fs(args.env);
var path = fs.normalize(args.getString(0));
var stat = fs.stat(path);
var res = new ObjectValue();
res.defineProperty(args.ctx, "type", stat.type.name);
res.defineProperty(args.ctx, "mode", stat.mode.name);
res.defineProperty(args.env, "type", stat.type.name);
res.defineProperty(args.env, "mode", stat.mode.name);
return res;
}
catch (FilesystemException e) { throw e.toEngineException(); }
@ -185,8 +185,8 @@ public class FilesystemLib {
}
@Expose(target = ExposeTarget.STATIC)
public static PromiseLib __exists(Arguments args) throws IOException {
return PromiseLib.await(args.ctx, () -> {
try { fs(args.ctx).stat(args.getString(0)); return true; }
return PromiseLib.await(args.env, () -> {
try { fs(args.env).stat(args.getString(0)); return true; }
catch (FilesystemException e) { return false; }
});
}

View File

@ -2,7 +2,6 @@ package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.runtime.Compiler;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.scope.ValueVariable;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.runtime.values.CodeFunction;
@ -20,10 +19,10 @@ public class FunctionLib {
private static int i;
@Expose public static Object __apply(Arguments args) {
return args.self(FunctionValue.class).call(args.ctx, args.get(0), args.convert(1, ArrayValue.class).toArray());
return args.self(FunctionValue.class).call(args.env, args.get(0), args.convert(1, ArrayValue.class).toArray());
}
@Expose public static Object __call(Arguments args) {
return args.self(FunctionValue.class).call(args.ctx, args.get(0), args.slice(1).args);
return args.self(FunctionValue.class).call(args.env, args.get(0), args.slice(1).args);
}
@Expose public static FunctionValue __bind(Arguments args) {
var self = args.self(FunctionValue.class);
@ -40,7 +39,7 @@ public class FunctionLib {
System.arraycopy(callArgs.args, 0, resArgs, bindArgs.length, callArgs.n());
}
return self.call(callArgs.ctx, thisArg, resArgs);
return self.call(callArgs.env, thisArg, resArgs);
});
}
@Expose public static String __toString(Arguments args) {
@ -62,8 +61,6 @@ public class FunctionLib {
@ExposeConstructor
public static Object __constructor(Arguments args) {
var compiler = Compiler.get(args);
var parts = args.convert(String.class);
if (parts.length == 0) parts = new String[] { "" };
@ -76,8 +73,8 @@ public class FunctionLib {
src += "){" + parts[parts.length - 1] + "}";
var body = compiler.compile(new Filename("jscript", "func/" + i++), src);
var func = new CodeFunction(Context.clean(args.ctx), "", body, new ValueVariable[0]);
return Values.call(args, func, null);
var body = Compiler.get(args.env).compile(new Filename("jscript", "func/" + i++), src);
var func = new CodeFunction(args.env, "", body, new ValueVariable[0]);
return Values.call(args.env, func, null);
}
}

View File

@ -1,8 +1,7 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.Frame;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.CodeFunction;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
@ -13,14 +12,14 @@ import me.topchetoeu.jscript.utils.interop.WrapperName;
public class GeneratorFunctionLib extends FunctionValue {
public final CodeFunction func;
@Override public Object call(Extensions ext, Object thisArg, Object ...args) {
@Override public Object call(Environment env, Object thisArg, Object ...args) {
var handler = new GeneratorLib();
var newArgs = new Object[args.length + 1];
newArgs[0] = new NativeFunction("yield", handler::yield);
System.arraycopy(args, 0, newArgs, 1, args.length);
handler.frame = new Frame(Context.of(ext), thisArg, newArgs, func);
handler.frame = new Frame(env, thisArg, newArgs, func);
return handler;
}

View File

@ -1,7 +1,7 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Frame;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Values;
@ -15,12 +15,12 @@ public class GeneratorLib {
private boolean done = false;
public Frame frame;
private ObjectValue next(Context ctx, Object inducedValue, Object inducedReturn, EngineException inducedError) {
private ObjectValue next(Environment env, Object inducedValue, Object inducedReturn, EngineException inducedError) {
if (done) {
if (inducedError != Values.NO_RETURN) throw inducedError;
var res = new ObjectValue();
res.defineProperty(ctx, "done", true);
res.defineProperty(ctx, "value", inducedReturn == Values.NO_RETURN ? null : inducedReturn);
res.defineProperty(env, "done", true);
res.defineProperty(env, "value", inducedReturn == Values.NO_RETURN ? null : inducedReturn);
return res;
}
@ -30,7 +30,11 @@ public class GeneratorLib {
frame.onPush();
while (!yielding) {
try {
res = frame.next(inducedValue, inducedReturn, inducedError);
if (inducedValue != Values.NO_RETURN) res = frame.next(inducedValue);
else if (inducedReturn != Values.NO_RETURN) res = frame.induceReturn(inducedValue);
else if (inducedError != null) res = frame.induceError(inducedError);
else res = frame.next();
inducedReturn = Values.NO_RETURN;
inducedError = null;
if (res != Values.NO_RETURN) {
@ -49,20 +53,20 @@ public class GeneratorLib {
else res = frame.pop();
var obj = new ObjectValue();
obj.defineProperty(ctx, "done", done);
obj.defineProperty(ctx, "value", res);
obj.defineProperty(env, "done", done);
obj.defineProperty(env, "value", res);
return obj;
}
@Expose public ObjectValue __next(Arguments args) {
if (args.n() == 0) return next(args.ctx, Values.NO_RETURN, Values.NO_RETURN, null);
else return next(args.ctx, args.get(0), Values.NO_RETURN, null);
if (args.n() == 0) return next(args.env, Values.NO_RETURN, Values.NO_RETURN, null);
else return next(args.env, args.get(0), Values.NO_RETURN, null);
}
@Expose public ObjectValue __throw(Arguments args) {
return next(args.ctx, Values.NO_RETURN, Values.NO_RETURN, new EngineException(args.get(0)).setExtensions(args.ctx));
return next(args.env, Values.NO_RETURN, Values.NO_RETURN, new EngineException(args.get(0)).setEnvironment(args.env));
}
@Expose public ObjectValue __return(Arguments args) {
return next(args.ctx, Values.NO_RETURN, args.get(0), null);
return next(args.env, Values.NO_RETURN, args.get(0), null);
}
@Override public String toString() {

View File

@ -2,10 +2,9 @@ package me.topchetoeu.jscript.lib;
import java.util.HashMap;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Environment;
import me.topchetoeu.jscript.runtime.EventLoop;
import me.topchetoeu.jscript.runtime.Key;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.environment.Key;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.scope.GlobalScope;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
@ -26,11 +25,11 @@ public class Internals {
@Expose(target = ExposeTarget.STATIC)
public static Object __require(Arguments args) {
var repo = ModuleRepo.get(args.ctx);
var repo = ModuleRepo.get(args.env);
if (repo != null) {
var res = repo.getModule(args.ctx, ModuleRepo.cwd(args.ctx), args.getString(0));
res.load(args.ctx);
var res = repo.getModule(args.env, ModuleRepo.cwd(args.env), args.getString(0));
res.load(args.env);
return res.value();
}
@ -43,7 +42,7 @@ public class Internals {
var delay = args.getDouble(1);
var arguments = args.slice(2).args;
if (!args.ctx.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop");
if (!args.env.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop");
var thread = new Thread(() -> {
var ms = (long)delay;
@ -52,13 +51,17 @@ public class Internals {
try { Thread.sleep(ms, ns); }
catch (InterruptedException e) { return; }
args.ctx.get(EventLoop.KEY).pushMsg(() -> func.call(new Context(args.ctx.extensions), null, arguments), false);
args.env.get(EventLoop.KEY).pushMsg(() -> func.call(args.env, null, arguments), false);
});
thread.start();
var i = args.ctx.init(I, 1);
args.ctx.add(I, i + 1);
args.ctx.init(THREADS, new HashMap<Integer, Thread>()).put(i, thread);
args.env.init(I, 1);
args.env.init(THREADS, new HashMap<>());
var i = args.env.get(I);
args.env.add(I, i + 1);
args.env.get(THREADS).put(i, thread);
return thread;
}
@ -68,7 +71,7 @@ public class Internals {
var delay = args.getDouble(1);
var arguments = args.slice(2).args;
if (!args.ctx.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop");
if (!args.env.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop");
var thread = new Thread(() -> {
var ms = (long)delay;
@ -80,13 +83,18 @@ public class Internals {
}
catch (InterruptedException e) { return; }
args.ctx.get(EventLoop.KEY).pushMsg(() -> func.call(new Context(args.ctx.extensions), null, arguments), false);
args.env.get(EventLoop.KEY).pushMsg(() -> func.call(args.env, null, arguments), false);
}
});
thread.start();
var i = args.ctx.init(I, 1);
args.ctx.add(I, i + 1);
args.ctx.init(THREADS, new HashMap<Integer, Thread>()).put(i, thread);
args.env.init(I, 1);
args.env.init(THREADS, new HashMap<>());
var i = args.env.get(I);
args.env.add(I, i + 1);
args.env.get(THREADS).put(i, thread);
return thread;
}
@ -94,7 +102,7 @@ public class Internals {
@Expose(target = ExposeTarget.STATIC)
public static void __clearTimeout(Arguments args) {
var i = args.getInt(0);
HashMap<Integer, Thread> map = args.ctx.get(THREADS);
HashMap<Integer, Thread> map = args.env.get(THREADS);
if (map == null) return;
var thread = map.get(i);
@ -132,15 +140,15 @@ public class Internals {
@Expose(target = ExposeTarget.STATIC, type = ExposeType.GETTER)
public static FileLib __stdin(Arguments args) {
return new FileLib(Filesystem.get(args.ctx).open("std://in", Mode.READ));
return new FileLib(Filesystem.get(args.env).open("std://in", Mode.READ));
}
@Expose(target = ExposeTarget.STATIC, type = ExposeType.GETTER)
public static FileLib __stdout(Arguments args) {
return new FileLib(Filesystem.get(args.ctx).open("std://out", Mode.READ));
return new FileLib(Filesystem.get(args.env).open("std://out", Mode.READ));
}
@Expose(target = ExposeTarget.STATIC, type = ExposeType.GETTER)
public static FileLib __stderr(Arguments args) {
return new FileLib(Filesystem.get(args.ctx).open("std://err", Mode.READ));
return new FileLib(Filesystem.get(args.env).open("std://err", Mode.READ));
}
@ExposeField(target = ExposeTarget.STATIC)
@ -211,12 +219,11 @@ public class Internals {
env.add(Environment.RANGE_ERR_PROTO, wp.getProto(RangeErrorLib.class));
env.add(Environment.REGEX_CONSTR, wp.getConstr(RegExpLib.class));
Values.setPrototype(new Context(), wp.getProto(ObjectLib.class), null);
Values.setPrototype(Environment.empty(), wp.getProto(ObjectLib.class), null);
env.add(NativeWrapperProvider.KEY, wp);
env.add(GlobalScope.KEY, glob);
return env;
}
}

View File

@ -19,6 +19,6 @@ public class JSONLib {
}
@Expose(target = ExposeTarget.STATIC)
public static String __stringify(Arguments args) {
return JSON.stringify(JSON.fromJs(args.ctx, args.get(0)));
return JSON.stringify(JSON.fromJs(args.env, args.get(0)));
}
}

View File

@ -4,7 +4,7 @@ import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.stream.Collectors;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Values;
@ -36,18 +36,18 @@ public class MapLib {
}
@Expose public ObjectValue __entries(Arguments args) {
return Values.toJSIterator(args.ctx, map
return Values.toJSIterator(args.env, map
.entrySet()
.stream()
.map(v -> new ArrayValue(args.ctx, v.getKey(), v.getValue()))
.map(v -> new ArrayValue(args.env, v.getKey(), v.getValue()))
.collect(Collectors.toList())
);
}
@Expose public ObjectValue __keys(Arguments args) {
return Values.toJSIterator(args.ctx, map.keySet());
return Values.toJSIterator(args.env, map.keySet());
}
@Expose public ObjectValue __values(Arguments args) {
return Values.toJSIterator(args.ctx, map.values());
return Values.toJSIterator(args.env, map.values());
}
@Expose public Object __get(Arguments args) {
@ -69,19 +69,19 @@ public class MapLib {
@Expose public void __forEach(Arguments args) {
var keys = new ArrayList<>(map.keySet());
for (var el : keys) Values.call(args.ctx, args.get(0), args.get(1), map.get(el), el, args.self);
for (var el : keys) Values.call(args.env, args.get(0), args.get(1), map.get(el), el, args.self);
}
public MapLib(Context ctx, Object iterable) {
for (var el : Values.fromJSIterator(ctx, iterable)) {
public MapLib(Environment env, Object iterable) {
for (var el : Values.fromJSIterator(env, iterable)) {
try {
map.put(Values.getMember(ctx, el, 0), Values.getMember(ctx, el, 1));
map.put(Values.getMember(env, el, 0), Values.getMember(env, el, 1));
}
catch (IllegalArgumentException e) { }
}
}
@ExposeConstructor public static MapLib __constructor(Arguments args) {
return new MapLib(args.ctx, args.get(0));
return new MapLib(args.env, args.get(0));
}
}

View File

@ -85,7 +85,7 @@ public class NumberLib {
else return args.getDouble(0);
}
@Expose public static String __toString(Arguments args) {
return Values.toString(args.ctx, args.self);
return Values.toString(args.env, args.self);
}
@Expose public static String __toFixed(Arguments args) {
var digits = args.getInt(0, 0);
@ -98,6 +98,6 @@ public class NumberLib {
}
@Expose public static double __valueOf(Arguments args) {
if (Values.isWrapper(args.self, NumberLib.class)) return Values.wrapper(args.self, NumberLib.class).value;
else return Values.toNumber(args.ctx, args.self);
else return Values.toNumber(args.env, args.self);
}
}

View File

@ -17,8 +17,8 @@ public class ObjectLib {
@Expose(target = ExposeTarget.STATIC)
public static Object __assign(Arguments args) {
for (var obj : args.slice(1).args) {
for (var key : Values.getMembers(args.ctx, obj, true, true)) {
Values.setMember(args.ctx, args.get(0), key, Values.getMember(args.ctx, obj, key));
for (var key : Values.getMembers(args.env, obj, true, true)) {
Values.setMember(args.env, args.get(0), key, Values.getMember(args.env, obj, key));
}
}
return args.get(0);
@ -26,14 +26,14 @@ public class ObjectLib {
@Expose(target = ExposeTarget.STATIC)
public static ObjectValue __create(Arguments args) {
var obj = new ObjectValue();
Values.setPrototype(args.ctx, obj, args.get(0));
Values.setPrototype(args.env, obj, args.get(0));
if (args.n() >= 1) {
var newArgs = new Object[args.n()];
System.arraycopy(args.args, 1, args, 1, args.n() - 1);
newArgs[0] = obj;
__defineProperties(new Arguments(args.ctx, null, newArgs));
__defineProperties(new Arguments(args.env, null, newArgs));
}
return obj;
@ -45,31 +45,31 @@ public class ObjectLib {
var key = args.get(1);
var attrib = args.convert(2, ObjectValue.class);
var hasVal = Values.hasMember(args.ctx, attrib, "value", false);
var hasGet = Values.hasMember(args.ctx, attrib, "get", false);
var hasSet = Values.hasMember(args.ctx, attrib, "set", false);
var hasVal = Values.hasMember(args.env, attrib, "value", false);
var hasGet = Values.hasMember(args.env, attrib, "get", false);
var hasSet = Values.hasMember(args.env, attrib, "set", false);
if (hasVal) {
if (hasGet || hasSet) throw EngineException.ofType("Cannot specify a value and accessors for a property.");
if (!obj.defineProperty(
args.ctx, key,
Values.getMember(args.ctx, attrib, "value"),
Values.toBoolean(Values.getMember(args.ctx, attrib, "writable")),
Values.toBoolean(Values.getMember(args.ctx, attrib, "configurable")),
Values.toBoolean(Values.getMember(args.ctx, attrib, "enumerable"))
args.env, key,
Values.getMember(args.env, attrib, "value"),
Values.toBoolean(Values.getMember(args.env, attrib, "writable")),
Values.toBoolean(Values.getMember(args.env, attrib, "configurable")),
Values.toBoolean(Values.getMember(args.env, attrib, "enumerable"))
)) throw EngineException.ofType("Can't define property '" + key + "'.");
}
else {
var get = Values.getMember(args.ctx, attrib, "get");
var set = Values.getMember(args.ctx, attrib, "set");
var get = Values.getMember(args.env, attrib, "get");
var set = Values.getMember(args.env, attrib, "set");
if (get != null && !(get instanceof FunctionValue)) throw EngineException.ofType("Get accessor must be a function.");
if (set != null && !(set instanceof FunctionValue)) throw EngineException.ofType("Set accessor must be a function.");
if (!obj.defineProperty(
args.ctx, key,
args.env, key,
(FunctionValue)get, (FunctionValue)set,
Values.toBoolean(Values.getMember(args.ctx, attrib, "configurable")),
Values.toBoolean(Values.getMember(args.ctx, attrib, "enumerable"))
Values.toBoolean(Values.getMember(args.env, attrib, "configurable")),
Values.toBoolean(Values.getMember(args.env, attrib, "enumerable"))
)) throw EngineException.ofType("Can't define property '" + key + "'.");
}
@ -81,7 +81,7 @@ public class ObjectLib {
var attrib = args.get(1);
for (var key : Values.getMembers(null, attrib, false, false)) {
__defineProperty(new Arguments(args.ctx, null, obj, key, Values.getMember(args.ctx, attrib, key)));
__defineProperty(new Arguments(args.env, null, obj, key, Values.getMember(args.env, attrib, key)));
}
return obj;
@ -93,8 +93,8 @@ public class ObjectLib {
var all = args.getBoolean(1);
var res = new ArrayValue();
for (var key : Values.getMembers(args.ctx, obj, true, false)) {
if (all || !(key instanceof Symbol)) res.set(args.ctx, res.size(), key);
for (var key : Values.getMembers(args.env, obj, true, false)) {
if (all || !(key instanceof Symbol)) res.set(args.env, res.size(), key);
}
return res;
@ -105,8 +105,8 @@ public class ObjectLib {
var obj = args.get(0);
var all = args.getBoolean(1);
for (var key : Values.getMembers(args.ctx, obj, true, false)) {
if (all || !(key instanceof Symbol)) res.set(args.ctx, res.size(), new ArrayValue(args.ctx, key, Values.getMember(args.ctx, obj, key)));
for (var key : Values.getMembers(args.env, obj, true, false)) {
if (all || !(key instanceof Symbol)) res.set(args.env, res.size(), new ArrayValue(args.env, key, Values.getMember(args.env, obj, key)));
}
return res;
@ -117,8 +117,8 @@ public class ObjectLib {
var obj = args.get(0);
var all = args.getBoolean(1);
for (var key : Values.getMembers(args.ctx, obj, true, false)) {
if (all || !(key instanceof Symbol)) res.set(args.ctx, res.size(), Values.getMember(args.ctx, obj, key));
for (var key : Values.getMembers(args.env, obj, true, false)) {
if (all || !(key instanceof Symbol)) res.set(args.env, res.size(), Values.getMember(args.env, obj, key));
}
return res;
@ -126,14 +126,14 @@ public class ObjectLib {
@Expose(target = ExposeTarget.STATIC)
public static ObjectValue __getOwnPropertyDescriptor(Arguments args) {
return Values.getMemberDescriptor(args.ctx, args.get(0), args.get(1));
return Values.getMemberDescriptor(args.env, args.get(0), args.get(1));
}
@Expose(target = ExposeTarget.STATIC)
public static ObjectValue __getOwnPropertyDescriptors(Arguments args) {
var res = new ObjectValue();
var obj = args.get(0);
for (var key : Values.getMembers(args.ctx, obj, true, true)) {
res.defineProperty(args.ctx, key, Values.getMemberDescriptor(args.ctx, obj, key));
for (var key : Values.getMembers(args.env, obj, true, true)) {
res.defineProperty(args.env, key, Values.getMemberDescriptor(args.env, obj, key));
}
return res;
}
@ -144,8 +144,8 @@ public class ObjectLib {
var obj = args.get(0);
var all = args.getBoolean(1);
for (var key : Values.getMembers(args.ctx, obj, true, true)) {
if (all || !(key instanceof Symbol)) res.set(args.ctx, res.size(), key);
for (var key : Values.getMembers(args.env, obj, true, true)) {
if (all || !(key instanceof Symbol)) res.set(args.env, res.size(), key);
}
return res;
@ -155,24 +155,24 @@ public class ObjectLib {
var obj = args.get(0);
var res = new ArrayValue();
for (var key : Values.getMembers(args.ctx, obj, true, true)) {
if (key instanceof Symbol) res.set(args.ctx, res.size(), key);
for (var key : Values.getMembers(args.env, obj, true, true)) {
if (key instanceof Symbol) res.set(args.env, res.size(), key);
}
return res;
}
@Expose(target = ExposeTarget.STATIC)
public static boolean __hasOwn(Arguments args) {
return Values.hasMember(args.ctx, args.get(0), args.get(1), true);
return Values.hasMember(args.env, args.get(0), args.get(1), true);
}
@Expose(target = ExposeTarget.STATIC)
public static ObjectValue __getPrototypeOf(Arguments args) {
return Values.getPrototype(args.ctx, args.get(0));
return Values.getPrototype(args.env, args.get(0));
}
@Expose(target = ExposeTarget.STATIC)
public static Object __setPrototypeOf(Arguments args) {
Values.setPrototype(args.ctx, args.get(0), args.get(1));
Values.setPrototype(args.env, args.get(0), args.get(1));
return args.get(0);
}
@ -180,9 +180,9 @@ public class ObjectLib {
public static ObjectValue __fromEntries(Arguments args) {
var res = new ObjectValue();
for (var el : Values.fromJSIterator(args.ctx, args.get(0))) {
for (var el : Values.fromJSIterator(args.env, args.get(0))) {
if (el instanceof ArrayValue) {
res.defineProperty(args.ctx, ((ArrayValue)el).get(0), ((ArrayValue)el).get(1));
res.defineProperty(args.env, ((ArrayValue)el).get(0), ((ArrayValue)el).get(1));
}
}
@ -249,15 +249,15 @@ public class ObjectLib {
}
@Expose
public static String __toString(Arguments args) {
var name = Values.getMember(args.ctx, args.self, Symbol.get("Symbol.typeName"));
var name = Values.getMember(args.env, args.self, Symbol.get("Symbol.typeName"));
if (name == null) name = "Unknown";
else name = Values.toString(args.ctx, name);
else name = Values.toString(args.env, name);
return "[object " + name + "]";
}
@Expose
public static boolean __hasOwnProperty(Arguments args) {
return Values.hasMember(args.ctx, args.self, args.get(0), true);
return Values.hasMember(args.env, args.self, args.get(0), true);
}
@ExposeConstructor

View File

@ -4,9 +4,8 @@ import java.util.ArrayList;
import java.util.List;
import me.topchetoeu.jscript.common.ResultRunnable;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.EventLoop;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.exceptions.InterruptException;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
@ -26,7 +25,7 @@ public class PromiseLib {
void onFulfil(Object val);
void onReject(EngineException err);
default Handle defer(Extensions loop) {
default Handle defer(Environment loop) {
var self = this;
return new Handle() {
@ -52,7 +51,7 @@ public class PromiseLib {
private boolean handled = false;
private Object val;
private void resolveSynchronized(Context ctx, Object val, int newState) {
private void resolveSynchronized(Environment env, Object val, int newState) {
this.val = val;
this.state = newState;
@ -65,7 +64,7 @@ public class PromiseLib {
}
if (state == STATE_REJECTED && !handled) {
Values.printError(((EngineException)val).setExtensions(ctx), "(in promise)");
Values.printError(((EngineException)val).setEnvironment(env), "(in promise)");
}
handles = null;
@ -78,24 +77,24 @@ public class PromiseLib {
// }, true);
}
private synchronized void resolve(Context ctx, Object val, int newState) {
private synchronized void resolve(Environment env, Object val, int newState) {
if (this.state != STATE_PENDING || newState == STATE_PENDING) return;
handle(ctx, val, new Handle() {
handle(env, val, new Handle() {
@Override public void onFulfil(Object val) {
resolveSynchronized(ctx, val, newState);
resolveSynchronized(env, val, newState);
}
@Override public void onReject(EngineException err) {
resolveSynchronized(ctx, val, STATE_REJECTED);
resolveSynchronized(env, val, STATE_REJECTED);
}
});
}
public synchronized void fulfill(Context ctx, Object val) {
resolve(ctx, val, STATE_FULFILLED);
public synchronized void fulfill(Environment env, Object val) {
resolve(env, val, STATE_FULFILLED);
}
public synchronized void reject(Context ctx, EngineException val) {
resolve(ctx, val, STATE_REJECTED);
public synchronized void reject(Environment env, EngineException val) {
resolve(env, val, STATE_REJECTED);
}
private void handle(Handle handle) {
@ -118,37 +117,37 @@ public class PromiseLib {
this.val = null;
}
public static PromiseLib await(Context ctx, ResultRunnable<Object> runner) {
public static PromiseLib await(Environment env, ResultRunnable<Object> runner) {
var res = new PromiseLib();
new Thread(() -> {
try {
res.fulfill(ctx, runner.run());
res.fulfill(env, runner.run());
}
catch (EngineException e) {
res.reject(ctx, e);
res.reject(env, e);
}
catch (Exception e) {
if (e instanceof InterruptException) throw e;
else {
res.reject(ctx, EngineException.ofError("Native code failed with " + e.getMessage()));
res.reject(env, EngineException.ofError("Native code failed with " + e.getMessage()));
}
}
}, "Promisifier").start();
return res;
}
public static PromiseLib await(Context ctx, Runnable runner) {
return await(ctx, () -> {
public static PromiseLib await(Environment env, Runnable runner) {
return await(env, () -> {
runner.run();
return null;
});
}
public static void handle(Context ctx, Object obj, Handle handle) {
public static void handle(Environment env, Object obj, Handle handle) {
if (Values.isWrapper(obj, PromiseLib.class)) {
var promise = Values.wrapper(obj, PromiseLib.class);
handle(ctx, promise, handle);
handle(env, promise, handle);
return;
}
if (obj instanceof PromiseLib) {
@ -159,9 +158,9 @@ public class PromiseLib {
var rethrow = new boolean[1];
try {
var then = Values.getMember(ctx, obj, "then");
var then = Values.getMember(env, obj, "then");
if (then instanceof FunctionValue) Values.call(ctx, then, obj,
if (then instanceof FunctionValue) Values.call(env, then, obj,
new NativeFunction(args -> {
try { handle.onFulfil(args.get(0)); }
catch (Exception e) {
@ -190,12 +189,12 @@ public class PromiseLib {
handle.onFulfil(obj);
}
public static PromiseLib ofResolved(Context ctx, Object value) {
public static PromiseLib ofResolved(Environment ctx, Object value) {
var res = new PromiseLib();
res.fulfill(ctx, value);
return res;
}
public static PromiseLib ofRejected(Context ctx, EngineException value) {
public static PromiseLib ofRejected(Environment ctx, EngineException value) {
var res = new PromiseLib();
res.reject(ctx, value);
return res;
@ -203,11 +202,11 @@ public class PromiseLib {
@Expose(value = "resolve", target = ExposeTarget.STATIC)
public static PromiseLib __ofResolved(Arguments args) {
return ofResolved(args.ctx, args.get(0));
return ofResolved(args.env, args.get(0));
}
@Expose(value = "reject", target = ExposeTarget.STATIC)
public static PromiseLib __ofRejected(Arguments args) {
return ofRejected(args.ctx, new EngineException(args.get(0)).setExtensions(args.ctx));
return ofRejected(args.env, new EngineException(args.get(0)).setEnvironment(args.env));
}
@Expose(target = ExposeTarget.STATIC)
@ -215,7 +214,7 @@ public class PromiseLib {
if (!(args.get(0) instanceof ArrayValue)) throw EngineException.ofType("Expected argument for any to be an array.");
var promises = args.convert(0, ArrayValue.class);
if (promises.size() == 0) return ofRejected(args.ctx, EngineException.ofError("No promises passed to 'Promise.any'.").setExtensions(args.ctx));
if (promises.size() == 0) return ofRejected(args.env, EngineException.ofError("No promises passed to 'Promise.any'.").setEnvironment(args.env));
var n = new int[] { promises.size() };
var res = new PromiseLib();
var errors = new ArrayValue();
@ -225,12 +224,12 @@ public class PromiseLib {
var val = promises.get(i);
if (res.state != STATE_PENDING) break;
handle(args.ctx, val, new Handle() {
public void onFulfil(Object val) { res.fulfill(args.ctx, val); }
handle(args.env, val, new Handle() {
public void onFulfil(Object val) { res.fulfill(args.env, val); }
public void onReject(EngineException err) {
errors.set(args.ctx, index, err.value);
errors.set(args.env, index, err.value);
n[0]--;
if (n[0] <= 0) res.reject(args.ctx, new EngineException(errors).setExtensions(args.ctx));
if (n[0] <= 0) res.reject(args.env, new EngineException(errors).setEnvironment(args.env));
}
});
}
@ -247,9 +246,9 @@ public class PromiseLib {
var val = promises.get(i);
if (res.state != STATE_PENDING) break;
handle(args.ctx, val, new Handle() {
@Override public void onFulfil(Object val) { res.fulfill(args.ctx, val); }
@Override public void onReject(EngineException err) { res.reject(args.ctx, err); }
handle(args.env, val, new Handle() {
@Override public void onFulfil(Object val) { res.fulfill(args.env, val); }
@Override public void onReject(EngineException err) { res.reject(args.env, err); }
});
}
@ -269,19 +268,19 @@ public class PromiseLib {
var index = i;
var val = promises.get(i);
handle(args.ctx, val, new Handle() {
handle(args.env, val, new Handle() {
@Override public void onFulfil(Object val) {
result.set(args.ctx, index, val);
result.set(args.env, index, val);
n[0]--;
if (n[0] <= 0) res.fulfill(args.ctx, result);
if (n[0] <= 0) res.fulfill(args.env, result);
}
@Override public void onReject(EngineException err) {
res.reject(args.ctx, err);
res.reject(args.env, err);
}
});
}
if (n[0] <= 0) res.fulfill(args.ctx, result);
if (n[0] <= 0) res.fulfill(args.env, result);
return res;
}
@ -298,31 +297,31 @@ public class PromiseLib {
var index = i;
handle(args.ctx, promises.get(i), new Handle() {
handle(args.env, promises.get(i), new Handle() {
@Override public void onFulfil(Object val) {
var desc = new ObjectValue();
desc.defineProperty(args.ctx, "status", "fulfilled");
desc.defineProperty(args.ctx, "value", val);
desc.defineProperty(args.env, "status", "fulfilled");
desc.defineProperty(args.env, "value", val);
result.set(args.ctx, index, desc);
result.set(args.env, index, desc);
n[0]--;
if (n[0] <= 0) res.fulfill(args.ctx, res);
if (n[0] <= 0) res.fulfill(args.env, res);
}
@Override public void onReject(EngineException err) {
var desc = new ObjectValue();
desc.defineProperty(args.ctx, "status", "reject");
desc.defineProperty(args.ctx, "value", err.value);
desc.defineProperty(args.env, "status", "reject");
desc.defineProperty(args.env, "value", err.value);
result.set(args.ctx, index, desc);
result.set(args.env, index, desc);
n[0]--;
if (n[0] <= 0) res.fulfill(args.ctx, res);
if (n[0] <= 0) res.fulfill(args.env, res);
}
});
}
if (n[0] <= 0) res.fulfill(args.ctx, result);
if (n[0] <= 0) res.fulfill(args.env, result);
return res;
}
@ -334,22 +333,22 @@ public class PromiseLib {
var res = new PromiseLib();
handle(args.ctx, args.self, new Handle() {
handle(args.env, args.self, new Handle() {
@Override public void onFulfil(Object val) {
try { res.fulfill(args.ctx, onFulfill.call(args.ctx, null, val)); }
catch (EngineException e) { res.reject(args.ctx, e); }
try { res.fulfill(args.env, onFulfill.call(args.env, null, val)); }
catch (EngineException e) { res.reject(args.env, e); }
}
@Override public void onReject(EngineException err) {
try { res.fulfill(args.ctx, onReject.call(args.ctx, null, err.value)); }
catch (EngineException e) { res.reject(args.ctx, e); }
try { res.fulfill(args.env, onReject.call(args.env, null, err.value)); }
catch (EngineException e) { res.reject(args.env, e); }
}
}.defer(args.ctx));
}.defer(args.env));
return res;
}
@Expose
public static Object __catch(Arguments args) {
return __then(new Arguments(args.ctx, args.self, null, args.get(0)));
return __then(new Arguments(args.env, args.self, null, args.get(0)));
}
@Expose
public static Object __finally(Arguments args) {
@ -357,22 +356,22 @@ public class PromiseLib {
var res = new PromiseLib();
handle(args.ctx, args.self, new Handle() {
handle(args.env, args.self, new Handle() {
@Override public void onFulfil(Object val) {
try {
func.call(args.ctx);
res.fulfill(args.ctx, val);
func.call(args.env);
res.fulfill(args.env, val);
}
catch (EngineException e) { res.reject(args.ctx, e); }
catch (EngineException e) { res.reject(args.env, e); }
}
@Override public void onReject(EngineException err) {
try {
func.call(args.ctx);
res.reject(args.ctx, err);
func.call(args.env);
res.reject(args.env, err);
}
catch (EngineException e) { res.reject(args.ctx, e); }
catch (EngineException e) { res.reject(args.env, e); }
}
}.defer(args.ctx));
}.defer(args.env));
return res;
}
@ -384,19 +383,19 @@ public class PromiseLib {
try {
func.call(
args.ctx, null,
args.env, null,
new NativeFunction(null, _args -> {
res.fulfill(_args.ctx, _args.get(0));
res.fulfill(_args.env, _args.get(0));
return null;
}),
new NativeFunction(null, _args -> {
res.reject(_args.ctx, new EngineException(_args.get(0)).setExtensions(_args.ctx));
res.reject(_args.env, new EngineException(_args.get(0)).setEnvironment(_args.env));
return null;
})
);
}
catch (EngineException e) {
res.reject(args.ctx, e);
res.reject(args.env, e);
}
return res;

View File

@ -4,7 +4,7 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.regex.Pattern;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
import me.topchetoeu.jscript.runtime.values.NativeWrapper;
@ -119,7 +119,7 @@ public class RegExpLib {
var res = new ArrayValue();
Object val;
while ((val = this.__exec(args)) != Values.NULL) {
res.set(args.ctx, res.size(), Values.getMember(args.ctx, val, 0));
res.set(args.env, res.size(), Values.getMember(args.env, val, 0));
}
lastI = 0;
return res;
@ -134,7 +134,7 @@ public class RegExpLib {
@Expose("@@Symbol.matchAll") public Object __matchAll(Arguments args) {
var pattern = this.toGlobal();
return Values.toJSIterator(args.ctx, new Iterator<Object>() {
return Values.toJSIterator(args.env, new Iterator<Object>() {
private Object val = null;
private boolean updated = false;
@ -167,7 +167,7 @@ public class RegExpLib {
while ((match = pattern.__exec(args)) != Values.NULL) {
var added = new ArrayList<String>();
var arrMatch = (ArrayValue)match;
int index = (int)Values.toNumber(args.ctx, Values.getMember(args.ctx, match, "index"));
int index = (int)Values.toNumber(args.env, Values.getMember(args.env, match, "index"));
var matchVal = (String)arrMatch.get(0);
if (index >= target.length()) break;
@ -184,18 +184,18 @@ public class RegExpLib {
if (sensible) {
if (hasLimit && res.size() + added.size() >= lim) break;
else for (var i = 0; i < added.size(); i++) res.set(args.ctx, res.size(), added.get(i));
else for (var i = 0; i < added.size(); i++) res.set(args.env, res.size(), added.get(i));
}
else {
for (var i = 0; i < added.size(); i++) {
if (hasLimit && res.size() >= lim) return res;
else res.set(args.ctx, res.size(), added.get(i));
else res.set(args.env, res.size(), added.get(i));
}
}
lastEnd = pattern.lastI;
}
if (lastEnd < target.length()) {
res.set(args.ctx, res.size(), target.substring(lastEnd));
res.set(args.env, res.size(), target.substring(lastEnd));
}
return res;
}
@ -209,7 +209,7 @@ public class RegExpLib {
var res = new StringBuilder();
while ((match = pattern.__exec(args)) != Values.NULL) {
var indices = (ArrayValue)((ArrayValue)Values.getMember(args.ctx, match, "indices")).get(0);
var indices = (ArrayValue)((ArrayValue)Values.getMember(args.env, match, "indices")).get(0);
var arrMatch = (ArrayValue)match;
var start = ((Number)indices.get(0)).intValue();
@ -222,10 +222,10 @@ public class RegExpLib {
arrMatch.copyTo(callArgs, 1, 1, arrMatch.size() - 1);
callArgs[callArgs.length - 2] = start;
callArgs[callArgs.length - 1] = target;
res.append(Values.toString(args.ctx, ((FunctionValue)replacement).call(args.ctx, null, callArgs)));
res.append(Values.toString(args.env, ((FunctionValue)replacement).call(args.env, null, callArgs)));
}
else {
res.append(Values.toString(args.ctx, replacement));
res.append(Values.toString(args.env, replacement));
}
lastEnd = end;
if (!pattern.global) break;
@ -313,26 +313,26 @@ public class RegExpLib {
@ExposeConstructor
public static RegExpLib __constructor(Arguments args) {
return new RegExpLib(cleanupPattern(args.ctx, args.get(0)), cleanupFlags(args.ctx, args.get(1)));
return new RegExpLib(cleanupPattern(args.env, args.get(0)), cleanupFlags(args.env, args.get(1)));
}
@Expose(target = ExposeTarget.STATIC)
public static RegExpLib __escape(Arguments args) {
return escape(Values.toString(args.ctx, args.get(0)), cleanupFlags(args.ctx, args.get(1)));
return escape(Values.toString(args.env, args.get(0)), cleanupFlags(args.env, args.get(1)));
}
private static String cleanupPattern(Context ctx, Object val) {
private static String cleanupPattern(Environment env, Object val) {
if (val == null) return "(?:)";
if (val instanceof RegExpLib) return ((RegExpLib)val).source;
if (val instanceof NativeWrapper && ((NativeWrapper)val).wrapped instanceof RegExpLib) {
return ((RegExpLib)((NativeWrapper)val).wrapped).source;
}
var res = Values.toString(ctx, val);
var res = Values.toString(env, val);
if (res.equals("")) return "(?:)";
return res;
}
private static String cleanupFlags(Context ctx, Object val) {
private static String cleanupFlags(Environment env, Object val) {
if (val == null) return "";
return Values.toString(ctx, val);
return Values.toString(env, val);
}
private static boolean checkEscaped(String s, int pos) {

View File

@ -4,7 +4,7 @@ import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.stream.Collectors;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Values;
@ -24,13 +24,13 @@ public class SetLib {
}
@Expose public ObjectValue __entries(Arguments args) {
return Values.toJSIterator(args.ctx, set.stream().map(v -> new ArrayValue(args.ctx, v, v)).collect(Collectors.toList()));
return Values.toJSIterator(args.env, set.stream().map(v -> new ArrayValue(args.env, v, v)).collect(Collectors.toList()));
}
@Expose public ObjectValue __keys(Arguments args) {
return Values.toJSIterator(args.ctx, set);
return Values.toJSIterator(args.env, set);
}
@Expose public ObjectValue __values(Arguments args) {
return Values.toJSIterator(args.ctx, set);
return Values.toJSIterator(args.env, set);
}
@Expose public Object __add(Arguments args) {
@ -55,15 +55,15 @@ public class SetLib {
@Expose public void __forEach(Arguments args) {
var keys = new ArrayList<>(set);
for (var el : keys) Values.call(args.ctx, args.get(0), args.get(1), el, el, args.self);
for (var el : keys) Values.call(args.env, args.get(0), args.get(1), el, el, args.self);
}
public SetLib(Context ctx, Object iterable) {
for (var el : Values.fromJSIterator(ctx, iterable)) set.add(el);
public SetLib(Environment env, Object iterable) {
for (var el : Values.fromJSIterator(env, iterable)) set.add(el);
}
@ExposeConstructor
public static SetLib __constructor(Arguments args) {
return new SetLib(args.ctx, args.get(0));
return new SetLib(args.env, args.get(0));
}
}

View File

@ -2,7 +2,7 @@ package me.topchetoeu.jscript.lib;
import java.util.regex.Pattern;
import me.topchetoeu.jscript.runtime.Environment;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
@ -101,23 +101,23 @@ public class StringLib {
var val = passThis(args, "indexOf");
var term = args.get(0);
var start = args.getInt(1);
var search = Values.getMember(args.ctx, term, Symbol.get("Symbol.search"));
var search = Values.getMember(args.env, term, Symbol.get("Symbol.search"));
if (search instanceof FunctionValue) {
return (int)Values.toNumber(args.ctx, Values.call(args.ctx, search, term, val, false, start));
return (int)Values.toNumber(args.env, Values.call(args.env, search, term, val, false, start));
}
else return val.indexOf(Values.toString(args.ctx, term), start);
else return val.indexOf(Values.toString(args.env, term), start);
}
@Expose public static int __lastIndexOf(Arguments args) {
var val = passThis(args, "lastIndexOf");
var term = args.get(0);
var start = args.getInt(1);
var search = Values.getMember(args.ctx, term, Symbol.get("Symbol.search"));
var search = Values.getMember(args.env, term, Symbol.get("Symbol.search"));
if (search instanceof FunctionValue) {
return (int)Values.toNumber(args.ctx, Values.call(args.ctx, search, term, val, true, start));
return (int)Values.toNumber(args.env, Values.call(args.env, search, term, val, true, start));
}
else return val.lastIndexOf(Values.toString(args.ctx, term), start);
else return val.lastIndexOf(Values.toString(args.env, term), start);
}
@Expose public static boolean __includes(Arguments args) {
@ -128,23 +128,23 @@ public class StringLib {
var val = passThis(args, "replace");
var term = args.get(0);
var replacement = args.get(1);
var replace = Values.getMember(args.ctx, term, Symbol.get("Symbol.replace"));
var replace = Values.getMember(args.env, term, Symbol.get("Symbol.replace"));
if (replace instanceof FunctionValue) {
return Values.toString(args.ctx, Values.call(args.ctx, replace, term, val, replacement));
return Values.toString(args.env, Values.call(args.env, replace, term, val, replacement));
}
else return val.replaceFirst(Pattern.quote(Values.toString(args.ctx, term)), Values.toString(args.ctx, replacement));
else return val.replaceFirst(Pattern.quote(Values.toString(args.env, term)), Values.toString(args.env, replacement));
}
@Expose public static String __replaceAll(Arguments args) {
var val = passThis(args, "replaceAll");
var term = args.get(0);
var replacement = args.get(1);
var replace = Values.getMember(args.ctx, term, Symbol.get("Symbol.replace"));
var replace = Values.getMember(args.env, term, Symbol.get("Symbol.replace"));
if (replace instanceof FunctionValue) {
return Values.toString(args.ctx, Values.call(args.ctx, replace, term, val, replacement));
return Values.toString(args.env, Values.call(args.env, replace, term, val, replacement));
}
else return val.replace(Values.toString(args.ctx, term), Values.toString(args.ctx, replacement));
else return val.replace(Values.toString(args.env, term), Values.toString(args.env, replacement));
}
@Expose public static ArrayValue __match(Arguments args) {
@ -154,21 +154,21 @@ public class StringLib {
FunctionValue match;
try {
var _match = Values.getMember(args.ctx, term, Symbol.get("Symbol.match"));
var _match = Values.getMember(args.env, term, Symbol.get("Symbol.match"));
if (_match instanceof FunctionValue) match = (FunctionValue)_match;
else if (args.ctx.hasNotNull(Environment.REGEX_CONSTR)) {
var regex = Values.callNew(args.ctx, args.ctx.get(Environment.REGEX_CONSTR), Values.toString(args.ctx, term), "");
_match = Values.getMember(args.ctx, regex, Symbol.get("Symbol.match"));
else if (args.env.hasNotNull(Environment.REGEX_CONSTR)) {
var regex = Values.callNew(args.env, args.env.get(Environment.REGEX_CONSTR), Values.toString(args.env, term), "");
_match = Values.getMember(args.env, regex, Symbol.get("Symbol.match"));
if (_match instanceof FunctionValue) match = (FunctionValue)_match;
else throw EngineException.ofError("Regular expressions don't support matching.");
}
else throw EngineException.ofError("Regular expressions not supported.");
}
catch (IllegalArgumentException e) { return new ArrayValue(args.ctx, ""); }
catch (IllegalArgumentException e) { return new ArrayValue(args.env, ""); }
var res = match.call(args.ctx, term, val);
var res = match.call(args.env, term, val);
if (res instanceof ArrayValue) return (ArrayValue)res;
else return new ArrayValue(args.ctx, "");
else return new ArrayValue(args.env, "");
}
@Expose public static Object __matchAll(Arguments args) {
var val = passThis(args, "matchAll");
@ -177,20 +177,20 @@ public class StringLib {
FunctionValue match = null;
try {
var _match = Values.getMember(args.ctx, term, Symbol.get("Symbol.matchAll"));
var _match = Values.getMember(args.env, term, Symbol.get("Symbol.matchAll"));
if (_match instanceof FunctionValue) match = (FunctionValue)_match;
}
catch (IllegalArgumentException e) { }
if (match == null && args.ctx.hasNotNull(Environment.REGEX_CONSTR)) {
var regex = Values.callNew(args.ctx, args.ctx.get(Environment.REGEX_CONSTR), Values.toString(args.ctx, term), "g");
var _match = Values.getMember(args.ctx, regex, Symbol.get("Symbol.matchAll"));
if (match == null && args.env.hasNotNull(Environment.REGEX_CONSTR)) {
var regex = Values.callNew(args.env, args.env.get(Environment.REGEX_CONSTR), Values.toString(args.env, term), "g");
var _match = Values.getMember(args.env, regex, Symbol.get("Symbol.matchAll"));
if (_match instanceof FunctionValue) match = (FunctionValue)_match;
else throw EngineException.ofError("Regular expressions don't support matching.");
}
else throw EngineException.ofError("Regular expressions not supported.");
return match.call(args.ctx, term, val);
return match.call(args.env, term, val);
}
@Expose public static ArrayValue __split(Arguments args) {
@ -199,23 +199,23 @@ public class StringLib {
var lim = args.get(1);
var sensible = args.getBoolean(2);
if (lim != null) lim = Values.toNumber(args.ctx, lim);
if (lim != null) lim = Values.toNumber(args.env, lim);
if (term != null && term != Values.NULL && !(term instanceof String)) {
var replace = Values.getMember(args.ctx, term, Symbol.get("Symbol.replace"));
var replace = Values.getMember(args.env, term, Symbol.get("Symbol.replace"));
if (replace instanceof FunctionValue) {
var tmp = ((FunctionValue)replace).call(args.ctx, term, val, lim, sensible);
var tmp = ((FunctionValue)replace).call(args.env, term, val, lim, sensible);
if (tmp instanceof ArrayValue) {
var parts = new ArrayValue(((ArrayValue)tmp).size());
for (int i = 0; i < parts.size(); i++) parts.set(args.ctx, i, Values.toString(args.ctx, ((ArrayValue)tmp).get(i)));
for (int i = 0; i < parts.size(); i++) parts.set(args.env, i, Values.toString(args.env, ((ArrayValue)tmp).get(i)));
return parts;
}
}
}
String[] parts;
var pattern = Pattern.quote(Values.toString(args.ctx, term));
var pattern = Pattern.quote(Values.toString(args.env, term));
if (lim == null) parts = val.split(pattern);
else if ((double)lim < 1) return new ArrayValue();
@ -228,7 +228,7 @@ public class StringLib {
if (parts.length > limit) res = new ArrayValue(limit);
else res = new ArrayValue(parts.length);
for (var i = 0; i < parts.length && i < limit; i++) res.set(args.ctx, i, parts[i]);
for (var i = 0; i < parts.length && i < limit; i++) res.set(args.env, i, parts[i]);
return res;
}
@ -238,7 +238,7 @@ public class StringLib {
for (; i < parts.length; i++) {
if (lim != null && (double)lim <= i) break;
res.set(args.ctx, i, parts[i]);
res.set(args.env, i, parts[i]);
}
return res;
@ -249,7 +249,7 @@ public class StringLib {
var start = normalizeI(args.getInt(0), self.length(), false);
var end = normalizeI(args.getInt(1, self.length()), self.length(), false);
return __substring(new Arguments(args.ctx, self, start, end));
return __substring(new Arguments(args.env, self, start, end));
}
@Expose public static String __concat(Arguments args) {

View File

@ -76,6 +76,6 @@ public class SymbolLib {
}
@Expose(target = ExposeTarget.STATIC)
public static String __keyFor(Arguments args) {
return passThis(new Arguments(args.ctx, args.get(0)), "keyFor").value;
return passThis(new Arguments(args.env, args.get(0)), "keyFor").value;
}
}

View File

@ -1,5 +0,0 @@
package me.topchetoeu.jscript.runtime;
public interface Childable {
Object child();
}

View File

@ -2,6 +2,9 @@ package me.topchetoeu.jscript.runtime;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.FunctionBody;
import me.topchetoeu.jscript.runtime.debug.DebugContext;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.environment.Key;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.scope.ValueVariable;
import me.topchetoeu.jscript.runtime.values.CodeFunction;
@ -11,13 +14,14 @@ public interface Compiler {
public FunctionBody compile(Filename filename, String source);
public static Compiler get(Extensions ext) {
public static Compiler get(Environment ext) {
return ext.get(KEY, (filename, src) -> {
throw EngineException.ofError("No compiler attached to engine.");
});
}
public static CodeFunction compile(Environment env, Filename filename, String raw) {
DebugContext.get(env).onSource(filename, raw);
return new CodeFunction(env, filename.toString(), Compiler.get(env).compile(filename, raw), new ValueVariable[0]);
}
}

View File

@ -1,98 +0,0 @@
package me.topchetoeu.jscript.runtime;
import java.util.Iterator;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.runtime.debug.DebugContext;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.scope.ValueVariable;
import me.topchetoeu.jscript.runtime.values.CodeFunction;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
public class Context implements Extensions {
public final Context parent;
public final Extensions extensions;
public final Frame frame;
public final int stackSize;
@Override public <T> void add(Key<T> key, T obj) {
if (extensions != null) extensions.add(key, obj);
}
@Override public <T> T get(Key<T> key) {
if (extensions != null && extensions.has(key)) return extensions.get(key);
return null;
}
@Override public boolean has(Key<?> key) {
return extensions != null && extensions.has(key);
}
@Override public boolean remove(Key<?> key) {
var res = false;
if (extensions != null) res |= extensions.remove(key);
return res;
}
@Override public Iterable<Key<?>> keys() {
if (extensions == null) return List.of();
else return extensions.keys();
}
public FunctionValue compile(Filename filename, String raw) {
DebugContext.get(this).onSource(filename, raw);
var result = new CodeFunction(extensions, filename.toString(), Compiler.get(this).compile(filename, raw), new ValueVariable[0]);
return result;
}
public Context pushFrame(Frame frame) {
var res = new Context(this, frame.function.extensions, frame, stackSize + 1);
return res;
}
public Iterable<Frame> frames() {
var self = this;
return () -> new Iterator<Frame>() {
private Context curr = self;
private void update() {
while (curr != null && curr.frame == null) curr = curr.parent;
}
@Override public boolean hasNext() {
update();
return curr != null;
}
@Override public Frame next() {
update();
var res = curr.frame;
curr = curr.parent;
return res;
}
};
}
private Context(Context parent, Extensions ext, Frame frame, int stackSize) {
this.parent = parent;
this.extensions = ext;
this.frame = frame;
this.stackSize = stackSize;
if (hasNotNull(Environment.MAX_STACK_COUNT) && stackSize > (int)get(Environment.MAX_STACK_COUNT)) {
throw EngineException.ofRange("Stack overflow!");
}
}
public Context() {
this(null, null, null, 0);
}
public Context(Extensions ext) {
this(null, clean(ext), null, 0);
}
public static Context of(Extensions ext) {
if (ext instanceof Context) return (Context)ext;
return new Context(ext);
}
public static Extensions clean(Extensions ext) {
if (ext instanceof Context) return clean(((Context)ext).extensions);
else return ext;
}
}

View File

@ -1,5 +0,0 @@
package me.topchetoeu.jscript.runtime;
public interface Copyable {
Object copy();
}

View File

@ -1,61 +0,0 @@
package me.topchetoeu.jscript.runtime;
import java.util.HashMap;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
import me.topchetoeu.jscript.runtime.values.NativeFunction;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
@SuppressWarnings("unchecked")
public class Environment implements Extensions {
public static final Key<Compiler> COMPILE_FUNC = new Key<>();
public static final Key<FunctionValue> REGEX_CONSTR = new Key<>();
public static final Key<Integer> MAX_STACK_COUNT = new Key<>();
public static final Key<Boolean> HIDE_STACK = new Key<>();
public static final Key<ObjectValue> OBJECT_PROTO = new Key<>();
public static final Key<ObjectValue> FUNCTION_PROTO = new Key<>();
public static final Key<ObjectValue> ARRAY_PROTO = new Key<>();
public static final Key<ObjectValue> BOOL_PROTO = new Key<>();
public static final Key<ObjectValue> NUMBER_PROTO = new Key<>();
public static final Key<ObjectValue> STRING_PROTO = new Key<>();
public static final Key<ObjectValue> SYMBOL_PROTO = new Key<>();
public static final Key<ObjectValue> ERROR_PROTO = new Key<>();
public static final Key<ObjectValue> SYNTAX_ERR_PROTO = new Key<>();
public static final Key<ObjectValue> TYPE_ERR_PROTO = new Key<>();
public static final Key<ObjectValue> RANGE_ERR_PROTO = new Key<>();
private HashMap<Key<?>, Object> data = new HashMap<>();
@Override public <T> void add(Key<T> key, T obj) {
data.put(key, obj);
}
@Override public <T> T get(Key<T> key) {
return (T)data.get(key);
}
@Override public boolean remove(Key<?> key) {
if (data.containsKey(key)) {
data.remove(key);
return true;
}
return false;
}
@Override public boolean has(Key<?> key) {
return data.containsKey(key);
}
@Override public Iterable<Key<?>> keys() {
return data.keySet();
}
public static FunctionValue regexConstructor(Extensions ext) {
return ext.init(REGEX_CONSTR, new NativeFunction("RegExp", args -> {
throw EngineException.ofError("Regular expressions not supported.").setExtensions(args.ctx);
}));
}
public Context context() {
return new Context(this);
}
}

View File

@ -3,13 +3,15 @@ package me.topchetoeu.jscript.runtime;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.ResultRunnable;
import me.topchetoeu.jscript.common.events.DataNotifier;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.environment.Key;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
public interface EventLoop {
public static final Key<EventLoop> KEY = new Key<>();
public static EventLoop get(Extensions ext) {
public static EventLoop get(Environment ext) {
if (ext.hasNotNull(KEY)) return ext.get(KEY);
else return new EventLoop() {
@Override public <T> DataNotifier<T> pushMsg(ResultRunnable<T> runnable, boolean micro) {
@ -23,15 +25,10 @@ public interface EventLoop {
return pushMsg(() -> { runnable.run(); return null; }, micro);
}
public default DataNotifier<Object> pushMsg(boolean micro, Extensions ext, FunctionValue func, Object thisArg, Object ...args) {
return pushMsg(() -> {
return func.call(Context.of(ext), thisArg, args);
}, micro);
public default DataNotifier<Object> pushMsg(boolean micro, Environment env, FunctionValue func, Object thisArg, Object ...args) {
return pushMsg(() -> func.call(env, thisArg, args), micro);
}
public default DataNotifier<Object> pushMsg(boolean micro, Extensions ext, Filename filename, String raw, Object thisArg, Object ...args) {
return pushMsg(() -> {
var ctx = Context.of(ext);
return ctx.compile(filename, raw).call(Context.of(ext), thisArg, args);
}, micro);
public default DataNotifier<Object> pushMsg(boolean micro, Environment env, Filename filename, String raw, Object thisArg, Object ...args) {
return pushMsg(() -> Compiler.compile(env, filename, raw).call(env, thisArg, args), micro);
}
}

View File

@ -1,77 +0,0 @@
package me.topchetoeu.jscript.runtime;
import java.util.List;
public interface Extensions extends Childable, Copyable {
public static Extensions EMPTY = new Extensions() {
@Override public <T> void add(Key<T> key, T obj) { }
@Override public boolean remove(Key<?> key) { return false; }
@Override public <T> T get(Key<T> key) { return null; }
@Override public boolean has(Key<?> key) { return false; }
@Override public Iterable<Key<?>> keys() { return List.of(); }
};
<T> T get(Key<T> key);
<T> void add(Key<T> key, T obj);
Iterable<Key<?>> keys();
boolean has(Key<?> key);
boolean remove(Key<?> key);
default void add(Key<Void> key) {
add(key, null);
}
default boolean hasNotNull(Key<?> key) {
return has(key) && get(key) != null;
}
default <T> T get(Key<T> key, T defaultVal) {
if (has(key)) return get(key);
else return defaultVal;
}
default <T> T init(Key<T> key, T val) {
if (has(key)) return get(key);
else {
add(key, val);
return val;
}
}
@SuppressWarnings("unchecked")
default void addAll(Extensions source) {
if (source == null) return;
for (var key : source.keys()) {
add((Key<Object>)key, (Object)source.get(key));
}
}
@Override
@SuppressWarnings("unchecked")
default Extensions copy() {
var res = new Environment();
for (var key : keys()) {
var val = get(key);
if (val instanceof Copyable) val = ((Copyable)val).copy();
res.add((Key<Object>)key, val);
}
return res;
}
@Override
@SuppressWarnings("unchecked")
default Extensions child() {
var res = new Environment();
for (var key : keys()) {
var val = get(key);
if (val instanceof Childable) val = ((Childable)val).child();
res.add((Key<Object>)key, val);
}
return res;
}
public static Extensions wrap(Extensions ext) {
if (ext == null) return EMPTY;
else return ext;
}
}

View File

@ -5,6 +5,8 @@ import java.util.Stack;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.runtime.debug.DebugContext;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.environment.Key;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.exceptions.InterruptException;
import me.topchetoeu.jscript.runtime.scope.LocalScope;
@ -16,6 +18,8 @@ import me.topchetoeu.jscript.runtime.values.ScopeValue;
import me.topchetoeu.jscript.runtime.values.Values;
public class Frame {
public static final Key<Frame> KEY = new Key<>();
public static enum TryState {
TRY,
CATCH,
@ -94,11 +98,13 @@ public class Frame {
public final Object[] args;
public final Stack<TryCtx> tryStack = new Stack<>();
public final CodeFunction function;
public final Context ctx;
public final Environment env;
public Object[] stack = new Object[32];
public int stackPtr = 0;
public int codePtr = 0;
public boolean jumpFlag = false, popTryFlag = false;
public boolean jumpFlag = false;
public boolean popTryFlag = false;
public void addTry(int start, int end, int catchStart, int finallyStart) {
var err = tryStack.empty() ? null : tryStack.peek().error;
@ -137,10 +143,10 @@ public class Frame {
System.arraycopy(stack, 0, newStack, 0, stack.length);
stack = newStack;
}
stack[stackPtr++] = Values.normalize(ctx, val);
stack[stackPtr++] = Values.normalize(env, val);
}
public Object next(Object value, Object returnValue, EngineException error) {
private Object next(Object value, Object returnValue, EngineException error) {
if (value != Values.NO_RETURN) push(value);
Instruction instr = null;
@ -148,18 +154,18 @@ public class Frame {
if (returnValue == Values.NO_RETURN && error == null) {
try {
if (Thread.currentThread().isInterrupted()) throw new InterruptException();
if (Thread.interrupted()) throw new InterruptException();
if (instr == null) returnValue = null;
else {
DebugContext.get(ctx).onInstruction(ctx, this, instr, Values.NO_RETURN, null, false);
DebugContext.get(env).onInstruction(env, this, instr, Values.NO_RETURN, null, false);
try {
this.jumpFlag = this.popTryFlag = false;
returnValue = InstructionRunner.exec(ctx, instr, this);
returnValue = InstructionRunner.exec(env, instr, this);
}
catch (EngineException e) {
error = e.add(ctx, function.name, DebugContext.get(ctx).getMapOrEmpty(function).toLocation(codePtr, true));
error = e.add(env, function.name, DebugContext.get(env).getMapOrEmpty(function).toLocation(codePtr, true));
}
}
}
@ -201,6 +207,7 @@ public class Frame {
tryStack.pop();
tryStack.push(newCtx);
}
error = null;
returnValue = Values.NO_RETURN;
break;
@ -239,33 +246,75 @@ public class Frame {
if (error != null) {
var caught = false;
for (var frame : ctx.frames()) {
for (var frame : DebugContext.get(env).getStackFrames()) {
for (var tryCtx : frame.tryStack) {
if (tryCtx.state == TryState.TRY) caught = true;
}
}
DebugContext.get(ctx).onInstruction(ctx, this, instr, null, error, caught);
DebugContext.get(env).onInstruction(env, this, instr, null, error, caught);
throw error;
}
if (returnValue != Values.NO_RETURN) {
DebugContext.get(ctx).onInstruction(ctx, this, instr, returnValue, null, false);
DebugContext.get(env).onInstruction(env, this, instr, returnValue, null, false);
return returnValue;
}
return Values.NO_RETURN;
}
public void onPush() {
DebugContext.get(ctx).onFramePush(ctx, this);
/**
* Executes the next instruction in the frame
*/
public Object next() {
return next(Values.NO_RETURN, Values.NO_RETURN, null);
}
public void onPop() {
DebugContext.get(ctx.parent).onFramePop(ctx.parent, this);
/**
* Induces a value on the stack (as if it were returned by the last function call)
* and executes the next instruction in the frame.
*
* @param value The value to induce
*/
public Object next(Object value) {
return next(value, Values.NO_RETURN, null);
}
/**
* Induces a thrown error and executes the next instruction.
* Note that this is different than just throwing the error outside the
* function, as the function executed could have a try-catch which
* would otherwise handle the error
*
* @param error The error to induce
*/
public Object induceError(EngineException error) {
return next(Values.NO_RETURN, Values.NO_RETURN, error);
}
/**
* Induces a return, as if there was a return statement before
* the currently executed instruction and executes the next instruction.
* Note that this is different than just returning the value outside the
* function, as the function executed could have a try-catch which
* would otherwise handle the error
*
* @param value The retunr value to induce
*/
public Object induceReturn(Object value) {
return next(Values.NO_RETURN, value, null);
}
public void onPush() {
DebugContext.get(env).onFramePush(env, this);
}
public void onPop() {
DebugContext.get(env).onFramePop(env, this);
}
/**
* Gets an object proxy of the local scope
*/
public ObjectValue getLocalScope() {
var names = new String[scope.locals.length];
var map = DebugContext.get(ctx).getMapOrEmpty(function);
var map = DebugContext.get(env).getMapOrEmpty(function);
for (int i = 0; i < scope.locals.length; i++) {
var name = "local_" + (i - 2);
@ -279,9 +328,12 @@ public class Frame {
return new ScopeValue(scope.locals, names);
}
/**
* Gets an object proxy of the capture scope
*/
public ObjectValue getCaptureScope() {
var names = new String[scope.captures.length];
var map = DebugContext.get(ctx).getMapOrEmpty(function);
var map = DebugContext.get(env).getMapOrEmpty(function);
for (int i = 0; i < scope.captures.length; i++) {
var name = "capture_" + (i - 2);
@ -291,16 +343,19 @@ public class Frame {
return new ScopeValue(scope.captures, names);
}
/**
* Gets an array proxy of the local scope
*/
public ObjectValue getValStackScope() {
return new ObjectValue() {
@Override
protected Object getField(Extensions ext, Object key) {
protected Object getField(Environment ext, Object key) {
var i = (int)Values.toNumber(ext, key);
if (i < 0 || i >= stackPtr) return null;
else return stack[i];
}
@Override
protected boolean hasField(Extensions ext, Object key) {
protected boolean hasField(Environment ext, Object key) {
return true;
}
@Override
@ -312,18 +367,18 @@ public class Frame {
};
}
public Frame(Context ctx, Object thisArg, Object[] args, CodeFunction func) {
public Frame(Environment env, Object thisArg, Object[] args, CodeFunction func) {
this.env = env;
this.args = args.clone();
this.scope = new LocalScope(func.body.localsN, func.captures);
this.scope.get(0).set(null, thisArg);
var argsObj = new ArrayValue();
for (var i = 0; i < args.length; i++) {
argsObj.set(ctx, i, args[i]);
argsObj.set(env, i, args[i]);
}
this.scope.get(1).value = argsObj;
this.thisArg = thisArg;
this.function = func;
this.ctx = ctx.pushFrame(this);
}
}

View File

@ -4,6 +4,7 @@ import java.util.Collections;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.scope.GlobalScope;
import me.topchetoeu.jscript.runtime.scope.ValueVariable;
@ -15,17 +16,17 @@ import me.topchetoeu.jscript.runtime.values.Symbol;
import me.topchetoeu.jscript.runtime.values.Values;
public class InstructionRunner {
private static Object execReturn(Extensions ext, Instruction instr, Frame frame) {
private static Object execReturn(Environment ext, Instruction instr, Frame frame) {
return frame.pop();
}
private static Object execThrow(Extensions ext, Instruction instr, Frame frame) {
private static Object execThrow(Environment ext, Instruction instr, Frame frame) {
throw new EngineException(frame.pop());
}
private static Object execThrowSyntax(Extensions ext, Instruction instr, Frame frame) {
private static Object execThrowSyntax(Environment ext, Instruction instr, Frame frame) {
throw EngineException.ofSyntax((String)instr.get(0));
}
private static Object execCall(Extensions ext, Instruction instr, Frame frame) {
private static Object execCall(Environment ext, Instruction instr, Frame frame) {
var callArgs = frame.take(instr.get(0));
var func = frame.pop();
var thisArg = frame.pop();
@ -35,7 +36,7 @@ public class InstructionRunner {
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execCallNew(Extensions ext, Instruction instr, Frame frame) {
private static Object execCallNew(Environment ext, Instruction instr, Frame frame) {
var callArgs = frame.take(instr.get(0));
var funcObj = frame.pop();
@ -45,13 +46,13 @@ public class InstructionRunner {
return Values.NO_RETURN;
}
private static Object execMakeVar(Extensions ext, Instruction instr, Frame frame) {
private static Object execMakeVar(Environment ext, Instruction instr, Frame frame) {
var name = (String)instr.get(0);
GlobalScope.get(ext).define(ext, name);
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execDefProp(Extensions ext, Instruction instr, Frame frame) {
private static Object execDefProp(Environment ext, Instruction instr, Frame frame) {
var setter = frame.pop();
var getter = frame.pop();
var name = frame.pop();
@ -66,7 +67,7 @@ public class InstructionRunner {
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execKeys(Extensions ext, Instruction instr, Frame frame) {
private static Object execKeys(Environment ext, Instruction instr, Frame frame) {
var val = frame.pop();
var members = Values.getMembers(ext, val, false, false);
@ -85,7 +86,7 @@ public class InstructionRunner {
return Values.NO_RETURN;
}
private static Object execTryStart(Extensions ext, Instruction instr, Frame frame) {
private static Object execTryStart(Environment ext, Instruction instr, Frame frame) {
int start = frame.codePtr + 1;
int catchStart = (int)instr.get(0);
int finallyStart = (int)instr.get(1);
@ -96,12 +97,12 @@ public class InstructionRunner {
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execTryEnd(Extensions ext, Instruction instr, Frame frame) {
private static Object execTryEnd(Environment ext, Instruction instr, Frame frame) {
frame.popTryFlag = true;
return Values.NO_RETURN;
}
private static Object execDup(Extensions ext, Instruction instr, Frame frame) {
private static Object execDup(Environment ext, Instruction instr, Frame frame) {
int count = instr.get(0);
for (var i = 0; i < count; i++) {
@ -111,7 +112,7 @@ public class InstructionRunner {
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execLoadValue(Extensions ext, Instruction instr, Frame frame) {
private static Object execLoadValue(Environment ext, Instruction instr, Frame frame) {
switch (instr.type) {
case PUSH_UNDEFINED: frame.push(null); break;
case PUSH_NULL: frame.push(Values.NULL); break;
@ -121,7 +122,7 @@ public class InstructionRunner {
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execLoadVar(Extensions ext, Instruction instr, Frame frame) {
private static Object execLoadVar(Environment ext, Instruction instr, Frame frame) {
var i = instr.get(0);
if (i instanceof String) frame.push(GlobalScope.get(ext).get(ext, (String)i));
@ -130,24 +131,24 @@ public class InstructionRunner {
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execLoadObj(Extensions ext, Instruction instr, Frame frame) {
private static Object execLoadObj(Environment ext, Instruction instr, Frame frame) {
frame.push(new ObjectValue());
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execLoadGlob(Extensions ext, Instruction instr, Frame frame) {
private static Object execLoadGlob(Environment ext, Instruction instr, Frame frame) {
frame.push(GlobalScope.get(ext).obj);
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execLoadArr(Extensions ext, Instruction instr, Frame frame) {
private static Object execLoadArr(Environment ext, Instruction instr, Frame frame) {
var res = new ArrayValue();
res.setSize(instr.get(0));
frame.push(res);
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execLoadFunc(Extensions ext, Instruction instr, Frame frame) {
private static Object execLoadFunc(Environment ext, Instruction instr, Frame frame) {
int id = instr.get(0);
var captures = new ValueVariable[instr.params.length - 1];
@ -162,7 +163,7 @@ public class InstructionRunner {
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execLoadMember(Extensions ext, Instruction instr, Frame frame) {
private static Object execLoadMember(Environment ext, Instruction instr, Frame frame) {
var key = frame.pop();
var obj = frame.pop();
@ -175,7 +176,7 @@ public class InstructionRunner {
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execLoadRegEx(Extensions ext, Instruction instr, Frame frame) {
private static Object execLoadRegEx(Environment ext, Instruction instr, Frame frame) {
if (ext.hasNotNull(Environment.REGEX_CONSTR)) {
frame.push(Values.callNew(ext, ext.get(Environment.REGEX_CONSTR), instr.get(0), instr.get(1)));
}
@ -186,12 +187,12 @@ public class InstructionRunner {
return Values.NO_RETURN;
}
private static Object execDiscard(Extensions ext, Instruction instr, Frame frame) {
private static Object execDiscard(Environment ext, Instruction instr, Frame frame) {
frame.pop();
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execStoreMember(Extensions ext, Instruction instr, Frame frame) {
private static Object execStoreMember(Environment ext, Instruction instr, Frame frame) {
var val = frame.pop();
var key = frame.pop();
var obj = frame.pop();
@ -201,7 +202,7 @@ public class InstructionRunner {
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execStoreVar(Extensions ext, Instruction instr, Frame frame) {
private static Object execStoreVar(Environment ext, Instruction instr, Frame frame) {
var val = (boolean)instr.get(1) ? frame.peek() : frame.pop();
var i = instr.get(0);
@ -211,18 +212,18 @@ public class InstructionRunner {
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execStoreSelfFunc(Extensions ext, Instruction instr, Frame frame) {
private static Object execStoreSelfFunc(Environment ext, Instruction instr, Frame frame) {
frame.scope.locals[(int)instr.get(0)].set(ext, frame.function);
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execJmp(Extensions ext, Instruction instr, Frame frame) {
private static Object execJmp(Environment ext, Instruction instr, Frame frame) {
frame.codePtr += (int)instr.get(0);
frame.jumpFlag = true;
return Values.NO_RETURN;
}
private static Object execJmpIf(Extensions ext, Instruction instr, Frame frame) {
private static Object execJmpIf(Environment ext, Instruction instr, Frame frame) {
if (Values.toBoolean(frame.pop())) {
frame.codePtr += (int)instr.get(0);
frame.jumpFlag = true;
@ -230,7 +231,7 @@ public class InstructionRunner {
else frame.codePtr ++;
return Values.NO_RETURN;
}
private static Object execJmpIfNot(Extensions ext, Instruction instr, Frame frame) {
private static Object execJmpIfNot(Environment ext, Instruction instr, Frame frame) {
if (Values.not(frame.pop())) {
frame.codePtr += (int)instr.get(0);
frame.jumpFlag = true;
@ -239,7 +240,7 @@ public class InstructionRunner {
return Values.NO_RETURN;
}
private static Object execTypeof(Extensions ext, Instruction instr, Frame frame) {
private static Object execTypeof(Environment ext, Instruction instr, Frame frame) {
String name = instr.get(0);
Object obj;
@ -256,12 +257,12 @@ public class InstructionRunner {
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execNop(Extensions ext, Instruction instr, Frame frame) {
private static Object execNop(Environment ext, Instruction instr, Frame frame) {
frame.codePtr++;
return Values.NO_RETURN;
}
private static Object execDelete(Extensions ext, Instruction instr, Frame frame) {
private static Object execDelete(Environment ext, Instruction instr, Frame frame) {
var key = frame.pop();
var val = frame.pop();
@ -270,7 +271,7 @@ public class InstructionRunner {
return Values.NO_RETURN;
}
private static Object execOperation(Extensions ext, Instruction instr, Frame frame) {
private static Object execOperation(Environment ext, Instruction instr, Frame frame) {
Operation op = instr.get(0);
var args = new Object[op.operands];
@ -281,7 +282,7 @@ public class InstructionRunner {
return Values.NO_RETURN;
}
public static Object exec(Extensions ext, Instruction instr, Frame frame) {
public static Object exec(Environment ext, Instruction instr, Frame frame) {
switch (instr.type) {
case NOP: return execNop(ext, instr, frame);
case RETURN: return execReturn(ext, instr, frame);

View File

@ -1,5 +0,0 @@
package me.topchetoeu.jscript.runtime;
public class Key<T> {
}

View File

@ -1,5 +1,6 @@
package me.topchetoeu.jscript.runtime;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
import me.topchetoeu.jscript.runtime.values.ObjectValue;

View File

@ -10,10 +10,9 @@ import me.topchetoeu.jscript.common.FunctionBody;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.mapping.FunctionMap;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.Frame;
import me.topchetoeu.jscript.runtime.Key;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.environment.Key;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.CodeFunction;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
@ -71,15 +70,19 @@ public class DebugContext {
if (maps == null || !(func instanceof CodeFunction)) return FunctionMap.EMPTY;
return getMapOrEmpty(((CodeFunction)func).body);
}
public List<Frame> getStackFrames() {
return this.debugger.getStackFrame();
}
public void onFramePop(Context ctx, Frame frame) {
if (debugger != null) debugger.onFramePop(ctx, frame);
public void onFramePop(Environment env, Frame frame) {
if (debugger != null) debugger.onFramePop(env, frame);
}
public void onFramePush(Context ctx, Frame frame) {
if (debugger != null) debugger.onFramePush(ctx, frame);
public void onFramePush(Environment env, Frame frame) {
if (debugger != null) debugger.onFramePush(env, frame);
}
public boolean onInstruction(Context ctx, Frame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) {
if (debugger != null) return debugger.onInstruction(ctx, frame, instruction, returnVal, error, caught);
public boolean onInstruction(Environment env, Frame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) {
if (debugger != null) return debugger.onInstruction(env, frame, instruction, returnVal, error, caught);
else return false;
}
public void onSource(Filename filename, String source) {
@ -102,26 +105,26 @@ public class DebugContext {
this(true);
}
public static boolean enabled(Extensions exts) {
public static boolean enabled(Environment exts) {
return exts != null && exts.hasNotNull(KEY) && !exts.has(IGNORE);
}
public static DebugContext get(Extensions exts) {
public static DebugContext get(Environment exts) {
if (enabled(exts)) return exts.get(KEY);
else return new DebugContext(false);
}
public static List<String> stackTrace(Context ctx) {
public static List<String> stackTrace(Environment env) {
var res = new ArrayList<String>();
var dbgCtx = get(ctx);
var dbgCtx = get(env);
for (var el : ctx.frames()) {
var name = el.function.name;
for (var frame : dbgCtx.getStackFrames()) {
var name = frame.function.name;
var map = dbgCtx.getMapOrEmpty(el.function);
var map = dbgCtx.getMapOrEmpty(frame.function);
Location loc = null;
if (map != null) {
loc = map.toLocation(el.codePtr, true);
loc = map.toLocation(frame.codePtr, true);
if (loc == null) loc = map.start();
}

View File

@ -1,11 +1,13 @@
package me.topchetoeu.jscript.runtime.debug;
import java.util.List;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.FunctionBody;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.mapping.FunctionMap;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Frame;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
public interface DebugHandler {
@ -35,7 +37,7 @@ public interface DebugHandler {
/**
* Called immediatly before an instruction is executed, as well as after an instruction, if it has threw or returned.
* This function might pause in order to await debugging commands.
* @param ctx The context of execution
* @param env The context of execution
* @param frame The frame in which execution is occuring
* @param instruction The instruction which was or will be executed
* @param returnVal The return value of the instruction, Values.NO_RETURN if none
@ -43,32 +45,35 @@ public interface DebugHandler {
* @param caught Whether or not the error has been caught
* @return Whether or not the frame should restart (currently does nothing)
*/
boolean onInstruction(Context ctx, Frame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught);
boolean onInstruction(Environment env, Frame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught);
/**
* Called immediatly before a frame has been pushed on the frame stack.
* This function might pause in order to await debugging commands.
* @param ctx The context of execution
* @param env The context of execution
* @param frame The code frame which was pushed
*/
void onFramePush(Context ctx, Frame frame);
void onFramePush(Environment env, Frame frame);
/**
* Called immediatly after a frame has been popped out of the frame stack.
* This function might pause in order to await debugging commands.
* @param ctx The context of execution
* @param env The context of execution
* @param frame The code frame which was popped out
*/
void onFramePop(Context ctx, Frame frame);
void onFramePop(Environment env, Frame frame);
List<Frame> getStackFrame();
public static DebugHandler empty() {
return new DebugHandler () {
@Override public void onFramePop(Context ctx, Frame frame) { }
@Override public void onFramePush(Context ctx, Frame frame) { }
@Override public boolean onInstruction(Context ctx, Frame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) {
@Override public void onFramePop(Environment env, Frame frame) { }
@Override public void onFramePush(Environment env, Frame frame) { }
@Override public boolean onInstruction(Environment env, Frame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) {
return false;
}
@Override public void onSourceLoad(Filename filename, String source) { }
@Override public void onFunctionLoad(FunctionBody body, FunctionMap map) { }
@Override public List<Frame> getStackFrame() { return List.of(); }
};
}
}

View File

@ -0,0 +1,144 @@
package me.topchetoeu.jscript.runtime.environment;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.function.Supplier;
import me.topchetoeu.jscript.runtime.Compiler;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
public class Environment {
public static final Key<Compiler> COMPILE_FUNC = new Key<>();
public static final Key<FunctionValue> REGEX_CONSTR = new Key<>();
public static final Key<Integer> MAX_STACK_COUNT = new Key<>();
public static final Key<Boolean> HIDE_STACK = new Key<>();
public static final Key<ObjectValue> OBJECT_PROTO = new Key<>();
public static final Key<ObjectValue> FUNCTION_PROTO = new Key<>();
public static final Key<ObjectValue> ARRAY_PROTO = new Key<>();
public static final Key<ObjectValue> BOOL_PROTO = new Key<>();
public static final Key<ObjectValue> NUMBER_PROTO = new Key<>();
public static final Key<ObjectValue> STRING_PROTO = new Key<>();
public static final Key<ObjectValue> SYMBOL_PROTO = new Key<>();
public static final Key<ObjectValue> ERROR_PROTO = new Key<>();
public static final Key<ObjectValue> SYNTAX_ERR_PROTO = new Key<>();
public static final Key<ObjectValue> TYPE_ERR_PROTO = new Key<>();
public static final Key<ObjectValue> RANGE_ERR_PROTO = new Key<>();
public final Environment parent;
private final Map<Key<Object>, Object> map = new HashMap<>();
private final Set<Key<Object>> hidden = new HashSet<>();
@SuppressWarnings("unchecked")
public <T> T get(Key<T> key) {
if (map.containsKey(key)) return (T)map.get(key);
else if (!hidden.contains(key) && parent != null) return parent.get(key);
else return null;
}
public boolean has(Key<?> key) {
if (map.containsKey(key)) return true;
else if (!hidden.contains(key) && parent != null) return parent.has(key);
else return false;
}
@SuppressWarnings("all")
public Set<Key<?>> keys() {
if (parent != null) {
if (map.size() == 0) return (Set)map.keySet();
var res = new HashSet();
res.addAll(parent.keys());
res.addAll(map.keySet());
return res;
}
else return (Set)map.keySet();
}
public boolean hasNotNull(Key<?> key) {
return get(key) != null;
}
public <T> T get(Key<T> key, T defaultVal) {
if (has(key)) return get(key);
else return defaultVal;
}
public <T> T get(Key<T> key, Supplier<T> defaultVal) {
if (has(key)) return get(key);
else return defaultVal.get();
}
@SuppressWarnings("unchecked")
public <T> Environment add(Key<T> key, T val) {
map.put((Key<Object>)key, val);
hidden.remove(key);
return this;
}
@SuppressWarnings("unchecked")
public Environment add(Key<Void> key) {
map.put((Key<Object>)(Key<?>)key, null);
hidden.remove(key);
return this;
}
@SuppressWarnings("all")
public Environment addAll(Map<Key<?>, ?> map) {
map.putAll((Map)map);
hidden.removeAll(map.keySet());
return this;
}
public Environment addAll(Environment env) {
this.map.putAll(env.map);
this.hidden.removeAll(env.map.keySet());
return this;
}
@SuppressWarnings("unchecked")
public Environment remove(Key<?> key) {
map.remove((Key<Object>)key);
hidden.add((Key<Object>)key);
return this;
}
public <T> Environment init(Key<T> key, T val) {
if (!has(key)) this.add(key, val);
return this;
}
public <T> Environment init(Key<T> key, Supplier<T> val) {
if (!has(key)) this.add(key, val.get());
return this;
}
public Environment child() {
return new Environment(this);
}
public Environment(Environment parent) {
this.parent = parent;
}
public Environment() {
this.parent = null;
}
public static Environment wrap(Environment ext) {
if (ext == null) return empty();
else return ext;
}
// public static Environment chain(int id, Environment ...envs) {
// var res = new Environment();
// for (var env : envs) res.addAll(env);
// return res;
// }
public static Environment empty() {
return new Environment();
}
public static int nextId() {
return new Random().nextInt();
}
}

View File

@ -0,0 +1,5 @@
package me.topchetoeu.jscript.runtime.environment;
public class Key<T> {
}

View File

@ -4,9 +4,7 @@ import java.util.ArrayList;
import java.util.List;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Environment;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.runtime.values.ObjectValue.PlaceholderProto;
@ -15,7 +13,7 @@ public class EngineException extends RuntimeException {
public static class StackElement {
public final Location location;
public final String name;
public final Extensions ext;
public final Environment ext;
public boolean visible() {
return ext == null || !ext.get(Environment.HIDE_STACK, false);
@ -30,12 +28,12 @@ public class EngineException extends RuntimeException {
return res.trim();
}
public StackElement(Extensions ext, Location location, String name) {
public StackElement(Environment ext, Location location, String name) {
if (name != null) name = name.trim();
if (name.equals("")) name = null;
if (ext == null) this.ext = null;
else this.ext = Context.clean(ext);
else this.ext = ext;
this.location = location;
this.name = name;
@ -44,13 +42,13 @@ public class EngineException extends RuntimeException {
public final Object value;
public EngineException cause;
public Extensions ext = null;
public Environment env = null;
public final List<StackElement> stackTrace = new ArrayList<>();
public EngineException add(Extensions ext, String name, Location location) {
var el = new StackElement(ext, location, name);
public EngineException add(Environment env, String name, Location location) {
var el = new StackElement(env, location, name);
if (el.name == null && el.location == null) return this;
setExtensions(ext);
setEnvironment(env);
stackTrace.add(el);
return this;
}
@ -58,12 +56,12 @@ public class EngineException extends RuntimeException {
this.cause = cause;
return this;
}
public EngineException setExtensions(Extensions ext) {
if (this.ext == null) this.ext = Context.clean(ext);
public EngineException setEnvironment(Environment env) {
if (this.env == null) this.env = env;
return this;
}
public String toString(Extensions ext) {
public String toString(Environment ext) {
var ss = new StringBuilder();
try {
ss.append(Values.toString(ext, value)).append('\n');

View File

@ -3,8 +3,8 @@ package me.topchetoeu.jscript.runtime.scope;
import java.util.HashSet;
import java.util.Set;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.Key;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.environment.Key;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
import me.topchetoeu.jscript.runtime.values.NativeFunction;
@ -16,7 +16,7 @@ public class GlobalScope {
public final ObjectValue obj;
public boolean has(Extensions ext, String name) {
public boolean has(Environment ext, String name) {
return Values.hasMember(ext, obj, name, false);
}
@ -26,33 +26,33 @@ public class GlobalScope {
return new GlobalScope(obj);
}
public Object define(Extensions ext, String name) {
public Object define(Environment ext, String name) {
if (Values.hasMember(ext, obj, name, false)) return name;
obj.defineProperty(ext, name, null);
return name;
}
public void define(Extensions ext, String name, Variable val) {
public void define(Environment ext, String name, Variable val) {
obj.defineProperty(ext, name,
new NativeFunction("get " + name, args -> val.get(args.ctx)),
new NativeFunction("set " + name, args -> { val.set(args.ctx, args.get(0)); return null; }),
new NativeFunction("get " + name, args -> val.get(args.env)),
new NativeFunction("set " + name, args -> { val.set(args.env, args.get(0)); return null; }),
true, true
);
}
public void define(Extensions ext, String name, boolean readonly, Object val) {
public void define(Environment ext, String name, boolean readonly, Object val) {
obj.defineProperty(ext, name, val, readonly, true, true);
}
public void define(Extensions ext, String ...names) {
public void define(Environment ext, String ...names) {
for (var n : names) define(ext, n);
}
public void define(Extensions ext, boolean readonly, FunctionValue val) {
public void define(Environment ext, boolean readonly, FunctionValue val) {
define(ext, val.name, readonly, val);
}
public Object get(Extensions ext, String name) {
public Object get(Environment ext, String name) {
if (!Values.hasMember(ext, obj, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist.");
else return Values.getMember(ext, obj, name);
}
public void set(Extensions ext, String name, Object val) {
public void set(Environment ext, String name, Object val) {
if (!Values.hasMember(ext, obj, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist.");
if (!Values.setMember(ext, obj, name, val)) throw EngineException.ofSyntax("The global '" + name + "' is readonly.");
}
@ -74,7 +74,7 @@ public class GlobalScope {
this.obj = val;
}
public static GlobalScope get(Extensions ext) {
public static GlobalScope get(Environment ext) {
if (ext.has(KEY)) return ext.get(KEY);
else return new GlobalScope();
}

View File

@ -1,6 +1,6 @@
package me.topchetoeu.jscript.runtime.scope;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.values.Values;
public class ValueVariable implements Variable {
@ -11,12 +11,12 @@ public class ValueVariable implements Variable {
public boolean readonly() { return readonly; }
@Override
public Object get(Extensions ext) {
public Object get(Environment ext) {
return value;
}
@Override
public void set(Extensions ext, Object val) {
public void set(Environment ext, Object val) {
if (readonly) return;
this.value = Values.normalize(ext, val);
}

View File

@ -1,9 +1,9 @@
package me.topchetoeu.jscript.runtime.scope;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.environment.Environment;
public interface Variable {
Object get(Extensions ext);
Object get(Environment ext);
default boolean readonly() { return true; }
default void set(Extensions ext, Object val) { }
default void set(Environment ext, Object val) { }
}

View File

@ -6,7 +6,7 @@ import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.environment.Environment;
// TODO: Make methods generic
public class ArrayValue extends ObjectValue implements Iterable<Object> {
@ -41,7 +41,7 @@ public class ArrayValue extends ObjectValue implements Iterable<Object> {
if (res == UNDEFINED) return null;
else return res;
}
public void set(Extensions ext, int i, Object val) {
public void set(Environment ext, int i, Object val) {
if (i < 0) return;
values = alloc(i);
@ -99,7 +99,7 @@ public class ArrayValue extends ObjectValue implements Iterable<Object> {
}
}
public void copyFrom(Extensions ext, Object[] arr, int sourceStart, int destStart, int count) {
public void copyFrom(Environment ext, Object[] arr, int sourceStart, int destStart, int count) {
for (var i = 0; i < count; i++) {
set(ext, i + destStart, arr[i + sourceStart]);
}
@ -131,7 +131,7 @@ public class ArrayValue extends ObjectValue implements Iterable<Object> {
}
@Override
protected Object getField(Extensions ext, Object key) {
protected Object getField(Environment ext, Object key) {
if (key instanceof Number) {
var i = ((Number)key).doubleValue();
if (i >= 0 && i - Math.floor(i) == 0) {
@ -142,7 +142,7 @@ public class ArrayValue extends ObjectValue implements Iterable<Object> {
return super.getField(ext, key);
}
@Override
protected boolean setField(Extensions ext, Object key, Object val) {
protected boolean setField(Environment ext, Object key, Object val) {
if (key instanceof Number) {
var i = Values.number(key);
if (i >= 0 && i - Math.floor(i) == 0) {
@ -154,7 +154,7 @@ public class ArrayValue extends ObjectValue implements Iterable<Object> {
return super.setField(ext, key, val);
}
@Override
protected boolean hasField(Extensions ext, Object key) {
protected boolean hasField(Environment ext, Object key) {
if (key instanceof Number) {
var i = Values.number(key);
if (i >= 0 && i - Math.floor(i) == 0) {
@ -165,7 +165,7 @@ public class ArrayValue extends ObjectValue implements Iterable<Object> {
return super.hasField(ext, key);
}
@Override
protected void deleteField(Extensions ext, Object key) {
protected void deleteField(Environment ext, Object key) {
if (key instanceof Number) {
var i = Values.number(key);
if (i >= 0 && i - Math.floor(i) == 0) {
@ -213,7 +213,7 @@ public class ArrayValue extends ObjectValue implements Iterable<Object> {
values = new Object[cap];
size = 0;
}
public ArrayValue(Extensions ext, Object ...values) {
public ArrayValue(Environment ext, Object ...values) {
this();
this.values = new Object[values.length];
size = values.length;
@ -221,7 +221,7 @@ public class ArrayValue extends ObjectValue implements Iterable<Object> {
for (var i = 0; i < size; i++) this.values[i] = Values.normalize(ext, values[i]);
}
public static ArrayValue of(Extensions ext, Collection<?> values) {
public static ArrayValue of(Environment ext, Collection<?> values) {
return new ArrayValue(ext, values.toArray(Object[]::new));
}
}

View File

@ -1,15 +1,14 @@
package me.topchetoeu.jscript.runtime.values;
import me.topchetoeu.jscript.common.FunctionBody;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.Frame;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.scope.ValueVariable;
public class CodeFunction extends FunctionValue {
public final FunctionBody body;
public final ValueVariable[] captures;
public Extensions extensions;
public Environment env;
// public Location loc() {
// for (var instr : body.instructions) {
@ -24,15 +23,13 @@ public class CodeFunction extends FunctionValue {
// else return name + "@" + loc;
// }
@Override
public Object call(Extensions ext, Object thisArg, Object ...args) {
var frame = new Frame(Context.of(ext), thisArg, args, this);
@Override public Object call(Environment env, Object thisArg, Object ...args) {
var frame = new Frame(env, thisArg, args, this);
frame.onPush();
try {
while (true) {
var res = frame.next(Values.NO_RETURN, Values.NO_RETURN, null);
var res = frame.next();
if (res != Values.NO_RETURN) return res;
}
}
@ -41,10 +38,10 @@ public class CodeFunction extends FunctionValue {
}
}
public CodeFunction(Extensions extensions, String name, FunctionBody body, ValueVariable[] captures) {
public CodeFunction(Environment env, String name, FunctionBody body, ValueVariable[] captures) {
super(name, body.argsN);
this.captures = captures;
this.extensions = Context.clean(extensions);
this.env = env;
this.body = body;
}
}

View File

@ -2,7 +2,7 @@ package me.topchetoeu.jscript.runtime.values;
import java.util.List;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.environment.Environment;
public abstract class FunctionValue extends ObjectValue {
public String name = "";
@ -13,26 +13,26 @@ public abstract class FunctionValue extends ObjectValue {
return String.format("function %s(...)", name);
}
public abstract Object call(Extensions ext, Object thisArg, Object ...args);
public Object call(Extensions ext) {
public abstract Object call(Environment ext, Object thisArg, Object ...args);
public Object call(Environment ext) {
return call(ext, null);
}
@Override
protected Object getField(Extensions ext, Object key) {
protected Object getField(Environment ext, Object key) {
if ("name".equals(key)) return name;
if ("length".equals(key)) return length;
return super.getField(ext, key);
}
@Override
protected boolean setField(Extensions ext, Object key, Object val) {
protected boolean setField(Environment ext, Object key, Object val) {
if ("name".equals(key)) name = Values.toString(ext, val);
else if ("length".equals(key)) length = (int)Values.toNumber(ext, val);
else return super.setField(ext, key, val);
return true;
}
@Override
protected boolean hasField(Extensions ext, Object key) {
protected boolean hasField(Environment ext, Object key) {
if ("name".equals(key)) return true;
if ("length".equals(key)) return true;
return super.hasField(ext, key);

View File

@ -1,7 +1,6 @@
package me.topchetoeu.jscript.runtime.values;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.utils.interop.Arguments;
public class NativeFunction extends FunctionValue {
@ -12,8 +11,8 @@ public class NativeFunction extends FunctionValue {
public final NativeFunctionRunner action;
@Override
public Object call(Extensions ext, Object thisArg, Object ...args) {
return action.run(new Arguments(Context.of(ext), thisArg, args));
public Object call(Environment env, Object thisArg, Object ...args) {
return action.run(new Arguments(env, thisArg, args));
}
public NativeFunction(String name, NativeFunctionRunner action) {

View File

@ -2,8 +2,8 @@ package me.topchetoeu.jscript.runtime.values;
import java.util.WeakHashMap;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.Key;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.environment.Key;
import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider;
public class NativeWrapper extends ObjectValue {
@ -28,12 +28,12 @@ public class NativeWrapper extends ObjectValue {
}
}
private static final Key<WeakHashMap<MapKey, NativeWrapper>> WRAPPERS = new Key<>();
private static final Key<WeakHashMap<MapKey, NativeWrapper>> WRAPPER_MAP = new Key<>();
private static final Object NATIVE_PROTO = new Object();
public final Object wrapped;
@Override
public ObjectValue getPrototype(Extensions ext) {
public ObjectValue getPrototype(Environment ext) {
if (ext != null && prototype == NATIVE_PROTO) {
var clazz = wrapped.getClass();
var res = NativeWrapperProvider.get(ext).getProto(clazz);
@ -60,17 +60,13 @@ public class NativeWrapper extends ObjectValue {
prototype = NATIVE_PROTO;
}
public static NativeWrapper of(Extensions exts, Object wrapped) {
if (exts == null) return new NativeWrapper(wrapped);
var wrappers = exts.get(WRAPPERS);
public static NativeWrapper of(Environment env, Object wrapped) {
if (env == null) return new NativeWrapper(wrapped);
if (wrappers == null) {
wrappers = new WeakHashMap<>();
exts.add(WRAPPERS, wrappers);
}
var wrappers = env.get(WRAPPER_MAP);
if (wrappers == null) return new NativeWrapper(wrapped);
var key = new MapKey(wrapped);
if (wrappers.containsKey(key)) return wrappers.get(key);
var res = new NativeWrapper(wrapped);

View File

@ -6,8 +6,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import me.topchetoeu.jscript.runtime.Environment;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.environment.Environment;
public class ObjectValue {
public static enum PlaceholderProto {
@ -54,10 +53,10 @@ public class ObjectValue {
public LinkedHashSet<Object> nonConfigurableSet = new LinkedHashSet<>();
public LinkedHashSet<Object> nonEnumerableSet = new LinkedHashSet<>();
private Property getProperty(Extensions ext, Object key) {
private Property getProperty(Environment env, Object key) {
if (properties.containsKey(key)) return properties.get(key);
var proto = getPrototype(ext);
if (proto != null) return proto.getProperty(ext, key);
var proto = getPrototype(env);
if (proto != null) return proto.getProperty(env, key);
else return null;
}
@ -86,8 +85,8 @@ public class ObjectValue {
state = State.FROZEN;
}
public final boolean defineProperty(Extensions ext, Object key, Object val, boolean writable, boolean configurable, boolean enumerable) {
key = Values.normalize(ext, key); val = Values.normalize(ext, val);
public final boolean defineProperty(Environment env, Object key, Object val, boolean writable, boolean configurable, boolean enumerable) {
key = Values.normalize(env, key); val = Values.normalize(env, val);
boolean reconfigured =
writable != memberWritable(key) ||
configurable != memberConfigurable(key) ||
@ -125,11 +124,11 @@ public class ObjectValue {
values.put(key, val);
return true;
}
public final boolean defineProperty(Extensions ext, Object key, Object val) {
return defineProperty(ext, key, val, true, true, true);
public final boolean defineProperty(Environment env, Object key, Object val) {
return defineProperty(env, key, val, true, true, true);
}
public final boolean defineProperty(Extensions ext, Object key, FunctionValue getter, FunctionValue setter, boolean configurable, boolean enumerable) {
key = Values.normalize(ext, key);
public final boolean defineProperty(Environment env, Object key, FunctionValue getter, FunctionValue setter, boolean configurable, boolean enumerable) {
key = Values.normalize(env, key);
if (
properties.containsKey(key) &&
properties.get(key).getter == getter &&
@ -152,17 +151,17 @@ public class ObjectValue {
return true;
}
public ObjectValue getPrototype(Extensions ext) {
public ObjectValue getPrototype(Environment env) {
if (prototype instanceof ObjectValue || prototype == null) return (ObjectValue)prototype;
try {
if (prototype == ARR_PROTO) return ext.get(Environment.ARRAY_PROTO);
if (prototype == FUNC_PROTO) return ext.get(Environment.FUNCTION_PROTO);
if (prototype == ERR_PROTO) return ext.get(Environment.ERROR_PROTO);
if (prototype == RANGE_ERR_PROTO) return ext.get(Environment.RANGE_ERR_PROTO);
if (prototype == SYNTAX_ERR_PROTO) return ext.get(Environment.SYNTAX_ERR_PROTO);
if (prototype == TYPE_ERR_PROTO) return ext.get(Environment.TYPE_ERR_PROTO);
return ext.get(Environment.OBJECT_PROTO);
if (prototype == ARR_PROTO) return env.get(Environment.ARRAY_PROTO);
if (prototype == FUNC_PROTO) return env.get(Environment.FUNCTION_PROTO);
if (prototype == ERR_PROTO) return env.get(Environment.ERROR_PROTO);
if (prototype == RANGE_ERR_PROTO) return env.get(Environment.RANGE_ERR_PROTO);
if (prototype == SYNTAX_ERR_PROTO) return env.get(Environment.SYNTAX_ERR_PROTO);
if (prototype == TYPE_ERR_PROTO) return env.get(Environment.TYPE_ERR_PROTO);
return env.get(Environment.OBJECT_PROTO);
}
catch (NullPointerException e) { return null; }
}
@ -185,10 +184,10 @@ public class ObjectValue {
* A method, used to get the value of a field. If a property is bound to
* this key, but not a field, this method should return null.
*/
protected Object getField(Extensions ext, Object key) {
protected Object getField(Environment env, Object key) {
if (values.containsKey(key)) return values.get(key);
var proto = getPrototype(ext);
if (proto != null) return proto.getField(ext, key);
var proto = getPrototype(env);
if (proto != null) return proto.getField(env, key);
else return null;
}
/**
@ -196,9 +195,9 @@ public class ObjectValue {
* bound to this key, a new field should be created with the given value
* @return Whether or not the operation was successful
*/
protected boolean setField(Extensions ext, Object key, Object val) {
protected boolean setField(Environment env, Object key, Object val) {
if (val instanceof FunctionValue && ((FunctionValue)val).name.equals("")) {
((FunctionValue)val).name = Values.toString(ext, key);
((FunctionValue)val).name = Values.toString(env, key);
}
values.put(key, val);
@ -207,40 +206,40 @@ public class ObjectValue {
/**
* Deletes the field bound to the given key.
*/
protected void deleteField(Extensions ext, Object key) {
protected void deleteField(Environment env, Object key) {
values.remove(key);
}
/**
* Returns whether or not there is a field bound to the given key.
* This must ignore properties
*/
protected boolean hasField(Extensions ext, Object key) {
protected boolean hasField(Environment env, Object key) {
return values.containsKey(key);
}
public final Object getMember(Extensions ext, Object key, Object thisArg) {
key = Values.normalize(ext, key);
public final Object getMember(Environment env, Object key, Object thisArg) {
key = Values.normalize(env, key);
if ("__proto__".equals(key)) {
var res = getPrototype(ext);
var res = getPrototype(env);
return res == null ? Values.NULL : res;
}
var prop = getProperty(ext, key);
var prop = getProperty(env, key);
if (prop != null) {
if (prop.getter == null) return null;
else return prop.getter.call(ext, Values.normalize(ext, thisArg));
else return prop.getter.call(env, Values.normalize(env, thisArg));
}
else return getField(ext, key);
else return getField(env, key);
}
public final boolean setMember(Extensions ext, Object key, Object val, Object thisArg, boolean onlyProps) {
key = Values.normalize(ext, key); val = Values.normalize(ext, val);
public final boolean setMember(Environment env, Object key, Object val, Object thisArg, boolean onlyProps) {
key = Values.normalize(env, key); val = Values.normalize(env, val);
var prop = getProperty(ext, key);
var prop = getProperty(env, key);
if (prop != null) {
if (prop.setter == null) return false;
prop.setter.call(ext, Values.normalize(ext, thisArg), val);
prop.setter.call(env, Values.normalize(env, thisArg), val);
return true;
}
else if (onlyProps) return false;
@ -249,32 +248,32 @@ public class ObjectValue {
values.put(key, val);
return true;
}
else if ("__proto__".equals(key)) return setPrototype(ext, val);
else if ("__proto__".equals(key)) return setPrototype(env, val);
else if (nonWritableSet.contains(key)) return false;
else return setField(ext, key, val);
else return setField(env, key, val);
}
public final boolean hasMember(Extensions ext, Object key, boolean own) {
key = Values.normalize(ext, key);
public final boolean hasMember(Environment env, Object key, boolean own) {
key = Values.normalize(env, key);
if (key != null && "__proto__".equals(key)) return true;
if (hasField(ext, key)) return true;
if (hasField(env, key)) return true;
if (properties.containsKey(key)) return true;
if (own) return false;
var proto = getPrototype(ext);
return proto != null && proto.hasMember(ext, key, own);
var proto = getPrototype(env);
return proto != null && proto.hasMember(env, key, own);
}
public final boolean deleteMember(Extensions ext, Object key) {
key = Values.normalize(ext, key);
public final boolean deleteMember(Environment env, Object key) {
key = Values.normalize(env, key);
if (!memberConfigurable(key)) return false;
properties.remove(key);
nonWritableSet.remove(key);
nonEnumerableSet.remove(key);
deleteField(ext, key);
deleteField(env, key);
return true;
}
public final boolean setPrototype(Extensions ext, Object val) {
val = Values.normalize(ext, val);
public final boolean setPrototype(Environment env, Object val) {
val = Values.normalize(env, val);
if (!extensible()) return false;
if (val == null || val == Values.NULL) {
@ -284,14 +283,14 @@ public class ObjectValue {
else if (val instanceof ObjectValue) {
var obj = (ObjectValue)val;
if (ext != null) {
if (obj == ext.get(Environment.OBJECT_PROTO)) prototype = OBJ_PROTO;
else if (obj == ext.get(Environment.ARRAY_PROTO)) prototype = ARR_PROTO;
else if (obj == ext.get(Environment.FUNCTION_PROTO)) prototype = FUNC_PROTO;
else if (obj == ext.get(Environment.ERROR_PROTO)) prototype = ERR_PROTO;
else if (obj == ext.get(Environment.SYNTAX_ERR_PROTO)) prototype = SYNTAX_ERR_PROTO;
else if (obj == ext.get(Environment.TYPE_ERR_PROTO)) prototype = TYPE_ERR_PROTO;
else if (obj == ext.get(Environment.RANGE_ERR_PROTO)) prototype = RANGE_ERR_PROTO;
if (env != null) {
if (obj == env.get(Environment.OBJECT_PROTO)) prototype = OBJ_PROTO;
else if (obj == env.get(Environment.ARRAY_PROTO)) prototype = ARR_PROTO;
else if (obj == env.get(Environment.FUNCTION_PROTO)) prototype = FUNC_PROTO;
else if (obj == env.get(Environment.ERROR_PROTO)) prototype = ERR_PROTO;
else if (obj == env.get(Environment.SYNTAX_ERR_PROTO)) prototype = SYNTAX_ERR_PROTO;
else if (obj == env.get(Environment.TYPE_ERR_PROTO)) prototype = TYPE_ERR_PROTO;
else if (obj == env.get(Environment.RANGE_ERR_PROTO)) prototype = RANGE_ERR_PROTO;
else prototype = obj;
}
else prototype = obj;
@ -301,22 +300,22 @@ public class ObjectValue {
return false;
}
public final ObjectValue getMemberDescriptor(Extensions ext, Object key) {
key = Values.normalize(ext, key);
public final ObjectValue getMemberDescriptor(Environment env, Object key) {
key = Values.normalize(env, key);
var prop = properties.get(key);
var res = new ObjectValue();
res.defineProperty(ext, "configurable", memberConfigurable(key));
res.defineProperty(ext, "enumerable", memberEnumerable(key));
res.defineProperty(env, "configurable", memberConfigurable(key));
res.defineProperty(env, "enumerable", memberEnumerable(key));
if (prop != null) {
res.defineProperty(ext, "get", prop.getter);
res.defineProperty(ext, "set", prop.setter);
res.defineProperty(env, "get", prop.getter);
res.defineProperty(env, "set", prop.setter);
}
else if (hasField(ext, key)) {
res.defineProperty(ext, "value", values.get(key));
res.defineProperty(ext, "writable", memberWritable(key));
else if (hasField(env, key)) {
res.defineProperty(env, "value", values.get(key));
res.defineProperty(env, "writable", memberWritable(key));
}
else return null;
return res;
@ -337,10 +336,10 @@ public class ObjectValue {
return res;
}
public ObjectValue(Extensions ext, Map<?, ?> values) {
public ObjectValue(Environment env, Map<?, ?> values) {
this(PlaceholderProto.OBJECT);
for (var el : values.entrySet()) {
defineProperty(ext, el.getKey(), el.getValue());
defineProperty(env, el.getKey(), el.getValue());
}
}
public ObjectValue(PlaceholderProto proto) {

View File

@ -3,7 +3,7 @@ package me.topchetoeu.jscript.runtime.values;
import java.util.HashMap;
import java.util.List;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.scope.ValueVariable;
public class ScopeValue extends ObjectValue {
@ -11,12 +11,12 @@ public class ScopeValue extends ObjectValue {
public final HashMap<String, Integer> names = new HashMap<>();
@Override
protected Object getField(Extensions ext, Object key) {
protected Object getField(Environment ext, Object key) {
if (names.containsKey(key)) return variables[names.get(key)].get(ext);
return super.getField(ext, key);
}
@Override
protected boolean setField(Extensions ext, Object key, Object val) {
protected boolean setField(Environment ext, Object key, Object val) {
if (names.containsKey(key)) {
variables[names.get(key)].set(ext, val);
return true;
@ -28,12 +28,12 @@ public class ScopeValue extends ObjectValue {
return super.setField(ext, key, val);
}
@Override
protected void deleteField(Extensions ext, Object key) {
protected void deleteField(Environment ext, Object key) {
if (names.containsKey(key)) return;
super.deleteField(ext, key);
}
@Override
protected boolean hasField(Extensions ext, Object key) {
protected boolean hasField(Environment ext, Object key) {
if (names.containsKey(key)) return true;
return super.hasField(ext, key);
}

View File

@ -0,0 +1,733 @@
package me.topchetoeu.jscript.runtime.values;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import me.topchetoeu.jscript.common.Operation;
// import me.topchetoeu.jscript.lib.PromiseLib;
import me.topchetoeu.jscript.runtime.debug.DebugContext;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.ConvertException;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider;
public interface Value {
public static enum CompareResult {
NOT_EQUAL,
EQUAL,
LESS,
GREATER;
public boolean less() { return this == LESS; }
public boolean greater() { return this == GREATER; }
public boolean lessOrEqual() { return this == LESS || this == EQUAL; }
public boolean greaterOrEqual() { return this == GREATER || this == EQUAL; }
public static CompareResult from(int cmp) {
if (cmp < 0) return LESS;
if (cmp > 0) return GREATER;
return EQUAL;
}
}
public static final Object NULL = new Object();
public static final Object NO_RETURN = new Object();
public static double number(Object val) {
if (val instanceof Number) return ((Number)val).doubleValue();
else return Double.NaN;
}
@SuppressWarnings("unchecked")
public static <T> T wrapper(Object val, Class<T> clazz) {
if (isWrapper(val)) val = ((NativeWrapper)val).wrapped;
if (val != null && clazz.isInstance(val)) return (T)val;
else return null;
}
public static String type(Object val) {
if (val == null) return "undefined";
if (val instanceof String) return "string";
if (val instanceof Number) return "number";
if (val instanceof Boolean) return "boolean";
if (val instanceof Symbol) return "symbol";
if (val instanceof FunctionValue) return "function";
return "object";
}
public boolean isPrimitive();
public BooleanValue toBoolean();
public default Value call(Environment env, Value self, Value ...args) {
throw EngineException.ofType("Tried to call a non-function value.");
}
public default Value callNew(Environment env, Value ...args) {
var res = new ObjectValue();
try {
var proto = Values.getMember(env, this, "prototype");
setPrototype(env, res, proto);
var ret = this.call(env, res, args);
if (!ret.isPrimitive()) return ret;
return res;
}
catch (IllegalArgumentException e) {
throw EngineException.ofType("Tried to call new on an invalid constructor.");
}
}
public default Value toPrimitive(Environment env, Value val) {
if (val.isPrimitive()) return val;
if (env != null) {
var valueOf = getMember(env, val, "valueOf");
if (valueOf instanceof FunctionValue) {
var res = valueOf.call(env, val);
if (res.isPrimitive()) return res;
}
var toString = getMember(env, val, "toString");
if (toString instanceof FunctionValue) {
var res = toString.call(env, val);
if (res.isPrimitive()) return res;
}
}
throw EngineException.ofType("Value couldn't be converted to a primitive.");
}
public default NumberValue toNumber(Environment ext, Object obj) {
var val = this.toPrimitive(ext, obj, ConvertHint.VALUEOF);
if (val instanceof NumberValue) return number(val);
if (val instanceof Boolean) return ((Boolean)val) ? 1 : 0;
if (val instanceof String) {
try { return Double.parseDouble((String)val); }
catch (NumberFormatException e) { return Double.NaN; }
}
return Double.NaN;
}
public default StringValue toString(Environment ext, Object obj) {
var val = toPrimitive(ext, obj, ConvertHint.VALUEOF);
if (val == null) return "undefined";
if (val == NULL) return "null";
if (val instanceof Number) {
var d = number(val);
if (d == Double.NEGATIVE_INFINITY) return "-Infinity";
if (d == Double.POSITIVE_INFINITY) return "Infinity";
if (Double.isNaN(d)) return "NaN";
return BigDecimal.valueOf(d).stripTrailingZeros().toPlainString();
}
if (val instanceof Boolean) return (Boolean)val ? "true" : "false";
if (val instanceof String) return (String)val;
if (val instanceof Symbol) return val.toString();
return "Unknown value";
}
public static Object add(Environment ext, Object a, Object b) {
if (a instanceof String || b instanceof String) return toString(ext, a) + toString(ext, b);
else return toNumber(ext, a) + toNumber(ext, b);
}
public static double subtract(Environment ext, Object a, Object b) {
return toNumber(ext, a) - toNumber(ext, b);
}
public static double multiply(Environment ext, Object a, Object b) {
return toNumber(ext, a) * toNumber(ext, b);
}
public static double divide(Environment ext, Object a, Object b) {
return toNumber(ext, a) / toNumber(ext, b);
}
public static double modulo(Environment ext, Object a, Object b) {
return toNumber(ext, a) % toNumber(ext, b);
}
public static double negative(Environment ext, Object obj) {
return -toNumber(ext, obj);
}
public static int and(Environment ext, Object a, Object b) {
return (int)toNumber(ext, a) & (int)toNumber(ext, b);
}
public static int or(Environment ext, Object a, Object b) {
return (int)toNumber(ext, a) | (int)toNumber(ext, b);
}
public static int xor(Environment ext, Object a, Object b) {
return (int)toNumber(ext, a) ^ (int)toNumber(ext, b);
}
public static int bitwiseNot(Environment ext, Object obj) {
return ~(int)toNumber(ext, obj);
}
public static int shiftLeft(Environment ext, Object a, Object b) {
return (int)toNumber(ext, a) << (int)toNumber(ext, b);
}
public static int shiftRight(Environment ext, Object a, Object b) {
return (int)toNumber(ext, a) >> (int)toNumber(ext, b);
}
public static long unsignedShiftRight(Environment ext, Object a, Object b) {
long _a = (long)toNumber(ext, a);
long _b = (long)toNumber(ext, b);
if (_a < 0) _a += 0x100000000l;
if (_b < 0) _b += 0x100000000l;
return _a >>> _b;
}
public static CompareResult compare(Environment ext, Object a, Object b) {
a = toPrimitive(ext, a, ConvertHint.VALUEOF);
b = toPrimitive(ext, b, ConvertHint.VALUEOF);
if (a instanceof String && b instanceof String) CompareResult.from(((String)a).compareTo((String)b));
var _a = toNumber(ext, a);
var _b = toNumber(ext, b);
if (Double.isNaN(_a) || Double.isNaN(_b)) return CompareResult.NOT_EQUAL;
return CompareResult.from(Double.compare(_a, _b));
}
public static boolean not(Object obj) {
return !toBoolean(obj);
}
public static boolean isInstanceOf(Environment ext, Object obj, Object proto) {
if (obj == null || obj == NULL || proto == null || proto == NULL) return false;
var val = getPrototype(ext, obj);
while (val != null) {
if (val.equals(proto)) return true;
val = val.getPrototype(ext);
}
return false;
}
public static Object operation(Environment ext, Operation op, Object ...args) {
switch (op) {
case ADD: return add(ext, args[0], args[1]);
case SUBTRACT: return subtract(ext, args[0], args[1]);
case DIVIDE: return divide(ext, args[0], args[1]);
case MULTIPLY: return multiply(ext, args[0], args[1]);
case MODULO: return modulo(ext, args[0], args[1]);
case AND: return and(ext, args[0], args[1]);
case OR: return or(ext, args[0], args[1]);
case XOR: return xor(ext, args[0], args[1]);
case EQUALS: return strictEquals(ext, args[0], args[1]);
case NOT_EQUALS: return !strictEquals(ext, args[0], args[1]);
case LOOSE_EQUALS: return looseEqual(ext, args[0], args[1]);
case LOOSE_NOT_EQUALS: return !looseEqual(ext, args[0], args[1]);
case GREATER: return compare(ext, args[0], args[1]).greater();
case GREATER_EQUALS: return compare(ext, args[0], args[1]).greaterOrEqual();
case LESS: return compare(ext, args[0], args[1]).less();
case LESS_EQUALS: return compare(ext, args[0], args[1]).lessOrEqual();
case INVERSE: return bitwiseNot(ext, args[0]);
case NOT: return not(args[0]);
case POS: return toNumber(ext, args[0]);
case NEG: return negative(ext, args[0]);
case SHIFT_LEFT: return shiftLeft(ext, args[0], args[1]);
case SHIFT_RIGHT: return shiftRight(ext, args[0], args[1]);
case USHIFT_RIGHT: return unsignedShiftRight(ext, args[0], args[1]);
case IN: return hasMember(ext, args[1], args[0], false);
case INSTANCEOF: {
var proto = getMember(ext, args[1], "prototype");
return isInstanceOf(ext, args[0], proto);
}
default: return null;
}
}
public static Object getMember(Environment 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 null.");
if (obj instanceof ObjectValue) return ((ObjectValue)obj).getMember(ctx, key, obj);
if (obj instanceof String && key instanceof Number) {
var i = number(key);
var s = (String)obj;
if (i >= 0 && i < s.length() && i - Math.floor(i) == 0) {
return s.charAt((int)i) + "";
}
}
var proto = getPrototype(ctx, obj);
if (proto == null) return "__proto__".equals(key) ? NULL : null;
else if (key != null && "__proto__".equals(key)) return proto;
else return proto.getMember(ctx, key, obj);
}
public static Object getMemberPath(Environment ctx, Object obj, Object ...path) {
var res = obj;
for (var key : path) res = getMember(ctx, res, key);
return res;
}
public static boolean setMember(Environment 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 null.");
if (key != null && "__proto__".equals(key)) return setPrototype(ctx, obj, val);
if (obj instanceof ObjectValue) return ((ObjectValue)obj).setMember(ctx, key, val, obj, false);
var proto = getPrototype(ctx, obj);
return proto.setMember(ctx, key, val, obj, true);
}
public static boolean hasMember(Environment ctx, Object obj, Object key, boolean own) {
if (obj == null || obj == NULL) return false;
obj = normalize(ctx, obj); key = normalize(ctx, key);
if ("__proto__".equals(key)) return true;
if (obj instanceof ObjectValue) return ((ObjectValue)obj).hasMember(ctx, key, own);
if (obj instanceof String && key instanceof Number) {
var i = number(key);
var s = (String)obj;
if (i >= 0 && i < s.length() && i - Math.floor(i) == 0) return true;
}
if (own) return false;
var proto = getPrototype(ctx, obj);
return proto != null && proto.hasMember(ctx, key, own);
}
public static boolean deleteMember(Environment ext, Object obj, Object key) {
if (obj == null || obj == NULL) return false;
obj = normalize(ext, obj); key = normalize(ext, key);
if (obj instanceof ObjectValue) return ((ObjectValue)obj).deleteMember(ext, key);
else return false;
}
public static ObjectValue getPrototype(Environment ext, Object obj) {
if (obj == null || obj == NULL) return null;
obj = normalize(ext, obj);
if (obj instanceof ObjectValue) return ((ObjectValue)obj).getPrototype(ext);
if (ext == null) return null;
if (obj instanceof String) return ext.get(Environment.STRING_PROTO);
else if (obj instanceof Number) return ext.get(Environment.NUMBER_PROTO);
else if (obj instanceof Boolean) return ext.get(Environment.BOOL_PROTO);
else if (obj instanceof Symbol) return ext.get(Environment.SYMBOL_PROTO);
return null;
}
public static boolean setPrototype(Environment ext, Object obj, Object proto) {
obj = normalize(ext, obj);
return obj instanceof ObjectValue && ((ObjectValue)obj).setPrototype(ext, proto);
}
public static void makePrototypeChain(Environment ext, Object... chain) {
for(var i = 1; i < chain.length; i++) {
setPrototype(ext, chain[i], chain[i - 1]);
}
}
public static List<Object> getMembers(Environment ext, Object obj, boolean own, boolean includeNonEnumerable) {
List<Object> res = new ArrayList<>();
if (obj instanceof ObjectValue) res = ((ObjectValue)obj).keys(includeNonEnumerable);
if (obj instanceof String) {
for (var i = 0; i < ((String)obj).length(); i++) res.add((double)i);
}
if (!own) {
var proto = getPrototype(ext, obj);
while (proto != null) {
res.addAll(proto.keys(includeNonEnumerable));
proto = getPrototype(ext, proto);
}
}
return res;
}
public static ObjectValue getMemberDescriptor(Environment ext, Object obj, Object key) {
if (obj instanceof ObjectValue) return ((ObjectValue)obj).getMemberDescriptor(ext, key);
else if (obj instanceof String && key instanceof Number) {
var i = ((Number)key).intValue();
var _i = ((Number)key).doubleValue();
if (i - _i != 0) return null;
if (i < 0 || i >= ((String)obj).length()) return null;
return new ObjectValue(ext, Map.of(
"value", ((String)obj).charAt(i) + "",
"writable", false,
"enumerable", true,
"configurable", false
));
}
else return null;
}
public static boolean strictEquals(Environment ext, Object a, Object b) {
a = normalize(ext, a);
b = normalize(ext, b);
if (a == null || b == null) return a == null && b == null;
if (isNan(a) || isNan(b)) return false;
if (a instanceof Number && number(a) == -0.) a = 0.;
if (b instanceof Number && number(b) == -0.) b = 0.;
return a == b || a.equals(b);
}
public static boolean looseEqual(Environment ext, Object a, Object b) {
a = normalize(ext, a); b = normalize(ext, b);
// In loose equality, null is equivalent to undefined
if (a == NULL) a = null;
if (b == NULL) b = null;
if (a == null || b == null) return a == null && b == null;
// If both are objects, just compare their references
if (!isPrimitive(a) && !isPrimitive(b)) return a == b;
// Convert values to primitives
a = toPrimitive(ext, a, ConvertHint.VALUEOF);
b = toPrimitive(ext, b, ConvertHint.VALUEOF);
// Compare symbols by reference
if (a instanceof Symbol || b instanceof Symbol) return a == b;
if (a instanceof Boolean || b instanceof Boolean) return toBoolean(a) == toBoolean(b);
if (a instanceof Number || b instanceof Number) return strictEquals(ext, toNumber(ext, a), toNumber(ext, b));
// Default to strings
return toString(ext, a).equals(toString(ext, b));
}
public static Object normalize(Environment ext, Object val) {
if (val instanceof Number) return number(val);
if (isPrimitive(val) || val instanceof ObjectValue) return val;
if (val instanceof Character) return val + "";
if (val instanceof Map) {
var res = new ObjectValue();
for (var entry : ((Map<?, ?>)val).entrySet()) {
res.defineProperty(ext, entry.getKey(), entry.getValue());
}
return res;
}
if (val instanceof Iterable) {
var res = new ArrayValue();
for (var entry : ((Iterable<?>)val)) {
res.set(ext, res.size(), entry);
}
return res;
}
if (val instanceof Class) {
if (ext == null) return null;
else return NativeWrapperProvider.get(ext).getConstr((Class<?>)val);
}
return NativeWrapper.of(ext, val);
}
@SuppressWarnings("unchecked")
public static <T> T convert(Environment ext, Object obj, Class<T> clazz) {
if (clazz == Void.class) return null;
if (obj instanceof NativeWrapper) {
var res = ((NativeWrapper)obj).wrapped;
if (clazz.isInstance(res)) return (T)res;
}
if (clazz == null || clazz == Object.class) return (T)obj;
if (obj instanceof ArrayValue) {
if (clazz.isAssignableFrom(ArrayList.class)) {
var raw = ((ArrayValue)obj).toArray();
var res = new ArrayList<>();
for (var i = 0; i < raw.length; i++) res.add(convert(ext, raw[i], Object.class));
return (T)new ArrayList<>(res);
}
if (clazz.isAssignableFrom(HashSet.class)) {
var raw = ((ArrayValue)obj).toArray();
var res = new HashSet<>();
for (var i = 0; i < raw.length; i++) res.add(convert(ext, raw[i], Object.class));
return (T)new HashSet<>(res);
}
if (clazz.isArray()) {
var raw = ((ArrayValue)obj).toArray();
Object res = Array.newInstance(clazz.getComponentType(), raw.length);
for (var i = 0; i < raw.length; i++) Array.set(res, i, convert(ext, raw[i], Object.class));
return (T)res;
}
}
if (obj instanceof ObjectValue && clazz.isAssignableFrom(HashMap.class)) {
var res = new HashMap<>();
for (var el : ((ObjectValue)obj).values.entrySet()) res.put(
convert(ext, el.getKey(), null),
convert(ext, el.getValue(), null)
);
return (T)res;
}
if (clazz == String.class) return (T)toString(ext, obj);
if (clazz == Boolean.class || clazz == Boolean.TYPE) return (T)(Boolean)toBoolean(obj);
if (clazz == Byte.class || clazz == byte.class) return (T)(Byte)(byte)toNumber(ext, obj);
if (clazz == Integer.class || clazz == int.class) return (T)(Integer)(int)toNumber(ext, obj);
if (clazz == Long.class || clazz == long.class) return (T)(Long)(long)toNumber(ext, obj);
if (clazz == Short.class || clazz == short.class) return (T)(Short)(short)toNumber(ext, obj);
if (clazz == Float.class || clazz == float.class) return (T)(Float)(float)toNumber(ext, obj);
if (clazz == Double.class || clazz == double.class) return (T)(Double)toNumber(ext, obj);
if (clazz == Character.class || clazz == char.class) {
if (obj instanceof Number) return (T)(Character)(char)number(obj);
else {
var res = toString(ext, obj);
if (res.length() == 0) throw new ConvertException("\"\"", "Character");
else return (T)(Character)res.charAt(0);
}
}
if (obj == null) return null;
if (clazz.isInstance(obj)) return (T)obj;
if (clazz.isAssignableFrom(NativeWrapper.class)) {
return (T)NativeWrapper.of(ext, obj);
}
throw new ConvertException(type(obj), clazz.getSimpleName());
}
public static Iterable<Object> fromJSIterator(Environment ext, Object obj) {
return () -> {
try {
var symbol = Symbol.get("Symbol.iterator");
var iteratorFunc = getMember(ext, obj, symbol);
if (!(iteratorFunc instanceof FunctionValue)) return Collections.emptyIterator();
var iterator = iteratorFunc instanceof FunctionValue ?
((FunctionValue)iteratorFunc).call(ext, obj, obj) :
iteratorFunc;
var nextFunc = getMember(ext, call(ext, iteratorFunc, obj), "next");
if (!(nextFunc instanceof FunctionValue)) return Collections.emptyIterator();
return new Iterator<Object>() {
private Object value = null;
public boolean consumed = true;
private FunctionValue next = (FunctionValue)nextFunc;
private void loadNext() {
if (next == null) value = null;
else if (consumed) {
var curr = next.call(ext, iterator);
if (curr == null) { next = null; value = null; }
if (toBoolean(Values.getMember(ext, curr, "done"))) { next = null; value = null; }
else {
this.value = Values.getMember(ext, curr, "value");
consumed = false;
}
}
}
@Override
public boolean hasNext() {
loadNext();
return next != null;
}
@Override
public Object next() {
loadNext();
var res = value;
value = null;
consumed = true;
return res;
}
};
}
catch (IllegalArgumentException | NullPointerException e) {
return Collections.emptyIterator();
}
};
}
public static ObjectValue toJSIterator(Environment ext, Iterator<?> it) {
var res = new ObjectValue();
try {
var key = getMember(ext, getMember(ext, ext.get(Environment.SYMBOL_PROTO), "constructor"), "iterator");
res.defineProperty(ext, key, new NativeFunction("", args -> args.self));
}
catch (IllegalArgumentException | NullPointerException e) { }
res.defineProperty(ext, "next", new NativeFunction("", args -> {
if (!it.hasNext()) return new ObjectValue(ext, Map.of("done", true));
else {
var obj = new ObjectValue();
obj.defineProperty(args.env, "value", it.next());
return obj;
}
}));
return res;
}
public static ObjectValue toJSIterator(Environment ext, Iterable<?> it) {
return toJSIterator(ext, it.iterator());
}
public static ObjectValue toJSAsyncIterator(Environment ext, Iterator<?> it) {
var res = new ObjectValue();
try {
var key = getMemberPath(ext, ext.get(Environment.SYMBOL_PROTO), "constructor", "asyncIterator");
res.defineProperty(ext, key, new NativeFunction("", args -> args.self));
}
catch (IllegalArgumentException | NullPointerException e) { }
res.defineProperty(ext, "next", new NativeFunction("", args -> {
return PromiseLib.await(args.env, () -> {
if (!it.hasNext()) return new ObjectValue(ext, Map.of("done", true));
else {
var obj = new ObjectValue();
obj.defineProperty(args.env, "value", it.next());
return obj;
}
});
}));
return res;
}
private static boolean isEmptyFunc(ObjectValue val) {
if (!(val instanceof FunctionValue)) return false;
if (!val.values.containsKey("prototype") || val.values.size() + val.properties.size() > 1) return false;
var proto = val.values.get("prototype");
if (!(proto instanceof ObjectValue)) return false;
var protoObj = (ObjectValue)proto;
if (protoObj.values.get("constructor") != val) return false;
if (protoObj.values.size() + protoObj.properties.size() != 1) return false;
return true;
}
private static String toReadable(Environment ext, Object val, HashSet<Object> passed, int tab) {
if (tab == 0 && val instanceof String) return (String)val;
if (passed.contains(val)) return "[circular]";
var printed = true;
var res = new StringBuilder();
var dbg = DebugContext.get(ext);
if (val instanceof FunctionValue) {
res.append(val.toString());
var loc = val instanceof CodeFunction ? dbg.getMapOrEmpty((CodeFunction)val).start() : null;
if (loc != null) res.append(" @ " + loc);
}
else if (val instanceof ArrayValue) {
res.append("[");
var obj = ((ArrayValue)val);
for (int i = 0; i < obj.size(); i++) {
if (i != 0) res.append(", ");
else res.append(" ");
if (obj.has(i)) res.append(toReadable(ext, obj.get(i), passed, tab));
else res.append("<empty>");
}
res.append(" ] ");
}
else if (val instanceof NativeWrapper) {
var obj = ((NativeWrapper)val).wrapped;
res.append("Native " + obj.toString() + " ");
}
else printed = false;
if (val instanceof ObjectValue) {
if (tab > 3) {
return "{...}";
}
passed.add(val);
var obj = (ObjectValue)val;
if (obj.values.size() + obj.properties.size() == 0 || isEmptyFunc(obj)) {
if (!printed) res.append("{}\n");
}
else {
res.append("{\n");
for (var el : obj.values.entrySet()) {
for (int i = 0; i < tab + 1; i++) res.append(" ");
res.append(toReadable(ext, el.getKey(), passed, tab + 1));
res.append(": ");
res.append(toReadable(ext, el.getValue(), passed, tab + 1));
res.append(",\n");
}
for (var el : obj.properties.entrySet()) {
for (int i = 0; i < tab + 1; i++) res.append(" ");
res.append(toReadable(ext, el.getKey(), passed, tab + 1));
res.append(": [prop],\n");
}
for (int i = 0; i < tab; i++) res.append(" ");
res.append("}");
}
passed.remove(val);
}
else if (val == null) return "undefined";
else if (val == Values.NULL) return "null";
else if (val instanceof String) return "'" + val + "'";
else return Values.toString(ext, val);
return res.toString();
}
public static String toReadable(Environment ext, Object val) {
return toReadable(ext, val, new HashSet<>(), 0);
}
public static String errorToReadable(RuntimeException err, String prefix) {
prefix = prefix == null ? "Uncaught" : "Uncaught " + prefix;
if (err instanceof EngineException) {
var ee = ((EngineException)err);
try {
return prefix + " " + ee.toString(ee.env);
}
catch (EngineException ex) {
return prefix + " " + toReadable(ee.env, ee.value);
}
}
else if (err instanceof SyntaxException) {
return prefix + " SyntaxError " + ((SyntaxException)err).msg;
}
else if (err.getCause() instanceof InterruptedException) return "";
else {
var str = new ByteArrayOutputStream();
err.printStackTrace(new PrintStream(str));
return prefix + " internal error " + str.toString();
}
}
public static void printValue(Environment ext, Object val) {
System.out.print(toReadable(ext, val));
}
public static void printError(RuntimeException err, String prefix) {
System.out.println(errorToReadable(err, prefix));
}
}

View File

@ -13,10 +13,9 @@ import java.util.List;
import java.util.Map;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.lib.PromiseLib;
import me.topchetoeu.jscript.runtime.Environment;
import me.topchetoeu.jscript.runtime.Extensions;
// import me.topchetoeu.jscript.lib.PromiseLib;
import me.topchetoeu.jscript.runtime.debug.DebugContext;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.ConvertException;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
@ -74,7 +73,7 @@ public class Values {
return "object";
}
private static Object tryCallConvertFunc(Extensions ext, Object obj, String name) {
private static Object tryCallConvertFunc(Environment ext, Object obj, String name) {
var func = getMember(ext, obj, name);
if (func instanceof FunctionValue) {
@ -95,7 +94,7 @@ public class Values {
obj == NULL;
}
public static Object toPrimitive(Extensions ext, Object obj, ConvertHint hint) {
public static Object toPrimitive(Environment ext, Object obj, ConvertHint hint) {
obj = normalize(ext, obj);
if (isPrimitive(obj)) return obj;
@ -116,7 +115,7 @@ public class Values {
if (obj instanceof Boolean) return (Boolean)obj;
return true;
}
public static double toNumber(Extensions ext, Object obj) {
public static double toNumber(Environment ext, Object obj) {
var val = toPrimitive(ext, obj, ConvertHint.VALUEOF);
if (val instanceof Number) return number(val);
@ -127,7 +126,7 @@ public class Values {
}
return Double.NaN;
}
public static String toString(Extensions ext, Object obj) {
public static String toString(Environment ext, Object obj) {
var val = toPrimitive(ext, obj, ConvertHint.VALUEOF);
if (val == null) return "undefined";
@ -147,47 +146,47 @@ public class Values {
return "Unknown value";
}
public static Object add(Extensions ext, Object a, Object b) {
public static Object add(Environment ext, Object a, Object b) {
if (a instanceof String || b instanceof String) return toString(ext, a) + toString(ext, b);
else return toNumber(ext, a) + toNumber(ext, b);
}
public static double subtract(Extensions ext, Object a, Object b) {
public static double subtract(Environment ext, Object a, Object b) {
return toNumber(ext, a) - toNumber(ext, b);
}
public static double multiply(Extensions ext, Object a, Object b) {
public static double multiply(Environment ext, Object a, Object b) {
return toNumber(ext, a) * toNumber(ext, b);
}
public static double divide(Extensions ext, Object a, Object b) {
public static double divide(Environment ext, Object a, Object b) {
return toNumber(ext, a) / toNumber(ext, b);
}
public static double modulo(Extensions ext, Object a, Object b) {
public static double modulo(Environment ext, Object a, Object b) {
return toNumber(ext, a) % toNumber(ext, b);
}
public static double negative(Extensions ext, Object obj) {
public static double negative(Environment ext, Object obj) {
return -toNumber(ext, obj);
}
public static int and(Extensions ext, Object a, Object b) {
public static int and(Environment ext, Object a, Object b) {
return (int)toNumber(ext, a) & (int)toNumber(ext, b);
}
public static int or(Extensions ext, Object a, Object b) {
public static int or(Environment ext, Object a, Object b) {
return (int)toNumber(ext, a) | (int)toNumber(ext, b);
}
public static int xor(Extensions ext, Object a, Object b) {
public static int xor(Environment ext, Object a, Object b) {
return (int)toNumber(ext, a) ^ (int)toNumber(ext, b);
}
public static int bitwiseNot(Extensions ext, Object obj) {
public static int bitwiseNot(Environment ext, Object obj) {
return ~(int)toNumber(ext, obj);
}
public static int shiftLeft(Extensions ext, Object a, Object b) {
public static int shiftLeft(Environment ext, Object a, Object b) {
return (int)toNumber(ext, a) << (int)toNumber(ext, b);
}
public static int shiftRight(Extensions ext, Object a, Object b) {
public static int shiftRight(Environment ext, Object a, Object b) {
return (int)toNumber(ext, a) >> (int)toNumber(ext, b);
}
public static long unsignedShiftRight(Extensions ext, Object a, Object b) {
public static long unsignedShiftRight(Environment ext, Object a, Object b) {
long _a = (long)toNumber(ext, a);
long _b = (long)toNumber(ext, b);
@ -196,7 +195,7 @@ public class Values {
return _a >>> _b;
}
public static CompareResult compare(Extensions ext, Object a, Object b) {
public static CompareResult compare(Environment ext, Object a, Object b) {
a = toPrimitive(ext, a, ConvertHint.VALUEOF);
b = toPrimitive(ext, b, ConvertHint.VALUEOF);
@ -214,7 +213,7 @@ public class Values {
return !toBoolean(obj);
}
public static boolean isInstanceOf(Extensions ext, Object obj, Object proto) {
public static boolean isInstanceOf(Environment ext, Object obj, Object proto) {
if (obj == null || obj == NULL || proto == null || proto == NULL) return false;
var val = getPrototype(ext, obj);
@ -226,7 +225,7 @@ public class Values {
return false;
}
public static Object operation(Extensions ext, Operation op, Object ...args) {
public static Object operation(Environment ext, Operation op, Object ...args) {
switch (op) {
case ADD: return add(ext, args[0], args[1]);
case SUBTRACT: return subtract(ext, args[0], args[1]);
@ -267,7 +266,7 @@ public class Values {
}
}
public static Object getMember(Extensions ctx, Object obj, Object key) {
public static Object getMember(Environment 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 null.");
@ -287,12 +286,12 @@ public class Values {
else if (key != null && "__proto__".equals(key)) return proto;
else return proto.getMember(ctx, key, obj);
}
public static Object getMemberPath(Extensions ctx, Object obj, Object ...path) {
public static Object getMemberPath(Environment ctx, Object obj, Object ...path) {
var res = obj;
for (var key : path) res = getMember(ctx, res, key);
return res;
}
public static boolean setMember(Extensions ctx, Object obj, Object key, Object val) {
public static boolean setMember(Environment 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 null.");
@ -302,7 +301,7 @@ public class Values {
var proto = getPrototype(ctx, obj);
return proto.setMember(ctx, key, val, obj, true);
}
public static boolean hasMember(Extensions ctx, Object obj, Object key, boolean own) {
public static boolean hasMember(Environment ctx, Object obj, Object key, boolean own) {
if (obj == null || obj == NULL) return false;
obj = normalize(ctx, obj); key = normalize(ctx, key);
@ -320,14 +319,14 @@ public class Values {
var proto = getPrototype(ctx, obj);
return proto != null && proto.hasMember(ctx, key, own);
}
public static boolean deleteMember(Extensions ext, Object obj, Object key) {
public static boolean deleteMember(Environment ext, Object obj, Object key) {
if (obj == null || obj == NULL) return false;
obj = normalize(ext, obj); key = normalize(ext, key);
if (obj instanceof ObjectValue) return ((ObjectValue)obj).deleteMember(ext, key);
else return false;
}
public static ObjectValue getPrototype(Extensions ext, Object obj) {
public static ObjectValue getPrototype(Environment ext, Object obj) {
if (obj == null || obj == NULL) return null;
obj = normalize(ext, obj);
if (obj instanceof ObjectValue) return ((ObjectValue)obj).getPrototype(ext);
@ -340,16 +339,16 @@ public class Values {
return null;
}
public static boolean setPrototype(Extensions ext, Object obj, Object proto) {
public static boolean setPrototype(Environment ext, Object obj, Object proto) {
obj = normalize(ext, obj);
return obj instanceof ObjectValue && ((ObjectValue)obj).setPrototype(ext, proto);
}
public static void makePrototypeChain(Extensions ext, Object... chain) {
public static void makePrototypeChain(Environment ext, Object... chain) {
for(var i = 1; i < chain.length; i++) {
setPrototype(ext, chain[i], chain[i - 1]);
}
}
public static List<Object> getMembers(Extensions ext, Object obj, boolean own, boolean includeNonEnumerable) {
public static List<Object> getMembers(Environment ext, Object obj, boolean own, boolean includeNonEnumerable) {
List<Object> res = new ArrayList<>();
if (obj instanceof ObjectValue) res = ((ObjectValue)obj).keys(includeNonEnumerable);
@ -369,7 +368,7 @@ public class Values {
return res;
}
public static ObjectValue getMemberDescriptor(Extensions ext, Object obj, Object key) {
public static ObjectValue getMemberDescriptor(Environment ext, Object obj, Object key) {
if (obj instanceof ObjectValue) return ((ObjectValue)obj).getMemberDescriptor(ext, key);
else if (obj instanceof String && key instanceof Number) {
var i = ((Number)key).intValue();
@ -387,11 +386,11 @@ public class Values {
else return null;
}
public static Object call(Extensions ext, Object func, Object thisArg, Object ...args) {
public static Object call(Environment ext, Object func, Object thisArg, Object ...args) {
if (!(func instanceof FunctionValue)) throw EngineException.ofType("Tried to call a non-function value.");
return ((FunctionValue)func).call(ext, thisArg, args);
}
public static Object callNew(Extensions ext, Object func, Object ...args) {
public static Object callNew(Environment ext, Object func, Object ...args) {
var res = new ObjectValue();
try {
var proto = Values.getMember(ext, func, "prototype");
@ -407,7 +406,7 @@ public class Values {
}
}
public static boolean strictEquals(Extensions ext, Object a, Object b) {
public static boolean strictEquals(Environment ext, Object a, Object b) {
a = normalize(ext, a);
b = normalize(ext, b);
@ -418,7 +417,7 @@ public class Values {
return a == b || a.equals(b);
}
public static boolean looseEqual(Extensions ext, Object a, Object b) {
public static boolean looseEqual(Environment ext, Object a, Object b) {
a = normalize(ext, a); b = normalize(ext, b);
// In loose equality, null is equivalent to undefined
@ -442,7 +441,7 @@ public class Values {
return toString(ext, a).equals(toString(ext, b));
}
public static Object normalize(Extensions ext, Object val) {
public static Object normalize(Environment ext, Object val) {
if (val instanceof Number) return number(val);
if (isPrimitive(val) || val instanceof ObjectValue) return val;
if (val instanceof Character) return val + "";
@ -476,7 +475,7 @@ public class Values {
}
@SuppressWarnings("unchecked")
public static <T> T convert(Extensions ext, Object obj, Class<T> clazz) {
public static <T> T convert(Environment ext, Object obj, Class<T> clazz) {
if (clazz == Void.class) return null;
if (obj instanceof NativeWrapper) {
@ -543,7 +542,7 @@ public class Values {
throw new ConvertException(type(obj), clazz.getSimpleName());
}
public static Iterable<Object> fromJSIterator(Extensions ext, Object obj) {
public static Iterable<Object> fromJSIterator(Environment ext, Object obj) {
return () -> {
try {
var symbol = Symbol.get("Symbol.iterator");
@ -596,7 +595,7 @@ public class Values {
};
}
public static ObjectValue toJSIterator(Extensions ext, Iterator<?> it) {
public static ObjectValue toJSIterator(Environment ext, Iterator<?> it) {
var res = new ObjectValue();
try {
@ -609,7 +608,7 @@ public class Values {
if (!it.hasNext()) return new ObjectValue(ext, Map.of("done", true));
else {
var obj = new ObjectValue();
obj.defineProperty(args.ctx, "value", it.next());
obj.defineProperty(args.env, "value", it.next());
return obj;
}
}));
@ -617,11 +616,11 @@ public class Values {
return res;
}
public static ObjectValue toJSIterator(Extensions ext, Iterable<?> it) {
public static ObjectValue toJSIterator(Environment ext, Iterable<?> it) {
return toJSIterator(ext, it.iterator());
}
public static ObjectValue toJSAsyncIterator(Extensions ext, Iterator<?> it) {
public static ObjectValue toJSAsyncIterator(Environment ext, Iterator<?> it) {
var res = new ObjectValue();
try {
@ -631,11 +630,11 @@ public class Values {
catch (IllegalArgumentException | NullPointerException e) { }
res.defineProperty(ext, "next", new NativeFunction("", args -> {
return PromiseLib.await(args.ctx, () -> {
return PromiseLib.await(args.env, () -> {
if (!it.hasNext()) return new ObjectValue(ext, Map.of("done", true));
else {
var obj = new ObjectValue();
obj.defineProperty(args.ctx, "value", it.next());
obj.defineProperty(args.env, "value", it.next());
return obj;
}
});
@ -654,7 +653,7 @@ public class Values {
if (protoObj.values.size() + protoObj.properties.size() != 1) return false;
return true;
}
private static String toReadable(Extensions ext, Object val, HashSet<Object> passed, int tab) {
private static String toReadable(Environment ext, Object val, HashSet<Object> passed, int tab) {
if (tab == 0 && val instanceof String) return (String)val;
if (passed.contains(val)) return "[circular]";
@ -727,7 +726,7 @@ public class Values {
return res.toString();
}
public static String toReadable(Extensions ext, Object val) {
public static String toReadable(Environment ext, Object val) {
return toReadable(ext, val, new HashSet<>(), 0);
}
public static String errorToReadable(RuntimeException err, String prefix) {
@ -735,10 +734,10 @@ public class Values {
if (err instanceof EngineException) {
var ee = ((EngineException)err);
try {
return prefix + " " + ee.toString(ee.ext);
return prefix + " " + ee.toString(ee.env);
}
catch (EngineException ex) {
return prefix + " " + toReadable(ee.ext, ee.value);
return prefix + " " + toReadable(ee.env, ee.value);
}
}
else if (err instanceof SyntaxException) {
@ -752,7 +751,7 @@ public class Values {
return prefix + " internal error " + str.toString();
}
}
public static void printValue(Extensions ext, Object val) {
public static void printValue(Environment ext, Object val) {
System.out.print(toReadable(ext, val));
}
public static void printError(RuntimeException err, String prefix) {

View File

@ -5,11 +5,11 @@ import me.topchetoeu.jscript.common.FunctionBody;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.runtime.Compiler;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.debug.DebugContext;
import me.topchetoeu.jscript.runtime.environment.Environment;
public class JSCompiler implements Compiler {
public final Extensions ext;
public final Environment ext;
private void registerFunc(FunctionBody body, CompileResult res) {
var map = res.map();
@ -30,7 +30,7 @@ public class JSCompiler implements Compiler {
return func;
}
public JSCompiler(Extensions ext) {
public JSCompiler(Environment ext) {
this.ext = ext;
}
}

View File

@ -10,11 +10,10 @@ import me.topchetoeu.jscript.common.Metadata;
import me.topchetoeu.jscript.common.Reading;
import me.topchetoeu.jscript.lib.Internals;
import me.topchetoeu.jscript.runtime.Compiler;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Engine;
import me.topchetoeu.jscript.runtime.Environment;
import me.topchetoeu.jscript.runtime.EventLoop;
import me.topchetoeu.jscript.runtime.debug.DebugContext;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.exceptions.InterruptException;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
@ -29,7 +28,6 @@ import me.topchetoeu.jscript.utils.filesystem.Mode;
import me.topchetoeu.jscript.utils.filesystem.PhysicalFilesystem;
import me.topchetoeu.jscript.utils.filesystem.RootFilesystem;
import me.topchetoeu.jscript.utils.filesystem.STDFilesystem;
import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider;
import me.topchetoeu.jscript.utils.modules.ModuleRepo;
import me.topchetoeu.jscript.utils.permissions.PermissionsManager;
import me.topchetoeu.jscript.utils.permissions.PermissionsProvider;
@ -38,7 +36,7 @@ public class JScriptRepl {
static Thread engineTask, debugTask;
static Engine engine = new Engine();
static DebugServer debugServer = new DebugServer();
static Environment environment = new Environment();
static Environment environment = Environment.empty();
static int j = 0;
static String[] args;
@ -97,8 +95,8 @@ public class JScriptRepl {
glob.define(null, false, new NativeFunction("go", args -> {
try {
var f = Path.of("do.js");
var func = args.ctx.compile(new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f)));
return func.call(args.ctx);
var func = Compiler.compile(args.env, new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f)));
return func.call(args.env);
}
catch (IOException e) {
throw new EngineException("Couldn't open do.js");
@ -106,7 +104,7 @@ public class JScriptRepl {
}));
glob.define(null, false, new NativeFunction("log", args -> {
for (var el : args.args) {
Values.printValue(args.ctx, el);
Values.printValue(args.env, el);
}
return null;
@ -120,7 +118,7 @@ public class JScriptRepl {
environment.add(PermissionsProvider.KEY, PermissionsManager.ALL_PERMS);
environment.add(Filesystem.KEY, fs);
environment.add(ModuleRepo.KEY, ModuleRepo.ofFilesystem(fs));
environment.add(Compiler.KEY, new JSCompiler(new Context(environment)));
environment.add(Compiler.KEY, new JSCompiler(environment));
environment.add(EventLoop.KEY, engine);
}
private static void initEngine() {

View File

@ -0,0 +1,212 @@
package me.topchetoeu.jscript.utils.debug;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.function.Supplier;
import me.topchetoeu.jscript.common.json.JSON;
import me.topchetoeu.jscript.common.json.JSONMap;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Symbol;
import me.topchetoeu.jscript.runtime.values.Values;
class ObjectManager {
public static class ObjRef {
public final ObjectValue obj;
public final Environment ext;
public final HashSet<String> heldGroups = new HashSet<>();
public boolean held = true;
public boolean shouldRelease() {
return !held && heldGroups.size() == 0;
}
public ObjRef(Environment ext, ObjectValue obj) {
this.ext = ext;
this.obj = obj;
}
}
private Supplier<Integer> idSupplier;
private HashMap<Integer, ObjRef> idToObject = new HashMap<>();
private HashMap<ObjectValue, Integer> objectToId = new HashMap<>();
private HashMap<String, ArrayList<ObjRef>> objectGroups = new HashMap<>();
public JSONMap serialize(Environment env, Object val, boolean byValue) {
val = Values.normalize(null, val);
env = SimpleDebugger.sanitizeEnvironment(env);
if (val == Values.NULL) {
return new JSONMap()
.set("type", "object")
.set("subtype", "null")
.setNull("value")
.set("description", "null");
}
if (val instanceof ObjectValue) {
var obj = (ObjectValue)val;
int id;
if (objectToId.containsKey(obj)) id = objectToId.get(obj);
else {
id = idSupplier.get();
var ref = new ObjRef(env, obj);
objectToId.put(obj, id);
idToObject.put(id, ref);
}
var type = "object";
String subtype = null;
String className = null;
if (obj instanceof FunctionValue) type = "function";
if (obj instanceof ArrayValue) subtype = "array";
try { className = Values.toString(env, Values.getMemberPath(env, obj, "constructor", "name")); }
catch (Exception e) { }
var res = new JSONMap()
.set("type", type)
.set("objectId", id + "");
if (subtype != null) res.set("subtype", subtype);
if (className != null) {
res.set("className", className);
res.set("description", className);
}
if (obj instanceof ArrayValue) res.set("description", "Array(" + ((ArrayValue)obj).size() + ")");
else if (obj instanceof FunctionValue) res.set("description", obj.toString());
else {
var defaultToString = false;
try {
defaultToString =
Values.getMember(env, obj, "toString") ==
Values.getMember(env, env.get(Environment.OBJECT_PROTO), "toString");
}
catch (Exception e) { }
try { res.set("description", className + (defaultToString ? "" : " { " + Values.toString(env, obj) + " }")); }
catch (Exception e) { }
}
if (byValue) try { res.put("value", JSON.fromJs(env, obj)); }
catch (Exception e) { }
return res;
}
if (val == null) return new JSONMap().set("type", "undefined");
if (val instanceof String) return new JSONMap().set("type", "string").set("value", (String)val);
if (val instanceof Boolean) return new JSONMap().set("type", "boolean").set("value", (Boolean)val);
if (val instanceof Symbol) return new JSONMap().set("type", "symbol").set("description", val.toString());
if (val instanceof Number) {
var num = (double)(Number)val;
var res = new JSONMap().set("type", "number");
if (Double.POSITIVE_INFINITY == num) res.set("unserializableValue", "Infinity");
else if (Double.NEGATIVE_INFINITY == num) res.set("unserializableValue", "-Infinity");
else if (Double.doubleToRawLongBits(num) == Double.doubleToRawLongBits(-0d)) res.set("unserializableValue", "-0");
else if (Double.doubleToRawLongBits(num) == Double.doubleToRawLongBits(0d)) res.set("unserializableValue", "0");
else if (Double.isNaN(num)) res.set("unserializableValue", "NaN");
else res.set("value", num);
return res;
}
throw new IllegalArgumentException("Unexpected JS object.");
}
public JSONMap serialize(Environment ext, Object val) {
return serialize(ext, val, false);
}
public void addToGroup(String name, Object val) {
if (val instanceof ObjectValue) {
var obj = (ObjectValue)val;
var id = objectToId.getOrDefault(obj, -1);
if (id < 0) return;
var ref = idToObject.get(id);
if (objectGroups.containsKey(name)) objectGroups.get(name).add(ref);
else objectGroups.put(name, new ArrayList<>(List.of(ref)));
ref.heldGroups.add(name);
}
}
public void removeGroup(String name) {
var objs = objectGroups.remove(name);
if (objs != null) {
for (var obj : objs) {
if (obj.heldGroups.remove(name) && obj.shouldRelease()) {
var id = objectToId.remove(obj.obj);
if (id != null) idToObject.remove(id);
}
}
}
}
public ObjRef get(int id) {
return idToObject.get(id);
}
public void release(int id) {
var ref = idToObject.get(id);
ref.held = false;
if (ref.shouldRelease()) {
objectToId.remove(ref.obj);
idToObject.remove(id);
}
}
public Object deserializeArgument(JSONMap val) {
if (val.isString("objectId")) return get(Integer.parseInt(val.string("objectId"))).obj;
else if (val.isString("unserializableValue")) switch (val.string("unserializableValue")) {
case "NaN": return Double.NaN;
case "-Infinity": return Double.NEGATIVE_INFINITY;
case "Infinity": return Double.POSITIVE_INFINITY;
case "-0": return -0.;
}
var res = val.get("value");
if (res == null) return null;
else return JSON.toJs(res);
}
public JSONMap serializeException(Environment ext, EngineException err) {
String text = null;
try {
text = Values.toString(ext, err.value);
}
catch (EngineException e) {
text = "[error while stringifying]";
}
return new JSONMap()
.set("exceptionId", idSupplier.get())
.set("exception", serialize(ext, err.value))
.set("text", text);
}
public void clear() {
this.idToObject.clear();
this.objectToId.clear();
this.objectGroups.clear();
}
public ObjectManager(Supplier<Integer> idSupplier) {
this.idSupplier = idSupplier;
}
}

View File

@ -23,20 +23,17 @@ import me.topchetoeu.jscript.common.json.JSONList;
import me.topchetoeu.jscript.common.json.JSONMap;
import me.topchetoeu.jscript.common.mapping.FunctionMap;
import me.topchetoeu.jscript.compilation.parsing.Parsing;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Engine;
import me.topchetoeu.jscript.runtime.Environment;
import me.topchetoeu.jscript.runtime.EventLoop;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.Frame;
import me.topchetoeu.jscript.runtime.debug.DebugContext;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
import me.topchetoeu.jscript.runtime.scope.GlobalScope;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Symbol;
import me.topchetoeu.jscript.runtime.values.Values;
// very simple indeed
@ -150,10 +147,10 @@ public class SimpleDebugger implements Debugger {
this.frame = frame;
this.id = id;
this.global = GlobalScope.get(frame.ctx).obj;
this.global = GlobalScope.get(frame.env).obj;
this.local = frame.getLocalScope();
this.capture = frame.getCaptureScope();
Values.makePrototypeChain(frame.ctx, global, capture, local);
Values.makePrototypeChain(frame.env, global, capture, local);
this.valstack = frame.getValStackScope();
this.serialized = new JSONMap()
@ -163,48 +160,32 @@ public class SimpleDebugger implements Debugger {
.add(new JSONMap()
.set("type", "local")
.set("name", "Local Scope")
.set("object", serializeObj(frame.ctx, local))
.set("object", objects.serialize(frame.env, local))
)
.add(new JSONMap()
.set("type", "closure")
.set("name", "Closure")
.set("object", serializeObj(frame.ctx, capture))
.set("object", objects.serialize(frame.env, capture))
)
.add(new JSONMap()
.set("type", "global")
.set("name", "Global Scope")
.set("object", serializeObj(frame.ctx.extensions, global))
.set("object", objects.serialize(frame.env, global))
)
.add(new JSONMap()
.set("type", "other")
.set("name", "Value Stack")
.set("object", serializeObj(frame.ctx.extensions, valstack))
.set("object", objects.serialize(frame.env, valstack))
)
);
}
}
private class ObjRef {
public final ObjectValue obj;
public final Extensions ext;
public final HashSet<String> heldGroups = new HashSet<>();
public boolean held = true;
public boolean shouldRelease() {
return !held && heldGroups.size() == 0;
}
public ObjRef(Extensions ext, ObjectValue obj) {
this.ext = ext;
this.obj = obj;
}
}
private static class RunResult {
public final Extensions ext;
public final Environment ext;
public final Object result;
public final EngineException error;
public RunResult(Extensions ext, Object result, EngineException error) {
public RunResult(Environment ext, Object result, EngineException error) {
this.ext = ext;
this.result = result;
this.error = error;
@ -232,15 +213,17 @@ public class SimpleDebugger implements Debugger {
private HashMap<Integer, DebugFrame> idToFrame = new HashMap<>();
private HashMap<Frame, DebugFrame> codeFrameToFrame = new HashMap<>();
private HashMap<Integer, ObjRef> idToObject = new HashMap<>();
private HashMap<ObjectValue, Integer> objectToId = new HashMap<>();
private HashMap<String, ArrayList<ObjRef>> objectGroups = new HashMap<>();
private ObjectManager objects = new ObjectManager(this::nextId);
// private HashMap<Integer, ObjRef> idToObject = new HashMap<>();
// private HashMap<ObjectValue, Integer> objectToId = new HashMap<>();
// private HashMap<String, ArrayList<ObjRef>> objectGroups = new HashMap<>();
private Notifier updateNotifier = new Notifier();
private boolean pendingPause = false;
private int nextId = 0;
private DebugFrame stepOutFrame = null, currFrame = null;
private DebugFrame stepOutFrame = null;
private List<DebugFrame> frames = new ArrayList<>();
private int stepOutPtr = 0;
private boolean compare(String src, String target) {
@ -288,18 +271,14 @@ public class SimpleDebugger implements Debugger {
}
else return codeFrameToFrame.get(frame);
}
private synchronized void updateFrames(Context ctx) {
var frame = ctx.frame;
if (frame == null) return;
currFrame = getFrame(frame);
}
private JSONList serializeFrames(Context ctx) {
private JSONList serializeFrames(Environment env) {
var res = new JSONList();
for (var el : ctx.frames()) {
for (var el : DebugContext.get(env).getStackFrames()) {
var frame = getFrame(el);
if (frame.location == null) continue;
frame.serialized.set("location", serializeLocation(frame.location));
if (frame.location != null) res.add(frame.serialized);
}
@ -339,151 +318,6 @@ public class SimpleDebugger implements Debugger {
.set("columnNumber", loc.start() - 1);
}
private JSONMap serializeObj(Extensions env, Object val, boolean byValue) {
val = Values.normalize(null, val);
env = sanitizeEnvironment(env);
var ctx = Context.of(env);
if (val == Values.NULL) {
return new JSONMap()
.set("type", "object")
.set("subtype", "null")
.setNull("value")
.set("description", "null");
}
if (val instanceof ObjectValue) {
var obj = (ObjectValue)val;
int id;
if (objectToId.containsKey(obj)) id = objectToId.get(obj);
else {
id = nextId();
var ref = new ObjRef(env, obj);
objectToId.put(obj, id);
idToObject.put(id, ref);
}
var type = "object";
String subtype = null;
String className = null;
if (obj instanceof FunctionValue) type = "function";
if (obj instanceof ArrayValue) subtype = "array";
try { className = Values.toString(ctx, Values.getMemberPath(ctx, obj, "constructor", "name")); }
catch (Exception e) { }
var res = new JSONMap()
.set("type", type)
.set("objectId", id + "");
if (subtype != null) res.set("subtype", subtype);
if (className != null) {
res.set("className", className);
res.set("description", className);
}
if (obj instanceof ArrayValue) res.set("description", "Array(" + ((ArrayValue)obj).size() + ")");
else if (obj instanceof FunctionValue) res.set("description", obj.toString());
else {
var defaultToString = false;
try {
defaultToString =
Values.getMember(ctx, obj, "toString") ==
Values.getMember(ctx, env.get(Environment.OBJECT_PROTO), "toString");
}
catch (Exception e) { }
try { res.set("description", className + (defaultToString ? "" : " { " + Values.toString(ctx, obj) + " }")); }
catch (Exception e) { }
}
if (byValue) try { res.put("value", JSON.fromJs(env, obj)); }
catch (Exception e) { }
return res;
}
if (val == null) return new JSONMap().set("type", "undefined");
if (val instanceof String) return new JSONMap().set("type", "string").set("value", (String)val);
if (val instanceof Boolean) return new JSONMap().set("type", "boolean").set("value", (Boolean)val);
if (val instanceof Symbol) return new JSONMap().set("type", "symbol").set("description", val.toString());
if (val instanceof Number) {
var num = (double)(Number)val;
var res = new JSONMap().set("type", "number");
if (Double.POSITIVE_INFINITY == num) res.set("unserializableValue", "Infinity");
else if (Double.NEGATIVE_INFINITY == num) res.set("unserializableValue", "-Infinity");
else if (Double.doubleToRawLongBits(num) == Double.doubleToRawLongBits(-0d)) res.set("unserializableValue", "-0");
else if (Double.doubleToRawLongBits(num) == Double.doubleToRawLongBits(0d)) res.set("unserializableValue", "0");
else if (Double.isNaN(num)) res.set("unserializableValue", "NaN");
else res.set("value", num);
return res;
}
throw new IllegalArgumentException("Unexpected JS object.");
}
private JSONMap serializeObj(Extensions ext, Object val) {
return serializeObj(ext, val, false);
}
private void addObjectGroup(String name, Object val) {
if (val instanceof ObjectValue) {
var obj = (ObjectValue)val;
var id = objectToId.getOrDefault(obj, -1);
if (id < 0) return;
var ref = idToObject.get(id);
if (objectGroups.containsKey(name)) objectGroups.get(name).add(ref);
else objectGroups.put(name, new ArrayList<>(List.of(ref)));
ref.heldGroups.add(name);
}
}
private void releaseGroup(String name) {
var objs = objectGroups.remove(name);
if (objs != null) for (var obj : objs) {
if (obj.heldGroups.remove(name) && obj.shouldRelease()) {
var id = objectToId.remove(obj.obj);
if (id != null) idToObject.remove(id);
}
}
}
private Object deserializeArgument(JSONMap val) {
if (val.isString("objectId")) return idToObject.get(Integer.parseInt(val.string("objectId"))).obj;
else if (val.isString("unserializableValue")) switch (val.string("unserializableValue")) {
case "NaN": return Double.NaN;
case "-Infinity": return Double.NEGATIVE_INFINITY;
case "Infinity": return Double.POSITIVE_INFINITY;
case "-0": return -0.;
}
var res = val.get("value");
if (res == null) return null;
else return JSON.toJs(res);
}
private JSONMap serializeException(Extensions ext, EngineException err) {
String text = null;
try {
text = Values.toString(Context.of(ext), err.value);
}
catch (EngineException e) {
text = "[error while stringifying]";
}
var res = new JSONMap()
.set("exceptionId", nextId())
.set("exception", serializeObj(ext, err.value))
.set("text", text);
return res;
}
private void resume(State state) {
try {
@ -496,11 +330,11 @@ public class SimpleDebugger implements Debugger {
close();
}
}
private void pauseDebug(Context ctx, Breakpoint bp) {
private void pauseDebug(Environment env, Breakpoint bp) {
try {
state = State.PAUSED_NORMAL;
var map = new JSONMap()
.set("callFrames", serializeFrames(ctx))
.set("callFrames", serializeFrames(env))
.set("reason", "debugCommand");
if (bp != null) map.set("hitBreakpoints", new JSONList().add(bp.id + ""));
@ -511,11 +345,11 @@ public class SimpleDebugger implements Debugger {
close();
}
}
private void pauseException(Context ctx) {
private void pauseException(Environment env) {
try {
state = State.PAUSED_EXCEPTION;
var map = new JSONMap()
.set("callFrames", serializeFrames(ctx))
.set("callFrames", serializeFrames(env))
.set("reason", "exception");
ws.send(new V8Event("Debugger.paused", map));
@ -540,26 +374,19 @@ public class SimpleDebugger implements Debugger {
}
}
private Extensions sanitizeEnvironment(Extensions ext) {
var res = ext.child();
res.remove(EventLoop.KEY);
res.remove(DebugContext.KEY);
res.add(DebugContext.IGNORE);
return res;
static Environment sanitizeEnvironment(Environment env) {
return env.child().remove(EventLoop.KEY).remove(DebugContext.KEY).add(DebugContext.IGNORE);
}
private RunResult run(DebugFrame codeFrame, String code) {
if (codeFrame == null) return new RunResult(null, code, new EngineException("Invalid code frame!"));
var engine = new Engine();
var env = codeFrame.frame.ctx.extensions.copy();
env.remove(DebugContext.KEY);
env.remove(EventLoop.KEY);
env.remove(GlobalScope.KEY);
env.add(EventLoop.KEY, engine);
env.add(GlobalScope.KEY, new GlobalScope(codeFrame.local));
var env = codeFrame.frame.env.child()
.remove(DebugContext.KEY)
.add(DebugContext.IGNORE)
.add(EventLoop.KEY, engine)
.add(GlobalScope.KEY, new GlobalScope(codeFrame.local));
var awaiter = engine.pushMsg(false, env, new Filename("jscript", "eval"), code, codeFrame.frame.thisArg, codeFrame.frame.args);
@ -571,20 +398,19 @@ public class SimpleDebugger implements Debugger {
catch (SyntaxException e) { return new RunResult(env, null, EngineException.ofSyntax(e.toString())); }
}
private ObjectValue vscodeAutoSuggest(Extensions ext, Object target, String query, boolean variable) {
private ObjectValue vscodeAutoSuggest(Environment env, Object target, String query, boolean variable) {
var res = new ArrayValue();
var passed = new HashSet<String>();
var tildas = "~";
var ctx = Context.of(ext);
if (target == null) target = GlobalScope.get(ext);
if (target == null) target = GlobalScope.get(env);
for (var proto = target; proto != null && proto != Values.NULL; proto = Values.getPrototype(ctx, proto)) {
for (var el : Values.getMembers(ctx, proto, true, true)) {
var strKey = Values.toString(ctx, el);
for (var proto = target; proto != null && proto != Values.NULL; proto = Values.getPrototype(env, proto)) {
for (var el : Values.getMembers(env, proto, true, true)) {
var strKey = Values.toString(env, el);
if (passed.contains(strKey)) continue;
passed.add(strKey);
var val = Values.getMember(ctx, Values.getMemberDescriptor(ctx, proto, el), "value");
var val = Values.getMember(env, Values.getMemberDescriptor(env, proto, el), "value");
var desc = new ObjectValue();
var sortText = "";
if (strKey.startsWith(query)) sortText += "0@";
@ -594,27 +420,27 @@ public class SimpleDebugger implements Debugger {
else sortText += "4@";
sortText += tildas + strKey;
desc.defineProperty(ctx, "label", strKey);
desc.defineProperty(ctx, "sortText", sortText);
desc.defineProperty(env, "label", strKey);
desc.defineProperty(env, "sortText", sortText);
if (val instanceof FunctionValue) {
if (strKey.equals("constructor")) desc.defineProperty(ctx, "type", "name");
else desc.defineProperty(ctx, "type", variable ? "function" : "method");
if (strKey.equals("constructor")) desc.defineProperty(env, "type", "name");
else desc.defineProperty(env, "type", variable ? "function" : "method");
}
else desc.defineProperty(ctx, "type", variable ? "variable" : "property");
else desc.defineProperty(env, "type", variable ? "variable" : "property");
switch (Values.type(val)) {
case "number":
case "boolean":
desc.defineProperty(ctx, "detail", Values.toString(ctx, val));
desc.defineProperty(env, "detail", Values.toString(env, val));
break;
case "object":
if (val == Values.NULL) desc.defineProperty(ctx, "detail", "null");
if (val == Values.NULL) desc.defineProperty(env, "detail", "null");
else try {
desc.defineProperty(ctx, "detail", Values.getMemberPath(ctx, target, "constructor", "name"));
desc.defineProperty(env, "detail", Values.getMemberPath(env, target, "constructor", "name"));
}
catch (IllegalArgumentException e) {
desc.defineProperty(ctx, "detail", "object");
desc.defineProperty(env, "detail", "object");
}
break;
case "function": {
@ -624,15 +450,15 @@ public class SimpleDebugger implements Debugger {
type += "?";
}
type += ")";
desc.defineProperty(ctx, "detail", type);
desc.defineProperty(env, "detail", type);
break;
}
default:
desc.defineProperty(ctx, "type", Values.type(val));
desc.defineProperty(env, "type", Values.type(val));
break;
}
res.set(ctx, res.size(), desc);
res.set(env, res.size(), desc);
}
tildas += "~";
@ -640,8 +466,8 @@ public class SimpleDebugger implements Debugger {
}
var resObj = new ObjectValue();
resObj.defineProperty(ctx, "result", res);
resObj.defineProperty(ctx, "isArray", target instanceof ArrayValue);
resObj.defineProperty(env, "result", res);
resObj.defineProperty(env, "isArray", target instanceof ArrayValue);
return resObj;
}
@ -679,13 +505,12 @@ public class SimpleDebugger implements Debugger {
idToFrame.clear();
codeFrameToFrame.clear();
idToObject.clear();
objectToId.clear();
objectGroups.clear();
objects.clear();
pendingPause = false;
stepOutFrame = currFrame = null;
frames.clear();
stepOutFrame = null;
stepOutPtr = 0;
for (var ctx : contexts.keySet()) ctx.detachDebugger(this);
@ -744,7 +569,6 @@ public class SimpleDebugger implements Debugger {
var bpt = new Breakpoint(nextId(), regex, line, col, cond);
idToBreakpoint.put(bpt.id, bpt);
for (var el : mappings.entrySet()) {
bpt.addFunc(el.getKey(), el.getValue());
}
@ -794,8 +618,8 @@ public class SimpleDebugger implements Debugger {
@Override public synchronized void stepInto(V8Message msg) throws IOException {
if (state == State.RESUMED) ws.send(new V8Error("Debugger is resumed."));
else {
stepOutFrame = currFrame;
stepOutPtr = currFrame.frame.codePtr;
stepOutFrame = frames.get(frames.size() - 1);
stepOutPtr = stepOutFrame.frame.codePtr;
resume(State.STEPPING_IN);
ws.send(msg.respond());
}
@ -803,8 +627,8 @@ public class SimpleDebugger implements Debugger {
@Override public synchronized void stepOut(V8Message msg) throws IOException {
if (state == State.RESUMED) ws.send(new V8Error("Debugger is resumed."));
else {
stepOutFrame = currFrame;
stepOutPtr = currFrame.frame.codePtr;
stepOutFrame = frames.get(frames.size() - 1);
stepOutPtr = stepOutFrame.frame.codePtr;
resume(State.STEPPING_OUT);
ws.send(msg.respond());
}
@ -812,8 +636,8 @@ public class SimpleDebugger implements Debugger {
@Override public synchronized void stepOver(V8Message msg) throws IOException {
if (state == State.RESUMED) ws.send(new V8Error("Debugger is resumed."));
else {
stepOutFrame = currFrame;
stepOutPtr = currFrame.frame.codePtr;
stepOutFrame = frames.get(frames.size() - 1);
stepOutPtr = stepOutFrame.frame.codePtr;
resume(State.STEPPING_OVER);
ws.send(msg.respond());
}
@ -827,34 +651,26 @@ public class SimpleDebugger implements Debugger {
var cf = idToFrame.get(cfId);
var res = run(cf, expr);
if (group != null) addObjectGroup(group, res.result);
if (group != null) objects.addToGroup(group, res.result);
if (res.error != null) ws.send(msg.respond(new JSONMap().set("exceptionDetails", serializeException(res.ext, res.error))));
else ws.send(msg.respond(new JSONMap().set("result", serializeObj(res.ext, res.result))));
if (res.error != null) ws.send(msg.respond(new JSONMap().set("exceptionDetails", objects.serializeException(res.ext, res.error))));
else ws.send(msg.respond(new JSONMap().set("result", objects.serialize(res.ext, res.result))));
}
@Override public synchronized void releaseObjectGroup(V8Message msg) throws IOException {
var group = msg.params.string("objectGroup");
releaseGroup(group);
objects.removeGroup(group);
ws.send(msg.respond());
}
@Override public synchronized void releaseObject(V8Message msg) throws IOException {
var id = Integer.parseInt(msg.params.string("objectId"));
var ref = idToObject.get(id);
ref.held = false;
if (ref.shouldRelease()) {
objectToId.remove(ref.obj);
idToObject.remove(id);
}
objects.release(id);
ws.send(msg.respond());
}
@Override public synchronized void getProperties(V8Message msg) throws IOException {
var ref = idToObject.get(Integer.parseInt(msg.params.string("objectId")));
var ref = objects.get(Integer.parseInt(msg.params.string("objectId")));
var obj = ref.obj;
var ext = ref.ext;
var ctx = Context.of(ext);
var env = ref.ext;
var res = new JSONList();
var own = true;
@ -866,17 +682,17 @@ public class SimpleDebugger implements Debugger {
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(ext, prop.getter));
if (prop.setter != null) propDesc.set("set", serializeObj(ext, prop.setter));
propDesc.set("name", Values.toString(env, key));
if (prop.getter != null) propDesc.set("get", objects.serialize(env, prop.getter));
if (prop.setter != null) propDesc.set("set", objects.serialize(env, prop.setter));
propDesc.set("enumerable", obj.memberEnumerable(key));
propDesc.set("configurable", obj.memberConfigurable(key));
propDesc.set("isOwn", true);
res.add(propDesc);
}
else {
propDesc.set("name", Values.toString(ctx, key));
propDesc.set("value", serializeObj(ext, Values.getMember(ctx, obj, key)));
propDesc.set("name", Values.toString(env, key));
propDesc.set("value", objects.serialize(env, Values.getMember(env, obj, key)));
propDesc.set("writable", obj.memberWritable(key));
propDesc.set("enumerable", obj.memberEnumerable(key));
propDesc.set("configurable", obj.memberConfigurable(key));
@ -885,12 +701,12 @@ public class SimpleDebugger implements Debugger {
}
}
var proto = Values.getPrototype(ctx, obj);
var proto = Values.getPrototype(env, obj);
if (own) {
var protoDesc = new JSONMap();
protoDesc.set("name", "__proto__");
protoDesc.set("value", serializeObj(ext, proto == null ? Values.NULL : proto));
protoDesc.set("value", objects.serialize(env, proto == null ? Values.NULL : proto));
protoDesc.set("writable", true);
protoDesc.set("enumerable", false);
protoDesc.set("configurable", false);
@ -911,14 +727,13 @@ public class SimpleDebugger implements Debugger {
.list("arguments", new JSONList())
.stream()
.map(v -> v.map())
.map(this::deserializeArgument)
.map(objects::deserializeArgument)
.collect(Collectors.toList());
var byValue = msg.params.bool("returnByValue", false);
var thisArgRef = idToObject.get(Integer.parseInt(msg.params.string("objectId")));
var thisArgRef = objects.get(Integer.parseInt(msg.params.string("objectId")));
var thisArg = thisArgRef.obj;
var ext = thisArgRef.ext;
var ctx = Context.of(ext);
var env = thisArgRef.ext;
while (true) {
var start = src.lastIndexOf("//# sourceURL=");
@ -934,27 +749,27 @@ public class SimpleDebugger implements Debugger {
else if (compare(src, VSCODE_SELF)) res = thisArg;
else if (compare(src, CHROME_GET_PROP_FUNC)) {
res = thisArg;
for (var el : JSON.parse(null, (String)args.get(0)).list()) res = Values.getMember(ctx, res, JSON.toJs(el));
for (var el : JSON.parse(null, (String)args.get(0)).list()) res = Values.getMember(env, res, JSON.toJs(el));
}
else if (compare(src, CHROME_GET_PROP_FUNC_2)) {
res = Values.call(ctx, args.get(0), thisArg);
res = Values.call(env, args.get(0), thisArg);
}
else if (compare(src, VSCODE_CALL)) {
var func = (FunctionValue)(args.size() < 1 ? null : args.get(0));
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ext, func.call(ctx, thisArg)))));
ws.send(msg.respond(new JSONMap().set("result", objects.serialize(env, func.call(env, thisArg)))));
}
else if (compare(src, VSCODE_AUTOCOMPLETE)) {
var target = args.get(0);
if (target == null) target = thisArg;
res = vscodeAutoSuggest(ext, target, Values.toString(ctx, args.get(1)), Values.toBoolean(args.get(2)));
res = vscodeAutoSuggest(env, target, Values.toString(env, args.get(1)), Values.toBoolean(args.get(2)));
}
else {
ws.send(new V8Error("Please use well-known functions with callFunctionOn"));
return;
}
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ext, res, byValue))));
ws.send(msg.respond(new JSONMap().set("result", objects.serialize(env, res, byValue))));
}
catch (EngineException e) { ws.send(msg.respond(new JSONMap().set("exceptionDetails", serializeException(ext, e)))); }
catch (EngineException e) { ws.send(msg.respond(new JSONMap().set("exceptionDetails", objects.serializeException(env, e)))); }
}
@Override public synchronized void runtimeEnable(V8Message msg) throws IOException {
@ -977,7 +792,7 @@ public class SimpleDebugger implements Debugger {
}
mappings.put(body, map);
}
@Override public boolean onInstruction(Context ctx, Frame cf, Instruction instruction, Object returnVal, EngineException error, boolean caught) {
@Override public boolean onInstruction(Environment env, Frame cf, Instruction instruction, Object returnVal, EngineException error, boolean caught) {
if (!enabled) return false;
boolean isBreakpointable;
@ -988,7 +803,7 @@ public class SimpleDebugger implements Debugger {
synchronized (this) {
frame = getFrame(cf);
var map = DebugContext.get(ctx).getMap(frame.frame.function);
var map = DebugContext.get(env).getMap(frame.frame.function);
frame.updateLoc(map.toLocation(frame.frame.codePtr));
loc = frame.location;
@ -996,27 +811,27 @@ public class SimpleDebugger implements Debugger {
isBreakpointable = loc != null && (bptType.shouldStepIn());
if (error != null && (execptionType == CatchType.ALL || execptionType == CatchType.UNCAUGHT && !caught)) {
pauseException(ctx);
pauseException(env);
}
else if (
loc != null &&
(state == State.STEPPING_IN || state == State.STEPPING_OVER) &&
returnVal != Values.NO_RETURN && stepOutFrame == frame
) {
pauseDebug(ctx, null);
pauseDebug(env, null);
}
else if (isBreakpointable && bpLocs.containsKey(loc)) {
for (var bp : bpLocs.get(loc)) {
var ok = bp.condition == null ? true : Values.toBoolean(run(currFrame, bp.condition).result);
if (ok) pauseDebug(ctx, bp);
var ok = bp.condition == null ? true : Values.toBoolean(run(frames.get(frames.size() - 1), bp.condition).result);
if (ok) pauseDebug(env, bp);
}
}
// else if (isBreakpointable && tmpBreakpts.remove(loc)) pauseDebug(ctx, null);
else if (isBreakpointable && pendingPause) {
pauseDebug(ctx, null);
pauseDebug(env, null);
pendingPause = false;
}
else if (instruction.type == Type.NOP && instruction.match("debug")) pauseDebug(ctx, null);
else if (instruction.type == Type.NOP && instruction.match("debug")) pauseDebug(env, null);
}
@ -1039,11 +854,11 @@ public class SimpleDebugger implements Debugger {
else if (stepOutPtr != frame.frame.codePtr) {
if (state == State.STEPPING_IN && bptType.shouldStepIn()) {
pauseDebug(ctx, null);
pauseDebug(env, null);
break;
}
else if (state == State.STEPPING_OVER && bptType.shouldStepOver()) {
pauseDebug(ctx, null);
pauseDebug(env, null);
break;
}
}
@ -1056,28 +871,32 @@ public class SimpleDebugger implements Debugger {
return false;
}
@Override public void onFramePush(Context ctx, Frame frame) {
var prevFrame = currFrame;
updateFrames(ctx);
@Override public void onFramePush(Environment env, Frame frame) {
var prevFrame = frames.get(frames.size() - 1);
var newFrame = getFrame(frame);
frames.add(newFrame);
if (stepOutFrame != null && stepOutFrame.frame == prevFrame.frame && state == State.STEPPING_IN) {
stepOutFrame = currFrame;
stepOutFrame = newFrame;
}
}
@Override public void onFramePop(Context ctx, Frame frame) {
updateFrames(ctx);
@Override public void onFramePop(Environment env, Frame frame) {
frames.remove(frames.size() - 1);
try { idToFrame.remove(codeFrameToFrame.remove(frame).id); }
catch (NullPointerException e) { }
if (ctx.stackSize == 0) {
if (frames.size() == 0) {
if (state == State.PAUSED_EXCEPTION || state == State.PAUSED_NORMAL) resume(State.RESUMED);
}
else if (stepOutFrame != null && stepOutFrame.frame == frame && state == State.STEPPING_OUT) {
state = State.STEPPING_IN;
stepOutFrame = currFrame;
stepOutFrame = frames.get(frames.size() - 1);
}
}
@Override public List<Frame> getStackFrame() {
return frames.stream().map(v -> v.frame).collect(Collectors.toList());
}
public SimpleDebugger attach(DebugContext ctx) {
ctx.attachDebugger(this);

View File

@ -1,7 +1,7 @@
package me.topchetoeu.jscript.utils.filesystem;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.Key;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.environment.Key;
public interface Filesystem {
public static final Key<Filesystem> KEY = new Key<>();
@ -12,7 +12,7 @@ public interface Filesystem {
FileStat stat(String path);
void close();
public static Filesystem get(Extensions exts) {
public static Filesystem get(Environment exts) {
return exts.get(KEY);
}
}

View File

@ -2,32 +2,14 @@ package me.topchetoeu.jscript.utils.interop;
import java.lang.reflect.Array;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.Key;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.values.NativeWrapper;
import me.topchetoeu.jscript.runtime.values.Values;
public class Arguments implements Extensions {
public class Arguments {
public final Object self;
public final Object[] args;
public final Context ctx;
@Override public <T> void add(Key<T> key, T obj) {
ctx.add(key, obj);
}
@Override public <T> T get(Key<T> key) {
return ctx.get(key);
}
@Override public boolean has(Key<?> key) {
return ctx.has(key);
}
@Override public boolean remove(Key<?> key) {
return ctx.remove(key);
}
@Override public Iterable<Key<?>> keys() {
return ctx.keys();
}
public final Environment env;
public int n() {
return args.length;
@ -41,7 +23,7 @@ public class Arguments implements Extensions {
return convert(-1, type);
}
public <T> T convert(int i, Class<T> type) {
return Values.convert(ctx, get(i), type);
return Values.convert(env, get(i), type);
}
public Object get(int i, boolean unwrap) {
Object res = null;
@ -63,7 +45,7 @@ public class Arguments implements Extensions {
public Arguments slice(int start) {
var res = new Object[Math.max(0, args.length - start)];
for (int j = start; j < args.length; j++) res[j - start] = get(j);
return new Arguments(ctx, args, res);
return new Arguments(env, args, res);
}
@SuppressWarnings("unchecked")
@ -113,26 +95,26 @@ public class Arguments implements Extensions {
return res;
}
public String getString(int i) { return Values.toString(ctx, get(i)); }
public String getString(int i) { return Values.toString(env, get(i)); }
public boolean getBoolean(int i) { return Values.toBoolean(get(i)); }
public int getInt(int i) { return (int)Values.toNumber(ctx, get(i)); }
public long getLong(int i) { return (long)Values.toNumber(ctx, get(i)); }
public double getDouble(int i) { return Values.toNumber(ctx, get(i)); }
public float getFloat(int i) { return (float)Values.toNumber(ctx, get(i)); }
public int getInt(int i) { return (int)Values.toNumber(env, get(i)); }
public long getLong(int i) { return (long)Values.toNumber(env, get(i)); }
public double getDouble(int i) { return Values.toNumber(env, get(i)); }
public float getFloat(int i) { return (float)Values.toNumber(env, get(i)); }
public int getInt(int i, int def) {
var res = get(i);
if (res == null) return def;
else return (int)Values.toNumber(ctx, res);
else return (int)Values.toNumber(env, res);
}
public String getString(int i, String def) {
var res = get(i);
if (res == null) return def;
else return Values.toString(ctx, res);
else return Values.toString(env, res);
}
public Arguments(Context ctx, Object thisArg, Object... args) {
this.ctx = ctx;
public Arguments(Environment env, Object thisArg, Object... args) {
this.env = env;
this.args = args;
this.self = thisArg;
}

View File

@ -12,10 +12,8 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Copyable;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.Key;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.environment.Key;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.exceptions.InterruptException;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
@ -24,7 +22,7 @@ import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Symbol;
import me.topchetoeu.jscript.runtime.values.Values;
public class NativeWrapperProvider implements Copyable {
public class NativeWrapperProvider {
public static final Key<NativeWrapperProvider> KEY = new Key<>();
private final HashMap<Class<?>, FunctionValue> constructors = new HashMap<>();
@ -35,7 +33,7 @@ public class NativeWrapperProvider implements Copyable {
private final HashMap<Class<?>, Class<?>> interfaceToProxy = new HashMap<>();
private final HashSet<Class<?>> ignore = new HashSet<>();
private static Object call(Context ctx, String name, Method method, Object thisArg, Object... args) {
private static Object call(Environment ctx, String name, Method method, Object thisArg, Object... args) {
try {
var realArgs = new Object[method.getParameterCount()];
System.arraycopy(args, 0, realArgs, 0, realArgs.length);
@ -60,7 +58,7 @@ public class NativeWrapperProvider implements Copyable {
}
}
private static FunctionValue create(String name, Method method) {
return new NativeFunction(name, args -> call(args.ctx, name, method, args.self, args));
return new NativeFunction(name, args -> call(args.env, name, method, args.self, args));
}
private static void checkSignature(Method method, boolean forceStatic, Class<?> ...params) {
if (forceStatic && !Modifier.isStatic(method.getModifiers())) throw new IllegalArgumentException(String.format(
@ -335,8 +333,8 @@ public class NativeWrapperProvider implements Copyable {
var parentConstr = getConstr(parent);
if (parentProto != null && parentConstr != null) {
Values.setPrototype(Extensions.EMPTY, proto, parentProto);
Values.setPrototype(Extensions.EMPTY, constr, parentConstr);
Values.setPrototype(Environment.empty(), proto, parentProto);
Values.setPrototype(Environment.empty(), constr, parentConstr);
return;
}
@ -450,7 +448,7 @@ public class NativeWrapperProvider implements Copyable {
public NativeWrapperProvider() { }
public static NativeWrapperProvider get(Extensions ext) {
public static NativeWrapperProvider get(Environment ext) {
return ext.get(KEY);
}
}

View File

@ -1,6 +1,6 @@
package me.topchetoeu.jscript.utils.modules;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.environment.Environment;
public abstract class Module {
private Object value;
@ -9,9 +9,9 @@ public abstract class Module {
public Object value() { return value; }
public boolean loaded() { return loaded; }
protected abstract Object onLoad(Context ctx);
protected abstract Object onLoad(Environment ctx);
public void load(Context ctx) {
public void load(Environment ctx) {
if (loaded) return;
this.value = onLoad(ctx);
this.loaded = true;

View File

@ -3,9 +3,8 @@ package me.topchetoeu.jscript.utils.modules;
import java.util.HashMap;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.Key;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.environment.Key;
import me.topchetoeu.jscript.runtime.scope.GlobalScope;
import me.topchetoeu.jscript.utils.filesystem.Filesystem;
import me.topchetoeu.jscript.utils.filesystem.Mode;
@ -14,34 +13,34 @@ public interface ModuleRepo {
public static final Key<ModuleRepo> KEY = new Key<>();
public static final Key<String> CWD = new Key<>();
public Module getModule(Context ctx, String cwd, String name);
public Module getModule(Environment ctx, String cwd, String name);
public static ModuleRepo ofFilesystem(Filesystem fs) {
var modules = new HashMap<String, Module>();
return (ctx, cwd, name) -> {
return (env, cwd, name) -> {
name = fs.normalize(cwd, name);
var filename = Filename.parse(name);
var src = fs.open(name, Mode.READ).readToString();
if (modules.containsKey(name)) return modules.get(name);
var env = Context.clean(ctx.extensions).child();
env.add(CWD, fs.normalize(name, ".."));
var glob = env.get(GlobalScope.KEY);
env.add(GlobalScope.KEY, glob.child());
var moduleEnv = env.child()
.add(CWD, fs.normalize(name, ".."))
.add(GlobalScope.KEY, env.hasNotNull(GlobalScope.KEY) ? env.get(GlobalScope.KEY).child() : new GlobalScope());
var mod = new SourceModule(filename, src, env);
var mod = new SourceModule(filename, src, moduleEnv);
modules.put(name, mod);
return mod;
};
}
public static String cwd(Extensions exts) {
return exts.init(CWD, "/");
public static String cwd(Environment exts) {
exts.init(CWD, "/");
return "/";
}
public static ModuleRepo get(Extensions exts) {
public static ModuleRepo get(Environment exts) {
return exts.get(KEY);
}
}

View File

@ -2,14 +2,14 @@ package me.topchetoeu.jscript.utils.modules;
import java.util.HashMap;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
public class RootModuleRepo implements ModuleRepo {
public final HashMap<String, ModuleRepo> repos = new HashMap<>();
@Override
public Module getModule(Context ctx, String cwd, String name) {
public Module getModule(Environment env, String cwd, String name) {
var i = name.indexOf(":");
String repoName, modName;
@ -25,6 +25,6 @@ public class RootModuleRepo implements ModuleRepo {
var repo = repos.get(repoName);
if (repo == null) throw EngineException.ofError("ModuleError", "Couldn't find module repo '" + repoName + "'.");
return repo.getModule(ctx, cwd, modName);
return repo.getModule(env, cwd, modName);
}
}

View File

@ -1,21 +1,20 @@
package me.topchetoeu.jscript.utils.modules;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.Compiler;
import me.topchetoeu.jscript.runtime.environment.Environment;
public class SourceModule extends Module {
public final Filename filename;
public final String source;
public final Extensions ext;
public final Environment ext;
@Override
protected Object onLoad(Context ctx) {
var res = new Context(ext).compile(filename, source);
return res.call(ctx);
protected Object onLoad(Environment env) {
return Compiler.compile(env, filename, source).call(env);
}
public SourceModule(Filename filename, String source, Extensions ext) {
public SourceModule(Filename filename, String source, Environment ext) {
this.filename = filename;
this.source = source;
this.ext = ext;

View File

@ -1,7 +1,7 @@
package me.topchetoeu.jscript.utils.permissions;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.Key;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.environment.Key;
public interface PermissionsProvider {
public static final Key<PermissionsProvider> KEY = new Key<>();
@ -20,7 +20,7 @@ public interface PermissionsProvider {
return hasPermission(new Permission(perm, matcher));
}
public static PermissionsProvider get(Extensions exts) {
public static PermissionsProvider get(Environment exts) {
return (perm, value) -> {
if (exts.hasNotNull(KEY)) return exts.get(KEY).hasPermission(perm);
else return true;