From 515011b3ef58762996246d1f484560da8b6c9c26 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Fri, 6 Sep 2024 10:01:34 +0300 Subject: [PATCH] refactor: improve meber listing system --- .../me/topchetoeu/jscript/runtime/Frame.java | 20 +++----- .../jscript/runtime/InstructionRunner.java | 5 +- .../jscript/runtime/JSONConverter.java | 9 ++-- .../jscript/runtime/values/Value.java | 51 ++++++++----------- .../runtime/values/objects/ArrayValue.java | 17 +++---- .../runtime/values/objects/ObjectValue.java | 30 ++++++++--- .../values/primitives/PrimitiveValue.java | 6 +-- .../values/primitives/StringValue.java | 31 +++++++++-- .../runtime/values/primitives/VoidValue.java | 10 ++-- 9 files changed, 98 insertions(+), 81 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/runtime/Frame.java b/src/main/java/me/topchetoeu/jscript/runtime/Frame.java index 90c0af9..fa4c7f2 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/Frame.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/Frame.java @@ -1,8 +1,8 @@ package me.topchetoeu.jscript.runtime; import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.LinkedHashSet; +import java.util.Set; import java.util.Stack; import me.topchetoeu.jscript.common.Instruction; @@ -402,19 +402,11 @@ public final class Frame { } }; } - @Override public Map getOwnMembers(Environment env) { - var res = new LinkedHashMap(); + @Override public Set getOwnMembers(Environment env, boolean onlyEnumerable) { + var res = new LinkedHashSet(); + res.addAll(super.getOwnMembers(env, onlyEnumerable)); - for (var i = 0; i < stackPtr; i++) { - var _i = i; - res.put(i + "", 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; - } - }); - } + for (var i = 0; i < stackPtr; i++) res.add(i + ""); return res; } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index a6186ac..ecfa152 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -7,7 +7,6 @@ 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; @@ -83,14 +82,14 @@ public class InstructionRunner { private static Value execKeys(Environment env, Instruction instr, Frame frame) { var val = frame.pop(); - var members = new ArrayList<>(val.getMembers(env, false, true).keySet()); + var members = new ArrayList<>(val.getMembers(env, false, true)); Collections.reverse(members); frame.push(null); for (var el : members) { var obj = new ObjectValue(); - obj.defineOwnMember(env, "value", FieldMember.of(new StringValue(el))); + obj.defineOwnMember(env, "value", new StringValue(el)); frame.push(obj); } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/JSONConverter.java b/src/main/java/me/topchetoeu/jscript/runtime/JSONConverter.java index b895dea..8a19ccc 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/JSONConverter.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/JSONConverter.java @@ -70,10 +70,11 @@ public class JSONConverter { var res = new JSONMap(); - for (var el : val.getMembers(env, true, true).entrySet()) { - var jsonEl = fromJs(env, el.getValue().get(env, val), prev); - if (jsonEl == null) continue; - res.put(el.getKey(), jsonEl); + for (var key : val.getOwnMembers(env, true)) { + var el = fromJs(env, val.getMember(env, key), prev); + if (el == null) continue; + + res.put(key, el); } prev.remove(val); diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java b/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java index bf17a7f..f96e7fb 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java @@ -7,8 +7,9 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.Map; +import java.util.Set; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.environment.Key; @@ -119,8 +120,8 @@ public abstract class Value { } public abstract Member getOwnMember(Environment env, KeyCache key); - public abstract Map getOwnMembers(Environment env); - public abstract Map getOwnSymbolMembers(Environment env); + public abstract Set getOwnMembers(Environment env, boolean onlyEnumerable); + public abstract Set getOwnSymbolMembers(Environment env, boolean onlyEnumerable); public abstract boolean defineOwnMember(Environment env, KeyCache key, Member member); public abstract boolean deleteOwnMember(Environment env, KeyCache key); @@ -317,8 +318,8 @@ public abstract class Value { return deleteMember(env, new KeyCache(key)); } - public final Map getMembers(Environment env, boolean own, boolean onlyEnumerable) { - var res = new LinkedHashMap(); + public final Set getMembers(Environment env, boolean own, boolean onlyEnumerable) { + var res = new LinkedHashSet(); var protos = new ArrayList(); for (var proto = this; proto != null; proto = proto.getPrototype(env)) { @@ -329,19 +330,13 @@ public abstract class Value { Collections.reverse(protos); for (var proto : protos) { - if (onlyEnumerable) { - for (var el : proto.getOwnMembers(env).entrySet()) { - if (!el.getValue().enumerable()) continue; - res.put(el.getKey(), el.getValue()); - } - } - else res.putAll(proto.getOwnMembers(env)); + res.addAll(proto.getOwnMembers(env, onlyEnumerable)); } return res; } - public final Map getSymbolMembers(Environment env, boolean own, boolean onlyEnumerable) { - var res = new LinkedHashMap(); + public final Set getSymbolMembers(Environment env, boolean own, boolean onlyEnumerable) { + var res = new LinkedHashSet(); var protos = new ArrayList(); for (var proto = this; proto != null; proto = proto.getPrototype(env)) { @@ -352,13 +347,7 @@ public abstract class Value { Collections.reverse(protos); for (var proto : protos) { - if (onlyEnumerable) { - for (var el : proto.getOwnSymbolMembers(env).entrySet()) { - if (!el.getValue().enumerable()) continue; - res.put(el.getKey(), el.getValue()); - } - } - else res.putAll(proto.getOwnSymbolMembers(env)); + res.addAll(proto.getOwnSymbolMembers(env, onlyEnumerable)); } return res; @@ -444,7 +433,7 @@ public abstract class Value { if ( func.prototype instanceof ObjectValue objProto && objProto.getMember(env, "constructor") == func && - objProto.getOwnMembers(env).size() + objProto.getOwnSymbolMembers(env).size() == 1 + objProto.getOwnMembers(env, true).size() + objProto.getOwnSymbolMembers(env, true).size() == 1 ) { keys.remove("constructor"); } } else if (this instanceof ArrayValue) { @@ -469,29 +458,29 @@ public abstract class Value { passed.add(this); - if (keys.size() + obj.getOwnSymbolMembers(env).size() == 0) { + if (keys.size() + obj.getOwnSymbolMembers(env, true).size() == 0) { if (!printed) res.append("{}\n"); } else if (!printed) { if (tab > 3) return "{...}"; res.append("{\n"); - for (var entry : obj.getOwnSymbolMembers(env).entrySet()) { + for (var entry : obj.getOwnSymbolMembers(env, true)) { for (int i = 0; i < tab + 1; i++) res.append(" "); - res.append("[" + entry.getKey().value + "]" + ": "); + res.append("[" + entry.value + "]" + ": "); - var member = entry.getValue(); - if (member instanceof FieldMember) res.append(((FieldMember)member).get(env, obj).toReadable(env, passed, tab + 1)); + 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]"); res.append(",\n"); } - for (var entry : obj.getOwnMembers(env).entrySet()) { + for (var entry : obj.getOwnMembers(env, true)) { for (int i = 0; i < tab + 1; i++) res.append(" "); - res.append(entry.getKey() + ": "); + res.append(entry + ": "); - var member = entry.getValue(); - if (member instanceof FieldMember) res.append(((FieldMember)member).get(env, obj).toReadable(env, passed, tab + 1)); + 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]"); res.append(",\n"); diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java index 43a326b..397ab5b 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java @@ -4,8 +4,8 @@ import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.LinkedHashSet; +import java.util.Set; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.values.KeyCache; @@ -183,17 +183,16 @@ public class ArrayValue extends ObjectValue implements Iterable { else return true; } - @Override public Map getOwnMembers(Environment env) { - var res = new LinkedHashMap(); + @Override public Set getOwnMembers(Environment env, boolean onlyEnumerable) { + var res = new LinkedHashSet(); + + res.addAll(super.getOwnMembers(env, onlyEnumerable)); for (var i = 0; i < size; i++) { - var member = getOwnMember(env, i); - if (member != null) res.put(i + "", member); + if (has(i)) res.add(i + ""); } - res.put("length", lengthField); - - res.putAll(super.getOwnMembers(env)); + if (!onlyEnumerable) res.add("length"); return res; } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java index da5cba4..ead6365 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java @@ -1,8 +1,8 @@ package me.topchetoeu.jscript.runtime.values.objects; -import java.util.Collections; import java.util.LinkedHashMap; -import java.util.Map; +import java.util.LinkedHashSet; +import java.util.Set; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.environment.Key; @@ -100,11 +100,29 @@ public class ObjectValue extends Value { return true; } - @Override public Map getOwnMembers(Environment env) { - return members; + @Override public Set getOwnMembers(Environment env, boolean onlyEnumerable) { + if (onlyEnumerable) { + var res = new LinkedHashSet(); + + for (var el : members.entrySet()) { + if (el.getValue().enumerable()) res.add(el.getKey()); + } + + return res; + } + else return members.keySet(); } - @Override public Map getOwnSymbolMembers(Environment env) { - return Collections.unmodifiableMap(symbolMembers); + @Override public Set getOwnSymbolMembers(Environment env, boolean onlyEnumerable) { + if (onlyEnumerable) { + var res = new LinkedHashSet(); + + for (var el : symbolMembers.entrySet()) { + if (el.getValue().enumerable()) res.add(el.getKey()); + } + + return res; + } + else return symbolMembers.keySet(); } @Override public ObjectValue getPrototype(Environment env) { diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java index 4d18f7a..6806775 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java @@ -1,6 +1,6 @@ package me.topchetoeu.jscript.runtime.values.primitives; -import java.util.Map; +import java.util.Set; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.values.KeyCache; @@ -17,6 +17,6 @@ public abstract class PrimitiveValue extends Value { @Override public final boolean setPrototype(Environment env, ObjectValue val) { return false; } @Override public Member getOwnMember(Environment env, KeyCache key) { return null; } - @Override public Map getOwnMembers(Environment env) { return Map.of(); } - @Override public Map getOwnSymbolMembers(Environment env) { return Map.of(); } + @Override public Set getOwnMembers(Environment env, boolean onlyEnumerable) { return Set.of(); } + @Override public Set getOwnSymbolMembers(Environment env, boolean onlyEnumerable) { return Set.of(); } } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java index 8df3f52..b2c58ca 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java @@ -1,11 +1,14 @@ package me.topchetoeu.jscript.runtime.values.primitives; -import java.util.Map; +import java.util.LinkedHashSet; +import java.util.Set; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; +import me.topchetoeu.jscript.runtime.values.KeyCache; import me.topchetoeu.jscript.runtime.values.Member; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; public final class StringValue extends PrimitiveValue { @@ -32,9 +35,29 @@ public final class StringValue extends PrimitiveValue { @Override public ObjectValue getPrototype(Environment env) { return env.get(STRING_PROTO); } - @Override public Map getOwnMembers(Environment env) { - // TODO Auto-generated method stub - return super.getOwnMembers(env); + @Override public Member getOwnMember(Environment env, KeyCache key) { + var num = key.toNumber(env); + var i = key.toInt(env); + + if (i == num && i >= 0 && i < value.length()) { + return FieldMember.of(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); + } + else return super.getOwnMember(env, key); + } + + @Override public Set getOwnMembers(Environment env, boolean onlyEnumerable) { + var res = new LinkedHashSet(); + + res.addAll(super.getOwnMembers(env, onlyEnumerable)); + + for (var i = 0; i < value.length(); i++) res.add(i + ""); + + if (!onlyEnumerable) res.add("length"); + + return res; } public StringValue(String value) { diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java index 9e0539d..4c7c199 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java @@ -1,6 +1,6 @@ package me.topchetoeu.jscript.runtime.values.primitives; -import java.util.Map; +import java.util.Set; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; @@ -24,17 +24,13 @@ public final class VoidValue extends PrimitiveValue { @Override public Member getOwnMember(Environment env, KeyCache key) { throw EngineException.ofError(String.format("Cannot read properties of %s (reading '%s')", name, key.toString(env))); } - @Override public Map getOwnMembers(Environment env) { + @Override public Set getOwnMembers(Environment env, boolean onlyEnumerable) { throw EngineException.ofError(String.format("Cannot read properties of %s (listing all members)", name)); } - @Override public Map getOwnSymbolMembers(Environment env) { + @Override public Set getOwnSymbolMembers(Environment env, boolean onlyEnumerable) { throw EngineException.ofError(String.format("Cannot read properties of %s (listing all symbol members)", name)); } - // @Override public Value call(Environment env, Value self, Value... args) { - // throw EngineException.ofType(String.format("Tried to call a value of %s", name)); - // } - public VoidValue(String name, StringValue type) { this.name = name; this.typeString = type;