Closed Bug 1869688 Opened 10 months ago Closed 9 months ago

Add API to convert UTF-8 string to external string if it fits ASCII range

Categories

(Core :: JavaScript Engine, task, P3)

task

Tracking

()

RESOLVED FIXED
123 Branch
Tracking Status
firefox123 --- fixed

People

(Reporter: arai, Assigned: arai)

References

Details

Attachments

(1 file)

Bug 1576076 added support for latin-1 external string.
we can extend it to receive UTF-8 string and use external string if it fits ASCII range.
that can be used by DOM UTF8String.

The idea here is to detect the smallest encoding in SpiderMonkey side.
the other option is to let the consumer track the smallest encoding and use latin-1 external strings API if applicable.

https://phabricator.services.mozilla.com/D44122 is likely of interest. It stalled due to my failure to find real-world performance numbers and, unfortunately, a synthetic benchmark didn't look so good.

Blocks: sm-meta
Severity: -- → N/A
Priority: -- → P3

Thanks for the pointer!

The expected consumer of this API is NonVoidUTF8StringToJsval, which is currently calling JS_NewStringCopyUTF8N, which already performs JS::FindSmallestEncoding internally.
So, at least the cost of JS::FindSmallestEncoding won't become troublesome with this change, as long as this doesn't increase the number of JS::FindSmallestEncoding calls.

I'll look into benchmarks and see how the external string work well with it.

https://searchfox.org/mozilla-central/rev/f961e5f2a22f4d41733545190892296e64c06858/dom/bindings/BindingUtils.h#2576-2579

inline bool NonVoidUTF8StringToJsval(JSContext* cx, const nsACString& str,
                                     JS::MutableHandle<JS::Value> rval) {
  JSString* jsStr =
      JS_NewStringCopyUTF8N(cx, {str.BeginReading(), str.Length()});

https://searchfox.org/mozilla-central/rev/f961e5f2a22f4d41733545190892296e64c06858/js/src/jsapi.cpp#3137-3138,3141

JS_PUBLIC_API JSString* JS_NewStringCopyUTF8N(JSContext* cx,
                                              const JS::UTF8Chars s) {
...
  return NewStringCopyUTF8N(cx, s);

https://searchfox.org/mozilla-central/rev/f961e5f2a22f4d41733545190892296e64c06858/js/src/vm/StringType.cpp#1876-1879

JSLinearString* NewStringCopyUTF8N(JSContext* cx, const JS::UTF8Chars utf8,
                                   gc::Heap heap) {
  JS::SmallestEncoding encoding = JS::FindSmallestEncoding(utf8);
  if (encoding == JS::SmallestEncoding::ASCII) {

Given UTF8String isn't widely used right now and also nsStringBuffer-backed string (which can be used in external string) is also limited,
this mostly targets localization code, where both directions of conversion between UTF8String and JSString happen.

UTF8String to JSString happens in the following:

https://searchfox.org/mozilla-central/rev/43c40b22c776d098afb89d5237da3464a7545532/intl/l10n/Localization.cpp#317-318,344

already_AddRefed<Promise> Localization::FormatMessages(
    const Sequence<OwningUTF8StringOrL10nIdArgs>& aKeys, ErrorResult& aRv) {
...
            promise->MaybeResolve(std::move(messages));

https://searchfox.org/mozilla-central/rev/43c40b22c776d098afb89d5237da3464a7545532/dom/promise/Promise.h#93-94

void MaybeResolve(T&& aArg) {
  MaybeSomething(std::forward<T>(aArg), &Promise::MaybeResolve);

https://searchfox.org/mozilla-central/rev/43c40b22c776d098afb89d5237da3464a7545532/dom/promise/Promise.h#422,430

void MaybeSomething(T&& aArgument, MaybeFunc aFunc) {
...
  if (!ToJSValue(cx, std::forward<T>(aArgument), &val)) {

https://searchfox.org/mozilla-central/rev/43c40b22c776d098afb89d5237da3464a7545532/dom/bindings/ToJSValue.h#285-287

[[nodiscard]] std::enable_if_t<is_dom_dictionary<T>, bool> ToJSValue(
    JSContext* aCx, const T& aArgument, JS::MutableHandle<JS::Value> aValue) {
  return aArgument.ToObjectInternal(aCx, aValue);

https://searchfox.org/mozilla-central/source/__GENERATED__/dom/bindings/LocalizationBinding.cpp#143-144,162,175

bool
AttributeNameValue::ToObjectInternal(JSContext* cx, JS::MutableHandle<JS::Value> rval) const
...
    if (!NonVoidUTF8StringToJsval(cx, currentValue, &temp)) {
...
    if (!NonVoidUTF8StringToJsval(cx, currentValue, &temp)) {

https://searchfox.org/mozilla-central/rev/43c40b22c776d098afb89d5237da3464a7545532/dom/bindings/BindingUtils.h#2576-2579

inline bool NonVoidUTF8StringToJsval(JSContext* cx, const nsACString& str,
                                     JS::MutableHandle<JS::Value> rval) {
  JSString* jsStr =
      JS_NewStringCopyUTF8N(cx, {str.BeginReading(), str.Length()});

and JSString to nsString happens in the following:

https://searchfox.org/mozilla-central/rev/43c40b22c776d098afb89d5237da3464a7545532/dom/l10n/DOMLocalization.cpp#228-229,264

virtual void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
                              ErrorResult& aRv) override {
...
        if (!slotPtr->SetValue().Init(aCx, temp)) {

https://searchfox.org/mozilla-central/source/__GENERATED__/dom/bindings/LocalizationBinding.cpp#830,835

L10nMessage::Init(JSContext* cx_, JS::Handle<JS::Value> val, const char* sourceDescription, bool passedToJSImpl)
...
  return Init(cx, val, sourceDescription, passedToJSImpl);

https://searchfox.org/mozilla-central/source/__GENERATED__/dom/bindings/LocalizationBinding.cpp#738,798

L10nMessage::Init(BindingCallContext& cx, JS::Handle<JS::Value> val, const char* sourceDescription, bool passedToJSImpl)
...
        if (!slot.Init(cx, temp, "Element of 'attributes' member of L10nMessage", passedToJSImpl)) {

https://searchfox.org/mozilla-central/source/__GENERATED__/dom/bindings/LocalizationBinding.cpp#69,104,121

AttributeNameValue::Init(BindingCallContext& cx, JS::Handle<JS::Value> val, const char* sourceDescription, bool passedToJSImpl)
...
    if (!ConvertJSValueToString(cx, temp.ref(), eStringify, eStringify, mName)) {
...
    if (!ConvertJSValueToString(cx, temp.ref(), eStringify, eStringify, mValue)) {

https://searchfox.org/mozilla-central/rev/43c40b22c776d098afb89d5237da3464a7545532/dom/bindings/BindingUtils.h#1871-1874,1903

static inline bool ConvertJSValueToString(
    JSContext* cx, JS::Handle<JS::Value> v,
    StringificationBehavior nullBehavior,
    StringificationBehavior undefinedBehavior, T& result) {
...
  return AssignJSString(cx, result, s);

https://searchfox.org/mozilla-central/rev/43c40b22c776d098afb89d5237da3464a7545532/dom/base/nsJSUtils.h#120,147

inline bool AssignJSString(JSContext* cx, T& dest, JSString* s) {
...
  auto maybe = JS_EncodeStringToUTF8BufferPartial(cx, s, handle.AsSpan());
Blocks: 1873827
Pushed by arai_a@mac.com: https://hg.mozilla.org/integration/autoland/rev/28aedf564d9d Add JS_NewMaybeExternalStringUTF8 to use Latin1Char JSExternalString for ASCII case. r=sfink
Status: ASSIGNED → RESOLVED
Closed: 9 months ago
Resolution: --- → FIXED
Target Milestone: --- → 123 Branch
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: