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) { for (var el : members) {
var obj = new ObjectValue(); var obj = new ObjectValue();
obj.defineOwnMember(env, "value", new StringValue(el)); obj.defineOwnMember(env, "value", StringValue.of(el));
frame.push(obj); frame.push(obj);
} }
@ -137,7 +137,7 @@ public class InstructionRunner {
case PUSH_NULL: frame.push(Value.NULL); break; case PUSH_NULL: frame.push(Value.NULL); break;
case PUSH_BOOL: frame.push(BoolValue.of(instr.get(0))); 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_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: default:
} }
@ -431,7 +431,7 @@ public class InstructionRunner {
res = BoolValue.of(stack[ptr - 1].hasMember(env, stack[ptr], false)); res = BoolValue.of(stack[ptr - 1].hasMember(env, stack[ptr], false));
break; break;
case INSTANCEOF: 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; break;
default: return null; default: return null;

View File

@ -19,7 +19,7 @@ import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue;
public class JSONConverter { public class JSONConverter {
public static Value toJs(JSONElement val) { public static Value toJs(JSONElement val) {
if (val.isBoolean()) return BoolValue.of(val.bool()); 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.isNumber()) return NumberValue.of(val.number());
if (val.isList()) return ArrayValue.of(val.list().stream().map(JSONConverter::toJs).collect(Collectors.toList())); if (val.isList()) return ArrayValue.of(val.list().stream().map(JSONConverter::toJs).collect(Collectors.toList()));
if (val.isMap()) { 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, "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, "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, "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; return res;
} }
@ -141,7 +141,7 @@ public class SimpleRepl {
sb.append(((StringValue)parts[i]).value); sb.append(((StringValue)parts[i]).value);
} }
return new StringValue(sb.toString()); return StringValue.of(sb.toString());
})); }));
res.defineOwnMember(env, "fromCharCode", new NativeFunction(args -> { res.defineOwnMember(env, "fromCharCode", new NativeFunction(args -> {
@ -152,7 +152,7 @@ public class SimpleRepl {
sb.append(((StringValue)parts[i]).value); sb.append(((StringValue)parts[i]).value);
} }
return new StringValue(sb.toString()); return StringValue.of(sb.toString());
})); }));
return res; return res;
@ -194,7 +194,7 @@ public class SimpleRepl {
var val = new ArrayValue(); var val = new ArrayValue();
for (var key : args.get(0).getOwnMembers(env, args.get(1).toBoolean())) { 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; return val;
@ -221,8 +221,8 @@ public class SimpleRepl {
return Value.UNDEFINED; return Value.UNDEFINED;
})); }));
res.defineOwnMember(env, "invokeType", new NativeFunction(args -> { res.defineOwnMember(env, "invokeType", new NativeFunction(args -> {
if (((ArgumentsValue)args.get(0)).frame.isNew) return new StringValue("new"); if (((ArgumentsValue)args.get(0)).frame.isNew) return StringValue.of("new");
else return new StringValue("call"); else return StringValue.of("call");
})); }));
res.defineOwnMember(env, "invoke", new NativeFunction(args -> { res.defineOwnMember(env, "invoke", new NativeFunction(args -> {
@ -249,7 +249,7 @@ public class SimpleRepl {
res.setPrototype(null, null); res.setPrototype(null, null);
res.defineOwnMember(env, "stringify", new NativeFunction(args -> { 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 -> { res.defineOwnMember(env, "parse", new NativeFunction(args -> {
return JSONConverter.toJs(JSON.parse(null, args.get(0).toString(env))); return JSONConverter.toJs(JSON.parse(null, args.get(0).toString(env)));
@ -260,8 +260,8 @@ public class SimpleRepl {
return Value.UNDEFINED; return Value.UNDEFINED;
})); }));
res.defineOwnMember(env, "invokeType", new NativeFunction(args -> { res.defineOwnMember(env, "invokeType", new NativeFunction(args -> {
if (((ArgumentsValue)args.get(0)).frame.isNew) return new StringValue("new"); if (((ArgumentsValue)args.get(0)).frame.isNew) return StringValue.of("new");
else return new StringValue("call"); else return StringValue.of("call");
})); }));
res.defineOwnMember(env, "invoke", new NativeFunction(args -> { res.defineOwnMember(env, "invoke", new NativeFunction(args -> {
var func = (FunctionValue)args.get(0); var func = (FunctionValue)args.get(0);

View File

@ -97,8 +97,8 @@ public class EngineException extends RuntimeException {
if (msg == null) msg = ""; if (msg == null) msg = "";
if (name != null) res.defineOwnMember(Environment.empty(), "name", new StringValue(name)); if (name != null) res.defineOwnMember(Environment.empty(), "name", StringValue.of(name));
res.defineOwnMember(Environment.empty(), "message", new StringValue(msg)); res.defineOwnMember(Environment.empty(), "message", StringValue.of(msg));
return res; return res;
} }

View File

@ -51,7 +51,7 @@ public final class KeyCache {
this.value = value; this.value = value;
} }
public KeyCache(String value) { public KeyCache(String value) {
this.value = new StringValue(value); this.value = StringValue.of(value);
this.stringCache = value; this.stringCache = value;
this.booleanCache = !value.equals(""); 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<ObjectValue> GLOBAL = Key.of();
public static final Key<Map<String, Value>> INTRINSICS = 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 UNDEFINED = new VoidValue("undefined", "undefined");
public static final VoidValue NULL = new VoidValue("null", new StringValue("object")); public static final VoidValue NULL = new VoidValue("null", "object");
public abstract StringValue type(); public abstract StringValue type();
public abstract boolean isPrimitive(); public abstract boolean isPrimitive();
@ -96,7 +96,7 @@ public abstract class Value {
} }
public final Value construct(Environment env, String name, Value ...args) { public final Value construct(Environment env, String name, Value ...args) {
var res = new ObjectValue(); 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); if (proto instanceof ObjectValue) res.setPrototype(env, (ObjectValue)proto);
else res.setPrototype(env, null); else res.setPrototype(env, null);
@ -395,9 +395,9 @@ public abstract class Value {
var curr = supplier.invoke(env, Value.UNDEFINED); var curr = supplier.invoke(env, Value.UNDEFINED);
if (curr == null) { supplier = null; value = null; } 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 { else {
this.value = curr.getMember(env, new StringValue("value")); this.value = curr.getMember(env, StringValue.of("value"));
consumed = false; consumed = false;
} }
} }
@ -615,7 +615,7 @@ public abstract class Value {
b = b.toPrimitive(env); b = b.toPrimitive(env);
if (a instanceof StringValue || b instanceof StringValue) { 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 { else {
var na = a.toNumber(env); 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; import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue;
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();
@ -21,8 +19,8 @@ public abstract class FunctionValue extends ObjectValue {
private final FieldMember nameField = new FieldMember(this, 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 StringValue.of("");
return new StringValue(name); return StringValue.of(name);
} }
@Override public boolean set(Environment env, Value val, Value self) { @Override public boolean set(Environment env, Value val, Value self) {
name = val.toString(env); 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) { 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;

View File

@ -30,8 +30,6 @@ public class ObjectValue extends Value {
} }
} }
private static final StringValue typeString = new StringValue("object");
protected PrototypeProvider prototype; protected PrototypeProvider prototype;
public LinkedHashMap<String, Member> members = new LinkedHashMap<>(); public LinkedHashMap<String, Member> members = new LinkedHashMap<>();
@ -40,14 +38,14 @@ public class ObjectValue extends Value {
@Override public boolean isPrimitive() { return false; } @Override public boolean isPrimitive() { return false; }
@Override public Value toPrimitive(Environment env) { @Override public Value toPrimitive(Environment env) {
if (env != null) { if (env != null) {
var valueOf = getMember(env, new StringValue("valueOf")); var valueOf = getMember(env, "valueOf");
if (valueOf instanceof FunctionValue) { if (valueOf instanceof FunctionValue) {
var res = valueOf.invoke(env, this); var res = valueOf.invoke(env, this);
if (res.isPrimitive()) return res; if (res.isPrimitive()) return res;
} }
var toString = getMember(env, new StringValue("toString")); var toString = getMember(env, "toString");
if (toString instanceof FunctionValue) { if (toString instanceof FunctionValue) {
var res = toString.invoke(env, this); var res = toString.invoke(env, this);
if (res.isPrimitive()) return res; 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 String toString(Environment env) { return toPrimitive(env).toString(env); }
@Override public boolean toBoolean() { return true; } @Override public boolean toBoolean() { return true; }
@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 StringValue.of("object"); }
private State state = State.NORMAL; 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 final class BoolValue extends PrimitiveValue {
public static final BoolValue TRUE = new BoolValue(true); public static final BoolValue TRUE = new BoolValue(true);
public static final BoolValue FALSE = new BoolValue(false); public static final BoolValue FALSE = new BoolValue(false);
private static final StringValue typeString = new StringValue("boolean");
public final boolean value; 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 boolean toBoolean() { return value; }
@Override public NumberValue toNumber(Environment ext) { return NumberValue.of(value ? 1 : 0); } @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) { @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; else return false;
} }

View File

@ -2,6 +2,7 @@ package me.topchetoeu.jscript.runtime.values.primitives;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Set; import java.util.Set;
import java.util.WeakHashMap;
import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.parsing.Parsing; 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; import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue;
public final class StringValue extends PrimitiveValue { public final class StringValue extends PrimitiveValue {
public final String value; private static final WeakHashMap<String, StringValue> cache = new WeakHashMap<>();
private static final StringValue typeString = new StringValue("string");
@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 boolean toBoolean() { return !value.equals(""); }
@Override public NumberValue toNumber(Environment ext) { @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 String toString(Environment ext) { return value; }
@Override public boolean equals(Object other) { @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; else return false;
} }
@ -61,7 +64,16 @@ public final class StringValue extends PrimitiveValue {
return res; return res;
} }
public StringValue(String value) { private StringValue(String value) {
this.value = 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 { public final class SymbolValue extends PrimitiveValue {
private static final HashMap<String, SymbolValue> registry = new HashMap<>(); private static final HashMap<String, SymbolValue> registry = new HashMap<>();
private static final StringValue typeString = new StringValue("symbol");
public final String value; public final String value;
public Value key() { 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 boolean toBoolean() { return false; }
@Override public String toString(Environment env) { @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 class VoidValue extends PrimitiveValue {
public final String name; 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 boolean toBoolean() { return false; }
@Override public NumberValue toNumber(Environment ext) { return NumberValue.NAN; } @Override public NumberValue toNumber(Environment ext) { return NumberValue.NAN; }
@Override public String toString(Environment ext) { return name; } @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))); 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.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 String toString() { return JSON.stringify(JSONElement.number(value)); }
@Override public boolean equals(Object other) { @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; else return false;
} }

View File

@ -21,7 +21,8 @@ public final class IntValue extends NumberValue {
@Override public String toString() { return value + ""; } @Override public String toString() { return value + ""; }
@Override public boolean equals(Object other) { @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; else return false;
} }

View File

@ -9,9 +9,8 @@ import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
public abstract class NumberValue extends PrimitiveValue { public abstract class NumberValue extends PrimitiveValue {
public static final NumberValue NAN = new DoubleValue(Double.NaN); 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 double getDouble();
public abstract int getInt(); public abstract int getInt();