fix: make member algorithms correct
This commit is contained in:
parent
ff4aa3dcfd
commit
00aeef5321
@ -2,13 +2,12 @@ package me.topchetoeu.jscript.runtime;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Operation;
|
||||
import me.topchetoeu.jscript.common.environment.Environment;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
||||
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.CodeFunction;
|
||||
import me.topchetoeu.jscript.runtime.values.functions.FunctionValue;
|
||||
@ -60,8 +59,8 @@ public class InstructionRunner {
|
||||
else if (val instanceof FunctionValue func) accessor = func;
|
||||
else throw EngineException.ofType("Getter must be a function or undefined");
|
||||
|
||||
if ((boolean)instr.get(0)) obj.defineOwnMember(env, key, new PropertyMember(obj, null, accessor, true, true));
|
||||
else obj.defineOwnMember(env, key, new PropertyMember(obj, accessor, null, true, true));
|
||||
if ((boolean)instr.get(0)) obj.defineOwnProperty(env, key, null, Optional.of(accessor), true, true);
|
||||
else obj.defineOwnProperty(env, key, Optional.of(accessor), null, true, true);
|
||||
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
@ -71,7 +70,7 @@ public class InstructionRunner {
|
||||
var key = frame.pop();
|
||||
var obj = frame.pop();
|
||||
|
||||
obj.defineOwnMember(env, key, FieldMember.of(obj, val, true, true, true));
|
||||
obj.defineOwnField(env, key, val, true, true, true);
|
||||
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
@ -86,7 +85,7 @@ public class InstructionRunner {
|
||||
|
||||
for (var el : members) {
|
||||
var obj = new ObjectValue();
|
||||
obj.defineOwnMember(env, "value", StringValue.of(el));
|
||||
obj.defineOwnField(env, "value", StringValue.of(el));
|
||||
frame.push(obj);
|
||||
}
|
||||
|
||||
@ -405,13 +404,13 @@ public class InstructionRunner {
|
||||
break;
|
||||
|
||||
case SHIFT_LEFT:
|
||||
res = Value.shiftLeft(env, stack[ptr], stack[ptr]);
|
||||
res = Value.shiftLeft(env, stack[ptr - 1], stack[ptr]);
|
||||
break;
|
||||
case SHIFT_RIGHT:
|
||||
res = Value.shiftRight(env, stack[ptr], stack[ptr]);
|
||||
res = Value.shiftRight(env, stack[ptr - 1], stack[ptr]);
|
||||
break;
|
||||
case USHIFT_RIGHT:
|
||||
res = Value.unsignedShiftRight(env, stack[ptr], stack[ptr]);
|
||||
res = Value.unsignedShiftRight(env, stack[ptr - 1], stack[ptr]);
|
||||
break;
|
||||
|
||||
case IN:
|
||||
@ -433,7 +432,7 @@ public class InstructionRunner {
|
||||
var name = (String)instr.get(0);
|
||||
|
||||
if (!Value.global(env).hasMember(env, name, false)) {
|
||||
if (!Value.global(env).defineOwnMember(env, name, Value.UNDEFINED)) throw EngineException.ofError("Couldn't define variable " + name);
|
||||
if (!Value.global(env).defineOwnField(env, name, Value.UNDEFINED)) throw EngineException.ofError("Couldn't define variable " + name);
|
||||
}
|
||||
|
||||
frame.codePtr++;
|
||||
|
@ -97,8 +97,8 @@ public class EngineException extends RuntimeException {
|
||||
|
||||
if (msg == null) msg = "";
|
||||
|
||||
if (name != null) res.defineOwnMember(Environment.empty(), "name", StringValue.of(name));
|
||||
res.defineOwnMember(Environment.empty(), "message", StringValue.of(msg));
|
||||
if (name != null) res.defineOwnField(Environment.empty(), "name", StringValue.of(name));
|
||||
res.defineOwnField(Environment.empty(), "message", StringValue.of(msg));
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
package me.topchetoeu.jscript.runtime.values;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import me.topchetoeu.jscript.common.environment.Environment;
|
||||
import me.topchetoeu.jscript.runtime.values.functions.FunctionValue;
|
||||
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
|
||||
@ -26,25 +28,27 @@ public interface Member {
|
||||
@Override public boolean configurable() { return configurable && self.getState().configurable; }
|
||||
@Override public boolean enumerable() { return enumerable; }
|
||||
|
||||
@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 (configurable()) {
|
||||
public boolean reconfigure(
|
||||
Environment env, Value self,
|
||||
Optional<FunctionValue> get, Optional<FunctionValue> set,
|
||||
Boolean enumerable, Boolean configurable
|
||||
) {
|
||||
if (this.configurable) {
|
||||
// We will overlay the getters and setters of the new member
|
||||
enumerable = prop.enumerable;
|
||||
configurable = prop.configurable;
|
||||
if (enumerable != null) this.enumerable = enumerable;
|
||||
if (configurable != null) this.configurable = configurable;
|
||||
|
||||
if (prop.getter != null) getter = prop.getter;
|
||||
if (prop.setter != null) setter = prop.setter;
|
||||
if (get != null) this.getter = get.orElse(null);
|
||||
if (set != null) this.setter = set.orElse(null);
|
||||
|
||||
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 (configurable != null && this.configurable != configurable) return false;
|
||||
if (enumerable != null && this.enumerable != enumerable) return false;
|
||||
if (get != null && get.orElse(null) != getter) return false;
|
||||
if (set != null && set.orElse(null) != setter) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -55,14 +59,14 @@ public interface Member {
|
||||
|
||||
// Don't touch the ordering, as it's emulating V8
|
||||
|
||||
if (getter == null) res.defineOwnMember(env, "getter", Value.UNDEFINED);
|
||||
else res.defineOwnMember(env, "getter", getter);
|
||||
if (getter == null) res.defineOwnField(env, "getter", Value.UNDEFINED);
|
||||
else res.defineOwnField(env, "getter", getter);
|
||||
|
||||
if (setter == null) res.defineOwnMember(env, "setter", Value.UNDEFINED);
|
||||
else res.defineOwnMember(env, "setter", setter);
|
||||
if (setter == null) res.defineOwnField(env, "setter", Value.UNDEFINED);
|
||||
else res.defineOwnField(env, "setter", setter);
|
||||
|
||||
res.defineOwnMember(env, "enumerable", BoolValue.of(enumerable));
|
||||
res.defineOwnMember(env, "configurable", BoolValue.of(configurable));
|
||||
res.defineOwnField(env, "enumerable", BoolValue.of(enumerable));
|
||||
res.defineOwnField(env, "configurable", BoolValue.of(configurable));
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -73,6 +77,13 @@ public interface Member {
|
||||
this.configurable = configurable;
|
||||
this.enumerable = enumerable;
|
||||
}
|
||||
public PropertyMember(Value self, Optional<FunctionValue> getter, Optional<FunctionValue> setter, Boolean configurable, Boolean enumerable) {
|
||||
this.self = self;
|
||||
this.getter = getter == null ? null : getter.orElse(null);
|
||||
this.setter = setter == null ? null : setter.orElse(null);
|
||||
this.configurable = configurable == null ? false : configurable;
|
||||
this.enumerable = enumerable == null ? false : enumerable;
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class FieldMember implements Member {
|
||||
@ -85,8 +96,10 @@ public interface Member {
|
||||
value = val;
|
||||
return true;
|
||||
}
|
||||
public SimpleFieldMember(Value self, Value value, boolean configurable, boolean enumerable, boolean writable) {
|
||||
public SimpleFieldMember(Value self, Value value, Boolean configurable, Boolean enumerable, Boolean writable) {
|
||||
super(self, configurable, enumerable, writable);
|
||||
if (value == null) value = Value.UNDEFINED;
|
||||
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@ -100,37 +113,39 @@ public interface Member {
|
||||
@Override public final boolean enumerable() { return enumerable; }
|
||||
public final boolean writable() { return writable && self.getState().writable; }
|
||||
|
||||
@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;
|
||||
|
||||
if (configurable()) {
|
||||
configurable = field.configurable;
|
||||
enumerable = field.enumerable;
|
||||
writable = field.enumerable;
|
||||
|
||||
public final boolean reconfigure(
|
||||
Environment env, Value self, Value val,
|
||||
Boolean writable, Boolean enumerable, Boolean configurable
|
||||
) {
|
||||
if (this.configurable) {
|
||||
if (writable != null) this.writable = writable;
|
||||
if (enumerable != null) this.enumerable = enumerable;
|
||||
if (configurable != null) this.configurable = configurable;
|
||||
if (val != null) {
|
||||
// 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;
|
||||
if (!set(env, val, 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 (configurable != null && this.configurable != configurable) return false;
|
||||
if (enumerable != null && this.enumerable != enumerable) return false;
|
||||
|
||||
if (!writable()) {
|
||||
if (this.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;
|
||||
if (writable != null && writable) return false;
|
||||
if (val != null && val.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 (writable != null) this.writable = writable;
|
||||
|
||||
if (!set(env, field.get(env, self), self)) writable = false;
|
||||
if (!set(env, val, self)) writable = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -138,14 +153,18 @@ public interface Member {
|
||||
|
||||
@Override public ObjectValue descriptor(Environment env, Value self) {
|
||||
var res = new ObjectValue();
|
||||
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));
|
||||
res.defineOwnField(env, "value", get(env, self));
|
||||
res.defineOwnField(env, "writable", BoolValue.of(writable));
|
||||
res.defineOwnField(env, "enumerable", BoolValue.of(enumerable));
|
||||
res.defineOwnField(env, "configurable", BoolValue.of(configurable));
|
||||
return res;
|
||||
}
|
||||
|
||||
public FieldMember(Value self, boolean configurable, boolean enumerable, boolean writable) {
|
||||
public FieldMember(Value self, Boolean configurable, Boolean enumerable, Boolean writable) {
|
||||
if (writable == null) writable = false;
|
||||
if (enumerable == null) enumerable = false;
|
||||
if (configurable == null) configurable = false;
|
||||
|
||||
this.self = self;
|
||||
this.configurable = configurable;
|
||||
this.enumerable = enumerable;
|
||||
@ -155,17 +174,16 @@ public interface Member {
|
||||
public static FieldMember of(Value self, Value value) {
|
||||
return new SimpleFieldMember(self, value, true, true, true);
|
||||
}
|
||||
public static FieldMember of(Value self, Value value, boolean writable) {
|
||||
public static FieldMember of(Value self, Value value, Boolean writable) {
|
||||
return new SimpleFieldMember(self, value, true, true, writable);
|
||||
}
|
||||
public static FieldMember of(Value self, Value value, boolean configurable, boolean enumerable, boolean 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 redefine(Environment env, Member newMember, Value self);
|
||||
public ObjectValue descriptor(Environment env, Value self);
|
||||
|
||||
public Value get(Environment env, Value self);
|
||||
|
@ -11,6 +11,7 @@ import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import me.topchetoeu.jscript.common.SyntaxException;
|
||||
@ -18,7 +19,6 @@ import me.topchetoeu.jscript.common.environment.Environment;
|
||||
import me.topchetoeu.jscript.common.environment.Key;
|
||||
import me.topchetoeu.jscript.runtime.EventLoop;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
||||
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;
|
||||
@ -110,7 +110,8 @@ public abstract class Value {
|
||||
public abstract Member getOwnMember(Environment env, KeyCache key);
|
||||
public abstract Set<String> getOwnMembers(Environment env, boolean onlyEnumerable);
|
||||
public abstract Set<SymbolValue> getOwnSymbolMembers(Environment env, boolean onlyEnumerable);
|
||||
public abstract boolean defineOwnMember(Environment env, KeyCache key, Member member);
|
||||
public abstract boolean defineOwnField(Environment env, KeyCache key, Value val, Boolean writable, Boolean enumerable, Boolean configurable);
|
||||
public abstract boolean defineOwnProperty(Environment env, KeyCache key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable);
|
||||
public abstract boolean deleteOwnMember(Environment env, KeyCache key);
|
||||
|
||||
public abstract ObjectValue getPrototype(Environment env);
|
||||
@ -135,33 +136,46 @@ public abstract class Value {
|
||||
return getOwnMember(env, new KeyCache(key));
|
||||
}
|
||||
|
||||
public final boolean defineOwnMember(Environment env, Value key, Member member) {
|
||||
return defineOwnMember(env, new KeyCache(key), member);
|
||||
public final boolean defineOwnProperty(Environment env, Value key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable) {
|
||||
return defineOwnProperty(env, new KeyCache(key), get, set, enumerable, configurable);
|
||||
}
|
||||
public final boolean defineOwnMember(Environment env, String key, Member member) {
|
||||
return defineOwnMember(env, new KeyCache(key), member);
|
||||
public final boolean defineOwnProperty(Environment env, String key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable) {
|
||||
return defineOwnProperty(env, new KeyCache(key), get, set, enumerable, configurable);
|
||||
}
|
||||
public final boolean defineOwnMember(Environment env, int key, Member member) {
|
||||
return defineOwnMember(env, new KeyCache(key), member);
|
||||
public final boolean defineOwnProperty(Environment env, int key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable) {
|
||||
return defineOwnProperty(env, new KeyCache(key), get, set, enumerable, configurable);
|
||||
}
|
||||
public final boolean defineOwnMember(Environment env, double key, Member member) {
|
||||
return defineOwnMember(env, new KeyCache(key), member);
|
||||
public final boolean defineOwnProperty(Environment env, double key, Optional<FunctionValue> get, Optional<FunctionValue> set, Boolean enumerable, Boolean configurable) {
|
||||
return defineOwnProperty(env, new KeyCache(key), get, set, enumerable, configurable);
|
||||
}
|
||||
|
||||
public final boolean defineOwnMember(Environment env, KeyCache key, Value val) {
|
||||
return defineOwnMember(env, key, FieldMember.of(this, val));
|
||||
public final boolean defineOwnField(Environment env, Value key, Value val, Boolean writable, Boolean enumerable, Boolean configurable) {
|
||||
return defineOwnField(env, new KeyCache(key), val, writable, enumerable, configurable);
|
||||
}
|
||||
public final boolean defineOwnMember(Environment env, Value key, Value val) {
|
||||
return defineOwnMember(env, new KeyCache(key), val);
|
||||
public final boolean defineOwnField(Environment env, String key, Value val, Boolean writable, Boolean enumerable, Boolean configurable) {
|
||||
return defineOwnField(env, new KeyCache(key), val, writable, enumerable, configurable);
|
||||
}
|
||||
public final boolean defineOwnMember(Environment env, String key, Value val) {
|
||||
return defineOwnMember(env, new KeyCache(key), val);
|
||||
public final boolean defineOwnField(Environment env, int key, Value val, Boolean writable, Boolean enumerable, Boolean configurable) {
|
||||
return defineOwnField(env, new KeyCache(key), val, writable, enumerable, configurable);
|
||||
}
|
||||
public final boolean defineOwnMember(Environment env, int key, Value val) {
|
||||
return defineOwnMember(env, new KeyCache(key), val);
|
||||
public final boolean defineOwnField(Environment env, double key, Value val, Boolean writable, Boolean enumerable, Boolean configurable) {
|
||||
return defineOwnField(env, new KeyCache(key), val, writable, enumerable, configurable);
|
||||
}
|
||||
public final boolean defineOwnMember(Environment env, double key, Value val) {
|
||||
return defineOwnMember(env, new KeyCache(key), val);
|
||||
|
||||
public final boolean defineOwnField(Environment env, KeyCache key, Value val) {
|
||||
return defineOwnField(env, key, val, true, true, true);
|
||||
}
|
||||
public final boolean defineOwnField(Environment env, Value key, Value val) {
|
||||
return defineOwnField(env, new KeyCache(key), val);
|
||||
}
|
||||
public final boolean defineOwnField(Environment env, String key, Value val) {
|
||||
return defineOwnField(env, new KeyCache(key), val);
|
||||
}
|
||||
public final boolean defineOwnField(Environment env, int key, Value val) {
|
||||
return defineOwnField(env, new KeyCache(key), val);
|
||||
}
|
||||
public final boolean defineOwnField(Environment env, double key, Value val) {
|
||||
return defineOwnField(env, new KeyCache(key), val);
|
||||
}
|
||||
|
||||
public final boolean deleteOwnMember(Environment env, Value key) {
|
||||
@ -221,14 +235,14 @@ public abstract class Value {
|
||||
var member = obj.getOwnMember(env, key);
|
||||
if (member != null && (member instanceof PropertyMember || obj == this)) {
|
||||
if (member.set(env, val, this)) {
|
||||
if (val instanceof FunctionValue) ((FunctionValue)val).setName(key.toString(env));
|
||||
if (val instanceof FunctionValue && !key.isSymbol()) ((FunctionValue)val).setName(key.toString(env));
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (defineOwnMember(env, key, val)) {
|
||||
if (defineOwnField(env, key, val)) {
|
||||
if (val instanceof FunctionValue func) {
|
||||
if (key.isSymbol()) func.setName(key.toSymbol().toString());
|
||||
else func.setName(key.toString(env));
|
||||
@ -255,7 +269,7 @@ public abstract class Value {
|
||||
var member = obj.getOwnMember(env, key);
|
||||
if (member != null) {
|
||||
if (member.set(env, val, obj)) {
|
||||
if (val instanceof FunctionValue) ((FunctionValue)val).setName(key.toString(env));
|
||||
if (!key.isSymbol() && val instanceof FunctionValue) ((FunctionValue)val).setName(key.toString(env));
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
@ -434,8 +448,8 @@ public abstract class Value {
|
||||
return new NativeFunction("", args -> {
|
||||
var obj = new ObjectValue();
|
||||
|
||||
if (!it.hasNext()) obj.defineOwnMember(args.env, "done", BoolValue.TRUE);
|
||||
else obj.defineOwnMember(args.env, "value", it.next());
|
||||
if (!it.hasNext()) obj.defineOwnField(args.env, "done", BoolValue.TRUE);
|
||||
else obj.defineOwnField(args.env, "value", it.next());
|
||||
|
||||
return obj;
|
||||
});
|
||||
|
@ -66,15 +66,16 @@ public abstract class FunctionValue extends ObjectValue {
|
||||
}
|
||||
|
||||
@Override public Member getOwnMember(Environment env, KeyCache key) {
|
||||
switch (key.toString(env)) {
|
||||
if (!key.isSymbol()) switch (key.toString(env)) {
|
||||
case "length": return lengthField;
|
||||
case "name": return nameField;
|
||||
case "prototype": return prototypeField;
|
||||
default: return super.getOwnMember(env, key);
|
||||
}
|
||||
|
||||
return super.getOwnMember(env, key);
|
||||
}
|
||||
@Override public boolean deleteOwnMember(Environment env, KeyCache key) {
|
||||
switch (key.toString(env)) {
|
||||
if (!key.isSymbol()) switch (key.toString(env)) {
|
||||
case "length":
|
||||
length = 0;
|
||||
return true;
|
||||
@ -83,8 +84,8 @@ public abstract class FunctionValue extends ObjectValue {
|
||||
return true;
|
||||
case "prototype":
|
||||
return false;
|
||||
default: return super.deleteOwnMember(env, key);
|
||||
}
|
||||
return super.deleteOwnMember(env, key);
|
||||
}
|
||||
|
||||
@Override public StringValue type() { return StringValue.of("function"); }
|
||||
@ -114,7 +115,7 @@ public abstract class FunctionValue extends ObjectValue {
|
||||
this.length = length;
|
||||
this.name = name;
|
||||
|
||||
prototype.defineOwnMember(null, "constructor", this);
|
||||
prototype.defineOwnField(null, "constructor", this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,11 +65,13 @@ public abstract class ArrayLikeValue extends ObjectValue {
|
||||
var i = key.toInt(env);
|
||||
|
||||
if (i == num && i >= 0 && i < size() && has(i)) return new IndexField(i, this);
|
||||
else if (key.toString(env).equals("length")) return lengthField;
|
||||
else if (!key.isSymbol() && key.toString(env).equals("length")) return lengthField;
|
||||
else return null;
|
||||
}
|
||||
@Override public boolean defineOwnMember(Environment env, KeyCache key, Member member) {
|
||||
if (!(member instanceof FieldMember) || super.getOwnMember(env, key) != null) return super.defineOwnMember(env, key, member);
|
||||
@Override public boolean defineOwnField(
|
||||
Environment env, KeyCache key, Value val,
|
||||
Boolean writable, Boolean enumerable, Boolean configurable
|
||||
) {
|
||||
if (!getState().writable) return false;
|
||||
|
||||
if (!key.isSymbol()) {
|
||||
@ -77,12 +79,18 @@ public abstract class ArrayLikeValue extends ObjectValue {
|
||||
var i = key.toInt(env);
|
||||
|
||||
if (i == num) {
|
||||
if (writable == null) writable = true;
|
||||
if (configurable == null) configurable = true;
|
||||
if (enumerable == null) enumerable = true;
|
||||
|
||||
if (writable && configurable && enumerable) {
|
||||
if (!getState().extendable && !has(i)) return false;
|
||||
if (set(env, i, ((FieldMember)member).get(env, this))) return true;
|
||||
if (set(env, i, val)) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.defineOwnMember(env, key, member);
|
||||
return super.defineOwnField(env, key, val, writable, enumerable, configurable);
|
||||
}
|
||||
@Override public boolean deleteOwnMember(Environment env, KeyCache key) {
|
||||
if (!super.deleteOwnMember(env, key)) return false;
|
||||
|
@ -1,11 +1,13 @@
|
||||
package me.topchetoeu.jscript.runtime.values.objects;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import me.topchetoeu.jscript.common.environment.Environment;
|
||||
@ -14,6 +16,7 @@ import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
||||
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.Member.PropertyMember;
|
||||
import me.topchetoeu.jscript.runtime.values.functions.FunctionValue;
|
||||
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
|
||||
@ -37,8 +40,13 @@ public class ObjectValue extends Value {
|
||||
|
||||
protected PrototypeProvider prototype;
|
||||
|
||||
public LinkedHashMap<String, Member> members = new LinkedHashMap<>();
|
||||
public LinkedHashMap<SymbolValue, Member> symbolMembers = new LinkedHashMap<>();
|
||||
private HashMap<String, FieldMember> fields = new HashMap<>();
|
||||
private HashMap<SymbolValue, FieldMember> symbolFields = new HashMap<>();
|
||||
private HashMap<String, PropertyMember> properties = new HashMap<>();
|
||||
private HashMap<SymbolValue, PropertyMember> symbolProperties = new HashMap<>();
|
||||
|
||||
private LinkedHashMap<String, Boolean> keys = new LinkedHashMap<>();
|
||||
private LinkedHashMap<SymbolValue, Boolean> symbols = new LinkedHashMap<>();
|
||||
|
||||
@Override public boolean isPrimitive() { return false; }
|
||||
@Override public Value toPrimitive(Environment env) {
|
||||
@ -78,57 +86,154 @@ public class ObjectValue extends Value {
|
||||
|
||||
@Override public Member getOwnMember(Environment env, KeyCache key) {
|
||||
if (key.isSymbol()) {
|
||||
if (symbolMembers.size() > 0) return symbolMembers.get(key.toSymbol());
|
||||
if (!symbols.containsKey(key.toSymbol())) return null;
|
||||
|
||||
if (symbols.get(key.toSymbol())) return symbolProperties.get(key.toSymbol());
|
||||
else return symbolFields.get(key.toSymbol());
|
||||
}
|
||||
else if (keys.containsKey(key.toString(env))) {
|
||||
if (keys.get(key.toString(env))) return properties.get(key.toString(env));
|
||||
else return fields.get(key.toString(env));
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
else if (members.size() > 0) return members.get(key.toString(env));
|
||||
else return null;
|
||||
@Override public boolean defineOwnField(
|
||||
Environment env, KeyCache key, Value val,
|
||||
Boolean writable, Boolean enumerable, Boolean configurable
|
||||
) {
|
||||
if (key.isSymbol()) {
|
||||
|
||||
if (symbols.containsKey(key.toSymbol())) {
|
||||
if (symbols.get(key.toSymbol())) {
|
||||
var prop = symbolProperties.get(key.toSymbol());
|
||||
if (!prop.configurable) return false;
|
||||
|
||||
symbolProperties.remove(key.toSymbol());
|
||||
}
|
||||
else return symbolFields.get(key.toSymbol()).reconfigure(env, this, val, writable, enumerable, configurable);
|
||||
}
|
||||
@Override public boolean defineOwnMember(Environment env, KeyCache key, Member member) {
|
||||
var old = getOwnMember(env, key);
|
||||
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);
|
||||
else members.put(key.toString(env), member);
|
||||
symbols.put(key.toSymbol(), false);
|
||||
symbolFields.put(key.toSymbol(), FieldMember.of(this, val, writable, enumerable, configurable));
|
||||
return true;
|
||||
}
|
||||
else if (keys.containsKey(key.toString(env))) {
|
||||
if (keys.get(key.toString(env))) {
|
||||
var prop = properties.get(key.toString(env));
|
||||
if (!prop.configurable) return false;
|
||||
|
||||
properties.remove(key.toString(env));
|
||||
}
|
||||
else return fields.get(key.toString(env)).reconfigure(env, this, val, writable, enumerable, configurable);
|
||||
}
|
||||
|
||||
keys.put(key.toString(env), false);
|
||||
fields.put(key.toString(env), FieldMember.of(this, val, writable, enumerable, configurable));
|
||||
return true;
|
||||
}
|
||||
@Override public boolean defineOwnProperty(
|
||||
Environment env, KeyCache key,
|
||||
Optional<FunctionValue> get, Optional<FunctionValue> set,
|
||||
Boolean enumerable, Boolean configurable
|
||||
) {
|
||||
if (key.isSymbol()) {
|
||||
if (symbols.containsKey(key.toSymbol())) {
|
||||
if (!symbols.get(key.toSymbol())) {
|
||||
var field = symbolFields.get(key.toSymbol());
|
||||
if (!field.configurable) return false;
|
||||
|
||||
symbolFields.remove(key.toSymbol());
|
||||
}
|
||||
else return symbolProperties.get(key.toSymbol()).reconfigure(env, this, get, set, enumerable, configurable);
|
||||
}
|
||||
|
||||
symbols.put(key.toSymbol(), true);
|
||||
symbolProperties.put(key.toSymbol(), new PropertyMember(this, get, set, enumerable, configurable));
|
||||
return true;
|
||||
}
|
||||
else if (keys.containsKey(key.toString(env))) {
|
||||
if (!keys.get(key.toString(env))) {
|
||||
var field = fields.get(key.toString(env));
|
||||
if (!field.configurable) return false;
|
||||
|
||||
fields.remove(key.toString(env));
|
||||
}
|
||||
else return properties.get(key.toString(env)).reconfigure(env, this, get, set, enumerable, configurable);
|
||||
}
|
||||
|
||||
keys.put(key.toString(env), true);
|
||||
properties.put(key.toString(env), new PropertyMember(this, get, set, enumerable, configurable));
|
||||
return true;
|
||||
}
|
||||
@Override public boolean deleteOwnMember(Environment env, KeyCache key) {
|
||||
if (!getState().extendable) return false;
|
||||
|
||||
var member = getOwnMember(env, key);
|
||||
if (member == null) return true;
|
||||
if (!member.configurable()) return false;
|
||||
if (key.isSymbol()) {
|
||||
if (!symbols.containsKey(key.toSymbol())) return true;
|
||||
|
||||
if (key.isSymbol()) symbolMembers.remove(key.toSymbol());
|
||||
else members.remove(key.toString(env));
|
||||
if (symbols.get(key.toSymbol())) {
|
||||
if (!symbolProperties.get(key.toSymbol()).configurable) return false;
|
||||
symbolProperties.remove(key.toSymbol());
|
||||
symbols.remove(key.toSymbol());
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (!symbolFields.get(key.toSymbol()).configurable) return false;
|
||||
symbolFields.remove(key.toSymbol());
|
||||
keys.remove(key.toString(env));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (keys.containsKey(key.toString(env))) {
|
||||
if (keys.get(key.toString(env))) {
|
||||
if (!properties.get(key.toString(env)).configurable) return false;
|
||||
properties.remove(key.toString(env));
|
||||
symbols.remove(key.toSymbol());
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (!fields.get(key.toString(env)).configurable) return false;
|
||||
fields.remove(key.toString(env));
|
||||
keys.remove(key.toString(env));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else return true;
|
||||
}
|
||||
|
||||
@Override public Set<String> getOwnMembers(Environment env, boolean onlyEnumerable) {
|
||||
if (onlyEnumerable) {
|
||||
var res = new LinkedHashSet<String>();
|
||||
|
||||
for (var el : members.entrySet()) {
|
||||
if (el.getValue().enumerable()) res.add(el.getKey());
|
||||
for (var el : keys.entrySet()) {
|
||||
if (el.getValue()) {
|
||||
if (properties.get(el.getKey()).enumerable) res.add(el.getKey());
|
||||
}
|
||||
else {
|
||||
if (fields.get(el.getKey()).enumerable) res.add(el.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
else return members.keySet();
|
||||
else return keys.keySet();
|
||||
}
|
||||
@Override public Set<SymbolValue> getOwnSymbolMembers(Environment env, boolean onlyEnumerable) {
|
||||
if (onlyEnumerable) {
|
||||
var res = new LinkedHashSet<SymbolValue>();
|
||||
|
||||
for (var el : symbolMembers.entrySet()) {
|
||||
if (el.getValue().enumerable()) res.add(el.getKey());
|
||||
for (var el : symbols.entrySet()) {
|
||||
if (el.getValue()) {
|
||||
if (symbolProperties.get(el.getKey()).enumerable) res.add(el.getKey());
|
||||
}
|
||||
else {
|
||||
if (symbolFields.get(el.getKey()).enumerable) res.add(el.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
else return symbolMembers.keySet();
|
||||
else return symbols.keySet();
|
||||
}
|
||||
|
||||
@Override public ObjectValue getPrototype(Environment env) {
|
||||
|
@ -1,34 +0,0 @@
|
||||
package me.topchetoeu.jscript.runtime.values.objects;
|
||||
|
||||
import me.topchetoeu.jscript.common.environment.Environment;
|
||||
import me.topchetoeu.jscript.runtime.values.Value;
|
||||
import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
|
||||
|
||||
public final class ScopeValue extends ObjectValue {
|
||||
private static class VariableField extends FieldMember {
|
||||
private int i;
|
||||
|
||||
public VariableField(int i, ScopeValue self) {
|
||||
super(self, false, true, true);
|
||||
this.i = i;
|
||||
}
|
||||
|
||||
@Override public Value get(Environment env, Value self) {
|
||||
return ((ScopeValue)self).variables[i][0];
|
||||
}
|
||||
|
||||
@Override public boolean set(Environment env, Value val, Value self) {
|
||||
((ScopeValue)self).variables[i][0] = val;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public final Value[][] variables;
|
||||
|
||||
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, this));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +1,28 @@
|
||||
package me.topchetoeu.jscript.runtime.values.primitives;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
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;
|
||||
import me.topchetoeu.jscript.runtime.values.functions.FunctionValue;
|
||||
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
|
||||
|
||||
public abstract class PrimitiveValue extends Value {
|
||||
@Override public final boolean defineOwnMember(Environment env, KeyCache key, Member member) { return false; }
|
||||
@Override public final boolean deleteOwnMember(Environment env, KeyCache key) { return false; }
|
||||
@Override public boolean defineOwnField(
|
||||
Environment env, KeyCache key, Value val,
|
||||
Boolean writable, Boolean enumerable, Boolean configurable
|
||||
) { return false; }
|
||||
@Override
|
||||
public boolean defineOwnProperty(
|
||||
Environment env, KeyCache key, Optional<FunctionValue> get, Optional<FunctionValue> set,
|
||||
Boolean enumerable, Boolean configurable
|
||||
) { return false; }
|
||||
@Override public boolean deleteOwnMember(Environment env, KeyCache key) { return true; }
|
||||
|
||||
@Override public final boolean isPrimitive() { return true; }
|
||||
@Override public final Value toPrimitive(Environment env) { return this; }
|
||||
|
||||
|
@ -4,12 +4,14 @@ import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
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;
|
||||
import me.topchetoeu.jscript.runtime.values.functions.FunctionValue;
|
||||
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
|
||||
import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue;
|
||||
|
||||
@ -23,8 +25,17 @@ public final class UserValue<T> extends Value {
|
||||
@Override public NumberValue toNumber(Environment ext) { return NumberValue.NAN; }
|
||||
@Override public String toString(Environment ext) { return "[user value]"; }
|
||||
|
||||
@Override public final boolean defineOwnMember(Environment env, KeyCache key, Member member) { return false; }
|
||||
@Override public final boolean deleteOwnMember(Environment env, KeyCache key) { return false; }
|
||||
@Override public boolean defineOwnField(
|
||||
Environment env, KeyCache key, Value val,
|
||||
Boolean writable, Boolean enumerable, Boolean configurable
|
||||
) { return false; }
|
||||
@Override
|
||||
public boolean defineOwnProperty(
|
||||
Environment env, KeyCache key, Optional<FunctionValue> get, Optional<FunctionValue> set,
|
||||
Boolean enumerable, Boolean configurable
|
||||
) { return false; }
|
||||
@Override public boolean deleteOwnMember(Environment env, KeyCache key) { return true; }
|
||||
|
||||
@Override public final boolean isPrimitive() { return false; }
|
||||
@Override public final Value toPrimitive(Environment env) { return NumberValue.NAN; }
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user