From 1c8a9b9da830da14e85e023d2568301a48d524c3 Mon Sep 17 00:00:00 2001 From: Marko Lahma Date: Sun, 3 Dec 2023 17:32:33 +0200 Subject: [PATCH] Optimize String.fromCodePoint (#1696) --- Jint/Native/String/StringConstructor.cs | 53 +++++++++++++++---------- Jint/Runtime/ExceptionHelper.cs | 5 +++ 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/Jint/Native/String/StringConstructor.cs b/Jint/Native/String/StringConstructor.cs index 5a30c3df72..fbb971fe55 100644 --- a/Jint/Native/String/StringConstructor.cs +++ b/Jint/Native/String/StringConstructor.cs @@ -76,48 +76,61 @@ 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 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!; } /// - /// 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) { 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) {