refactor: greatly improve Environment API

This commit is contained in:
TopchetoEU 2023-12-26 17:12:20 +02:00
parent d7f6010319
commit 38acc20a6f
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
21 changed files with 371 additions and 240 deletions

View File

@ -131,10 +131,10 @@ public class Main {
private static void initTypescript() { private static void initTypescript() {
try { try {
var tsEnv = Internals.apply(new Environment(null, null, null)); var tsEnv = Internals.apply(new Environment(null, null, null));
tsEnv.stackVisible = false; tsEnv.stackHidden = false;
tsEnv.global.define(null, "module", false, new ObjectValue()); tsEnv.global.define(null, "module", false, new ObjectValue());
var bsEnv = Internals.apply(new Environment(null, null, null)); var bsEnv = Internals.apply(new Environment(null, null, null));
bsEnv.stackVisible = false; bsEnv.stackHidden = false;
engine.pushMsg( engine.pushMsg(
false, tsEnv, false, tsEnv,

View File

@ -10,6 +10,7 @@ import java.util.stream.Collectors;
import me.topchetoeu.jscript.Filename; import me.topchetoeu.jscript.Filename;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.engine.debug.DebugContext;
import me.topchetoeu.jscript.engine.frame.CodeFrame; import me.topchetoeu.jscript.engine.frame.CodeFrame;
import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.ArrayValue;
import me.topchetoeu.jscript.engine.values.FunctionValue; 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.exceptions.EngineException;
import me.topchetoeu.jscript.mapping.SourceMap; import me.topchetoeu.jscript.mapping.SourceMap;
public class Context { public class Context extends ExtensionStack {
private final Stack<Environment> env = new Stack<>(); private final Stack<Environment> env = new Stack<>();
private final ArrayList<CodeFrame> frames = new ArrayList<>(); private final ArrayList<CodeFrame> frames = new ArrayList<>();
public final Engine engine; public final Engine engine;
@Override
protected Extensions[] extensionStack() {
return new Extensions[] { environment(), engine.globalEnvironment };
}
public Environment environment() { public Environment environment() {
return env.empty() ? null : env.peek(); return env.empty() ? null : env.peek();
} }
@ -36,7 +42,7 @@ public class Context {
public FunctionValue compile(Filename filename, String raw) { public FunctionValue compile(Filename filename, String raw) {
var env = environment(); 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"); var function = (FunctionValue)Values.getMember(this, result, "function");
if (!engine.debugging) return function; if (!engine.debugging) return function;
@ -62,24 +68,23 @@ public class Context {
breakpoints = newBreakpoints; breakpoints = newBreakpoints;
} }
engine.onSource(filename, raw, breakpoints, map); DebugContext.get(this).onSource(filename, raw, breakpoints, map);
return function; return function;
} }
public void pushFrame(CodeFrame frame) { public void pushFrame(CodeFrame frame) {
frames.add(frame); frames.add(frame);
if (frames.size() > engine.maxStackFrames) throw EngineException.ofRange("Stack overflow!"); if (frames.size() > engine.maxStackFrames) throw EngineException.ofRange("Stack overflow!");
pushEnv(frame.function.environment); pushEnv(frame.function.environment);
engine.onFramePush(this, frame); DebugContext.get(this).onFramePush(this, frame);
} }
public boolean popFrame(CodeFrame frame) { public boolean popFrame(CodeFrame frame) {
if (frames.size() == 0) return false; if (frames.size() == 0) return false;
if (frames.get(frames.size() - 1) != frame) return false; if (frames.get(frames.size() - 1) != frame) return false;
frames.remove(frames.size() - 1); frames.remove(frames.size() - 1);
popEnv(); popEnv();
engine.onFramePop(this, frame); DebugContext.get(this).onFramePop(this, frame);
return true; return true;
} }
public CodeFrame peekFrame() { public CodeFrame peekFrame() {

View File

@ -17,7 +17,7 @@ import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.exceptions.InterruptException; import me.topchetoeu.jscript.exceptions.InterruptException;
import me.topchetoeu.jscript.mapping.SourceMap; import me.topchetoeu.jscript.mapping.SourceMap;
public class Engine implements DebugController { public class Engine {
private class UncompiledFunction extends FunctionValue { private class UncompiledFunction extends FunctionValue {
public final Filename filename; public final Filename filename;
public final String raw; public final String raw;
@ -61,47 +61,15 @@ public class Engine implements DebugController {
private static int nextId = 0; private static int nextId = 0;
public static final HashMap<Long, FunctionBody> functions = new HashMap<>(); public static final HashMap<Long, FunctionBody> functions = new HashMap<>();
public final Environment globalEnvironment = new Environment();
public final int id = ++nextId; public final int id = ++nextId;
public final boolean debugging; public final boolean debugging;
public int maxStackFrames = 10000; public int maxStackFrames = 10000;
private final HashMap<Filename, String> sources = new HashMap<>();
private final HashMap<Filename, TreeSet<Location>> bpts = new HashMap<>();
private final HashMap<Filename, SourceMap> 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 Thread thread;
private PriorityBlockingQueue<Task> tasks = new PriorityBlockingQueue<>(); private PriorityBlockingQueue<Task> 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) { private void runTask(Task task) {
try { try {
task.notifier.next(task.func.call(task.ctx, task.thisArg, task.args)); 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); 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<Location> 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) { public Engine(boolean debugging) {
this.debugging = debugging; this.debugging = debugging;
} }

View File

@ -13,117 +13,109 @@ import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.engine.values.Symbol; import me.topchetoeu.jscript.engine.values.Symbol;
import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.filesystem.RootFilesystem;
import me.topchetoeu.jscript.interop.Native; 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.interop.NativeWrapperProvider;
import me.topchetoeu.jscript.modules.RootModuleRepo;
import me.topchetoeu.jscript.parsing.Parsing; import me.topchetoeu.jscript.parsing.Parsing;
import me.topchetoeu.jscript.permissions.Permission;
import me.topchetoeu.jscript.permissions.PermissionsProvider;
// TODO: Remove hardcoded extensions form environment // TODO: Remove hardcoded extensions form environment
public class Environment implements PermissionsProvider { @SuppressWarnings("unchecked")
private HashMap<String, ObjectValue> prototypes = new HashMap<>(); public class Environment implements Extensions {
private static int nextId = 0;
public final Data data = new Data();
public static final HashMap<String, Symbol> symbols = new HashMap<>(); public static final HashMap<String, Symbol> 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<Symbol, Object> data = new HashMap<>();
public GlobalScope global; public GlobalScope global;
public WrappersProvider wrappers; 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 int id = ++nextId;
@Native public FunctionValue compile = new NativeFunction("compile", (ctx, thisArg, args) -> { @Override public <T> void add(Symbol key, T obj) {
var source = Values.toString(ctx, args[0]); data.put(key, obj);
var filename = Values.toString(ctx, args[1]); }
var isDebug = Values.toBoolean(args[2]); @Override public <T> T get(Symbol key) {
return (T)data.get(key);
var env = Values.wrapper(args[2], Environment.class); }
var res = new ObjectValue(); @Override public boolean remove(Symbol key) {
if (data.containsKey(key)) {
var target = Parsing.compile(env, Filename.parse(filename), source); data.remove(key);
Engine.functions.putAll(target.functions); return true;
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 false;
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);
} }
@Native public void setProto(String name, ObjectValue val) { @Override public boolean has(Symbol key) {
prototypes.put(name, val); return data.containsKey(key);
} }
@Native public Symbol symbol(String name) { public static FunctionValue compileFunc(Extensions ext) {
return getSymbol(name); 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() { public Environment fork() {
return global.obj; var res = new Environment(null, global);
}
@NativeSetter("global") public void setGlobal(ObjectValue val) {
global = new GlobalScope(val);
}
@Native public Environment fork() {
var res = new Environment(compile, null, global);
res.wrappers = wrappers.fork(res); res.wrappers = wrappers.fork(res);
res.regexConstructor = regexConstructor; res.global = global;
res.prototypes = new HashMap<>(prototypes); res.data.putAll(data);
return res; return res;
} }
@Native public Environment child() { public Environment child() {
var res = fork(); var res = fork();
res.global = res.global.globalChild(); 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; 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) { public Context context(Engine engine) {
return new Context(engine, this); return new Context(engine, this);
} }
public static Symbol getSymbol(String name) { public Environment(WrappersProvider nativeConverter, GlobalScope global) {
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;
if (nativeConverter == null) nativeConverter = new NativeWrapperProvider(this); if (nativeConverter == null) nativeConverter = new NativeWrapperProvider(this);
if (global == null) global = new GlobalScope(); if (global == null) global = new GlobalScope();
@ -131,6 +123,6 @@ public class Environment implements PermissionsProvider {
this.global = global; this.global = global;
} }
public Environment() { public Environment() {
this(null, null, null); this(null, null);
} }
} }

View File

@ -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 <T> void add(Symbol key, T obj) {
for (var el : extensionStack()) {
if (el != null) {
el.add(key, obj);
return;
}
}
}
@Override public <T> 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;
}
}

View File

@ -0,0 +1,24 @@
package me.topchetoeu.jscript.engine;
import me.topchetoeu.jscript.engine.values.Symbol;
public interface Extensions {
<T> T get(Symbol key);
<T> void add(Symbol key, T obj);
boolean has(Symbol key);
boolean remove(Symbol key);
default <T> T get(Symbol key, T defaultVal) {
if (has(key)) return get(key);
else return defaultVal;
}
default <T> T init(Symbol key, T val) {
if (has(key)) return get(key);
else {
add(key, val);
return val;
}
}
}

View File

@ -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<Filename, String> sources;
private HashMap<Filename, TreeSet<Location>> bpts;
private HashMap<Filename, SourceMap> 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<Location> 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);
}
}

View File

@ -48,4 +48,15 @@ public interface DebugController {
* @param frame The code frame which was popped out * @param frame The code frame which was popped out
*/ */
void onFramePop(Context ctx, CodeFrame frame); 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<Location> breakpoints, SourceMap map) { }
};
}
} }

View File

@ -15,6 +15,7 @@ import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Instruction.Type; import me.topchetoeu.jscript.compilation.Instruction.Type;
import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Engine; 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.CodeFrame;
import me.topchetoeu.jscript.engine.frame.Runners; import me.topchetoeu.jscript.engine.frame.Runners;
import me.topchetoeu.jscript.engine.scope.GlobalScope; import me.topchetoeu.jscript.engine.scope.GlobalScope;
@ -341,7 +342,7 @@ public class SimpleDebugger implements Debugger {
try { try {
defaultToString = defaultToString =
Values.getMember(ctx, obj, "toString") == 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) { } catch (Exception e) { }
@ -495,7 +496,7 @@ public class SimpleDebugger implements Debugger {
var res = new ArrayValue(); var res = new ArrayValue();
var passed = new HashSet<String>(); var passed = new HashSet<String>();
var tildas = "~"; 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 proto = target; proto != null && proto != Values.NULL; proto = Values.getPrototype(ctx, proto)) {
for (var el : Values.getMembers(ctx, proto, true, true)) { for (var el : Values.getMembers(ctx, proto, true, true)) {
@ -870,7 +871,7 @@ public class SimpleDebugger implements Debugger {
if (!frame.debugData) return false; 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; loc = frame.location;
isBreakpointable = loc != null && (instruction.breakpoint.shouldStepIn()); isBreakpointable = loc != null && (instruction.breakpoint.shouldStepIn());
@ -953,12 +954,12 @@ public class SimpleDebugger implements Debugger {
} }
@Override public synchronized void connect() { @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.")); ws.send(new V8Error("A debugger is already attached to this engine."));
} }
} }
@Override public synchronized void disconnect() { @Override public synchronized void disconnect() {
target.detachDebugger(); DebugContext.get(target.globalEnvironment).detachDebugger();
enabled = false; enabled = false;
updateNotifier.next(); updateNotifier.next();
} }

View File

@ -6,6 +6,7 @@ import java.util.Stack;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.engine.Context; 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.LocalScope;
import me.topchetoeu.jscript.engine.scope.ValueVariable; import me.topchetoeu.jscript.engine.scope.ValueVariable;
import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.ArrayValue;
@ -198,8 +199,7 @@ public class CodeFrame {
if (instr == null) returnValue = null; if (instr == null) returnValue = null;
else { else {
// System.out.println(instr + "@" + instr.location); DebugContext.get(ctx).onInstruction(ctx, this, instr, Runners.NO_RETURN, null, false);
ctx.engine.onInstruction(ctx, this, instr, Runners.NO_RETURN, null, false);
if (instr.location != null) prevLoc = instr.location; 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; throw error;
} }
if (returnValue != Runners.NO_RETURN) { 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; return returnValue;
} }

View File

@ -5,6 +5,7 @@ import java.util.Collections;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Engine; import me.topchetoeu.jscript.engine.Engine;
import me.topchetoeu.jscript.engine.Environment;
import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ValueVariable; import me.topchetoeu.jscript.engine.scope.ValueVariable;
import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.ArrayValue;
@ -197,7 +198,13 @@ public class Runners {
return execLoadMember(ctx, instr, frame); return execLoadMember(ctx, instr, frame);
} }
public static Object execLoadRegEx(Context ctx, Instruction instr, CodeFrame 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++; frame.codePtr++;
return NO_RETURN; return NO_RETURN;
} }

View File

@ -7,6 +7,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Environment;
public class ObjectValue { public class ObjectValue {
public static enum PlaceholderProto { public static enum PlaceholderProto {
@ -146,13 +147,13 @@ public class ObjectValue {
public ObjectValue getPrototype(Context ctx) { public ObjectValue getPrototype(Context ctx) {
try { try {
if (prototype == OBJ_PROTO) return ctx.environment().proto("object"); if (prototype == OBJ_PROTO) return ctx.environment().get(Environment.OBJECT_PROTO);
if (prototype == ARR_PROTO) return ctx.environment().proto("array"); if (prototype == ARR_PROTO) return ctx.environment().get(Environment.ARRAY_PROTO);
if (prototype == FUNC_PROTO) return ctx.environment().proto("function"); if (prototype == FUNC_PROTO) return ctx.environment().get(Environment.FUNCTION_PROTO);
if (prototype == ERR_PROTO) return ctx.environment().proto("error"); if (prototype == ERR_PROTO) return ctx.environment().get(Environment.ERROR_PROTO);
if (prototype == RANGE_ERR_PROTO) return ctx.environment().proto("rangeErr"); if (prototype == RANGE_ERR_PROTO) return ctx.environment().get(Environment.RANGE_ERR_PROTO);
if (prototype == SYNTAX_ERR_PROTO) return ctx.environment().proto("syntaxErr"); if (prototype == SYNTAX_ERR_PROTO) return ctx.environment().get(Environment.SYNTAX_ERR_PROTO);
if (prototype == TYPE_ERR_PROTO) return ctx.environment().proto("typeErr"); if (prototype == TYPE_ERR_PROTO) return ctx.environment().get(Environment.TYPE_ERR_PROTO);
} }
catch (NullPointerException e) { return null; } catch (NullPointerException e) { return null; }
@ -170,13 +171,13 @@ public class ObjectValue {
var obj = Values.object(val); var obj = Values.object(val);
if (ctx != null && ctx.environment() != null) { if (ctx != null && ctx.environment() != null) {
if (obj == ctx.environment().proto("object")) prototype = OBJ_PROTO; if (obj == ctx.environment().get(Environment.OBJECT_PROTO)) prototype = OBJ_PROTO;
else if (obj == ctx.environment().proto("array")) prototype = ARR_PROTO; else if (obj == ctx.environment().get(Environment.ARRAY_PROTO)) prototype = ARR_PROTO;
else if (obj == ctx.environment().proto("function")) prototype = FUNC_PROTO; else if (obj == ctx.environment().get(Environment.FUNCTION_PROTO)) prototype = FUNC_PROTO;
else if (obj == ctx.environment().proto("error")) prototype = ERR_PROTO; else if (obj == ctx.environment().get(Environment.ERROR_PROTO)) prototype = ERR_PROTO;
else if (obj == ctx.environment().proto("syntaxErr")) prototype = SYNTAX_ERR_PROTO; else if (obj == ctx.environment().get(Environment.SYNTAX_ERR_PROTO)) prototype = SYNTAX_ERR_PROTO;
else if (obj == ctx.environment().proto("typeErr")) prototype = TYPE_ERR_PROTO; else if (obj == ctx.environment().get(Environment.TYPE_ERR_PROTO)) prototype = TYPE_ERR_PROTO;
else if (obj == ctx.environment().proto("rangeErr")) prototype = RANGE_ERR_PROTO; else if (obj == ctx.environment().get(Environment.RANGE_ERR_PROTO)) prototype = RANGE_ERR_PROTO;
else prototype = obj; else prototype = obj;
} }
else prototype = obj; else prototype = obj;

View File

@ -1,6 +1,10 @@
package me.topchetoeu.jscript.engine.values; package me.topchetoeu.jscript.engine.values;
import java.util.HashMap;
public final class Symbol { public final class Symbol {
private static final HashMap<String, Symbol> registry = new HashMap<>();
public final String value; public final String value;
public Symbol(String value) { public Symbol(String value) {
@ -12,4 +16,13 @@ public final class Symbol {
if (value == null) return "Symbol"; if (value == null) return "Symbol";
else return "@@" + value; 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;
}
}
} }

View File

@ -11,6 +11,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Environment;
import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.frame.ConvertHint; import me.topchetoeu.jscript.engine.frame.ConvertHint;
import me.topchetoeu.jscript.exceptions.ConvertException; import me.topchetoeu.jscript.exceptions.ConvertException;
@ -346,10 +347,10 @@ public class Values {
if (isObject(obj)) return object(obj).getPrototype(ctx); if (isObject(obj)) return object(obj).getPrototype(ctx);
if (ctx == null) return null; if (ctx == null) return null;
if (obj instanceof String) return ctx.environment().proto("string"); if (obj instanceof String) return ctx.environment().get(Environment.STRING_PROTO);
else if (obj instanceof Number) return ctx.environment().proto("number"); else if (obj instanceof Number) return ctx.environment().get(Environment.NUMBER_PROTO);
else if (obj instanceof Boolean) return ctx.environment().proto("bool"); else if (obj instanceof Boolean) return ctx.environment().get(Environment.BOOL_PROTO);
else if (obj instanceof Symbol) return ctx.environment().proto("symbol"); else if (obj instanceof Symbol) return ctx.environment().get(Environment.SYMBOL_PROTO);
return null; return null;
} }
@ -550,7 +551,7 @@ public class Values {
public static Iterable<Object> fromJSIterator(Context ctx, Object obj) { public static Iterable<Object> fromJSIterator(Context ctx, Object obj) {
return () -> { return () -> {
try { try {
var symbol = ctx.environment().symbol("Symbol.iterator"); var symbol = Symbol.get("Symbol.iterator");
var iteratorFunc = getMember(ctx, obj, symbol); var iteratorFunc = getMember(ctx, obj, symbol);
if (!isFunction(iteratorFunc)) return Collections.emptyIterator(); if (!isFunction(iteratorFunc)) return Collections.emptyIterator();
@ -604,7 +605,7 @@ public class Values {
var res = new ObjectValue(); var res = new ObjectValue();
try { 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)); res.defineProperty(ctx, key, new NativeFunction("", (_ctx, thisArg, args) -> thisArg));
} }
catch (IllegalArgumentException | NullPointerException e) { } catch (IllegalArgumentException | NullPointerException e) { }
@ -629,7 +630,7 @@ public class Values {
var res = new ObjectValue(); var res = new ObjectValue();
try { 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)); res.defineProperty(ctx, key, new NativeFunction("", (_ctx, thisArg, args) -> thisArg));
} }
catch (IllegalArgumentException | NullPointerException e) { } catch (IllegalArgumentException | NullPointerException e) { }

View File

@ -7,6 +7,7 @@ import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Engine; import me.topchetoeu.jscript.engine.Engine;
import me.topchetoeu.jscript.engine.Environment; 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.ObjectValue;
import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto; import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto;
@ -18,13 +19,13 @@ public class EngineException extends RuntimeException {
public final Context ctx; public final Context ctx;
public boolean visible() { 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() { public String toString() {
var res = ""; var res = "";
var loc = location; 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 (loc != null) res += "at " + loc.toString() + " ";
if (function != null && !function.equals("")) res += "in " + function + " "; if (function != null && !function.equals("")) res += "in " + function + " ";

View File

@ -8,6 +8,7 @@ import me.topchetoeu.jscript.engine.WrappersProvider;
import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.engine.values.NativeFunction; import me.topchetoeu.jscript.engine.values.NativeFunction;
import me.topchetoeu.jscript.engine.values.ObjectValue; 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.EngineException;
import me.topchetoeu.jscript.exceptions.UncheckedException; import me.topchetoeu.jscript.exceptions.UncheckedException;
@ -28,7 +29,7 @@ public class NativeWrapperProvider implements WrappersProvider {
if (nat.thisArg() && !member || !nat.thisArg() && !memberMatch) continue; if (nat.thisArg() && !member || !nat.thisArg() && !memberMatch) continue;
Object name = nat.value(); 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(); else if (name.equals("")) name = method.getName();
var val = target.values.get(name); var val = target.values.get(name);
@ -42,7 +43,7 @@ public class NativeWrapperProvider implements WrappersProvider {
if (get.thisArg() && !member || !get.thisArg() && !memberMatch) continue; if (get.thisArg() && !member || !get.thisArg() && !memberMatch) continue;
Object name = get.value(); 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(); else if (name.equals("")) name = method.getName();
var prop = target.properties.get(name); var prop = target.properties.get(name);
@ -59,7 +60,7 @@ public class NativeWrapperProvider implements WrappersProvider {
if (set.thisArg() && !member || !set.thisArg() && !memberMatch) continue; if (set.thisArg() && !member || !set.thisArg() && !memberMatch) continue;
Object name = set.value(); 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(); else if (name.equals("")) name = method.getName();
var prop = target.properties.get(name); var prop = target.properties.get(name);
@ -82,7 +83,7 @@ public class NativeWrapperProvider implements WrappersProvider {
if (nat != null) { if (nat != null) {
Object name = nat.value(); 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(); else if (name.equals("")) name = field.getName();
var getter = OverloadFunction.of("get " + name, Overload.getterFromField(field)); var getter = OverloadFunction.of("get " + name, Overload.getterFromField(field));
@ -98,7 +99,7 @@ public class NativeWrapperProvider implements WrappersProvider {
if (nat != null) { if (nat != null) {
Object name = nat.value(); 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(); else if (name.equals("")) name = cl.getName();
var getter = new NativeFunction("get " + name, (ctx, thisArg, args) -> cl); 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) { public static ObjectValue makeProto(Environment ctx, Class<?> clazz) {
var res = new ObjectValue(); 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()) { for (var overload : clazz.getDeclaredMethods()) {
var init = overload.getAnnotation(NativeInit.class); var init = overload.getAnnotation(NativeInit.class);

View File

@ -4,6 +4,7 @@ import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Environment; import me.topchetoeu.jscript.engine.Environment;
import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.ArrayValue;
import me.topchetoeu.jscript.engine.values.ObjectValue; 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.Values;
import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto; import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto;
import me.topchetoeu.jscript.interop.InitType; import me.topchetoeu.jscript.interop.InitType;
@ -35,7 +36,7 @@ import me.topchetoeu.jscript.interop.NativeInit;
if (thisArg instanceof ObjectValue) { if (thisArg instanceof ObjectValue) {
var stack = Values.getMember(ctx, thisArg, "stack"); var stack = Values.getMember(ctx, thisArg, "stack");
if (!(stack instanceof ArrayValue)) stack = null; 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, return toString(ctx,
thisArg == cause, thisArg == cause,
cause, cause,

View File

@ -10,6 +10,7 @@ import me.topchetoeu.jscript.engine.DataKey;
import me.topchetoeu.jscript.engine.Environment; import me.topchetoeu.jscript.engine.Environment;
import me.topchetoeu.jscript.engine.scope.GlobalScope; import me.topchetoeu.jscript.engine.scope.GlobalScope;
import me.topchetoeu.jscript.engine.values.FunctionValue; 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.engine.values.Values;
import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.interop.Native; import me.topchetoeu.jscript.interop.Native;
@ -17,9 +18,6 @@ import me.topchetoeu.jscript.interop.NativeGetter;
import me.topchetoeu.jscript.parsing.Parsing; import me.topchetoeu.jscript.parsing.Parsing;
public class Internals { public class Internals {
private static final DataKey<HashMap<Integer, Thread>> THREADS = new DataKey<>();
private static final DataKey<Integer> I = new DataKey<>();
@Native public static Object require(Context ctx, String name) { @Native public static Object require(Context ctx, String name) {
var env = ctx.environment(); var env = ctx.environment();
var res = env.modules.getModule(ctx, env.moduleCwd, name); 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 thread = new Thread(() -> {
var ms = (long)delay; var ms = (long)delay;
var ns = (int)((delay - ms) * 10000000); var ns = (int)((delay - ms) * 10000000);
@ -61,12 +59,9 @@ public class Internals {
}); });
thread.start(); thread.start();
int i = ctx.environment().data.increase(I, 1, 0); return thread;
var threads = ctx.environment().data.get(THREADS, new HashMap<>());
threads.put(++i, thread);
return i;
} }
@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 thread = new Thread(() -> {
var ms = (long)delay; var ms = (long)delay;
var ns = (int)((delay - ms) * 10000000); var ns = (int)((delay - ms) * 10000000);
@ -76,26 +71,19 @@ public class Internals {
Thread.sleep(ms, ns); Thread.sleep(ms, ns);
} }
catch (InterruptedException e) { return; } catch (InterruptedException e) { return; }
ctx.engine.pushMsg(false, ctx.environment(), func, null, args); ctx.engine.pushMsg(false, ctx.environment(), func, null, args);
} }
}); });
thread.start();
int i = ctx.environment().data.increase(I, 1, 0); return thread;
var threads = ctx.environment().data.get(THREADS, new HashMap<>());
threads.put(++i, thread);
return i;
} }
@Native public static void clearTimeout(Context ctx, int i) { @Native public static void clearTimeout(Context ctx, Thread t) {
var threads = ctx.environment().data.get(THREADS, new HashMap<>()); t.interrupt();
var thread = threads.remove(i);
if (thread != null) thread.interrupt();
} }
@Native public static void clearInterval(Context ctx, int i) { @Native public static void clearInterval(Context ctx, Thread t) {
clearTimeout(ctx, i); t.interrupt();
} }
@Native public static double parseInt(Context ctx, String val) { @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(TypeErrorLib.class));
glob.define(false, wp.getConstr(RangeErrorLib.class)); glob.define(false, wp.getConstr(RangeErrorLib.class));
env.setProto("object", wp.getProto(ObjectLib.class)); env.add(Environment.OBJECT_PROTO, wp.getProto(ObjectLib.class));
env.setProto("function", wp.getProto(FunctionLib.class)); env.add(Environment.FUNCTION_PROTO, wp.getProto(FunctionLib.class));
env.setProto("array", wp.getProto(ArrayLib.class)); env.add(Environment.ARRAY_PROTO, wp.getProto(ArrayLib.class));
env.setProto("bool", wp.getProto(BooleanLib.class)); env.add(Environment.BOOL_PROTO, wp.getProto(BooleanLib.class));
env.setProto("number", wp.getProto(NumberLib.class)); env.add(Environment.NUMBER_PROTO, wp.getProto(NumberLib.class));
env.setProto("string", wp.getProto(StringLib.class)); env.add(Environment.STRING_PROTO, wp.getProto(StringLib.class));
env.setProto("symbol", wp.getProto(SymbolLib.class)); env.add(Environment.SYMBOL_PROTO, wp.getProto(SymbolLib.class));
env.setProto("error", wp.getProto(ErrorLib.class)); env.add(Environment.ERROR_PROTO, wp.getProto(ErrorLib.class));
env.setProto("syntaxErr", wp.getProto(SyntaxErrorLib.class)); env.add(Environment.SYNTAX_ERR_PROTO, wp.getProto(SyntaxErrorLib.class));
env.setProto("typeErr", wp.getProto(TypeErrorLib.class)); env.add(Environment.TYPE_ERR_PROTO, wp.getProto(TypeErrorLib.class));
env.setProto("rangeErr", wp.getProto(RangeErrorLib.class)); env.add(Environment.RANGE_ERR_PROTO, wp.getProto(RangeErrorLib.class));
wp.getProto(ObjectLib.class).setPrototype(null, null); wp.getProto(ObjectLib.class).setPrototype(null, null);
env.regexConstructor = wp.getConstr(RegExpLib.class); env.add(Environment.REGEX_CONSTR, wp.getConstr(RegExpLib.class));
return env; return env;
} }

View File

@ -191,7 +191,7 @@ import me.topchetoeu.jscript.interop.NativeConstructor;
return thisArg; return thisArg;
} }
@Native(thisArg = true) public static String toString(Context ctx, Object 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"; if (name == null) name = "Unknown";
else name = Values.toString(ctx, name); else name = Values.toString(ctx, name);

View File

@ -3,9 +3,11 @@ package me.topchetoeu.jscript.lib;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import me.topchetoeu.jscript.engine.Context; 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.ArrayValue;
import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.engine.values.ObjectValue; 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.Values;
import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.interop.Native; import me.topchetoeu.jscript.interop.Native;
@ -80,7 +82,7 @@ import me.topchetoeu.jscript.interop.NativeGetter;
var val = passThis(ctx, "indexOf", thisArg); var val = passThis(ctx, "indexOf", thisArg);
if (term != null && term != Values.NULL && !(term instanceof String)) { 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) { if (search instanceof FunctionValue) {
return (int)Values.toNumber(ctx, ((FunctionValue)search).call(ctx, term, val, false, start)); 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); var val = passThis(ctx, "lastIndexOf", thisArg);
if (term != null && term != Values.NULL && !(term instanceof String)) { 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) { if (search instanceof FunctionValue) {
return (int)Values.toNumber(ctx, ((FunctionValue)search).call(ctx, term, val, true, pos)); 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); var val = passThis(ctx, "replace", thisArg);
if (term != null && term != Values.NULL && !(term instanceof String)) { 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) { if (replace instanceof FunctionValue) {
return Values.toString(ctx, ((FunctionValue)replace).call(ctx, term, val, replacement)); 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); var val = passThis(ctx, "replaceAll", thisArg);
if (term != null && term != Values.NULL && !(term instanceof String)) { 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) { if (replace instanceof FunctionValue) {
return Values.toString(ctx, ((FunctionValue)replace).call(ctx, term, val, replacement)); return Values.toString(ctx, ((FunctionValue)replace).call(ctx, term, val, replacement));
} }
@ -136,11 +138,11 @@ import me.topchetoeu.jscript.interop.NativeGetter;
FunctionValue match; FunctionValue match;
try { 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; if (_match instanceof FunctionValue) match = (FunctionValue)_match;
else if (ctx.environment().regexConstructor != null) { else if (ctx.environment().has(Environment.REGEX_CONSTR)) {
var regex = Values.callNew(ctx, ctx.environment().regexConstructor, Values.toString(ctx, term), ""); var regex = Values.callNew(ctx, ctx.environment().get(Environment.REGEX_CONSTR), Values.toString(ctx, term), "");
_match = Values.getMember(ctx, regex, ctx.environment().symbol("Symbol.match")); _match = Values.getMember(ctx, regex, Symbol.get("Symbol.match"));
if (_match instanceof FunctionValue) match = (FunctionValue)_match; if (_match instanceof FunctionValue) match = (FunctionValue)_match;
else throw EngineException.ofError("Regular expressions don't support matching."); else throw EngineException.ofError("Regular expressions don't support matching.");
} }
@ -158,14 +160,14 @@ import me.topchetoeu.jscript.interop.NativeGetter;
FunctionValue match = null; FunctionValue match = null;
try { 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; if (_match instanceof FunctionValue) match = (FunctionValue)_match;
} }
catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) { }
if (match == null && ctx.environment().regexConstructor != null) { if (match == null && ctx.environment().has(Environment.REGEX_CONSTR)) {
var regex = Values.callNew(ctx, ctx.environment().regexConstructor, Values.toString(ctx, term), "g"); var regex = Values.callNew(ctx, ctx.environment().get(Environment.REGEX_CONSTR), Values.toString(ctx, term), "g");
var _match = Values.getMember(ctx, regex, ctx.environment().symbol("Symbol.matchAll")); var _match = Values.getMember(ctx, regex, Symbol.get("Symbol.matchAll"));
if (_match instanceof FunctionValue) match = (FunctionValue)_match; if (_match instanceof FunctionValue) match = (FunctionValue)_match;
else throw EngineException.ofError("Regular expressions don't support matching."); 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 (lim != null) lim = Values.toNumber(ctx, lim);
if (term != null && term != Values.NULL && !(term instanceof String)) { 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) { if (replace instanceof FunctionValue) {
var tmp = ((FunctionValue)replace).call(ctx, term, val, lim, sensible); var tmp = ((FunctionValue)replace).call(ctx, term, val, lim, sensible);

View File

@ -10,20 +10,19 @@ import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.interop.Native; import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeConstructor; import me.topchetoeu.jscript.interop.NativeConstructor;
import me.topchetoeu.jscript.interop.NativeGetter;
@Native("Symbol") public class SymbolLib { @Native("Symbol") public class SymbolLib {
private static final Map<String, Symbol> symbols = new HashMap<>(); private static final Map<String, Symbol> symbols = new HashMap<>();
@NativeGetter public static Symbol typeName(Context ctx) { return ctx.environment().symbol("Symbol.typeName"); } @Native public static final Symbol typeName = Symbol.get("Symbol.typeName");
@NativeGetter public static Symbol replace(Context ctx) { return ctx.environment().symbol("Symbol.replace"); } @Native public static final Symbol replace = Symbol.get("Symbol.replace");
@NativeGetter public static Symbol match(Context ctx) { return ctx.environment().symbol("Symbol.match"); } @Native public static final Symbol match = Symbol.get("Symbol.match");
@NativeGetter public static Symbol matchAll(Context ctx) { return ctx.environment().symbol("Symbol.matchAll"); } @Native public static final Symbol matchAll = Symbol.get("Symbol.matchAll");
@NativeGetter public static Symbol split(Context ctx) { return ctx.environment().symbol("Symbol.split"); } @Native public static final Symbol split = Symbol.get("Symbol.split");
@NativeGetter public static Symbol search(Context ctx) { return ctx.environment().symbol("Symbol.search"); } @Native public static final Symbol search = Symbol.get("Symbol.search");
@NativeGetter public static Symbol iterator(Context ctx) { return ctx.environment().symbol("Symbol.iterator"); } @Native public static final Symbol iterator = Symbol.get("Symbol.iterator");
@NativeGetter public static Symbol asyncIterator(Context ctx) { return ctx.environment().symbol("Symbol.asyncIterator"); } @Native public static final Symbol asyncIterator = Symbol.get("Symbol.asyncIterator");
@NativeGetter public static Symbol cause(Context ctx) { return ctx.environment().symbol("Symbol.cause"); } @Native public static final Symbol cause = Symbol.get("Symbol.cause");
public final Symbol value; public final Symbol value;