clean up member logic
This commit is contained in:
parent
3e6816cb2c
commit
1f42263051
@ -1,8 +1,6 @@
|
||||
package me.topchetoeu.jscript.runtime;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
@ -11,11 +9,9 @@ import me.topchetoeu.jscript.common.environment.Key;
|
||||
import me.topchetoeu.jscript.runtime.debug.DebugContext;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.InterruptException;
|
||||
import me.topchetoeu.jscript.runtime.values.KeyCache;
|
||||
import me.topchetoeu.jscript.runtime.values.Member;
|
||||
import me.topchetoeu.jscript.runtime.values.Value;
|
||||
import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
|
||||
import me.topchetoeu.jscript.runtime.values.functions.CodeFunction;
|
||||
import me.topchetoeu.jscript.runtime.values.objects.ArrayLikeValue;
|
||||
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
|
||||
|
||||
public final class Frame {
|
||||
@ -385,31 +381,39 @@ public final class Frame {
|
||||
* Gets an array proxy of the local locals
|
||||
*/
|
||||
public ObjectValue getValStackScope() {
|
||||
return new ObjectValue() {
|
||||
@Override public Member getOwnMember(Environment env, KeyCache key) {
|
||||
var res = super.getOwnMember(env, key);
|
||||
if (res != null) return res;
|
||||
return new ArrayLikeValue() {
|
||||
@Override public Value get(int i) { return stack[i]; }
|
||||
@Override public void set(int i, Value val) { stack[i] = val; }
|
||||
@Override public boolean has(int i) { return i >= 0 && i < size(); }
|
||||
@Override public void remove(int i) { }
|
||||
|
||||
var num = key.toNumber(env);
|
||||
var i = key.toInt(env);
|
||||
@Override public int size() { return stackPtr; }
|
||||
@Override public boolean setSize(int val) { return false; }
|
||||
|
||||
if (num != i || i < 0 || i >= stackPtr) return null;
|
||||
else return new FieldMember(false, true, true) {
|
||||
@Override public Value get(Environment env, Value self) { return stack[i]; }
|
||||
@Override public boolean set(Environment env, Value val, Value self) {
|
||||
stack[i] = val;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
@Override public Set<String> getOwnMembers(Environment env, boolean onlyEnumerable) {
|
||||
var res = new LinkedHashSet<String>();
|
||||
res.addAll(super.getOwnMembers(env, onlyEnumerable));
|
||||
// @Override public Member getOwnMember(Environment env, KeyCache key) {
|
||||
// var res = super.getOwnMember(env, key);
|
||||
// if (res != null) return res;
|
||||
|
||||
for (var i = 0; i < stackPtr; i++) res.add(i + "");
|
||||
// var num = key.toNumber(env);
|
||||
// var i = key.toInt(env);
|
||||
|
||||
return res;
|
||||
}
|
||||
// if (num != i || i < 0 || i >= stackPtr) return null;
|
||||
// else return new FieldMember(this, false, true, true) {
|
||||
// @Override public Value get(Environment env, Value self) { return stack[i]; }
|
||||
// @Override public boolean set(Environment env, Value val, Value self) {
|
||||
// stack[i] = val;
|
||||
// return true;
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
// @Override public Set<String> getOwnMembers(Environment env, boolean onlyEnumerable) {
|
||||
// var res = new LinkedHashSet<String>();
|
||||
// res.addAll(super.getOwnMembers(env, onlyEnumerable));
|
||||
|
||||
// for (var i = 0; i < stackPtr; i++) res.add(i + "");
|
||||
|
||||
// return res;
|
||||
// }
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@ public class InstructionRunner {
|
||||
else if (setterVal instanceof FunctionValue) setter = (FunctionValue)setterVal;
|
||||
else throw EngineException.ofType("Setter must be a function or undefined.");
|
||||
|
||||
obj.defineOwnMember(env, key, new PropertyMember(getter, setter, true, true));
|
||||
obj.defineOwnMember(env, key, new PropertyMember(obj, getter, setter, true, true));
|
||||
|
||||
frame.push(obj);
|
||||
frame.codePtr++;
|
||||
|
@ -8,7 +8,6 @@ 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.exceptions.EngineException;
|
||||
import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
|
||||
import me.topchetoeu.jscript.runtime.values.Value;
|
||||
import me.topchetoeu.jscript.runtime.values.objects.ArrayValue;
|
||||
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
|
||||
@ -27,7 +26,7 @@ public class JSONConverter {
|
||||
var res = new ObjectValue();
|
||||
|
||||
for (var el : val.map().entrySet()) {
|
||||
res.defineOwnMember(null, el.getKey(), FieldMember.of(toJs(el.getValue())));
|
||||
res.defineOwnMember(null, el.getKey(), toJs(el.getValue()));
|
||||
}
|
||||
|
||||
return res;
|
||||
|
@ -171,9 +171,7 @@ public class SimpleRepl {
|
||||
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;
|
||||
return BoolValue.of(obj.defineOwnMember(args.env, key, FieldMember.of(obj, value, configurable, enumerable, writable)));
|
||||
}));
|
||||
res.defineOwnMember(env, "defineProperty", new NativeFunction(args -> {
|
||||
var obj = (ObjectValue)args.get(0);
|
||||
@ -183,9 +181,7 @@ public class SimpleRepl {
|
||||
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;
|
||||
return BoolValue.of(obj.defineOwnMember(args.env, key, new PropertyMember(obj, getter, setter, configurable, enumerable)));
|
||||
}));
|
||||
res.defineOwnMember(env, "getPrototype", new NativeFunction(args -> {
|
||||
return args.get(0).getPrototype(env);
|
||||
|
@ -6,7 +6,6 @@ import java.util.List;
|
||||
import me.topchetoeu.jscript.common.environment.Environment;
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
import me.topchetoeu.jscript.runtime.values.Value;
|
||||
import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
|
||||
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
|
||||
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue.PrototypeProvider;
|
||||
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
|
||||
@ -98,8 +97,8 @@ public class EngineException extends RuntimeException {
|
||||
|
||||
if (msg == null) msg = "";
|
||||
|
||||
if (name != null) res.defineOwnMember(Environment.empty(), "name", FieldMember.of(new StringValue(name)));
|
||||
res.defineOwnMember(Environment.empty(), "message", FieldMember.of(new StringValue(msg)));
|
||||
if (name != null) res.defineOwnMember(Environment.empty(), "name", new StringValue(name));
|
||||
res.defineOwnMember(Environment.empty(), "message", new StringValue(msg));
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -7,10 +7,11 @@ import me.topchetoeu.jscript.runtime.values.primitives.BoolValue;
|
||||
|
||||
public interface Member {
|
||||
public static final class PropertyMember implements Member {
|
||||
public final FunctionValue getter;
|
||||
public final FunctionValue setter;
|
||||
public final boolean configurable;
|
||||
public final boolean enumerable;
|
||||
public final Value self;
|
||||
public FunctionValue getter;
|
||||
public FunctionValue setter;
|
||||
public boolean configurable;
|
||||
public boolean enumerable;
|
||||
|
||||
@Override public Value get(Environment env, Value self) {
|
||||
if (getter != null) return getter.call(env, false, "", self);
|
||||
@ -22,36 +23,51 @@ public interface Member {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public boolean configurable() { return configurable; }
|
||||
@Override public boolean configurable() { return configurable && self.getState().configurable; }
|
||||
@Override public boolean enumerable() { return enumerable; }
|
||||
|
||||
@Override public boolean configure(Environment env, Member newMember, Value self) {
|
||||
if (!(newMember instanceof PropertyMember)) return false;
|
||||
var prop = (PropertyMember)newMember;
|
||||
@Override public boolean redefine(Environment env, Member newMember, Value self) {
|
||||
// If the given member isn't a property, we can't redefine
|
||||
if (!(newMember instanceof PropertyMember prop)) return false;
|
||||
|
||||
if (prop.configurable != configurable) return false;
|
||||
if (configurable()) {
|
||||
// We will overlay the getters and setters of the new member
|
||||
enumerable = prop.enumerable;
|
||||
configurable = prop.configurable;
|
||||
|
||||
if (prop.getter != null) getter = prop.getter;
|
||||
if (prop.setter != null) setter = prop.setter;
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// We will pretend that a redefinition has occurred if the two members match exactly
|
||||
if (prop.configurable() != configurable()) return false;
|
||||
if (prop.enumerable != enumerable) return false;
|
||||
if (prop.getter != getter || prop.setter != setter) return false;
|
||||
|
||||
if (prop.getter == getter) return true;
|
||||
if (prop.setter == setter) return true;
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override public ObjectValue descriptor(Environment env, Value self) {
|
||||
var res = new ObjectValue();
|
||||
|
||||
if (getter == null) res.defineOwnMember(env, "getter", FieldMember.of(Value.UNDEFINED));
|
||||
else res.defineOwnMember(env, "getter", FieldMember.of(getter));
|
||||
// Don't touch the ordering, as it's emulating V8
|
||||
|
||||
if (setter == null) res.defineOwnMember(env, "setter", FieldMember.of(Value.UNDEFINED));
|
||||
else res.defineOwnMember(env, "setter", FieldMember.of(setter));
|
||||
if (getter == null) res.defineOwnMember(env, "getter", Value.UNDEFINED);
|
||||
else res.defineOwnMember(env, "getter", getter);
|
||||
|
||||
res.defineOwnMember(env, "enumerable", FieldMember.of(BoolValue.of(enumerable)));
|
||||
res.defineOwnMember(env, "configurable", FieldMember.of(BoolValue.of(configurable)));
|
||||
if (setter == null) res.defineOwnMember(env, "setter", Value.UNDEFINED);
|
||||
else res.defineOwnMember(env, "setter", setter);
|
||||
|
||||
res.defineOwnMember(env, "enumerable", BoolValue.of(enumerable));
|
||||
res.defineOwnMember(env, "configurable", BoolValue.of(configurable));
|
||||
return res;
|
||||
}
|
||||
|
||||
public PropertyMember(FunctionValue getter, FunctionValue setter, boolean configurable, boolean enumerable) {
|
||||
public PropertyMember(Value self, FunctionValue getter, FunctionValue setter, boolean configurable, boolean enumerable) {
|
||||
this.self = self;
|
||||
this.getter = getter;
|
||||
this.setter = setter;
|
||||
this.configurable = configurable;
|
||||
@ -69,60 +85,87 @@ public interface Member {
|
||||
value = val;
|
||||
return true;
|
||||
}
|
||||
public SimpleFieldMember(Value value, boolean configurable, boolean enumerable, boolean writable) {
|
||||
super(configurable, enumerable, writable);
|
||||
public SimpleFieldMember(Value self, Value value, boolean configurable, boolean enumerable, boolean writable) {
|
||||
super(self, configurable, enumerable, writable);
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public final Value self;
|
||||
public boolean configurable;
|
||||
public boolean enumerable;
|
||||
public boolean writable;
|
||||
|
||||
@Override public final boolean configurable() { return configurable; }
|
||||
@Override public final boolean configurable() { return configurable && self.getState().configurable; }
|
||||
@Override public final boolean enumerable() { return enumerable; }
|
||||
@Override public final boolean configure(Environment env, Member newMember, Value self) {
|
||||
if (!(newMember instanceof FieldMember)) return false;
|
||||
var field = (FieldMember)newMember;
|
||||
public final boolean writable() { return writable && self.getState().writable; }
|
||||
|
||||
if (field.configurable != configurable) return false;
|
||||
if (field.enumerable != enumerable) return false;
|
||||
if (!writable) return field.get(env, self).equals(get(env, self));
|
||||
@Override public final boolean redefine(Environment env, Member newMember, Value self) {
|
||||
// If the given member isn't a field, we can't redefine
|
||||
if (!(newMember instanceof FieldMember field)) return false;
|
||||
|
||||
set(env, field.get(env, self), self);
|
||||
writable = field.writable;
|
||||
if (configurable()) {
|
||||
configurable = field.configurable;
|
||||
enumerable = field.enumerable;
|
||||
writable = field.enumerable;
|
||||
|
||||
// We will try to set a new value. However, the underlying field might be immutably readonly
|
||||
// In such case, we will silently fail, since this is not covered by the specification
|
||||
if (!set(env, field.get(env, self), self)) writable = false;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// New field settings must be an exact match
|
||||
if (configurable() != field.configurable()) return false;
|
||||
if (enumerable() != field.enumerable()) return false;
|
||||
|
||||
if (!writable()) {
|
||||
// If the field isn't writable, the redefinition should be an exact match
|
||||
if (field.writable()) return false;
|
||||
if (field.get(env, self).equals(this.get(env, self))) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// Writable non-configurable fields may be made readonly or their values may be changed
|
||||
writable = field.writable;
|
||||
|
||||
if (!set(env, field.get(env, self), self)) writable = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override public ObjectValue descriptor(Environment env, Value self) {
|
||||
var res = new ObjectValue();
|
||||
res.defineOwnMember(env, "value", FieldMember.of(get(env, self)));
|
||||
res.defineOwnMember(env, "writable", FieldMember.of(BoolValue.of(writable)));
|
||||
res.defineOwnMember(env, "enumerable", FieldMember.of(BoolValue.of(enumerable)));
|
||||
res.defineOwnMember(env, "configurable", FieldMember.of(BoolValue.of(configurable)));
|
||||
res.defineOwnMember(env, "value", get(env, self));
|
||||
res.defineOwnMember(env, "writable", BoolValue.of(writable));
|
||||
res.defineOwnMember(env, "enumerable", BoolValue.of(enumerable));
|
||||
res.defineOwnMember(env, "configurable", BoolValue.of(configurable));
|
||||
return res;
|
||||
}
|
||||
|
||||
public FieldMember(boolean configurable, boolean enumerable, boolean writable) {
|
||||
public FieldMember(Value self, boolean configurable, boolean enumerable, boolean writable) {
|
||||
this.self = self;
|
||||
this.configurable = configurable;
|
||||
this.enumerable = enumerable;
|
||||
this.writable = writable;
|
||||
}
|
||||
|
||||
public static FieldMember of(Value value) {
|
||||
return new SimpleFieldMember(value, true, true, true);
|
||||
public static FieldMember of(Value self, Value value) {
|
||||
return new SimpleFieldMember(self, value, true, true, true);
|
||||
}
|
||||
public static FieldMember of(Value value, boolean writable) {
|
||||
return new SimpleFieldMember(value, true, true, writable);
|
||||
public static FieldMember of(Value self, Value value, boolean writable) {
|
||||
return new SimpleFieldMember(self, value, true, true, writable);
|
||||
}
|
||||
public static FieldMember of(Value value, boolean configurable, boolean enumerable, boolean writable) {
|
||||
return new SimpleFieldMember(value, configurable, enumerable, writable);
|
||||
public static FieldMember of(Value self, Value value, boolean configurable, boolean enumerable, boolean writable) {
|
||||
return new SimpleFieldMember(self, value, configurable, enumerable, writable);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean configurable();
|
||||
public boolean enumerable();
|
||||
public boolean configure(Environment env, Member newMember, Value self);
|
||||
public boolean redefine(Environment env, Member newMember, Value self);
|
||||
public ObjectValue descriptor(Environment env, Value self);
|
||||
|
||||
public Value get(Environment env, Value self);
|
||||
|
@ -20,6 +20,7 @@ import me.topchetoeu.jscript.runtime.debug.DebugContext;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
||||
import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
|
||||
import me.topchetoeu.jscript.runtime.values.Member.PropertyMember;
|
||||
import me.topchetoeu.jscript.runtime.values.functions.FunctionValue;
|
||||
import me.topchetoeu.jscript.runtime.values.functions.NativeFunction;
|
||||
import me.topchetoeu.jscript.runtime.values.objects.ArrayValue;
|
||||
@ -31,21 +32,21 @@ import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue;
|
||||
import me.topchetoeu.jscript.runtime.values.primitives.VoidValue;
|
||||
|
||||
public abstract class Value {
|
||||
public static enum CompareResult {
|
||||
NOT_EQUAL,
|
||||
EQUAL,
|
||||
LESS,
|
||||
GREATER;
|
||||
public static enum State {
|
||||
NORMAL(true, true, true),
|
||||
NON_EXTENDABLE(false, true, true),
|
||||
SEALED(false, false, true),
|
||||
FROZEN(false, false, false);
|
||||
|
||||
public boolean less() { return this == LESS; }
|
||||
public boolean greater() { return this == GREATER; }
|
||||
public boolean lessOrEqual() { return this == LESS || this == EQUAL; }
|
||||
public boolean greaterOrEqual() { return this == GREATER || this == EQUAL; }
|
||||
|
||||
public static CompareResult from(int cmp) {
|
||||
if (cmp < 0) return LESS;
|
||||
if (cmp > 0) return GREATER;
|
||||
return EQUAL;
|
||||
public final boolean extendable;
|
||||
public final boolean configurable;
|
||||
public final boolean writable;
|
||||
|
||||
private State(boolean extendable, boolean configurable, boolean writable) {
|
||||
this.extendable = extendable;
|
||||
this.writable = writable;
|
||||
this.configurable = configurable;
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,6 +133,12 @@ public abstract class Value {
|
||||
public abstract ObjectValue getPrototype(Environment env);
|
||||
public abstract boolean setPrototype(Environment env, ObjectValue val);
|
||||
|
||||
public abstract State getState();
|
||||
|
||||
public abstract void preventExtensions();
|
||||
public abstract void seal();
|
||||
public abstract void freeze();
|
||||
|
||||
public final Member getOwnMember(Environment env, Value key) {
|
||||
return getOwnMember(env, new KeyCache(key));
|
||||
}
|
||||
@ -159,19 +166,19 @@ public abstract class Value {
|
||||
}
|
||||
|
||||
public final boolean defineOwnMember(Environment env, KeyCache key, Value val) {
|
||||
return defineOwnMember(env, key, FieldMember.of(val));
|
||||
return defineOwnMember(env, key, FieldMember.of(this, val));
|
||||
}
|
||||
public final boolean defineOwnMember(Environment env, Value key, Value val) {
|
||||
return defineOwnMember(env, new KeyCache(key), FieldMember.of(val));
|
||||
return defineOwnMember(env, new KeyCache(key), val);
|
||||
}
|
||||
public final boolean defineOwnMember(Environment env, String key, Value val) {
|
||||
return defineOwnMember(env, new KeyCache(key), FieldMember.of(val));
|
||||
return defineOwnMember(env, new KeyCache(key), val);
|
||||
}
|
||||
public final boolean defineOwnMember(Environment env, int key, Value val) {
|
||||
return defineOwnMember(env, new KeyCache(key), FieldMember.of(val));
|
||||
return defineOwnMember(env, new KeyCache(key), val);
|
||||
}
|
||||
public final boolean defineOwnMember(Environment env, double key, Value val) {
|
||||
return defineOwnMember(env, new KeyCache(key), FieldMember.of(val));
|
||||
return defineOwnMember(env, new KeyCache(key), val);
|
||||
}
|
||||
|
||||
public final boolean deleteOwnMember(Environment env, Value key) {
|
||||
@ -238,7 +245,7 @@ public abstract class Value {
|
||||
}
|
||||
}
|
||||
|
||||
if (defineOwnMember(env, key, FieldMember.of(val))) {
|
||||
if (defineOwnMember(env, key, val)) {
|
||||
if (val instanceof FunctionValue) ((FunctionValue)val).setName(key.toString(env));
|
||||
return true;
|
||||
}
|
||||
@ -475,7 +482,13 @@ public abstract class Value {
|
||||
|
||||
var member = obj.getOwnMember(env, entry);
|
||||
if (member instanceof FieldMember field) res.append(field.get(env, obj).toReadable(env, passed, tab + 1));
|
||||
else res.append("[property]");
|
||||
else if (member instanceof PropertyMember prop) {
|
||||
if (prop.getter == null && prop.setter == null) res.append("[No accessors]");
|
||||
else if (prop.getter == null) res.append("[Setter]");
|
||||
else if (prop.setter == null) res.append("[Getter]");
|
||||
else res.append("[Getter/Setter]");
|
||||
}
|
||||
else res.append("[???]");
|
||||
|
||||
res.append(",\n");
|
||||
}
|
||||
@ -485,7 +498,13 @@ public abstract class Value {
|
||||
|
||||
var member = obj.getOwnMember(env, entry);
|
||||
if (member instanceof FieldMember field) res.append(field.get(env, obj).toReadable(env, passed, tab + 1));
|
||||
else res.append("[property]");
|
||||
else if (member instanceof PropertyMember prop) {
|
||||
if (prop.getter == null && prop.setter == null) res.append("[No accessors]");
|
||||
else if (prop.getter == null) res.append("[Setter]");
|
||||
else if (prop.setter == null) res.append("[Getter]");
|
||||
else res.append("[Getter/Setter]");
|
||||
}
|
||||
else res.append("[???]");
|
||||
|
||||
res.append(",\n");
|
||||
}
|
||||
@ -520,8 +539,8 @@ public abstract class Value {
|
||||
return new NativeFunction("", args -> {
|
||||
var obj = new ObjectValue();
|
||||
|
||||
if (!it.hasNext()) obj.defineOwnMember(args.env, "done", FieldMember.of(BoolValue.TRUE));
|
||||
else obj.defineOwnMember(args.env, "value", FieldMember.of(it.next()));
|
||||
if (!it.hasNext()) obj.defineOwnMember(args.env, "done", BoolValue.TRUE);
|
||||
else obj.defineOwnMember(args.env, "value", it.next());
|
||||
|
||||
return obj;
|
||||
});
|
||||
|
@ -10,6 +10,8 @@ import me.topchetoeu.jscript.runtime.values.primitives.NumberValue;
|
||||
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
|
||||
|
||||
public abstract class FunctionValue extends ObjectValue {
|
||||
private static final StringValue typeString = new StringValue("function");
|
||||
|
||||
public String name = "";
|
||||
public int length;
|
||||
public Value prototype = new ObjectValue();
|
||||
@ -17,7 +19,7 @@ public abstract class FunctionValue extends ObjectValue {
|
||||
public boolean enableCall = true;
|
||||
public boolean enableNew = true;
|
||||
|
||||
private final FieldMember nameField = new FieldMember(true, false, false) {
|
||||
private final FieldMember nameField = new FieldMember(this, true, false, false) {
|
||||
@Override public Value get(Environment env, Value self) {
|
||||
if (name == null) return new StringValue("");
|
||||
return new StringValue(name);
|
||||
@ -27,7 +29,7 @@ public abstract class FunctionValue extends ObjectValue {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
private final FieldMember lengthField = new FieldMember(true, false, false) {
|
||||
private final FieldMember lengthField = new FieldMember(this, true, false, false) {
|
||||
@Override public Value get(Environment env, Value self) {
|
||||
return new NumberValue(length);
|
||||
}
|
||||
@ -35,7 +37,7 @@ public abstract class FunctionValue extends ObjectValue {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
private final FieldMember prototypeField = new FieldMember(false, false, true) {
|
||||
private final FieldMember prototypeField = new FieldMember(this, false, false, true) {
|
||||
@Override public Value get(Environment env, Value self) {
|
||||
return prototype;
|
||||
}
|
||||
@ -77,6 +79,8 @@ public abstract class FunctionValue extends ObjectValue {
|
||||
}
|
||||
}
|
||||
|
||||
@Override public StringValue type() { return typeString; }
|
||||
|
||||
public void setName(String val) {
|
||||
if (this.name == null || this.name.equals("")) this.name = val;
|
||||
}
|
||||
@ -88,7 +92,7 @@ public abstract class FunctionValue extends ObjectValue {
|
||||
this.length = length;
|
||||
this.name = name;
|
||||
|
||||
prototype.defineOwnMember(null, "constructor", FieldMember.of(this));
|
||||
prototype.defineOwnMember(null, "constructor", this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,13 +20,6 @@ public class ObjectValue extends Value {
|
||||
public ObjectValue get(Environment env);
|
||||
}
|
||||
|
||||
public static enum State {
|
||||
NORMAL,
|
||||
NO_EXTENSIONS,
|
||||
SEALED,
|
||||
FROZEN,
|
||||
}
|
||||
|
||||
public static class Property {
|
||||
public final FunctionValue getter;
|
||||
public final FunctionValue setter;
|
||||
@ -41,8 +34,6 @@ public class ObjectValue extends Value {
|
||||
|
||||
protected PrototypeProvider prototype;
|
||||
|
||||
public boolean extensible = true;
|
||||
|
||||
public LinkedHashMap<String, Member> members = new LinkedHashMap<>();
|
||||
public LinkedHashMap<SymbolValue, Member> symbolMembers = new LinkedHashMap<>();
|
||||
|
||||
@ -70,9 +61,17 @@ public class ObjectValue extends Value {
|
||||
@Override public NumberValue toNumber(Environment env) { return toPrimitive(env).toNumber(env); }
|
||||
@Override public StringValue type() { return typeString; }
|
||||
|
||||
private State state = State.NORMAL;
|
||||
|
||||
@Override public State getState() { return state; }
|
||||
|
||||
public final void preventExtensions() {
|
||||
extensible = false;
|
||||
if (state == State.NORMAL) state = State.NON_EXTENDABLE;
|
||||
}
|
||||
public final void seal() {
|
||||
if (state == State.NORMAL || state == State.NON_EXTENDABLE) state = State.SEALED;
|
||||
}
|
||||
@Override public final void freeze() { state = State.FROZEN; }
|
||||
|
||||
@Override public Member getOwnMember(Environment env, KeyCache key) {
|
||||
if (key.isSymbol()) return symbolMembers.get(key.toSymbol());
|
||||
@ -80,7 +79,7 @@ public class ObjectValue extends Value {
|
||||
}
|
||||
@Override public boolean defineOwnMember(Environment env, KeyCache key, Member member) {
|
||||
var old = getOwnMember(env, key);
|
||||
if (old != null && old.configure(env, member, this)) return true;
|
||||
if (old != null && old.redefine(env, member, this)) return true;
|
||||
if (old != null && !old.configurable()) return false;
|
||||
|
||||
if (key.isSymbol()) symbolMembers.put(key.toSymbol(), member);
|
||||
@ -89,11 +88,11 @@ public class ObjectValue extends Value {
|
||||
return true;
|
||||
}
|
||||
@Override public boolean deleteOwnMember(Environment env, KeyCache key) {
|
||||
if (!extensible) return false;
|
||||
if (!getState().extendable) return false;
|
||||
|
||||
var member = getOwnMember(env, key);
|
||||
if (member == null) return true;
|
||||
if (member.configurable()) return false;
|
||||
if (!member.configurable()) return false;
|
||||
|
||||
if (key.isSymbol()) symbolMembers.remove(key.toSymbol());
|
||||
else members.remove(key.toString(env));
|
||||
@ -134,12 +133,12 @@ public class ObjectValue extends Value {
|
||||
}
|
||||
|
||||
public final boolean setPrototype(PrototypeProvider val) {
|
||||
if (!extensible) return false;
|
||||
if (!getState().extendable) return false;
|
||||
prototype = val;
|
||||
return true;
|
||||
}
|
||||
public final boolean setPrototype(Key<ObjectValue> key) {
|
||||
if (!extensible) return false;
|
||||
if (!getState().extendable) return false;
|
||||
prototype = env -> env.get(key);
|
||||
return true;
|
||||
}
|
||||
|
@ -5,20 +5,20 @@ import me.topchetoeu.jscript.runtime.values.Value;
|
||||
import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
|
||||
|
||||
public final class ScopeValue extends ObjectValue {
|
||||
private class VariableField extends FieldMember {
|
||||
private static class VariableField extends FieldMember {
|
||||
private int i;
|
||||
|
||||
public VariableField(int i) {
|
||||
super(false, true, true);
|
||||
public VariableField(int i, ScopeValue self) {
|
||||
super(self, false, true, true);
|
||||
this.i = i;
|
||||
}
|
||||
|
||||
@Override public Value get(Environment env, Value self) {
|
||||
return variables[i][0];
|
||||
return ((ScopeValue)self).variables[i][0];
|
||||
}
|
||||
|
||||
@Override public boolean set(Environment env, Value val, Value self) {
|
||||
variables[i][0] = val;
|
||||
((ScopeValue)self).variables[i][0] = val;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -28,7 +28,7 @@ public final class ScopeValue extends ObjectValue {
|
||||
public ScopeValue(Value[][] variables, String[] names) {
|
||||
this.variables = variables;
|
||||
for (var i = 0; i < names.length && i < variables.length; i++) {
|
||||
defineOwnMember(Environment.empty(), i, new VariableField(i));
|
||||
defineOwnMember(Environment.empty(), i, new VariableField(i, this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,4 +19,10 @@ public abstract class PrimitiveValue extends Value {
|
||||
@Override public Member getOwnMember(Environment env, KeyCache key) { return null; }
|
||||
@Override public Set<String> getOwnMembers(Environment env, boolean onlyEnumerable) { return Set.of(); }
|
||||
@Override public Set<SymbolValue> getOwnSymbolMembers(Environment env, boolean onlyEnumerable) { return Set.of(); }
|
||||
|
||||
@Override public State getState() { return State.FROZEN; }
|
||||
|
||||
@Override public void preventExtensions() {}
|
||||
@Override public void seal() {}
|
||||
@Override public void freeze() {}
|
||||
}
|
||||
|
@ -40,10 +40,10 @@ public final class StringValue extends PrimitiveValue {
|
||||
var i = key.toInt(env);
|
||||
|
||||
if (i == num && i >= 0 && i < value.length()) {
|
||||
return FieldMember.of(new StringValue(value.charAt(i) + ""), false, true, false);
|
||||
return FieldMember.of(this, new StringValue(value.charAt(i) + ""), false, true, false);
|
||||
}
|
||||
else if (key.toString(env).equals("length")) {
|
||||
return FieldMember.of(new NumberValue(value.length()), false, false, false);
|
||||
return FieldMember.of(this, new NumberValue(value.length()), false, false, false);
|
||||
}
|
||||
else return super.getOwnMember(env, key);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user