clean up member logic

This commit is contained in:
TopchetoEU 2024-09-14 13:53:58 +03:00
parent 3e6816cb2c
commit 1f42263051
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
12 changed files with 201 additions and 132 deletions

View File

@ -1,8 +1,6 @@
package me.topchetoeu.jscript.runtime; package me.topchetoeu.jscript.runtime;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.Stack; import java.util.Stack;
import me.topchetoeu.jscript.common.Instruction; 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.debug.DebugContext;
import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.exceptions.InterruptException; 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.Value;
import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; import me.topchetoeu.jscript.runtime.values.functions.CodeFunction;
import me.topchetoeu.jscript.runtime.values.objects.ArrayLikeValue;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
public final class Frame { public final class Frame {
@ -385,31 +381,39 @@ public final class Frame {
* Gets an array proxy of the local locals * Gets an array proxy of the local locals
*/ */
public ObjectValue getValStackScope() { public ObjectValue getValStackScope() {
return new ObjectValue() { return new ArrayLikeValue() {
@Override public Member getOwnMember(Environment env, KeyCache key) { @Override public Value get(int i) { return stack[i]; }
var res = super.getOwnMember(env, key); @Override public void set(int i, Value val) { stack[i] = val; }
if (res != null) return res; @Override public boolean has(int i) { return i >= 0 && i < size(); }
@Override public void remove(int i) { }
var num = key.toNumber(env); @Override public int size() { return stackPtr; }
var i = key.toInt(env); @Override public boolean setSize(int val) { return false; }
if (num != i || i < 0 || i >= stackPtr) return null; // @Override public Member getOwnMember(Environment env, KeyCache key) {
else return new FieldMember(false, true, true) { // var res = super.getOwnMember(env, key);
@Override public Value get(Environment env, Value self) { return stack[i]; } // if (res != null) return res;
@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 + ""); // 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;
// }
}; };
} }

View File

@ -73,7 +73,7 @@ public class InstructionRunner {
else if (setterVal instanceof FunctionValue) setter = (FunctionValue)setterVal; else if (setterVal instanceof FunctionValue) setter = (FunctionValue)setterVal;
else throw EngineException.ofType("Setter must be a function or undefined."); 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.push(obj);
frame.codePtr++; frame.codePtr++;

View File

@ -8,7 +8,6 @@ import me.topchetoeu.jscript.common.json.JSONElement;
import me.topchetoeu.jscript.common.json.JSONList; import me.topchetoeu.jscript.common.json.JSONList;
import me.topchetoeu.jscript.common.json.JSONMap; import me.topchetoeu.jscript.common.json.JSONMap;
import me.topchetoeu.jscript.runtime.exceptions.EngineException; 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.Value;
import me.topchetoeu.jscript.runtime.values.objects.ArrayValue; import me.topchetoeu.jscript.runtime.values.objects.ArrayValue;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
@ -27,7 +26,7 @@ public class JSONConverter {
var res = new ObjectValue(); var res = new ObjectValue();
for (var el : val.map().entrySet()) { 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; return res;

View File

@ -171,9 +171,7 @@ public class SimpleRepl {
var configurable = args.get(4).toBoolean(); var configurable = args.get(4).toBoolean();
var value = args.get(5); var value = args.get(5);
obj.defineOwnMember(args.env, key, FieldMember.of(value, enumerable, configurable, writable)); return BoolValue.of(obj.defineOwnMember(args.env, key, FieldMember.of(obj, value, configurable, enumerable, writable)));
return Value.UNDEFINED;
})); }));
res.defineOwnMember(env, "defineProperty", new NativeFunction(args -> { res.defineOwnMember(env, "defineProperty", new NativeFunction(args -> {
var obj = (ObjectValue)args.get(0); 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 getter = args.get(4) instanceof VoidValue ? null : (FunctionValue)args.get(4);
var setter = args.get(5) instanceof VoidValue ? null : (FunctionValue)args.get(5); var setter = args.get(5) instanceof VoidValue ? null : (FunctionValue)args.get(5);
obj.defineOwnMember(args.env, key, new PropertyMember(getter, setter, configurable, enumerable)); return BoolValue.of(obj.defineOwnMember(args.env, key, new PropertyMember(obj, getter, setter, configurable, enumerable)));
return Value.UNDEFINED;
})); }));
res.defineOwnMember(env, "getPrototype", new NativeFunction(args -> { res.defineOwnMember(env, "getPrototype", new NativeFunction(args -> {
return args.get(0).getPrototype(env); return args.get(0).getPrototype(env);

View File

@ -6,7 +6,6 @@ import java.util.List;
import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.runtime.values.Value; 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;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue.PrototypeProvider; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue.PrototypeProvider;
import me.topchetoeu.jscript.runtime.values.primitives.StringValue; import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
@ -98,8 +97,8 @@ public class EngineException extends RuntimeException {
if (msg == null) msg = ""; if (msg == null) msg = "";
if (name != null) res.defineOwnMember(Environment.empty(), "name", FieldMember.of(new StringValue(name))); if (name != null) res.defineOwnMember(Environment.empty(), "name", new StringValue(name));
res.defineOwnMember(Environment.empty(), "message", FieldMember.of(new StringValue(msg))); res.defineOwnMember(Environment.empty(), "message", new StringValue(msg));
return res; return res;
} }

View File

@ -7,10 +7,11 @@ import me.topchetoeu.jscript.runtime.values.primitives.BoolValue;
public interface Member { public interface Member {
public static final class PropertyMember implements Member { public static final class PropertyMember implements Member {
public final FunctionValue getter; public final Value self;
public final FunctionValue setter; public FunctionValue getter;
public final boolean configurable; public FunctionValue setter;
public final boolean enumerable; public boolean configurable;
public boolean enumerable;
@Override public Value get(Environment env, Value self) { @Override public Value get(Environment env, Value self) {
if (getter != null) return getter.call(env, false, "", self); if (getter != null) return getter.call(env, false, "", self);
@ -22,36 +23,51 @@ public interface Member {
return true; 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 enumerable() { return enumerable; }
@Override public boolean configure(Environment env, Member newMember, Value self) { @Override public boolean redefine(Environment env, Member newMember, Value self) {
if (!(newMember instanceof PropertyMember)) return false; // If the given member isn't a property, we can't redefine
var prop = (PropertyMember)newMember; 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.enumerable != enumerable) return false;
if (prop.getter != getter || prop.setter != setter) return false;
if (prop.getter == getter) return true; return true;
if (prop.setter == setter) return true; }
return false;
} }
@Override public ObjectValue descriptor(Environment env, Value self) { @Override public ObjectValue descriptor(Environment env, Value self) {
var res = new ObjectValue(); var res = new ObjectValue();
if (getter == null) res.defineOwnMember(env, "getter", FieldMember.of(Value.UNDEFINED)); // Don't touch the ordering, as it's emulating V8
else res.defineOwnMember(env, "getter", FieldMember.of(getter));
if (setter == null) res.defineOwnMember(env, "setter", FieldMember.of(Value.UNDEFINED)); if (getter == null) res.defineOwnMember(env, "getter", Value.UNDEFINED);
else res.defineOwnMember(env, "setter", FieldMember.of(setter)); else res.defineOwnMember(env, "getter", getter);
res.defineOwnMember(env, "enumerable", FieldMember.of(BoolValue.of(enumerable))); if (setter == null) res.defineOwnMember(env, "setter", Value.UNDEFINED);
res.defineOwnMember(env, "configurable", FieldMember.of(BoolValue.of(configurable))); else res.defineOwnMember(env, "setter", setter);
res.defineOwnMember(env, "enumerable", BoolValue.of(enumerable));
res.defineOwnMember(env, "configurable", BoolValue.of(configurable));
return res; 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.getter = getter;
this.setter = setter; this.setter = setter;
this.configurable = configurable; this.configurable = configurable;
@ -69,60 +85,87 @@ public interface Member {
value = val; value = val;
return true; return true;
} }
public SimpleFieldMember(Value value, boolean configurable, boolean enumerable, boolean writable) { public SimpleFieldMember(Value self, Value value, boolean configurable, boolean enumerable, boolean writable) {
super(configurable, enumerable, writable); super(self, configurable, enumerable, writable);
this.value = value; this.value = value;
} }
} }
public final Value self;
public boolean configurable; public boolean configurable;
public boolean enumerable; public boolean enumerable;
public boolean writable; 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 enumerable() { return enumerable; }
@Override public final boolean configure(Environment env, Member newMember, Value self) { public final boolean writable() { return writable && self.getState().writable; }
if (!(newMember instanceof FieldMember)) return false;
var field = (FieldMember)newMember;
if (field.configurable != configurable) return false; @Override public final boolean redefine(Environment env, Member newMember, Value self) {
if (field.enumerable != enumerable) return false; // If the given member isn't a field, we can't redefine
if (!writable) return field.get(env, self).equals(get(env, self)); if (!(newMember instanceof FieldMember field)) return false;
set(env, field.get(env, self), self); if (configurable()) {
writable = field.writable; 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; 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) { @Override public ObjectValue descriptor(Environment env, Value self) {
var res = new ObjectValue(); var res = new ObjectValue();
res.defineOwnMember(env, "value", FieldMember.of(get(env, self))); res.defineOwnMember(env, "value", get(env, self));
res.defineOwnMember(env, "writable", FieldMember.of(BoolValue.of(writable))); res.defineOwnMember(env, "writable", BoolValue.of(writable));
res.defineOwnMember(env, "enumerable", FieldMember.of(BoolValue.of(enumerable))); res.defineOwnMember(env, "enumerable", BoolValue.of(enumerable));
res.defineOwnMember(env, "configurable", FieldMember.of(BoolValue.of(configurable))); res.defineOwnMember(env, "configurable", BoolValue.of(configurable));
return res; 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.configurable = configurable;
this.enumerable = enumerable; this.enumerable = enumerable;
this.writable = writable; this.writable = writable;
} }
public static FieldMember of(Value value) { public static FieldMember of(Value self, Value value) {
return new SimpleFieldMember(value, true, true, true); return new SimpleFieldMember(self, value, true, true, true);
} }
public static FieldMember of(Value value, boolean writable) { public static FieldMember of(Value self, Value value, boolean writable) {
return new SimpleFieldMember(value, true, true, writable); return new SimpleFieldMember(self, value, true, true, writable);
} }
public static FieldMember of(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(value, configurable, enumerable, writable); return new SimpleFieldMember(self, value, configurable, enumerable, writable);
} }
} }
public boolean configurable(); public boolean configurable();
public boolean enumerable(); 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 ObjectValue descriptor(Environment env, Value self);
public Value get(Environment env, Value self); public Value get(Environment env, Value self);

View File

@ -20,6 +20,7 @@ import me.topchetoeu.jscript.runtime.debug.DebugContext;
import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
import me.topchetoeu.jscript.runtime.values.Member.FieldMember; 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.FunctionValue;
import me.topchetoeu.jscript.runtime.values.functions.NativeFunction; import me.topchetoeu.jscript.runtime.values.functions.NativeFunction;
import me.topchetoeu.jscript.runtime.values.objects.ArrayValue; 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; import me.topchetoeu.jscript.runtime.values.primitives.VoidValue;
public abstract class Value { public abstract class Value {
public static enum CompareResult { public static enum State {
NOT_EQUAL, NORMAL(true, true, true),
EQUAL, NON_EXTENDABLE(false, true, true),
LESS, SEALED(false, false, true),
GREATER; 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) { public final boolean extendable;
if (cmp < 0) return LESS; public final boolean configurable;
if (cmp > 0) return GREATER; public final boolean writable;
return EQUAL;
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 ObjectValue getPrototype(Environment env);
public abstract boolean setPrototype(Environment env, ObjectValue val); 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) { public final Member getOwnMember(Environment env, Value key) {
return getOwnMember(env, new KeyCache(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) { 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) { 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) { 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) { 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) { 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) { 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)); if (val instanceof FunctionValue) ((FunctionValue)val).setName(key.toString(env));
return true; return true;
} }
@ -475,7 +482,13 @@ public abstract class Value {
var member = obj.getOwnMember(env, entry); var member = obj.getOwnMember(env, entry);
if (member instanceof FieldMember field) res.append(field.get(env, obj).toReadable(env, passed, tab + 1)); 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"); res.append(",\n");
} }
@ -485,7 +498,13 @@ public abstract class Value {
var member = obj.getOwnMember(env, entry); var member = obj.getOwnMember(env, entry);
if (member instanceof FieldMember field) res.append(field.get(env, obj).toReadable(env, passed, tab + 1)); 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"); res.append(",\n");
} }
@ -520,8 +539,8 @@ public abstract class Value {
return new NativeFunction("", args -> { return new NativeFunction("", args -> {
var obj = new ObjectValue(); var obj = new ObjectValue();
if (!it.hasNext()) obj.defineOwnMember(args.env, "done", FieldMember.of(BoolValue.TRUE)); if (!it.hasNext()) obj.defineOwnMember(args.env, "done", BoolValue.TRUE);
else obj.defineOwnMember(args.env, "value", FieldMember.of(it.next())); else obj.defineOwnMember(args.env, "value", it.next());
return obj; return obj;
}); });

View File

@ -10,6 +10,8 @@ import me.topchetoeu.jscript.runtime.values.primitives.NumberValue;
import me.topchetoeu.jscript.runtime.values.primitives.StringValue; import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
public abstract class FunctionValue extends ObjectValue { public abstract class FunctionValue extends ObjectValue {
private static final StringValue typeString = new StringValue("function");
public String name = ""; public String name = "";
public int length; public int length;
public Value prototype = new ObjectValue(); public Value prototype = new ObjectValue();
@ -17,7 +19,7 @@ public abstract class FunctionValue extends ObjectValue {
public boolean enableCall = true; public boolean enableCall = true;
public boolean enableNew = 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) { @Override public Value get(Environment env, Value self) {
if (name == null) return new StringValue(""); if (name == null) return new StringValue("");
return new StringValue(name); return new StringValue(name);
@ -27,7 +29,7 @@ public abstract class FunctionValue extends ObjectValue {
return true; 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) { @Override public Value get(Environment env, Value self) {
return new NumberValue(length); return new NumberValue(length);
} }
@ -35,7 +37,7 @@ public abstract class FunctionValue extends ObjectValue {
return false; 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) { @Override public Value get(Environment env, Value self) {
return prototype; return prototype;
} }
@ -77,6 +79,8 @@ public abstract class FunctionValue extends ObjectValue {
} }
} }
@Override public StringValue type() { return typeString; }
public void setName(String val) { public void setName(String val) {
if (this.name == null || this.name.equals("")) this.name = 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.length = length;
this.name = name; this.name = name;
prototype.defineOwnMember(null, "constructor", FieldMember.of(this)); prototype.defineOwnMember(null, "constructor", this);
} }
} }

View File

@ -20,13 +20,6 @@ public class ObjectValue extends Value {
public ObjectValue get(Environment env); public ObjectValue get(Environment env);
} }
public static enum State {
NORMAL,
NO_EXTENSIONS,
SEALED,
FROZEN,
}
public static class Property { public static class Property {
public final FunctionValue getter; public final FunctionValue getter;
public final FunctionValue setter; public final FunctionValue setter;
@ -41,8 +34,6 @@ public class ObjectValue extends Value {
protected PrototypeProvider prototype; protected PrototypeProvider prototype;
public boolean extensible = true;
public LinkedHashMap<String, Member> members = new LinkedHashMap<>(); public LinkedHashMap<String, Member> members = new LinkedHashMap<>();
public LinkedHashMap<SymbolValue, Member> symbolMembers = 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 NumberValue toNumber(Environment env) { return toPrimitive(env).toNumber(env); }
@Override public StringValue type() { return typeString; } @Override public StringValue type() { return typeString; }
private State state = State.NORMAL;
@Override public State getState() { return state; }
public final void preventExtensions() { 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) { @Override public Member getOwnMember(Environment env, KeyCache key) {
if (key.isSymbol()) return symbolMembers.get(key.toSymbol()); 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) { @Override public boolean defineOwnMember(Environment env, KeyCache key, Member member) {
var old = getOwnMember(env, key); 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 (old != null && !old.configurable()) return false;
if (key.isSymbol()) symbolMembers.put(key.toSymbol(), member); if (key.isSymbol()) symbolMembers.put(key.toSymbol(), member);
@ -89,11 +88,11 @@ public class ObjectValue extends Value {
return true; return true;
} }
@Override public boolean deleteOwnMember(Environment env, KeyCache key) { @Override public boolean deleteOwnMember(Environment env, KeyCache key) {
if (!extensible) return false; if (!getState().extendable) return false;
var member = getOwnMember(env, key); var member = getOwnMember(env, key);
if (member == null) return true; if (member == null) return true;
if (member.configurable()) return false; if (!member.configurable()) return false;
if (key.isSymbol()) symbolMembers.remove(key.toSymbol()); if (key.isSymbol()) symbolMembers.remove(key.toSymbol());
else members.remove(key.toString(env)); else members.remove(key.toString(env));
@ -134,12 +133,12 @@ public class ObjectValue extends Value {
} }
public final boolean setPrototype(PrototypeProvider val) { public final boolean setPrototype(PrototypeProvider val) {
if (!extensible) return false; if (!getState().extendable) return false;
prototype = val; prototype = val;
return true; return true;
} }
public final boolean setPrototype(Key<ObjectValue> key) { public final boolean setPrototype(Key<ObjectValue> key) {
if (!extensible) return false; if (!getState().extendable) return false;
prototype = env -> env.get(key); prototype = env -> env.get(key);
return true; return true;
} }

View File

@ -5,20 +5,20 @@ import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.Member.FieldMember; import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
public final class ScopeValue extends ObjectValue { public final class ScopeValue extends ObjectValue {
private class VariableField extends FieldMember { private static class VariableField extends FieldMember {
private int i; private int i;
public VariableField(int i) { public VariableField(int i, ScopeValue self) {
super(false, true, true); super(self, false, true, true);
this.i = i; this.i = i;
} }
@Override public Value get(Environment env, Value self) { @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) { @Override public boolean set(Environment env, Value val, Value self) {
variables[i][0] = val; ((ScopeValue)self).variables[i][0] = val;
return true; return true;
} }
} }
@ -28,7 +28,7 @@ public final class ScopeValue extends ObjectValue {
public ScopeValue(Value[][] variables, String[] names) { public ScopeValue(Value[][] variables, String[] names) {
this.variables = variables; this.variables = variables;
for (var i = 0; i < names.length && i < variables.length; i++) { 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));
} }
} }
} }

View File

@ -19,4 +19,10 @@ public abstract class PrimitiveValue extends Value {
@Override public Member getOwnMember(Environment env, KeyCache key) { return null; } @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<String> getOwnMembers(Environment env, boolean onlyEnumerable) { return Set.of(); }
@Override public Set<SymbolValue> getOwnSymbolMembers(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() {}
} }

View File

@ -40,10 +40,10 @@ public final class StringValue extends PrimitiveValue {
var i = key.toInt(env); var i = key.toInt(env);
if (i == num && i >= 0 && i < value.length()) { 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")) { 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); else return super.getOwnMember(env, key);
} }