diff --git a/Directory.Packages.props b/Directory.Packages.props
index c59f121edb..2489c94f9e 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -8,14 +8,15 @@
-
+
+
-
+
@@ -28,6 +29,6 @@
-
+
\ No newline at end of file
diff --git a/Jint.Benchmark/README.md b/Jint.Benchmark/README.md
index e2d10c98c4..7a2a8cf90f 100644
--- a/Jint.Benchmark/README.md
+++ b/Jint.Benchmark/README.md
@@ -9,7 +9,7 @@ dotnet run -c Release --allCategories EngineComparison
* tests are run in global engine strict mode, as YantraJS always uses strict mode which improves performance
* `Jint` and `Jint_ParsedScript` shows the difference between always parsing the script source file and reusing parsed `Script` instance.
-Last updated 2023-11-05
+Last updated 2023-11-24
* Jint main
* Jurassic 3.2.7
diff --git a/Jint.Tests.PublicInterface/Jint.Tests.PublicInterface.csproj b/Jint.Tests.PublicInterface/Jint.Tests.PublicInterface.csproj
index b5340a5658..2896385681 100644
--- a/Jint.Tests.PublicInterface/Jint.Tests.PublicInterface.csproj
+++ b/Jint.Tests.PublicInterface/Jint.Tests.PublicInterface.csproj
@@ -26,6 +26,7 @@
+
diff --git a/Jint.Tests.PublicInterface/TimeSystemTests.cs b/Jint.Tests.PublicInterface/TimeSystemTests.cs
index 7d4df5bcd1..049f594536 100644
--- a/Jint.Tests.PublicInterface/TimeSystemTests.cs
+++ b/Jint.Tests.PublicInterface/TimeSystemTests.cs
@@ -1,5 +1,6 @@
using System.Globalization;
using Jint.Runtime;
+using Microsoft.Extensions.Time.Testing;
using NodaTime;
namespace Jint.Tests.PublicInterface;
@@ -33,6 +34,29 @@ public void CanProduceValidDatesUsingNodaTimeIntegration(string input, long expe
Assert.Equal(expected, engine.Evaluate($"new Date({input}) * 1").AsNumber());
}
+
+ [Fact]
+ public void CanUseTimeProvider()
+ {
+ var defaultEngine = new Engine();
+ var defaultNow = DateTimeOffset.Now.ToUnixTimeMilliseconds();
+ var defaultScriptNow = defaultEngine.Evaluate("new Date() * 1").AsNumber();
+ Assert.InRange(defaultScriptNow, defaultNow, defaultNow + 100);
+
+ var timeProvider = new FakeTimeProvider();
+ timeProvider.SetUtcNow(new DateTimeOffset(2023, 11, 6, 0, 0, 0, 0, TimeSpan.Zero));
+
+ var timeProviderEngine = new Engine(options =>
+ {
+ options.TimeSystem = new TimeProviderTimeSystem(timeProvider);
+ });
+
+ var timeProviderNow = timeProvider.GetUtcNow().ToUnixTimeMilliseconds();
+ var timeProviderScriptNow = timeProviderEngine.Evaluate("new Date() * 1").AsNumber();
+ Assert.InRange(timeProviderNow, timeProviderScriptNow, timeProviderScriptNow + 100);
+
+ Assert.NotInRange(timeProviderScriptNow, defaultScriptNow - 10000, defaultScriptNow + 10000);
+ }
}
file sealed class NodaTimeSystem : DefaultTimeSystem
@@ -52,3 +76,18 @@ public override TimeSpan GetUtcOffset(long epochMilliseconds)
return offset.ToTimeSpan();
}
}
+
+file sealed class TimeProviderTimeSystem : DefaultTimeSystem
+{
+ private readonly TimeProvider _timeProvider;
+
+ public TimeProviderTimeSystem(TimeProvider timeProvider) : base(TimeZoneInfo.Utc, CultureInfo.CurrentCulture)
+ {
+ _timeProvider = timeProvider;
+ }
+
+ public override DateTimeOffset GetUtcNow()
+ {
+ return _timeProvider.GetUtcNow();
+ }
+}
diff --git a/Jint.Tests.Test262/Test262Harness.settings.json b/Jint.Tests.Test262/Test262Harness.settings.json
index f6ac59bf09..d5c7c99fa5 100644
--- a/Jint.Tests.Test262/Test262Harness.settings.json
+++ b/Jint.Tests.Test262/Test262Harness.settings.json
@@ -1,5 +1,5 @@
{
- "SuiteGitSha": "6396ebde0316639292530460d1ef961fd9bbe0d4",
+ "SuiteGitSha": "2060494f280ba89d71a0f51d4ff171bafe476a05",
//"SuiteDirectory": "//mnt/c/work/test262",
"TargetPath": "./Generated",
"Namespace": "Jint.Tests.Test262",
@@ -12,6 +12,7 @@
"decorators",
"generators",
"import-assertions",
+ "import-attributes",
"iterator-helpers",
"regexp-duplicate-named-groups",
"regexp-lookbehind",
@@ -47,6 +48,7 @@
"built-ins/RegExp/prototype/exec/S15.10.6.2_A1_T6.js",
"language/literals/regexp/u-case-mapping.js",
"built-ins/RegExp/lookahead-quantifier-match-groups.js",
+ "built-ins/RegExp/unicode_full_case_folding.js",
// requires investigation how to process complex function name evaluation for property
"built-ins/Function/prototype/toString/method-computed-property-name.js",
@@ -85,6 +87,8 @@
"language/expressions/object/method-definition/name-prop-name-yield-id.js",
"language/statements/class/elements/*-generator-method-*.js",
"language/expressions/class/elements/*-generator-method-*.js",
+ "built-ins/Set/prototype/union/allows-set-like-class.js",
+ "built-ins/Set/prototype/union/allows-set-like-object.js",
// generators not implemented
"built-ins/Object/prototype/toString/proxy-function.js",
diff --git a/Jint.Tests/Runtime/EngineLimitTests.cs b/Jint.Tests/Runtime/EngineLimitTests.cs
index fe0577883b..9f839e1cc4 100644
--- a/Jint.Tests/Runtime/EngineLimitTests.cs
+++ b/Jint.Tests/Runtime/EngineLimitTests.cs
@@ -9,7 +9,7 @@ public class EngineLimitTests
{
#if RELEASE
- const int FunctionNestingCount = 840;
+ const int FunctionNestingCount = 1010;
#else
const int FunctionNestingCount = 510;
#endif
diff --git a/Jint/Extensions/Polyfills.cs b/Jint/Extensions/Polyfills.cs
index 0117c84606..0fdb9dc89a 100644
--- a/Jint/Extensions/Polyfills.cs
+++ b/Jint/Extensions/Polyfills.cs
@@ -1,21 +1,17 @@
-using System.Runtime.CompilerServices;
-
namespace Jint;
internal static class Polyfills
{
#if NETFRAMEWORK || NETSTANDARD2_0
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
internal static bool Contains(this string source, char c) => source.IndexOf(c) != -1;
+
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ internal static bool StartsWith(this string source, char c) => source.Length > 0 && source[0] == c;
#endif
#if NETFRAMEWORK
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
internal static bool Contains(this ReadOnlySpan source, string c) => source.IndexOf(c) != -1;
#endif
-
-#if NETFRAMEWORK || NETSTANDARD2_0
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal static bool StartsWith(this string source, char c) => source.Length > 0 && source[0] == c;
-#endif
}
diff --git a/Jint/Native/Array/ArrayPrototype.cs b/Jint/Native/Array/ArrayPrototype.cs
index f1e16039d6..697487f766 100644
--- a/Jint/Native/Array/ArrayPrototype.cs
+++ b/Jint/Native/Array/ArrayPrototype.cs
@@ -1,12 +1,12 @@
#pragma warning disable CA1859 // Use concrete types when possible for improved performance -- most of prototype methods return JsValue
using System.Linq;
+using System.Text;
using Jint.Collections;
using Jint.Native.Iterator;
using Jint.Native.Number;
using Jint.Native.Object;
using Jint.Native.Symbol;
-using Jint.Pooling;
using Jint.Runtime;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Descriptors.Specialized;
@@ -1265,15 +1265,15 @@ static string StringFromJsValue(JsValue value)
return s;
}
- using var sb = StringBuilderPool.Rent();
- sb.Builder.Append(s);
+ using var sb = new ValueStringBuilder(stackalloc char[256]);
+ sb.Append(s);
for (uint k = 1; k < len; k++)
{
if (sep != "")
{
- sb.Builder.Append(sep);
+ sb.Append(sep);
}
- sb.Builder.Append(StringFromJsValue(o.Get(k)));
+ sb.Append(StringFromJsValue(o.Get(k)));
}
return sb.ToString();
diff --git a/Jint/Native/BigInt/BigIntPrototype.cs b/Jint/Native/BigInt/BigIntPrototype.cs
index 86875afaed..8b5311afc4 100644
--- a/Jint/Native/BigInt/BigIntPrototype.cs
+++ b/Jint/Native/BigInt/BigIntPrototype.cs
@@ -1,9 +1,9 @@
using System.Globalization;
using System.Numerics;
+using System.Text;
using Jint.Collections;
using Jint.Native.Object;
using Jint.Native.Symbol;
-using Jint.Pooling;
using Jint.Runtime;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Interop;
@@ -114,22 +114,24 @@ private JsValue ToBigIntString(JsValue thisObject, JsValue[] arguments)
value = -value;
}
- const string digits = "0123456789abcdefghijklmnopqrstuvwxyz";
+ const string Digits = "0123456789abcdefghijklmnopqrstuvwxyz";
- using var builder = StringBuilderPool.Rent();
- var sb = builder.Builder;
+ var sb = new ValueStringBuilder(stackalloc char[64]);
for (; value > 0; value /= radixMV)
{
var d = (int) (value % radixMV);
- sb.Append(digits[d]);
+ sb.Append(Digits[d]);
}
- var charArray = new char[sb.Length];
- sb.CopyTo(0, charArray, 0, charArray.Length);
- System.Array.Reverse(charArray);
+ if (negative)
+ {
+ sb.Append('-');
+ }
+
+ sb.Reverse();
- return (negative ? "-" : "") + new string(charArray);
+ return sb.ToString();
}
///
diff --git a/Jint/Native/Date/DateConstructor.cs b/Jint/Native/Date/DateConstructor.cs
index ac89e19ccb..449589cd7e 100644
--- a/Jint/Native/Date/DateConstructor.cs
+++ b/Jint/Native/Date/DateConstructor.cs
@@ -98,9 +98,9 @@ private static JsValue Utc(JsValue thisObject, JsValue[] arguments)
return finalDate.TimeClip().ToJsValue();
}
- private static JsValue Now(JsValue thisObject, JsValue[] arguments)
+ private JsValue Now(JsValue thisObject, JsValue[] arguments)
{
- return (long) (DateTime.UtcNow - Epoch).TotalMilliseconds;
+ return (long) (_timeSystem.GetUtcNow().DateTime - Epoch).TotalMilliseconds;
}
protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments)
@@ -120,7 +120,7 @@ public override ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
newTarget,
static intrinsics => intrinsics.Date.PrototypeObject,
static (engine, _, dateValue) => new JsDate(engine, dateValue),
- (DateTime.UtcNow - Epoch).TotalMilliseconds);
+ (_timeSystem.GetUtcNow().DateTime - Epoch).TotalMilliseconds);
}
return ConstructUnlikely(arguments, newTarget);
diff --git a/Jint/Native/JsValue.cs b/Jint/Native/JsValue.cs
index cfab33dbab..f23f8b187a 100644
--- a/Jint/Native/JsValue.cs
+++ b/Jint/Native/JsValue.cs
@@ -54,7 +54,18 @@ internal IteratorInstance GetIterator(Realm realm, GeneratorKind hint = Generato
}
[Pure]
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal IteratorInstance GetIteratorFromMethod(Realm realm, ICallable method)
+ {
+ var iterator = method.Call(this);
+ if (iterator is not ObjectInstance objectInstance)
+ {
+ ExceptionHelper.ThrowTypeError(realm);
+ return null!;
+ }
+ return new IteratorInstance.ObjectIterator(objectInstance);
+ }
+
+ [Pure]
internal bool TryGetIterator(Realm realm, [NotNullWhen(true)] out IteratorInstance? iterator, GeneratorKind hint = GeneratorKind.Sync, ICallable? method = null)
{
var obj = TypeConverter.ToObject(realm, this);
diff --git a/Jint/Native/Json/JsonParser.cs b/Jint/Native/Json/JsonParser.cs
index d1173854e0..3dc380781e 100644
--- a/Jint/Native/Json/JsonParser.cs
+++ b/Jint/Native/Json/JsonParser.cs
@@ -5,8 +5,6 @@
using System.Runtime.InteropServices;
using System.Text;
using Esprima;
-using Jint.Native.Object;
-using Jint.Pooling;
using Jint.Runtime;
namespace Jint.Native.Json
@@ -172,9 +170,9 @@ private string ScanPunctuatorValue(int start, char code)
}
}
- private Token ScanNumericLiteral(ref State state)
+ private Token ScanNumericLiteral()
{
- var sb = state.TokenBuffer;
+ using var sb = new ValueStringBuilder(stackalloc char[64]);
var start = _index;
var ch = _source.CharCodeAt(_index);
var canBeInteger = true;
@@ -249,7 +247,6 @@ private Token ScanNumericLiteral(ref State state)
}
var number = sb.ToString();
- sb.Clear();
JsNumber value;
if (canBeInteger && long.TryParse(number, NumberStyles.Integer, CultureInfo.InvariantCulture, out var longResult) && longResult != -0)
@@ -312,7 +309,7 @@ private Token ScanStringLiteral(ref State state)
int start = _index;
++_index;
- var sb = state.TokenBuffer;
+ using var sb = new ValueStringBuilder(stackalloc char[64]);
while (_index < _length)
{
char ch = _source[_index++];
@@ -382,8 +379,7 @@ private Token ScanStringLiteral(ref State state)
ThrowError(_index, Messages.UnexpectedEOS);
}
- string value = sb.ToString();
- sb.Clear();
+ var value = sb.ToString();
return CreateToken(Tokens.String, value, '\"', new JsString(value), new TextRange(start, _index));
}
@@ -407,14 +403,14 @@ private Token Advance(ref State state)
{
if (IsDecimalDigit(_source.CharCodeAt(_index + 1)))
{
- return ScanNumericLiteral(ref state);
+ return ScanNumericLiteral();
}
return ScanPunctuator();
}
if (IsDecimalDigit(ch))
{
- return ScanNumericLiteral(ref state);
+ return ScanNumericLiteral();
}
if (ch == 't' || ch == 'f')
@@ -704,8 +700,7 @@ public JsValue Parse(string code, ParserOptions? options)
_length = _source.Length;
_lookahead = null!;
- using var wrapper = StringBuilderPool.Rent();
- State state = new State(wrapper.Builder);
+ State state = new State();
Peek(ref state);
JsValue jsv = ParseJsonValue(ref state);
@@ -719,22 +714,9 @@ public JsValue Parse(string code, ParserOptions? options)
return jsv;
}
+ [StructLayout(LayoutKind.Auto)]
private ref struct State
{
- public State(StringBuilder tokenBuffer)
- {
- TokenBuffer = tokenBuffer;
- CurrentDepth = 0;
- }
-
- ///
- /// StringBuilder instance which can be used to collect
- /// characters into a single string. Must only be used
- /// when no child-parser gets called. Must be cleared
- /// after usage.
- ///
- public StringBuilder TokenBuffer { get; }
-
///
/// The current recursion depth
///
diff --git a/Jint/Native/Json/JsonSerializer.cs b/Jint/Native/Json/JsonSerializer.cs
index c13da3cb97..9b011b80ee 100644
--- a/Jint/Native/Json/JsonSerializer.cs
+++ b/Jint/Native/Json/JsonSerializer.cs
@@ -5,11 +5,9 @@
using Jint.Native.BigInt;
using Jint.Native.Boolean;
using Jint.Native.Number;
-using Jint.Native.Number.Dtoa;
using Jint.Native.Object;
using Jint.Native.Proxy;
using Jint.Native.String;
-using Jint.Pooling;
using Jint.Runtime;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Interop;
@@ -54,15 +52,20 @@ public JsValue Serialize(JsValue value, JsValue replacer, JsValue space)
var wrapper = _engine.Realm.Intrinsics.Object.Construct(Arguments.Empty);
wrapper.DefineOwnProperty(JsString.Empty, new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable));
- using var jsonBuilder = StringBuilderPool.Rent();
-
- var target = new SerializerState(jsonBuilder.Builder);
- if (SerializeJSONProperty(JsString.Empty, wrapper, ref target) == SerializeResult.Undefined)
+ string result;
+ var json = new ValueStringBuilder();
+ try
{
- return JsValue.Undefined;
+ if (SerializeJSONProperty(JsString.Empty, wrapper, ref json) == SerializeResult.Undefined)
+ {
+ return JsValue.Undefined;
+ }
}
-
- return new JsString(target.Json.ToString());
+ finally
+ {
+ result = json.ToString();
+ }
+ return new JsString(result);
}
private void SetupReplacer(JsValue replacer)
@@ -154,25 +157,25 @@ private static string BuildSpacingGap(JsValue space)
///
/// https://tc39.es/ecma262/#sec-serializejsonproperty
///
- private SerializeResult SerializeJSONProperty(JsValue key, JsValue holder, ref SerializerState target)
+ private SerializeResult SerializeJSONProperty(JsValue key, JsValue holder, ref ValueStringBuilder json)
{
var value = ReadUnwrappedValue(key, holder);
if (ReferenceEquals(value, JsValue.Null))
{
- target.Json.Append("null");
+ json.Append("null");
return SerializeResult.NotUndefined;
}
if (value.IsBoolean())
{
- target.Json.Append(((JsBoolean) value)._value ? "true" : "false");
+ json.Append(((JsBoolean) value)._value ? "true" : "false");
return SerializeResult.NotUndefined;
}
if (value.IsString())
{
- QuoteJSONString(value.ToString(), target.Json);
+ QuoteJSONString(value.ToString(), ref json);
return SerializeResult.NotUndefined;
}
@@ -182,7 +185,7 @@ private SerializeResult SerializeJSONProperty(JsValue key, JsValue holder, ref S
if (value.IsInteger())
{
- target.Json.Append((long) doubleValue);
+ json.Append(((long) doubleValue).ToString(CultureInfo.InvariantCulture));
return SerializeResult.NotUndefined;
}
@@ -191,16 +194,15 @@ private SerializeResult SerializeJSONProperty(JsValue key, JsValue holder, ref S
{
if (TypeConverter.CanBeStringifiedAsLong(doubleValue))
{
- target.Json.Append((long) doubleValue);
+ json.Append(((long) doubleValue).ToString(CultureInfo.InvariantCulture));
return SerializeResult.NotUndefined;
}
- target.DtoaBuilder.Reset();
- NumberPrototype.NumberToString(doubleValue, target.DtoaBuilder, target.Json);
+ json.Append(NumberPrototype.ToNumberString(doubleValue));
return SerializeResult.NotUndefined;
}
- target.Json.Append(JsString.NullString);
+ json.Append("null");
return SerializeResult.NotUndefined;
}
@@ -213,18 +215,18 @@ private SerializeResult SerializeJSONProperty(JsValue key, JsValue holder, ref S
{
if (CanSerializesAsArray(objectInstance))
{
- SerializeJSONArray(objectInstance, ref target);
+ SerializeJSONArray(objectInstance, ref json);
return SerializeResult.NotUndefined;
}
if (objectInstance is IObjectWrapper wrapper
&& _engine.Options.Interop.SerializeToJson is { } serialize)
{
- target.Json.Append(serialize(wrapper.Target));
+ json.Append(serialize(wrapper.Target));
return SerializeResult.NotUndefined;
}
- SerializeJSONObject(objectInstance, ref target);
+ SerializeJSONObject(objectInstance, ref json);
return SerializeResult.NotUndefined;
}
@@ -305,16 +307,16 @@ private static bool CanSerializesAsArray(ObjectInstance value)
///
/// MethodImplOptions.AggressiveOptimization = 512 which is only exposed in .NET Core.
///
- [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions)512)]
- private static unsafe void QuoteJSONString(string value, StringBuilder target)
+ [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)]
+ private static unsafe void QuoteJSONString(string value, ref ValueStringBuilder json)
{
if (value.Length == 0)
{
- target.Append("\"\"");
+ json.Append("\"\"");
return;
}
- target.Append('"');
+ json.Append('"');
#if NETCOREAPP1_0_OR_GREATER
fixed (char* ptr = value)
@@ -327,7 +329,7 @@ private static unsafe void QuoteJSONString(string value, StringBuilder target)
if (index < 0)
{
// append the remaining text which doesn't need any encoding.
- target.Append(value.AsSpan(offset));
+ json.Append(value.AsSpan(offset));
break;
}
@@ -335,10 +337,10 @@ private static unsafe void QuoteJSONString(string value, StringBuilder target)
if (index - offset > 0)
{
// append everything which does not need any encoding until the found index.
- target.Append(value.AsSpan(offset, index - offset));
+ json.Append(value.AsSpan(offset, index - offset));
}
- AppendJsonStringCharacter(value, ref index, target);
+ AppendJsonStringCharacter(value, ref index, ref json);
offset = index + 1;
remainingLength = value.Length - offset;
@@ -351,59 +353,59 @@ private static unsafe void QuoteJSONString(string value, StringBuilder target)
#else
for (var i = 0; i < value.Length; i++)
{
- AppendJsonStringCharacter(value, ref i, target);
+ AppendJsonStringCharacter(value, ref i, ref json);
}
#endif
- target.Append('"');
+ json.Append('"');
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void AppendJsonStringCharacter(string value, ref int index, StringBuilder target)
+ private static void AppendJsonStringCharacter(string value, ref int index, ref ValueStringBuilder json)
{
var c = value[index];
switch (c)
{
case '\"':
- target.Append("\\\"");
+ json.Append("\\\"");
break;
case '\\':
- target.Append("\\\\");
+ json.Append("\\\\");
break;
case '\b':
- target.Append("\\b");
+ json.Append("\\b");
break;
case '\f':
- target.Append("\\f");
+ json.Append("\\f");
break;
case '\n':
- target.Append("\\n");
+ json.Append("\\n");
break;
case '\r':
- target.Append("\\r");
+ json.Append("\\r");
break;
case '\t':
- target.Append("\\t");
+ json.Append("\\t");
break;
default:
if (char.IsSurrogatePair(value, index))
{
#if NETCOREAPP1_0_OR_GREATER
- target.Append(value.AsSpan(index, 2));
+ json.Append(value.AsSpan(index, 2));
index++;
#else
- target.Append(c);
+ json.Append(c);
index++;
- target.Append(value[index]);
+ json.Append(value[index]);
#endif
}
else if (c < 0x20 || char.IsSurrogate(c))
{
- target.Append("\\u");
- target.Append(((int) c).ToString("x4", CultureInfo.InvariantCulture));
+ json.Append("\\u");
+ json.Append(((int) c).ToString("x4", CultureInfo.InvariantCulture));
}
else
{
- target.Append(c);
+ json.Append(c);
}
break;
}
@@ -412,12 +414,12 @@ private static void AppendJsonStringCharacter(string value, ref int index, Strin
///
/// https://tc39.es/ecma262/#sec-serializejsonarray
///
- private void SerializeJSONArray(ObjectInstance value, ref SerializerState target)
+ private void SerializeJSONArray(ObjectInstance value, ref ValueStringBuilder json)
{
var len = TypeConverter.ToUint32(value.Get(CommonProperties.Length));
if (len == 0)
{
- target.Json.Append("[]");
+ json.Append("[]");
return;
}
@@ -435,22 +437,22 @@ private void SerializeJSONArray(ObjectInstance value, ref SerializerState target
{
if (hasPrevious)
{
- target.Json.Append(separator);
+ json.Append(separator);
}
else
{
- target.Json.Append('[');
+ json.Append('[');
}
if (_gap.Length > 0)
{
- target.Json.Append('\n');
- target.Json.Append(_indent);
+ json.Append('\n');
+ json.Append(_indent);
}
- if (SerializeJSONProperty(i, value, ref target) == SerializeResult.Undefined)
+ if (SerializeJSONProperty(i, value, ref json) == SerializeResult.Undefined)
{
- target.Json.Append(JsString.NullString);
+ json.Append("null");
}
hasPrevious = true;
@@ -460,16 +462,16 @@ private void SerializeJSONArray(ObjectInstance value, ref SerializerState target
{
_stack.Exit();
_indent = stepback;
- target.Json.Append("[]");
+ json.Append("[]");
return;
}
if (_gap.Length > 0)
{
- target.Json.Append('\n');
- target.Json.Append(stepback);
+ json.Append('\n');
+ json.Append(stepback);
}
- target.Json.Append(']');
+ json.Append(']');
_stack.Exit();
_indent = stepback;
@@ -478,14 +480,14 @@ private void SerializeJSONArray(ObjectInstance value, ref SerializerState target
///
/// https://tc39.es/ecma262/#sec-serializejsonobject
///
- private void SerializeJSONObject(ObjectInstance value, ref SerializerState target)
+ private void SerializeJSONObject(ObjectInstance value, ref ValueStringBuilder json)
{
var enumeration = _propertyList is null
? PropertyEnumeration.FromObjectInstance(value)
: PropertyEnumeration.FromList(_propertyList);
if (enumeration.IsEmpty)
{
- target.Json.Append("{}");
+ json.Append("{}");
return;
}
@@ -501,33 +503,33 @@ private void SerializeJSONObject(ObjectInstance value, ref SerializerState targe
for (var i = 0; i < enumeration.Keys.Count; i++)
{
var p = enumeration.Keys[i];
- int position = target.Json.Length;
+ int position = json.Length;
if (hasPrevious)
{
- target.Json.Append(separator);
+ json.Append(separator);
}
else
{
- target.Json.Append('{');
+ json.Append('{');
}
if (_gap.Length > 0)
{
- target.Json.Append('\n');
- target.Json.Append(_indent);
+ json.Append('\n');
+ json.Append(_indent);
}
- QuoteJSONString(p.ToString(), target.Json);
- target.Json.Append(':');
+ QuoteJSONString(p.ToString(), ref json);
+ json.Append(':');
if (_gap.Length > 0)
{
- target.Json.Append(' ');
+ json.Append(' ');
}
- if (SerializeJSONProperty(p, value, ref target) == SerializeResult.Undefined)
+ if (SerializeJSONProperty(p, value, ref json) == SerializeResult.Undefined)
{
- target.Json.Length = position;
+ json.Length = position;
}
else
{
@@ -539,33 +541,21 @@ private void SerializeJSONObject(ObjectInstance value, ref SerializerState targe
{
_stack.Exit();
_indent = stepback;
- target.Json.Append("{}");
+ json.Append("{}");
return;
}
if (_gap.Length > 0)
{
- target.Json.Append('\n');
- target.Json.Append(stepback);
+ json.Append('\n');
+ json.Append(stepback);
}
- target.Json.Append('}');
+ json.Append('}');
_stack.Exit();
_indent = stepback;
}
- private readonly ref struct SerializerState
- {
- public SerializerState(StringBuilder jsonBuilder)
- {
- Json = jsonBuilder;
- DtoaBuilder = TypeConverter.CreateDtoaBuilderForDouble();
- }
-
- public readonly StringBuilder Json;
- public readonly DtoaBuilder DtoaBuilder;
- }
-
private enum SerializeResult
{
NotUndefined,
diff --git a/Jint/Native/Number/Dtoa/BignumDtoa.cs b/Jint/Native/Number/Dtoa/BignumDtoa.cs
index 098f4cedf9..4389c538f5 100644
--- a/Jint/Native/Number/Dtoa/BignumDtoa.cs
+++ b/Jint/Native/Number/Dtoa/BignumDtoa.cs
@@ -11,7 +11,7 @@ public static void NumberToString(
double v,
DtoaMode mode,
int requested_digits,
- DtoaBuilder builder,
+ ref DtoaBuilder builder,
out int decimal_point)
{
var bits = (ulong) BitConverter.DoubleToInt64Bits(v);
@@ -73,7 +73,7 @@ public static void NumberToString(
delta_minus,
delta_plus,
is_even,
- builder);
+ ref builder);
break;
case DtoaMode.Fixed:
BignumToFixed(
@@ -81,7 +81,7 @@ public static void NumberToString(
ref decimal_point,
numerator,
denominator,
- builder);
+ ref builder);
break;
case DtoaMode.Precision:
GenerateCountedDigits(
@@ -89,7 +89,7 @@ public static void NumberToString(
ref decimal_point,
numerator,
denominator,
- builder);
+ ref builder);
break;
default:
ExceptionHelper.ThrowArgumentOutOfRangeException();
@@ -117,7 +117,7 @@ private static void GenerateShortestDigits(
Bignum delta_minus,
Bignum delta_plus,
bool is_even,
- DtoaBuilder buffer)
+ ref DtoaBuilder buffer)
{
// Small optimization: if delta_minus and delta_plus are the same just reuse
// one of the two bignums.
@@ -239,7 +239,7 @@ static void GenerateCountedDigits(
ref int decimal_point,
Bignum numerator,
Bignum denominator,
- DtoaBuilder buffer)
+ ref DtoaBuilder buffer)
{
Debug.Assert(count >= 0);
for (int i = 0; i < count - 1; ++i)
@@ -286,7 +286,7 @@ static void BignumToFixed(
ref int decimal_point,
Bignum numerator,
Bignum denominator,
- DtoaBuilder buffer)
+ ref DtoaBuilder buffer)
{
// Note that we have to look at more than just the requested_digits, since
// a number could be rounded up. Example: v=0.5 with requested_digits=0.
@@ -329,7 +329,7 @@ static void BignumToFixed(
// The requested digits correspond to the digits after the point.
// The variable 'needed_digits' includes the digits before the point.
int needed_digits = (decimal_point) + requested_digits;
- GenerateCountedDigits(needed_digits, ref decimal_point, numerator, denominator, buffer);
+ GenerateCountedDigits(needed_digits, ref decimal_point, numerator, denominator, ref buffer);
}
}
diff --git a/Jint/Native/Number/Dtoa/DtoaBuilder.cs b/Jint/Native/Number/Dtoa/DtoaBuilder.cs
index 07998053d0..f0bc39c504 100644
--- a/Jint/Native/Number/Dtoa/DtoaBuilder.cs
+++ b/Jint/Native/Number/Dtoa/DtoaBuilder.cs
@@ -1,55 +1,53 @@
-#nullable disable
-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-namespace Jint.Native.Number.Dtoa
-{
- internal sealed class DtoaBuilder
- {
- // allocate buffer for generated digits + extra notation + padding zeroes
- internal readonly char[] _chars;
- internal int Length;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
- public DtoaBuilder(int size)
- {
- _chars = new char[size];
- }
+namespace Jint.Native.Number.Dtoa;
- public DtoaBuilder() : this(FastDtoa.KFastDtoaMaximalLength + 8)
- {
- }
+[StructLayout(LayoutKind.Auto)]
+internal ref struct DtoaBuilder
+{
+ // allocate buffer for generated digits + extra notation + padding zeroes
+ internal readonly Span _chars;
+ internal int Length;
- internal void Append(char c)
- {
- _chars[Length++] = c;
- }
+ public DtoaBuilder(Span initialBuffer)
+ {
+ _chars = initialBuffer;
+ }
- internal void DecreaseLast()
- {
- _chars[Length - 1]--;
- }
+ internal void Append(char c)
+ {
+ _chars[Length++] = c;
+ }
- public void Reset()
- {
- Length = 0;
- System.Array.Clear(_chars, 0, _chars.Length);
- }
+ internal void DecreaseLast()
+ {
+ _chars[Length - 1]--;
+ }
- public char this[int i]
- {
- get => _chars[i];
- set
- {
- _chars[i] = value;
- Length = System.Math.Max(Length, i + 1);
- }
- }
+ public void Reset()
+ {
+ Length = 0;
+ _chars.Clear();
+ }
- public override string ToString()
+ public char this[int i]
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _chars[i];
+ set
{
- return "[chars:" + new string(_chars, 0, Length) + "]";
+ _chars[i] = value;
+ Length = System.Math.Max(Length, i + 1);
}
}
-}
\ No newline at end of file
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlySpan Slice(int start, int length) => _chars.Slice(start, length);
+
+ public override string ToString() => "[chars:" + _chars.Slice(0, Length).ToString() + "]";
+}
diff --git a/Jint/Native/Number/Dtoa/DtoaNumberFormatter.cs b/Jint/Native/Number/Dtoa/DtoaNumberFormatter.cs
index 06b9a2b07a..310f72391a 100644
--- a/Jint/Native/Number/Dtoa/DtoaNumberFormatter.cs
+++ b/Jint/Native/Number/Dtoa/DtoaNumberFormatter.cs
@@ -8,7 +8,7 @@ namespace Jint.Native.Number.Dtoa
internal static class DtoaNumberFormatter
{
public static void DoubleToAscii(
- DtoaBuilder buffer,
+ ref DtoaBuilder buffer,
double v,
DtoaMode mode,
int requested_digits,
@@ -44,14 +44,14 @@ public static void DoubleToAscii(
bool fast_worked = false;
switch (mode) {
case DtoaMode.Shortest:
- fast_worked = FastDtoa.NumberToString(v, DtoaMode.Shortest, 0, out point, buffer);
+ fast_worked = FastDtoa.NumberToString(v, DtoaMode.Shortest, 0, out point, ref buffer);
break;
case DtoaMode.Fixed:
//fast_worked = FastFixedDtoa(v, requested_digits, buffer, length, point);
ExceptionHelper.ThrowNotImplementedException();
break;
case DtoaMode.Precision:
- fast_worked = FastDtoa.NumberToString(v, DtoaMode.Precision, requested_digits, out point, buffer);
+ fast_worked = FastDtoa.NumberToString(v, DtoaMode.Precision, requested_digits, out point, ref buffer);
break;
default:
ExceptionHelper.ThrowArgumentOutOfRangeException();
@@ -65,7 +65,7 @@ public static void DoubleToAscii(
// If the fast dtoa didn't succeed use the slower bignum version.
buffer.Reset();
- BignumDtoa.NumberToString(v, mode, requested_digits, buffer, out point);
+ BignumDtoa.NumberToString(v, mode, requested_digits, ref buffer, out point);
}
}
-}
\ No newline at end of file
+}
diff --git a/Jint/Native/Number/Dtoa/FastDtoa.cs b/Jint/Native/Number/Dtoa/FastDtoa.cs
index fed935103c..8846eb4b83 100644
--- a/Jint/Native/Number/Dtoa/FastDtoa.cs
+++ b/Jint/Native/Number/Dtoa/FastDtoa.cs
@@ -64,7 +64,7 @@ internal sealed class FastDtoa
// representable number to the input.
// Modifies the generated digits in the buffer to approach (round towards) w.
private static bool RoundWeed(
- DtoaBuilder buffer,
+ ref DtoaBuilder buffer,
ulong distanceTooHighW,
ulong unsafeInterval,
ulong rest,
@@ -183,7 +183,7 @@ private static bool RoundWeed(
//
// Precondition: rest < ten_kappa.
static bool RoundWeedCounted(
- DtoaBuilder buffer,
+ ref DtoaBuilder buffer,
ulong rest,
ulong ten_kappa,
ulong unit,
@@ -413,7 +413,7 @@ private static bool DigitGen(
in DiyFp low,
in DiyFp w,
in DiyFp high,
- DtoaBuilder buffer,
+ ref DtoaBuilder buffer,
int mk,
out int kappa)
{
@@ -473,7 +473,7 @@ private static bool DigitGen(
// Rounding down (by not emitting the remaining digits) yields a number
// that lies within the unsafe interval.
return RoundWeed(
- buffer,
+ ref buffer,
DiyFp.Minus(tooHigh, w).F,
unsafeInterval.F,
rest,
@@ -511,7 +511,7 @@ private static bool DigitGen(
if (fractionals < unsafeInterval.F)
{
return RoundWeed(
- buffer,
+ ref buffer,
DiyFp.Minus(tooHigh, w).F*unit,
unsafeInterval.F,
fractionals,
@@ -552,7 +552,7 @@ private static bool DigitGen(
static bool DigitGenCounted(
in DiyFp w,
int requested_digits,
- DtoaBuilder buffer,
+ ref DtoaBuilder buffer,
out int kappa)
{
Debug.Assert(MinimalTargetExponent <= w.E && w.E <= MaximalTargetExponent);
@@ -592,7 +592,7 @@ static bool DigitGenCounted(
if (requested_digits == 0)
{
ulong rest = (((ulong) integrals) << -one.E) + fractionals;
- return RoundWeedCounted(buffer, rest,(ulong) divisor << -one.E, w_error, ref kappa);
+ return RoundWeedCounted(ref buffer, rest,(ulong) divisor << -one.E, w_error, ref kappa);
}
// The integrals have been generated. We are at the point of the decimal
@@ -615,7 +615,7 @@ static bool DigitGenCounted(
(kappa)--;
}
if (requested_digits != 0) return false;
- return RoundWeedCounted(buffer, fractionals, one.F, w_error, ref kappa);
+ return RoundWeedCounted(ref buffer, fractionals, one.F, w_error, ref kappa);
}
// Provides a decimal representation of v.
@@ -629,7 +629,7 @@ static bool DigitGenCounted(
// The last digit will be closest to the actual v. That is, even if several
// digits might correctly yield 'v' when read again, the closest will be
// computed.
- private static bool Grisu3(double v, DtoaBuilder buffer, out int decimal_exponent)
+ private static bool Grisu3(double v, ref DtoaBuilder buffer, out int decimal_exponent)
{
ulong bits = (ulong) BitConverter.DoubleToInt64Bits(v);
DiyFp w = DoubleHelper.AsNormalizedDiyFp(bits);
@@ -681,7 +681,7 @@ private static bool Grisu3(double v, DtoaBuilder buffer, out int decimal_exponen
// the buffer will be filled with "123" und the decimal_exponent will be
// decreased by 2.
int kappa;
- var digitGen = DigitGen(scaledBoundaryMinus, scaledW, scaledBoundaryPlus, buffer, mk, out kappa);
+ var digitGen = DigitGen(scaledBoundaryMinus, scaledW, scaledBoundaryPlus, ref buffer, mk, out kappa);
decimal_exponent = -mk + kappa;
return digitGen;
}
@@ -695,7 +695,7 @@ private static bool Grisu3(double v, DtoaBuilder buffer, out int decimal_exponen
static bool Grisu3Counted(
double v,
int requested_digits,
- DtoaBuilder buffer,
+ ref DtoaBuilder buffer,
out int decimal_exponent)
{
ulong bits = (ulong) BitConverter.DoubleToInt64Bits(v);
@@ -725,7 +725,7 @@ static bool Grisu3Counted(
// return together with a kappa such that scaled_w ~= buffer * 10^kappa. (It
// will not always be exactly the same since DigitGenCounted only produces a
// limited number of digits.)
- bool result = DigitGenCounted(scaled_w, requested_digits, buffer, out var kappa);
+ bool result = DigitGenCounted(scaled_w, requested_digits, ref buffer, out var kappa);
decimal_exponent = -mk + kappa;
return result;
}
@@ -735,7 +735,7 @@ public static bool NumberToString(
DtoaMode mode,
int requested_digits,
out int decimal_point,
- DtoaBuilder buffer)
+ ref DtoaBuilder buffer)
{
Debug.Assert(v > 0);
Debug.Assert(!double.IsNaN(v));
@@ -746,10 +746,10 @@ public static bool NumberToString(
switch (mode)
{
case DtoaMode.Shortest:
- result = Grisu3(v, buffer, out decimal_exponent);
+ result = Grisu3(v, ref buffer, out decimal_exponent);
break;
case DtoaMode.Precision:
- result = Grisu3Counted(v, requested_digits, buffer, out decimal_exponent);
+ result = Grisu3Counted(v, requested_digits, ref buffer, out decimal_exponent);
break;
default:
ExceptionHelper.ThrowArgumentOutOfRangeException();
diff --git a/Jint/Native/Number/NumberPrototype.cs b/Jint/Native/Number/NumberPrototype.cs
index 186be11282..8e4f8cdeba 100644
--- a/Jint/Native/Number/NumberPrototype.cs
+++ b/Jint/Native/Number/NumberPrototype.cs
@@ -4,7 +4,6 @@
using Jint.Collections;
using Jint.Native.Number.Dtoa;
using Jint.Native.Object;
-using Jint.Pooling;
using Jint.Runtime;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Interop;
@@ -16,6 +15,9 @@ namespace Jint.Native.Number
///
internal sealed class NumberPrototype : NumberInstance
{
+ private const int SmallDtoaLength = FastDtoa.KFastDtoaMaximalLength + 8;
+ private const int LargeDtoaLength = 101;
+
private readonly Realm _realm;
private readonly NumberConstructor _constructor;
@@ -201,12 +203,12 @@ private JsValue ToExponential(JsValue thisObject, JsValue[] arguments)
}
int decimalPoint;
- DtoaBuilder dtoaBuilder;
+ var dtoaBuilder = new DtoaBuilder(stackalloc char[f == -1 ? SmallDtoaLength : LargeDtoaLength]);
+
if (f == -1)
{
- dtoaBuilder = new DtoaBuilder();
DtoaNumberFormatter.DoubleToAscii(
- dtoaBuilder,
+ ref dtoaBuilder,
x,
DtoaMode.Shortest,
requested_digits: 0,
@@ -216,9 +218,8 @@ private JsValue ToExponential(JsValue thisObject, JsValue[] arguments)
}
else
{
- dtoaBuilder = new DtoaBuilder(101);
DtoaNumberFormatter.DoubleToAscii(
- dtoaBuilder,
+ ref dtoaBuilder,
x,
DtoaMode.Precision,
requested_digits: f + 1,
@@ -230,7 +231,7 @@ private JsValue ToExponential(JsValue thisObject, JsValue[] arguments)
Debug.Assert(dtoaBuilder.Length <= f + 1);
int exponent = decimalPoint - 1;
- var result = CreateExponentialRepresentation(dtoaBuilder, exponent, negative, f+1);
+ var result = CreateExponentialRepresentation(ref dtoaBuilder, exponent, negative, f+1);
return result;
}
@@ -266,9 +267,9 @@ private JsValue ToPrecision(JsValue thisObject, JsValue[] arguments)
ExceptionHelper.ThrowRangeError(_realm, "precision must be between 1 and 100");
}
- var dtoaBuilder = new DtoaBuilder(101);
+ var dtoaBuilder = new DtoaBuilder(stackalloc char[LargeDtoaLength]);
DtoaNumberFormatter.DoubleToAscii(
- dtoaBuilder,
+ ref dtoaBuilder,
x,
DtoaMode.Precision,
p,
@@ -279,50 +280,49 @@ private JsValue ToPrecision(JsValue thisObject, JsValue[] arguments)
int exponent = decimalPoint - 1;
if (exponent < -6 || exponent >= p)
{
- return CreateExponentialRepresentation(dtoaBuilder, exponent, negative, p);
+ return CreateExponentialRepresentation(ref dtoaBuilder, exponent, negative, p);
}
- using (var builder = StringBuilderPool.Rent())
+ var sb = new ValueStringBuilder(stackalloc char[128]);
+
+ // Use fixed notation.
+ if (negative)
{
- // Use fixed notation.
- if (negative)
- {
- builder.Builder.Append('-');
- }
+ sb.Append('-');
+ }
- if (decimalPoint <= 0)
- {
- builder.Builder.Append("0.");
- builder.Builder.Append('0', -decimalPoint);
- builder.Builder.Append(dtoaBuilder._chars, 0, dtoaBuilder.Length);
- builder.Builder.Append('0', p - dtoaBuilder.Length);
- }
- else
+ if (decimalPoint <= 0)
+ {
+ sb.Append("0.");
+ sb.Append('0', -decimalPoint);
+ sb.Append(dtoaBuilder._chars.Slice(0, dtoaBuilder.Length));
+ sb.Append('0', p - dtoaBuilder.Length);
+ }
+ else
+ {
+ int m = System.Math.Min(dtoaBuilder.Length, decimalPoint);
+ sb.Append(dtoaBuilder._chars.Slice(0, m));
+ sb.Append('0', System.Math.Max(0, decimalPoint - dtoaBuilder.Length));
+ if (decimalPoint < p)
{
- int m = System.Math.Min(dtoaBuilder.Length, decimalPoint);
- builder.Builder.Append(dtoaBuilder._chars, 0, m);
- builder.Builder.Append('0', System.Math.Max(0, decimalPoint - dtoaBuilder.Length));
- if (decimalPoint < p)
+ sb.Append('.');
+ var extra = negative ? 2 : 1;
+ if (dtoaBuilder.Length > decimalPoint)
{
- builder.Builder.Append('.');
- var extra = negative ? 2 : 1;
- if (dtoaBuilder.Length > decimalPoint)
- {
- int len = dtoaBuilder.Length - decimalPoint;
- int n = System.Math.Min(len, p - (builder.Builder.Length - extra));
- builder.Builder.Append(dtoaBuilder._chars, decimalPoint, n);
- }
-
- builder.Builder.Append('0', System.Math.Max(0, extra + (p - builder.Builder.Length)));
+ int len = dtoaBuilder.Length - decimalPoint;
+ int n = System.Math.Min(len, p - (sb.Length - extra));
+ sb.Append(dtoaBuilder._chars.Slice(decimalPoint, n));
}
- }
- return builder.ToString();
+ sb.Append('0', System.Math.Max(0, extra + (p - sb.Length)));
+ }
}
+
+ return sb.ToString();
}
private static string CreateExponentialRepresentation(
- DtoaBuilder buffer,
+ ref DtoaBuilder buffer,
int exponent,
bool negative,
int significantDigits)
@@ -334,26 +334,25 @@ private static string CreateExponentialRepresentation(
exponent = -exponent;
}
- using (var builder = StringBuilderPool.Rent())
+ var sb = new ValueStringBuilder(stackalloc char[128]);
+ if (negative)
{
- if (negative)
- {
- builder.Builder.Append('-');
- }
- builder.Builder.Append(buffer._chars[0]);
- if (significantDigits != 1)
- {
- builder.Builder.Append('.');
- builder.Builder.Append(buffer._chars, 1, buffer.Length - 1);
- int length = buffer.Length;
- builder.Builder.Append('0', significantDigits - length);
- }
-
- builder.Builder.Append('e');
- builder.Builder.Append(negativeExponent ? '-' : '+');
- builder.Builder.Append(exponent);
- return builder.ToString();
+ sb.Append('-');
}
+ sb.Append(buffer[0]);
+ if (significantDigits != 1)
+ {
+ sb.Append('.');
+ sb.Append(buffer.Slice(1, buffer.Length - 1));
+ int length = buffer.Length;
+ sb.Append('0', significantDigits - length);
+ }
+
+ sb.Append('e');
+ sb.Append(negativeExponent ? '-' : '+');
+ sb.Append(exponent.ToString(CultureInfo.InvariantCulture));
+
+ return sb.ToString();
}
private JsValue ToNumberString(JsValue thisObject, JsValue[] arguments)
@@ -419,15 +418,15 @@ internal static string ToBase(long n, int radix)
return "0";
}
- using var result = StringBuilderPool.Rent();
+ var sb = new ValueStringBuilder(stackalloc char[64]);
while (n > 0)
{
var digit = (int) (n % radix);
- n = n / radix;
- result.Builder.Insert(0, Digits[digit]);
+ n /= radix;
+ sb.Append(Digits[digit]);
}
-
- return result.ToString();
+ sb.Reverse();
+ return sb.ToString();
}
internal static string ToFractionBase(double n, int radix)
@@ -441,57 +440,48 @@ internal static string ToFractionBase(double n, int radix)
return "0";
}
- using var result = StringBuilderPool.Rent();
+ var result = new ValueStringBuilder(stackalloc char[64]);
while (n > 0 && result.Length < 50) // arbitrary limit
{
var c = n*radix;
var d = (int) c;
n = c - d;
- result.Builder.Append(Digits[d]);
+ result.Append(Digits[d]);
}
return result.ToString();
}
- private static string ToNumberString(double m)
- {
- using var stringBuilder = StringBuilderPool.Rent();
- NumberToString(m, new DtoaBuilder(), stringBuilder.Builder);
- return stringBuilder.Builder.ToString();
- }
-
- internal static void NumberToString(
- double m,
- DtoaBuilder builder,
- StringBuilder stringBuilder)
+ internal static string ToNumberString(double m)
{
if (double.IsNaN(m))
{
- stringBuilder.Append("NaN");
- return;
+ return "NaN";
}
if (m == 0)
{
- stringBuilder.Append('0');
- return;
+ return "0";
}
if (double.IsInfinity(m))
{
- stringBuilder.Append(double.IsNegativeInfinity(m) ? "-Infinity" : "Infinity");
- return;
+ return double.IsNegativeInfinity(m) ? "-Infinity" : "Infinity";
}
+ var builder = new DtoaBuilder(stackalloc char[SmallDtoaLength]);
+
DtoaNumberFormatter.DoubleToAscii(
- builder,
+ ref builder,
m,
DtoaMode.Shortest,
0,
out var negative,
out var decimal_point);
+
+ var stringBuilder = new ValueStringBuilder(stackalloc char[64]);
if (negative)
{
stringBuilder.Append('-');
@@ -500,22 +490,22 @@ internal static void NumberToString(
if (builder.Length <= decimal_point && decimal_point <= 21)
{
// ECMA-262 section 9.8.1 step 6.
- stringBuilder.Append(builder._chars, 0, builder.Length);
+ stringBuilder.Append(builder._chars.Slice(0, builder.Length));
stringBuilder.Append('0', decimal_point - builder.Length);
}
else if (0 < decimal_point && decimal_point <= 21)
{
// ECMA-262 section 9.8.1 step 7.
- stringBuilder.Append(builder._chars, 0, decimal_point);
+ stringBuilder.Append(builder._chars.Slice(0, decimal_point));
stringBuilder.Append('.');
- stringBuilder.Append(builder._chars, decimal_point, builder.Length - decimal_point);
+ stringBuilder.Append(builder._chars.Slice(decimal_point, builder.Length - decimal_point));
}
else if (decimal_point <= 0 && decimal_point > -6)
{
// ECMA-262 section 9.8.1 step 8.
stringBuilder.Append("0.");
stringBuilder.Append('0', -decimal_point);
- stringBuilder.Append(builder._chars, 0, builder.Length);
+ stringBuilder.Append(builder._chars.Slice(0, builder.Length));
}
else
{
@@ -524,7 +514,7 @@ internal static void NumberToString(
if (builder.Length != 1)
{
stringBuilder.Append('.');
- stringBuilder.Append(builder._chars, 1, builder.Length - 1);
+ stringBuilder.Append(builder._chars.Slice(1, builder.Length - 1));
}
stringBuilder.Append('e');
@@ -535,8 +525,10 @@ internal static void NumberToString(
exponent = -exponent;
}
- stringBuilder.Append(exponent);
+ stringBuilder.Append(exponent.ToString(CultureInfo.InvariantCulture));
}
+
+ return stringBuilder.ToString();
}
}
}
diff --git a/Jint/Native/RegExp/RegExpPrototype.cs b/Jint/Native/RegExp/RegExpPrototype.cs
index 20f989a52b..53c4bdff7c 100644
--- a/Jint/Native/RegExp/RegExpPrototype.cs
+++ b/Jint/Native/RegExp/RegExpPrototype.cs
@@ -1,12 +1,12 @@
#pragma warning disable CA1859 // Use concrete types when possible for improved performance -- most of prototype methods return JsValue
+using System.Text;
using System.Text.RegularExpressions;
using Jint.Collections;
using Jint.Native.Number;
using Jint.Native.Object;
using Jint.Native.String;
using Jint.Native.Symbol;
-using Jint.Pooling;
using Jint.Runtime;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Interop;
@@ -337,8 +337,7 @@ internal static string GetSubstitution(
// $` Inserts the portion of the string that precedes the matched substring.
// $' Inserts the portion of the string that follows the matched substring.
// $n or $nn Where n or nn are decimal digits, inserts the nth parenthesized submatch string, provided the first argument was a RegExp object.
- using var replacementBuilder = StringBuilderPool.Rent();
- var sb = replacementBuilder.Builder;
+ using var sb = new ValueStringBuilder(stackalloc char[128]);
for (var i = 0; i < replacement.Length; i++)
{
char c = replacement[i];
@@ -353,14 +352,12 @@ internal static string GetSubstitution(
case '&':
sb.Append(matched);
break;
-#pragma warning disable CA1846
case '`':
- sb.Append(str.Substring(0, position));
+ sb.Append(str.AsSpan(0, position));
break;
case '\'':
- sb.Append(str.Substring(position + matched.Length));
+ sb.Append(str.AsSpan(position + matched.Length));
break;
-#pragma warning restore CA1846
case '<':
var gtPos = replacement.IndexOf('>', i + 1);
if (gtPos == -1 || namedCaptures.IsUndefined())
@@ -430,7 +427,7 @@ internal static string GetSubstitution(
}
}
- return replacementBuilder.ToString();
+ return sb.ToString();
}
///
diff --git a/Jint/Native/Set/JsSet.cs b/Jint/Native/Set/JsSet.cs
index b5fb4d4553..ea95fccecb 100644
--- a/Jint/Native/Set/JsSet.cs
+++ b/Jint/Native/Set/JsSet.cs
@@ -9,9 +9,13 @@ internal sealed class JsSet : ObjectInstance
{
internal readonly OrderedSet _set;
- public JsSet(Engine engine) : base(engine)
+ public JsSet(Engine engine) : this(engine, new OrderedSet(SameValueZeroComparer.Instance))
{
- _set = new OrderedSet(SameValueZeroComparer.Instance);
+ }
+
+ public JsSet(Engine engine, OrderedSet set) : base(engine)
+ {
+ _set = set;
}
public override PropertyDescriptor GetOwnProperty(JsValue property)
diff --git a/Jint/Native/Set/SetConstructor.cs b/Jint/Native/Set/SetConstructor.cs
index 2335432519..75b3656cbd 100644
--- a/Jint/Native/Set/SetConstructor.cs
+++ b/Jint/Native/Set/SetConstructor.cs
@@ -25,7 +25,7 @@ internal SetConstructor(
_prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
}
- private SetPrototype PrototypeObject { get; }
+ internal SetPrototype PrototypeObject { get; }
protected override void Initialize()
{
diff --git a/Jint/Native/Set/SetPrototype.cs b/Jint/Native/Set/SetPrototype.cs
index 11e0753c52..b67d5d55e2 100644
--- a/Jint/Native/Set/SetPrototype.cs
+++ b/Jint/Native/Set/SetPrototype.cs
@@ -32,15 +32,16 @@ protected override void Initialize()
{
["length"] = new PropertyDescriptor(0, PropertyFlag.Configurable),
["constructor"] = new PropertyDescriptor(_constructor, PropertyFlag.NonEnumerable),
- ["add"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "add", Add, 1, PropertyFlag.Configurable), true, false, true),
- ["clear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "clear", Clear, 0, PropertyFlag.Configurable), true, false, true),
- ["delete"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "delete", Delete, 1, PropertyFlag.Configurable), true, false, true),
- ["entries"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "entries", Entries, 0, PropertyFlag.Configurable), true, false, true),
- ["forEach"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "forEach", ForEach, 1, PropertyFlag.Configurable), true, false, true),
- ["has"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "has", Has, 1, PropertyFlag.Configurable), true, false, true),
- ["keys"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "keys", Values, 0, PropertyFlag.Configurable), true, false, true),
- ["values"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "values", Values, 0, PropertyFlag.Configurable), true, false, true),
- ["size"] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(Engine, "get size", Size, 0, PropertyFlag.Configurable), set: null, PropertyFlag.Configurable)
+ ["add"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "add", Add, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
+ ["clear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "clear", Clear, 0, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
+ ["delete"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "delete", Delete, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
+ ["entries"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "entries", Entries, 0, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
+ ["forEach"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "forEach", ForEach, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
+ ["has"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "has", Has, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
+ ["keys"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "keys", Values, 0, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
+ ["values"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "values", Values, 0, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
+ ["size"] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(Engine, "get size", Size, 0, PropertyFlag.Configurable), set: null, PropertyFlag.Configurable),
+ ["union"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "union", Union, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable)
};
SetProperties(properties);
@@ -112,6 +113,68 @@ private JsValue ForEach(JsValue thisObject, JsValue[] arguments)
return Undefined;
}
+ private JsValue Union(JsValue thisObject, JsValue[] arguments)
+ {
+ var set = AssertSetInstance(thisObject);
+ var other = arguments.At(0);
+ var otherRec = GetSetRecord(other);
+ var keysIter = otherRec.Set.GetIteratorFromMethod(_realm, otherRec.Keys);
+ var resultSetData = set._set.Clone();
+ while (keysIter.TryIteratorStep(out var next))
+ {
+ var nextValue = next.Get(CommonProperties.Value);
+ if (nextValue == JsNumber.NegativeZero)
+ {
+ nextValue = JsNumber.PositiveZero;
+ }
+ resultSetData.Add(nextValue);
+ }
+
+ var result = new JsSet(_engine, resultSetData)
+ {
+ _prototype = _engine.Realm.Intrinsics.Set.PrototypeObject
+ };
+ return result;
+
+ }
+
+ private readonly record struct SetRecord(JsValue Set, double Size, ICallable Has, ICallable Keys);
+
+ private SetRecord GetSetRecord(JsValue obj)
+ {
+ if (obj is not ObjectInstance)
+ {
+ ExceptionHelper.ThrowTypeError(_realm);
+ }
+
+ var rawSize = obj.Get("size");
+ var numSize = TypeConverter.ToNumber(rawSize);
+ if (double.IsNaN(numSize))
+ {
+ ExceptionHelper.ThrowTypeError(_realm);
+ }
+
+ var intSize = TypeConverter.ToIntegerOrInfinity(numSize);
+ if (intSize < 0)
+ {
+ ExceptionHelper.ThrowRangeError(_realm);
+ }
+
+ var has = obj.Get(CommonProperties.Has);
+ if (!has.IsCallable)
+ {
+ ExceptionHelper.ThrowTypeError(_realm);
+ }
+
+ var keys = obj.Get(CommonProperties.Keys);
+ if (!keys.IsCallable)
+ {
+ ExceptionHelper.ThrowTypeError(_realm);
+ }
+
+ return new SetRecord(Set: obj, Size: intSize, Has: (ICallable) has, Keys: (ICallable) keys);
+ }
+
private ObjectInstance Values(JsValue thisObject, JsValue[] arguments)
{
var set = AssertSetInstance(thisObject);
diff --git a/Jint/Native/String/StringConstructor.cs b/Jint/Native/String/StringConstructor.cs
index 5a30c3df72..36063335aa 100644
--- a/Jint/Native/String/StringConstructor.cs
+++ b/Jint/Native/String/StringConstructor.cs
@@ -1,10 +1,10 @@
#pragma warning disable CA1859 // Use concrete types when possible for improved performance -- most of prototype methods return JsValue
+using System.Text;
using Jint.Collections;
using Jint.Native.Array;
using Jint.Native.Function;
using Jint.Native.Object;
-using Jint.Pooling;
using Jint.Runtime;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Interop;
@@ -76,48 +76,60 @@ private static JsValue FromCharCode(JsValue? thisObj, JsValue[] arguments)
return JsString.Create(new string(elements));
}
+ ///
+ /// https://tc39.es/ecma262/#sec-string.fromcodepoint
+ ///
private JsValue FromCodePoint(JsValue thisObject, JsValue[] arguments)
{
- var codeUnits = new List();
- string result = "";
+ JsNumber codePoint;
+ using var result = new ValueStringBuilder(stackalloc char[128]);
foreach (var a in arguments)
{
- var codePoint = TypeConverter.ToNumber(a);
- if (codePoint < 0
- || codePoint > 0x10FFFF
- || double.IsInfinity(codePoint)
- || double.IsNaN(codePoint)
- || TypeConverter.ToInt32(codePoint) != codePoint)
+ int point;
+ codePoint = TypeConverter.ToJsNumber(a);
+ if (codePoint.IsInteger())
{
- ExceptionHelper.ThrowRangeError(_realm, "Invalid code point " + codePoint);
+ point = (int) codePoint._value;
+ if (point is < 0 or > 0x10FFFF)
+ {
+ goto rangeError;
+ }
+ }
+ else
+ {
+ var pointTemp = codePoint._value;
+ if (pointTemp < 0 || pointTemp > 0x10FFFF || double.IsInfinity(pointTemp) || double.IsNaN(pointTemp) || TypeConverter.ToInt32(pointTemp) != pointTemp)
+ {
+ goto rangeError;
+ }
+
+ point = (int) pointTemp;
}
- var point = (uint) codePoint;
if (point <= 0xFFFF)
{
// BMP code point
- codeUnits.Add(JsNumber.Create(point));
+ result.Append((char) point);
}
else
{
// Astral code point; split in surrogate halves
// https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
point -= 0x10000;
- codeUnits.Add(JsNumber.Create((point >> 10) + 0xD800)); // highSurrogate
- codeUnits.Add(JsNumber.Create((point % 0x400) + 0xDC00)); // lowSurrogate
- }
- if (codeUnits.Count >= 0x3fff)
- {
- result += FromCharCode(null, codeUnits.ToArray());
- codeUnits.Clear();
+ result.Append((char) ((point >> 10) + 0xD800)); // highSurrogate
+ result.Append((char) (point % 0x400 + 0xDC00)); // lowSurrogate
}
}
- return result + FromCharCode(null, codeUnits.ToArray());
+ return JsString.Create(result.ToString());
+
+ rangeError:
+ _engine.SignalError(ExceptionHelper.CreateRangeError(_realm, "Invalid code point " + codePoint));
+ return null!;
}
///
- /// https://www.ecma-international.org/ecma-262/6.0/#sec-string.raw
+ /// https://tc39.es/ecma262/#sec-string.raw
///
private JsValue Raw(JsValue thisObject, JsValue[] arguments)
{
@@ -132,18 +144,18 @@ private JsValue Raw(JsValue thisObject, JsValue[] arguments)
return JsString.Empty;
}
- using var result = StringBuilderPool.Rent();
+ using var result = new ValueStringBuilder();
for (var i = 0; i < length; i++)
{
if (i > 0)
{
if (i < arguments.Length && !arguments[i].IsUndefined())
{
- result.Builder.Append(TypeConverter.ToString(arguments[i]));
+ result.Append(TypeConverter.ToString(arguments[i]));
}
}
- result.Builder.Append(TypeConverter.ToString(operations.Get((ulong) i)));
+ result.Append(TypeConverter.ToString(operations.Get((ulong) i)));
}
return result.ToString();
diff --git a/Jint/Native/String/StringPrototype.cs b/Jint/Native/String/StringPrototype.cs
index 1cf2201bef..f203b4837e 100644
--- a/Jint/Native/String/StringPrototype.cs
+++ b/Jint/Native/String/StringPrototype.cs
@@ -9,7 +9,6 @@
using Jint.Native.Object;
using Jint.Native.RegExp;
using Jint.Native.Symbol;
-using Jint.Pooling;
using Jint.Runtime;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Interop;
@@ -643,8 +642,7 @@ static int StringIndexOf(string s, string search, int fromIndex)
var advanceBy = System.Math.Max(1, searchLength);
var endOfLastMatch = 0;
- using var pool = StringBuilderPool.Rent();
- var result = pool.Builder;
+ using var result = new ValueStringBuilder();
var position = StringIndexOf(thisString, searchString, 0);
while (position != -1)
@@ -662,7 +660,9 @@ static int StringIndexOf(string s, string search, int fromIndex)
replacement = RegExpPrototype.GetSubstitution(searchString, thisString, position, captures, Undefined, TypeConverter.ToString(replaceValue));
}
- result.Append(preserved).Append(replacement);
+ result.Append(preserved);
+ result.Append(replacement);
+
endOfLastMatch = position + searchLength;
position = StringIndexOf(thisString, searchString, position + advanceBy);
@@ -671,7 +671,7 @@ static int StringIndexOf(string s, string search, int fromIndex)
if (endOfLastMatch < thisString.Length)
{
#if NETFRAMEWORK
- result.Append(thisString.Substring(endOfLastMatch));
+ result.Append(thisString.AsSpan(endOfLastMatch));
#else
result.Append(thisString[endOfLastMatch..]);
#endif
@@ -1144,11 +1144,10 @@ private JsValue Repeat(JsValue thisObject, JsValue[] arguments)
return new string(s[0], (int) n);
}
- using var sb = StringBuilderPool.Rent();
- sb.Builder.EnsureCapacity((int) (n * s.Length));
+ var sb = new ValueStringBuilder((int) (n * s.Length));
for (var i = 0; i < n; ++i)
{
- sb.Builder.Append(s);
+ sb.Append(s);
}
return sb.ToString();
@@ -1170,8 +1169,7 @@ private JsValue ToWellFormed(JsValue thisObject, JsValue[] arguments)
var strLen = s.Length;
var k = 0;
- using var builder = StringBuilderPool.Rent();
- var result = builder.Builder;
+ var result = new ValueStringBuilder();
while (k < strLen)
{
var cp = CodePointAt(s, k);
@@ -1182,7 +1180,7 @@ private JsValue ToWellFormed(JsValue thisObject, JsValue[] arguments)
}
else
{
- result.Append(s, k, cp.CodeUnitCount);
+ result.Append(s.AsSpan(k, cp.CodeUnitCount));
}
k += cp.CodeUnitCount;
}
diff --git a/Jint/Native/TypedArray/IntrinsicTypedArrayPrototype.cs b/Jint/Native/TypedArray/IntrinsicTypedArrayPrototype.cs
index dff4978054..b494970002 100644
--- a/Jint/Native/TypedArray/IntrinsicTypedArrayPrototype.cs
+++ b/Jint/Native/TypedArray/IntrinsicTypedArrayPrototype.cs
@@ -1,6 +1,7 @@
#pragma warning disable CA1859 // Use concrete types when possible for improved performance -- most of prototype methods return JsValue
using System.Linq;
+using System.Text;
using Jint.Collections;
using Jint.Native.Array;
using Jint.Native.ArrayBuffer;
@@ -8,7 +9,6 @@
using Jint.Native.Number;
using Jint.Native.Object;
using Jint.Native.Symbol;
-using Jint.Pooling;
using Jint.Runtime;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Interop;
@@ -637,15 +637,15 @@ static string StringFromJsValue(JsValue value)
return s;
}
- using var sb = StringBuilderPool.Rent();
- sb.Builder.Append(s);
+ using var result = new ValueStringBuilder();
+ result.Append(s);
for (var k = 1; k < len; k++)
{
- sb.Builder.Append(sep);
- sb.Builder.Append(StringFromJsValue(o[k]));
+ result.Append(sep);
+ result.Append(StringFromJsValue(o[k]));
}
- return sb.ToString();
+ return result.ToString();
}
///
diff --git a/Jint/Pooling/StringBuilderPool.cs b/Jint/Pooling/StringBuilderPool.cs
deleted file mode 100644
index c5184d7ac0..0000000000
--- a/Jint/Pooling/StringBuilderPool.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Diagnostics;
-using System.Text;
-
-namespace Jint.Pooling
-{
- ///
- /// Pooling of StringBuilder instances.
- ///
- internal static class StringBuilderPool
- {
- private static readonly ConcurrentObjectPool _pool;
-
- static StringBuilderPool()
- {
- _pool = new ConcurrentObjectPool(() => new StringBuilder());
- }
-
- public static BuilderWrapper Rent()
- {
- var builder = _pool.Allocate();
- Debug.Assert(builder.Length == 0);
- return new BuilderWrapper(builder, _pool);
- }
-
- internal readonly struct BuilderWrapper : IDisposable
- {
- public readonly StringBuilder Builder;
- private readonly ConcurrentObjectPool _pool;
-
- public BuilderWrapper(StringBuilder builder, ConcurrentObjectPool pool)
- {
- Builder = builder;
- _pool = pool;
- }
-
- public int Length => Builder.Length;
-
- public override string ToString()
- {
- return Builder.ToString();
- }
-
- public void Dispose()
- {
- var builder = Builder;
-
- // do not store builders that are too large.
- if (builder.Capacity <= 1024 * 1024)
- {
- builder.Clear();
- _pool.Free(builder);
- }
- else
- {
- _pool.ForgetTrackedObject(builder);
- }
- }
- }
- }
-}
diff --git a/Jint/Pooling/ValueStringBuilder.cs b/Jint/Pooling/ValueStringBuilder.cs
new file mode 100644
index 0000000000..f5bd679329
--- /dev/null
+++ b/Jint/Pooling/ValueStringBuilder.cs
@@ -0,0 +1,336 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Buffers;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// ReSharper disable once CheckNamespace
+namespace System.Text;
+
+internal ref struct ValueStringBuilder
+{
+ private char[]? _arrayToReturnToPool;
+ private Span _chars;
+ private int _pos;
+
+ public ValueStringBuilder(Span initialBuffer)
+ {
+ _arrayToReturnToPool = null;
+ _chars = initialBuffer;
+ _pos = 0;
+ }
+
+ public ValueStringBuilder(int initialCapacity)
+ {
+ _arrayToReturnToPool = ArrayPool.Shared.Rent(initialCapacity);
+ _chars = _arrayToReturnToPool;
+ _pos = 0;
+ }
+
+ public int Length
+ {
+ get => _pos;
+ set
+ {
+ Debug.Assert(value >= 0);
+ Debug.Assert(value <= _chars.Length);
+ _pos = value;
+ }
+ }
+
+ public int Capacity => _chars.Length;
+
+ public void EnsureCapacity(int capacity)
+ {
+ // This is not expected to be called this with negative capacity
+ Debug.Assert(capacity >= 0);
+
+ // If the caller has a bug and calls this with negative capacity, make sure to call Grow to throw an exception.
+ if ((uint)capacity > (uint)_chars.Length)
+ Grow(capacity - _pos);
+ }
+
+ ///
+ /// Get a pinnable reference to the builder.
+ /// Does not ensure there is a null char after
+ /// This overload is pattern matched in the C# 7.3+ compiler so you can omit
+ /// the explicit method call, and write eg "fixed (char* c = builder)"
+ ///
+ public ref char GetPinnableReference()
+ {
+ return ref MemoryMarshal.GetReference(_chars);
+ }
+
+ ///
+ /// Get a pinnable reference to the builder.
+ ///
+ /// Ensures that the builder has a null char after
+ public ref char GetPinnableReference(bool terminate)
+ {
+ if (terminate)
+ {
+ EnsureCapacity(Length + 1);
+ _chars[Length] = '\0';
+ }
+ return ref MemoryMarshal.GetReference(_chars);
+ }
+
+ public ref char this[int index]
+ {
+ get
+ {
+ Debug.Assert(index < _pos);
+ return ref _chars[index];
+ }
+ }
+
+ public override string ToString()
+ {
+ string s = _chars.Slice(0, _pos).ToString();
+ Dispose();
+ return s;
+ }
+
+ /// Returns the underlying storage of the builder.
+ public Span RawChars => _chars;
+
+ ///
+ /// Returns a span around the contents of the builder.
+ ///
+ /// Ensures that the builder has a null char after
+ public ReadOnlySpan AsSpan(bool terminate)
+ {
+ if (terminate)
+ {
+ EnsureCapacity(Length + 1);
+ _chars[Length] = '\0';
+ }
+ return _chars.Slice(0, _pos);
+ }
+
+ public ReadOnlySpan AsSpan() => _chars.Slice(0, _pos);
+ public ReadOnlySpan AsSpan(int start) => _chars.Slice(start, _pos - start);
+ public ReadOnlySpan AsSpan(int start, int length) => _chars.Slice(start, length);
+
+ public void Reverse()
+ {
+ _chars.Slice(0, _pos).Reverse();
+ }
+
+ public bool TryCopyTo(Span destination, out int charsWritten)
+ {
+ if (_chars.Slice(0, _pos).TryCopyTo(destination))
+ {
+ charsWritten = _pos;
+ Dispose();
+ return true;
+ }
+ else
+ {
+ charsWritten = 0;
+ Dispose();
+ return false;
+ }
+ }
+
+ public void Insert(int index, char value, int count)
+ {
+ if (_pos > _chars.Length - count)
+ {
+ Grow(count);
+ }
+
+ int remaining = _pos - index;
+ _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count));
+ _chars.Slice(index, count).Fill(value);
+ _pos += count;
+ }
+
+ public void Insert(int index, string? s)
+ {
+ if (s == null)
+ {
+ return;
+ }
+
+ int count = s.Length;
+
+ if (_pos > (_chars.Length - count))
+ {
+ Grow(count);
+ }
+
+ int remaining = _pos - index;
+ _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count));
+ s
+#if !NETCOREAPP
+ .AsSpan()
+#endif
+ .CopyTo(_chars.Slice(index));
+ _pos += count;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Append(char c)
+ {
+ int pos = _pos;
+ Span chars = _chars;
+ if ((uint)pos < (uint)chars.Length)
+ {
+ chars[pos] = c;
+ _pos = pos + 1;
+ }
+ else
+ {
+ GrowAndAppend(c);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Append(string? s)
+ {
+ if (s == null)
+ {
+ return;
+ }
+
+ int pos = _pos;
+ if (s.Length == 1 && (uint)pos < (uint)_chars.Length) // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc.
+ {
+ _chars[pos] = s[0];
+ _pos = pos + 1;
+ }
+ else
+ {
+ AppendSlow(s);
+ }
+ }
+
+ private void AppendSlow(string s)
+ {
+ int pos = _pos;
+ if (pos > _chars.Length - s.Length)
+ {
+ Grow(s.Length);
+ }
+
+ s
+#if !NETCOREAPP
+ .AsSpan()
+#endif
+ .CopyTo(_chars.Slice(pos));
+ _pos += s.Length;
+ }
+
+ public void Append(char c, int count)
+ {
+ if (_pos > _chars.Length - count)
+ {
+ Grow(count);
+ }
+
+ Span dst = _chars.Slice(_pos, count);
+ for (int i = 0; i < dst.Length; i++)
+ {
+ dst[i] = c;
+ }
+ _pos += count;
+ }
+
+ public unsafe void Append(char* value, int length)
+ {
+ int pos = _pos;
+ if (pos > _chars.Length - length)
+ {
+ Grow(length);
+ }
+
+ Span dst = _chars.Slice(_pos, length);
+ for (int i = 0; i < dst.Length; i++)
+ {
+ dst[i] = *value++;
+ }
+ _pos += length;
+ }
+
+ public void Append(scoped ReadOnlySpan value)
+ {
+ int pos = _pos;
+ if (pos > _chars.Length - value.Length)
+ {
+ Grow(value.Length);
+ }
+
+ value.CopyTo(_chars.Slice(_pos));
+ _pos += value.Length;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Span AppendSpan(int length)
+ {
+ int origPos = _pos;
+ if (origPos > _chars.Length - length)
+ {
+ Grow(length);
+ }
+
+ _pos = origPos + length;
+ return _chars.Slice(origPos, length);
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private void GrowAndAppend(char c)
+ {
+ Grow(1);
+ Append(c);
+ }
+
+ ///
+ /// Resize the internal buffer either by doubling current buffer size or
+ /// by adding to
+ /// whichever is greater.
+ ///
+ ///
+ /// Number of chars requested beyond current position.
+ ///
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private void Grow(int additionalCapacityBeyondPos)
+ {
+ Debug.Assert(additionalCapacityBeyondPos > 0);
+ Debug.Assert(_pos > _chars.Length - additionalCapacityBeyondPos, "Grow called incorrectly, no resize is needed.");
+
+ const uint ArrayMaxLength = 0x7FFFFFC7; // same as Array.MaxLength
+
+ // Increase to at least the required size (_pos + additionalCapacityBeyondPos), but try
+ // to double the size if possible, bounding the doubling to not go beyond the max array length.
+ int newCapacity = (int)Math.Max(
+ (uint)(_pos + additionalCapacityBeyondPos),
+ Math.Min((uint)_chars.Length * 2, ArrayMaxLength));
+
+ // Make sure to let Rent throw an exception if the caller has a bug and the desired capacity is negative.
+ // This could also go negative if the actual required length wraps around.
+ char[] poolArray = ArrayPool.Shared.Rent(newCapacity);
+
+ _chars.Slice(0, _pos).CopyTo(poolArray);
+
+ char[]? toReturn = _arrayToReturnToPool;
+ _chars = _arrayToReturnToPool = poolArray;
+ if (toReturn != null)
+ {
+ ArrayPool.Shared.Return(toReturn);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Dispose()
+ {
+ char[]? toReturn = _arrayToReturnToPool;
+ this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again
+ if (toReturn != null)
+ {
+ ArrayPool.Shared.Return(toReturn);
+ }
+ }
+}
diff --git a/Jint/Runtime/CallStack/JintCallStack.cs b/Jint/Runtime/CallStack/JintCallStack.cs
index d6a234791d..a44317c114 100644
--- a/Jint/Runtime/CallStack/JintCallStack.cs
+++ b/Jint/Runtime/CallStack/JintCallStack.cs
@@ -1,11 +1,11 @@
using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
using System.Linq;
using System.Text;
using Esprima;
using Esprima.Ast;
using Jint.Collections;
using Jint.Native.Function;
-using Jint.Pooling;
using Jint.Runtime.Environments;
using Jint.Runtime.Interpreter.Expressions;
@@ -119,19 +119,17 @@ public override string ToString()
internal string BuildCallStackString(Location location, int excludeTop = 0)
{
static void AppendLocation(
- StringBuilder sb,
+ ref ValueStringBuilder sb,
string shortDescription,
in Location loc,
in CallStackElement? element)
{
- sb
- .Append(" at");
+ sb.Append(" at");
if (!string.IsNullOrWhiteSpace(shortDescription))
{
- sb
- .Append(' ')
- .Append(shortDescription);
+ sb.Append(' ');
+ sb.Append(shortDescription);
}
if (element?.Arguments is not null)
@@ -151,24 +149,23 @@ static void AppendLocation(
sb.Append(')');
}
- sb
- .Append(' ')
- .Append(loc.Source)
- .Append(':')
- .Append(loc.End.Line)
- .Append(':')
- .Append(loc.Start.Column + 1) // report column number instead of index
- .AppendLine();
+ sb.Append(' ');
+ sb.Append(loc.Source);
+ sb.Append(':');
+ sb.Append(loc.End.Line.ToString(CultureInfo.InvariantCulture));
+ sb.Append(':');
+ sb.Append((loc.Start.Column + 1).ToString(CultureInfo.InvariantCulture)); // report column number instead of index
+ sb.Append(Environment.NewLine);
}
- using var sb = StringBuilderPool.Rent();
+ var builder = new ValueStringBuilder();
// stack is one frame behind function-wise when we start to process it from expression level
var index = _stack._size - 1 - excludeTop;
var element = index >= 0 ? _stack[index] : (CallStackElement?) null;
var shortDescription = element?.ToString() ?? "";
- AppendLocation(sb.Builder, shortDescription, location, element);
+ AppendLocation(ref builder, shortDescription, location, element);
location = element?.Location ?? default;
index--;
@@ -178,13 +175,17 @@ static void AppendLocation(
element = index >= 0 ? _stack[index] : null;
shortDescription = element?.ToString() ?? "";
- AppendLocation(sb.Builder, shortDescription, location, element);
+ AppendLocation(ref builder, shortDescription, location, element);
location = element?.Location ?? default;
index--;
}
- return sb.ToString().TrimEnd();
+ var result = builder.AsSpan().TrimEnd().ToString();
+
+ builder.Dispose();
+
+ return result;
}
///
diff --git a/Jint/Runtime/CommonProperties.cs b/Jint/Runtime/CommonProperties.cs
index ee2e6bcd08..e7537d75e4 100644
--- a/Jint/Runtime/CommonProperties.cs
+++ b/Jint/Runtime/CommonProperties.cs
@@ -1,29 +1,30 @@
using Jint.Native;
-namespace Jint.Runtime
+namespace Jint.Runtime;
+
+internal static class CommonProperties
{
- internal static class CommonProperties
- {
- internal static readonly JsString Arguments = JsString.CachedCreate("arguments");
- internal static readonly JsString Caller = JsString.CachedCreate("caller");
- internal static readonly JsString Callee = JsString.CachedCreate("callee");
- internal static readonly JsString Constructor = JsString.CachedCreate("constructor");
- internal static readonly JsString Eval = JsString.CachedCreate("eval");
- internal static readonly JsString Infinity = JsString.CachedCreate("Infinity");
- internal static readonly JsString Length = JsString.CachedCreate("length");
- internal static readonly JsString Name = JsString.CachedCreate("name");
- internal static readonly JsString Prototype = JsString.CachedCreate("prototype");
- internal static readonly JsString Size = JsString.CachedCreate("size");
- internal static readonly JsString Next = JsString.CachedCreate("next");
- internal static readonly JsString Done = JsString.CachedCreate("done");
- internal static readonly JsString Value = JsString.CachedCreate("value");
- internal static readonly JsString Return = JsString.CachedCreate("return");
- internal static readonly JsString Set = JsString.CachedCreate("set");
- internal static readonly JsString Get = JsString.CachedCreate("get");
- internal static readonly JsString Writable = JsString.CachedCreate("writable");
- internal static readonly JsString Enumerable = JsString.CachedCreate("enumerable");
- internal static readonly JsString Configurable = JsString.CachedCreate("configurable");
- internal static readonly JsString Stack = JsString.CachedCreate("stack");
- internal static readonly JsString Message = JsString.CachedCreate("message");
- }
+ internal static readonly JsString Arguments = JsString.CachedCreate("arguments");
+ internal static readonly JsString Callee = JsString.CachedCreate("callee");
+ internal static readonly JsString Caller = JsString.CachedCreate("caller");
+ internal static readonly JsString Configurable = JsString.CachedCreate("configurable");
+ internal static readonly JsString Constructor = JsString.CachedCreate("constructor");
+ internal static readonly JsString Done = JsString.CachedCreate("done");
+ internal static readonly JsString Enumerable = JsString.CachedCreate("enumerable");
+ internal static readonly JsString Eval = JsString.CachedCreate("eval");
+ internal static readonly JsString Get = JsString.CachedCreate("get");
+ internal static readonly JsString Has = JsString.CachedCreate("has");
+ internal static readonly JsString Infinity = JsString.CachedCreate("Infinity");
+ internal static readonly JsString Keys = JsString.CachedCreate("keys");
+ internal static readonly JsString Length = JsString.CachedCreate("length");
+ internal static readonly JsString Message = JsString.CachedCreate("message");
+ internal static readonly JsString Name = JsString.CachedCreate("name");
+ internal static readonly JsString Next = JsString.CachedCreate("next");
+ internal static readonly JsString Prototype = JsString.CachedCreate("prototype");
+ internal static readonly JsString Return = JsString.CachedCreate("return");
+ internal static readonly JsString Set = JsString.CachedCreate("set");
+ internal static readonly JsString Size = JsString.CachedCreate("size");
+ internal static readonly JsString Stack = JsString.CachedCreate("stack");
+ internal static readonly JsString Value = JsString.CachedCreate("value");
+ internal static readonly JsString Writable = JsString.CachedCreate("writable");
}
diff --git a/Jint/Runtime/DefaultTimeSystem.cs b/Jint/Runtime/DefaultTimeSystem.cs
index 72f348abe2..001a2ecd68 100644
--- a/Jint/Runtime/DefaultTimeSystem.cs
+++ b/Jint/Runtime/DefaultTimeSystem.cs
@@ -51,6 +51,11 @@ public DefaultTimeSystem(TimeZoneInfo timeZoneInfo, CultureInfo parsingCulture)
DefaultTimeZone = timeZoneInfo;
}
+ public virtual DateTimeOffset GetUtcNow()
+ {
+ return DateTimeOffset.UtcNow;
+ }
+
public TimeZoneInfo DefaultTimeZone { get; }
public virtual bool TryParse(string date, out long epochMilliseconds)
diff --git a/Jint/Runtime/ExceptionHelper.cs b/Jint/Runtime/ExceptionHelper.cs
index e47bb5a518..475edbdd70 100644
--- a/Jint/Runtime/ExceptionHelper.cs
+++ b/Jint/Runtime/ExceptionHelper.cs
@@ -87,6 +87,11 @@ public static ErrorDispatchInfo CreateUriError(Realm realm, string message)
return new ErrorDispatchInfo(realm.Intrinsics.UriError, message);
}
+ public static ErrorDispatchInfo CreateRangeError(Realm realm, string message)
+ {
+ return new ErrorDispatchInfo(realm.Intrinsics.RangeError, message);
+ }
+
[DoesNotReturn]
public static void ThrowNotImplementedException(string? message = null)
{
diff --git a/Jint/Runtime/ITimeSystem.cs b/Jint/Runtime/ITimeSystem.cs
index e691a4c7c2..07729dec1a 100644
--- a/Jint/Runtime/ITimeSystem.cs
+++ b/Jint/Runtime/ITimeSystem.cs
@@ -10,6 +10,12 @@ namespace Jint.Runtime;
///
public interface ITimeSystem
{
+ ///
+ /// Retrieves current UTC time.
+ ///
+ /// Current UTC time.
+ DateTimeOffset GetUtcNow();
+
///
/// Return the default time zone system is using. Usually , but can be altered via
/// engine configuration, see .
diff --git a/Jint/Runtime/Interpreter/Expressions/JintExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintExpression.cs
index fe78d83ef8..a4269ca8e1 100644
--- a/Jint/Runtime/Interpreter/Expressions/JintExpression.cs
+++ b/Jint/Runtime/Interpreter/Expressions/JintExpression.cs
@@ -34,7 +34,7 @@ public virtual JsValue GetValue(EvaluationContext context)
return context.Engine.GetValue(reference, true);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)]
public object Evaluate(EvaluationContext context)
{
var oldSyntaxElement = context.LastSyntaxElement;
diff --git a/Jint/Runtime/Interpreter/Expressions/JintTemplateLiteralExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintTemplateLiteralExpression.cs
index e3b7688ac4..8b0b2157bf 100644
--- a/Jint/Runtime/Interpreter/Expressions/JintTemplateLiteralExpression.cs
+++ b/Jint/Runtime/Interpreter/Expressions/JintTemplateLiteralExpression.cs
@@ -1,6 +1,6 @@
+using System.Text;
using Esprima.Ast;
using Jint.Native;
-using Jint.Pooling;
namespace Jint.Runtime.Interpreter.Expressions;
@@ -40,16 +40,16 @@ protected override object EvaluateInternal(EvaluationContext context)
_initialized = true;
}
- using var sb = StringBuilderPool.Rent();
+ using var sb = new ValueStringBuilder();
ref readonly var elements = ref _templateLiteralExpression.Quasis;
for (var i = 0; i < elements.Count; i++)
{
var quasi = elements[i];
- sb.Builder.Append(quasi.Value.Cooked);
+ sb.Append(quasi.Value.Cooked);
if (i < _expressions.Length)
{
var value = _expressions[i].GetValue(context);
- sb.Builder.Append(TypeConverter.ToString(value));
+ sb.Append(TypeConverter.ToString(value));
}
}
diff --git a/Jint/Runtime/Interpreter/JintFunctionDefinition.cs b/Jint/Runtime/Interpreter/JintFunctionDefinition.cs
index 774ca65364..fadc34018b 100644
--- a/Jint/Runtime/Interpreter/JintFunctionDefinition.cs
+++ b/Jint/Runtime/Interpreter/JintFunctionDefinition.cs
@@ -33,7 +33,7 @@ public JintFunctionDefinition(IFunction function)
///
/// https://tc39.es/ecma262/#sec-ordinarycallevaluatebody
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)]
internal Completion EvaluateBody(EvaluationContext context, FunctionInstance functionObject, JsValue[] argumentsList)
{
Completion result;
diff --git a/Jint/Runtime/Interpreter/JintStatementList.cs b/Jint/Runtime/Interpreter/JintStatementList.cs
index 52bd55b0a0..0b199d4008 100644
--- a/Jint/Runtime/Interpreter/JintStatementList.cs
+++ b/Jint/Runtime/Interpreter/JintStatementList.cs
@@ -1,3 +1,4 @@
+using System.Runtime.CompilerServices;
using Esprima.Ast;
using Jint.Native;
using Jint.Native.Error;
@@ -63,6 +64,8 @@ private void Initialize(EvaluationContext context)
_jintStatements = jintStatements;
}
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)]
public Completion Execute(EvaluationContext context)
{
if (!_initialized)
diff --git a/Jint/Runtime/Interpreter/Statements/JintStatement.cs b/Jint/Runtime/Interpreter/Statements/JintStatement.cs
index 1f59a6640d..9832e62d47 100644
--- a/Jint/Runtime/Interpreter/Statements/JintStatement.cs
+++ b/Jint/Runtime/Interpreter/Statements/JintStatement.cs
@@ -26,7 +26,7 @@ protected JintStatement(Statement statement)
_statement = statement;
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)]
public Completion Execute(EvaluationContext context)
{
if (_statement.Type != Nodes.BlockStatement)
diff --git a/Jint/Runtime/JavaScriptException.cs b/Jint/Runtime/JavaScriptException.cs
index d285606da4..d79b546062 100644
--- a/Jint/Runtime/JavaScriptException.cs
+++ b/Jint/Runtime/JavaScriptException.cs
@@ -1,8 +1,8 @@
+using System.Text;
using Esprima;
using Jint.Native;
using Jint.Native.Error;
using Jint.Native.Object;
-using Jint.Pooling;
using Jint.Runtime.Descriptors;
namespace Jint.Runtime;
@@ -132,8 +132,7 @@ public override string? StackTrace
public override string ToString()
{
- using var rent = StringBuilderPool.Rent();
- var sb = rent.Builder;
+ var sb = new ValueStringBuilder();
sb.Append("Error");
var message = Message;
@@ -150,7 +149,7 @@ public override string ToString()
sb.Append(stackTrace);
}
- return rent.ToString();
+ return sb.ToString();
}
}
}
diff --git a/Jint/Runtime/OrderedSet.cs b/Jint/Runtime/OrderedSet.cs
index 251cdd61a9..9db1ca1220 100644
--- a/Jint/Runtime/OrderedSet.cs
+++ b/Jint/Runtime/OrderedSet.cs
@@ -1,50 +1,58 @@
-namespace Jint.Runtime
+namespace Jint.Runtime;
+
+internal sealed class OrderedSet
{
- internal sealed class OrderedSet
- {
- internal readonly List _list;
- private readonly HashSet _set;
+ internal List _list;
+ private HashSet _set;
- public OrderedSet(IEqualityComparer comparer)
- {
- _list = new List();
- _set = new HashSet(comparer);
- }
+ public OrderedSet(IEqualityComparer comparer)
+ {
+ _list = new List();
+ _set = new HashSet(comparer);
+ }
- public T this[int index]
+ public T this[int index]
+ {
+ get => _list[index];
+ set
{
- get => _list[index];
- set
+ if (_set.Add(value))
{
- if (_set.Add(value))
- {
- _list[index] = value;
- }
+ _list[index] = value;
}
}
+ }
- public void Add(T item)
+ public OrderedSet Clone()
+ {
+ return new OrderedSet(EqualityComparer.Default)
{
- if (_set.Add(item))
- {
- _list.Add(item);
- }
- }
+ _set = new HashSet(this._set, this._set.Comparer),
+ _list = new List(this._list)
+ };
+ }
- public void Clear()
+ public void Add(T item)
+ {
+ if (_set.Add(item))
{
- _list.Clear();
- _set.Clear();
+ _list.Add(item);
}
+ }
+
+ public void Clear()
+ {
+ _list.Clear();
+ _set.Clear();
+ }
- public bool Contains(T item) => _set.Contains(item);
+ public bool Contains(T item) => _set.Contains(item);
- public int Count => _list.Count;
+ public int Count => _list.Count;
- public bool Remove(T item)
- {
- _set.Remove(item);
- return _list.Remove(item);
- }
+ public bool Remove(T item)
+ {
+ _set.Remove(item);
+ return _list.Remove(item);
}
}
diff --git a/Jint/Runtime/TypeConverter.cs b/Jint/Runtime/TypeConverter.cs
index 381f22bf4d..f434d79a33 100644
--- a/Jint/Runtime/TypeConverter.cs
+++ b/Jint/Runtime/TypeConverter.cs
@@ -7,11 +7,9 @@
using Jint.Extensions;
using Jint.Native;
using Jint.Native.Number;
-using Jint.Native.Number.Dtoa;
using Jint.Native.Object;
using Jint.Native.String;
using Jint.Native.Symbol;
-using Jint.Pooling;
using Jint.Runtime.Interop;
namespace Jint.Runtime
@@ -896,20 +894,7 @@ internal static string ToString(double d)
return ToString((long) d);
}
- using var stringBuilder = StringBuilderPool.Rent();
- // we can create smaller array as we know the format to be short
- NumberPrototype.NumberToString(d, CreateDtoaBuilderForDouble(), stringBuilder.Builder);
- return stringBuilder.Builder.ToString();
- }
-
- ///
- /// Creates a new with the default buffer
- /// size for
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal static DtoaBuilder CreateDtoaBuilderForDouble()
- {
- return new DtoaBuilder(17);
+ return NumberPrototype.ToNumberString(d);
}
///