diff --git a/src/java/me/topchetoeu/jscript/common/json/JSON.java b/src/java/me/topchetoeu/jscript/common/json/JSON.java index 0b639f0..2aaa8f1 100644 --- a/src/java/me/topchetoeu/jscript/common/json/JSON.java +++ b/src/java/me/topchetoeu/jscript/common/json/JSON.java @@ -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.Context; +import me.topchetoeu.jscript.runtime.Extensions; 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(Context ctx, Object val, HashSet prev) { + private static JSONElement fromJs(Extensions ext, Object val, HashSet 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); @@ -44,7 +44,7 @@ public class JSON { var res = new JSONList(); for (var el : ((ArrayValue)val).toArray()) { - var jsonEl = fromJs(ctx, el, prev); + var jsonEl = fromJs(ext, el, prev); if (jsonEl == null) jsonEl = JSONElement.NULL; res.add(jsonEl); } @@ -58,8 +58,8 @@ public class JSON { var res = new JSONMap(); - for (var el : Values.getMembers(ctx, val, false, false)) { - var jsonEl = fromJs(ctx, Values.getMember(ctx, val, el), prev); + for (var el : Values.getMembers(ext, val, false, false)) { + var jsonEl = fromJs(ext, Values.getMember(ext, val, el), prev); if (jsonEl == null) continue; if (el instanceof String || el instanceof Number) res.put(el.toString(), jsonEl); } @@ -70,8 +70,8 @@ public class JSON { if (val == null) return null; return null; } - public static JSONElement fromJs(Context ctx, Object val) { - return fromJs(ctx, val, new HashSet<>()); + public static JSONElement fromJs(Extensions ext, Object val) { + return fromJs(ext, val, new HashSet<>()); } public static ParseRes parseIdentifier(List tokens, int i) { diff --git a/src/java/me/topchetoeu/jscript/lib/AsyncFunctionLib.java b/src/java/me/topchetoeu/jscript/lib/AsyncFunctionLib.java index 6b10d51..2151164 100644 --- a/src/java/me/topchetoeu/jscript/lib/AsyncFunctionLib.java +++ b/src/java/me/topchetoeu/jscript/lib/AsyncFunctionLib.java @@ -2,6 +2,7 @@ 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.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.CodeFunction; @@ -65,8 +66,9 @@ public class AsyncFunctionLib extends FunctionValue { } @Override - public Object call(Context ctx, Object thisArg, Object ...args) { + public Object call(Extensions ext, 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); diff --git a/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorFunctionLib.java b/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorFunctionLib.java index 61fa95f..43e7689 100644 --- a/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorFunctionLib.java +++ b/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorFunctionLib.java @@ -1,6 +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.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.CodeFunction; @@ -13,7 +14,7 @@ public class AsyncGeneratorFunctionLib extends FunctionValue { public final CodeFunction func; @Override - public Object call(Context ctx, Object thisArg, Object ...args) { + public Object call(Extensions ext, Object thisArg, Object ...args) { var handler = new AsyncGeneratorLib(); var newArgs = new Object[args.length + 2]; @@ -21,7 +22,7 @@ public class AsyncGeneratorFunctionLib extends FunctionValue { newArgs[1] = new NativeFunction("yield", handler::yield); System.arraycopy(args, 0, newArgs, 2, args.length); - handler.frame = new Frame(ctx, thisArg, newArgs, func); + handler.frame = new Frame(Context.of(ext), thisArg, newArgs, func); return handler; } diff --git a/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java b/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java index 3096f51..32778ac 100644 --- a/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java +++ b/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java @@ -101,7 +101,7 @@ public class AsyncGeneratorLib { } @Expose public PromiseLib __throw(Arguments args) { this.currPromise = new PromiseLib(); - next(args.ctx, Values.NO_RETURN, Values.NO_RETURN, new EngineException(args.get(0)).setCtx(args.ctx)); + next(args.ctx, Values.NO_RETURN, Values.NO_RETURN, new EngineException(args.get(0)).setExtensions(args.ctx)); return this.currPromise; } } \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/lib/GeneratorFunctionLib.java b/src/java/me/topchetoeu/jscript/lib/GeneratorFunctionLib.java index 7d95ef6..7075dae 100644 --- a/src/java/me/topchetoeu/jscript/lib/GeneratorFunctionLib.java +++ b/src/java/me/topchetoeu/jscript/lib/GeneratorFunctionLib.java @@ -1,6 +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.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.CodeFunction; @@ -12,14 +13,14 @@ import me.topchetoeu.jscript.utils.interop.WrapperName; public class GeneratorFunctionLib extends FunctionValue { public final CodeFunction func; - @Override public Object call(Context ctx, Object thisArg, Object ...args) { + @Override public Object call(Extensions ext, 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(ctx, thisArg, newArgs, func); + handler.frame = new Frame(Context.of(ext), thisArg, newArgs, func); return handler; } diff --git a/src/java/me/topchetoeu/jscript/lib/GeneratorLib.java b/src/java/me/topchetoeu/jscript/lib/GeneratorLib.java index 19d93ae..9849c7d 100644 --- a/src/java/me/topchetoeu/jscript/lib/GeneratorLib.java +++ b/src/java/me/topchetoeu/jscript/lib/GeneratorLib.java @@ -59,7 +59,7 @@ public class GeneratorLib { else return next(args.ctx, 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)).setCtx(args.ctx)); + return next(args.ctx, Values.NO_RETURN, Values.NO_RETURN, new EngineException(args.get(0)).setExtensions(args.ctx)); } @Expose public ObjectValue __return(Arguments args) { return next(args.ctx, Values.NO_RETURN, args.get(0), null); diff --git a/src/java/me/topchetoeu/jscript/lib/Internals.java b/src/java/me/topchetoeu/jscript/lib/Internals.java index 8c276c8..67564c6 100644 --- a/src/java/me/topchetoeu/jscript/lib/Internals.java +++ b/src/java/me/topchetoeu/jscript/lib/Internals.java @@ -17,6 +17,7 @@ import me.topchetoeu.jscript.utils.interop.Expose; import me.topchetoeu.jscript.utils.interop.ExposeField; import me.topchetoeu.jscript.utils.interop.ExposeTarget; import me.topchetoeu.jscript.utils.interop.ExposeType; +import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider; import me.topchetoeu.jscript.utils.modules.ModuleRepo; public class Internals { @@ -51,7 +52,7 @@ public class Internals { try { Thread.sleep(ms, ns); } catch (InterruptedException e) { return; } - args.ctx.get(EventLoop.KEY).pushMsg(() -> func.call(new Context(args.ctx.environment), null, arguments), false); + args.ctx.get(EventLoop.KEY).pushMsg(() -> func.call(new Context(args.ctx.extensions), null, arguments), false); }); thread.start(); @@ -79,7 +80,7 @@ public class Internals { } catch (InterruptedException e) { return; } - args.ctx.get(EventLoop.KEY).pushMsg(() -> func.call(new Context(args.ctx.environment), null, arguments), false); + args.ctx.get(EventLoop.KEY).pushMsg(() -> func.call(new Context(args.ctx.extensions), null, arguments), false); } }); thread.start(); @@ -165,8 +166,8 @@ public class Internals { } public static Environment apply(Environment env) { - var wp = env.wrappers; - var glob = env.global = new GlobalScope(wp.getNamespace(Internals.class)); + var wp = new NativeWrapperProvider(); + var glob = new GlobalScope(wp.getNamespace(Internals.class)); glob.define(null, "Math", false, wp.getNamespace(MathLib.class)); glob.define(null, "JSON", false, wp.getNamespace(JSONLib.class)); @@ -209,8 +210,12 @@ public class Internals { env.add(Environment.TYPE_ERR_PROTO, wp.getProto(TypeErrorLib.class)); env.add(Environment.RANGE_ERR_PROTO, wp.getProto(RangeErrorLib.class)); - Values.setPrototype(Context.NULL, wp.getProto(ObjectLib.class), null); env.add(Environment.REGEX_CONSTR, wp.getConstr(RegExpLib.class)); + Values.setPrototype(new Context(), wp.getProto(ObjectLib.class), null); + + env.add(NativeWrapperProvider.KEY, wp); + env.add(GlobalScope.KEY, glob); + return env; } diff --git a/src/java/me/topchetoeu/jscript/lib/PromiseLib.java b/src/java/me/topchetoeu/jscript/lib/PromiseLib.java index 6025cb4..66829de 100644 --- a/src/java/me/topchetoeu/jscript/lib/PromiseLib.java +++ b/src/java/me/topchetoeu/jscript/lib/PromiseLib.java @@ -65,7 +65,7 @@ public class PromiseLib { } if (state == STATE_REJECTED && !handled) { - Values.printError(((EngineException)val).setCtx(ctx), "(in promise)"); + Values.printError(((EngineException)val).setExtensions(ctx), "(in promise)"); } handles = null; @@ -207,7 +207,7 @@ public class PromiseLib { } @Expose(value = "reject", target = ExposeTarget.STATIC) public static PromiseLib __ofRejected(Arguments args) { - return ofRejected(args.ctx, new EngineException(args.get(0)).setCtx(args.ctx)); + return ofRejected(args.ctx, new EngineException(args.get(0)).setExtensions(args.ctx)); } @Expose(target = ExposeTarget.STATIC) @@ -215,7 +215,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'.").setCtx(args.ctx)); + if (promises.size() == 0) return ofRejected(args.ctx, EngineException.ofError("No promises passed to 'Promise.any'.").setExtensions(args.ctx)); var n = new int[] { promises.size() }; var res = new PromiseLib(); var errors = new ArrayValue(); @@ -230,7 +230,7 @@ public class PromiseLib { public void onReject(EngineException err) { errors.set(args.ctx, index, err.value); n[0]--; - if (n[0] <= 0) res.reject(args.ctx, new EngineException(errors).setCtx(args.ctx)); + if (n[0] <= 0) res.reject(args.ctx, new EngineException(errors).setExtensions(args.ctx)); } }); } @@ -390,7 +390,7 @@ public class PromiseLib { return null; }), new NativeFunction(null, _args -> { - res.reject(_args.ctx, new EngineException(_args.get(0)).setCtx(_args.ctx)); + res.reject(_args.ctx, new EngineException(_args.get(0)).setExtensions(_args.ctx)); return null; }) ); diff --git a/src/java/me/topchetoeu/jscript/runtime/Childable.java b/src/java/me/topchetoeu/jscript/runtime/Childable.java new file mode 100644 index 0000000..f1f358e --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/Childable.java @@ -0,0 +1,5 @@ +package me.topchetoeu.jscript.runtime; + +public interface Childable { + Object child(); +} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/runtime/Context.java b/src/java/me/topchetoeu/jscript/runtime/Context.java index 0f93e4b..950f068 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Context.java +++ b/src/java/me/topchetoeu/jscript/runtime/Context.java @@ -11,39 +11,37 @@ import me.topchetoeu.jscript.runtime.values.CodeFunction; import me.topchetoeu.jscript.runtime.values.FunctionValue; public class Context implements Extensions { - public static final Context NULL = new Context(); - public final Context parent; - public final Environment environment; + public final Extensions extensions; public final Frame frame; // public final Engine engine; public final int stackSize; @Override public void add(Key key, T obj) { - if (environment != null) environment.add(key, obj); + if (extensions != null) extensions.add(key, obj); // else if (engine != null) engine.add(key, obj); } @Override public T get(Key key) { - if (environment != null && environment.has(key)) return environment.get(key); + if (extensions != null && extensions.has(key)) return extensions.get(key); // else if (engine != null && engine.has(key)) return engine.get(key); return null; } @Override public boolean has(Key key) { return - environment != null && environment.has(key); + extensions != null && extensions.has(key); // engine != null && engine.has(key); } @Override public boolean remove(Key key) { var res = false; - if (environment != null) res |= environment.remove(key); + if (extensions != null) res |= extensions.remove(key); // else if (engine != null) res |= engine.remove(key); return res; } @Override public Iterable> keys() { - if (environment == null) return List.of(); - else return environment.keys(); + if (extensions == null) return List.of(); + else return extensions.keys(); // if (engine == null && environment == null) return List.of(); // if (engine == null) return environment.keys(); @@ -57,12 +55,12 @@ public class Context implements Extensions { public FunctionValue compile(Filename filename, String raw) { DebugContext.get(this).onSource(filename, raw); - var result = new CodeFunction(environment, filename.toString(), Compiler.get(this).compile(filename, raw), new ValueVariable[0]); + 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.environment, frame, stackSize + 1); + var res = new Context(this, frame.function.extensions, frame, stackSize + 1); return res; } @@ -88,10 +86,9 @@ public class Context implements Extensions { }; } - - private Context(Context parent, Environment environment, Frame frame, int stackSize) { + private Context(Context parent, Extensions ext, Frame frame, int stackSize) { this.parent = parent; - this.environment = environment; + this.extensions = ext; this.frame = frame; this.stackSize = stackSize; @@ -103,7 +100,16 @@ public class Context implements Extensions { public Context() { this(null, null, null, 0); } - public Context(Environment env) { - this(null, env, null, 0); + public Context(Extensions ext) { + this(null, 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 ((Context)ext).extensions; + else return ext; } } diff --git a/src/java/me/topchetoeu/jscript/runtime/Copyable.java b/src/java/me/topchetoeu/jscript/runtime/Copyable.java new file mode 100644 index 0000000..cb2e75e --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/Copyable.java @@ -0,0 +1,5 @@ +package me.topchetoeu.jscript.runtime; + +public interface Copyable { + Object copy(); +} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/runtime/Environment.java b/src/java/me/topchetoeu/jscript/runtime/Environment.java index ff123f3..4e232d8 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Environment.java +++ b/src/java/me/topchetoeu/jscript/runtime/Environment.java @@ -3,11 +3,9 @@ package me.topchetoeu.jscript.runtime; import java.util.HashMap; import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.scope.GlobalScope; import me.topchetoeu.jscript.runtime.values.FunctionValue; import me.topchetoeu.jscript.runtime.values.NativeFunction; import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider; @SuppressWarnings("unchecked") public class Environment implements Extensions { @@ -31,9 +29,6 @@ public class Environment implements Extensions { private HashMap, Object> data = new HashMap<>(); - public GlobalScope global; - public WrapperProvider wrappers; - @Override public void add(Key key, T obj) { data.put(key, obj); } @@ -56,37 +51,11 @@ public class Environment implements Extensions { public static FunctionValue regexConstructor(Extensions ext) { return ext.init(REGEX_CONSTR, new NativeFunction("RegExp", args -> { - throw EngineException.ofError("Regular expressions not supported.").setCtx(args.ctx); + throw EngineException.ofError("Regular expressions not supported.").setExtensions(args.ctx); })); } - public Environment copy() { - var res = new Environment(); - - res.wrappers = wrappers.fork(res); - res.global = global; - res.data.putAll(data); - - return res; - } - public Environment child() { - var res = copy(); - res.global = res.global.globalChild(); - return res; - } - public Context context() { return new Context(this); } - - public Environment(WrapperProvider nativeConverter, GlobalScope global) { - if (nativeConverter == null) nativeConverter = new NativeWrapperProvider(); - if (global == null) global = new GlobalScope(); - - this.wrappers = nativeConverter; - this.global = global; - } - public Environment() { - this(null, null); - } } diff --git a/src/java/me/topchetoeu/jscript/runtime/EventLoop.java b/src/java/me/topchetoeu/jscript/runtime/EventLoop.java index 68920d2..6046971 100644 --- a/src/java/me/topchetoeu/jscript/runtime/EventLoop.java +++ b/src/java/me/topchetoeu/jscript/runtime/EventLoop.java @@ -23,15 +23,15 @@ public interface EventLoop { return pushMsg(() -> { runnable.run(); return null; }, micro); } - public default DataNotifier pushMsg(boolean micro, Environment env, FunctionValue func, Object thisArg, Object ...args) { + public default DataNotifier pushMsg(boolean micro, Extensions ext, FunctionValue func, Object thisArg, Object ...args) { return pushMsg(() -> { - return func.call(new Context(env), thisArg, args); + return func.call(Context.of(ext), thisArg, args); }, micro); } - public default DataNotifier pushMsg(boolean micro, Environment env, Filename filename, String raw, Object thisArg, Object ...args) { + public default DataNotifier pushMsg(boolean micro, Extensions ext, Filename filename, String raw, Object thisArg, Object ...args) { return pushMsg(() -> { - var ctx = new Context(env); - return ctx.compile(filename, raw).call(new Context(env), thisArg, args); + var ctx = Context.of(ext); + return ctx.compile(filename, raw).call(Context.of(ext), thisArg, args); }, micro); } } diff --git a/src/java/me/topchetoeu/jscript/runtime/Extensions.java b/src/java/me/topchetoeu/jscript/runtime/Extensions.java index d824790..3410e3e 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Extensions.java +++ b/src/java/me/topchetoeu/jscript/runtime/Extensions.java @@ -2,7 +2,7 @@ package me.topchetoeu.jscript.runtime; import java.util.List; -public interface Extensions { +public interface Extensions extends Childable, Copyable { public static Extensions EMPTY = new Extensions() { @Override public void add(Key key, T obj) { } @Override public boolean remove(Key key) { return false; } @@ -47,6 +47,29 @@ public interface Extensions { } } + @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)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)key, val); + } + return res; + } + public static Extensions wrap(Extensions ext) { if (ext == null) return EMPTY; else return ext; diff --git a/src/java/me/topchetoeu/jscript/runtime/Frame.java b/src/java/me/topchetoeu/jscript/runtime/Frame.java index d952596..7cdb025 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Frame.java +++ b/src/java/me/topchetoeu/jscript/runtime/Frame.java @@ -294,13 +294,13 @@ public class Frame { public ObjectValue getValStackScope() { return new ObjectValue() { @Override - protected Object getField(Context ctx, Object key) { - var i = (int)Values.toNumber(ctx, key); + protected Object getField(Extensions 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(Context ctx, Object key) { + protected boolean hasField(Extensions ext, Object key) { return true; } @Override diff --git a/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index 2b94aa1..7a31109 100644 --- a/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -5,6 +5,7 @@ import java.util.Collections; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.runtime.exceptions.EngineException; +import me.topchetoeu.jscript.runtime.scope.GlobalScope; import me.topchetoeu.jscript.runtime.scope.ValueVariable; import me.topchetoeu.jscript.runtime.values.ArrayValue; import me.topchetoeu.jscript.runtime.values.CodeFunction; @@ -14,43 +15,43 @@ import me.topchetoeu.jscript.runtime.values.Symbol; import me.topchetoeu.jscript.runtime.values.Values; public class InstructionRunner { - private static Object execReturn(Context ctx, Instruction instr, Frame frame) { + private static Object execReturn(Extensions ext, Instruction instr, Frame frame) { return frame.pop(); } - private static Object execThrow(Context ctx, Instruction instr, Frame frame) { + private static Object execThrow(Extensions ext, Instruction instr, Frame frame) { throw new EngineException(frame.pop()); } - private static Object execThrowSyntax(Context ctx, Instruction instr, Frame frame) { + private static Object execThrowSyntax(Extensions ext, Instruction instr, Frame frame) { throw EngineException.ofSyntax((String)instr.get(0)); } - private static Object execCall(Context ctx, Instruction instr, Frame frame) { + private static Object execCall(Extensions ext, Instruction instr, Frame frame) { var callArgs = frame.take(instr.get(0)); var func = frame.pop(); var thisArg = frame.pop(); - frame.push(Values.call(ctx, func, thisArg, callArgs)); + frame.push(Values.call(ext, func, thisArg, callArgs)); frame.codePtr++; return Values.NO_RETURN; } - private static Object execCallNew(Context ctx, Instruction instr, Frame frame) { + private static Object execCallNew(Extensions ext, Instruction instr, Frame frame) { var callArgs = frame.take(instr.get(0)); var funcObj = frame.pop(); - frame.push(Values.callNew(ctx, funcObj, callArgs)); + frame.push(Values.callNew(ext, funcObj, callArgs)); frame.codePtr++; return Values.NO_RETURN; } - private static Object execMakeVar(Context ctx, Instruction instr, Frame frame) { + private static Object execMakeVar(Extensions ext, Instruction instr, Frame frame) { var name = (String)instr.get(0); - ctx.environment.global.define(ctx, name); + GlobalScope.get(ext).define(ext, name); frame.codePtr++; return Values.NO_RETURN; } - private static Object execDefProp(Context ctx, Instruction instr, Frame frame) { + private static Object execDefProp(Extensions ext, Instruction instr, Frame frame) { var setter = frame.pop(); var getter = frame.pop(); var name = frame.pop(); @@ -59,16 +60,16 @@ public class InstructionRunner { if (getter != null && !(getter instanceof FunctionValue)) throw EngineException.ofType("Getter must be a function or undefined."); if (setter != null && !(setter instanceof FunctionValue)) throw EngineException.ofType("Setter must be a function or undefined."); if (!(obj instanceof ObjectValue)) throw EngineException.ofType("Property apply target must be an object."); - ((ObjectValue)obj).defineProperty(ctx, name, (FunctionValue)getter, (FunctionValue)setter, false, false); + ((ObjectValue)obj).defineProperty(ext, name, (FunctionValue)getter, (FunctionValue)setter, false, false); frame.push(obj); frame.codePtr++; return Values.NO_RETURN; } - private static Object execKeys(Context ctx, Instruction instr, Frame frame) { + private static Object execKeys(Extensions ext, Instruction instr, Frame frame) { var val = frame.pop(); - var members = Values.getMembers(ctx, val, false, false); + var members = Values.getMembers(ext, val, false, false); Collections.reverse(members); frame.push(null); @@ -76,7 +77,7 @@ public class InstructionRunner { for (var el : members) { if (el instanceof Symbol) continue; var obj = new ObjectValue(); - obj.defineProperty(ctx, "value", el); + obj.defineProperty(ext, "value", el); frame.push(obj); } @@ -84,7 +85,7 @@ public class InstructionRunner { return Values.NO_RETURN; } - private static Object execTryStart(Context ctx, Instruction instr, Frame frame) { + private static Object execTryStart(Extensions ext, Instruction instr, Frame frame) { int start = frame.codePtr + 1; int catchStart = (int)instr.get(0); int finallyStart = (int)instr.get(1); @@ -95,12 +96,12 @@ public class InstructionRunner { frame.codePtr++; return Values.NO_RETURN; } - private static Object execTryEnd(Context ctx, Instruction instr, Frame frame) { + private static Object execTryEnd(Extensions ext, Instruction instr, Frame frame) { frame.popTryFlag = true; return Values.NO_RETURN; } - private static Object execDup(Context ctx, Instruction instr, Frame frame) { + private static Object execDup(Extensions ext, Instruction instr, Frame frame) { int count = instr.get(0); for (var i = 0; i < count; i++) { @@ -110,7 +111,7 @@ public class InstructionRunner { frame.codePtr++; return Values.NO_RETURN; } - private static Object execLoadValue(Context ctx, Instruction instr, Frame frame) { + private static Object execLoadValue(Extensions ext, Instruction instr, Frame frame) { switch (instr.type) { case PUSH_UNDEFINED: frame.push(null); break; case PUSH_NULL: frame.push(Values.NULL); break; @@ -120,33 +121,33 @@ public class InstructionRunner { frame.codePtr++; return Values.NO_RETURN; } - private static Object execLoadVar(Context ctx, Instruction instr, Frame frame) { + private static Object execLoadVar(Extensions ext, Instruction instr, Frame frame) { var i = instr.get(0); - if (i instanceof String) frame.push(ctx.environment.global.get(ctx, (String)i)); - else frame.push(frame.scope.get((int)i).get(ctx)); + if (i instanceof String) frame.push(GlobalScope.get(ext).get(ext, (String)i)); + else frame.push(frame.scope.get((int)i).get(ext)); frame.codePtr++; return Values.NO_RETURN; } - private static Object execLoadObj(Context ctx, Instruction instr, Frame frame) { + private static Object execLoadObj(Extensions ext, Instruction instr, Frame frame) { frame.push(new ObjectValue()); frame.codePtr++; return Values.NO_RETURN; } - private static Object execLoadGlob(Context ctx, Instruction instr, Frame frame) { - frame.push(ctx.environment.global.obj); + private static Object execLoadGlob(Extensions ext, Instruction instr, Frame frame) { + frame.push(GlobalScope.get(ext).obj); frame.codePtr++; return Values.NO_RETURN; } - private static Object execLoadArr(Context ctx, Instruction instr, Frame frame) { + private static Object execLoadArr(Extensions 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(Context ctx, Instruction instr, Frame frame) { + private static Object execLoadFunc(Extensions ext, Instruction instr, Frame frame) { int id = instr.get(0); var captures = new ValueVariable[instr.params.length - 1]; @@ -154,19 +155,19 @@ public class InstructionRunner { captures[i - 1] = frame.scope.get(instr.get(i)); } - var func = new CodeFunction(ctx.environment, "", frame.function.body.children[id], captures); + var func = new CodeFunction(ext, "", frame.function.body.children[id], captures); frame.push(func); frame.codePtr++; return Values.NO_RETURN; } - private static Object execLoadMember(Context ctx, Instruction instr, Frame frame) { + private static Object execLoadMember(Extensions ext, Instruction instr, Frame frame) { var key = frame.pop(); var obj = frame.pop(); try { - frame.push(Values.getMember(ctx, obj, key)); + frame.push(Values.getMember(ext, obj, key)); } catch (IllegalArgumentException e) { throw EngineException.ofType(e.getMessage()); @@ -174,9 +175,9 @@ public class InstructionRunner { frame.codePtr++; return Values.NO_RETURN; } - private static Object execLoadRegEx(Context ctx, Instruction instr, Frame frame) { - if (ctx.hasNotNull(Environment.REGEX_CONSTR)) { - frame.push(Values.callNew(ctx, ctx.get(Environment.REGEX_CONSTR), instr.get(0), instr.get(1))); + private static Object execLoadRegEx(Extensions 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))); } else { throw EngineException.ofSyntax("Regex is not supported."); @@ -185,43 +186,43 @@ public class InstructionRunner { return Values.NO_RETURN; } - private static Object execDiscard(Context ctx, Instruction instr, Frame frame) { + private static Object execDiscard(Extensions ext, Instruction instr, Frame frame) { frame.pop(); frame.codePtr++; return Values.NO_RETURN; } - private static Object execStoreMember(Context ctx, Instruction instr, Frame frame) { + private static Object execStoreMember(Extensions ext, Instruction instr, Frame frame) { var val = frame.pop(); var key = frame.pop(); var obj = frame.pop(); - if (!Values.setMember(ctx, obj, key, val)) throw EngineException.ofSyntax("Can't set member '" + key + "'."); + if (!Values.setMember(ext, obj, key, val)) throw EngineException.ofSyntax("Can't set member '" + key + "'."); if ((boolean)instr.get(0)) frame.push(val); frame.codePtr++; return Values.NO_RETURN; } - private static Object execStoreVar(Context ctx, Instruction instr, Frame frame) { + private static Object execStoreVar(Extensions ext, Instruction instr, Frame frame) { var val = (boolean)instr.get(1) ? frame.peek() : frame.pop(); var i = instr.get(0); - if (i instanceof String) ctx.environment.global.set(ctx, (String)i, val); - else frame.scope.get((int)i).set(ctx, val); + if (i instanceof String) GlobalScope.get(ext).set(ext, (String)i, val); + else frame.scope.get((int)i).set(ext, val); frame.codePtr++; return Values.NO_RETURN; } - private static Object execStoreSelfFunc(Context ctx, Instruction instr, Frame frame) { - frame.scope.locals[(int)instr.get(0)].set(ctx, frame.function); + private static Object execStoreSelfFunc(Extensions 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(Context ctx, Instruction instr, Frame frame) { + private static Object execJmp(Extensions ext, Instruction instr, Frame frame) { frame.codePtr += (int)instr.get(0); frame.jumpFlag = true; return Values.NO_RETURN; } - private static Object execJmpIf(Context ctx, Instruction instr, Frame frame) { + private static Object execJmpIf(Extensions ext, Instruction instr, Frame frame) { if (Values.toBoolean(frame.pop())) { frame.codePtr += (int)instr.get(0); frame.jumpFlag = true; @@ -229,7 +230,7 @@ public class InstructionRunner { else frame.codePtr ++; return Values.NO_RETURN; } - private static Object execJmpIfNot(Context ctx, Instruction instr, Frame frame) { + private static Object execJmpIfNot(Extensions ext, Instruction instr, Frame frame) { if (Values.not(frame.pop())) { frame.codePtr += (int)instr.get(0); frame.jumpFlag = true; @@ -238,13 +239,13 @@ public class InstructionRunner { return Values.NO_RETURN; } - private static Object execTypeof(Context ctx, Instruction instr, Frame frame) { + private static Object execTypeof(Extensions ext, Instruction instr, Frame frame) { String name = instr.get(0); Object obj; if (name != null) { - if (ctx.environment.global.has(ctx, name)) { - obj = ctx.environment.global.get(ctx, name); + if (GlobalScope.get(ext).has(ext, name)) { + obj = GlobalScope.get(ext).get(ext, name); } else obj = null; } @@ -255,73 +256,73 @@ public class InstructionRunner { frame.codePtr++; return Values.NO_RETURN; } - private static Object execNop(Context ctx, Instruction instr, Frame frame) { + private static Object execNop(Extensions ext, Instruction instr, Frame frame) { frame.codePtr++; return Values.NO_RETURN; } - private static Object execDelete(Context ctx, Instruction instr, Frame frame) { + private static Object execDelete(Extensions ext, Instruction instr, Frame frame) { var key = frame.pop(); var val = frame.pop(); - if (!Values.deleteMember(ctx, val, key)) throw EngineException.ofSyntax("Can't delete member '" + key + "'."); + if (!Values.deleteMember(ext, val, key)) throw EngineException.ofSyntax("Can't delete member '" + key + "'."); frame.codePtr++; return Values.NO_RETURN; } - private static Object execOperation(Context ctx, Instruction instr, Frame frame) { + private static Object execOperation(Extensions ext, Instruction instr, Frame frame) { Operation op = instr.get(0); var args = new Object[op.operands]; for (var i = op.operands - 1; i >= 0; i--) args[i] = frame.pop(); - frame.push(Values.operation(ctx, op, args)); + frame.push(Values.operation(ext, op, args)); frame.codePtr++; return Values.NO_RETURN; } - public static Object exec(Context ctx, Instruction instr, Frame frame) { + public static Object exec(Extensions ext, Instruction instr, Frame frame) { switch (instr.type) { - case NOP: return execNop(ctx, instr, frame); - case RETURN: return execReturn(ctx, instr, frame); - case THROW: return execThrow(ctx, instr, frame); - case THROW_SYNTAX: return execThrowSyntax(ctx, instr, frame); - case CALL: return execCall(ctx, instr, frame); - case CALL_NEW: return execCallNew(ctx, instr, frame); - case TRY_START: return execTryStart(ctx, instr, frame); - case TRY_END: return execTryEnd(ctx, instr, frame); + case NOP: return execNop(ext, instr, frame); + case RETURN: return execReturn(ext, instr, frame); + case THROW: return execThrow(ext, instr, frame); + case THROW_SYNTAX: return execThrowSyntax(ext, instr, frame); + case CALL: return execCall(ext, instr, frame); + case CALL_NEW: return execCallNew(ext, instr, frame); + case TRY_START: return execTryStart(ext, instr, frame); + case TRY_END: return execTryEnd(ext, instr, frame); - case DUP: return execDup(ctx, instr, frame); + case DUP: return execDup(ext, instr, frame); case PUSH_UNDEFINED: case PUSH_NULL: case PUSH_STRING: case PUSH_NUMBER: case PUSH_BOOL: - return execLoadValue(ctx, instr, frame); - case LOAD_VAR: return execLoadVar(ctx, instr, frame); - case LOAD_OBJ: return execLoadObj(ctx, instr, frame); - case LOAD_ARR: return execLoadArr(ctx, instr, frame); - case LOAD_FUNC: return execLoadFunc(ctx, instr, frame); - case LOAD_MEMBER: return execLoadMember(ctx, instr, frame); - case LOAD_REGEX: return execLoadRegEx(ctx, instr, frame); - case LOAD_GLOB: return execLoadGlob(ctx, instr, frame); + return execLoadValue(ext, instr, frame); + case LOAD_VAR: return execLoadVar(ext, instr, frame); + case LOAD_OBJ: return execLoadObj(ext, instr, frame); + case LOAD_ARR: return execLoadArr(ext, instr, frame); + case LOAD_FUNC: return execLoadFunc(ext, instr, frame); + case LOAD_MEMBER: return execLoadMember(ext, instr, frame); + case LOAD_REGEX: return execLoadRegEx(ext, instr, frame); + case LOAD_GLOB: return execLoadGlob(ext, instr, frame); - case DISCARD: return execDiscard(ctx, instr, frame); - case STORE_MEMBER: return execStoreMember(ctx, instr, frame); - case STORE_VAR: return execStoreVar(ctx, instr, frame); - case STORE_SELF_FUNC: return execStoreSelfFunc(ctx, instr, frame); - case MAKE_VAR: return execMakeVar(ctx, instr, frame); + case DISCARD: return execDiscard(ext, instr, frame); + case STORE_MEMBER: return execStoreMember(ext, instr, frame); + case STORE_VAR: return execStoreVar(ext, instr, frame); + case STORE_SELF_FUNC: return execStoreSelfFunc(ext, instr, frame); + case MAKE_VAR: return execMakeVar(ext, instr, frame); - case KEYS: return execKeys(ctx, instr, frame); - case DEF_PROP: return execDefProp(ctx, instr, frame); - case TYPEOF: return execTypeof(ctx, instr, frame); - case DELETE: return execDelete(ctx, instr, frame); + case KEYS: return execKeys(ext, instr, frame); + case DEF_PROP: return execDefProp(ext, instr, frame); + case TYPEOF: return execTypeof(ext, instr, frame); + case DELETE: return execDelete(ext, instr, frame); - case JMP: return execJmp(ctx, instr, frame); - case JMP_IF: return execJmpIf(ctx, instr, frame); - case JMP_IFN: return execJmpIfNot(ctx, instr, frame); + case JMP: return execJmp(ext, instr, frame); + case JMP_IF: return execJmpIf(ext, instr, frame); + case JMP_IFN: return execJmpIfNot(ext, instr, frame); - case OPERATION: return execOperation(ctx, instr, frame); + case OPERATION: return execOperation(ext, instr, frame); default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + "."); } diff --git a/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java b/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java index 787dbee..20d19bc 100644 --- a/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java +++ b/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java @@ -6,6 +6,7 @@ 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.values.ObjectValue; import me.topchetoeu.jscript.runtime.values.Values; import me.topchetoeu.jscript.runtime.values.ObjectValue.PlaceholderProto; @@ -14,10 +15,10 @@ public class EngineException extends RuntimeException { public static class StackElement { public final Location location; public final String name; - public final Context ctx; + public final Extensions ext; public boolean visible() { - return ctx == null || !ctx.get(Environment.HIDE_STACK, false); + return ext == null || !ext.get(Environment.HIDE_STACK, false); } public String toString() { var res = ""; @@ -29,12 +30,13 @@ public class EngineException extends RuntimeException { return res.trim(); } - public StackElement(Context ctx, Location location, String name) { + public StackElement(Extensions ext, Location location, String name) { if (name != null) name = name.trim(); if (name.equals("")) name = null; - if (ctx == null) this.ctx = null; - else this.ctx = new Context(ctx.environment); + if (ext == null) this.ext = null; + else this.ext = Context.clean(ext); + this.location = location; this.name = name; } @@ -42,13 +44,13 @@ public class EngineException extends RuntimeException { public final Object value; public EngineException cause; - public Environment env = null; + public Extensions ext = null; public final List stackTrace = new ArrayList<>(); - public EngineException add(Context ctx, String name, Location location) { - var el = new StackElement(ctx, location, name); + public EngineException add(Extensions ext, String name, Location location) { + var el = new StackElement(ext, location, name); if (el.name == null && el.location == null) return this; - setCtx(ctx); + setExtensions(ext); stackTrace.add(el); return this; } @@ -56,15 +58,15 @@ public class EngineException extends RuntimeException { this.cause = cause; return this; } - public EngineException setCtx(Context ctx) { - if (this.env == null) this.env = ctx.environment; + public EngineException setExtensions(Extensions ext) { + if (this.ext == null) this.ext = Context.clean(ext); return this; } - public String toString(Context ctx) { + public String toString(Extensions ext) { var ss = new StringBuilder(); try { - ss.append(Values.toString(ctx, value)).append('\n'); + ss.append(Values.toString(ext, value)).append('\n'); } catch (EngineException e) { ss.append("[Error while stringifying]\n"); @@ -72,7 +74,7 @@ public class EngineException extends RuntimeException { for (var line : stackTrace) { if (line.visible()) ss.append(" ").append(line.toString()).append("\n"); } - if (cause != null) ss.append("Caused by ").append(cause.toString(ctx)).append('\n'); + if (cause != null) ss.append("Caused by ").append(cause.toString(ext)).append('\n'); ss.deleteCharAt(ss.length() - 1); return ss.toString(); } diff --git a/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java b/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java index 161a802..d68d24f 100644 --- a/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java +++ b/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java @@ -3,7 +3,8 @@ package me.topchetoeu.jscript.runtime.scope; import java.util.HashSet; import java.util.Set; -import me.topchetoeu.jscript.runtime.Context; +import me.topchetoeu.jscript.runtime.Extensions; +import me.topchetoeu.jscript.runtime.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.FunctionValue; import me.topchetoeu.jscript.runtime.values.NativeFunction; @@ -11,10 +12,12 @@ import me.topchetoeu.jscript.runtime.values.ObjectValue; import me.topchetoeu.jscript.runtime.values.Values; public class GlobalScope { + public static final Key KEY = new Key<>(); + public final ObjectValue obj; - public boolean has(Context ctx, String name) { - return Values.hasMember(null, obj, name, false); + public boolean has(Extensions ext, String name) { + return Values.hasMember(ext, obj, name, false); } public GlobalScope globalChild() { @@ -23,35 +26,35 @@ public class GlobalScope { return new GlobalScope(obj); } - public Object define(Context ctx, String name) { - if (Values.hasMember(ctx, obj, name, false)) return name; - obj.defineProperty(ctx, name, null); + public Object define(Extensions ext, String name) { + if (Values.hasMember(ext, obj, name, false)) return name; + obj.defineProperty(ext, name, null); return name; } - public void define(Context ctx, String name, Variable val) { - obj.defineProperty(ctx, name, + public void define(Extensions 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; }), true, true ); } - public void define(Context ctx, String name, boolean readonly, Object val) { - obj.defineProperty(ctx, name, val, readonly, true, true); + public void define(Extensions ext, String name, boolean readonly, Object val) { + obj.defineProperty(ext, name, val, readonly, true, true); } - public void define(Context ctx, String ...names) { - for (var n : names) define(ctx, n); + public void define(Extensions ext, String ...names) { + for (var n : names) define(ext, n); } - public void define(Context ctx, boolean readonly, FunctionValue val) { - define(ctx, val.name, readonly, val); + public void define(Extensions ext, boolean readonly, FunctionValue val) { + define(ext, val.name, readonly, val); } - public Object get(Context ctx, String name) { - if (!Values.hasMember(ctx, obj, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist."); - else return Values.getMember(ctx, obj, name); + public Object get(Extensions 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(Context ctx, String name, Object val) { - if (!Values.hasMember(ctx, obj, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist."); - if (!Values.setMember(ctx, obj, name, val)) throw EngineException.ofSyntax("The global '" + name + "' is readonly."); + public void set(Extensions 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."); } public Set keys() { @@ -70,4 +73,9 @@ public class GlobalScope { public GlobalScope(ObjectValue val) { this.obj = val; } + + public static GlobalScope get(Extensions ext) { + if (ext.has(KEY)) return ext.get(KEY); + else return new GlobalScope(); + } } diff --git a/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java b/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java index 892e01e..eb988b3 100644 --- a/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java +++ b/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java @@ -1,6 +1,6 @@ package me.topchetoeu.jscript.runtime.scope; -import me.topchetoeu.jscript.runtime.Context; +import me.topchetoeu.jscript.runtime.Extensions; import me.topchetoeu.jscript.runtime.values.Values; public class ValueVariable implements Variable { @@ -11,14 +11,14 @@ public class ValueVariable implements Variable { public boolean readonly() { return readonly; } @Override - public Object get(Context ctx) { + public Object get(Extensions ext) { return value; } @Override - public void set(Context ctx, Object val) { + public void set(Extensions ext, Object val) { if (readonly) return; - this.value = Values.normalize(ctx, val); + this.value = Values.normalize(ext, val); } public ValueVariable(boolean readonly, Object val) { diff --git a/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java b/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java index ed99895..6d00d80 100644 --- a/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java +++ b/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java @@ -1,9 +1,9 @@ package me.topchetoeu.jscript.runtime.scope; -import me.topchetoeu.jscript.runtime.Context; +import me.topchetoeu.jscript.runtime.Extensions; public interface Variable { - Object get(Context ctx); + Object get(Extensions ext); default boolean readonly() { return true; } - default void set(Context ctx, Object val) { } + default void set(Extensions ext, Object val) { } } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/ArrayValue.java b/src/java/me/topchetoeu/jscript/runtime/values/ArrayValue.java index 02c7947..a13c014 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/ArrayValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/ArrayValue.java @@ -6,7 +6,7 @@ import java.util.Comparator; import java.util.Iterator; import java.util.List; -import me.topchetoeu.jscript.runtime.Context; +import me.topchetoeu.jscript.runtime.Extensions; // TODO: Make methods generic public class ArrayValue extends ObjectValue implements Iterable { @@ -41,12 +41,12 @@ public class ArrayValue extends ObjectValue implements Iterable { if (res == UNDEFINED) return null; else return res; } - public void set(Context ctx, int i, Object val) { + public void set(Extensions ext, int i, Object val) { if (i < 0) return; values = alloc(i); - val = Values.normalize(ctx, val); + val = Values.normalize(ext, val); if (val == null) val = UNDEFINED; values[i] = val; if (i >= size) size = i + 1; @@ -99,9 +99,9 @@ public class ArrayValue extends ObjectValue implements Iterable { } } - public void copyFrom(Context ctx, Object[] arr, int sourceStart, int destStart, int count) { + public void copyFrom(Extensions ext, Object[] arr, int sourceStart, int destStart, int count) { for (var i = 0; i < count; i++) { - set(ctx, i + destStart, arr[i + sourceStart]); + set(ext, i + destStart, arr[i + sourceStart]); } } @@ -131,7 +131,7 @@ public class ArrayValue extends ObjectValue implements Iterable { } @Override - protected Object getField(Context ctx, Object key) { + protected Object getField(Extensions ext, Object key) { if (key instanceof Number) { var i = ((Number)key).doubleValue(); if (i >= 0 && i - Math.floor(i) == 0) { @@ -139,22 +139,22 @@ public class ArrayValue extends ObjectValue implements Iterable { } } - return super.getField(ctx, key); + return super.getField(ext, key); } @Override - protected boolean setField(Context ctx, Object key, Object val) { + protected boolean setField(Extensions ext, Object key, Object val) { if (key instanceof Number) { var i = Values.number(key); if (i >= 0 && i - Math.floor(i) == 0) { - set(ctx, (int)i, val); + set(ext, (int)i, val); return true; } } - return super.setField(ctx, key, val); + return super.setField(ext, key, val); } @Override - protected boolean hasField(Context ctx, Object key) { + protected boolean hasField(Extensions ext, Object key) { if (key instanceof Number) { var i = Values.number(key); if (i >= 0 && i - Math.floor(i) == 0) { @@ -162,10 +162,10 @@ public class ArrayValue extends ObjectValue implements Iterable { } } - return super.hasField(ctx, key); + return super.hasField(ext, key); } @Override - protected void deleteField(Context ctx, Object key) { + protected void deleteField(Extensions ext, Object key) { if (key instanceof Number) { var i = Values.number(key); if (i >= 0 && i - Math.floor(i) == 0) { @@ -174,7 +174,7 @@ public class ArrayValue extends ObjectValue implements Iterable { } } - super.deleteField(ctx, key); + super.deleteField(ext, key); } @Override @@ -213,15 +213,15 @@ public class ArrayValue extends ObjectValue implements Iterable { values = new Object[cap]; size = 0; } - public ArrayValue(Context ctx, Object ...values) { + public ArrayValue(Extensions ext, Object ...values) { this(); this.values = new Object[values.length]; size = values.length; - for (var i = 0; i < size; i++) this.values[i] = Values.normalize(ctx, values[i]); + for (var i = 0; i < size; i++) this.values[i] = Values.normalize(ext, values[i]); } - public static ArrayValue of(Context ctx, Collection values) { - return new ArrayValue(ctx, values.toArray(Object[]::new)); + public static ArrayValue of(Extensions ext, Collection values) { + return new ArrayValue(ext, values.toArray(Object[]::new)); } } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/CodeFunction.java b/src/java/me/topchetoeu/jscript/runtime/values/CodeFunction.java index 0843c8f..607376b 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/CodeFunction.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/CodeFunction.java @@ -2,14 +2,14 @@ package me.topchetoeu.jscript.runtime.values; import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.runtime.Context; -import me.topchetoeu.jscript.runtime.Environment; +import me.topchetoeu.jscript.runtime.Extensions; import me.topchetoeu.jscript.runtime.Frame; import me.topchetoeu.jscript.runtime.scope.ValueVariable; public class CodeFunction extends FunctionValue { public final FunctionBody body; public final ValueVariable[] captures; - public Environment environment; + public Extensions extensions; // public Location loc() { // for (var instr : body.instructions) { @@ -25,8 +25,8 @@ public class CodeFunction extends FunctionValue { // } @Override - public Object call(Context ctx, Object thisArg, Object ...args) { - var frame = new Frame(ctx, thisArg, args, this); + public Object call(Extensions ext, Object thisArg, Object ...args) { + var frame = new Frame(Context.of(ext), thisArg, args, this); frame.onPush(); @@ -41,10 +41,10 @@ public class CodeFunction extends FunctionValue { } } - public CodeFunction(Environment environment, String name, FunctionBody body, ValueVariable[] captures) { + public CodeFunction(Extensions extensions, String name, FunctionBody body, ValueVariable[] captures) { super(name, body.argsN); this.captures = captures; - this.environment = environment; + this.extensions = extensions; this.body = body; } } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/FunctionValue.java b/src/java/me/topchetoeu/jscript/runtime/values/FunctionValue.java index 9b54aaa..ced4c9a 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/FunctionValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/FunctionValue.java @@ -2,7 +2,7 @@ package me.topchetoeu.jscript.runtime.values; import java.util.List; -import me.topchetoeu.jscript.runtime.Context; +import me.topchetoeu.jscript.runtime.Extensions; public abstract class FunctionValue extends ObjectValue { public String name = ""; @@ -13,29 +13,29 @@ public abstract class FunctionValue extends ObjectValue { return String.format("function %s(...)", name); } - public abstract Object call(Context ctx, Object thisArg, Object ...args); - public Object call(Context ctx) { - return call(ctx, null); + public abstract Object call(Extensions ext, Object thisArg, Object ...args); + public Object call(Extensions ext) { + return call(ext, null); } @Override - protected Object getField(Context ctx, Object key) { + protected Object getField(Extensions ext, Object key) { if ("name".equals(key)) return name; if ("length".equals(key)) return length; - return super.getField(ctx, key); + return super.getField(ext, key); } @Override - protected boolean setField(Context ctx, Object key, Object val) { - if ("name".equals(key)) name = Values.toString(ctx, val); - else if ("length".equals(key)) length = (int)Values.toNumber(ctx, val); - else return super.setField(ctx, key, val); + protected boolean setField(Extensions 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(Context ctx, Object key) { + protected boolean hasField(Extensions ext, Object key) { if ("name".equals(key)) return true; if ("length".equals(key)) return true; - return super.hasField(ctx, key); + return super.hasField(ext, key); } @Override diff --git a/src/java/me/topchetoeu/jscript/runtime/values/NativeFunction.java b/src/java/me/topchetoeu/jscript/runtime/values/NativeFunction.java index cbc3f58..e1145f9 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/NativeFunction.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/NativeFunction.java @@ -1,6 +1,7 @@ package me.topchetoeu.jscript.runtime.values; import me.topchetoeu.jscript.runtime.Context; +import me.topchetoeu.jscript.runtime.Extensions; import me.topchetoeu.jscript.utils.interop.Arguments; public class NativeFunction extends FunctionValue { @@ -11,8 +12,8 @@ public class NativeFunction extends FunctionValue { public final NativeFunctionRunner action; @Override - public Object call(Context ctx, Object thisArg, Object ...args) { - return action.run(new Arguments(ctx, thisArg, args)); + public Object call(Extensions ext, Object thisArg, Object ...args) { + return action.run(new Arguments(Context.of(ext), thisArg, args)); } public NativeFunction(String name, NativeFunctionRunner action) { diff --git a/src/java/me/topchetoeu/jscript/runtime/values/NativeWrapper.java b/src/java/me/topchetoeu/jscript/runtime/values/NativeWrapper.java index 3bb7e9f..85f8a11 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/NativeWrapper.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/NativeWrapper.java @@ -2,9 +2,9 @@ package me.topchetoeu.jscript.runtime.values; import java.util.WeakHashMap; -import me.topchetoeu.jscript.runtime.Context; import me.topchetoeu.jscript.runtime.Extensions; import me.topchetoeu.jscript.runtime.Key; +import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider; public class NativeWrapper extends ObjectValue { private static final Key> WRAPPERS = new Key<>(); @@ -12,13 +12,13 @@ public class NativeWrapper extends ObjectValue { public final Object wrapped; @Override - public ObjectValue getPrototype(Context ctx) { - if (ctx.environment != null && prototype == NATIVE_PROTO) { + public ObjectValue getPrototype(Extensions ext) { + if (ext != null && prototype == NATIVE_PROTO) { var clazz = wrapped.getClass(); - var res = ctx.environment.wrappers.getProto(clazz); + var res = NativeWrapperProvider.get(ext).getProto(clazz); if (res != null) return res; } - return super.getPrototype(ctx); + return super.getPrototype(ext); } @Override @@ -40,9 +40,14 @@ public class NativeWrapper extends ObjectValue { } public static NativeWrapper of(Extensions exts, Object wrapped) { - var wrappers = exts == null ? null : exts.get(WRAPPERS); + if (exts == null) return new NativeWrapper(wrapped); + var wrappers = exts.get(WRAPPERS); + + if (wrappers == null) { + wrappers = new WeakHashMap<>(); + exts.add(WRAPPERS, wrappers); + } - if (wrappers == null) return new NativeWrapper(wrapped); if (wrappers.containsKey(wrapped)) return wrappers.get(wrapped); var res = new NativeWrapper(wrapped); diff --git a/src/java/me/topchetoeu/jscript/runtime/values/ObjectValue.java b/src/java/me/topchetoeu/jscript/runtime/values/ObjectValue.java index 81f4ea3..c1e0768 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/ObjectValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/ObjectValue.java @@ -6,8 +6,8 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import me.topchetoeu.jscript.runtime.Context; import me.topchetoeu.jscript.runtime.Environment; +import me.topchetoeu.jscript.runtime.Extensions; public class ObjectValue { public static enum PlaceholderProto { @@ -54,10 +54,10 @@ public class ObjectValue { public LinkedHashSet nonConfigurableSet = new LinkedHashSet<>(); public LinkedHashSet nonEnumerableSet = new LinkedHashSet<>(); - private Property getProperty(Context ctx, Object key) { + private Property getProperty(Extensions ext, Object key) { if (properties.containsKey(key)) return properties.get(key); - var proto = getPrototype(ctx); - if (proto != null) return proto.getProperty(ctx, key); + var proto = getPrototype(ext); + if (proto != null) return proto.getProperty(ext, key); else return null; } @@ -86,8 +86,8 @@ public class ObjectValue { state = State.FROZEN; } - public final boolean defineProperty(Context ctx, Object key, Object val, boolean writable, boolean configurable, boolean enumerable) { - key = Values.normalize(ctx, key); val = Values.normalize(ctx, val); + 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); boolean reconfigured = writable != memberWritable(key) || configurable != memberConfigurable(key) || @@ -125,11 +125,11 @@ public class ObjectValue { values.put(key, val); return true; } - public final boolean defineProperty(Context ctx, Object key, Object val) { - return defineProperty(ctx, key, val, true, true, true); + public final boolean defineProperty(Extensions ext, Object key, Object val) { + return defineProperty(ext, key, val, true, true, true); } - public final boolean defineProperty(Context ctx, Object key, FunctionValue getter, FunctionValue setter, boolean configurable, boolean enumerable) { - key = Values.normalize(ctx, key); + public final boolean defineProperty(Extensions ext, Object key, FunctionValue getter, FunctionValue setter, boolean configurable, boolean enumerable) { + key = Values.normalize(ext, key); if ( properties.containsKey(key) && properties.get(key).getter == getter && @@ -152,17 +152,17 @@ public class ObjectValue { return true; } - public ObjectValue getPrototype(Context ctx) { + public ObjectValue getPrototype(Extensions ext) { if (prototype instanceof ObjectValue || prototype == null) return (ObjectValue)prototype; try { - if (prototype == ARR_PROTO) return ctx.get(Environment.ARRAY_PROTO); - if (prototype == FUNC_PROTO) return ctx.get(Environment.FUNCTION_PROTO); - if (prototype == ERR_PROTO) return ctx.get(Environment.ERROR_PROTO); - if (prototype == RANGE_ERR_PROTO) return ctx.get(Environment.RANGE_ERR_PROTO); - if (prototype == SYNTAX_ERR_PROTO) return ctx.get(Environment.SYNTAX_ERR_PROTO); - if (prototype == TYPE_ERR_PROTO) return ctx.get(Environment.TYPE_ERR_PROTO); - return ctx.get(Environment.OBJECT_PROTO); + 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); } catch (NullPointerException e) { return null; } } @@ -185,10 +185,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(Context ctx, Object key) { + protected Object getField(Extensions ext, Object key) { if (values.containsKey(key)) return values.get(key); - var proto = getPrototype(ctx); - if (proto != null) return proto.getField(ctx, key); + var proto = getPrototype(ext); + if (proto != null) return proto.getField(ext, key); else return null; } /** @@ -196,9 +196,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(Context ctx, Object key, Object val) { + protected boolean setField(Extensions ext, Object key, Object val) { if (val instanceof FunctionValue && ((FunctionValue)val).name.equals("")) { - ((FunctionValue)val).name = Values.toString(ctx, key); + ((FunctionValue)val).name = Values.toString(ext, key); } values.put(key, val); @@ -207,40 +207,40 @@ public class ObjectValue { /** * Deletes the field bound to the given key. */ - protected void deleteField(Context ctx, Object key) { + protected void deleteField(Extensions ext, 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(Context ctx, Object key) { + protected boolean hasField(Extensions ext, Object key) { return values.containsKey(key); } - public final Object getMember(Context ctx, Object key, Object thisArg) { - key = Values.normalize(ctx, key); + public final Object getMember(Extensions ext, Object key, Object thisArg) { + key = Values.normalize(ext, key); if ("__proto__".equals(key)) { - var res = getPrototype(ctx); + var res = getPrototype(ext); return res == null ? Values.NULL : res; } - var prop = getProperty(ctx, key); + var prop = getProperty(ext, key); if (prop != null) { if (prop.getter == null) return null; - else return prop.getter.call(ctx, Values.normalize(ctx, thisArg)); + else return prop.getter.call(ext, Values.normalize(ext, thisArg)); } - else return getField(ctx, key); + else return getField(ext, key); } - public final boolean setMember(Context ctx, Object key, Object val, Object thisArg, boolean onlyProps) { - key = Values.normalize(ctx, key); val = Values.normalize(ctx, val); + public final boolean setMember(Extensions ext, Object key, Object val, Object thisArg, boolean onlyProps) { + key = Values.normalize(ext, key); val = Values.normalize(ext, val); - var prop = getProperty(ctx, key); + var prop = getProperty(ext, key); if (prop != null) { if (prop.setter == null) return false; - prop.setter.call(ctx, Values.normalize(ctx, thisArg), val); + prop.setter.call(ext, Values.normalize(ext, thisArg), val); return true; } else if (onlyProps) return false; @@ -249,32 +249,32 @@ public class ObjectValue { values.put(key, val); return true; } - else if ("__proto__".equals(key)) return setPrototype(ctx, val); + else if ("__proto__".equals(key)) return setPrototype(ext, val); else if (nonWritableSet.contains(key)) return false; - else return setField(ctx, key, val); + else return setField(ext, key, val); } - public final boolean hasMember(Context ctx, Object key, boolean own) { - key = Values.normalize(ctx, key); + public final boolean hasMember(Extensions ext, Object key, boolean own) { + key = Values.normalize(ext, key); if (key != null && "__proto__".equals(key)) return true; - if (hasField(ctx, key)) return true; + if (hasField(ext, key)) return true; if (properties.containsKey(key)) return true; if (own) return false; - var proto = getPrototype(ctx); - return proto != null && proto.hasMember(ctx, key, own); + var proto = getPrototype(ext); + return proto != null && proto.hasMember(ext, key, own); } - public final boolean deleteMember(Context ctx, Object key) { - key = Values.normalize(ctx, key); + public final boolean deleteMember(Extensions ext, Object key) { + key = Values.normalize(ext, key); if (!memberConfigurable(key)) return false; properties.remove(key); nonWritableSet.remove(key); nonEnumerableSet.remove(key); - deleteField(ctx, key); + deleteField(ext, key); return true; } - public final boolean setPrototype(Context ctx, Object val) { - val = Values.normalize(ctx, val); + public final boolean setPrototype(Extensions ext, Object val) { + val = Values.normalize(ext, val); if (!extensible()) return false; if (val == null || val == Values.NULL) { @@ -284,14 +284,14 @@ public class ObjectValue { else if (val instanceof ObjectValue) { var obj = (ObjectValue)val; - if (ctx != null) { - if (obj == ctx.get(Environment.OBJECT_PROTO)) prototype = OBJ_PROTO; - else if (obj == ctx.get(Environment.ARRAY_PROTO)) prototype = ARR_PROTO; - else if (obj == ctx.get(Environment.FUNCTION_PROTO)) prototype = FUNC_PROTO; - else if (obj == ctx.get(Environment.ERROR_PROTO)) prototype = ERR_PROTO; - else if (obj == ctx.get(Environment.SYNTAX_ERR_PROTO)) prototype = SYNTAX_ERR_PROTO; - else if (obj == ctx.get(Environment.TYPE_ERR_PROTO)) prototype = TYPE_ERR_PROTO; - else if (obj == ctx.get(Environment.RANGE_ERR_PROTO)) prototype = RANGE_ERR_PROTO; + 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; else prototype = obj; } else prototype = obj; @@ -301,22 +301,22 @@ public class ObjectValue { return false; } - public final ObjectValue getMemberDescriptor(Context ctx, Object key) { - key = Values.normalize(ctx, key); + public final ObjectValue getMemberDescriptor(Extensions ext, Object key) { + key = Values.normalize(ext, key); var prop = properties.get(key); var res = new ObjectValue(); - res.defineProperty(ctx, "configurable", memberConfigurable(key)); - res.defineProperty(ctx, "enumerable", memberEnumerable(key)); + res.defineProperty(ext, "configurable", memberConfigurable(key)); + res.defineProperty(ext, "enumerable", memberEnumerable(key)); if (prop != null) { - res.defineProperty(ctx, "get", prop.getter); - res.defineProperty(ctx, "set", prop.setter); + res.defineProperty(ext, "get", prop.getter); + res.defineProperty(ext, "set", prop.setter); } - else if (hasField(ctx, key)) { - res.defineProperty(ctx, "value", values.get(key)); - res.defineProperty(ctx, "writable", memberWritable(key)); + else if (hasField(ext, key)) { + res.defineProperty(ext, "value", values.get(key)); + res.defineProperty(ext, "writable", memberWritable(key)); } else return null; return res; @@ -337,10 +337,10 @@ public class ObjectValue { return res; } - public ObjectValue(Context ctx, Map values) { + public ObjectValue(Extensions ext, Map values) { this(PlaceholderProto.OBJECT); for (var el : values.entrySet()) { - defineProperty(ctx, el.getKey(), el.getValue()); + defineProperty(ext, el.getKey(), el.getValue()); } } public ObjectValue(PlaceholderProto proto) { diff --git a/src/java/me/topchetoeu/jscript/runtime/values/ScopeValue.java b/src/java/me/topchetoeu/jscript/runtime/values/ScopeValue.java index 099ab96..a64f4c2 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/ScopeValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/ScopeValue.java @@ -3,7 +3,7 @@ package me.topchetoeu.jscript.runtime.values; import java.util.HashMap; import java.util.List; -import me.topchetoeu.jscript.runtime.Context; +import me.topchetoeu.jscript.runtime.Extensions; import me.topchetoeu.jscript.runtime.scope.ValueVariable; public class ScopeValue extends ObjectValue { @@ -11,31 +11,31 @@ public class ScopeValue extends ObjectValue { public final HashMap names = new HashMap<>(); @Override - protected Object getField(Context ctx, Object key) { - if (names.containsKey(key)) return variables[names.get(key)].get(ctx); - return super.getField(ctx, key); + protected Object getField(Extensions ext, Object key) { + if (names.containsKey(key)) return variables[names.get(key)].get(ext); + return super.getField(ext, key); } @Override - protected boolean setField(Context ctx, Object key, Object val) { + protected boolean setField(Extensions ext, Object key, Object val) { if (names.containsKey(key)) { - variables[names.get(key)].set(ctx, val); + variables[names.get(key)].set(ext, val); return true; } - var proto = getPrototype(ctx); - if (proto != null && proto.hasMember(ctx, key, false) && proto.setField(ctx, key, val)) return true; + var proto = getPrototype(ext); + if (proto != null && proto.hasMember(ext, key, false) && proto.setField(ext, key, val)) return true; - return super.setField(ctx, key, val); + return super.setField(ext, key, val); } @Override - protected void deleteField(Context ctx, Object key) { + protected void deleteField(Extensions ext, Object key) { if (names.containsKey(key)) return; - super.deleteField(ctx, key); + super.deleteField(ext, key); } @Override - protected boolean hasField(Context ctx, Object key) { + protected boolean hasField(Extensions ext, Object key) { if (names.containsKey(key)) return true; - return super.hasField(ctx, key); + return super.hasField(ext, key); } @Override public List keys(boolean includeNonEnumerable) { diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Values.java b/src/java/me/topchetoeu/jscript/runtime/values/Values.java index ed4b288..db93cd8 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/Values.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/Values.java @@ -14,12 +14,13 @@ import java.util.Map; import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.lib.PromiseLib; -import me.topchetoeu.jscript.runtime.Context; import me.topchetoeu.jscript.runtime.Environment; +import me.topchetoeu.jscript.runtime.Extensions; import me.topchetoeu.jscript.runtime.debug.DebugContext; 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 class Values { public static enum CompareResult { @@ -73,11 +74,11 @@ public class Values { return "object"; } - private static Object tryCallConvertFunc(Context ctx, Object obj, String name) { - var func = getMember(ctx, obj, name); + private static Object tryCallConvertFunc(Extensions ext, Object obj, String name) { + var func = getMember(ext, obj, name); if (func instanceof FunctionValue) { - var res = Values.call(ctx, func, obj); + var res = Values.call(ext, func, obj); if (isPrimitive(res)) return res; } @@ -94,16 +95,16 @@ public class Values { obj == NULL; } - public static Object toPrimitive(Context ctx, Object obj, ConvertHint hint) { - obj = normalize(ctx, obj); + public static Object toPrimitive(Extensions ext, Object obj, ConvertHint hint) { + obj = normalize(ext, obj); if (isPrimitive(obj)) return obj; var first = hint == ConvertHint.VALUEOF ? "valueOf" : "toString"; var second = hint == ConvertHint.VALUEOF ? "toString" : "valueOf"; - if (ctx != null) { - try { return tryCallConvertFunc(ctx, obj, first); } - catch (EngineException unused) { return tryCallConvertFunc(ctx, obj, second); } + if (ext != null) { + try { return tryCallConvertFunc(ext, obj, first); } + catch (EngineException unused) { return tryCallConvertFunc(ext, obj, second); } } throw EngineException.ofType("Value couldn't be converted to a primitive."); @@ -115,8 +116,8 @@ public class Values { if (obj instanceof Boolean) return (Boolean)obj; return true; } - public static double toNumber(Context ctx, Object obj) { - var val = toPrimitive(ctx, obj, ConvertHint.VALUEOF); + public static double toNumber(Extensions ext, Object obj) { + var val = toPrimitive(ext, obj, ConvertHint.VALUEOF); if (val instanceof Number) return number(val); if (val instanceof Boolean) return ((Boolean)val) ? 1 : 0; @@ -126,8 +127,8 @@ public class Values { } return Double.NaN; } - public static String toString(Context ctx, Object obj) { - var val = toPrimitive(ctx, obj, ConvertHint.VALUEOF); + public static String toString(Extensions ext, Object obj) { + var val = toPrimitive(ext, obj, ConvertHint.VALUEOF); if (val == null) return "undefined"; if (val == NULL) return "null"; @@ -146,63 +147,63 @@ public class Values { return "Unknown value"; } - public static Object add(Context ctx, Object a, Object b) { - if (a instanceof String || b instanceof String) return toString(ctx, a) + toString(ctx, b); - else return toNumber(ctx, a) + toNumber(ctx, b); + public static Object add(Extensions 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(Context ctx, Object a, Object b) { - return toNumber(ctx, a) - toNumber(ctx, b); + public static double subtract(Extensions ext, Object a, Object b) { + return toNumber(ext, a) - toNumber(ext, b); } - public static double multiply(Context ctx, Object a, Object b) { - return toNumber(ctx, a) * toNumber(ctx, b); + public static double multiply(Extensions ext, Object a, Object b) { + return toNumber(ext, a) * toNumber(ext, b); } - public static double divide(Context ctx, Object a, Object b) { - return toNumber(ctx, a) / toNumber(ctx, b); + public static double divide(Extensions ext, Object a, Object b) { + return toNumber(ext, a) / toNumber(ext, b); } - public static double modulo(Context ctx, Object a, Object b) { - return toNumber(ctx, a) % toNumber(ctx, b); + public static double modulo(Extensions ext, Object a, Object b) { + return toNumber(ext, a) % toNumber(ext, b); } - public static double negative(Context ctx, Object obj) { - return -toNumber(ctx, obj); + public static double negative(Extensions ext, Object obj) { + return -toNumber(ext, obj); } - public static int and(Context ctx, Object a, Object b) { - return (int)toNumber(ctx, a) & (int)toNumber(ctx, b); + public static int and(Extensions ext, Object a, Object b) { + return (int)toNumber(ext, a) & (int)toNumber(ext, b); } - public static int or(Context ctx, Object a, Object b) { - return (int)toNumber(ctx, a) | (int)toNumber(ctx, b); + public static int or(Extensions ext, Object a, Object b) { + return (int)toNumber(ext, a) | (int)toNumber(ext, b); } - public static int xor(Context ctx, Object a, Object b) { - return (int)toNumber(ctx, a) ^ (int)toNumber(ctx, b); + public static int xor(Extensions ext, Object a, Object b) { + return (int)toNumber(ext, a) ^ (int)toNumber(ext, b); } - public static int bitwiseNot(Context ctx, Object obj) { - return ~(int)toNumber(ctx, obj); + public static int bitwiseNot(Extensions ext, Object obj) { + return ~(int)toNumber(ext, obj); } - public static int shiftLeft(Context ctx, Object a, Object b) { - return (int)toNumber(ctx, a) << (int)toNumber(ctx, b); + public static int shiftLeft(Extensions ext, Object a, Object b) { + return (int)toNumber(ext, a) << (int)toNumber(ext, b); } - public static int shiftRight(Context ctx, Object a, Object b) { - return (int)toNumber(ctx, a) >> (int)toNumber(ctx, b); + public static int shiftRight(Extensions ext, Object a, Object b) { + return (int)toNumber(ext, a) >> (int)toNumber(ext, b); } - public static long unsignedShiftRight(Context ctx, Object a, Object b) { - long _a = (long)toNumber(ctx, a); - long _b = (long)toNumber(ctx, b); + public static long unsignedShiftRight(Extensions 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(Context ctx, Object a, Object b) { - a = toPrimitive(ctx, a, ConvertHint.VALUEOF); - b = toPrimitive(ctx, b, ConvertHint.VALUEOF); + public static CompareResult compare(Extensions 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(ctx, a); - var _b = toNumber(ctx, b); + var _a = toNumber(ext, a); + var _b = toNumber(ext, b); if (Double.isNaN(_a) || Double.isNaN(_b)) return CompareResult.NOT_EQUAL; @@ -213,60 +214,60 @@ public class Values { return !toBoolean(obj); } - public static boolean isInstanceOf(Context ctx, Object obj, Object proto) { + public static boolean isInstanceOf(Extensions ext, Object obj, Object proto) { if (obj == null || obj == NULL || proto == null || proto == NULL) return false; - var val = getPrototype(ctx, obj); + var val = getPrototype(ext, obj); while (val != null) { if (val.equals(proto)) return true; - val = val.getPrototype(ctx); + val = val.getPrototype(ext); } return false; } - public static Object operation(Context ctx, Operation op, Object ...args) { + public static Object operation(Extensions ext, Operation op, Object ...args) { switch (op) { - case ADD: return add(ctx, args[0], args[1]); - case SUBTRACT: return subtract(ctx, args[0], args[1]); - case DIVIDE: return divide(ctx, args[0], args[1]); - case MULTIPLY: return multiply(ctx, args[0], args[1]); - case MODULO: return modulo(ctx, args[0], args[1]); + 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(ctx, args[0], args[1]); - case OR: return or(ctx, args[0], args[1]); - case XOR: return xor(ctx, 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(ctx, args[0], args[1]); - case NOT_EQUALS: return !strictEquals(ctx, args[0], args[1]); - case LOOSE_EQUALS: return looseEqual(ctx, args[0], args[1]); - case LOOSE_NOT_EQUALS: return !looseEqual(ctx, 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(ctx, args[0], args[1]).greater(); - case GREATER_EQUALS: return compare(ctx, args[0], args[1]).greaterOrEqual(); - case LESS: return compare(ctx, args[0], args[1]).less(); - case LESS_EQUALS: return compare(ctx, args[0], args[1]).lessOrEqual(); + 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(ctx, args[0]); + case INVERSE: return bitwiseNot(ext, args[0]); case NOT: return not(args[0]); - case POS: return toNumber(ctx, args[0]); - case NEG: return negative(ctx, args[0]); + case POS: return toNumber(ext, args[0]); + case NEG: return negative(ext, args[0]); - case SHIFT_LEFT: return shiftLeft(ctx, args[0], args[1]); - case SHIFT_RIGHT: return shiftRight(ctx, args[0], args[1]); - case USHIFT_RIGHT: return unsignedShiftRight(ctx, args[0], args[1]); + 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(ctx, args[1], args[0], false); + case IN: return hasMember(ext, args[1], args[0], false); case INSTANCEOF: { - var proto = getMember(ctx, args[1], "prototype"); - return isInstanceOf(ctx, args[0], proto); + var proto = getMember(ext, args[1], "prototype"); + return isInstanceOf(ext, args[0], proto); } default: return null; } } - public static Object getMember(Context ctx, Object obj, Object key) { + public static Object getMember(Extensions 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."); @@ -286,12 +287,12 @@ public class Values { else if (key != null && "__proto__".equals(key)) return proto; else return proto.getMember(ctx, key, obj); } - public static Object getMemberPath(Context ctx, Object obj, Object ...path) { + public static Object getMemberPath(Extensions ctx, Object obj, Object ...path) { var res = obj; for (var key : path) res = getMember(ctx, res, key); return res; } - public static boolean setMember(Context ctx, Object obj, Object key, Object val) { + public static boolean setMember(Extensions 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."); @@ -301,7 +302,7 @@ public class Values { var proto = getPrototype(ctx, obj); return proto.setMember(ctx, key, val, obj, true); } - public static boolean hasMember(Context ctx, Object obj, Object key, boolean own) { + public static boolean hasMember(Extensions ctx, Object obj, Object key, boolean own) { if (obj == null || obj == NULL) return false; obj = normalize(ctx, obj); key = normalize(ctx, key); @@ -319,36 +320,36 @@ public class Values { var proto = getPrototype(ctx, obj); return proto != null && proto.hasMember(ctx, key, own); } - public static boolean deleteMember(Context ctx, Object obj, Object key) { + public static boolean deleteMember(Extensions ext, Object obj, Object key) { if (obj == null || obj == NULL) return false; - obj = normalize(ctx, obj); key = normalize(ctx, key); + obj = normalize(ext, obj); key = normalize(ext, key); - if (obj instanceof ObjectValue) return ((ObjectValue)obj).deleteMember(ctx, key); + if (obj instanceof ObjectValue) return ((ObjectValue)obj).deleteMember(ext, key); else return false; } - public static ObjectValue getPrototype(Context ctx, Object obj) { + public static ObjectValue getPrototype(Extensions ext, Object obj) { if (obj == null || obj == NULL) return null; - obj = normalize(ctx, obj); - if (obj instanceof ObjectValue) return ((ObjectValue)obj).getPrototype(ctx); - if (ctx == 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 ctx.get(Environment.STRING_PROTO); - else if (obj instanceof Number) return ctx.get(Environment.NUMBER_PROTO); - else if (obj instanceof Boolean) return ctx.get(Environment.BOOL_PROTO); - else if (obj instanceof Symbol) return ctx.get(Environment.SYMBOL_PROTO); + 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(Context ctx, Object obj, Object proto) { - obj = normalize(ctx, obj); - return obj instanceof ObjectValue && ((ObjectValue)obj).setPrototype(ctx, proto); + public static boolean setPrototype(Extensions ext, Object obj, Object proto) { + obj = normalize(ext, obj); + return obj instanceof ObjectValue && ((ObjectValue)obj).setPrototype(ext, proto); } - public static void makePrototypeChain(Context ctx, Object... chain) { + public static void makePrototypeChain(Extensions ext, Object... chain) { for(var i = 1; i < chain.length; i++) { - setPrototype(ctx, chain[i], chain[i - 1]); + setPrototype(ext, chain[i], chain[i - 1]); } } - public static List getMembers(Context ctx, Object obj, boolean own, boolean includeNonEnumerable) { + public static List getMembers(Extensions ext, Object obj, boolean own, boolean includeNonEnumerable) { List res = new ArrayList<>(); if (obj instanceof ObjectValue) res = ((ObjectValue)obj).keys(includeNonEnumerable); @@ -357,26 +358,26 @@ public class Values { } if (!own) { - var proto = getPrototype(ctx, obj); + var proto = getPrototype(ext, obj); while (proto != null) { res.addAll(proto.keys(includeNonEnumerable)); - proto = getPrototype(ctx, proto); + proto = getPrototype(ext, proto); } } return res; } - public static ObjectValue getMemberDescriptor(Context ctx, Object obj, Object key) { - if (obj instanceof ObjectValue) return ((ObjectValue)obj).getMemberDescriptor(ctx, key); + public static ObjectValue getMemberDescriptor(Extensions 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(ctx, Map.of( + return new ObjectValue(ext, Map.of( "value", ((String)obj).charAt(i) + "", "writable", false, "enumerable", true, @@ -386,17 +387,17 @@ public class Values { else return null; } - public static Object call(Context ctx, Object func, Object thisArg, Object ...args) { + public static Object call(Extensions 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(ctx, thisArg, args); + return ((FunctionValue)func).call(ext, thisArg, args); } - public static Object callNew(Context ctx, Object func, Object ...args) { + public static Object callNew(Extensions ext, Object func, Object ...args) { var res = new ObjectValue(); try { - var proto = Values.getMember(ctx, func, "prototype"); - setPrototype(ctx, res, proto); + var proto = Values.getMember(ext, func, "prototype"); + setPrototype(ext, res, proto); - var ret = call(ctx, func, res, args); + var ret = call(ext, func, res, args); if (!isPrimitive(ret)) return ret; return res; @@ -406,8 +407,9 @@ public class Values { } } - public static boolean strictEquals(Context ctx, Object a, Object b) { - a = normalize(ctx, a); b = normalize(ctx, b); + public static boolean strictEquals(Extensions 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; @@ -416,8 +418,8 @@ public class Values { return a == b || a.equals(b); } - public static boolean looseEqual(Context ctx, Object a, Object b) { - a = normalize(ctx, a); b = normalize(ctx, b); + public static boolean looseEqual(Extensions 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; @@ -428,19 +430,19 @@ public class Values { if (!isPrimitive(a) && !isPrimitive(b)) return a == b; // Convert values to primitives - a = toPrimitive(ctx, a, ConvertHint.VALUEOF); - b = toPrimitive(ctx, b, ConvertHint.VALUEOF); + 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(ctx, toNumber(ctx, a), toNumber(ctx, b)); + if (a instanceof Number || b instanceof Number) return strictEquals(ext, toNumber(ext, a), toNumber(ext, b)); // Default to strings - return toString(ctx, a).equals(toString(ctx, b)); + return toString(ext, a).equals(toString(ext, b)); } - public static Object normalize(Context ctx, Object val) { + public static Object normalize(Extensions ext, Object val) { if (val instanceof Number) return number(val); if (isPrimitive(val) || val instanceof ObjectValue) return val; if (val instanceof Character) return val + ""; @@ -449,7 +451,7 @@ public class Values { var res = new ObjectValue(); for (var entry : ((Map)val).entrySet()) { - res.defineProperty(ctx, entry.getKey(), entry.getValue()); + res.defineProperty(ext, entry.getKey(), entry.getValue()); } return res; @@ -459,22 +461,22 @@ public class Values { var res = new ArrayValue(); for (var entry : ((Iterable)val)) { - res.set(ctx, res.size(), entry); + res.set(ext, res.size(), entry); } return res; } if (val instanceof Class) { - if (ctx == null) return null; - else return ctx.environment.wrappers.getConstr((Class)val); + if (ext == null) return null; + else return NativeWrapperProvider.get(ext).getConstr((Class)val); } - return NativeWrapper.of(ctx, val); + return NativeWrapper.of(ext, val); } @SuppressWarnings("unchecked") - public static T convert(Context ctx, Object obj, Class clazz) { + public static T convert(Extensions ext, Object obj, Class clazz) { if (clazz == Void.class) return null; if (obj instanceof NativeWrapper) { @@ -488,19 +490,19 @@ public class Values { 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(ctx, raw[i], Object.class)); + 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(ctx, raw[i], Object.class)); + 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(ctx, raw[i], Object.class)); + for (var i = 0; i < raw.length; i++) Array.set(res, i, convert(ext, raw[i], Object.class)); return (T)res; } } @@ -508,25 +510,25 @@ public class Values { if (obj instanceof ObjectValue && clazz.isAssignableFrom(HashMap.class)) { var res = new HashMap<>(); for (var el : ((ObjectValue)obj).values.entrySet()) res.put( - convert(ctx, el.getKey(), null), - convert(ctx, el.getValue(), null) + convert(ext, el.getKey(), null), + convert(ext, el.getValue(), null) ); return (T)res; } - if (clazz == String.class) return (T)toString(ctx, obj); + 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(ctx, obj); - if (clazz == Integer.class || clazz == int.class) return (T)(Integer)(int)toNumber(ctx, obj); - if (clazz == Long.class || clazz == long.class) return (T)(Long)(long)toNumber(ctx, obj); - if (clazz == Short.class || clazz == short.class) return (T)(Short)(short)toNumber(ctx, obj); - if (clazz == Float.class || clazz == float.class) return (T)(Float)(float)toNumber(ctx, obj); - if (clazz == Double.class || clazz == double.class) return (T)(Double)toNumber(ctx, 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(ctx, obj); + var res = toString(ext, obj); if (res.length() == 0) throw new ConvertException("\"\"", "Character"); else return (T)(Character)res.charAt(0); } @@ -535,23 +537,23 @@ public class Values { if (obj == null) return null; if (clazz.isInstance(obj)) return (T)obj; if (clazz.isAssignableFrom(NativeWrapper.class)) { - return (T)NativeWrapper.of(ctx, obj); + return (T)NativeWrapper.of(ext, obj); } throw new ConvertException(type(obj), clazz.getSimpleName()); } - public static Iterable fromJSIterator(Context ctx, Object obj) { + public static Iterable fromJSIterator(Extensions ext, Object obj) { return () -> { try { var symbol = Symbol.get("Symbol.iterator"); - var iteratorFunc = getMember(ctx, obj, symbol); + var iteratorFunc = getMember(ext, obj, symbol); if (!(iteratorFunc instanceof FunctionValue)) return Collections.emptyIterator(); var iterator = iteratorFunc instanceof FunctionValue ? - ((FunctionValue)iteratorFunc).call(ctx, obj, obj) : + ((FunctionValue)iteratorFunc).call(ext, obj, obj) : iteratorFunc; - var nextFunc = getMember(ctx, call(ctx, iteratorFunc, obj), "next"); + var nextFunc = getMember(ext, call(ext, iteratorFunc, obj), "next"); if (!(nextFunc instanceof FunctionValue)) return Collections.emptyIterator(); @@ -563,11 +565,11 @@ public class Values { private void loadNext() { if (next == null) value = null; else if (consumed) { - var curr = next.call(ctx, iterator); + var curr = next.call(ext, iterator); if (curr == null) { next = null; value = null; } - if (toBoolean(Values.getMember(ctx, curr, "done"))) { next = null; value = null; } + if (toBoolean(Values.getMember(ext, curr, "done"))) { next = null; value = null; } else { - this.value = Values.getMember(ctx, curr, "value"); + this.value = Values.getMember(ext, curr, "value"); consumed = false; } } @@ -594,17 +596,17 @@ public class Values { }; } - public static ObjectValue toJSIterator(Context ctx, Iterator it) { + public static ObjectValue toJSIterator(Extensions ext, Iterator it) { var res = new ObjectValue(); try { - var key = getMember(ctx, getMember(ctx, ctx.get(Environment.SYMBOL_PROTO), "constructor"), "iterator"); - res.defineProperty(ctx, key, new NativeFunction("", args -> args.self)); + 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(ctx, "next", new NativeFunction("", args -> { - if (!it.hasNext()) return new ObjectValue(ctx, Map.of("done", true)); + 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.ctx, "value", it.next()); @@ -615,22 +617,22 @@ public class Values { return res; } - public static ObjectValue toJSIterator(Context ctx, Iterable it) { - return toJSIterator(ctx, it.iterator()); + public static ObjectValue toJSIterator(Extensions ext, Iterable it) { + return toJSIterator(ext, it.iterator()); } - public static ObjectValue toJSAsyncIterator(Context ctx, Iterator it) { + public static ObjectValue toJSAsyncIterator(Extensions ext, Iterator it) { var res = new ObjectValue(); try { - var key = getMemberPath(ctx, ctx.get(Environment.SYMBOL_PROTO), "constructor", "asyncIterator"); - res.defineProperty(ctx, key, new NativeFunction("", args -> args.self)); + 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(ctx, "next", new NativeFunction("", args -> { + res.defineProperty(ext, "next", new NativeFunction("", args -> { return PromiseLib.await(args.ctx, () -> { - if (!it.hasNext()) return new ObjectValue(ctx, Map.of("done", true)); + if (!it.hasNext()) return new ObjectValue(ext, Map.of("done", true)); else { var obj = new ObjectValue(); obj.defineProperty(args.ctx, "value", it.next()); @@ -652,14 +654,14 @@ public class Values { if (protoObj.values.size() + protoObj.properties.size() != 1) return false; return true; } - private static String toReadable(Context ctx, Object val, HashSet passed, int tab) { + private static String toReadable(Extensions ext, Object val, HashSet 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(ctx); + var dbg = DebugContext.get(ext); if (val instanceof FunctionValue) { res.append(val.toString()); @@ -673,7 +675,7 @@ public class Values { for (int i = 0; i < obj.size(); i++) { if (i != 0) res.append(", "); else res.append(" "); - if (obj.has(i)) res.append(toReadable(ctx, obj.get(i), passed, tab)); + if (obj.has(i)) res.append(toReadable(ext, obj.get(i), passed, tab)); else res.append(""); } res.append(" ] "); @@ -700,14 +702,14 @@ public class Values { for (var el : obj.values.entrySet()) { for (int i = 0; i < tab + 1; i++) res.append(" "); - res.append(toReadable(ctx, el.getKey(), passed, tab + 1)); + res.append(toReadable(ext, el.getKey(), passed, tab + 1)); res.append(": "); - res.append(toReadable(ctx, el.getValue(), passed, tab + 1)); + 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(ctx, el.getKey(), passed, tab + 1)); + res.append(toReadable(ext, el.getKey(), passed, tab + 1)); res.append(": [prop],\n"); } @@ -720,23 +722,23 @@ public class Values { else if (val == null) return "undefined"; else if (val == Values.NULL) return "null"; else if (val instanceof String) return "'" + val + "'"; - else return Values.toString(ctx, val); + else return Values.toString(ext, val); return res.toString(); } - public static String toReadable(Context ctx, Object val) { - return toReadable(ctx, val, new HashSet<>(), 0); + public static String toReadable(Extensions 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(new Context(ee.env)); + return prefix + " " + ee.toString(ee.ext); } catch (EngineException ex) { - return prefix + " " + toReadable(new Context(ee.env), ee.value); + return prefix + " " + toReadable(ee.ext, ee.value); } } else if (err instanceof SyntaxException) { @@ -750,8 +752,8 @@ public class Values { return prefix + " internal error " + str.toString(); } } - public static void printValue(Context ctx, Object val) { - System.out.print(toReadable(ctx, val)); + public static void printValue(Extensions ext, Object val) { + System.out.print(toReadable(ext, val)); } public static void printError(RuntimeException err, String prefix) { System.out.println(errorToReadable(err, prefix)); diff --git a/src/java/me/topchetoeu/jscript/utils/debug/SimpleDebugger.java b/src/java/me/topchetoeu/jscript/utils/debug/SimpleDebugger.java index 5386ba6..f7af2e1 100644 --- a/src/java/me/topchetoeu/jscript/utils/debug/SimpleDebugger.java +++ b/src/java/me/topchetoeu/jscript/utils/debug/SimpleDebugger.java @@ -27,6 +27,7 @@ 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.exceptions.EngineException; @@ -149,7 +150,7 @@ public class SimpleDebugger implements Debugger { this.frame = frame; this.id = id; - this.global = frame.function.environment.global.obj; + this.global = GlobalScope.get(frame.ctx).obj; this.local = frame.getLocalScope(); this.capture = frame.getCaptureScope(); Values.makePrototypeChain(frame.ctx, global, capture, local); @@ -162,29 +163,29 @@ public class SimpleDebugger implements Debugger { .add(new JSONMap() .set("type", "local") .set("name", "Local Scope") - .set("object", serializeObj(frame.ctx.environment, local)) + .set("object", serializeObj(frame.ctx, local)) ) .add(new JSONMap() .set("type", "closure") .set("name", "Closure") - .set("object", serializeObj(frame.ctx.environment, capture)) + .set("object", serializeObj(frame.ctx, capture)) ) .add(new JSONMap() .set("type", "global") .set("name", "Global Scope") - .set("object", serializeObj(frame.ctx.environment, global)) + .set("object", serializeObj(frame.ctx.extensions, global)) ) .add(new JSONMap() .set("type", "other") .set("name", "Value Stack") - .set("object", serializeObj(frame.ctx.environment, valstack)) + .set("object", serializeObj(frame.ctx.extensions, valstack)) ) ); } } private class ObjRef { public final ObjectValue obj; - public final Environment env; + public final Extensions ext; public final HashSet heldGroups = new HashSet<>(); public boolean held = true; @@ -192,19 +193,19 @@ public class SimpleDebugger implements Debugger { return !held && heldGroups.size() == 0; } - public ObjRef(Environment env, ObjectValue obj) { - this.env = env; + public ObjRef(Extensions ext, ObjectValue obj) { + this.ext = ext; this.obj = obj; } } private static class RunResult { - public final Environment ctx; + public final Extensions ext; public final Object result; public final EngineException error; - public RunResult(Environment env, Object result, EngineException error) { - this.ctx = env; + public RunResult(Extensions ext, Object result, EngineException error) { + this.ext = ext; this.result = result; this.error = error; } @@ -338,10 +339,10 @@ public class SimpleDebugger implements Debugger { .set("columnNumber", loc.start() - 1); } - private JSONMap serializeObj(Environment env, Object val, boolean byValue) { + private JSONMap serializeObj(Extensions env, Object val, boolean byValue) { val = Values.normalize(null, val); env = sanitizeEnvironment(env); - var ctx = env.context(); + var ctx = Context.of(env); if (val == Values.NULL) { return new JSONMap() @@ -400,7 +401,7 @@ public class SimpleDebugger implements Debugger { } - if (byValue) try { res.put("value", JSON.fromJs(env.context(), obj)); } + if (byValue) try { res.put("value", JSON.fromJs(env, obj)); } catch (Exception e) { } return res; @@ -426,8 +427,8 @@ public class SimpleDebugger implements Debugger { throw new IllegalArgumentException("Unexpected JS object."); } - private JSONMap serializeObj(Environment env, Object val) { - return serializeObj(env, val, false); + private JSONMap serializeObj(Extensions ext, Object val) { + return serializeObj(ext, val, false); } private void addObjectGroup(String name, Object val) { if (val instanceof ObjectValue) { @@ -466,11 +467,11 @@ public class SimpleDebugger implements Debugger { else return JSON.toJs(res); } - private JSONMap serializeException(Environment env, EngineException err) { + private JSONMap serializeException(Extensions ext, EngineException err) { String text = null; try { - text = Values.toString(env.context(), err.value); + text = Values.toString(Context.of(ext), err.value); } catch (EngineException e) { text = "[error while stringifying]"; @@ -478,7 +479,7 @@ public class SimpleDebugger implements Debugger { var res = new JSONMap() .set("exceptionId", nextId()) - .set("exception", serializeObj(env, err.value)) + .set("exception", serializeObj(ext, err.value)) .set("text", text); return res; @@ -539,8 +540,8 @@ public class SimpleDebugger implements Debugger { } } - private Environment sanitizeEnvironment(Environment env) { - var res = env.child(); + private Extensions sanitizeEnvironment(Extensions ext) { + var res = ext.child(); res.remove(EventLoop.KEY); res.remove(DebugContext.KEY); @@ -552,12 +553,13 @@ public class SimpleDebugger implements Debugger { 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.environment.copy(); + var env = codeFrame.frame.ctx.extensions.copy(); - env.global = new GlobalScope(codeFrame.local); - env.remove(EventLoop.KEY); 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 awaiter = engine.pushMsg(false, env, new Filename("jscript", "eval"), code, codeFrame.frame.thisArg, codeFrame.frame.args); @@ -569,12 +571,12 @@ public class SimpleDebugger implements Debugger { catch (SyntaxException e) { return new RunResult(env, null, EngineException.ofSyntax(e.toString())); } } - private ObjectValue vscodeAutoSuggest(Environment env, Object target, String query, boolean variable) { + private ObjectValue vscodeAutoSuggest(Extensions ext, Object target, String query, boolean variable) { var res = new ArrayValue(); var passed = new HashSet(); var tildas = "~"; - var ctx = env.context(); - if (target == null) target = env.global; + var ctx = Context.of(ext); + if (target == null) target = GlobalScope.get(ext); for (var proto = target; proto != null && proto != Values.NULL; proto = Values.getPrototype(ctx, proto)) { for (var el : Values.getMembers(ctx, proto, true, true)) { @@ -827,8 +829,8 @@ public class SimpleDebugger implements Debugger { if (group != null) addObjectGroup(group, res.result); - if (res.error != null) ws.send(msg.respond(new JSONMap().set("exceptionDetails", serializeException(res.ctx, res.error)))); - else ws.send(msg.respond(new JSONMap().set("result", serializeObj(res.ctx, res.result)))); + 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)))); } @Override public synchronized void releaseObjectGroup(V8Message msg) throws IOException { @@ -851,8 +853,8 @@ public class SimpleDebugger implements Debugger { @Override public synchronized void getProperties(V8Message msg) throws IOException { var ref = idToObject.get(Integer.parseInt(msg.params.string("objectId"))); var obj = ref.obj; - var env = ref.env; - var ctx = env.context(); + var ext = ref.ext; + var ctx = Context.of(ext); var res = new JSONList(); var own = true; @@ -865,8 +867,8 @@ public class SimpleDebugger implements Debugger { var prop = obj.properties.get(key); propDesc.set("name", Values.toString(ctx, key)); - if (prop.getter != null) propDesc.set("get", serializeObj(env, prop.getter)); - if (prop.setter != null) propDesc.set("set", serializeObj(env, prop.setter)); + if (prop.getter != null) propDesc.set("get", serializeObj(ext, prop.getter)); + if (prop.setter != null) propDesc.set("set", serializeObj(ext, prop.setter)); propDesc.set("enumerable", obj.memberEnumerable(key)); propDesc.set("configurable", obj.memberConfigurable(key)); propDesc.set("isOwn", true); @@ -874,7 +876,7 @@ public class SimpleDebugger implements Debugger { } else { propDesc.set("name", Values.toString(ctx, key)); - propDesc.set("value", serializeObj(env, Values.getMember(ctx, obj, key))); + propDesc.set("value", serializeObj(ext, Values.getMember(ctx, obj, key))); propDesc.set("writable", obj.memberWritable(key)); propDesc.set("enumerable", obj.memberEnumerable(key)); propDesc.set("configurable", obj.memberConfigurable(key)); @@ -888,7 +890,7 @@ public class SimpleDebugger implements Debugger { if (own) { var protoDesc = new JSONMap(); protoDesc.set("name", "__proto__"); - protoDesc.set("value", serializeObj(env, proto == null ? Values.NULL : proto)); + protoDesc.set("value", serializeObj(ext, proto == null ? Values.NULL : proto)); protoDesc.set("writable", true); protoDesc.set("enumerable", false); protoDesc.set("configurable", false); @@ -915,8 +917,8 @@ public class SimpleDebugger implements Debugger { var thisArgRef = idToObject.get(Integer.parseInt(msg.params.string("objectId"))); var thisArg = thisArgRef.obj; - var env = thisArgRef.env; - var ctx = env.context(); + var ext = thisArgRef.ext; + var ctx = Context.of(ext); while (true) { var start = src.lastIndexOf("//# sourceURL="); @@ -939,20 +941,20 @@ public class SimpleDebugger implements Debugger { } 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(env, func.call(ctx, thisArg))))); + ws.send(msg.respond(new JSONMap().set("result", serializeObj(ext, func.call(ctx, thisArg))))); } else if (compare(src, VSCODE_AUTOCOMPLETE)) { var target = args.get(0); if (target == null) target = thisArg; - res = vscodeAutoSuggest(env, target, Values.toString(ctx, args.get(1)), Values.toBoolean(args.get(2))); + res = vscodeAutoSuggest(ext, target, Values.toString(ctx, 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(env, res, byValue)))); + ws.send(msg.respond(new JSONMap().set("result", serializeObj(ext, res, byValue)))); } - catch (EngineException e) { ws.send(msg.respond(new JSONMap().set("exceptionDetails", serializeException(env, e)))); } + catch (EngineException e) { ws.send(msg.respond(new JSONMap().set("exceptionDetails", serializeException(ext, e)))); } } @Override public synchronized void runtimeEnable(V8Message msg) throws IOException { diff --git a/src/java/me/topchetoeu/jscript/utils/interop/Arguments.java b/src/java/me/topchetoeu/jscript/utils/interop/Arguments.java index 78d729f..4e8c2ff 100644 --- a/src/java/me/topchetoeu/jscript/utils/interop/Arguments.java +++ b/src/java/me/topchetoeu/jscript/utils/interop/Arguments.java @@ -3,14 +3,32 @@ 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.values.NativeWrapper; import me.topchetoeu.jscript.runtime.values.Values; -public class Arguments { +public class Arguments implements Extensions { public final Object self; public final Object[] args; public final Context ctx; + @Override public void add(Key key, T obj) { + ctx.add(key, obj); + } + @Override public T get(Key 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> keys() { + return ctx.keys(); + } + public int n() { return args.length; } diff --git a/src/java/me/topchetoeu/jscript/utils/interop/NativeWrapperProvider.java b/src/java/me/topchetoeu/jscript/utils/interop/NativeWrapperProvider.java index 2b9c6e3..1099053 100644 --- a/src/java/me/topchetoeu/jscript/utils/interop/NativeWrapperProvider.java +++ b/src/java/me/topchetoeu/jscript/utils/interop/NativeWrapperProvider.java @@ -11,8 +11,9 @@ import java.util.stream.Collectors; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.runtime.Context; -import me.topchetoeu.jscript.runtime.Environment; -import me.topchetoeu.jscript.runtime.WrapperProvider; +import me.topchetoeu.jscript.runtime.Copyable; +import me.topchetoeu.jscript.runtime.Extensions; +import me.topchetoeu.jscript.runtime.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.InterruptException; import me.topchetoeu.jscript.runtime.values.FunctionValue; @@ -21,7 +22,9 @@ 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 WrapperProvider { +public class NativeWrapperProvider implements Copyable { + public static final Key KEY = new Key<>(); + private final HashMap, FunctionValue> constructors = new HashMap<>(); private final HashMap, ObjectValue> prototypes = new HashMap<>(); private final HashMap, ObjectValue> namespaces = new HashMap<>(); @@ -298,8 +301,8 @@ public class NativeWrapperProvider implements WrapperProvider { var parentConstr = getConstr(parent); if (parentProto != null && parentConstr != null) { - Values.setPrototype(Context.NULL, proto, parentProto); - Values.setPrototype(Context.NULL, constr, parentConstr); + Values.setPrototype(Extensions.EMPTY, proto, parentProto); + Values.setPrototype(Extensions.EMPTY, constr, parentConstr); return; } @@ -371,11 +374,13 @@ public class NativeWrapperProvider implements WrapperProvider { return null; } - @Override public WrapperProvider fork(Environment env) { + public NativeWrapperProvider copy() { var res = new NativeWrapperProvider(); + for (var pair : classToProxy.entrySet()) { res.set(pair.getKey(), pair.getValue()); } + return this; } @@ -409,4 +414,8 @@ public class NativeWrapperProvider implements WrapperProvider { } public NativeWrapperProvider() { } + + public static NativeWrapperProvider get(Extensions ext) { + return ext.get(KEY); + } } diff --git a/src/java/me/topchetoeu/jscript/utils/modules/ModuleRepo.java b/src/java/me/topchetoeu/jscript/utils/modules/ModuleRepo.java index b02af45..f7da8a0 100644 --- a/src/java/me/topchetoeu/jscript/utils/modules/ModuleRepo.java +++ b/src/java/me/topchetoeu/jscript/utils/modules/ModuleRepo.java @@ -25,7 +25,7 @@ public interface ModuleRepo { if (modules.containsKey(name)) return modules.get(name); - var env = ctx.environment.child(); + var env = ctx.extensions.child(); env.add(CWD, fs.normalize(name, "..")); var mod = new SourceModule(filename, src, env); diff --git a/src/java/me/topchetoeu/jscript/utils/modules/SourceModule.java b/src/java/me/topchetoeu/jscript/utils/modules/SourceModule.java index 993952d..4efd72a 100644 --- a/src/java/me/topchetoeu/jscript/utils/modules/SourceModule.java +++ b/src/java/me/topchetoeu/jscript/utils/modules/SourceModule.java @@ -2,22 +2,22 @@ package me.topchetoeu.jscript.utils.modules; import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.runtime.Context; -import me.topchetoeu.jscript.runtime.Environment; +import me.topchetoeu.jscript.runtime.Extensions; public class SourceModule extends Module { public final Filename filename; public final String source; - public final Environment env; + public final Extensions ext; @Override protected Object onLoad(Context ctx) { - var res = new Context(env).compile(filename, source); + var res = new Context(ext).compile(filename, source); return res.call(ctx); } - public SourceModule(Filename filename, String source, Environment env) { + public SourceModule(Filename filename, String source, Extensions ext) { this.filename = filename; this.source = source; - this.env = env; + this.ext = ext; } }