refactor: make Environment more reusable

This commit is contained in:
TopchetoEU 2024-08-30 18:48:48 +03:00
parent 6767a707ed
commit a6b4f3bc87
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
34 changed files with 886 additions and 310 deletions

319
src/assets/lib/index.js Normal file
View File

@ -0,0 +1,319 @@
(function(target, primordials) {
var makeSymbol = primordials.symbol.makeSymbol;
var getSymbol = primordials.symbol.getSymbol;
var getSymbolKey = primordials.symbol.getSymbolKey;
var getSymbolDescription = primordials.symbol.getSymbolDescription;
var parseInt = primordials.number.parseInt;
var parseFloat = primordials.number.parseFloat;
var isNaN = primordials.number.isNaN;
var NaN = primordials.number.NaN;
var Infinity = primordials.number.Infinity;
var fromCharCode = primordials.string.fromCharCode;
var fromCodePoint = primordials.string.fromCodePoint;
var stringBuild = primordials.string.stringBuild;
var defineProperty = primordials.object.defineProperty;
var defineField = primordials.object.defineField;
var getOwnMember = primordials.object.getMember;
var getOwnSymbolMember = primordials.object.getOwnSymbolMember;
var getOwnMembers = primordials.object.getOwnMembers;
var getOwnSymbolMembers = primordials.object.getOwnSymbolMembers;
var getPrototype = primordials.object.getPrototype;
var setPrototype = primordials.object.setPrototype;
var invokeType = primordials.function.invokeType;
var setConstructable = primordials.function.setConstructable;
var setCallable = primordials.function.setCallable;
var invoke = primordials.function.invoke;
var setGlobalPrototype = primordials.setGlobalPrototype;
var compile = primordials.compile;
var json = primordials.json;
var valueKey = makeSymbol("Primitive.value");
function unwrapThis(self, type, constr, name, arg, defaultVal) {
if (arg == null) arg = "this";
if (typeof self === type) return self;
if (self instanceof constr && valueKey in self) self = self[valueKey];
if (typeof self === type) return self;
if (arguments.length > 5) return defaultVal;
throw new TypeError(name + " requires that '" + arg + "' be a " + constr.name);
}
function wrapIndex(i, len) {
}
var Symbol = function(name) {
if (arguments.length === 0) return makeSymbol("");
else return makeSymbol(name + "");
};
setConstructable(Symbol, false);
defineField(Symbol, "for", true, false, true, function(name) {
return getSymbol(name + "");
});
defineField(Symbol, "keyFor", true, false, true, function(symbol) {
return getSymbolKey(unwrapThis(symbol, "symbol", Symbol, "Symbol.keyFor"));
});
defineField(Symbol, "asyncIterator", false, false, false, Symbol("Symbol.asyncIterator"));
defineField(Symbol, "iterator", false, false, false, Symbol("Symbol.iterator"));
defineField(Symbol, "match", false, false, false, Symbol("Symbol.match"));
defineField(Symbol, "matchAll", false, false, false, Symbol("Symbol.matchAll"));
defineField(Symbol, "replace", false, false, false, Symbol("Symbol.replace"));
defineField(Symbol, "search", false, false, false, Symbol("Symbol.search"));
defineField(Symbol, "split", false, false, false, Symbol("Symbol.split"));
defineField(Symbol, "toStringTag", false, false, false, Symbol("Symbol.toStringTag"));
defineField(Symbol, "prototype", false, false, false, {});
defineProperty(Symbol.prototype, "description", false, true, function () {
return getSymbolDescription(unwrapThis(this, "symbol", Symbol, "Symbol.prototype.description"));
}, undefined);
defineField(Symbol.prototype, "toString", true, false, true, function() {
return "Symbol(" + unwrapThis(this, "symbol", Symbol, "Symbol.prototype.toString").description + ")";
});
defineField(Symbol.prototype, "valueOf", true, false, true, function() {
return unwrapThis(this, "symbol", Symbol, "Symbol.prototype.valueOf");
});
target.Symbol = Symbol;
var Number = function(value) {
if (invokeType(arguments) === "call") {
if (arguments.length === 0) return 0;
else return +value;
}
this[valueKey] = target.Number(value);
};
defineField(Number, "isFinite", true, false, true, function(value) {
value = unwrapThis(value, "number", Number, "Number.isFinite", "value", undefined);
if (value === undefined || isNaN(value)) return false;
if (value === Infinity || value === -Infinity) return false;
return true;
});
defineField(Number, "isInteger", true, false, true, function(value) {
value = unwrapThis(value, "number", Number, "Number.isInteger", "value", undefined);
if (value === undefined) return false;
return parseInt(value) === value;
});
defineField(Number, "isNaN", true, false, true, function(value) {
return isNaN(value);
});
defineField(Number, "isSafeInteger", true, false, true, function(value) {
value = unwrapThis(value, "number", Number, "Number.isSafeInteger", "value", undefined);
if (value === undefined || parseInt(value) !== value) return false;
return value >= -9007199254740991 && value <= 9007199254740991;
});
defineField(Number, "parseFloat", true, false, true, function(value) {
value = 0 + value;
return parseFloat(value);
});
defineField(Number, "parseInt", true, false, true, function(value, radix) {
value = 0 + value;
radix = +radix;
if (isNaN(radix)) radix = 10;
return parseInt(value, radix);
});
defineField(Number, "EPSILON", false, false, false, 2.220446049250313e-16);
defineField(Number, "MIN_SAFE_INTEGER", false, false, false, -9007199254740991);
defineField(Number, "MAX_SAFE_INTEGER", false, false, false, 9007199254740991);
defineField(Number, "POSITIVE_INFINITY", false, false, false, +Infinity);
defineField(Number, "NEGATIVE_INFINITY", false, false, false, -Infinity);
defineField(Number, "NaN", false, false, false, NaN);
defineField(Number, "MAX_VALUE", false, false, false, 1.7976931348623157e+308);
defineField(Number, "MIN_VALUE", false, false, false, 5e-324);
defineField(Number, "prototype", false, false, false, {});
defineField(Number.prototype, "toString", true, false, true);
defineField(Number.prototype, "toString", true, false, true, function() {
return "" + unwrapThis(this, "number", Number, "Number.prototype.toString");
});
defineField(Number.prototype, "valueOf", true, false, true, function() {
return unwrapThis(this, "number", Number, "Number.prototype.toString");
});
target.Number = Number;
var String = function(value) {
if (invokeType(arguments) === "call") {
if (arguments.length === 0) return "";
else return value + "";
}
this[valueKey] = target.String(value);
};
defineField(String, "fromCharCode", true, false, true, function() {
var res = [];
res[arguments.length] = 0;
for (var i = 0; i < arguments.length; i++) {
res[res.length] = fromCharCode(+arguments[i]);
}
return stringBuild(res);
});
defineField(String, "fromCodePoint", true, false, true, function(value) {
var res = [];
res[arguments.length] = 0;
for (var i = 0; i < arguments.length; i++) {
res[res.length] = fromCodePoint(+arguments[i]);
}
return stringBuild(res);
});
defineField(String, "prototype", false, false, false, {});
defineField(String.prototype, "at", true, false, true, function(index) {
return "" + unwrapThis(this, "string", String, "String.prototype.at");
});
defineField(String.prototype, "toString", true, false, true, function() {
return unwrapThis(this, "string", String, "String.prototype.toString");
});
defineField(String.prototype, "valueOf", true, false, true, function() {
return unwrapThis(this, "string", String, "String.prototype.valueOf");
});
target.String = String;
var Boolean = function(value) {
if (invokeType(arguments) === "call") {
if (arguments.length === 0) return false;
else return !!value;
}
this[valueKey] = target.Boolean(value);
};
defineField(Boolean, "prototype", false, false, false, {});
defineField(Boolean.prototype, "toString", true, false, true, function() {
return "" + unwrapThis(this, "boolean", Boolean, "Boolean.prototype.toString");
});
defineField(Boolean.prototype, "valueOf", true, false, true, function() {
return unwrapThis(this, "boolean", Boolean, "Boolean.prototype.valueOf");
});
target.Boolean = Boolean;
var Object = function(value) {
if (typeof value === 'object' && value !== null) return value;
if (typeof value === 'string') return new String(value);
if (typeof value === 'number') return new Number(value);
if (typeof value === 'boolean') return new Boolean(value);
if (typeof value === 'symbol') {
var res = {};
setPrototype(res, Symbol.prototype);
res[valueKey] = value;
return res;
}
var target = this;
if (target === undefined || target === null || typeof target !== 'object') target = {};
this[valueKey] = target.Object(value);
};
defineField(Object, "prototype", false, false, false, setPrototype({}, null));
defineField(Object.prototype, "toString", true, false, true, function() {
if (this !== null && this !== undefined && (Symbol.toStringTag in this)) return "[object " + this[Symbol.toStringTag] + "]";
else if (typeof this === "number" || this instanceof Number) return "[object Number]";
else if (typeof this === "symbol" || this instanceof Symbol) return "[object Symbol]";
else if (typeof this === "string" || this instanceof String) return "[object String]";
else if (typeof this === "boolean" || this instanceof Boolean) return "[object Boolean]";
else if (typeof this === "function") return "[object Function]";
else return "[object Object]";
});
defineField(Object.prototype, "valueOf", true, false, true, function() {
return this;
});
target.Boolean = Boolean;
var Function = function() {
if (invokeType(arguments) === "new") return Function(value);
var res = ["return function ("];
for (var i = 0; i < arguments.length - 1; i++) {
if (i > 0) res[res.length] = ",";
res[res.length] = arguments[i];
}
res[res.length] = "){";
res[res.length] = String(arguments[arguments.length - 1]);
res[res.length] = "}";
log(res);
return compile(stringBuild(res))();
};
defineField(Function, "compile", true, false, true, function(src, options) {
if (options == null) options = {};
if (src == null) src = "";
if (options.globals == null) options.globals = [];
if (options.wrap == null) options.wrap = true;
var res = [];
if (options.wrap) res[res.length] = "return (function() {\n";
if (options.globals.length > 0) {
res[res.length] = "var ";
for (var i = 0; i < options.globals.length; i++) {
if (i > 0) res[res.length] = ",";
res[res.length] = options.globals[i];
}
res[res.length] = ";(function(g){";
for (var i = 0; i < options.globals.length; i++) {
var name = options.globals[i];
res[res.length] = name;
res[res.length] = "=g[";
res[res.length] = json.stringify(name);
res[res.length] = "];";
}
res[res.length] = "})(arguments[0] || {});\n";
}
res[res.length] = src;
if (options.wrap) res[res.length] = "\n})(arguments[0])";
return compile(stringBuild(res));
});
defineField(Function, "prototype", false, false, false, setPrototype({}, null));
defineField(Function.prototype, "toString", true, false, true, function() {
if (this.name !== "") return "function " + this.name + "(...) { ... }";
else return "function (...) { ... }";
});
defineField(Function.prototype, "valueOf", true, false, true, function() {
return this;
});
target.Function = Function;
setGlobalPrototype("string", String.prototype);
setGlobalPrototype("number", Number.prototype);
setGlobalPrototype("boolean", Boolean.prototype);
setGlobalPrototype("symbol", Symbol.prototype);
setGlobalPrototype("object", Object.prototype);
})(arguments[0], arguments[1]);

View File

@ -1,18 +1,18 @@
package me.topchetoeu.jscript.common;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.environment.Key;
import me.topchetoeu.jscript.common.parsing.Filename;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.ES5;
import me.topchetoeu.jscript.compilation.JavaScript;
import me.topchetoeu.jscript.runtime.debug.DebugContext;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.environment.Key;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.scope.ValueVariable;
import me.topchetoeu.jscript.runtime.values.functions.CodeFunction;
public interface Compiler {
public static final Compiler DEFAULT = (env, filename, raw) -> {
var res = ES5.compile(filename, raw);
var res = JavaScript.compile(filename, raw);
var body = res.body();
DebugContext.get(env).onSource(filename, raw);
registerFunc(env, body, res);
@ -20,7 +20,7 @@ public interface Compiler {
return body;
};
public Key<Compiler> KEY = new Key<>();
public Key<Compiler> KEY = Key.of();
public FunctionBody compile(Environment env, Filename filename, String source);

View File

@ -0,0 +1,195 @@
package me.topchetoeu.jscript.common.environment;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.function.Supplier;
public class Environment {
public final Environment parent;
private final Map<Key<Object>, Object> map = new HashMap<>();
private final Set<Key<Object>> hidden = new HashSet<>();
private final Map<MultiKey<Object>, Set<Object>> multi = new HashMap<>();
private final Map<MultiKey<Object>, Set<Object>> multiHidden = new HashMap<>();
@SuppressWarnings("unchecked")
private <T> Set<T> getAll(MultiKey<T> key, boolean forceClone) {
Set<T> parent = null, child = null;
boolean cloned = false;
if (this.parent != null && !hidden.contains(key)) {
parent = this.parent.getAll(key, false);
if (parent.size() == 0) parent = null;
else if (multiHidden.containsKey(key)) {
parent = new HashSet<>(parent);
parent.removeAll(multiHidden.get(key));
cloned = true;
}
}
if (multi.containsKey(key)) {
child = (Set<T>)multi.get(key);
if (child.size() == 0) child = null;
}
if (!forceClone) {
if (parent == null && child == null) return Set.of();
if (parent == null && child != null) return child;
if (parent != null && child == null) return parent;
}
if (!cloned) parent = new HashSet<>();
parent.addAll(child);
return parent;
}
private <T> T getMulti(MultiKey<T> key) {
return key.of(getAll(key, false));
}
private boolean hasMulti(MultiKey<?> key) {
return getAll(key, false).size() > 0;
}
@SuppressWarnings("all")
private <T> Environment addMulti(MultiKey<T> key, T value) {
if (!multi.containsKey(key)) {
if (hidden.contains(key)) {
multiHidden.put((MultiKey)key, (Set)parent.getAll(key, true));
hidden.remove(key);
}
multi.put((MultiKey)key, new HashSet<>());
}
multi.get(key).add(value);
return this;
}
@SuppressWarnings("unchecked")
public <T> T get(Key<T> key) {
if (key instanceof MultiKey) return getMulti((MultiKey<T>)key);
if (map.containsKey(key)) return (T)map.get(key);
else if (!hidden.contains(key) && parent != null) return parent.get(key);
else return null;
}
public boolean has(Key<?> key) {
if (key instanceof MultiKey) return hasMulti((MultiKey<?>)key);
if (map.containsKey(key)) return true;
else if (!hidden.contains(key) && parent != null) return parent.has(key);
else return false;
}
public boolean hasNotNull(Key<?> key) {
return get(key) != null;
}
public <T> T get(Key<T> key, T defaultVal) {
if (has(key)) return get(key);
else return defaultVal;
}
public <T> T get(Key<T> key, Supplier<T> defaultVal) {
if (has(key)) return get(key);
else return defaultVal.get();
}
@SuppressWarnings("unchecked")
public <T> Environment add(Key<T> key, T val) {
if (key instanceof MultiKey) return add(key, val);
map.put((Key<Object>)key, val);
hidden.remove(key);
return this;
}
public Environment add(Key<Void> key) {
return add(key, null);
}
@SuppressWarnings("all")
public Environment addAll(Map<Key<?>, ?> map, boolean iterableAsMulti) {
for (var pair : map.entrySet()) {
if (iterableAsMulti && pair.getKey() instanceof MultiKey && pair.getValue() instanceof Iterable) {
for (var val : (Iterable<?>)pair.getValue()) {
addMulti((MultiKey<Object>)pair.getKey(), val);
}
}
else add((Key<Object>)pair.getKey(), pair.getValue());
}
map.putAll((Map)map);
hidden.removeAll(map.keySet());
return this;
}
public Environment addAll(Map<Key<?>, ?> map) {
return addAll(map, true);
}
// public Environment addAll(Environment env) {
// this.map.putAll(env.map);
// this.hidden.removeAll(env.map.keySet());
// for (var el : env.multi.entrySet()) {
// for (var val : el.getValue()) {
// add(el.getKey(), val);
// }
// }
// return this;
// }
@SuppressWarnings("unchecked")
public Environment remove(Key<?> key) {
map.remove(key);
multi.remove(key);
multiHidden.remove(key);
hidden.add((Key<Object>)key);
return this;
}
@SuppressWarnings("all")
public <T> Environment remove(MultiKey<T> key, T val) {
if (multi.containsKey(key)) {
multi.get(key).remove(val);
multiHidden.get(key).add(val);
if (multi.get(key).size() == 0) {
multi.remove(key);
multiHidden.remove(key);
hidden.add((Key)key);
}
}
return this;
}
public <T> Environment init(Key<T> key, T val) {
if (!has(key)) this.add(key, val);
return this;
}
public <T> Environment init(Key<T> key, Supplier<T> val) {
if (!has(key)) this.add(key, val.get());
return this;
}
public Environment child() {
return new Environment(this);
}
public Environment(Environment parent) {
this.parent = parent;
}
public Environment() {
this.parent = null;
}
public static Environment wrap(Environment env) {
if (env == null) return empty();
else return env;
}
public static Environment empty() {
return new Environment();
}
public static int nextId() {
return new Random().nextInt();
}
}

View File

@ -0,0 +1,7 @@
package me.topchetoeu.jscript.common.environment;
public interface Key<T> {
public static <T> Key<T> of() {
return new Key<>() { };
}
}

View File

@ -0,0 +1,7 @@
package me.topchetoeu.jscript.common.environment;
import java.util.Set;
public interface MultiKey<T> extends Key<T> {
public T of(Set<T> values);
}

View File

@ -4,15 +4,15 @@ import java.util.concurrent.Future;
import java.util.function.Supplier;
import me.topchetoeu.jscript.common.Compiler;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.environment.Key;
import me.topchetoeu.jscript.common.parsing.Filename;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.environment.Key;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.functions.FunctionValue;
public interface EventLoop {
public static final Key<EventLoop> KEY = new Key<>();
public static final Key<EventLoop> KEY = Key.of();
public static EventLoop get(Environment ext) {
if (ext.hasNotNull(KEY)) return ext.get(KEY);

View File

@ -6,9 +6,9 @@ import java.util.Map;
import java.util.Stack;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.environment.Key;
import me.topchetoeu.jscript.runtime.debug.DebugContext;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.environment.Key;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.exceptions.InterruptException;
import me.topchetoeu.jscript.runtime.scope.LocalScope;
@ -20,10 +20,9 @@ import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
import me.topchetoeu.jscript.runtime.values.functions.CodeFunction;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
import me.topchetoeu.jscript.runtime.values.objects.ScopeValue;
import me.topchetoeu.jscript.runtime.values.primitives.VoidValue;
public class Frame {
public static final Key<Frame> KEY = new Key<>();
public static final Key<Frame> KEY = Key.of();
public static enum TryState {
TRY,
@ -127,7 +126,7 @@ public class Frame {
else return stack[stackPtr - 1 - offset];
}
public Value pop() {
if (stackPtr == 0) return VoidValue.UNDEFINED;
if (stackPtr == 0) return Value.UNDEFINED;
return stack[--stackPtr];
}
public Value[] take(int n) {
@ -138,7 +137,7 @@ public class Frame {
int copyN = stackPtr - srcI;
Value[] res = new Value[n];
Arrays.fill(res, VoidValue.UNDEFINED);
Arrays.fill(res, Value.UNDEFINED);
System.arraycopy(stack, srcI, res, dstI, copyN);
stackPtr -= copyN;

View File

@ -5,7 +5,7 @@ import java.util.Collections;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.scope.GlobalScope;
import me.topchetoeu.jscript.runtime.scope.ValueVariable;
@ -19,7 +19,6 @@ import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
import me.topchetoeu.jscript.runtime.values.primitives.BoolValue;
import me.topchetoeu.jscript.runtime.values.primitives.NumberValue;
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
import me.topchetoeu.jscript.runtime.values.primitives.VoidValue;
public class InstructionRunner {
private static Value execReturn(Environment env, Instruction instr, Frame frame) {
@ -36,7 +35,7 @@ public class InstructionRunner {
var callArgs = frame.take(instr.get(0));
var func = frame.pop();
frame.push(func.call(env, false, instr.get(1), VoidValue.UNDEFINED, callArgs));
frame.push(func.call(env, false, instr.get(1), Value.UNDEFINED, callArgs));
frame.codePtr++;
return null;
@ -75,11 +74,11 @@ public class InstructionRunner {
FunctionValue getter, setter;
if (getterVal == VoidValue.UNDEFINED) getter = null;
if (getterVal == Value.UNDEFINED) getter = null;
else if (getterVal instanceof FunctionValue) getter = (FunctionValue)getterVal;
else throw EngineException.ofType("Getter must be a function or undefined.");
if (setterVal == VoidValue.UNDEFINED) setter = null;
if (setterVal == Value.UNDEFINED) setter = null;
else if (setterVal instanceof FunctionValue) setter = (FunctionValue)setterVal;
else throw EngineException.ofType("Setter must be a function or undefined.");
@ -135,8 +134,8 @@ public class InstructionRunner {
}
private static Value execLoadValue(Environment env, Instruction instr, Frame frame) {
switch (instr.type) {
case PUSH_UNDEFINED: frame.push(VoidValue.UNDEFINED); break;
case PUSH_NULL: frame.push(VoidValue.NULL); break;
case PUSH_UNDEFINED: frame.push(Value.UNDEFINED); break;
case PUSH_NULL: frame.push(Value.NULL); break;
case PUSH_BOOL: frame.push(BoolValue.of(instr.get(0))); break;
case PUSH_NUMBER: frame.push(new NumberValue(instr.get(0))); break;
case PUSH_STRING: frame.push(new StringValue(instr.get(0))); break;
@ -157,7 +156,7 @@ public class InstructionRunner {
}
private static Value execLoadObj(Environment env, Instruction instr, Frame frame) {
var obj = new ObjectValue();
obj.setPrototype(Environment.OBJECT_PROTO);
obj.setPrototype(Value.OBJECT_PROTO);
frame.push(obj);
frame.codePtr++;
return null;
@ -204,8 +203,8 @@ public class InstructionRunner {
return null;
}
private static Value execLoadRegEx(Environment env, Instruction instr, Frame frame) {
if (env.hasNotNull(Environment.REGEX_CONSTR)) {
frame.push(env.get(Environment.REGEX_CONSTR).callNew(env, instr.get(0), instr.get(1)));
if (env.hasNotNull(Value.REGEX_CONSTR)) {
frame.push(env.get(Value.REGEX_CONSTR).callNew(env, instr.get(0), instr.get(1)));
}
else {
throw EngineException.ofSyntax("Regex is not supported.");

View File

@ -3,10 +3,10 @@ package me.topchetoeu.jscript.runtime;
import java.util.HashSet;
import java.util.stream.Collectors;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.json.JSONElement;
import me.topchetoeu.jscript.common.json.JSONList;
import me.topchetoeu.jscript.common.json.JSONMap;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
import me.topchetoeu.jscript.runtime.values.Value;
@ -32,8 +32,8 @@ public class JSONConverter {
return res;
}
if (val.isNull()) return VoidValue.NULL;
return VoidValue.UNDEFINED;
if (val.isNull()) return Value.NULL;
return Value.UNDEFINED;
}
public static JSONElement fromJs(Environment ext, Value val) {
@ -46,7 +46,7 @@ public class JSONConverter {
if (val instanceof BoolValue) return JSONElement.bool(((BoolValue)val).value);
if (val instanceof NumberValue) return JSONElement.number(((NumberValue)val).value);
if (val instanceof StringValue) return JSONElement.string(((StringValue)val).value);
if (val == VoidValue.NULL) return JSONElement.NULL;
if (val == Value.NULL) return JSONElement.NULL;
if (val instanceof VoidValue) return null;
if (val instanceof ArrayValue) {

View File

@ -9,16 +9,25 @@ import java.util.concurrent.ExecutionException;
import me.topchetoeu.jscript.common.Compiler;
import me.topchetoeu.jscript.common.Metadata;
import me.topchetoeu.jscript.common.Reading;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.json.JSON;
import me.topchetoeu.jscript.common.parsing.Filename;
import me.topchetoeu.jscript.runtime.debug.DebugContext;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.exceptions.InterruptException;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
import me.topchetoeu.jscript.runtime.scope.GlobalScope;
import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
import me.topchetoeu.jscript.runtime.values.Member.PropertyMember;
import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.functions.FunctionValue;
import me.topchetoeu.jscript.runtime.values.functions.NativeFunction;
import me.topchetoeu.jscript.runtime.values.objects.ArrayValue;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
import me.topchetoeu.jscript.runtime.values.primitives.BoolValue;
import me.topchetoeu.jscript.runtime.values.primitives.NumberValue;
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue;
import me.topchetoeu.jscript.runtime.values.primitives.VoidValue;
public class SimpleRepl {
@ -31,6 +40,11 @@ public class SimpleRepl {
private static void reader() {
try {
try {
try { initGlobals(); } catch (ExecutionException e) { throw e.getCause(); }
}
catch (EngineException | SyntaxException e) { System.err.println(Value.errorToReadable(e, null)); }
for (var arg : args) {
try {
var file = Path.of(arg);
@ -59,7 +73,7 @@ public class SimpleRepl {
var res = engine.pushMsg(
false, environment,
new Filename("jscript", "repl/" + i + ".js"), raw,
VoidValue.UNDEFINED
Value.UNDEFINED
).get();
System.err.println(res.toReadable(environment));
}
@ -79,10 +93,240 @@ public class SimpleRepl {
}
}
private static ObjectValue symbolPrimordials(Environment env) {
var res = new ObjectValue();
res.setPrototype(null, null);
res.defineOwnMember(env, "makeSymbol", new NativeFunction(args -> new SymbolValue(args.get(0).toString(args.env).value)));
res.defineOwnMember(env, "getSymbol", new NativeFunction(args -> SymbolValue.get(args.get(0).toString(args.env).value)));
res.defineOwnMember(env, "getSymbolKey", new NativeFunction(args -> ((SymbolValue)args.get(0)).key()));
res.defineOwnMember(env, "getSymbolDescriptor", new NativeFunction(args -> new StringValue(((SymbolValue)args.get(0)).value)));
return res;
}
private static ObjectValue numberPrimordials(Environment env) {
var res = new ObjectValue();
res.setPrototype(null, null);
res.defineOwnMember(env, "parseInt", new NativeFunction(args -> {
var radix = args.get(1).toInt(env);
if (radix != 10 && args.get(0) instanceof NumberValue) {
return new NumberValue(args.get(0).toNumber(env).value - args.get(0).toNumber(env).value % 1);
}
else {
return NumberValue.parseInt(args.get(0).toString(), radix, false);
}
}));
res.defineOwnMember(env, "parseFloat", new NativeFunction(args -> {
if (args.get(0) instanceof NumberValue) {
return args.get(0);
}
else return NumberValue.parseFloat(args.get(0).toString(), false);
}));
res.defineOwnMember(env, "isNaN", new NativeFunction(args -> BoolValue.of(args.get(0).isNaN())));
res.defineOwnMember(env, "NaN", new NumberValue(Double.NaN));
res.defineOwnMember(env, "Infinity", new NumberValue(Double.POSITIVE_INFINITY));
return res;
}
private static ObjectValue stringPrimordials(Environment env) {
var res = new ObjectValue();
res.setPrototype(null, null);
res.defineOwnMember(env, "stringBuild", new NativeFunction(args -> {
var parts = ((ArrayValue)args.get(0)).toArray();
var sb = new StringBuilder();
for (var i = 0; i < parts.length; i++) {
sb.append(((StringValue)parts[i]).value);
}
return new StringValue(sb.toString());
}));
res.defineOwnMember(env, "fromCharCode", new NativeFunction(args -> {
var parts = ((ArrayValue)args.get(0)).toArray();
var sb = new StringBuilder();
for (var i = 0; i < parts.length; i++) {
sb.append(((StringValue)parts[i]).value);
}
return new StringValue(sb.toString());
}));
return res;
}
private static ObjectValue objectPrimordials(Environment env) {
var res = new ObjectValue();
res.setPrototype(null, null);
res.defineOwnMember(env, "defineField", new NativeFunction(args -> {
var obj = (ObjectValue)args.get(0);
var key = args.get(1);
var writable = args.get(2).toBoolean();
var enumerable = args.get(3).toBoolean();
var configurable = args.get(4).toBoolean();
var value = args.get(5);
obj.defineOwnMember(args.env, key, FieldMember.of(value, enumerable, configurable, writable));
return Value.UNDEFINED;
}));
res.defineOwnMember(env, "defineProperty", new NativeFunction(args -> {
var obj = (ObjectValue)args.get(0);
var key = args.get(1);
var enumerable = args.get(2).toBoolean();
var configurable = args.get(3).toBoolean();
var getter = args.get(4) instanceof VoidValue ? null : (FunctionValue)args.get(4);
var setter = args.get(5) instanceof VoidValue ? null : (FunctionValue)args.get(5);
obj.defineOwnMember(args.env, key, new PropertyMember(getter, setter, configurable, enumerable));
return Value.UNDEFINED;
}));
res.defineOwnMember(env, "getPrototype", new NativeFunction(args -> {
return args.get(0).getPrototype(env);
}));
res.defineOwnMember(env, "setPrototype", new NativeFunction(args -> {
var proto = args.get(1) instanceof VoidValue ? null : (ObjectValue)args.get(1);
args.get(0).setPrototype(env, proto);
return args.get(0);
}));
return res;
}
private static ObjectValue functionPrimordials(Environment env) {
var res = new ObjectValue();
res.setPrototype(null, null);
res.defineOwnMember(env, "setCallable", new NativeFunction(args -> {
var func = (FunctionValue)args.get(0);
func.enableCall = args.get(1).toBoolean();
return Value.UNDEFINED;
}));
res.defineOwnMember(env, "setConstructable", new NativeFunction(args -> {
var func = (FunctionValue)args.get(0);
func.enableNew = args.get(1).toBoolean();
return Value.UNDEFINED;
}));
res.defineOwnMember(env, "invokeType", new NativeFunction(args -> {
if (((ArgumentsValue)args.get(0)).frame.isNew) return new StringValue("new");
else return new StringValue("call");
}));
res.defineOwnMember(env, "invoke", new NativeFunction(args -> {
var func = (FunctionValue)args.get(0);
var self = args.get(1);
var funcArgs = (ArrayValue)args.get(2);
return func.call(env, self, funcArgs.toArray());
}));
return res;
}
private static ObjectValue jsonPrimordials(Environment env) {
var res = new ObjectValue();
res.setPrototype(null, null);
res.defineOwnMember(env, "stringify", new NativeFunction(args -> {
return new StringValue(JSON.stringify(JSONConverter.fromJs(env, args.get(0))));
}));
res.defineOwnMember(env, "parse", new NativeFunction(args -> {
return JSONConverter.toJs(JSON.parse(null, args.get(0).toString(env).value));
}));
res.defineOwnMember(env, "setConstructable", new NativeFunction(args -> {
var func = (FunctionValue)args.get(0);
func.enableNew = args.get(1).toBoolean();
return Value.UNDEFINED;
}));
res.defineOwnMember(env, "invokeType", new NativeFunction(args -> {
if (((ArgumentsValue)args.get(0)).frame.isNew) return new StringValue("new");
else return new StringValue("call");
}));
res.defineOwnMember(env, "invoke", new NativeFunction(args -> {
var func = (FunctionValue)args.get(0);
var self = args.get(1);
var funcArgs = (ArrayValue)args.get(2);
return func.call(env, self, funcArgs.toArray());
}));
return res;
}
private static ObjectValue primordials(Environment env) {
var res = new ObjectValue();
res.setPrototype(null, null);
res.defineOwnMember(env, "symbol", symbolPrimordials(env));
res.defineOwnMember(env, "number", numberPrimordials(env));
res.defineOwnMember(env, "string", stringPrimordials(env));
res.defineOwnMember(env, "object", objectPrimordials(env));
res.defineOwnMember(env, "function", functionPrimordials(env));
res.defineOwnMember(env, "json", jsonPrimordials(env));
int[] i = new int[1];
res.defineOwnMember(env, "setGlobalPrototype", new NativeFunction(args -> {
var type = args.get(0).toString(env).value;
var obj = (ObjectValue)args.get(1);
switch (type) {
case "string":
args.env.add(Value.STRING_PROTO, obj);
break;
case "number":
args.env.add(Value.NUMBER_PROTO, obj);
break;
case "boolean":
args.env.add(Value.BOOL_PROTO, obj);
break;
case "symbol":
args.env.add(Value.SYMBOL_PROTO, obj);
break;
case "object":
args.env.add(Value.OBJECT_PROTO, obj);
break;
}
return Value.UNDEFINED;
}));
res.defineOwnMember(env, "compile", new NativeFunction(args -> {
return Compiler.compileFunc(env, new Filename("jscript", "func" + i[0]++ + ".js"), args.get(0).toString(env).value);
}));
return res;
}
private static void initEnv() {
// glob.define(null, false, new NativeFunction("go", args -> {
// try {
// var f = Path.of("do.js");
// var func = Compiler.compile(args.env, new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f)));
// return func.call(args.env);
// }
// catch (IOException e) {
// throw new EngineException("Couldn't open do.js");
// }
// }));
// var fs = new RootFilesystem(PermissionsProvider.get(environment));
// fs.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE));
// fs.protocols.put("file", new PhysicalFilesystem("."));
// fs.protocols.put("std", new STDFilesystem(System.in, System.out, System.err));
// environment.add(PermissionsProvider.KEY, PermissionsManager.ALL_PERMS);
// environment.add(Filesystem.KEY, fs);
// environment.add(ModuleRepo.KEY, ModuleRepo.ofFilesystem(fs));
// environment.add(Compiler.KEY, new JSCompiler(environment));
environment.add(EventLoop.KEY, engine);
environment.add(GlobalScope.KEY, new GlobalScope());
environment.add(DebugContext.KEY, new DebugContext());
// environment.add(EventLoop.KEY, engine);
environment.add(Compiler.KEY, Compiler.DEFAULT);
var glob = GlobalScope.get(environment);
@ -101,7 +345,19 @@ public class SimpleRepl {
}));
}
private static void initEngine() {
// var ctx = new DebugContext();
// environment.add(DebugContext.KEY, ctx);
// debugServer.targets.put("target", (ws, req) -> new SimpleDebugger(ws).attach(ctx));
engineTask = engine.start();
// debugTask = debugServer.start(new InetSocketAddress("127.0.0.1", 9229), true);
}
private static void initGlobals() throws InterruptedException, ExecutionException {
EventLoop.get(environment).pushMsg(
false, environment,
Filename.parse("jscript://init.js"), Reading.resourceToString("lib/index.js"),
Value.UNDEFINED, GlobalScope.get(environment).object, primordials(environment)
).get();
}
public static void main(String args[]) throws InterruptedException {

View File

@ -7,19 +7,19 @@ import java.util.WeakHashMap;
import me.topchetoeu.jscript.common.FunctionBody;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.environment.Key;
import me.topchetoeu.jscript.common.mapping.FunctionMap;
import me.topchetoeu.jscript.common.parsing.Filename;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.runtime.Frame;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.environment.Key;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.functions.CodeFunction;
import me.topchetoeu.jscript.runtime.values.functions.FunctionValue;
public class DebugContext {
public static final Key<DebugContext> KEY = new Key<>();
public static final Key<Void> IGNORE = new Key<>();
public static final Key<DebugContext> KEY = Key.of();
public static final Key<Void> IGNORE = Key.of();
private HashMap<Filename, String> sources;
private WeakHashMap<FunctionBody, FunctionMap> maps;

View File

@ -4,10 +4,10 @@ import java.util.List;
import me.topchetoeu.jscript.common.FunctionBody;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.mapping.FunctionMap;
import me.topchetoeu.jscript.common.parsing.Filename;
import me.topchetoeu.jscript.runtime.Frame;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
public interface DebugHandler {

View File

@ -1,144 +0,0 @@
package me.topchetoeu.jscript.runtime.environment;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.function.Supplier;
import me.topchetoeu.jscript.common.Compiler;
import me.topchetoeu.jscript.runtime.values.functions.FunctionValue;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
public class Environment {
public static final Key<Compiler> COMPILE_FUNC = new Key<>();
public static final Key<FunctionValue> REGEX_CONSTR = new Key<>();
public static final Key<Integer> MAX_STACK_COUNT = new Key<>();
public static final Key<Boolean> HIDE_STACK = new Key<>();
public static final Key<ObjectValue> OBJECT_PROTO = new Key<>();
public static final Key<ObjectValue> FUNCTION_PROTO = new Key<>();
public static final Key<ObjectValue> ARRAY_PROTO = new Key<>();
public static final Key<ObjectValue> BOOL_PROTO = new Key<>();
public static final Key<ObjectValue> NUMBER_PROTO = new Key<>();
public static final Key<ObjectValue> STRING_PROTO = new Key<>();
public static final Key<ObjectValue> SYMBOL_PROTO = new Key<>();
public static final Key<ObjectValue> ERROR_PROTO = new Key<>();
public static final Key<ObjectValue> SYNTAX_ERR_PROTO = new Key<>();
public static final Key<ObjectValue> TYPE_ERR_PROTO = new Key<>();
public static final Key<ObjectValue> RANGE_ERR_PROTO = new Key<>();
public final Environment parent;
private final Map<Key<Object>, Object> map = new HashMap<>();
private final Set<Key<Object>> hidden = new HashSet<>();
@SuppressWarnings("unchecked")
public <T> T get(Key<T> key) {
if (map.containsKey(key)) return (T)map.get(key);
else if (!hidden.contains(key) && parent != null) return parent.get(key);
else return null;
}
public boolean has(Key<?> key) {
if (map.containsKey(key)) return true;
else if (!hidden.contains(key) && parent != null) return parent.has(key);
else return false;
}
@SuppressWarnings("all")
public Set<Key<?>> keys() {
if (parent != null) {
if (map.size() == 0) return (Set)map.keySet();
var res = new HashSet();
res.addAll(parent.keys());
res.addAll(map.keySet());
return res;
}
else return (Set)map.keySet();
}
public boolean hasNotNull(Key<?> key) {
return get(key) != null;
}
public <T> T get(Key<T> key, T defaultVal) {
if (has(key)) return get(key);
else return defaultVal;
}
public <T> T get(Key<T> key, Supplier<T> defaultVal) {
if (has(key)) return get(key);
else return defaultVal.get();
}
@SuppressWarnings("unchecked")
public <T> Environment add(Key<T> key, T val) {
map.put((Key<Object>)key, val);
hidden.remove(key);
return this;
}
@SuppressWarnings("unchecked")
public Environment add(Key<Void> key) {
map.put((Key<Object>)(Key<?>)key, null);
hidden.remove(key);
return this;
}
@SuppressWarnings("all")
public Environment addAll(Map<Key<?>, ?> map) {
map.putAll((Map)map);
hidden.removeAll(map.keySet());
return this;
}
public Environment addAll(Environment env) {
this.map.putAll(env.map);
this.hidden.removeAll(env.map.keySet());
return this;
}
@SuppressWarnings("unchecked")
public Environment remove(Key<?> key) {
map.remove((Key<Object>)key);
hidden.add((Key<Object>)key);
return this;
}
public <T> Environment init(Key<T> key, T val) {
if (!has(key)) this.add(key, val);
return this;
}
public <T> Environment init(Key<T> key, Supplier<T> val) {
if (!has(key)) this.add(key, val.get());
return this;
}
public Environment child() {
return new Environment(this);
}
public Environment(Environment parent) {
this.parent = parent;
}
public Environment() {
this.parent = null;
}
public static Environment wrap(Environment ext) {
if (ext == null) return empty();
else return ext;
}
// public static Environment chain(int id, Environment ...envs) {
// var res = new Environment();
// for (var env : envs) res.addAll(env);
// return res;
// }
public static Environment empty() {
return new Environment();
}
public static int nextId() {
return new Random().nextInt();
}
}

View File

@ -1,5 +0,0 @@
package me.topchetoeu.jscript.runtime.environment;
public class Key<T> {
}

View File

@ -3,8 +3,8 @@ package me.topchetoeu.jscript.runtime.exceptions;
import java.util.ArrayList;
import java.util.List;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
@ -19,7 +19,7 @@ public class EngineException extends RuntimeException {
public final Environment ext;
public boolean visible() {
return ext == null || !ext.get(Environment.HIDE_STACK, false);
return ext == null || !ext.get(Value.HIDE_STACK, false);
}
public String toString() {
var res = "";
@ -109,18 +109,18 @@ public class EngineException extends RuntimeException {
}
public static EngineException ofError(String name, String msg) {
return new EngineException(err(name, msg, env -> env.get(Environment.ERROR_PROTO)));
return new EngineException(err(name, msg, env -> env.get(Value.ERROR_PROTO)));
}
public static EngineException ofError(String msg) {
return new EngineException(err(null, msg, env -> env.get(Environment.ERROR_PROTO)));
return new EngineException(err(null, msg, env -> env.get(Value.ERROR_PROTO)));
}
public static EngineException ofSyntax(String msg) {
return new EngineException(err(null, msg, env -> env.get(Environment.SYNTAX_ERR_PROTO)));
return new EngineException(err(null, msg, env -> env.get(Value.SYNTAX_ERR_PROTO)));
}
public static EngineException ofType(String msg) {
return new EngineException(err(null, msg, env -> env.get(Environment.TYPE_ERR_PROTO)));
return new EngineException(err(null, msg, env -> env.get(Value.TYPE_ERR_PROTO)));
}
public static EngineException ofRange(String msg) {
return new EngineException(err(null, msg, env -> env.get(Environment.RANGE_ERR_PROTO)));
return new EngineException(err(null, msg, env -> env.get(Value.RANGE_ERR_PROTO)));
}
}

View File

@ -3,18 +3,17 @@ package me.topchetoeu.jscript.runtime.scope;
import java.util.HashSet;
import java.util.Set;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.environment.Key;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.environment.Key;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
import me.topchetoeu.jscript.runtime.values.functions.FunctionValue;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
import me.topchetoeu.jscript.runtime.values.primitives.VoidValue;
public class GlobalScope {
public static final Key<GlobalScope> KEY = new Key<>();
public static final Key<GlobalScope> KEY = Key.of();
public final ObjectValue object;
@ -35,7 +34,7 @@ public class GlobalScope {
object.defineOwnMember(ext, name, FieldMember.of(val, !readonly));
}
public void define(Environment ext, boolean readonly, String ...names) {
for (var name : names) define(ext, name, new ValueVariable(readonly, VoidValue.UNDEFINED));
for (var name : names) define(ext, name, new ValueVariable(readonly, Value.UNDEFINED));
}
public void define(Environment ext, boolean readonly, FunctionValue val) {
define(ext, readonly, val.name, val);

View File

@ -1,6 +1,6 @@
package me.topchetoeu.jscript.runtime.scope;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.runtime.values.Value;
public class ValueVariable implements Variable {

View File

@ -1,6 +1,6 @@
package me.topchetoeu.jscript.runtime.scope;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.Member.FieldMember;

View File

@ -1,6 +1,6 @@
package me.topchetoeu.jscript.runtime.values;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.runtime.values.primitives.NumberValue;
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue;

View File

@ -1,10 +1,9 @@
package me.topchetoeu.jscript.runtime.values;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.runtime.values.functions.FunctionValue;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
import me.topchetoeu.jscript.runtime.values.primitives.BoolValue;
import me.topchetoeu.jscript.runtime.values.primitives.VoidValue;
public interface Member {
public static final class PropertyMember implements Member {
@ -15,7 +14,7 @@ public interface Member {
@Override public Value get(Environment env, Value self) {
if (getter != null) return getter.call(env, self);
else return VoidValue.UNDEFINED;
else return Value.UNDEFINED;
}
@Override public boolean set(Environment env, Value val, Value self) {
if (setter == null) return false;
@ -41,10 +40,10 @@ public interface Member {
@Override public ObjectValue descriptor(Environment env, Value self) {
var res = new ObjectValue();
if (getter == null) res.defineOwnMember(env, "getter", FieldMember.of(VoidValue.UNDEFINED));
if (getter == null) res.defineOwnMember(env, "getter", FieldMember.of(Value.UNDEFINED));
else res.defineOwnMember(env, "getter", FieldMember.of(getter));
if (setter == null) res.defineOwnMember(env, "setter", FieldMember.of(VoidValue.UNDEFINED));
if (setter == null) res.defineOwnMember(env, "setter", FieldMember.of(Value.UNDEFINED));
else res.defineOwnMember(env, "setter", FieldMember.of(setter));
res.defineOwnMember(env, "enumerable", FieldMember.of(BoolValue.of(enumerable)));

View File

@ -10,11 +10,12 @@ import java.util.LinkedHashMap;
import java.util.Map;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.environment.Key;
import me.topchetoeu.jscript.common.json.JSON;
import me.topchetoeu.jscript.common.json.JSONElement;
import me.topchetoeu.jscript.runtime.EventLoop;
import me.topchetoeu.jscript.runtime.debug.DebugContext;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
@ -48,8 +49,24 @@ public abstract class Value {
}
}
public static final Object NULL = new Object();
public static final Object NO_RETURN = new Object();
public static final Key<FunctionValue> REGEX_CONSTR = Key.of();
public static final Key<Integer> MAX_STACK_COUNT = Key.of();
public static final Key<Boolean> HIDE_STACK = Key.of();
public static final Key<ObjectValue> OBJECT_PROTO = Key.of();
public static final Key<ObjectValue> FUNCTION_PROTO = Key.of();
public static final Key<ObjectValue> ARRAY_PROTO = Key.of();
public static final Key<ObjectValue> BOOL_PROTO = Key.of();
public static final Key<ObjectValue> NUMBER_PROTO = Key.of();
public static final Key<ObjectValue> STRING_PROTO = Key.of();
public static final Key<ObjectValue> SYMBOL_PROTO = Key.of();
public static final Key<ObjectValue> ERROR_PROTO = Key.of();
public static final Key<ObjectValue> SYNTAX_ERR_PROTO = Key.of();
public static final Key<ObjectValue> TYPE_ERR_PROTO = Key.of();
public static final Key<ObjectValue> RANGE_ERR_PROTO = Key.of();
public static final VoidValue UNDEFINED = new VoidValue("undefined", new StringValue("undefined"));
public static final VoidValue NULL = new VoidValue("null", new StringValue("object"));
public abstract StringValue type();
public abstract boolean isPrimitive();
@ -267,7 +284,7 @@ public abstract class Value {
if (member != null) return member.get(env, obj);
}
return VoidValue.UNDEFINED;
return Value.UNDEFINED;
}
public final Value getMember(Environment env, Value key) {
return getMember(env, new KeyCache(key));
@ -437,74 +454,6 @@ public abstract class Value {
return a.toString(env).strictEquals(env, b.toString(env));
}
// @SuppressWarnings("unchecked")
// public static <T> T convert(Environment ext, Class<T> clazz) {
// if (clazz == Void.class) return null;
// if (this instanceof NativeWrapper) {
// var res = ((NativeWrapper)this).wrapped;
// if (clazz.isInstance(res)) return (T)res;
// }
// if (clazz == null || clazz == Object.class) return (T)this;
// if (this instanceof ArrayValue) {
// if (clazz.isAssignableFrom(ArrayList.class)) {
// var raw = ((ArrayValue)this).toArray();
// var res = new ArrayList<>();
// for (var i = 0; i < raw.length; i++) res.add(convert(ext, raw[i], Object.class));
// return (T)new ArrayList<>(res);
// }
// if (clazz.isAssignableFrom(HashSet.class)) {
// var raw = ((ArrayValue)this).toArray();
// var res = new HashSet<>();
// for (var i = 0; i < raw.length; i++) res.add(convert(ext, raw[i], Object.class));
// return (T)new HashSet<>(res);
// }
// if (clazz.isArray()) {
// var raw = ((ArrayValue)this).toArray();
// Object res = Array.newInstance(clazz.getComponentType(), raw.length);
// for (var i = 0; i < raw.length; i++) Array.set(res, i, convert(ext, raw[i], Object.class));
// return (T)res;
// }
// }
// if (this instanceof ObjectValue && clazz.isAssignableFrom(HashMap.class)) {
// var res = new HashMap<>();
// for (var el : ((ObjectValue)this).values.entrySet()) res.put(
// convert(ext, el.getKey(), null),
// convert(ext, el.getValue(), null)
// );
// return (T)res;
// }
// if (clazz == String.class) return (T)toString(ext, this);
// if (clazz == Boolean.class || clazz == Boolean.TYPE) return (T)(Boolean)toBoolean(this);
// if (clazz == Byte.class || clazz == byte.class) return (T)(Byte)(byte)toNumber(ext, this);
// if (clazz == Integer.class || clazz == int.class) return (T)(Integer)(int)toNumber(ext, this);
// if (clazz == Long.class || clazz == long.class) return (T)(Long)(long)toNumber(ext, this);
// if (clazz == Short.class || clazz == short.class) return (T)(Short)(short)toNumber(ext, this);
// if (clazz == Float.class || clazz == float.class) return (T)(Float)(float)toNumber(ext, this);
// if (clazz == Double.class || clazz == double.class) return (T)(Double)toNumber(ext, this);
// if (clazz == Character.class || clazz == char.class) {
// if (this instanceof Number) return (T)(Character)(char)number(this);
// else {
// var res = toString(ext, this);
// if (res.length() == 0) throw new ConvertException("\"\"", "Character");
// else return (T)(Character)res.charAt(0);
// }
// }
// if (this == null) return null;
// if (clazz.isInstance(this)) return (T)this;
// if (clazz.isAssignableFrom(NativeWrapper.class)) {
// return (T)NativeWrapper.of(ext, this);
// }
// throw new ConvertException(type(this), clazz.getSimpleName());
// }
public Iterable<Object> toIterable(Environment env) {
return () -> {
if (!(this instanceof FunctionValue)) return Collections.emptyIterator();
@ -518,7 +467,7 @@ public abstract class Value {
private void loadNext() {
if (supplier == null) value = null;
else if (consumed) {
var curr = supplier.call(env, VoidValue.UNDEFINED);
var curr = supplier.call(env, Value.UNDEFINED);
if (curr == null) { supplier = null; value = null; }
if (curr.getMember(env, new StringValue("done")).toBoolean()) { supplier = null; value = null; }
@ -561,12 +510,12 @@ public abstract class Value {
public void callWith(Environment env, Iterable<? extends Value> it) {
for (var el : it) {
this.call(env, VoidValue.UNDEFINED, el);
this.call(env, Value.UNDEFINED, el);
}
}
public void callWithAsync(Environment env, Iterable<? extends Value> it, boolean async) {
for (var el : it) {
env.get(EventLoop.KEY).pushMsg(() -> this.call(env, VoidValue.UNDEFINED, el), true);
env.get(EventLoop.KEY).pushMsg(() -> this.call(env, Value.UNDEFINED, el), true);
}
}

View File

@ -1,9 +1,8 @@
package me.topchetoeu.jscript.runtime.values.functions;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.primitives.VoidValue;
public class Arguments {
public final Value self;
@ -23,7 +22,7 @@ public class Arguments {
return get(-1);
}
public Value get(int i) {
if (i >= args.length || i < -1) return VoidValue.UNDEFINED;
if (i >= args.length || i < -1) return Value.UNDEFINED;
else if (i == -1) return self;
else return args[i];
}

View File

@ -1,8 +1,8 @@
package me.topchetoeu.jscript.runtime.values.functions;
import me.topchetoeu.jscript.common.FunctionBody;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.runtime.Frame;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.scope.ValueVariable;
import me.topchetoeu.jscript.runtime.values.Value;

View File

@ -1,6 +1,6 @@
package me.topchetoeu.jscript.runtime.values.functions;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.runtime.values.KeyCache;
import me.topchetoeu.jscript.runtime.values.Member;
import me.topchetoeu.jscript.runtime.values.Value;
@ -82,7 +82,7 @@ public abstract class FunctionValue extends ObjectValue {
}
public FunctionValue(String name, int length) {
setPrototype(Environment.FUNCTION_PROTO);
setPrototype(FUNCTION_PROTO);
if (name == null) name = "";
this.length = length;

View File

@ -1,6 +1,6 @@
package me.topchetoeu.jscript.runtime.values.functions;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.runtime.values.Value;
public class NativeFunction extends FunctionValue {

View File

@ -7,7 +7,7 @@ import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.runtime.values.KeyCache;
import me.topchetoeu.jscript.runtime.values.Member;
import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
@ -73,7 +73,7 @@ public class ArrayValue extends ObjectValue implements Iterable<Value> {
if (i < 0 || i >= size) return null;
var res = values[i];
if (res == null) return VoidValue.UNDEFINED;
if (res == null) return Value.UNDEFINED;
else return res;
}
public void set(int i, Value val) {
@ -217,7 +217,7 @@ public class ArrayValue extends ObjectValue implements Iterable<Value> {
this(16);
}
public ArrayValue(int cap) {
setPrototype(env -> env.get(Environment.ARRAY_PROTO));
setPrototype(env -> env.get(ARRAY_PROTO));
values = new Value[Math.min(cap, 16)];
size = 0;
}

View File

@ -4,8 +4,8 @@ import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.environment.Key;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.environment.Key;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.KeyCache;
import me.topchetoeu.jscript.runtime.values.Member;

View File

@ -1,6 +1,6 @@
package me.topchetoeu.jscript.runtime.values.objects;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.runtime.scope.ValueVariable;
import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.Member.FieldMember;

View File

@ -1,6 +1,6 @@
package me.topchetoeu.jscript.runtime.values.primitives;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
@ -20,7 +20,7 @@ public final class BoolValue extends PrimitiveValue {
@Override public StringValue toString(Environment ext) { return new StringValue(value ? "true" : "false"); }
@Override public ObjectValue getPrototype(Environment env) {
return env.get(Environment.BOOL_PROTO);
return env.get(BOOL_PROTO);
}
@Override public boolean strictEquals(Environment ext, Value other) {

View File

@ -1,10 +1,10 @@
package me.topchetoeu.jscript.runtime.values.primitives;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.json.JSON;
import me.topchetoeu.jscript.common.json.JSONElement;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
@ -22,7 +22,7 @@ public final class NumberValue extends PrimitiveValue {
@Override public String toString() { return JSON.stringify(JSONElement.number(value)); }
@Override public ObjectValue getPrototype(Environment env) {
return env.get(Environment.NUMBER_PROTO);
return env.get(NUMBER_PROTO);
}
@Override public CompareResult compare(Environment env, Value other) {

View File

@ -2,7 +2,7 @@ package me.topchetoeu.jscript.runtime.values.primitives;
import java.util.Map;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.runtime.values.KeyCache;
import me.topchetoeu.jscript.runtime.values.Member;
import me.topchetoeu.jscript.runtime.values.Value;

View File

@ -3,9 +3,9 @@ package me.topchetoeu.jscript.runtime.values.primitives;
import java.util.Map;
import java.util.Objects;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.runtime.values.Member;
import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
@ -38,7 +38,7 @@ public final class StringValue extends PrimitiveValue {
@Override public boolean strictEquals(Environment ext, Value other) {
return (other instanceof StringValue) && Objects.equals(((StringValue)other).value, value);
}
@Override public ObjectValue getPrototype(Environment env) { return env.get(Environment.STRING_PROTO); }
@Override public ObjectValue getPrototype(Environment env) { return env.get(STRING_PROTO); }
@Override public Map<String, Member> getOwnMembers(Environment env) {
// TODO Auto-generated method stub

View File

@ -2,7 +2,7 @@ package me.topchetoeu.jscript.runtime.values.primitives;
import java.util.HashMap;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
@ -14,7 +14,7 @@ public final class SymbolValue extends PrimitiveValue {
public final String value;
public Value key() {
return registry.containsKey(value) && registry.get(value) == this ? new StringValue(value) : VoidValue.UNDEFINED;
return registry.containsKey(value) && registry.get(value) == this ? new StringValue(value) : Value.UNDEFINED;
}
@Override public StringValue type() { return typeString; }
@ -30,7 +30,7 @@ public final class SymbolValue extends PrimitiveValue {
@Override public boolean strictEquals(Environment ext, Value other) {
return other == this;
}
@Override public ObjectValue getPrototype(Environment env) { return env.get(Environment.SYMBOL_PROTO); }
@Override public ObjectValue getPrototype(Environment env) { return env.get(SYMBOL_PROTO); }
@Override public String toString() {
if (value == null) return "Symbol()";

View File

@ -2,7 +2,7 @@ package me.topchetoeu.jscript.runtime.values.primitives;
import java.util.Map;
import me.topchetoeu.jscript.runtime.environment.Environment;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.KeyCache;
import me.topchetoeu.jscript.runtime.values.Member;
@ -10,10 +10,7 @@ import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
public final class VoidValue extends PrimitiveValue {
public static final VoidValue UNDEFINED = new VoidValue("undefined", new StringValue("undefined"));
public static final VoidValue NULL = new VoidValue("null", new StringValue("object"));
private final StringValue namestring;
private final StringValue nameString;
public final String name;
public final StringValue typeString;
@ -21,12 +18,12 @@ public final class VoidValue extends PrimitiveValue {
@Override public StringValue type() { return typeString; }
@Override public boolean toBoolean() { return false; }
@Override public NumberValue toNumber(Environment ext) { return NumberValue.NAN; }
@Override public StringValue toString(Environment ext) { return namestring; }
@Override public StringValue toString(Environment ext) { return nameString; }
@Override public Value add(Environment ext, Value other) {
if (!other.isPrimitive()) other = other.toPrimitive(ext);
if (other instanceof StringValue) return namestring.add(ext, other);
if (other instanceof StringValue) return nameString.add(ext, other);
else return NumberValue.NAN;
}
@ -52,6 +49,6 @@ public final class VoidValue extends PrimitiveValue {
public VoidValue(String name, StringValue type) {
this.name = name;
this.typeString = type;
this.namestring = new StringValue(name);
this.nameString = new StringValue(name);
}
}