Skip to content

Commit

Permalink
Optimize String.fromCodePoint (#1696)
Browse files Browse the repository at this point in the history
  • Loading branch information
lahma authored Dec 3, 2023
1 parent 4272c3b commit 1c8a9b9
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 20 deletions.
53 changes: 33 additions & 20 deletions Jint/Native/String/StringConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,48 +76,61 @@ private static JsValue FromCharCode(JsValue? thisObj, JsValue[] arguments)
return JsString.Create(new string(elements));
}

/// <summary>
/// https://tc39.es/ecma262/#sec-string.fromcodepoint
/// </summary>
private JsValue FromCodePoint(JsValue thisObject, JsValue[] arguments)
{
var codeUnits = new List<JsValue>();
string result = "";
JsNumber codePoint;
using var wrapper = StringBuilderPool.Rent();
var result = wrapper.Builder;
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!;
}

/// <summary>
/// https://www.ecma-international.org/ecma-262/6.0/#sec-string.raw
/// https://tc39.es/ecma262/#sec-string.raw
/// </summary>
private JsValue Raw(JsValue thisObject, JsValue[] arguments)
{
Expand Down
5 changes: 5 additions & 0 deletions Jint/Runtime/ExceptionHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down

0 comments on commit 1c8a9b9

Please sign in to comment.