diff --git a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index fe3aeb5..de79882 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -94,7 +94,7 @@ public class InstructionRunner { for (var el : members) { var obj = new ObjectValue(); - obj.defineOwnMember(env, "value", new StringValue(el)); + obj.defineOwnMember(env, "value", StringValue.of(el)); frame.push(obj); } @@ -137,7 +137,7 @@ public class InstructionRunner { case PUSH_NULL: frame.push(Value.NULL); break; case PUSH_BOOL: frame.push(BoolValue.of(instr.get(0))); break; case PUSH_NUMBER: frame.push(NumberValue.of((double)instr.get(0))); break; - case PUSH_STRING: frame.push(new StringValue(instr.get(0))); break; + case PUSH_STRING: frame.push(StringValue.of(instr.get(0))); break; default: } @@ -431,7 +431,7 @@ public class InstructionRunner { res = BoolValue.of(stack[ptr - 1].hasMember(env, stack[ptr], false)); break; case INSTANCEOF: - res = BoolValue.of(stack[ptr - 1].isInstanceOf(env, stack[ptr].getMember(env, new StringValue("prototype")))); + res = BoolValue.of(stack[ptr - 1].isInstanceOf(env, stack[ptr].getMember(env, StringValue.of("prototype")))); break; default: return null; diff --git a/src/main/java/me/topchetoeu/jscript/runtime/JSONConverter.java b/src/main/java/me/topchetoeu/jscript/runtime/JSONConverter.java index 714a2b5..a451588 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/JSONConverter.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/JSONConverter.java @@ -19,7 +19,7 @@ import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue; public class JSONConverter { public static Value toJs(JSONElement val) { if (val.isBoolean()) return BoolValue.of(val.bool()); - if (val.isString()) return new StringValue(val.string()); + if (val.isString()) return StringValue.of(val.string()); if (val.isNumber()) return NumberValue.of(val.number()); if (val.isList()) return ArrayValue.of(val.list().stream().map(JSONConverter::toJs).collect(Collectors.toList())); if (val.isMap()) { diff --git a/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java b/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java index 5740a3a..2075db9 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java @@ -97,7 +97,7 @@ public class SimpleRepl { res.defineOwnMember(env, "makeSymbol", new NativeFunction(args -> new SymbolValue(args.get(0).toString(args.env)))); res.defineOwnMember(env, "getSymbol", new NativeFunction(args -> SymbolValue.get(args.get(0).toString(args.env)))); res.defineOwnMember(env, "getSymbolKey", new NativeFunction(args -> ((SymbolValue)args.get(0)).key())); - res.defineOwnMember(env, "getSymbolDescriptor", new NativeFunction(args -> new StringValue(((SymbolValue)args.get(0)).value))); + res.defineOwnMember(env, "getSymbolDescriptor", new NativeFunction(args -> StringValue.of(((SymbolValue)args.get(0)).value))); return res; } @@ -141,7 +141,7 @@ public class SimpleRepl { sb.append(((StringValue)parts[i]).value); } - return new StringValue(sb.toString()); + return StringValue.of(sb.toString()); })); res.defineOwnMember(env, "fromCharCode", new NativeFunction(args -> { @@ -152,7 +152,7 @@ public class SimpleRepl { sb.append(((StringValue)parts[i]).value); } - return new StringValue(sb.toString()); + return StringValue.of(sb.toString()); })); return res; @@ -194,7 +194,7 @@ public class SimpleRepl { var val = new ArrayValue(); for (var key : args.get(0).getOwnMembers(env, args.get(1).toBoolean())) { - val.set(args.env, val.size(), new StringValue(key)); + val.set(args.env, val.size(), StringValue.of(key)); } return val; @@ -221,8 +221,8 @@ public class SimpleRepl { return Value.UNDEFINED; })); res.defineOwnMember(env, "invokeType", new NativeFunction(args -> { - if (((ArgumentsValue)args.get(0)).frame.isNew) return new StringValue("new"); - else return new StringValue("call"); + if (((ArgumentsValue)args.get(0)).frame.isNew) return StringValue.of("new"); + else return StringValue.of("call"); })); res.defineOwnMember(env, "invoke", new NativeFunction(args -> { @@ -249,7 +249,7 @@ public class SimpleRepl { res.setPrototype(null, null); res.defineOwnMember(env, "stringify", new NativeFunction(args -> { - return new StringValue(JSON.stringify(JSONConverter.fromJs(env, args.get(0)))); + return StringValue.of(JSON.stringify(JSONConverter.fromJs(env, args.get(0)))); })); res.defineOwnMember(env, "parse", new NativeFunction(args -> { return JSONConverter.toJs(JSON.parse(null, args.get(0).toString(env))); @@ -260,8 +260,8 @@ public class SimpleRepl { return Value.UNDEFINED; })); res.defineOwnMember(env, "invokeType", new NativeFunction(args -> { - if (((ArgumentsValue)args.get(0)).frame.isNew) return new StringValue("new"); - else return new StringValue("call"); + if (((ArgumentsValue)args.get(0)).frame.isNew) return StringValue.of("new"); + else return StringValue.of("call"); })); res.defineOwnMember(env, "invoke", new NativeFunction(args -> { var func = (FunctionValue)args.get(0); diff --git a/src/main/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java b/src/main/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java index ce26c0c..c89314b 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java @@ -97,8 +97,8 @@ public class EngineException extends RuntimeException { if (msg == null) msg = ""; - if (name != null) res.defineOwnMember(Environment.empty(), "name", new StringValue(name)); - res.defineOwnMember(Environment.empty(), "message", new StringValue(msg)); + if (name != null) res.defineOwnMember(Environment.empty(), "name", StringValue.of(name)); + res.defineOwnMember(Environment.empty(), "message", StringValue.of(msg)); return res; } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/KeyCache.java b/src/main/java/me/topchetoeu/jscript/runtime/values/KeyCache.java index 5ab25e5..88be685 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/KeyCache.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/KeyCache.java @@ -51,7 +51,7 @@ public final class KeyCache { this.value = value; } public KeyCache(String value) { - this.value = new StringValue(value); + this.value = StringValue.of(value); this.stringCache = value; this.booleanCache = !value.equals(""); } 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 812bd58..1497fb2 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java @@ -74,8 +74,8 @@ public abstract class Value { public static final Key GLOBAL = Key.of(); public static final Key> INTRINSICS = Key.of(); - public static final VoidValue UNDEFINED = new VoidValue("undefined", new StringValue("undefined")); - public static final VoidValue NULL = new VoidValue("null", new StringValue("object")); + public static final VoidValue UNDEFINED = new VoidValue("undefined", "undefined"); + public static final VoidValue NULL = new VoidValue("null", "object"); public abstract StringValue type(); public abstract boolean isPrimitive(); @@ -96,7 +96,7 @@ public abstract class Value { } public final Value construct(Environment env, String name, Value ...args) { var res = new ObjectValue(); - var proto = getMember(env, new StringValue("prototype")); + var proto = getMember(env, StringValue.of("prototype")); if (proto instanceof ObjectValue) res.setPrototype(env, (ObjectValue)proto); else res.setPrototype(env, null); @@ -395,9 +395,9 @@ public abstract class Value { var curr = supplier.invoke(env, Value.UNDEFINED); if (curr == null) { supplier = null; value = null; } - if (curr.getMember(env, new StringValue("done")).toBoolean()) { supplier = null; value = null; } + if (curr.getMember(env, StringValue.of("done")).toBoolean()) { supplier = null; value = null; } else { - this.value = curr.getMember(env, new StringValue("value")); + this.value = curr.getMember(env, StringValue.of("value")); consumed = false; } } @@ -615,7 +615,7 @@ public abstract class Value { b = b.toPrimitive(env); if (a instanceof StringValue || b instanceof StringValue) { - return new StringValue(a.toString(env) + b.toString(env)); + return StringValue.of(a.toString(env) + b.toString(env)); } else { var na = a.toNumber(env); diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java index fd1aa9d..9714be9 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java @@ -10,8 +10,6 @@ import me.topchetoeu.jscript.runtime.values.primitives.StringValue; import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue; 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(); @@ -21,8 +19,8 @@ public abstract class FunctionValue extends ObjectValue { 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); + if (name == null) return StringValue.of(""); + return StringValue.of(name); } @Override public boolean set(Environment env, Value val, Value self) { name = val.toString(env); @@ -79,7 +77,7 @@ public abstract class FunctionValue extends ObjectValue { } } - @Override public StringValue type() { return typeString; } + @Override public StringValue type() { return StringValue.of("function"); } public void setName(String val) { if (this.name == null || this.name.equals("")) this.name = val; 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 7c62e1b..b48b1a1 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 @@ -30,8 +30,6 @@ public class ObjectValue extends Value { } } - private static final StringValue typeString = new StringValue("object"); - protected PrototypeProvider prototype; public LinkedHashMap members = new LinkedHashMap<>(); @@ -40,14 +38,14 @@ public class ObjectValue extends Value { @Override public boolean isPrimitive() { return false; } @Override public Value toPrimitive(Environment env) { if (env != null) { - var valueOf = getMember(env, new StringValue("valueOf")); + var valueOf = getMember(env, "valueOf"); if (valueOf instanceof FunctionValue) { var res = valueOf.invoke(env, this); if (res.isPrimitive()) return res; } - var toString = getMember(env, new StringValue("toString")); + var toString = getMember(env, "toString"); if (toString instanceof FunctionValue) { var res = toString.invoke(env, this); if (res.isPrimitive()) return res; @@ -59,7 +57,7 @@ public class ObjectValue extends Value { @Override public String toString(Environment env) { return toPrimitive(env).toString(env); } @Override public boolean toBoolean() { return true; } @Override public NumberValue toNumber(Environment env) { return toPrimitive(env).toNumber(env); } - @Override public StringValue type() { return typeString; } + @Override public StringValue type() { return StringValue.of("object"); } private State state = State.NORMAL; diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java index 9b09c22..cb963e4 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java @@ -7,11 +7,10 @@ import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue; public final class BoolValue extends PrimitiveValue { public static final BoolValue TRUE = new BoolValue(true); public static final BoolValue FALSE = new BoolValue(false); - private static final StringValue typeString = new StringValue("boolean"); public final boolean value; - @Override public StringValue type() { return typeString; } + @Override public StringValue type() { return StringValue.of("boolean"); } @Override public boolean toBoolean() { return value; } @Override public NumberValue toNumber(Environment ext) { return NumberValue.of(value ? 1 : 0); } @@ -22,7 +21,8 @@ public final class BoolValue extends PrimitiveValue { } @Override public boolean equals(Object other) { - if (other instanceof BoolValue bool) return value == bool.value; + if (other == this) return true; + else if (other instanceof BoolValue bool) return value == bool.value; else return false; } 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 fb32ca4..7409021 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 @@ -2,6 +2,7 @@ package me.topchetoeu.jscript.runtime.values.primitives; import java.util.LinkedHashSet; import java.util.Set; +import java.util.WeakHashMap; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.parsing.Parsing; @@ -13,10 +14,11 @@ import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue; public final class StringValue extends PrimitiveValue { - public final String value; - private static final StringValue typeString = new StringValue("string"); + private static final WeakHashMap cache = new WeakHashMap<>(); - @Override public StringValue type() { return typeString; } + public final String value; + + @Override public StringValue type() { return of("string"); } @Override public boolean toBoolean() { return !value.equals(""); } @Override public NumberValue toNumber(Environment ext) { @@ -30,7 +32,8 @@ public final class StringValue extends PrimitiveValue { @Override public String toString(Environment ext) { return value; } @Override public boolean equals(Object other) { - if (other instanceof StringValue val) return value.length() == val.value.length() && value.equals(val.value); + if (this == other) return true; + else if (other instanceof StringValue val) return value.length() == val.value.length() && value.equals(val.value); else return false; } @@ -61,7 +64,16 @@ public final class StringValue extends PrimitiveValue { return res; } - public StringValue(String value) { + private StringValue(String value) { this.value = value; } + + public static StringValue of(String value) { + if (cache.containsKey(value)) return cache.get(value); + else { + StringValue res; + cache.put(value, res = new StringValue(value)); + return res; + } + } } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java index 5b7c17b..15e96c1 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java @@ -10,15 +10,14 @@ import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue; public final class SymbolValue extends PrimitiveValue { private static final HashMap registry = new HashMap<>(); - private static final StringValue typeString = new StringValue("symbol"); public final String value; public Value key() { - return registry.containsKey(value) && registry.get(value) == this ? new StringValue(value) : Value.UNDEFINED; + return registry.containsKey(value) && registry.get(value) == this ? StringValue.of(value) : Value.UNDEFINED; } - @Override public StringValue type() { return typeString; } + @Override public StringValue type() { return StringValue.of("symbol"); } @Override public boolean toBoolean() { return false; } @Override public String toString(Environment env) { 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 e54c57a..0949ef3 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 @@ -9,9 +9,9 @@ import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue; public final class VoidValue extends PrimitiveValue { public final String name; - public final StringValue typeString; + public final String type; - @Override public StringValue type() { return typeString; } + @Override public StringValue type() { return StringValue.of(type); } @Override public boolean toBoolean() { return false; } @Override public NumberValue toNumber(Environment ext) { return NumberValue.NAN; } @Override public String toString(Environment ext) { return name; } @@ -22,8 +22,8 @@ public final class VoidValue extends PrimitiveValue { throw EngineException.ofError(String.format("Cannot read properties of %s (reading '%s')", name, key.toString(env))); } - public VoidValue(String name, StringValue type) { + public VoidValue(String name, String type) { this.name = name; - this.typeString = type; + this.type = type; } } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/DoubleValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/DoubleValue.java index 488961c..183ff49 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/DoubleValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/DoubleValue.java @@ -25,7 +25,8 @@ public final class DoubleValue extends NumberValue { @Override public String toString() { return JSON.stringify(JSONElement.number(value)); } @Override public boolean equals(Object other) { - if (other instanceof NumberValue val) return value == val.getDouble(); + if (this == other) return true; + else if (other instanceof NumberValue val) return value == val.getDouble(); else return false; } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/IntValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/IntValue.java index 9b3c74c..0e10367 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/IntValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/IntValue.java @@ -21,7 +21,8 @@ public final class IntValue extends NumberValue { @Override public String toString() { return value + ""; } @Override public boolean equals(Object other) { - if (other instanceof NumberValue val) return val.isLong() && value == val.getLong(); + if (this == other) return true; + else if (other instanceof NumberValue val) return val.isLong() && value == val.getLong(); else return false; } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/NumberValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/NumberValue.java index dcab4d9..5060d2f 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/NumberValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/NumberValue.java @@ -9,9 +9,8 @@ import me.topchetoeu.jscript.runtime.values.primitives.StringValue; public abstract class NumberValue extends PrimitiveValue { public static final NumberValue NAN = new DoubleValue(Double.NaN); - private static final StringValue typeString = new StringValue("number"); - @Override public final StringValue type() { return typeString; } + @Override public final StringValue type() { return StringValue.of("number"); } public abstract double getDouble(); public abstract int getInt();