Skip to content

Commit

Permalink
Support caching of string instances in script preparation (#1678)
Browse files Browse the repository at this point in the history
  • Loading branch information
lahma authored Nov 11, 2023
1 parent 791384f commit 675ad49
Show file tree
Hide file tree
Showing 18 changed files with 155 additions and 97 deletions.
5 changes: 4 additions & 1 deletion Jint.Benchmark/EngineComparisonBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ public void NilJS()
public void YantraJS()
{
var engine = new YantraJS.Core.JSContext();
engine.Eval(_files[FileName]);
// By default YantraJS is strict mode only, in strict mode
// we need to pass `this` explicitly in global context
// if script is expecting global context as `this`
engine.Eval(_files[FileName], null, engine);
}
}
6 changes: 5 additions & 1 deletion Jint/Engine.Ast.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Esprima;
using Esprima.Ast;
using Jint.Native;
using Jint.Runtime.Environments;
using Jint.Runtime.Interpreter;
using Jint.Runtime.Interpreter.Expressions;
Expand Down Expand Up @@ -54,7 +55,7 @@ public void NodeVisitor(Node node)

if (!_bindingNames.TryGetValue(name, out var bindingName))
{
_bindingNames[name] = bindingName = new EnvironmentRecord.BindingName(name);
_bindingNames[name] = bindingName = new EnvironmentRecord.BindingName(JsString.CachedCreate(name));
}

node.AssociatedData = bindingName;
Expand All @@ -63,6 +64,9 @@ public void NodeVisitor(Node node)
case Nodes.Literal:
node.AssociatedData = JintLiteralExpression.ConvertToJsValue((Literal) node);
break;
case Nodes.MemberExpression:
node.AssociatedData = JintMemberExpression.InitializeDeterminedProperty((MemberExpression) node, cache: true);
break;
case Nodes.ArrowFunctionExpression:
case Nodes.FunctionDeclaration:
case Nodes.FunctionExpression:
Expand Down
2 changes: 1 addition & 1 deletion Jint/Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ internal JsValue GetValue(Reference reference, bool returnReferenceToPool)

private bool TryHandleStringValue(JsValue property, JsString s, ref ObjectInstance? o, out JsValue jsValue)
{
if (property == CommonProperties.Length)
if (CommonProperties.Length.Equals(property))
{
jsValue = JsNumber.Create((uint) s.Length);
return true;
Expand Down
30 changes: 15 additions & 15 deletions Jint/Native/Array/ArrayInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ internal bool CanUseFastAccess

public sealed override bool DefineOwnProperty(JsValue property, PropertyDescriptor desc)
{
if (property == CommonProperties.Length)
if (CommonProperties.Length.Equals(property))
{
return DefineLength(desc);
}
Expand Down Expand Up @@ -296,7 +296,7 @@ internal uint GetLength()

protected sealed override void AddProperty(JsValue property, PropertyDescriptor descriptor)
{
if (property == CommonProperties.Length)
if (CommonProperties.Length.Equals(property ))
{
_length = descriptor;
return;
Expand All @@ -307,7 +307,7 @@ protected sealed override void AddProperty(JsValue property, PropertyDescriptor

protected sealed override bool TryGetProperty(JsValue property, [NotNullWhen(true)] out PropertyDescriptor? descriptor)
{
if (property == CommonProperties.Length)
if (CommonProperties.Length.Equals(property))
{
descriptor = _length;
return _length != null;
Expand Down Expand Up @@ -416,7 +416,7 @@ public sealed override IEnumerable<KeyValuePair<JsValue, PropertyDescriptor>> Ge

public sealed override PropertyDescriptor GetOwnProperty(JsValue property)
{
if (property == CommonProperties.Length)
if (CommonProperties.Length.Equals(property))
{
return _length ?? PropertyDescriptor.Undefined;
}
Expand Down Expand Up @@ -451,7 +451,7 @@ public sealed override JsValue Get(JsValue property, JsValue receiver)
return value;
}

if (property == CommonProperties.Length)
if (CommonProperties.Length.Equals(property))
{
var length = _length?._value;
if (length is not null)
Expand All @@ -468,13 +468,13 @@ public sealed override bool Set(JsValue property, JsValue value, JsValue receive
var isSafeSelfTarget = IsSafeSelfTarget(receiver);
if (isSafeSelfTarget && CanUseFastAccess)
{
if (IsArrayIndex(property, out var index))
if (!ReferenceEquals(property, CommonProperties.Length) && IsArrayIndex(property, out var index))
{
SetIndexValue(index, value, updateLength: true);
return true;
}

if (property == CommonProperties.Length
if (CommonProperties.Length.Equals(property)
&& _length is { Writable: true }
&& value is JsNumber jsNumber
&& jsNumber.IsInteger()
Expand Down Expand Up @@ -532,7 +532,7 @@ protected internal sealed override void SetOwnProperty(JsValue property, Propert
{
WriteArrayValue(index, desc);
}
else if (property == CommonProperties.Length)
else if (CommonProperties.Length.Equals(property))
{
_length = desc;
}
Expand Down Expand Up @@ -564,33 +564,33 @@ private void TrackChanges(JsValue property, PropertyDescriptor desc, bool isArra
}
}

public sealed override void RemoveOwnProperty(JsValue p)
public sealed override void RemoveOwnProperty(JsValue property)
{
if (IsArrayIndex(p, out var index))
if (IsArrayIndex(property, out var index))
{
Delete(index);
}

if (p == CommonProperties.Length)
if (CommonProperties.Length.Equals(property))
{
_length = null;
}

base.RemoveOwnProperty(p);
base.RemoveOwnProperty(property);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool IsArrayIndex(JsValue p, out uint index)
{
if (p is JsNumber number)
if (p.IsNumber())
{
var value = number._value;
var value = ((JsNumber) p)._value;
var intValue = (uint) value;
index = intValue;
return value == intValue && intValue != uint.MaxValue;
}

index = ParseArrayIndex(p.ToString());
index = !p.IsSymbol() ? ParseArrayIndex(p.ToString()) : uint.MaxValue;
return index != uint.MaxValue;

// 15.4 - Use an optimized version of the specification
Expand Down
24 changes: 12 additions & 12 deletions Jint/Native/Function/FunctionInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,15 @@ internal sealed override IEnumerable<JsValue> GetInitialOwnStringPropertyKeys()

public override PropertyDescriptor GetOwnProperty(JsValue property)
{
if (property == CommonProperties.Prototype)
if (CommonProperties.Prototype.Equals(property))
{
return _prototypeDescriptor ?? PropertyDescriptor.Undefined;
}
if (property == CommonProperties.Length)
if (CommonProperties.Length.Equals(property))
{
return _length ?? PropertyDescriptor.Undefined;
}
if (property == CommonProperties.Name)
if (CommonProperties.Name.Equals(property))
{
return _nameDescriptor ?? PropertyDescriptor.Undefined;
}
Expand All @@ -144,15 +144,15 @@ public override PropertyDescriptor GetOwnProperty(JsValue property)

protected internal override void SetOwnProperty(JsValue property, PropertyDescriptor desc)
{
if (property == CommonProperties.Prototype)
if (CommonProperties.Prototype.Equals(property))
{
_prototypeDescriptor = desc;
}
else if (property == CommonProperties.Length)
else if (CommonProperties.Length.Equals(property))
{
_length = desc;
}
else if (property == CommonProperties.Name)
else if (CommonProperties.Name.Equals(property))
{
_nameDescriptor = desc;
}
Expand All @@ -164,15 +164,15 @@ protected internal override void SetOwnProperty(JsValue property, PropertyDescri

public override void RemoveOwnProperty(JsValue property)
{
if (property == CommonProperties.Prototype)
if (CommonProperties.Prototype.Equals(property))
{
_prototypeDescriptor = null;
}
if (property == CommonProperties.Length)
if (CommonProperties.Length.Equals(property))
{
_length = null;
}
if (property == CommonProperties.Name)
if (CommonProperties.Name.Equals(property))
{
_nameDescriptor = null;
}
Expand Down Expand Up @@ -401,7 +401,7 @@ public override IEnumerable<KeyValuePair<JsValue, PropertyDescriptor>> GetOwnPro

public override PropertyDescriptor GetOwnProperty(JsValue property)
{
if (property == CommonProperties.Constructor)
if (CommonProperties.Constructor.Equals(property))
{
return _constructor ?? PropertyDescriptor.Undefined;
}
Expand All @@ -411,7 +411,7 @@ public override PropertyDescriptor GetOwnProperty(JsValue property)

protected internal override void SetOwnProperty(JsValue property, PropertyDescriptor desc)
{
if (property == CommonProperties.Constructor)
if (CommonProperties.Constructor.Equals(property))
{
_constructor = desc;
}
Expand All @@ -423,7 +423,7 @@ protected internal override void SetOwnProperty(JsValue property, PropertyDescri

public override void RemoveOwnProperty(JsValue property)
{
if (property == CommonProperties.Constructor)
if (CommonProperties.Constructor.Equals(property))
{
_constructor = null;
}
Expand Down
4 changes: 2 additions & 2 deletions Jint/Native/Iterator/IteratorResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ public static IteratorResult CreateKeyValueIteratorPosition(Engine engine, JsVal

public override JsValue Get(JsValue property, JsValue receiver)
{
if (property == CommonProperties.Value)
if (CommonProperties.Value.Equals(property))
{
return _value;
}

if (property == CommonProperties.Done)
if (CommonProperties.Done.Equals(property))
{
return _done;
}
Expand Down
67 changes: 50 additions & 17 deletions Jint/Native/JsString.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Text;
using Jint.Runtime;
Expand All @@ -12,27 +13,29 @@ public class JsString : JsValue, IEquatable<JsString>, IEquatable<string>
private static readonly JsString[] _charToStringJsValue;
private static readonly JsString[] _intToStringJsValue;

public static readonly JsString Empty = new JsString("");
internal static readonly JsString NullString = new JsString("null");
internal static readonly JsString UndefinedString = new JsString("undefined");
internal static readonly JsString ObjectString = new JsString("object");
internal static readonly JsString FunctionString = new JsString("function");
internal static readonly JsString BooleanString = new JsString("boolean");
internal static readonly JsString StringString = new JsString("string");
internal static readonly JsString NumberString = new JsString("number");
internal static readonly JsString BigIntString = new JsString("bigint");
internal static readonly JsString SymbolString = new JsString("symbol");
internal static readonly JsString DefaultString = new JsString("default");
internal static readonly JsString NumberZeroString = new JsString("0");
internal static readonly JsString NumberOneString = new JsString("1");
internal static readonly JsString TrueString = new JsString("true");
internal static readonly JsString FalseString = new JsString("false");
internal static readonly JsString LengthString = new JsString("length");
internal static readonly JsValue CommaString = new JsString(",");
public static readonly JsString Empty;
internal static readonly JsString NullString;
internal static readonly JsString UndefinedString;
internal static readonly JsString ObjectString;
internal static readonly JsString FunctionString;
internal static readonly JsString BooleanString;
internal static readonly JsString StringString;
internal static readonly JsString NumberString;
internal static readonly JsString BigIntString;
internal static readonly JsString SymbolString;
internal static readonly JsString DefaultString;
internal static readonly JsString NumberZeroString;
internal static readonly JsString NumberOneString;
internal static readonly JsString TrueString;
internal static readonly JsString FalseString;
internal static readonly JsString LengthString;
internal static readonly JsValue CommaString;

[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal string _value;

private static ConcurrentDictionary<string, JsString> _stringCache;

static JsString()
{
_charToJsValue = new JsString[AsciiMax + 1];
Expand All @@ -49,6 +52,26 @@ static JsString()
{
_intToStringJsValue[i] = new JsString(TypeConverter.ToString(i));
}


_stringCache = new ConcurrentDictionary<string, JsString>();
Empty = new JsString("", InternalTypes.String);
NullString = CachedCreate("null");
UndefinedString = CachedCreate("undefined");
ObjectString = CachedCreate("object");
FunctionString = CachedCreate("function");
BooleanString = CachedCreate("boolean");
StringString = CachedCreate("string");
NumberString = CachedCreate("number");
BigIntString = CachedCreate("bigint");
SymbolString = CachedCreate("symbol");
DefaultString = CachedCreate("default");
NumberZeroString = CachedCreate("0");
NumberOneString = CachedCreate("1");
TrueString = CachedCreate("true");
FalseString = CachedCreate("false");
LengthString = CachedCreate("length");
CommaString = CachedCreate(",");
}

public JsString(string value) : this(value, InternalTypes.String)
Expand Down Expand Up @@ -146,6 +169,16 @@ internal static JsString Create(string value)
return new JsString(value);
}

internal static JsString CachedCreate(string value)
{
if (value.Length < 2)
{
return Create(value);
}

return _stringCache.GetOrAdd(value, static x => new JsString(x));
}

internal static JsString Create(char value)
{
var temp = _charToJsValue;
Expand Down
4 changes: 2 additions & 2 deletions Jint/Native/Map/JsMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public JsMap(Engine engine, Realm realm) : base(engine)

public override PropertyDescriptor GetOwnProperty(JsValue property)
{
if (property == CommonProperties.Size)
if (CommonProperties.Size.Equals(property))
{
return new PropertyDescriptor(_map.Count, PropertyFlag.AllForbidden);
}
Expand All @@ -28,7 +28,7 @@ public override PropertyDescriptor GetOwnProperty(JsValue property)

protected override bool TryGetProperty(JsValue property, [NotNullWhen(true)] out PropertyDescriptor? descriptor)
{
if (property == CommonProperties.Size)
if (CommonProperties.Size.Equals(property))
{
descriptor = new PropertyDescriptor(_map.Count, PropertyFlag.AllForbidden);
return true;
Expand Down
2 changes: 1 addition & 1 deletion Jint/Native/Proxy/JsProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public override JsValue Get(JsValue property, JsValue receiver)
AssertTargetNotRevoked(property);
var target = _target;

if (property == KeyFunctionRevoke || !TryCallHandler(TrapGet, new[] { target, TypeConverter.ToPropertyKey(property), receiver }, out var result))
if (KeyFunctionRevoke.Equals(property) || !TryCallHandler(TrapGet, new[] { target, TypeConverter.ToPropertyKey(property), receiver }, out var result))
{
return target.Get(property, receiver);
}
Expand Down
4 changes: 2 additions & 2 deletions Jint/Native/RegExp/JsRegExp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public string Flags

public override PropertyDescriptor GetOwnProperty(JsValue property)
{
if (property == PropertyLastIndex)
if (PropertyLastIndex.Equals(property))
{
return _prototypeDescriptor ?? PropertyDescriptor.Undefined;
}
Expand All @@ -88,7 +88,7 @@ public override PropertyDescriptor GetOwnProperty(JsValue property)

protected internal override void SetOwnProperty(JsValue property, PropertyDescriptor desc)
{
if (property == PropertyLastIndex)
if (PropertyLastIndex.Equals(property))
{
_prototypeDescriptor = desc;
return;
Expand Down
Loading

0 comments on commit 675ad49

Please sign in to comment.