Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/js/src/builtin/intl/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 27 kB image not shown  

Quellcode-Bibliothek Collator.cpp

  Sprache: C
 

/* 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/. */


/* Intl.Collator implementation. */

#include "builtin/intl/Collator.h"

#include "mozilla/Assertions.h"
#include "mozilla/intl/Collator.h"
#include "mozilla/intl/Locale.h"
#include "mozilla/Maybe.h"
#include "mozilla/Span.h"
#include "mozilla/UsingEnum.h"

#include "builtin/Array.h"
#include "builtin/intl/CommonFunctions.h"
#include "builtin/intl/FormatBuffer.h"
#include "builtin/intl/LanguageTag.h"
#include "builtin/intl/LocaleNegotiation.h"
#include "builtin/intl/Packed.h"
#include "builtin/intl/ParameterNegotiation.h"
#include "builtin/intl/SharedIntlData.h"
#include "gc/GCContext.h"
#include "js/PropertySpec.h"
#include "js/StableStringChars.h"
#include "js/TypeDecls.h"
#include "vm/GlobalObject.h"
#include "vm/JSContext.h"
#include "vm/PlainObject.h"  // js::PlainObject
#include "vm/Runtime.h"
#include "vm/StringType.h"

#include "vm/GeckoProfiler-inl.h"
#include "vm/JSObject-inl.h"

using namespace js;
using namespace js::intl;

const JSClassOps CollatorObject::classOps_ = {
    .finalize = CollatorObject::finalize,
};

const JSClass CollatorObject::class_ = {
    "Intl.Collator",
    JSCLASS_HAS_RESERVED_SLOTS(CollatorObject::SLOT_COUNT) |
        JSCLASS_HAS_CACHED_PROTO(JSProto_Collator) |
        JSCLASS_BACKGROUND_FINALIZE,
    &CollatorObject::classOps_,
    &CollatorObject::classSpec_,
};

const JSClass& CollatorObject::protoClass_ = PlainObject::class_;

static bool collator_supportedLocalesOf(JSContext* cx, unsigned argc,
                                        Value* vp);

static bool collator_compare(JSContext* cx, unsigned argc, Value* vp);

static bool collator_resolvedOptions(JSContext* cx, unsigned argc, Value* vp);

static bool collator_toSource(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  args.rval().setString(cx->names().Collator);
  return true;
}

static const JSFunctionSpec collator_static_methods[] = {
    JS_FN("supportedLocalesOf", collator_supportedLocalesOf, 10),
    JS_FS_END,
};

static const JSFunctionSpec collator_methods[] = {
    JS_FN("resolvedOptions", collator_resolvedOptions, 00),
    JS_FN("toSource", collator_toSource, 00),
    JS_FS_END,
};

static const JSPropertySpec collator_properties[] = {
    JS_PSG("compare", collator_compare, 0),
    JS_STRING_SYM_PS(toStringTag, "Intl.Collator", JSPROP_READONLY),
    JS_PS_END,
};

static bool Collator(JSContext* cx, unsigned argc, Value* vp);

const ClassSpec CollatorObject::classSpec_ = {
    GenericCreateConstructor<Collator, 0, gc::AllocKind::FUNCTION>,
    GenericCreatePrototype<CollatorObject>,
    collator_static_methods,
    nullptr,
    collator_methods,
    collator_properties,
    nullptr,
    ClassSpec::DontDefineConstructor,
};

struct js::intl::CollatorOptions {
  enum class Usage : int8_t { Sort, Search };
  Usage usage = Usage::Sort;

  using Sensitivity = mozilla::intl::CollatorSensitivity;
  Sensitivity sensitivity = Sensitivity::Variant;

  using IgnorePunctuation = mozilla::intl::CollatorIgnorePunctuation;
  IgnorePunctuation ignorePunctuation = IgnorePunctuation::Locale;

  using Numeric = mozilla::intl::CollatorNumeric;
  Numeric numeric = Numeric::Locale;

  using CaseFirst = mozilla::intl::CollatorCaseFirst;
  CaseFirst caseFirst = CaseFirst::Locale;
};

struct PackedCollatorOptions {
  using RawValue = uint32_t;

  using UsageField = packed::EnumField<RawValue, CollatorOptions::Usage::Sort,
                                       CollatorOptions::Usage::Search>;

  using SensitivityField =
      packed::EnumField<UsageField, CollatorOptions::Sensitivity::Variant,
                        CollatorOptions::Sensitivity::Base>;

  using IgnorePunctuationField =
      packed::EnumField<SensitivityField,
                        CollatorOptions::IgnorePunctuation::Locale,
                        CollatorOptions::IgnorePunctuation::Off>;

  using NumericField = packed::EnumField<IgnorePunctuationField,
                                         CollatorOptions::Numeric::Locale,
                                         CollatorOptions::Numeric::Off>;

  using CaseFirstField =
      packed::EnumField<NumericField, CollatorOptions::CaseFirst::Locale,
                        CollatorOptions::CaseFirst::False>;

  using PackedValue = packed::PackedValue<CaseFirstField>;

  static auto pack(const CollatorOptions& options) {
    RawValue rawValue =
        UsageField::pack(options.usage) |
        SensitivityField::pack(options.sensitivity) |
        IgnorePunctuationField::pack(options.ignorePunctuation) |
        NumericField::pack(options.numeric) |
        CaseFirstField::pack(options.caseFirst);
    return PackedValue::toValue(rawValue);
  }

  static auto unpack(JS::Value value) {
    RawValue rawValue = PackedValue::fromValue(value);
    return CollatorOptions{
        .usage = UsageField::unpack(rawValue),
        .sensitivity = SensitivityField::unpack(rawValue),
        .ignorePunctuation = IgnorePunctuationField::unpack(rawValue),
        .numeric = NumericField::unpack(rawValue),
        .caseFirst = CaseFirstField::unpack(rawValue),
    };
  }
};

CollatorOptions js::intl::CollatorObject::getOptions() const {
  const auto& slot = getFixedSlot(OPTIONS_SLOT);
  if (slot.isUndefined()) {
    return {};
  }
  return PackedCollatorOptions::unpack(slot);
}

void js::intl::CollatorObject::setOptions(const CollatorOptions& options) {
  setFixedSlot(OPTIONS_SLOT, PackedCollatorOptions::pack(options));
}

static constexpr std::string_view UsageToString(CollatorOptions::Usage usage) {
  MOZ_USING_ENUM(CollatorOptions::Usage, Sort, Search);
  switch (usage) {
    case Sort:
      return "sort";
    case Search:
      return "search";
  }
  MOZ_CRASH("invalid collator usage");
}

static constexpr std::string_view SensitivityToString(
    CollatorOptions::Sensitivity sensitivity) {
  MOZ_USING_ENUM(CollatorOptions::Sensitivity, Base, Accent, Case, Variant);
  switch (sensitivity) {
    case Base:
      return "base";
    case Accent:
      return "accent";
    case Case:
      return "case";
    case Variant:
      return "variant";
  }
  MOZ_CRASH("invalid collator sensitivity");
}

static constexpr std::string_view CaseFirstToString(
    CollatorOptions::CaseFirst caseFirst) {
  MOZ_USING_ENUM(CollatorOptions::CaseFirst, False, Lower, Upper, Locale);
  switch (caseFirst) {
    case False:
      return "false";
    case Lower:
      return "lower";
    case Upper:
      return "upper";
    case Locale:
      MOZ_CRASH("expected to have resolved away this case");
  }
  MOZ_CRASH("invalid collator case first");
}

template <typename Option>
static auto MapBoolean(mozilla::Maybe<bool> option) {
  return option.isNothing() ? Option::Locale
         : *option          ? Option::On
                            : Option::Off;
}

/**
 * 10.1.2 Intl.Collator([ locales [, options]])
 *
 * ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
 */

static bool InitializeCollator(JSContext* cx, Handle<CollatorObject*> collator,
                               Handle<JS::Value> locales,
                               Handle<JS::Value> optionsValue) {
  // Steps 4-5.
  auto* requestedLocales = CanonicalizeLocaleList(cx, locales);
  if (!requestedLocales) {
    return false;
  }
  collator->setRequestedLocales(requestedLocales);

  CollatorOptions colOptions{};

  if (!optionsValue.isUndefined()) {
    // Step 6.
    Rooted<JSObject*> options(cx, JS::ToObject(cx, optionsValue));
    if (!options) {
      return false;
    }

    // Steps 7-8.
    static constexpr auto usages = MapOptions<UsageToString>(
        CollatorOptions::Usage::Sort, CollatorOptions::Usage::Search);
    if (!GetStringOption(cx, options, cx->names().usage, usages,
                         CollatorOptions::Usage::Sort, &colOptions.usage)) {
      return false;
    }

    // Step 9-10. (Performed in ResolveLocale)

    // Step 11. (Inlined ResolveOptions)

    // ResolveOptions, steps 1-3. (Already performed above)

    // ResolveOptions, step 4.
    LocaleMatcher matcher;
    if (!GetLocaleMatcherOption(cx, options, &matcher)) {
      return false;
    }

    // ResolveOptions, step 5.
    //
    // This implementation only supports the "lookup" locale matcher, therefore
    // the "localeMatcher" option doesn't need to be stored.

    // ResolveOptions, step 6.
    Rooted<JSLinearString*> collation(cx);
    if (!GetUnicodeExtensionOption(cx, options, UnicodeExtensionKey::Collation,
                                   &collation)) {
      return false;
    }
    if (collation) {
      collator->setCollation(collation);
    }

    // ResolveOptions, step 6.
    mozilla::Maybe<bool> numeric;
    if (!GetBooleanOption(cx, options, cx->names().numeric, &numeric)) {
      return false;
    }
    colOptions.numeric = MapBoolean<CollatorOptions::Numeric>(numeric);

    // ResolveOptions, step 6.
    mozilla::Maybe<CollatorOptions::CaseFirst> caseFirst;
    static constexpr auto caseFirsts = MapOptions<CaseFirstToString>(
        CollatorOptions::CaseFirst::Upper, CollatorOptions::CaseFirst::Lower,
        CollatorOptions::CaseFirst::False);
    if (!GetStringOption(cx, options, cx->names().caseFirst, caseFirsts,
                         &caseFirst)) {
      return false;
    }
    colOptions.caseFirst =
        caseFirst.valueOr(CollatorOptions::CaseFirst::Locale);

    // ResolveOptions, step 7. (Not applicable)

    // ResolveOptions, step 8. (Performed in ResolveLocale)

    // ResolveOptions, step 9. (Return)

    // Steps 12-19 and 21. (Performed in ResolveLocale)

    // Step 20.
    mozilla::Maybe<CollatorOptions::Sensitivity> sensitivity;
    static constexpr auto sensitivities =
        MapOptions<SensitivityToString>(CollatorOptions::Sensitivity::Base,
                                        CollatorOptions::Sensitivity::Accent,
                                        CollatorOptions::Sensitivity::Case,
                                        CollatorOptions::Sensitivity::Variant);
    if (!GetStringOption(cx, options, cx->names().sensitivity, sensitivities,
                         &sensitivity)) {
      return false;
    }

    // In theory the default sensitivity for the "search" collator is locale
    // dependent; in reality the CLDR/ICU default strength is always tertiary.
    // Therefore use "variant" as the default value for both collation modes.
    colOptions.sensitivity =
        sensitivity.valueOr(CollatorOptions::Sensitivity::Variant);

    // Step 22.
    mozilla::Maybe<bool> ignorePunctuation;
    if (!GetBooleanOption(cx, options, cx->names().ignorePunctuation,
                          &ignorePunctuation)) {
      return false;
    }
    colOptions.ignorePunctuation =
        MapBoolean<CollatorOptions::IgnorePunctuation>(ignorePunctuation);
  }
  collator->setOptions(colOptions);

  return true;
}

/**
 * 10.1.2 Intl.Collator([ locales [, options]])
 *
 * ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
 */

static bool Collator(JSContext* cx, unsigned argc, Value* vp) {
  AutoJSConstructorProfilerEntry pseudoFrame(cx, "Intl.Collator");
  CallArgs args = CallArgsFromVp(argc, vp);

  // Step 1 (Handled by OrdinaryCreateFromConstructor fallback code).

  // Steps 2-3 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
  Rooted<JSObject*> proto(cx);
  if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Collator, &proto)) {
    return false;
  }

  Rooted<CollatorObject*> collator(
      cx, NewObjectWithClassProto<CollatorObject>(cx, proto));
  if (!collator) {
    return false;
  }

  // Steps 4-22.
  if (!InitializeCollator(cx, collator, args.get(0), args.get(1))) {
    return false;
  }

  // Step 23.
  args.rval().setObject(*collator);
  return true;
}

CollatorObject* js::intl::CreateCollator(JSContext* cx, Handle<Value> locales,
                                         Handle<Value> options) {
  Rooted<CollatorObject*> collator(cx,
                                   NewBuiltinClassInstance<CollatorObject>(cx));
  if (!collator) {
    return nullptr;
  }

  if (!InitializeCollator(cx, collator, locales, options)) {
    return nullptr;
  }
  return collator;
}

CollatorObject* js::intl::GetOrCreateCollator(JSContext* cx,
                                              Handle<Value> locales,
                                              Handle<Value> options) {
  // Try to use a cached instance when |locales| is either undefined or a
  // string, and |options| is undefined.
  if ((locales.isUndefined() || locales.isString()) && options.isUndefined()) {
    Rooted<JSLinearString*> locale(cx);
    if (locales.isString()) {
      locale = locales.toString()->ensureLinear(cx);
      if (!locale) {
        return nullptr;
      }
    }
    return cx->global()->globalIntlData().getOrCreateCollator(cx, locale);
  }

  // Create a new Intl.Collator instance.
  return CreateCollator(cx, locales, options);
}

void js::intl::CollatorObject::finalize(JS::GCContext* gcx, JSObject* obj) {
  auto* collator = &obj->as<CollatorObject>();

  if (auto* coll = collator->getCollator()) {
    RemoveICUCellMemory(gcx, obj, CollatorObject::EstimatedMemoryUse);
    delete coll;
  }
}

/**
 * Resolve the actual locale to finish initialization of the Collator.
 */

static bool ResolveLocale(JSContext* cx, Handle<CollatorObject*> collator) {
  // Return if the locale was already resolved.
  if (collator->isLocaleResolved()) {
    return true;
  }
  auto colOptions = collator->getOptions();

  Rooted<ArrayObject*> requestedLocales(
      cx, &collator->getRequestedLocales()->as<ArrayObject>());

  // %Intl.Collator%.[[RelevantExtensionKeys]] is « "co", "kf", "kn" ».
  mozilla::EnumSet<UnicodeExtensionKey> relevantExtensionKeys{
      UnicodeExtensionKey::Collation,
      UnicodeExtensionKey::CollationCaseFirst,
      UnicodeExtensionKey::CollationNumeric,
  };

  // Initialize locale options from constructor arguments.
  Rooted<LocaleOptions> localeOptions(cx);
  if (auto* co = collator->getCollation()) {
    localeOptions.setUnicodeExtension(UnicodeExtensionKey::Collation, co);
  }
  if (colOptions.caseFirst != CollatorOptions::CaseFirst::Locale) {
    MOZ_USING_ENUM(CollatorOptions::CaseFirst, False, Lower, Upper, Locale);

    JSLinearString* kf;
    switch (colOptions.caseFirst) {
      case Upper:
        kf = cx->names().upper;
        break;
      case Lower:
        kf = cx->names().lower;
        break;
      case False:
        kf = cx->names().false_;
        break;
      case Locale:
        MOZ_CRASH("Unreachable switch-case");
    }
    localeOptions.setUnicodeExtension(UnicodeExtensionKey::CollationCaseFirst,
                                      kf);
  }
  if (colOptions.numeric != CollatorOptions::Numeric::Locale) {
    JSLinearString* kn = (colOptions.numeric == CollatorOptions::Numeric::On)
                             ? cx->names().true_
                             : cx->names().false_;
    localeOptions.setUnicodeExtension(UnicodeExtensionKey::CollationNumeric,
                                      kn);
  }

  // Use the default locale data for "sort" usage.
  // Use the collator search locale data for "search" usage.
  LocaleData localeData;
  if (colOptions.usage == CollatorOptions::Usage::Sort) {
    localeData = LocaleData::Default;
  } else {
    localeData = LocaleData::CollatorSearch;
  }

  // Resolve the actual locale.
  Rooted<ResolvedLocale> resolved(cx);
  if (!ResolveLocale(cx, AvailableLocaleKind::Collator, requestedLocales,
                     localeOptions, relevantExtensionKeys, localeData,
                     &resolved)) {
    return false;
  }

  // In theory the default sensitivity for the "search" collator is locale
  // dependent; in reality the CLDR/ICU default strength is always tertiary.
  // Therefore use "variant" as the default value for both collation modes,
  // which is why there is not a sensitivity handling step here.

  // Finish initialization by setting the actual locale and collation.
  auto* locale = resolved.toLocale(cx);
  if (!locale) {
    return false;
  }
  collator->setLocale(locale);

  if (auto co = resolved.extension(UnicodeExtensionKey::Collation)) {
    collator->setCollation(co);
  } else {
    // The first element of the collations array must be |null| per ES2026 Intl,
    // 10.2.3 Internal Slots. The |Intl.Collator| constructor maps |null| to
    // "default".
    collator->setCollation(cx->names().default_);
  }

  if (auto kf = resolved.extension(UnicodeExtensionKey::CollationCaseFirst)) {
    if (StringEqualsLiteral(kf, "upper")) {
      colOptions.caseFirst = CollatorOptions::CaseFirst::Upper;
    } else if (StringEqualsLiteral(kf, "lower")) {
      colOptions.caseFirst = CollatorOptions::CaseFirst::Lower;
    } else {
      MOZ_ASSERT(StringEqualsLiteral(kf, "false"));
      colOptions.caseFirst = CollatorOptions::CaseFirst::False;
    }
  } else {
    MOZ_ASSERT(colOptions.caseFirst == CollatorOptions::CaseFirst::Locale);
  }

  if (auto kn = resolved.extension(UnicodeExtensionKey::CollationNumeric)) {
    if (StringEqualsLiteral(kn, "true")) {
      colOptions.numeric = CollatorOptions::Numeric::On;
    } else {
      MOZ_ASSERT(StringEqualsLiteral(kn, "false"));
      colOptions.numeric = CollatorOptions::Numeric::Off;
    }
  } else {
    MOZ_ASSERT(colOptions.numeric == CollatorOptions::Numeric::Locale);
  }

  // Set the resolved options.
  collator->setOptions(colOptions);

  MOZ_ASSERT(collator->isLocaleResolved(), "locale successfully resolved");
  return true;
}

/**
 * Returns a new mozilla::intl::Collator with the locale and collation options
 * of the given Collator.
 */

static mozilla::intl::Collator* NewIntlCollator(
    JSContext* cx, Handle<CollatorObject*> collator) {
  if (!ResolveLocale(cx, collator)) {
    return nullptr;
  }
  auto colOptions = collator->getOptions();

  JS::RootedVector<UnicodeExtensionKeyword> keywords(cx);

  // ICU expects collation as Unicode locale extensions on locale.
  if (colOptions.usage == CollatorOptions::Usage::Search) {
    if (!keywords.emplaceBack("co", cx->names().search)) {
      return nullptr;
    }

    // Search collations can't select a different collation, so the collation
    // property is guaranteed to be "default".
    MOZ_ASSERT(collator->getCollation() == cx->names().default_);
  } else {
    auto* collation = collator->getCollation();

    // Set collation as a Unicode locale extension when it was specified.
    if (collation != cx->names().default_) {
      if (!keywords.emplaceBack("co", collation)) {
        return nullptr;
      }
    }
  }

  Rooted<JSLinearString*> localeStr(cx, collator->getLocale());
  auto locale = FormatLocale(cx, localeStr, keywords);
  if (!locale) {
    return nullptr;
  }

  mozilla::intl::CollatorOptions options = {
      .sensitivity = colOptions.sensitivity,
      .caseFirst = colOptions.caseFirst,
      .ignorePunctuation = colOptions.ignorePunctuation,
      .numeric = colOptions.numeric,
  };

  auto collResult = mozilla::intl::Collator::TryCreate(
      mozilla::MakeStringSpan(locale.get()), options);
  if (collResult.isErr()) {
    ReportInternalError(cx, collResult.unwrapErr());
    return nullptr;
  }
  auto coll = collResult.unwrap();

  return coll.release();
}

static mozilla::intl::Collator* GetOrCreateCollator(
    JSContext* cx, Handle<CollatorObject*> collator) {
  // Obtain a cached mozilla::intl::Collator object.
  if (auto* coll = collator->getCollator()) {
    return coll;
  }

  auto* coll = NewIntlCollator(cx, collator);
  if (!coll) {
    return nullptr;
  }
  collator->setCollator(coll);

  AddICUCellMemory(collator, CollatorObject::EstimatedMemoryUse);
  return coll;
}

static bool ResolvedOptions(JSContext* cx, Handle<CollatorObject*> collator,
                            CollatorOptions* result) {
  if (!ResolveLocale(cx, collator)) {
    return false;
  }
  auto options = collator->getOptions();

  // If any option is set to |Locale|, we need to construct a Collator to
  // determine the resolved options.
  if (options.ignorePunctuation == CollatorOptions::IgnorePunctuation::Locale ||
      options.numeric == CollatorOptions::Numeric::Locale ||
      options.caseFirst == CollatorOptions::CaseFirst::Locale) {
    auto* coll = GetOrCreateCollator(cx, collator);
    if (!coll) {
      return false;
    }
    auto resolvedOptions = coll->ResolvedOptions();

    // Assert all fixed input options match the resolved options.
    MOZ_ASSERT(options.sensitivity == resolvedOptions.sensitivity);
    MOZ_ASSERT_IF(
        options.ignorePunctuation != CollatorOptions::IgnorePunctuation::Locale,
        options.ignorePunctuation == resolvedOptions.ignorePunctuation);
    MOZ_ASSERT_IF(options.numeric != CollatorOptions::Numeric::Locale,
                  options.numeric == resolvedOptions.numeric);
    MOZ_ASSERT_IF(options.caseFirst != CollatorOptions::CaseFirst::Locale,
                  options.caseFirst == resolvedOptions.caseFirst);

    // Assert all options are resolved to a locale-specific value.
    MOZ_ASSERT(resolvedOptions.ignorePunctuation !=
               CollatorOptions::IgnorePunctuation::Locale);
    MOZ_ASSERT(resolvedOptions.numeric != CollatorOptions::Numeric::Locale);
    MOZ_ASSERT(resolvedOptions.caseFirst != CollatorOptions::CaseFirst::Locale);

    // Case first defaults to "false" for all search collations.
    MOZ_ASSERT_IF(
        options.usage == CollatorOptions::Usage::Search &&
            options.caseFirst == CollatorOptions::CaseFirst::Locale,
        resolvedOptions.caseFirst == CollatorOptions::CaseFirst::False);

    // Numeric defaults to "false" for all locales.
    MOZ_ASSERT_IF(options.numeric == CollatorOptions::Numeric::Locale,
                  resolvedOptions.numeric == CollatorOptions::Numeric::Off);

    options.ignorePunctuation = resolvedOptions.ignorePunctuation;
    options.numeric = resolvedOptions.numeric;
    options.caseFirst = resolvedOptions.caseFirst;

    // Set the resolved options.
    collator->setOptions(options);
  }

  *result = options;
  return true;
}

bool js::intl::CompareStrings(JSContext* cx, Handle<CollatorObject*> collator,
                              Handle<JSString*> str1, Handle<JSString*> str2,
                              MutableHandle<Value> result) {
  MOZ_ASSERT(str1);
  MOZ_ASSERT(str2);

  if (str1 == str2) {
    result.setInt32(0);
    return true;
  }

  auto* coll = GetOrCreateCollator(cx, collator);
  if (!coll) {
    return false;
  }

  JSLinearString* linear1 = str1->ensureLinear(cx);
  if (!linear1) {
    return false;
  }
  JSLinearString* linear2 = str2->ensureLinear(cx);
  if (!linear2) {
    return false;
  }

  int32_t ret;
  JS::AutoCheckCannotGC nogc;
  if (linear1->hasLatin1Chars()) {
    if (linear2->hasLatin1Chars()) {
      ret = coll->CompareLatin1(linear1->latin1Range(nogc),
                                linear2->latin1Range(nogc));
    } else {
      ret = coll->CompareLatin1UTF16(linear1->latin1Range(nogc),
                                     linear2->twoByteRange(nogc));
    }
  } else {
    if (linear2->hasTwoByteChars()) {
      ret = coll->CompareUTF16(linear1->twoByteRange(nogc),
                               linear2->twoByteRange(nogc));
    } else {
      // We don't have `CompareUTF16Latin1`, so we're responsible for flipping
      // the result here.
      ret = -(coll->CompareLatin1UTF16(linear2->latin1Range(nogc),
                                       linear1->twoByteRange(nogc)));
    }
  }

  result.setInt32(ret);
  return true;
}

static bool IsCollator(Handle<JS::Value> v) {
  return v.isObject() && v.toObject().is<CollatorObject>();
}

static constexpr uint32_t CollatorCompareFunction_Collator = 0;

/**
 * Collator Compare Functions
 */

static bool CollatorCompareFunction(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  // Steps 1-2.
  auto* compare = &args.callee().as<JSFunction>();
  auto collValue = compare->getExtendedSlot(CollatorCompareFunction_Collator);
  Rooted<CollatorObject*> collator(cx,
                                   &collValue.toObject().as<CollatorObject>());

  // Step 3.
  Rooted<JSString*> x(cx, JS::ToString(cx, args.get(0)));
  if (!x) {
    return false;
  }

  // Step 4.
  Rooted<JSString*> y(cx, JS::ToString(cx, args.get(1)));
  if (!y) {
    return false;
  }

  // Step 5.
  return CompareStrings(cx, collator, x, y, args.rval());
}

/**
 * get Intl.Collator.prototype.compare
 */

static bool collator_compare(JSContext* cx, const CallArgs& args) {
  Rooted<CollatorObject*> collator(
      cx, &args.thisv().toObject().as<CollatorObject>());

  // Step 3.
  auto* boundCompare = collator->getBoundCompare();
  if (!boundCompare) {
    Handle<PropertyName*> funName = cx->names().empty_;
    auto* fn =
        NewNativeFunction(cx, CollatorCompareFunction, 2, funName,
                          gc::AllocKind::FUNCTION_EXTENDED, GenericObject);
    if (!fn) {
      return false;
    }
    fn->initExtendedSlot(CollatorCompareFunction_Collator,
                         ObjectValue(*collator));

    collator->setBoundCompare(fn);
    boundCompare = fn;
  }

  // Step 4.
  args.rval().setObject(*boundCompare);
  return true;
}

/**
 * get Intl.Collator.prototype.compare
 */

static bool collator_compare(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsCollator, collator_compare>(cx, args);
}

/**
 * Intl.Collator.prototype.resolvedOptions ( )
 */

static bool collator_resolvedOptions(JSContext* cx, const CallArgs& args) {
  Rooted<CollatorObject*> collator(
      cx, &args.thisv().toObject().as<CollatorObject>());

  CollatorOptions colOptions;
  if (!ResolvedOptions(cx, collator, &colOptions)) {
    return false;
  }

  // Step 3.
  Rooted<IdValueVector> options(cx, cx);

  // Step 4.
  if (!options.emplaceBack(NameToId(cx->names().locale),
                           StringValue(collator->getLocale()))) {
    return false;
  }

  auto* usage = NewStringCopy<CanGC>(cx, UsageToString(colOptions.usage));
  if (!usage) {
    return false;
  }
  if (!options.emplaceBack(NameToId(cx->names().usage), StringValue(usage))) {
    return false;
  }

  auto* sensitivity =
      NewStringCopy<CanGC>(cx, SensitivityToString(colOptions.sensitivity));
  if (!sensitivity) {
    return false;
  }
  if (!options.emplaceBack(NameToId(cx->names().sensitivity),
                           StringValue(sensitivity))) {
    return false;
  }

  if (!options.emplaceBack(
          NameToId(cx->names().ignorePunctuation),
          BooleanValue(colOptions.ignorePunctuation ==
                       CollatorOptions::IgnorePunctuation::On))) {
    return false;
  }

  if (!options.emplaceBack(NameToId(cx->names().collation),
                           StringValue(collator->getCollation()))) {
    return false;
  }

  if (!options.emplaceBack(
          NameToId(cx->names().numeric),
          BooleanValue(colOptions.numeric == CollatorOptions::Numeric::On))) {
    return false;
  }

  auto* caseFirst =
      NewStringCopy<CanGC>(cx, CaseFirstToString(colOptions.caseFirst));
  if (!caseFirst) {
    return false;
  }
  if (!options.emplaceBack(NameToId(cx->names().caseFirst),
                           StringValue(caseFirst))) {
    return false;
  }

  // Step 5.
  auto* result = NewPlainObjectWithUniqueNames(cx, options);
  if (!result) {
    return false;
  }
  args.rval().setObject(*result);
  return true;
}

/**
 * Intl.Collator.prototype.resolvedOptions ( )
 */

static bool collator_resolvedOptions(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsCollator, collator_resolvedOptions>(cx, args);
}

/**
 * Intl.Collator.supportedLocalesOf ( locales [ , options ] )
 */

static bool collator_supportedLocalesOf(JSContext* cx, unsigned argc,
                                        Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  // Steps 1-3.
  auto* array = SupportedLocalesOf(cx, AvailableLocaleKind::Collator,
                                   args.get(0), args.get(1));
  if (!array) {
    return false;
  }
  args.rval().setObject(*array);
  return true;
}

Messung V0.5 in Prozent
C=88 H=98 G=93

¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.11Bemerkung:  ¤

*Bot Zugriff






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.