optimization: keep StringValue instances tied to one String instance

This commit is contained in:
TopchetoEU 2024-09-14 19:45:05 +03:00
parent e9f889576c
commit 9b957335bf
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
15 changed files with 59 additions and 51 deletions

View File

@ -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;

View File

@ -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()) {

View File

@ -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);

View File

@ -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;
}

View File

@ -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("");
}

View File

@ -74,8 +74,8 @@ public abstract class Value {
public static final Key<ObjectValue> GLOBAL = Key.of();
public static final Key<Map<String, Value>> 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);

View File

@ -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;

View File

@ -30,8 +30,6 @@ public class ObjectValue extends Value {
}
}
private static final StringValue typeString = new StringValue("object");
protected PrototypeProvider prototype;
public LinkedHashMap<String, Member> 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;

View File

@ -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;
}

View File

@ -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<String, StringValue> 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;
}
}
}

View File

@ -10,15 +10,14 @@ import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue;
public final class SymbolValue extends PrimitiveValue {
private static final HashMap<String, SymbolValue> 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) {

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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();