optimization: keep StringValue instances tied to one String instance
This commit is contained in:
parent
e9f889576c
commit
9b957335bf
@ -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;
|
||||
|
@ -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()) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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("");
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user