From 38acc20a6f4f00f6619de3daf862e722ea2df06d Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Tue, 26 Dec 2023 17:12:20 +0200 Subject: [PATCH] refactor: greatly improve Environment API --- src/me/topchetoeu/jscript/Main.java | 4 +- src/me/topchetoeu/jscript/engine/Context.java | 17 +- src/me/topchetoeu/jscript/engine/Engine.java | 57 +------ .../jscript/engine/Environment.java | 152 +++++++++--------- .../jscript/engine/ExtensionStack.java | 39 +++++ .../topchetoeu/jscript/engine/Extensions.java | 24 +++ .../jscript/engine/debug/DebugContext.java | 96 +++++++++++ .../jscript/engine/debug/DebugController.java | 11 ++ .../jscript/engine/debug/SimpleDebugger.java | 11 +- .../jscript/engine/frame/CodeFrame.java | 8 +- .../jscript/engine/frame/Runners.java | 9 +- .../jscript/engine/values/ObjectValue.java | 29 ++-- .../jscript/engine/values/Symbol.java | 13 ++ .../jscript/engine/values/Values.java | 15 +- .../jscript/exceptions/EngineException.java | 5 +- .../interop/NativeWrapperProvider.java | 13 +- src/me/topchetoeu/jscript/lib/ErrorLib.java | 3 +- src/me/topchetoeu/jscript/lib/Internals.java | 56 +++---- src/me/topchetoeu/jscript/lib/ObjectLib.java | 2 +- src/me/topchetoeu/jscript/lib/StringLib.java | 28 ++-- src/me/topchetoeu/jscript/lib/SymbolLib.java | 19 ++- 21 files changed, 371 insertions(+), 240 deletions(-) create mode 100644 src/me/topchetoeu/jscript/engine/ExtensionStack.java create mode 100644 src/me/topchetoeu/jscript/engine/Extensions.java create mode 100644 src/me/topchetoeu/jscript/engine/debug/DebugContext.java diff --git a/src/me/topchetoeu/jscript/Main.java b/src/me/topchetoeu/jscript/Main.java index ca32edd..399f5aa 100644 --- a/src/me/topchetoeu/jscript/Main.java +++ b/src/me/topchetoeu/jscript/Main.java @@ -131,10 +131,10 @@ public class Main { private static void initTypescript() { try { var tsEnv = Internals.apply(new Environment(null, null, null)); - tsEnv.stackVisible = false; + tsEnv.stackHidden = false; tsEnv.global.define(null, "module", false, new ObjectValue()); var bsEnv = Internals.apply(new Environment(null, null, null)); - bsEnv.stackVisible = false; + bsEnv.stackHidden = false; engine.pushMsg( false, tsEnv, diff --git a/src/me/topchetoeu/jscript/engine/Context.java b/src/me/topchetoeu/jscript/engine/Context.java index 5761c82..bd991a8 100644 --- a/src/me/topchetoeu/jscript/engine/Context.java +++ b/src/me/topchetoeu/jscript/engine/Context.java @@ -10,6 +10,7 @@ import java.util.stream.Collectors; import me.topchetoeu.jscript.Filename; import me.topchetoeu.jscript.Location; +import me.topchetoeu.jscript.engine.debug.DebugContext; import me.topchetoeu.jscript.engine.frame.CodeFrame; import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.FunctionValue; @@ -17,11 +18,16 @@ import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.mapping.SourceMap; -public class Context { +public class Context extends ExtensionStack { private final Stack env = new Stack<>(); private final ArrayList frames = new ArrayList<>(); public final Engine engine; + @Override + protected Extensions[] extensionStack() { + return new Extensions[] { environment(), engine.globalEnvironment }; + } + public Environment environment() { return env.empty() ? null : env.peek(); } @@ -36,7 +42,7 @@ public class Context { public FunctionValue compile(Filename filename, String raw) { var env = environment(); - var result = env.compile.call(this, null, raw, filename.toString(), env); + var result = Environment.compileFunc(this).call(this, null, raw, filename.toString(), env); var function = (FunctionValue)Values.getMember(this, result, "function"); if (!engine.debugging) return function; @@ -62,24 +68,23 @@ public class Context { breakpoints = newBreakpoints; } - engine.onSource(filename, raw, breakpoints, map); + DebugContext.get(this).onSource(filename, raw, breakpoints, map); return function; } - public void pushFrame(CodeFrame frame) { frames.add(frame); if (frames.size() > engine.maxStackFrames) throw EngineException.ofRange("Stack overflow!"); pushEnv(frame.function.environment); - engine.onFramePush(this, frame); + DebugContext.get(this).onFramePush(this, frame); } public boolean popFrame(CodeFrame frame) { if (frames.size() == 0) return false; if (frames.get(frames.size() - 1) != frame) return false; frames.remove(frames.size() - 1); popEnv(); - engine.onFramePop(this, frame); + DebugContext.get(this).onFramePop(this, frame); return true; } public CodeFrame peekFrame() { diff --git a/src/me/topchetoeu/jscript/engine/Engine.java b/src/me/topchetoeu/jscript/engine/Engine.java index 50ff0d3..9066e83 100644 --- a/src/me/topchetoeu/jscript/engine/Engine.java +++ b/src/me/topchetoeu/jscript/engine/Engine.java @@ -17,7 +17,7 @@ import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.exceptions.InterruptException; import me.topchetoeu.jscript.mapping.SourceMap; -public class Engine implements DebugController { +public class Engine { private class UncompiledFunction extends FunctionValue { public final Filename filename; public final String raw; @@ -61,47 +61,15 @@ public class Engine implements DebugController { private static int nextId = 0; public static final HashMap functions = new HashMap<>(); + public final Environment globalEnvironment = new Environment(); + public final int id = ++nextId; public final boolean debugging; public int maxStackFrames = 10000; - private final HashMap sources = new HashMap<>(); - private final HashMap> bpts = new HashMap<>(); - private final HashMap maps = new HashMap<>(); - - public Location mapToCompiled(Location location) { - var map = maps.get(location.filename()); - if (map == null) return location; - return map.toCompiled(location); - } - public Location mapToOriginal(Location location) { - var map = maps.get(location.filename()); - if (map == null) return location; - return map.toOriginal(location); - } - - private DebugController debugger; private Thread thread; private PriorityBlockingQueue tasks = new PriorityBlockingQueue<>(); - public synchronized boolean attachDebugger(DebugController debugger) { - if (!debugging || this.debugger != null) return false; - - for (var source : sources.entrySet()) debugger.onSource( - source.getKey(), source.getValue(), - bpts.get(source.getKey()), - maps.get(source.getKey()) - ); - - this.debugger = debugger; - return true; - } - public synchronized boolean detachDebugger() { - if (!debugging || this.debugger == null) return false; - this.debugger = null; - return true; - } - private void runTask(Task task) { try { task.notifier.next(task.func.call(task.ctx, task.thisArg, task.args)); @@ -150,25 +118,6 @@ public class Engine implements DebugController { return pushMsg(micro, env, new UncompiledFunction(filename, raw), thisArg, args); } - @Override - public void onFramePush(Context ctx, CodeFrame frame) { - if (debugging && debugger != null) debugger.onFramePush(ctx, frame); - } - @Override public void onFramePop(Context ctx, CodeFrame frame) { - if (debugging && debugger != null) debugger.onFramePop(ctx, frame); - } - @Override public boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) { - if (debugging && debugger != null) return debugger.onInstruction(ctx, frame, instruction, returnVal, error, caught); - else return false; - } - @Override public void onSource(Filename filename, String source, TreeSet breakpoints, SourceMap map) { - if (!debugging) return; - if (debugger != null) debugger.onSource(filename, source, breakpoints, map); - sources.put(filename, source); - bpts.put(filename, breakpoints); - maps.put(filename, map); - } - public Engine(boolean debugging) { this.debugging = debugging; } diff --git a/src/me/topchetoeu/jscript/engine/Environment.java b/src/me/topchetoeu/jscript/engine/Environment.java index 8f6e1bc..5a58248 100644 --- a/src/me/topchetoeu/jscript/engine/Environment.java +++ b/src/me/topchetoeu/jscript/engine/Environment.java @@ -13,117 +13,109 @@ import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.Symbol; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.exceptions.EngineException; -import me.topchetoeu.jscript.filesystem.RootFilesystem; import me.topchetoeu.jscript.interop.Native; -import me.topchetoeu.jscript.interop.NativeGetter; -import me.topchetoeu.jscript.interop.NativeSetter; import me.topchetoeu.jscript.interop.NativeWrapperProvider; -import me.topchetoeu.jscript.modules.RootModuleRepo; import me.topchetoeu.jscript.parsing.Parsing; -import me.topchetoeu.jscript.permissions.Permission; -import me.topchetoeu.jscript.permissions.PermissionsProvider; // TODO: Remove hardcoded extensions form environment -public class Environment implements PermissionsProvider { - private HashMap prototypes = new HashMap<>(); +@SuppressWarnings("unchecked") +public class Environment implements Extensions { + private static int nextId = 0; - public final Data data = new Data(); public static final HashMap symbols = new HashMap<>(); + public static final Symbol WRAPPERS = Symbol.get("Environment.wrappers"); + public static final Symbol COMPILE_FUNC = Symbol.get("Environment.compile"); + + public static final Symbol REGEX_CONSTR = Symbol.get("Environment.regexConstructor"); + public static final Symbol STACK = Symbol.get("Environment.stack"); + public static final Symbol HIDE_STACK = Symbol.get("Environment.hideStack"); + + public static final Symbol OBJECT_PROTO = Symbol.get("Environment.objectPrototype"); + public static final Symbol FUNCTION_PROTO = Symbol.get("Environment.functionPrototype"); + public static final Symbol ARRAY_PROTO = Symbol.get("Environment.arrayPrototype"); + public static final Symbol BOOL_PROTO = Symbol.get("Environment.boolPrototype"); + public static final Symbol NUMBER_PROTO = Symbol.get("Environment.numberPrototype"); + public static final Symbol STRING_PROTO = Symbol.get("Environment.stringPrototype"); + public static final Symbol SYMBOL_PROTO = Symbol.get("Environment.symbolPrototype"); + public static final Symbol ERROR_PROTO = Symbol.get("Environment.errorPrototype"); + public static final Symbol SYNTAX_ERR_PROTO = Symbol.get("Environment.syntaxErrorPrototype"); + public static final Symbol TYPE_ERR_PROTO = Symbol.get("Environment.typeErrorPrototype"); + public static final Symbol RANGE_ERR_PROTO = Symbol.get("Environment.rangeErrorPrototype"); + + private HashMap data = new HashMap<>(); + public GlobalScope global; public WrappersProvider wrappers; - public PermissionsProvider permissions = null; - public final RootFilesystem filesystem = new RootFilesystem(this); - public final RootModuleRepo modules = new RootModuleRepo(); - public String moduleCwd = "/"; - - private static int nextId = 0; - - @Native public boolean stackVisible = true; @Native public int id = ++nextId; - @Native public FunctionValue compile = new NativeFunction("compile", (ctx, thisArg, args) -> { - var source = Values.toString(ctx, args[0]); - var filename = Values.toString(ctx, args[1]); - var isDebug = Values.toBoolean(args[2]); - - var env = Values.wrapper(args[2], Environment.class); - var res = new ObjectValue(); - - var target = Parsing.compile(env, Filename.parse(filename), source); - Engine.functions.putAll(target.functions); - Engine.functions.remove(0l); - - res.defineProperty(ctx, "function", target.func(env)); - res.defineProperty(ctx, "mapChain", new ArrayValue()); - - if (isDebug) { - res.defineProperty(ctx, "breakpoints", ArrayValue.of(ctx, target.breakpoints.stream().map(Location::toString).collect(Collectors.toList()))); + @Override public void add(Symbol key, T obj) { + data.put(key, obj); + } + @Override public T get(Symbol key) { + return (T)data.get(key); + } + @Override public boolean remove(Symbol key) { + if (data.containsKey(key)) { + data.remove(key); + return true; } - - return res; - }); - @Native public FunctionValue regexConstructor = new NativeFunction("RegExp", (ctx, thisArg, args) -> { - throw EngineException.ofError("Regular expressions not supported.").setCtx(ctx.environment(), ctx.engine); - }); - - @Native public ObjectValue proto(String name) { - return prototypes.get(name); + return false; } - @Native public void setProto(String name, ObjectValue val) { - prototypes.put(name, val); + @Override public boolean has(Symbol key) { + return data.containsKey(key); } - @Native public Symbol symbol(String name) { - return getSymbol(name); + public static FunctionValue compileFunc(Extensions ext) { + return ext.init(COMPILE_FUNC, new NativeFunction("compile", (ctx, thisArg, args) -> { + var source = Values.toString(ctx, args[0]); + var filename = Values.toString(ctx, args[1]); + var isDebug = Values.toBoolean(args[2]); + + var env = Values.wrapper(args[2], Environment.class); + var res = new ObjectValue(); + + var target = Parsing.compile(env, Filename.parse(filename), source); + Engine.functions.putAll(target.functions); + Engine.functions.remove(0l); + + res.defineProperty(ctx, "function", target.func(env)); + res.defineProperty(ctx, "mapChain", new ArrayValue()); + + if (isDebug) { + res.defineProperty(ctx, "breakpoints", ArrayValue.of(ctx, target.breakpoints.stream().map(Location::toString).collect(Collectors.toList()))); + } + + return res; + })); + } + public static FunctionValue regexConstructor(Extensions ext) { + return ext.init(COMPILE_FUNC, new NativeFunction("RegExp", (ctx, thisArg, args) -> { + throw EngineException.ofError("Regular expressions not supported.").setCtx(ctx.environment(), ctx.engine); + })); } - @NativeGetter("global") public ObjectValue getGlobal() { - return global.obj; - } - @NativeSetter("global") public void setGlobal(ObjectValue val) { - global = new GlobalScope(val); - } + public Environment fork() { + var res = new Environment(null, global); - @Native public Environment fork() { - var res = new Environment(compile, null, global); res.wrappers = wrappers.fork(res); - res.regexConstructor = regexConstructor; - res.prototypes = new HashMap<>(prototypes); + res.global = global; + res.data.putAll(data); + return res; } - @Native public Environment child() { + public Environment child() { var res = fork(); res.global = res.global.globalChild(); - res.permissions = this.permissions; - res.filesystem.protocols.putAll(this.filesystem.protocols); - res.modules.repos.putAll(this.modules.repos); return res; } - @Override public boolean hasPermission(Permission perm, char delim) { - return permissions == null || permissions.hasPermission(perm, delim); - } - @Override public boolean hasPermission(Permission perm) { - return permissions == null || permissions.hasPermission(perm); - } - public Context context(Engine engine) { return new Context(engine, this); } - public static Symbol getSymbol(String name) { - if (symbols.containsKey(name)) return symbols.get(name); - else { - var res = new Symbol(name); - symbols.put(name, res); - return res; - } - } - - public Environment(FunctionValue compile, WrappersProvider nativeConverter, GlobalScope global) { - if (compile != null) this.compile = compile; + public Environment(WrappersProvider nativeConverter, GlobalScope global) { if (nativeConverter == null) nativeConverter = new NativeWrapperProvider(this); if (global == null) global = new GlobalScope(); @@ -131,6 +123,6 @@ public class Environment implements PermissionsProvider { this.global = global; } public Environment() { - this(null, null, null); + this(null, null); } } diff --git a/src/me/topchetoeu/jscript/engine/ExtensionStack.java b/src/me/topchetoeu/jscript/engine/ExtensionStack.java new file mode 100644 index 0000000..336e903 --- /dev/null +++ b/src/me/topchetoeu/jscript/engine/ExtensionStack.java @@ -0,0 +1,39 @@ +package me.topchetoeu.jscript.engine; + +import me.topchetoeu.jscript.engine.values.Symbol; + +public abstract class ExtensionStack implements Extensions { + protected abstract Extensions[] extensionStack(); + + @Override public void add(Symbol key, T obj) { + for (var el : extensionStack()) { + if (el != null) { + el.add(key, obj); + return; + } + } + } + @Override public T get(Symbol key) { + for (var el : extensionStack()) { + if (el != null && el.has(key)) return el.get(key); + } + + return null; + } + @Override public boolean has(Symbol key) { + for (var el : extensionStack()) { + if (el != null && el.has(key)) return true; + } + + return false; + } + @Override public boolean remove(Symbol key) { + var anyRemoved = false; + + for (var el : extensionStack()) { + if (el != null) anyRemoved &= el.remove(key); + } + + return anyRemoved; + } +} diff --git a/src/me/topchetoeu/jscript/engine/Extensions.java b/src/me/topchetoeu/jscript/engine/Extensions.java new file mode 100644 index 0000000..83072c9 --- /dev/null +++ b/src/me/topchetoeu/jscript/engine/Extensions.java @@ -0,0 +1,24 @@ +package me.topchetoeu.jscript.engine; + +import me.topchetoeu.jscript.engine.values.Symbol; + +public interface Extensions { + T get(Symbol key); + void add(Symbol key, T obj); + + boolean has(Symbol key); + boolean remove(Symbol key); + + default T get(Symbol key, T defaultVal) { + if (has(key)) return get(key); + else return defaultVal; + } + + default T init(Symbol key, T val) { + if (has(key)) return get(key); + else { + add(key, val); + return val; + } + } +} diff --git a/src/me/topchetoeu/jscript/engine/debug/DebugContext.java b/src/me/topchetoeu/jscript/engine/debug/DebugContext.java new file mode 100644 index 0000000..6baae19 --- /dev/null +++ b/src/me/topchetoeu/jscript/engine/debug/DebugContext.java @@ -0,0 +1,96 @@ +package me.topchetoeu.jscript.engine.debug; + +import java.util.HashMap; +import java.util.TreeSet; + +import me.topchetoeu.jscript.Filename; +import me.topchetoeu.jscript.Location; +import me.topchetoeu.jscript.compilation.Instruction; +import me.topchetoeu.jscript.engine.Context; +import me.topchetoeu.jscript.engine.Extensions; +import me.topchetoeu.jscript.engine.frame.CodeFrame; +import me.topchetoeu.jscript.engine.values.Symbol; +import me.topchetoeu.jscript.exceptions.EngineException; +import me.topchetoeu.jscript.mapping.SourceMap; + +public class DebugContext implements DebugController { + public static final Symbol DEBUG_CTX = Symbol.get("Engine.debug"); + + private HashMap sources; + private HashMap> bpts; + private HashMap maps; + private DebugController debugger; + + public boolean attachDebugger(DebugController debugger) { + if (this.debugger != null) return false; + + if (sources != null) { + for (var source : sources.entrySet()) debugger.onSource( + source.getKey(), source.getValue(), + bpts.get(source.getKey()), + maps.get(source.getKey()) + ); + } + + this.debugger = debugger; + return true; + } + public boolean detachDebugger() { + this.debugger = null; + return true; + } + + public DebugController debugger() { + if (debugger == null) return DebugController.empty(); + else return debugger; + } + + @Override public void onFramePop(Context ctx, CodeFrame frame) { + if (debugger != null) debugger.onFramePop(ctx, frame); + } + @Override public void onFramePush(Context ctx, CodeFrame frame) { + if (debugger != null) debugger.onFramePush(ctx, frame); + } + @Override public boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) { + if (debugger != null) return debugger.onInstruction(ctx, frame, instruction, returnVal, error, caught); + else return false; + } + @Override public void onSource(Filename filename, String source, TreeSet breakpoints, SourceMap map) { + if (debugger != null) debugger.onSource(filename, source, breakpoints, map); + if (sources != null) sources.put(filename, source); + if (bpts != null) bpts.put(filename, breakpoints); + if (maps != null) maps.put(filename, map); + } + + public Location mapToCompiled(Location location) { + if (maps == null) return location; + + var map = maps.get(location.filename()); + if (map == null) return location; + return map.toCompiled(location); + } + public Location mapToOriginal(Location location) { + if (maps == null) return location; + + var map = maps.get(location.filename()); + if (map == null) return location; + return map.toOriginal(location); + } + + private DebugContext(boolean enabled) { + if (enabled) { + sources = new HashMap<>(); + bpts = new HashMap<>(); + maps = new HashMap<>(); + } + } + + public DebugContext() { + this(true); + } + + public static DebugContext get(Extensions exts) { + if (exts.has(DEBUG_CTX)) return exts.get(DEBUG_CTX); + else return new DebugContext(false); + } +} diff --git a/src/me/topchetoeu/jscript/engine/debug/DebugController.java b/src/me/topchetoeu/jscript/engine/debug/DebugController.java index 36fa7b1..d03d4f6 100644 --- a/src/me/topchetoeu/jscript/engine/debug/DebugController.java +++ b/src/me/topchetoeu/jscript/engine/debug/DebugController.java @@ -48,4 +48,15 @@ public interface DebugController { * @param frame The code frame which was popped out */ void onFramePop(Context ctx, CodeFrame frame); + + public static DebugController empty() { + return new DebugController() { + @Override public void onFramePop(Context ctx, CodeFrame frame) { } + @Override public void onFramePush(Context ctx, CodeFrame frame) { } + @Override public boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) { + return false; + } + @Override public void onSource(Filename filename, String source, TreeSet breakpoints, SourceMap map) { } + }; + } } diff --git a/src/me/topchetoeu/jscript/engine/debug/SimpleDebugger.java b/src/me/topchetoeu/jscript/engine/debug/SimpleDebugger.java index a21af73..1ac5307 100644 --- a/src/me/topchetoeu/jscript/engine/debug/SimpleDebugger.java +++ b/src/me/topchetoeu/jscript/engine/debug/SimpleDebugger.java @@ -15,6 +15,7 @@ import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction.Type; import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.Engine; +import me.topchetoeu.jscript.engine.Environment; import me.topchetoeu.jscript.engine.frame.CodeFrame; import me.topchetoeu.jscript.engine.frame.Runners; import me.topchetoeu.jscript.engine.scope.GlobalScope; @@ -341,7 +342,7 @@ public class SimpleDebugger implements Debugger { try { defaultToString = Values.getMember(ctx, obj, "toString") == - Values.getMember(ctx, ctx.environment().proto("object"), "toString"); + Values.getMember(ctx, ctx.environment().get(Environment.OBJECT_PROTO), "toString"); } catch (Exception e) { } @@ -495,7 +496,7 @@ public class SimpleDebugger implements Debugger { var res = new ArrayValue(); var passed = new HashSet(); var tildas = "~"; - if (target == null) target = ctx.environment().getGlobal(); + if (target == null) target = ctx.environment().global; for (var proto = target; proto != null && proto != Values.NULL; proto = Values.getPrototype(ctx, proto)) { for (var el : Values.getMembers(ctx, proto, true, true)) { @@ -870,7 +871,7 @@ public class SimpleDebugger implements Debugger { if (!frame.debugData) return false; - if (instruction.location != null) frame.updateLoc(ctx.engine.mapToCompiled(instruction.location)); + if (instruction.location != null) frame.updateLoc(DebugContext.get(ctx).mapToCompiled(instruction.location)); loc = frame.location; isBreakpointable = loc != null && (instruction.breakpoint.shouldStepIn()); @@ -953,12 +954,12 @@ public class SimpleDebugger implements Debugger { } @Override public synchronized void connect() { - if (!target.attachDebugger(this)) { + if (!DebugContext.get(target.globalEnvironment).attachDebugger(this)) { ws.send(new V8Error("A debugger is already attached to this engine.")); } } @Override public synchronized void disconnect() { - target.detachDebugger(); + DebugContext.get(target.globalEnvironment).detachDebugger(); enabled = false; updateNotifier.next(); } diff --git a/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java b/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java index e4411ce..bc2d032 100644 --- a/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java +++ b/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java @@ -6,6 +6,7 @@ import java.util.Stack; import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.engine.Context; +import me.topchetoeu.jscript.engine.debug.DebugContext; import me.topchetoeu.jscript.engine.scope.LocalScope; import me.topchetoeu.jscript.engine.scope.ValueVariable; import me.topchetoeu.jscript.engine.values.ArrayValue; @@ -198,8 +199,7 @@ public class CodeFrame { if (instr == null) returnValue = null; else { - // System.out.println(instr + "@" + instr.location); - ctx.engine.onInstruction(ctx, this, instr, Runners.NO_RETURN, null, false); + DebugContext.get(ctx).onInstruction(ctx, this, instr, Runners.NO_RETURN, null, false); if (instr.location != null) prevLoc = instr.location; @@ -291,11 +291,11 @@ public class CodeFrame { } } - ctx.engine.onInstruction(ctx, this, instr, null, error, caught); + DebugContext.get(ctx).onInstruction(ctx, this, instr, null, error, caught); throw error; } if (returnValue != Runners.NO_RETURN) { - ctx.engine.onInstruction(ctx, this, instr, returnValue, null, false); + DebugContext.get(ctx).onInstruction(ctx, this, instr, returnValue, null, false); return returnValue; } diff --git a/src/me/topchetoeu/jscript/engine/frame/Runners.java b/src/me/topchetoeu/jscript/engine/frame/Runners.java index d889a20..1d795b6 100644 --- a/src/me/topchetoeu/jscript/engine/frame/Runners.java +++ b/src/me/topchetoeu/jscript/engine/frame/Runners.java @@ -5,6 +5,7 @@ import java.util.Collections; import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.Engine; +import me.topchetoeu.jscript.engine.Environment; import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.scope.ValueVariable; import me.topchetoeu.jscript.engine.values.ArrayValue; @@ -197,7 +198,13 @@ public class Runners { return execLoadMember(ctx, instr, frame); } public static Object execLoadRegEx(Context ctx, Instruction instr, CodeFrame frame) { - frame.push(ctx, ctx.environment().regexConstructor.call(ctx, null, instr.get(0), instr.get(1))); + var env = ctx.environment(); + if (env.has(Environment.REGEX_CONSTR)) { + frame.push(ctx, Values.callNew(ctx, env.get(Environment.REGEX_CONSTR))); + } + else { + throw EngineException.ofSyntax("Regex is not supported."); + } frame.codePtr++; return NO_RETURN; } diff --git a/src/me/topchetoeu/jscript/engine/values/ObjectValue.java b/src/me/topchetoeu/jscript/engine/values/ObjectValue.java index 7cb4bae..ea2fe38 100644 --- a/src/me/topchetoeu/jscript/engine/values/ObjectValue.java +++ b/src/me/topchetoeu/jscript/engine/values/ObjectValue.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Map; import me.topchetoeu.jscript.engine.Context; +import me.topchetoeu.jscript.engine.Environment; public class ObjectValue { public static enum PlaceholderProto { @@ -146,13 +147,13 @@ public class ObjectValue { public ObjectValue getPrototype(Context ctx) { try { - if (prototype == OBJ_PROTO) return ctx.environment().proto("object"); - if (prototype == ARR_PROTO) return ctx.environment().proto("array"); - if (prototype == FUNC_PROTO) return ctx.environment().proto("function"); - if (prototype == ERR_PROTO) return ctx.environment().proto("error"); - if (prototype == RANGE_ERR_PROTO) return ctx.environment().proto("rangeErr"); - if (prototype == SYNTAX_ERR_PROTO) return ctx.environment().proto("syntaxErr"); - if (prototype == TYPE_ERR_PROTO) return ctx.environment().proto("typeErr"); + if (prototype == OBJ_PROTO) return ctx.environment().get(Environment.OBJECT_PROTO); + if (prototype == ARR_PROTO) return ctx.environment().get(Environment.ARRAY_PROTO); + if (prototype == FUNC_PROTO) return ctx.environment().get(Environment.FUNCTION_PROTO); + if (prototype == ERR_PROTO) return ctx.environment().get(Environment.ERROR_PROTO); + if (prototype == RANGE_ERR_PROTO) return ctx.environment().get(Environment.RANGE_ERR_PROTO); + if (prototype == SYNTAX_ERR_PROTO) return ctx.environment().get(Environment.SYNTAX_ERR_PROTO); + if (prototype == TYPE_ERR_PROTO) return ctx.environment().get(Environment.TYPE_ERR_PROTO); } catch (NullPointerException e) { return null; } @@ -170,13 +171,13 @@ public class ObjectValue { var obj = Values.object(val); if (ctx != null && ctx.environment() != null) { - if (obj == ctx.environment().proto("object")) prototype = OBJ_PROTO; - else if (obj == ctx.environment().proto("array")) prototype = ARR_PROTO; - else if (obj == ctx.environment().proto("function")) prototype = FUNC_PROTO; - else if (obj == ctx.environment().proto("error")) prototype = ERR_PROTO; - else if (obj == ctx.environment().proto("syntaxErr")) prototype = SYNTAX_ERR_PROTO; - else if (obj == ctx.environment().proto("typeErr")) prototype = TYPE_ERR_PROTO; - else if (obj == ctx.environment().proto("rangeErr")) prototype = RANGE_ERR_PROTO; + if (obj == ctx.environment().get(Environment.OBJECT_PROTO)) prototype = OBJ_PROTO; + else if (obj == ctx.environment().get(Environment.ARRAY_PROTO)) prototype = ARR_PROTO; + else if (obj == ctx.environment().get(Environment.FUNCTION_PROTO)) prototype = FUNC_PROTO; + else if (obj == ctx.environment().get(Environment.ERROR_PROTO)) prototype = ERR_PROTO; + else if (obj == ctx.environment().get(Environment.SYNTAX_ERR_PROTO)) prototype = SYNTAX_ERR_PROTO; + else if (obj == ctx.environment().get(Environment.TYPE_ERR_PROTO)) prototype = TYPE_ERR_PROTO; + else if (obj == ctx.environment().get(Environment.RANGE_ERR_PROTO)) prototype = RANGE_ERR_PROTO; else prototype = obj; } else prototype = obj; diff --git a/src/me/topchetoeu/jscript/engine/values/Symbol.java b/src/me/topchetoeu/jscript/engine/values/Symbol.java index b24d8f1..65aeac9 100644 --- a/src/me/topchetoeu/jscript/engine/values/Symbol.java +++ b/src/me/topchetoeu/jscript/engine/values/Symbol.java @@ -1,6 +1,10 @@ package me.topchetoeu.jscript.engine.values; +import java.util.HashMap; + public final class Symbol { + private static final HashMap registry = new HashMap<>(); + public final String value; public Symbol(String value) { @@ -12,4 +16,13 @@ public final class Symbol { if (value == null) return "Symbol"; else return "@@" + value; } + + public static Symbol get(String name) { + if (registry.containsKey(name)) return registry.get(name); + else { + var res = new Symbol(name); + registry.put(name, res); + return res; + } + } } diff --git a/src/me/topchetoeu/jscript/engine/values/Values.java b/src/me/topchetoeu/jscript/engine/values/Values.java index d9031ee..d8b07e3 100644 --- a/src/me/topchetoeu/jscript/engine/values/Values.java +++ b/src/me/topchetoeu/jscript/engine/values/Values.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Map; import me.topchetoeu.jscript.engine.Context; +import me.topchetoeu.jscript.engine.Environment; import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.frame.ConvertHint; import me.topchetoeu.jscript.exceptions.ConvertException; @@ -346,10 +347,10 @@ public class Values { if (isObject(obj)) return object(obj).getPrototype(ctx); if (ctx == null) return null; - if (obj instanceof String) return ctx.environment().proto("string"); - else if (obj instanceof Number) return ctx.environment().proto("number"); - else if (obj instanceof Boolean) return ctx.environment().proto("bool"); - else if (obj instanceof Symbol) return ctx.environment().proto("symbol"); + if (obj instanceof String) return ctx.environment().get(Environment.STRING_PROTO); + else if (obj instanceof Number) return ctx.environment().get(Environment.NUMBER_PROTO); + else if (obj instanceof Boolean) return ctx.environment().get(Environment.BOOL_PROTO); + else if (obj instanceof Symbol) return ctx.environment().get(Environment.SYMBOL_PROTO); return null; } @@ -550,7 +551,7 @@ public class Values { public static Iterable fromJSIterator(Context ctx, Object obj) { return () -> { try { - var symbol = ctx.environment().symbol("Symbol.iterator"); + var symbol = Symbol.get("Symbol.iterator"); var iteratorFunc = getMember(ctx, obj, symbol); if (!isFunction(iteratorFunc)) return Collections.emptyIterator(); @@ -604,7 +605,7 @@ public class Values { var res = new ObjectValue(); try { - var key = getMember(ctx, getMember(ctx, ctx.environment().proto("symbol"), "constructor"), "iterator"); + var key = getMember(ctx, getMember(ctx, ctx.environment().get(Environment.SYMBOL_PROTO), "constructor"), "iterator"); res.defineProperty(ctx, key, new NativeFunction("", (_ctx, thisArg, args) -> thisArg)); } catch (IllegalArgumentException | NullPointerException e) { } @@ -629,7 +630,7 @@ public class Values { var res = new ObjectValue(); try { - var key = getMemberPath(ctx, ctx.environment().proto("symbol"), "constructor", "asyncIterator"); + var key = getMemberPath(ctx, ctx.environment().get(Environment.SYMBOL_PROTO), "constructor", "asyncIterator"); res.defineProperty(ctx, key, new NativeFunction("", (_ctx, thisArg, args) -> thisArg)); } catch (IllegalArgumentException | NullPointerException e) { } diff --git a/src/me/topchetoeu/jscript/exceptions/EngineException.java b/src/me/topchetoeu/jscript/exceptions/EngineException.java index 2639aab..1b42c95 100644 --- a/src/me/topchetoeu/jscript/exceptions/EngineException.java +++ b/src/me/topchetoeu/jscript/exceptions/EngineException.java @@ -7,6 +7,7 @@ import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.Engine; import me.topchetoeu.jscript.engine.Environment; +import me.topchetoeu.jscript.engine.debug.DebugContext; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto; @@ -18,13 +19,13 @@ public class EngineException extends RuntimeException { public final Context ctx; public boolean visible() { - return ctx == null || ctx.environment() == null || ctx.environment().stackVisible; + return ctx == null || ctx.environment() == null || !ctx.environment().get(Environment.HIDE_STACK, false); } public String toString() { var res = ""; var loc = location; - if (loc != null && ctx != null && ctx.engine != null) loc = ctx.engine.mapToCompiled(loc); + if (loc != null && ctx != null && ctx.engine != null) loc = DebugContext.get(ctx).mapToCompiled(loc); if (loc != null) res += "at " + loc.toString() + " "; if (function != null && !function.equals("")) res += "in " + function + " "; diff --git a/src/me/topchetoeu/jscript/interop/NativeWrapperProvider.java b/src/me/topchetoeu/jscript/interop/NativeWrapperProvider.java index 0d02d9b..fa7be76 100644 --- a/src/me/topchetoeu/jscript/interop/NativeWrapperProvider.java +++ b/src/me/topchetoeu/jscript/interop/NativeWrapperProvider.java @@ -8,6 +8,7 @@ import me.topchetoeu.jscript.engine.WrappersProvider; import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.NativeFunction; import me.topchetoeu.jscript.engine.values.ObjectValue; +import me.topchetoeu.jscript.engine.values.Symbol; import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.exceptions.UncheckedException; @@ -28,7 +29,7 @@ public class NativeWrapperProvider implements WrappersProvider { if (nat.thisArg() && !member || !nat.thisArg() && !memberMatch) continue; Object name = nat.value(); - if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2)); + if (name.toString().startsWith("@@")) name = Symbol.get(name.toString().substring(2)); else if (name.equals("")) name = method.getName(); var val = target.values.get(name); @@ -42,7 +43,7 @@ public class NativeWrapperProvider implements WrappersProvider { if (get.thisArg() && !member || !get.thisArg() && !memberMatch) continue; Object name = get.value(); - if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2)); + if (((String)name).startsWith("@@")) name = Symbol.get(((String)name).substring(2)); else if (name.equals("")) name = method.getName(); var prop = target.properties.get(name); @@ -59,7 +60,7 @@ public class NativeWrapperProvider implements WrappersProvider { if (set.thisArg() && !member || !set.thisArg() && !memberMatch) continue; Object name = set.value(); - if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2)); + if (((String)name).startsWith("@@")) name = Symbol.get(((String)name).substring(2)); else if (name.equals("")) name = method.getName(); var prop = target.properties.get(name); @@ -82,7 +83,7 @@ public class NativeWrapperProvider implements WrappersProvider { if (nat != null) { Object name = nat.value(); - if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2)); + if (((String)name).startsWith("@@")) name = Symbol.get(((String)name).substring(2)); else if (name.equals("")) name = field.getName(); var getter = OverloadFunction.of("get " + name, Overload.getterFromField(field)); @@ -98,7 +99,7 @@ public class NativeWrapperProvider implements WrappersProvider { if (nat != null) { Object name = nat.value(); - if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2)); + if (((String)name).startsWith("@@")) name = Symbol.get(((String)name).substring(2)); else if (name.equals("")) name = cl.getName(); var getter = new NativeFunction("get " + name, (ctx, thisArg, args) -> cl); @@ -123,7 +124,7 @@ public class NativeWrapperProvider implements WrappersProvider { public static ObjectValue makeProto(Environment ctx, Class clazz) { var res = new ObjectValue(); - res.defineProperty(null, ctx.symbol("Symbol.typeName"), getName(clazz)); + res.defineProperty(null, Symbol.get("Symbol.typeName"), getName(clazz)); for (var overload : clazz.getDeclaredMethods()) { var init = overload.getAnnotation(NativeInit.class); diff --git a/src/me/topchetoeu/jscript/lib/ErrorLib.java b/src/me/topchetoeu/jscript/lib/ErrorLib.java index c681ed4..e3cc945 100644 --- a/src/me/topchetoeu/jscript/lib/ErrorLib.java +++ b/src/me/topchetoeu/jscript/lib/ErrorLib.java @@ -4,6 +4,7 @@ import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.Environment; import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.ObjectValue; +import me.topchetoeu.jscript.engine.values.Symbol; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto; import me.topchetoeu.jscript.interop.InitType; @@ -35,7 +36,7 @@ import me.topchetoeu.jscript.interop.NativeInit; if (thisArg instanceof ObjectValue) { var stack = Values.getMember(ctx, thisArg, "stack"); if (!(stack instanceof ArrayValue)) stack = null; - var cause = Values.getMember(ctx, thisArg, ctx.environment().symbol("Symbol.cause")); + var cause = Values.getMember(ctx, thisArg, Symbol.get("Symbol.cause")); return toString(ctx, thisArg == cause, cause, diff --git a/src/me/topchetoeu/jscript/lib/Internals.java b/src/me/topchetoeu/jscript/lib/Internals.java index 2059e62..c1582a2 100644 --- a/src/me/topchetoeu/jscript/lib/Internals.java +++ b/src/me/topchetoeu/jscript/lib/Internals.java @@ -10,6 +10,7 @@ import me.topchetoeu.jscript.engine.DataKey; import me.topchetoeu.jscript.engine.Environment; import me.topchetoeu.jscript.engine.scope.GlobalScope; import me.topchetoeu.jscript.engine.values.FunctionValue; +import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.interop.Native; @@ -17,9 +18,6 @@ import me.topchetoeu.jscript.interop.NativeGetter; import me.topchetoeu.jscript.parsing.Parsing; public class Internals { - private static final DataKey> THREADS = new DataKey<>(); - private static final DataKey I = new DataKey<>(); - @Native public static Object require(Context ctx, String name) { var env = ctx.environment(); var res = env.modules.getModule(ctx, env.moduleCwd, name); @@ -47,7 +45,7 @@ public class Internals { } } - @Native public static int setTimeout(Context ctx, FunctionValue func, int delay, Object ...args) { + @Native public static Thread setTimeout(Context ctx, FunctionValue func, int delay, Object ...args) { var thread = new Thread(() -> { var ms = (long)delay; var ns = (int)((delay - ms) * 10000000); @@ -61,12 +59,9 @@ public class Internals { }); thread.start(); - int i = ctx.environment().data.increase(I, 1, 0); - var threads = ctx.environment().data.get(THREADS, new HashMap<>()); - threads.put(++i, thread); - return i; + return thread; } - @Native public static int setInterval(Context ctx, FunctionValue func, int delay, Object ...args) { + @Native public static Thread setInterval(Context ctx, FunctionValue func, int delay, Object ...args) { var thread = new Thread(() -> { var ms = (long)delay; var ns = (int)((delay - ms) * 10000000); @@ -76,26 +71,19 @@ public class Internals { Thread.sleep(ms, ns); } catch (InterruptedException e) { return; } - + ctx.engine.pushMsg(false, ctx.environment(), func, null, args); } }); - thread.start(); - int i = ctx.environment().data.increase(I, 1, 0); - var threads = ctx.environment().data.get(THREADS, new HashMap<>()); - threads.put(++i, thread); - return i; + return thread; } - @Native public static void clearTimeout(Context ctx, int i) { - var threads = ctx.environment().data.get(THREADS, new HashMap<>()); - - var thread = threads.remove(i); - if (thread != null) thread.interrupt(); + @Native public static void clearTimeout(Context ctx, Thread t) { + t.interrupt(); } - @Native public static void clearInterval(Context ctx, int i) { - clearTimeout(ctx, i); + @Native public static void clearInterval(Context ctx, Thread t) { + t.interrupt(); } @Native public static double parseInt(Context ctx, String val) { @@ -205,22 +193,22 @@ public class Internals { glob.define(false, wp.getConstr(TypeErrorLib.class)); glob.define(false, wp.getConstr(RangeErrorLib.class)); - env.setProto("object", wp.getProto(ObjectLib.class)); - env.setProto("function", wp.getProto(FunctionLib.class)); - env.setProto("array", wp.getProto(ArrayLib.class)); + env.add(Environment.OBJECT_PROTO, wp.getProto(ObjectLib.class)); + env.add(Environment.FUNCTION_PROTO, wp.getProto(FunctionLib.class)); + env.add(Environment.ARRAY_PROTO, wp.getProto(ArrayLib.class)); - env.setProto("bool", wp.getProto(BooleanLib.class)); - env.setProto("number", wp.getProto(NumberLib.class)); - env.setProto("string", wp.getProto(StringLib.class)); - env.setProto("symbol", wp.getProto(SymbolLib.class)); + env.add(Environment.BOOL_PROTO, wp.getProto(BooleanLib.class)); + env.add(Environment.NUMBER_PROTO, wp.getProto(NumberLib.class)); + env.add(Environment.STRING_PROTO, wp.getProto(StringLib.class)); + env.add(Environment.SYMBOL_PROTO, wp.getProto(SymbolLib.class)); - env.setProto("error", wp.getProto(ErrorLib.class)); - env.setProto("syntaxErr", wp.getProto(SyntaxErrorLib.class)); - env.setProto("typeErr", wp.getProto(TypeErrorLib.class)); - env.setProto("rangeErr", wp.getProto(RangeErrorLib.class)); + env.add(Environment.ERROR_PROTO, wp.getProto(ErrorLib.class)); + env.add(Environment.SYNTAX_ERR_PROTO, wp.getProto(SyntaxErrorLib.class)); + env.add(Environment.TYPE_ERR_PROTO, wp.getProto(TypeErrorLib.class)); + env.add(Environment.RANGE_ERR_PROTO, wp.getProto(RangeErrorLib.class)); wp.getProto(ObjectLib.class).setPrototype(null, null); - env.regexConstructor = wp.getConstr(RegExpLib.class); + env.add(Environment.REGEX_CONSTR, wp.getConstr(RegExpLib.class)); return env; } diff --git a/src/me/topchetoeu/jscript/lib/ObjectLib.java b/src/me/topchetoeu/jscript/lib/ObjectLib.java index 83457fa..2114f20 100644 --- a/src/me/topchetoeu/jscript/lib/ObjectLib.java +++ b/src/me/topchetoeu/jscript/lib/ObjectLib.java @@ -191,7 +191,7 @@ import me.topchetoeu.jscript.interop.NativeConstructor; return thisArg; } @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) { - var name = Values.getMember(ctx, thisArg, ctx.environment().symbol("Symbol.typeName")); + var name = Values.getMember(ctx, thisArg, Symbol.get("Symbol.typeName")); if (name == null) name = "Unknown"; else name = Values.toString(ctx, name); diff --git a/src/me/topchetoeu/jscript/lib/StringLib.java b/src/me/topchetoeu/jscript/lib/StringLib.java index 23a5359..232c2f0 100644 --- a/src/me/topchetoeu/jscript/lib/StringLib.java +++ b/src/me/topchetoeu/jscript/lib/StringLib.java @@ -3,9 +3,11 @@ package me.topchetoeu.jscript.lib; import java.util.regex.Pattern; import me.topchetoeu.jscript.engine.Context; +import me.topchetoeu.jscript.engine.Environment; import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.ObjectValue; +import me.topchetoeu.jscript.engine.values.Symbol; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.interop.Native; @@ -80,7 +82,7 @@ import me.topchetoeu.jscript.interop.NativeGetter; var val = passThis(ctx, "indexOf", thisArg); if (term != null && term != Values.NULL && !(term instanceof String)) { - var search = Values.getMember(ctx, term, ctx.environment().symbol("Symbol.search")); + var search = Values.getMember(ctx, term, Symbol.get("Symbol.search")); if (search instanceof FunctionValue) { return (int)Values.toNumber(ctx, ((FunctionValue)search).call(ctx, term, val, false, start)); } @@ -92,7 +94,7 @@ import me.topchetoeu.jscript.interop.NativeGetter; var val = passThis(ctx, "lastIndexOf", thisArg); if (term != null && term != Values.NULL && !(term instanceof String)) { - var search = Values.getMember(ctx, term, ctx.environment().symbol("Symbol.search")); + var search = Values.getMember(ctx, term, Symbol.get("Symbol.search")); if (search instanceof FunctionValue) { return (int)Values.toNumber(ctx, ((FunctionValue)search).call(ctx, term, val, true, pos)); } @@ -109,7 +111,7 @@ import me.topchetoeu.jscript.interop.NativeGetter; var val = passThis(ctx, "replace", thisArg); if (term != null && term != Values.NULL && !(term instanceof String)) { - var replace = Values.getMember(ctx, term, ctx.environment().symbol("Symbol.replace")); + var replace = Values.getMember(ctx, term, Symbol.get("Symbol.replace")); if (replace instanceof FunctionValue) { return Values.toString(ctx, ((FunctionValue)replace).call(ctx, term, val, replacement)); } @@ -121,7 +123,7 @@ import me.topchetoeu.jscript.interop.NativeGetter; var val = passThis(ctx, "replaceAll", thisArg); if (term != null && term != Values.NULL && !(term instanceof String)) { - var replace = Values.getMember(ctx, term, ctx.environment().symbol("Symbol.replace")); + var replace = Values.getMember(ctx, term, Symbol.get("Symbol.replace")); if (replace instanceof FunctionValue) { return Values.toString(ctx, ((FunctionValue)replace).call(ctx, term, val, replacement)); } @@ -136,11 +138,11 @@ import me.topchetoeu.jscript.interop.NativeGetter; FunctionValue match; try { - var _match = Values.getMember(ctx, term, ctx.environment().symbol("Symbol.match")); + var _match = Values.getMember(ctx, term, Symbol.get("Symbol.match")); if (_match instanceof FunctionValue) match = (FunctionValue)_match; - else if (ctx.environment().regexConstructor != null) { - var regex = Values.callNew(ctx, ctx.environment().regexConstructor, Values.toString(ctx, term), ""); - _match = Values.getMember(ctx, regex, ctx.environment().symbol("Symbol.match")); + else if (ctx.environment().has(Environment.REGEX_CONSTR)) { + var regex = Values.callNew(ctx, ctx.environment().get(Environment.REGEX_CONSTR), Values.toString(ctx, term), ""); + _match = Values.getMember(ctx, regex, Symbol.get("Symbol.match")); if (_match instanceof FunctionValue) match = (FunctionValue)_match; else throw EngineException.ofError("Regular expressions don't support matching."); } @@ -158,14 +160,14 @@ import me.topchetoeu.jscript.interop.NativeGetter; FunctionValue match = null; try { - var _match = Values.getMember(ctx, term, ctx.environment().symbol("Symbol.matchAll")); + var _match = Values.getMember(ctx, term, Symbol.get("Symbol.matchAll")); if (_match instanceof FunctionValue) match = (FunctionValue)_match; } catch (IllegalArgumentException e) { } - if (match == null && ctx.environment().regexConstructor != null) { - var regex = Values.callNew(ctx, ctx.environment().regexConstructor, Values.toString(ctx, term), "g"); - var _match = Values.getMember(ctx, regex, ctx.environment().symbol("Symbol.matchAll")); + if (match == null && ctx.environment().has(Environment.REGEX_CONSTR)) { + var regex = Values.callNew(ctx, ctx.environment().get(Environment.REGEX_CONSTR), Values.toString(ctx, term), "g"); + var _match = Values.getMember(ctx, regex, Symbol.get("Symbol.matchAll")); if (_match instanceof FunctionValue) match = (FunctionValue)_match; else throw EngineException.ofError("Regular expressions don't support matching."); } @@ -180,7 +182,7 @@ import me.topchetoeu.jscript.interop.NativeGetter; if (lim != null) lim = Values.toNumber(ctx, lim); if (term != null && term != Values.NULL && !(term instanceof String)) { - var replace = Values.getMember(ctx, term, ctx.environment().symbol("Symbol.replace")); + var replace = Values.getMember(ctx, term, Symbol.get("Symbol.replace")); if (replace instanceof FunctionValue) { var tmp = ((FunctionValue)replace).call(ctx, term, val, lim, sensible); diff --git a/src/me/topchetoeu/jscript/lib/SymbolLib.java b/src/me/topchetoeu/jscript/lib/SymbolLib.java index f8bb132..900fb20 100644 --- a/src/me/topchetoeu/jscript/lib/SymbolLib.java +++ b/src/me/topchetoeu/jscript/lib/SymbolLib.java @@ -10,20 +10,19 @@ import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.interop.Native; import me.topchetoeu.jscript.interop.NativeConstructor; -import me.topchetoeu.jscript.interop.NativeGetter; @Native("Symbol") public class SymbolLib { private static final Map symbols = new HashMap<>(); - @NativeGetter public static Symbol typeName(Context ctx) { return ctx.environment().symbol("Symbol.typeName"); } - @NativeGetter public static Symbol replace(Context ctx) { return ctx.environment().symbol("Symbol.replace"); } - @NativeGetter public static Symbol match(Context ctx) { return ctx.environment().symbol("Symbol.match"); } - @NativeGetter public static Symbol matchAll(Context ctx) { return ctx.environment().symbol("Symbol.matchAll"); } - @NativeGetter public static Symbol split(Context ctx) { return ctx.environment().symbol("Symbol.split"); } - @NativeGetter public static Symbol search(Context ctx) { return ctx.environment().symbol("Symbol.search"); } - @NativeGetter public static Symbol iterator(Context ctx) { return ctx.environment().symbol("Symbol.iterator"); } - @NativeGetter public static Symbol asyncIterator(Context ctx) { return ctx.environment().symbol("Symbol.asyncIterator"); } - @NativeGetter public static Symbol cause(Context ctx) { return ctx.environment().symbol("Symbol.cause"); } + @Native public static final Symbol typeName = Symbol.get("Symbol.typeName"); + @Native public static final Symbol replace = Symbol.get("Symbol.replace"); + @Native public static final Symbol match = Symbol.get("Symbol.match"); + @Native public static final Symbol matchAll = Symbol.get("Symbol.matchAll"); + @Native public static final Symbol split = Symbol.get("Symbol.split"); + @Native public static final Symbol search = Symbol.get("Symbol.search"); + @Native public static final Symbol iterator = Symbol.get("Symbol.iterator"); + @Native public static final Symbol asyncIterator = Symbol.get("Symbol.asyncIterator"); + @Native public static final Symbol cause = Symbol.get("Symbol.cause"); public final Symbol value;