refactor: clean up context and stack data

This commit is contained in:
TopchetoEU 2023-10-07 13:07:07 +03:00
parent 926b9c17d8
commit 517e3e6657
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
22 changed files with 226 additions and 211 deletions

View File

@ -7,7 +7,7 @@ import java.io.InputStreamReader;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import me.topchetoeu.jscript.engine.Message; 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.values.NativeFunction; import me.topchetoeu.jscript.engine.values.NativeFunction;
@ -65,7 +65,7 @@ public class Main {
env = new Environment(null, null, null); env = new Environment(null, null, null);
var exited = new boolean[1]; var exited = new boolean[1];
engine.pushMsg(false, new Message(engine), new NativeFunction((ctx, thisArg, _a) -> { engine.pushMsg(false, null, new NativeFunction((ctx, thisArg, _a) -> {
new Internals().apply(env); new Internals().apply(env);
env.global.define("exit", _ctx -> { env.global.define("exit", _ctx -> {
@ -94,7 +94,7 @@ public class Main {
var raw = in.readLine(); var raw = in.readLine();
if (raw == null) break; if (raw == null) break;
engine.pushMsg(false, env.context(new Message(engine)), "<stdio>", raw, null).toObservable().once(valuePrinter); engine.pushMsg(false, new Context(engine).pushEnv(env), "<stdio>", raw, null).toObservable().once(valuePrinter);
} }
catch (EngineException e) { catch (EngineException e) {
try { try {

View File

@ -1,27 +1,39 @@
package me.topchetoeu.jscript.engine; package me.topchetoeu.jscript.engine;
import java.util.Stack;
import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.parsing.Parsing; import me.topchetoeu.jscript.parsing.Parsing;
public class Context { public class Context {
public final Environment env; private final Stack<Environment> env = new Stack<>();
public final Message message; public final Data data;
public final Engine engine;
public Environment environment() {
return env.empty() ? null : env.peek();
}
public Context pushEnv(Environment env) {
this.env.push(env);
return this;
}
public void popEnv() {
if (!env.empty()) this.env.pop();
}
public FunctionValue compile(String filename, String raw) throws InterruptedException { public FunctionValue compile(String filename, String raw) throws InterruptedException {
var res = Values.toString(this, env.compile.call(this, null, raw, filename)); var res = Values.toString(this, environment().compile.call(this, null, raw, filename));
return Parsing.compile(message.engine.functions, env, filename, res); return Parsing.compile(engine.functions, environment(), filename, res);
} }
public Context setEnv(Environment env) { public Context(Engine engine, Data data) {
return new Context(env, message); this.data = new Data(engine.data);
if (data != null) this.data.addAll(data);
this.engine = engine;
} }
public Context setMsg(Message msg) { public Context(Engine engine) {
return new Context(env, msg); this(engine, null);
}
public Context(Environment env, Message msg) {
this.env = env;
this.message = msg;
} }
} }

View File

@ -1,43 +1,53 @@
package me.topchetoeu.jscript.engine; package me.topchetoeu.jscript.engine;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Map;
import java.util.Map.Entry;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class Data implements Iterable<Entry<DataKey<?>, ?>> { public class Data {
public final Data parent;
private HashMap<DataKey<Object>, Object> data = new HashMap<>(); private HashMap<DataKey<Object>, Object> data = new HashMap<>();
public Data copy() { public Data copy() {
return new Data().addAll(this); return new Data().addAll(this);
} }
public Data addAll(Iterable<Entry<DataKey<?>, ?>> data) { public Data addAll(Map<DataKey<?>, ?> data) {
for (var el : data) { for (var el : data.entrySet()) {
add((DataKey<Object>)el.getKey(), (Object)el.getValue()); get((DataKey<Object>)el.getKey(), (Object)el.getValue());
}
return this;
}
public Data addAll(Data data) {
for (var el : data.data.entrySet()) {
get((DataKey<Object>)el.getKey(), (Object)el.getValue());
} }
return this; return this;
} }
public <T> T remove(DataKey<T> key) {
return (T)data.remove(key);
}
public <T> Data set(DataKey<T> key, T val) { public <T> Data set(DataKey<T> key, T val) {
if (val == null) data.remove(key); data.put((DataKey<Object>)key, (Object)val);
else data.put((DataKey<Object>)key, (Object)val);
return this; return this;
} }
public <T> T add(DataKey<T> key, T val) { public <T> T get(DataKey<T> key, T val) {
if (data.containsKey(key)) return (T)data.get(key); for (var it = this; it != null; it = it.parent) {
else { if (it.data.containsKey(key)) {
if (val == null) data.remove(key); this.set(key, val);
else data.put((DataKey<Object>)key, (Object)val); return (T)data.get((DataKey<Object>)key);
return val; }
} }
set(key, val);
return val;
} }
public <T> T get(DataKey<T> key) { public <T> T get(DataKey<T> key) {
return get(key, null); for (var it = this; it != null; it = it.parent) {
} if (it.data.containsKey(key)) return (T)data.get((DataKey<Object>)key);
public <T> T get(DataKey<T> key, T defaultVal) { }
if (!has(key)) return defaultVal; return null;
else return (T)data.get(key);
} }
public boolean has(DataKey<?> key) { return data.containsKey(key); } public boolean has(DataKey<?> key) { return data.containsKey(key); }
@ -53,8 +63,10 @@ public class Data implements Iterable<Entry<DataKey<?>, ?>> {
return increase(key, 1, 0); return increase(key, 1, 0);
} }
@Override public Data() {
public Iterator<Entry<DataKey<?>, ?>> iterator() { this.parent = null;
return (Iterator<Entry<DataKey<?>, ?>>)data.entrySet(); }
public Data(Data parent) {
this.parent = parent;
} }
} }

View File

@ -13,19 +13,18 @@ public class Engine {
private class UncompiledFunction extends FunctionValue { private class UncompiledFunction extends FunctionValue {
public final String filename; public final String filename;
public final String raw; public final String raw;
public final Environment env; private FunctionValue compiled = null;
@Override @Override
public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException { public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
ctx = ctx.setEnv(env); if (compiled == null) compiled = ctx.compile(filename, raw);
return ctx.compile(filename, raw).call(ctx, thisArg, args); return compiled.call(ctx, thisArg, args);
} }
public UncompiledFunction(Environment env, String filename, String raw) { public UncompiledFunction(String filename, String raw) {
super(filename, 0); super(filename, 0);
this.filename = filename; this.filename = filename;
this.raw = raw; this.raw = raw;
this.env = env;
} }
} }
@ -34,10 +33,10 @@ public class Engine {
public final Object thisArg; public final Object thisArg;
public final Object[] args; public final Object[] args;
public final DataNotifier<Object> notifier = new DataNotifier<>(); public final DataNotifier<Object> notifier = new DataNotifier<>();
public final Message msg; public final Context ctx;
public Task(Message ctx, FunctionValue func, Object thisArg, Object[] args) { public Task(Context ctx, FunctionValue func, Object thisArg, Object[] args) {
this.msg = ctx; this.ctx = ctx;
this.func = func; this.func = func;
this.thisArg = thisArg; this.thisArg = thisArg;
this.args = args; this.args = args;
@ -52,10 +51,11 @@ public class Engine {
public final int id = ++nextId; public final int id = ++nextId;
public final HashMap<Long, Instruction[]> functions = new HashMap<>(); public final HashMap<Long, Instruction[]> functions = new HashMap<>();
public final Data data = new Data().set(StackData.MAX_FRAMES, 10000);
private void runTask(Task task) throws InterruptedException { private void runTask(Task task) throws InterruptedException {
try { try {
task.notifier.next(task.func.call(task.msg.context(null), task.thisArg, task.args)); task.notifier.next(task.func.call(task.ctx, task.thisArg, task.args));
} }
catch (InterruptedException e) { catch (InterruptedException e) {
task.notifier.error(new RuntimeException(e)); task.notifier.error(new RuntimeException(e));
@ -105,17 +105,13 @@ public class Engine {
return this.thread != null; return this.thread != null;
} }
public Awaitable<Object> pushMsg(boolean micro, Message ctx, FunctionValue func, Object thisArg, Object ...args) { public Awaitable<Object> pushMsg(boolean micro, Context ctx, FunctionValue func, Object thisArg, Object ...args) {
var msg = new Task(ctx, func, thisArg, args); var msg = new Task(ctx == null ? new Context(this) : ctx, func, thisArg, args);
if (micro) microTasks.addLast(msg); if (micro) microTasks.addLast(msg);
else macroTasks.addLast(msg); else macroTasks.addLast(msg);
return msg.notifier; return msg.notifier;
} }
public Awaitable<Object> pushMsg(boolean micro, Context ctx, String filename, String raw, Object thisArg, Object ...args) { public Awaitable<Object> pushMsg(boolean micro, Context ctx, String filename, String raw, Object thisArg, Object ...args) {
return pushMsg(micro, ctx.message, new UncompiledFunction(ctx.env, filename, raw), thisArg, args); return pushMsg(micro, ctx, new UncompiledFunction(filename, raw), thisArg, args);
} }
// public Engine() {
// this.typeRegister = new NativeTypeRegister();
// }
} }

View File

@ -20,7 +20,7 @@ public class Environment {
public final HashMap<String, Symbol> symbols = new HashMap<>(); public final HashMap<String, Symbol> symbols = new HashMap<>();
public GlobalScope global; public GlobalScope global;
public WrappersProvider wrappersProvider; public WrappersProvider wrappers;
@Native public FunctionValue compile; @Native public FunctionValue compile;
@Native public FunctionValue regexConstructor = new NativeFunction("RegExp", (ctx, thisArg, args) -> { @Native public FunctionValue regexConstructor = new NativeFunction("RegExp", (ctx, thisArg, args) -> {
@ -57,7 +57,7 @@ public class Environment {
} }
@Native public Environment fork() { @Native public Environment fork() {
var res = new Environment(compile, wrappersProvider, global); var res = new Environment(compile, wrappers, global);
res.regexConstructor = regexConstructor; res.regexConstructor = regexConstructor;
res.prototypes = new HashMap<>(prototypes); res.prototypes = new HashMap<>(prototypes);
return res; return res;
@ -68,8 +68,11 @@ public class Environment {
return res; return res;
} }
public Context context(Message msg) { public Context context(Engine engine, Data data) {
return new Context(this, msg); return new Context(engine, data).pushEnv(this);
}
public Context context(Engine engine) {
return new Context(engine).pushEnv(this);
} }
public Environment(FunctionValue compile, WrappersProvider nativeConverter, GlobalScope global) { public Environment(FunctionValue compile, WrappersProvider nativeConverter, GlobalScope global) {
@ -77,7 +80,7 @@ public class Environment {
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();
this.wrappersProvider = nativeConverter; this.wrappers = nativeConverter;
this.compile = compile; this.compile = compile;
this.global = global; this.global = global;
} }

View File

@ -1,63 +0,0 @@
package me.topchetoeu.jscript.engine;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import me.topchetoeu.jscript.engine.frame.CodeFrame;
import me.topchetoeu.jscript.exceptions.EngineException;
public class Message {
public final Engine engine;
private final ArrayList<CodeFrame> frames = new ArrayList<>();
public int maxStackFrames = 1000;
public final Data data = new Data();
public List<CodeFrame> frames() { return Collections.unmodifiableList(frames); }
public Message addData(Data data) {
this.data.addAll(data);
return this;
}
public Message pushFrame(Context ctx, CodeFrame frame) throws InterruptedException {
this.frames.add(frame);
if (this.frames.size() > maxStackFrames) throw EngineException.ofRange("Stack overflow!");
return this;
}
public boolean popFrame(CodeFrame frame) {
if (this.frames.size() == 0) return false;
if (this.frames.get(this.frames.size() - 1) != frame) return false;
this.frames.remove(this.frames.size() - 1);
return true;
}
public List<String> stackTrace() {
var res = new ArrayList<String>();
for (var el : frames) {
var name = el.function.name;
var loc = el.function.loc();
var trace = "";
if (loc != null) trace += "at " + loc.toString() + " ";
if (name != null && !name.equals("")) trace += "in " + name + " ";
trace = trace.trim();
if (!res.equals("")) res.add(trace);
}
return res;
}
public Context context(Environment env) {
return new Context(env, this);
}
public Message(Engine engine) {
this.engine = engine;
}
}

View File

@ -0,0 +1,52 @@
package me.topchetoeu.jscript.engine;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import me.topchetoeu.jscript.engine.debug.DebugServer;
import me.topchetoeu.jscript.engine.frame.CodeFrame;
import me.topchetoeu.jscript.exceptions.EngineException;
public class StackData {
public static final DataKey<ArrayList<CodeFrame>> FRAMES = new DataKey<>();
public static final DataKey<Integer> MAX_FRAMES = new DataKey<>();
public static final DataKey<DebugServer> DEBUGGER = new DataKey<>();
public static void pushFrame(Context ctx, CodeFrame frame) throws InterruptedException {
var frames = ctx.data.get(FRAMES, new ArrayList<>());
frames.add(frame);
if (frames.size() > ctx.data.get(MAX_FRAMES, 10000)) throw EngineException.ofRange("Stack overflow!");
ctx.pushEnv(frame.function.environment);
}
public static boolean popFrame(Context ctx, CodeFrame frame) {
var frames = ctx.data.get(FRAMES, new ArrayList<>());
if (frames.size() == 0) return false;
if (frames.get(frames.size() - 1) != frame) return false;
frames.remove(frames.size() - 1);
ctx.popEnv();
return true;
}
public static List<CodeFrame> frames(Context ctx) {
return Collections.unmodifiableList(ctx.data.get(FRAMES, new ArrayList<>()));
}
public static List<String> stackTrace(Context ctx) {
var res = new ArrayList<String>();
for (var el : frames(ctx)) {
var name = el.function.name;
var loc = el.function.loc();
var trace = "";
if (loc != null) trace += "at " + loc.toString() + " ";
if (name != null && !name.equals("")) trace += "in " + name + " ";
trace = trace.trim();
if (!trace.equals("")) res.add(trace);
}
return res;
}
}

View File

@ -95,7 +95,7 @@ public class CodeFrame {
private void setCause(Context ctx, EngineException err, EngineException cause) throws InterruptedException { private void setCause(Context ctx, EngineException err, EngineException cause) throws InterruptedException {
if (err.value instanceof ObjectValue) { if (err.value instanceof ObjectValue) {
Values.setMember(ctx, err, ctx.env.symbol("Symbol.cause"), cause); Values.setMember(ctx, err, ctx.environment().symbol("Symbol.cause"), cause);
} }
err.cause = cause; err.cause = cause;
} }
@ -227,19 +227,6 @@ public class CodeFrame {
return Runners.NO_RETURN; return Runners.NO_RETURN;
} }
public Object run(Context ctx) throws InterruptedException {
try {
ctx.message.pushFrame(ctx, this);
while (true) {
var res = next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, null);
if (res != Runners.NO_RETURN) return res;
}
}
finally {
ctx.message.popFrame(this);
}
}
public CodeFrame(Context ctx, Object thisArg, Object[] args, CodeFunction func) { public CodeFrame(Context ctx, Object thisArg, Object[] args, CodeFunction func) {
this.args = args.clone(); this.args = args.clone();
this.scope = new LocalScope(func.localsN, func.captures); this.scope = new LocalScope(func.localsN, func.captures);

View File

@ -63,7 +63,7 @@ public class Runners {
public static Object execMakeVar(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { public static Object execMakeVar(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
var name = (String)instr.get(0); var name = (String)instr.get(0);
ctx.env.global.define(name); ctx.environment().global.define(name);
frame.codePtr++; frame.codePtr++;
return NO_RETURN; return NO_RETURN;
} }
@ -158,7 +158,7 @@ public class Runners {
public static Object execLoadVar(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { public static Object execLoadVar(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
var i = instr.get(0); var i = instr.get(0);
if (i instanceof String) frame.push(ctx, ctx.env.global.get(ctx, (String)i)); if (i instanceof String) frame.push(ctx, ctx.environment().global.get(ctx, (String)i));
else frame.push(ctx, frame.scope.get((int)i).get(ctx)); else frame.push(ctx, frame.scope.get((int)i).get(ctx));
frame.codePtr++; frame.codePtr++;
@ -170,7 +170,7 @@ public class Runners {
return NO_RETURN; return NO_RETURN;
} }
public static Object execLoadGlob(Context ctx, Instruction instr, CodeFrame frame) { public static Object execLoadGlob(Context ctx, Instruction instr, CodeFrame frame) {
frame.push(ctx, ctx.env.global.obj); frame.push(ctx, ctx.environment().global.obj);
frame.codePtr++; frame.codePtr++;
return NO_RETURN; return NO_RETURN;
} }
@ -191,8 +191,8 @@ public class Runners {
captures[i - 3] = frame.scope.get(instr.get(i)); captures[i - 3] = frame.scope.get(instr.get(i));
} }
var body = ctx.message.engine.functions.get(id); var body = ctx.engine.functions.get(id);
var func = new CodeFunction(ctx.env, "", localsN, len, captures, body); var func = new CodeFunction(ctx.environment(), "", localsN, len, captures, body);
frame.push(ctx, func); frame.push(ctx, func);
@ -217,7 +217,7 @@ public class Runners {
return execLoadMember(ctx, instr, frame); return execLoadMember(ctx, instr, frame);
} }
public static Object execLoadRegEx(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { public static Object execLoadRegEx(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
frame.push(ctx, ctx.env.regexConstructor.call(ctx, null, instr.get(0), instr.get(1))); frame.push(ctx, ctx.environment().regexConstructor.call(ctx, null, instr.get(0), instr.get(1)));
frame.codePtr++; frame.codePtr++;
return NO_RETURN; return NO_RETURN;
} }
@ -241,7 +241,7 @@ public class Runners {
var val = (boolean)instr.get(1) ? frame.peek() : frame.pop(); var val = (boolean)instr.get(1) ? frame.peek() : frame.pop();
var i = instr.get(0); var i = instr.get(0);
if (i instanceof String) ctx.env.global.set(ctx, (String)i, val); if (i instanceof String) ctx.environment().global.set(ctx, (String)i, val);
else frame.scope.get((int)i).set(ctx, val); else frame.scope.get((int)i).set(ctx, val);
frame.codePtr++; frame.codePtr++;
@ -288,8 +288,8 @@ public class Runners {
Object obj; Object obj;
if (name != null) { if (name != null) {
if (ctx.env.global.has(ctx, name)) { if (ctx.environment().global.has(ctx, name)) {
obj = ctx.env.global.get(ctx, name); obj = ctx.environment().global.get(ctx, name);
} }
else obj = null; else obj = null;
} }

View File

@ -4,7 +4,9 @@ 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.Environment; import me.topchetoeu.jscript.engine.Environment;
import me.topchetoeu.jscript.engine.StackData;
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.scope.ValueVariable; import me.topchetoeu.jscript.engine.scope.ValueVariable;
public class CodeFunction extends FunctionValue { public class CodeFunction extends FunctionValue {
@ -29,7 +31,18 @@ public class CodeFunction extends FunctionValue {
@Override @Override
public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException { public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
return new CodeFrame(ctx, thisArg, args, this).run(ctx.setEnv(environment)); var frame = new CodeFrame(ctx, thisArg, args, this);
try {
StackData.pushFrame(ctx, frame);
while (true) {
var res = frame.next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, null);
if (res != Runners.NO_RETURN) return res;
}
}
finally {
StackData.popFrame(ctx, frame);
}
} }
public CodeFunction(Environment environment, String name, int localsN, int length, ValueVariable[] captures, Instruction[] body) { public CodeFunction(Environment environment, String name, int localsN, int length, ValueVariable[] captures, Instruction[] body) {

View File

@ -8,8 +8,7 @@ public class NativeWrapper extends ObjectValue {
@Override @Override
public ObjectValue getPrototype(Context ctx) throws InterruptedException { public ObjectValue getPrototype(Context ctx) throws InterruptedException {
if (prototype == NATIVE_PROTO) if (prototype == NATIVE_PROTO) return ctx.environment().wrappers.getProto(wrapped.getClass());
return ctx.env.wrappersProvider.getProto(wrapped.getClass());
else return super.getPrototype(ctx); else return super.getPrototype(ctx);
} }

View File

@ -147,13 +147,13 @@ public class ObjectValue {
public ObjectValue getPrototype(Context ctx) throws InterruptedException { public ObjectValue getPrototype(Context ctx) throws InterruptedException {
try { try {
if (prototype == OBJ_PROTO) return ctx.env.proto("object"); if (prototype == OBJ_PROTO) return ctx.environment().proto("object");
if (prototype == ARR_PROTO) return ctx.env.proto("array"); if (prototype == ARR_PROTO) return ctx.environment().proto("array");
if (prototype == FUNC_PROTO) return ctx.env.proto("function"); if (prototype == FUNC_PROTO) return ctx.environment().proto("function");
if (prototype == ERR_PROTO) return ctx.env.proto("error"); if (prototype == ERR_PROTO) return ctx.environment().proto("error");
if (prototype == RANGE_ERR_PROTO) return ctx.env.proto("rangeErr"); if (prototype == RANGE_ERR_PROTO) return ctx.environment().proto("rangeErr");
if (prototype == SYNTAX_ERR_PROTO) return ctx.env.proto("syntaxErr"); if (prototype == SYNTAX_ERR_PROTO) return ctx.environment().proto("syntaxErr");
if (prototype == TYPE_ERR_PROTO) return ctx.env.proto("typeErr"); if (prototype == TYPE_ERR_PROTO) return ctx.environment().proto("typeErr");
} }
catch (NullPointerException e) { catch (NullPointerException e) {
return null; return null;
@ -172,14 +172,14 @@ public class ObjectValue {
else if (Values.isObject(val)) { else if (Values.isObject(val)) {
var obj = Values.object(val); var obj = Values.object(val);
if (ctx != null && ctx.env != null) { if (ctx != null && ctx.environment() != null) {
if (obj == ctx.env.proto("object")) prototype = OBJ_PROTO; if (obj == ctx.environment().proto("object")) prototype = OBJ_PROTO;
else if (obj == ctx.env.proto("array")) prototype = ARR_PROTO; else if (obj == ctx.environment().proto("array")) prototype = ARR_PROTO;
else if (obj == ctx.env.proto("function")) prototype = FUNC_PROTO; else if (obj == ctx.environment().proto("function")) prototype = FUNC_PROTO;
else if (obj == ctx.env.proto("error")) prototype = ERR_PROTO; else if (obj == ctx.environment().proto("error")) prototype = ERR_PROTO;
else if (obj == ctx.env.proto("syntaxErr")) prototype = SYNTAX_ERR_PROTO; else if (obj == ctx.environment().proto("syntaxErr")) prototype = SYNTAX_ERR_PROTO;
else if (obj == ctx.env.proto("typeErr")) prototype = TYPE_ERR_PROTO; else if (obj == ctx.environment().proto("typeErr")) prototype = TYPE_ERR_PROTO;
else if (obj == ctx.env.proto("rangeErr")) prototype = RANGE_ERR_PROTO; else if (obj == ctx.environment().proto("rangeErr")) prototype = RANGE_ERR_PROTO;
else prototype = obj; else prototype = obj;
} }
else prototype = obj; else prototype = obj;

View File

@ -321,10 +321,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.env.proto("string"); if (obj instanceof String) return ctx.environment().proto("string");
else if (obj instanceof Number) return ctx.env.proto("number"); else if (obj instanceof Number) return ctx.environment().proto("number");
else if (obj instanceof Boolean) return ctx.env.proto("bool"); else if (obj instanceof Boolean) return ctx.environment().proto("bool");
else if (obj instanceof Symbol) return ctx.env.proto("symbol"); else if (obj instanceof Symbol) return ctx.environment().proto("symbol");
return null; return null;
} }
@ -446,7 +446,7 @@ public class Values {
if (val instanceof Class) { if (val instanceof Class) {
if (ctx == null) return null; if (ctx == null) return null;
else return ctx.env.wrappersProvider.getConstr((Class<?>)val); else return ctx.environment().wrappers.getConstr((Class<?>)val);
} }
return new NativeWrapper(val); return new NativeWrapper(val);
@ -520,7 +520,7 @@ public class Values {
public static Iterable<Object> toJavaIterable(Context ctx, Object obj) { public static Iterable<Object> toJavaIterable(Context ctx, Object obj) {
return () -> { return () -> {
try { try {
var symbol = ctx.env.symbol("Symbol.iterator"); var symbol = ctx.environment().symbol("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();
@ -590,7 +590,7 @@ public class Values {
var res = new ObjectValue(); var res = new ObjectValue();
try { try {
var key = getMember(ctx, getMember(ctx, ctx.env.proto("symbol"), "constructor"), "iterator"); var key = getMember(ctx, getMember(ctx, ctx.environment().proto("symbol"), "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) { }

View File

@ -1,6 +1,7 @@
package me.topchetoeu.jscript.lib; package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.StackData;
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.values.CodeFunction; import me.topchetoeu.jscript.engine.values.CodeFunction;
@ -19,7 +20,8 @@ public class AsyncFunctionLib extends FunctionValue {
private void next(Context ctx, Object inducedValue, Object inducedError) throws InterruptedException { private void next(Context ctx, Object inducedValue, Object inducedError) throws InterruptedException {
Object res = null; Object res = null;
ctx.message.pushFrame(ctx, frame); StackData.pushFrame(ctx, frame);
ctx.pushEnv(frame.function.environment);
awaiting = false; awaiting = false;
while (!awaiting) { while (!awaiting) {
@ -37,7 +39,7 @@ public class AsyncFunctionLib extends FunctionValue {
} }
} }
ctx.message.popFrame(frame); StackData.popFrame(ctx, frame);
if (awaiting) { if (awaiting) {
PromiseLib.then(ctx, frame.pop(), new NativeFunction(this::fulfill), new NativeFunction(this::reject)); PromiseLib.then(ctx, frame.pop(), new NativeFunction(this::fulfill), new NativeFunction(this::reject));

View File

@ -3,6 +3,7 @@ package me.topchetoeu.jscript.lib;
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.StackData;
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.values.CodeFunction; import me.topchetoeu.jscript.engine.values.CodeFunction;
@ -34,7 +35,7 @@ public class AsyncGeneratorLib extends FunctionValue {
} }
Object res = null; Object res = null;
ctx.message.pushFrame(ctx, frame); StackData.pushFrame(ctx, frame);
state = 0; state = 0;
while (state == 0) { while (state == 0) {
@ -55,7 +56,7 @@ public class AsyncGeneratorLib extends FunctionValue {
} }
} }
ctx.message.popFrame(frame); StackData.popFrame(ctx, frame);
if (state == 1) { if (state == 1) {
PromiseLib.then(ctx, frame.pop(), new NativeFunction(this::fulfill), new NativeFunction(this::reject)); PromiseLib.then(ctx, frame.pop(), new NativeFunction(this::fulfill), new NativeFunction(this::reject));

View File

@ -2,6 +2,7 @@ package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Environment; import me.topchetoeu.jscript.engine.Environment;
import me.topchetoeu.jscript.engine.StackData;
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.Values; import me.topchetoeu.jscript.engine.values.Values;
@ -52,7 +53,7 @@ public class ErrorLib {
var target = new ObjectValue(); var target = new ObjectValue();
if (thisArg instanceof ObjectValue) target = (ObjectValue)thisArg; if (thisArg instanceof ObjectValue) target = (ObjectValue)thisArg;
target.defineProperty(ctx, "stack", new ArrayValue(ctx, ctx.message.stackTrace().toArray())); target.defineProperty(ctx, "stack", new ArrayValue(ctx, StackData.stackTrace(ctx)));
target.defineProperty(ctx, "name", "Error"); target.defineProperty(ctx, "name", "Error");
if (message == null) target.defineProperty(ctx, "message", ""); if (message == null) target.defineProperty(ctx, "message", "");
else target.defineProperty(ctx, "message", Values.toString(ctx, message)); else target.defineProperty(ctx, "message", Values.toString(ctx, message));

View File

@ -1,6 +1,7 @@
package me.topchetoeu.jscript.lib; package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.StackData;
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.values.CodeFunction; import me.topchetoeu.jscript.engine.values.CodeFunction;
@ -30,7 +31,7 @@ public class GeneratorLib extends FunctionValue {
} }
Object res = null; Object res = null;
ctx.message.pushFrame(ctx, frame); StackData.pushFrame(ctx, frame);
yielding = false; yielding = false;
while (!yielding) { while (!yielding) {
@ -48,7 +49,7 @@ public class GeneratorLib extends FunctionValue {
} }
} }
ctx.message.popFrame(frame); StackData.popFrame(ctx, frame);
if (done) frame = null; if (done) frame = null;
else res = frame.pop(); else res = frame.pop();

View File

@ -31,12 +31,12 @@ public class Internals {
} }
catch (InterruptedException e) { return; } catch (InterruptedException e) { return; }
ctx.message.engine.pushMsg(false, ctx.message, func, null, args); ctx.engine.pushMsg(false, ctx, func, null, args);
}); });
thread.start(); thread.start();
int i = ctx.env.data.increase(I, 1, 0); int i = ctx.environment().data.increase(I, 1, 0);
var threads = ctx.env.data.add(THREADS, new HashMap<>()); var threads = ctx.environment().data.get(THREADS, new HashMap<>());
threads.put(++i, thread); threads.put(++i, thread);
return i; return i;
} }
@ -51,19 +51,19 @@ public class Internals {
} }
catch (InterruptedException e) { return; } catch (InterruptedException e) { return; }
ctx.message.engine.pushMsg(false, ctx.message, func, null, args); ctx.engine.pushMsg(false, ctx, func, null, args);
} }
}); });
thread.start(); thread.start();
int i = ctx.env.data.increase(I, 1, 0); int i = ctx.environment().data.increase(I, 1, 0);
var threads = ctx.env.data.add(THREADS, new HashMap<>()); var threads = ctx.environment().data.get(THREADS, new HashMap<>());
threads.put(++i, thread); threads.put(++i, thread);
return i; return i;
} }
@Native public static void clearTimeout(Context ctx, int i) { @Native public static void clearTimeout(Context ctx, int i) {
var threads = ctx.env.data.add(THREADS, new HashMap<>()); var threads = ctx.environment().data.get(THREADS, new HashMap<>());
var thread = threads.remove(i); var thread = threads.remove(i);
if (thread != null) thread.interrupt(); if (thread != null) thread.interrupt();
@ -80,7 +80,7 @@ public class Internals {
} }
public void apply(Environment env) { public void apply(Environment env) {
var wp = env.wrappersProvider; var wp = env.wrappers;
var glob = env.global = new GlobalScope(wp.getNamespace(Internals.class)); var glob = env.global = new GlobalScope(wp.getNamespace(Internals.class));
glob.define(null, "Math", false, wp.getNamespace(MathLib.class)); glob.define(null, "Math", false, wp.getNamespace(MathLib.class));

View File

@ -194,7 +194,7 @@ public class ObjectLib {
return thisArg; return thisArg;
} }
@Native(thisArg = true) public static String toString(Context ctx, Object thisArg) throws InterruptedException { @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) throws InterruptedException {
var name = Values.getMember(ctx, thisArg, ctx.env.symbol("Symbol.typeName")); var name = Values.getMember(ctx, thisArg, ctx.environment().symbol("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

@ -6,7 +6,6 @@ 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.Environment;
import me.topchetoeu.jscript.engine.Message;
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.NativeFunction; import me.topchetoeu.jscript.engine.values.NativeFunction;
@ -263,7 +262,7 @@ public class PromiseLib {
else if (state == STATE_REJECTED) { else if (state == STATE_REJECTED) {
for (var handle : handles) handle.rejected.call(handle.ctx, null, val); for (var handle : handles) handle.rejected.call(handle.ctx, null, val);
if (handles.size() == 0) { if (handles.size() == 0) {
ctx.message.engine.pushMsg(true, ctx.message, new NativeFunction((_ctx, _thisArg, _args) -> { ctx.engine.pushMsg(true, ctx, new NativeFunction((_ctx, _thisArg, _args) -> {
if (!handled) { if (!handled) {
try { Values.printError(new EngineException(val).setContext(ctx), "(in promise)"); } try { Values.printError(new EngineException(val).setContext(ctx), "(in promise)"); }
catch (InterruptedException ex) { } catch (InterruptedException ex) { }
@ -297,9 +296,9 @@ public class PromiseLib {
} }
private void handle(Context ctx, FunctionValue fulfill, FunctionValue reject) { private void handle(Context ctx, FunctionValue fulfill, FunctionValue reject) {
if (state == STATE_FULFILLED) ctx.message.engine.pushMsg(true, new Message(ctx.message.engine), fulfill, null, val); if (state == STATE_FULFILLED) ctx.engine.pushMsg(true, ctx, fulfill, null, val);
else if (state == STATE_REJECTED) { else if (state == STATE_REJECTED) {
ctx.message.engine.pushMsg(true, new Message(ctx.message.engine), reject, null, val); ctx.engine.pushMsg(true, ctx, reject, null, val);
handled = true; handled = true;
} }
else handles.add(new Handle(ctx, fulfill, reject)); else handles.add(new Handle(ctx, fulfill, reject));

View File

@ -76,7 +76,7 @@ public class StringLib {
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.env.symbol("Symbol.search")); var search = Values.getMember(ctx, term, ctx.environment().symbol("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));
} }
@ -88,7 +88,7 @@ public class StringLib {
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.env.symbol("Symbol.search")); var search = Values.getMember(ctx, term, ctx.environment().symbol("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));
} }
@ -105,7 +105,7 @@ public class StringLib {
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.env.symbol("Symbol.replace")); var replace = Values.getMember(ctx, term, ctx.environment().symbol("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));
} }
@ -117,7 +117,7 @@ public class StringLib {
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.env.symbol("Symbol.replace")); var replace = Values.getMember(ctx, term, ctx.environment().symbol("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));
} }
@ -132,11 +132,11 @@ public class StringLib {
FunctionValue match; FunctionValue match;
try { try {
var _match = Values.getMember(ctx, term, ctx.env.symbol("Symbol.match")); var _match = Values.getMember(ctx, term, ctx.environment().symbol("Symbol.match"));
if (_match instanceof FunctionValue) match = (FunctionValue)_match; if (_match instanceof FunctionValue) match = (FunctionValue)_match;
else if (ctx.env.regexConstructor != null) { else if (ctx.environment().regexConstructor != null) {
var regex = Values.callNew(ctx, ctx.env.regexConstructor, Values.toString(ctx, term), ""); var regex = Values.callNew(ctx, ctx.environment().regexConstructor, Values.toString(ctx, term), "");
_match = Values.getMember(ctx, regex, ctx.env.symbol("Symbol.match")); _match = Values.getMember(ctx, regex, ctx.environment().symbol("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.");
} }
@ -154,14 +154,14 @@ public class StringLib {
FunctionValue match = null; FunctionValue match = null;
try { try {
var _match = Values.getMember(ctx, term, ctx.env.symbol("Symbol.matchAll")); var _match = Values.getMember(ctx, term, ctx.environment().symbol("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.env.regexConstructor != null) { if (match == null && ctx.environment().regexConstructor != null) {
var regex = Values.callNew(ctx, ctx.env.regexConstructor, Values.toString(ctx, term), "g"); var regex = Values.callNew(ctx, ctx.environment().regexConstructor, Values.toString(ctx, term), "g");
var _match = Values.getMember(ctx, regex, ctx.env.symbol("Symbol.matchAll")); var _match = Values.getMember(ctx, regex, ctx.environment().symbol("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.");
} }
@ -176,7 +176,7 @@ public class StringLib {
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.env.symbol("Symbol.replace")); var replace = Values.getMember(ctx, term, ctx.environment().symbol("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

@ -18,14 +18,14 @@ import me.topchetoeu.jscript.interop.NativeInit;
public class SymbolLib { 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.env.symbol("Symbol.typeName"); } @NativeGetter public static Symbol typeName(Context ctx) { return ctx.environment().symbol("Symbol.typeName"); }
@NativeGetter public static Symbol replace(Context ctx) { return ctx.env.symbol("Symbol.replace"); } @NativeGetter public static Symbol replace(Context ctx) { return ctx.environment().symbol("Symbol.replace"); }
@NativeGetter public static Symbol match(Context ctx) { return ctx.env.symbol("Symbol.match"); } @NativeGetter public static Symbol match(Context ctx) { return ctx.environment().symbol("Symbol.match"); }
@NativeGetter public static Symbol matchAll(Context ctx) { return ctx.env.symbol("Symbol.matchAll"); } @NativeGetter public static Symbol matchAll(Context ctx) { return ctx.environment().symbol("Symbol.matchAll"); }
@NativeGetter public static Symbol split(Context ctx) { return ctx.env.symbol("Symbol.split"); } @NativeGetter public static Symbol split(Context ctx) { return ctx.environment().symbol("Symbol.split"); }
@NativeGetter public static Symbol search(Context ctx) { return ctx.env.symbol("Symbol.search"); } @NativeGetter public static Symbol search(Context ctx) { return ctx.environment().symbol("Symbol.search"); }
@NativeGetter public static Symbol iterator(Context ctx) { return ctx.env.symbol("Symbol.iterator"); } @NativeGetter public static Symbol iterator(Context ctx) { return ctx.environment().symbol("Symbol.iterator"); }
@NativeGetter public static Symbol asyncIterator(Context ctx) { return ctx.env.symbol("Symbol.asyncIterator"); } @NativeGetter public static Symbol asyncIterator(Context ctx) { return ctx.environment().symbol("Symbol.asyncIterator"); }
public final Symbol value; public final Symbol value;