feat: implement map and set polyfills in java

This commit is contained in:
2023-09-26 08:31:27 +03:00
parent cf36b7adc5
commit e16c0fedb1
21 changed files with 208 additions and 201 deletions

View File

@@ -206,7 +206,7 @@ public class ArrayValue extends ObjectValue implements Iterable<Object> {
}
public ArrayValue(Context ctx, Object ...values) {
this();
values = new Object[values.length];
this.values = new Object[values.length];
size = values.length;
for (var i = 0; i < size; i++) this.values[i] = Values.normalize(ctx, values[i]);

View File

@@ -519,7 +519,7 @@ public class Values {
throw new ConvertException(type(obj), clazz.getSimpleName());
}
public static Iterable<Object> toJavaIterable(Context ctx, Object obj) throws InterruptedException {
public static Iterable<Object> toJavaIterable(Context ctx, Object obj) {
return () -> {
try {
var symbol = ctx.env.symbol("Symbol.iterator");
@@ -527,7 +527,7 @@ public class Values {
var iteratorFunc = getMember(ctx, obj, symbol);
if (!isFunction(iteratorFunc)) return Collections.emptyIterator();
var iterator = iteratorFunc instanceof FunctionValue ?
((FunctionValue)iteratorFunc).call(ctx, iteratorFunc, obj) :
((FunctionValue)iteratorFunc).call(ctx, obj, obj) :
iteratorFunc;
var nextFunc = getMember(ctx, call(ctx, iteratorFunc, obj), "next");
@@ -536,7 +536,7 @@ public class Values {
return new Iterator<Object>() {
private Object value = null;
public boolean consumed = true;
private FunctionValue next = function(iterator);
private FunctionValue next = (FunctionValue)nextFunc;
private void loadNext() throws InterruptedException {
if (next == null) value = null;
@@ -588,9 +588,8 @@ public class Values {
};
}
public static ObjectValue fromJavaIterable(Context ctx, Iterable<?> iterable) throws InterruptedException {
public static ObjectValue fromJavaIterator(Context ctx, Iterator<?> it) throws InterruptedException {
var res = new ObjectValue();
var it = iterable.iterator();
try {
var key = getMember(ctx, getMember(ctx, ctx.env.proto("symbol"), "constructor"), "iterator");
@@ -606,6 +605,10 @@ public class Values {
return res;
}
public static ObjectValue fromJavaIterable(Context ctx, Iterable<?> it) throws InterruptedException {
return fromJavaIterator(ctx, it.iterator());
}
private static void printValue(Context ctx, Object val, HashSet<Object> passed, int tab) throws InterruptedException {
if (passed.contains(val)) {
System.out.print("[circular]");

View File

@@ -33,7 +33,7 @@ public class Overload {
return new Overload(
(ctx, th, args) -> method.newInstance(args),
method.isVarArgs(), passThis,
Modifier.isStatic(method.getModifiers()) ? null : method.getDeclaringClass(),
null,
method.getParameterTypes()
);
}

View File

@@ -5,6 +5,7 @@ import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.engine.values.Values;
@@ -56,7 +57,15 @@ public class OverloadFunction extends FunctionValue {
}
var thisArgType = overload.passThis ? overload.params[consumesEngine ? 1 : 0] : overload.thisArg;
Object _this = thisArgType == null ? null : Values.convert(ctx, thisArg, thisArgType);
Object _this;
try {
_this = thisArgType == null ? null : Values.convert(ctx, thisArg, thisArgType);
}
catch (ConvertException e) {
if (overloads.size() > 1) continue loop;
else throw EngineException.ofType(String.format("This argument can't be converted from %s to %s", e.source, e.target));
}
if (consumesEngine) newArgs[0] = ctx;
if (overload.passThis) {
@@ -74,15 +83,19 @@ public class OverloadFunction extends FunctionValue {
continue;
}
catch (InvocationTargetException e) {
var loc = new Location(0, 0, "<internal>");
if (e.getTargetException() instanceof EngineException) {
throw ((EngineException)e.getTargetException());
throw ((EngineException)e.getTargetException()).add(name, loc);
}
else if (e.getTargetException() instanceof NullPointerException) {
throw EngineException.ofType("Unexpected value of 'undefined'.").add(name, loc);
}
else {
throw EngineException.ofError(e.getTargetException().getMessage());
throw EngineException.ofError(e.getTargetException().getMessage()).add(name, loc);
}
}
catch (ReflectiveOperationException e) {
throw EngineException.ofError(e.getMessage());
throw EngineException.ofError(e.getMessage()).add(name, new Location(0, 0, "<internal>"));
}
catch (Exception e) {
throw e;

View File

@@ -14,6 +14,7 @@ import me.topchetoeu.jscript.interop.NativeGetter;
import me.topchetoeu.jscript.interop.NativeSetter;
public class ArrayPolyfill {
@Native("@@Symbol.typeName") public final String name = "AsyncFunction";
@NativeGetter(thisArg = true) public static int length(Context ctx, ArrayValue thisArg) throws InterruptedException {
return thisArg.size();
}

View File

@@ -7,11 +7,13 @@ import me.topchetoeu.jscript.engine.values.CodeFunction;
import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.engine.values.NativeFunction;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.interop.Native;
public class AsyncFunctionPolyfill extends FunctionValue {
public final FunctionValue factory;
public static class AsyncHelper {
@Native("@@Symbol.typeName") public final String name = "AsyncFunction";
public PromisePolyfill promise = new PromisePolyfill();
public CodeFrame frame;

View File

@@ -16,6 +16,7 @@ public class AsyncGeneratorPolyfill extends FunctionValue {
public final FunctionValue factory;
public static class AsyncGenerator {
@Native("@@Symbol.typeName") public final String name = "AsyncGenerator";
private int state = 0;
private boolean done = false;
private PromisePolyfill currPromise;

View File

@@ -7,6 +7,8 @@ import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeConstructor;
public class BooleanPolyfill {
@Native("@@Symbol.typeName") public final String name = "Boolean";
public static final BooleanPolyfill TRUE = new BooleanPolyfill(true);
public static final BooleanPolyfill FALSE = new BooleanPolyfill(false);

View File

@@ -8,7 +8,9 @@ import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.interop.Native;
public class FunctionPolyfill {
@Native(thisArg = true) public static Object apply(Context ctx, FunctionValue func, Object thisArg, ArrayValue args) throws InterruptedException {
@Native("@@Symbol.typeName") public final String name = "Function";
@Native(thisArg = true) public static Object apply(Context ctx, FunctionValue func, Object thisArg, ArrayValue args) throws InterruptedException {
return func.call(ctx, thisArg, args.toArray());
}
@Native(thisArg = true) public static Object call(Context ctx, FunctionValue func, Object thisArg, Object... args) throws InterruptedException {

View File

@@ -18,6 +18,8 @@ public class GeneratorPolyfill extends FunctionValue {
private boolean done = false;
public CodeFrame frame;
@Native("@@Symbol.typeName") public final String name = "Generator";
private ObjectValue next(Context ctx, Object inducedValue, Object inducedReturn, Object inducedError) throws InterruptedException {
if (done) {
if (inducedError != Runners.NO_RETURN) throw new EngineException(inducedError);

View File

@@ -14,7 +14,7 @@ import me.topchetoeu.jscript.interop.Native;
public class Internals {
public final Environment targetEnv;
@Native public final FunctionValue object, function, promise, array, bool, number, string;
@Native public final FunctionValue object, function, promise, array, bool, number, string, map, set;
@Native public void markSpecial(FunctionValue ...funcs) {
for (var func : funcs) {
@@ -160,5 +160,7 @@ public class Internals {
this.bool = targetEnv.wrappersProvider.getConstr(BooleanPolyfill.class);
this.number = targetEnv.wrappersProvider.getConstr(NumberPolyfill.class);
this.string = targetEnv.wrappersProvider.getConstr(StringPolyfill.class);
this.map = targetEnv.wrappersProvider.getConstr(MapPolyfill.class);
this.set = targetEnv.wrappersProvider.getConstr(SetPolyfill.class);
}
}

View File

@@ -0,0 +1,78 @@
package me.topchetoeu.jscript.polyfills;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.stream.Collectors;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.values.ArrayValue;
import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeGetter;
public class MapPolyfill {
@Native("@@Symbol.typeName") public final String name = "Map";
private LinkedHashMap<Object, Object> map = new LinkedHashMap<>();
@Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) throws InterruptedException {
return this.entries(ctx);
}
@Native public void clear() {
map.clear();
}
@Native public boolean delete(Object key) {
if (map.containsKey(key)) {
map.remove(key);
return true;
}
return false;
}
@Native public ObjectValue entries(Context ctx) throws InterruptedException {
var res = map.entrySet().stream().map(v -> {
return new ArrayValue(ctx, v.getKey(), v.getValue());
}).collect(Collectors.toList());
return Values.fromJavaIterator(ctx, res.iterator());
}
@Native public ObjectValue keys(Context ctx) throws InterruptedException {
var res = new ArrayList<>(map.keySet());
return Values.fromJavaIterator(ctx, res.iterator());
}
@Native public ObjectValue values(Context ctx) throws InterruptedException {
var res = new ArrayList<>(map.values());
return Values.fromJavaIterator(ctx, res.iterator());
}
@Native public Object get(Object key) {
return map.get(key);
}
@Native public MapPolyfill set(Object key, Object val) {
map.put(key, val);
return this;
}
@Native public boolean has(Object key) {
return map.containsKey(key);
}
@NativeGetter public int size() {
return map.size();
}
@NativeGetter public void forEach(Context ctx, FunctionValue func, Object thisArg) throws InterruptedException {
var keys = new ArrayList<>(map.keySet());
for (var el : keys) func.call(ctx, thisArg, el, map.get(el), this);
}
@Native public MapPolyfill(Context ctx, Object iterable) throws InterruptedException {
for (var el : Values.toJavaIterable(ctx, iterable)) {
try {
set(Values.getMember(ctx, el, 0), Values.getMember(ctx, el, 1));
}
catch (IllegalArgumentException e) { }
}
}
}

View File

@@ -7,6 +7,8 @@ import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeConstructor;
public class NumberPolyfill {
@Native("@@Symbol.typeName") public final String name = "Number";
@Native public static final double EPSILON = java.lang.Math.ulp(1.0);
@Native public static final double MAX_SAFE_INTEGER = 9007199254740991.;
@Native public static final double MIN_SAFE_INTEGER = -MAX_SAFE_INTEGER;

View File

@@ -11,6 +11,8 @@ import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeConstructor;
public class ObjectPolyfill {
@Native("@@Symbol.typeName") public final String name = "Object";
@Native public static ObjectValue assign(Context ctx, ObjectValue dst, Object... src) throws InterruptedException {
for (var obj : src) {
for (var key : Values.getMembers(ctx, obj, true, true)) {
@@ -204,9 +206,9 @@ public class ObjectPolyfill {
@NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object arg) throws InterruptedException {
if (arg == null || arg == Values.NULL) return new ObjectValue();
else if (arg instanceof Boolean) return BooleanPolyfill.constructor(ctx, thisArg, arg);
else if (arg instanceof Number) return Values.callNew(ctx, ctx.env.global.get(ctx, "Number"), arg);
else if (arg instanceof String) return Values.callNew(ctx, ctx.env.global.get(ctx, "String"), arg);
else if (arg instanceof Symbol) return Values.callNew(ctx, ctx.env.global.get(ctx, "Symbol"), arg);
else if (arg instanceof Number) return NumberPolyfill.constructor(ctx, thisArg, arg);
else if (arg instanceof String) return StringPolyfill.constructor(ctx, thisArg, arg);
// else if (arg instanceof Symbol) return SymbolPolyfill.constructor(ctx, thisArg, arg);
else return arg;
}
}

View File

@@ -28,6 +28,8 @@ public class PromisePolyfill {
}
}
@Native("@@Symbol.typeName") public final String name = "Promise";
@Native("resolve")
public static PromisePolyfill ofResolved(Context ctx, Object val) throws InterruptedException {
var res = new PromisePolyfill();

View File

@@ -0,0 +1,63 @@
package me.topchetoeu.jscript.polyfills;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.stream.Collectors;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.values.ArrayValue;
import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeGetter;
public class SetPolyfill {
@Native("@@Symbol.typeName") public final String name = "Set";
private LinkedHashSet<Object> set = new LinkedHashSet<>();
@Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) throws InterruptedException {
return this.values(ctx);
}
@Native public ObjectValue entries(Context ctx) throws InterruptedException {
var res = set.stream().map(v -> new ArrayValue(ctx, v, v)).collect(Collectors.toList());
return Values.fromJavaIterator(ctx, res.iterator());
}
@Native public ObjectValue keys(Context ctx) throws InterruptedException {
var res = new ArrayList<>(set);
return Values.fromJavaIterator(ctx, res.iterator());
}
@Native public ObjectValue values(Context ctx) throws InterruptedException {
var res = new ArrayList<>(set);
return Values.fromJavaIterator(ctx, res.iterator());
}
@Native public Object add(Object key) {
return set.add(key);
}
@Native public boolean delete(Object key) {
return set.remove(key);
}
@Native public boolean has(Object key) {
return set.contains(key);
}
@Native public void clear() {
set.clear();
}
@NativeGetter public int size() {
return set.size();
}
@NativeGetter public void forEach(Context ctx, FunctionValue func, Object thisArg) throws InterruptedException {
var keys = new ArrayList<>(set);
for (var el : keys) func.call(ctx, thisArg, el, el, this);
}
@Native public SetPolyfill(Context ctx, Object iterable) throws InterruptedException {
for (var el : Values.toJavaIterable(ctx, iterable)) add(el);
}
}

View File

@@ -14,6 +14,8 @@ import me.topchetoeu.jscript.interop.NativeGetter;
// TODO: implement index wrapping properly
public class StringPolyfill {
@Native("@@Symbol.typeName") public final String name = "String";
public final String value;
private static String passThis(String funcName, Object val) {