tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CommonFunctions.js (26500B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 /* Portions Copyright Norbert Lindenberg 2011-2012. */
      6 
      7 #ifdef DEBUG
      8 #define JS_CONCAT2(x, y) x##y
      9 #define JS_CONCAT(x, y) JS_CONCAT2(x, y)
     10 #define assertIsValidAndCanonicalLanguageTag(locale, desc) \
     11  do { \
     12    var JS_CONCAT(canonical, __LINE__) = intl_TryValidateAndCanonicalizeLanguageTag(locale); \
     13    assert(JS_CONCAT(canonical, __LINE__) !== null, \
     14           `${desc} is a structurally valid language tag`); \
     15    assert(JS_CONCAT(canonical, __LINE__) === locale, \
     16           `${desc} is a canonicalized language tag`); \
     17  } while (false)
     18 #else
     19 #define assertIsValidAndCanonicalLanguageTag(locale, desc) ; // Elided assertion.
     20 #endif
     21 
     22 /**
     23 * Returns the start index of a "Unicode locale extension sequence", which the
     24 * specification defines as: "any substring of a language tag that starts with
     25 * a separator '-' and the singleton 'u' and includes the maximum sequence of
     26 * following non-singleton subtags and their preceding '-' separators."
     27 *
     28 * Alternatively, this may be defined as: the components of a language tag that
     29 * match the `unicode_locale_extensions` production in UTS 35.
     30 *
     31 * Spec: ECMAScript Internationalization API Specification, 6.2.1.
     32 */
     33 function startOfUnicodeExtensions(locale) {
     34  assert(typeof locale === "string", "locale is a string");
     35 
     36  // Search for "-u-" marking the start of a Unicode extension sequence.
     37  var start = callFunction(std_String_indexOf, locale, "-u-");
     38  if (start < 0) {
     39    return -1;
     40  }
     41 
     42  // And search for "-x-" marking the start of any privateuse component to
     43  // handle the case when "-u-" was only found within a privateuse subtag.
     44  var privateExt = callFunction(std_String_indexOf, locale, "-x-");
     45  if (privateExt >= 0 && privateExt < start) {
     46    return -1;
     47  }
     48 
     49  return start;
     50 }
     51 
     52 /**
     53 * Returns the end index of a Unicode locale extension sequence.
     54 */
     55 function endOfUnicodeExtensions(locale, start) {
     56  assert(typeof locale === "string", "locale is a string");
     57  assert(0 <= start && start < locale.length, "start is an index into locale");
     58  assert(
     59    Substring(locale, start, 3) === "-u-",
     60    "start points to Unicode extension sequence"
     61  );
     62 
     63  // Search for the start of the next singleton or privateuse subtag.
     64  //
     65  // Begin searching after the smallest possible Unicode locale extension
     66  // sequence, namely |"-u-" 2alphanum|. End searching once the remaining
     67  // characters can't fit the smallest possible singleton or privateuse
     68  // subtag, namely |"-x-" alphanum|. Note the reduced end-limit means
     69  // indexing inside the loop is always in-range.
     70  for (var i = start + 5, end = locale.length - 4; i <= end; i++) {
     71    if (locale[i] !== "-") {
     72      continue;
     73    }
     74    if (locale[i + 2] === "-") {
     75      return i;
     76    }
     77 
     78    // Skip over (i + 1) and (i + 2) because we've just verified they
     79    // aren't "-", so the next possible delimiter can only be at (i + 3).
     80    i += 2;
     81  }
     82 
     83  // If no singleton or privateuse subtag was found, the Unicode extension
     84  // sequence extends until the end of the string.
     85  return locale.length;
     86 }
     87 
     88 /**
     89 * Removes Unicode locale extension sequences from the given language tag.
     90 */
     91 function removeUnicodeExtensions(locale) {
     92  assertIsValidAndCanonicalLanguageTag(
     93    locale,
     94    "locale with possible Unicode extension"
     95  );
     96 
     97  var start = startOfUnicodeExtensions(locale);
     98  if (start < 0) {
     99    return locale;
    100  }
    101 
    102  var end = endOfUnicodeExtensions(locale, start);
    103 
    104  var left = Substring(locale, 0, start);
    105  var right = Substring(locale, end, locale.length - end);
    106  var combined = left + right;
    107 
    108  assertIsValidAndCanonicalLanguageTag(combined, "the recombined locale");
    109  assert(
    110    startOfUnicodeExtensions(combined) < 0,
    111    "recombination failed to remove all Unicode locale extension sequences"
    112  );
    113 
    114  return combined;
    115 }
    116 
    117 /**
    118 * Returns Unicode locale extension sequences from the given language tag.
    119 */
    120 function getUnicodeExtensions(locale) {
    121  assertIsValidAndCanonicalLanguageTag(locale, "locale with Unicode extension");
    122 
    123  var start = startOfUnicodeExtensions(locale);
    124  assert(start >= 0, "start of Unicode extension sequence not found");
    125  var end = endOfUnicodeExtensions(locale, start);
    126 
    127  return Substring(locale, start, end - start);
    128 }
    129 
    130 /**
    131 * Returns true if the input contains only ASCII alphabetical characters.
    132 */
    133 function IsASCIIAlphaString(s) {
    134  assert(typeof s === "string", "IsASCIIAlphaString");
    135 
    136  for (var i = 0; i < s.length; i++) {
    137    var c = callFunction(std_String_charCodeAt, s, i);
    138    if (!((0x41 <= c && c <= 0x5a) || (0x61 <= c && c <= 0x7a))) {
    139      return false;
    140    }
    141  }
    142  return true;
    143 }
    144 
    145 /**
    146 * Canonicalizes a locale list.
    147 *
    148 * Spec: ECMAScript Internationalization API Specification, 9.2.1.
    149 */
    150 function CanonicalizeLocaleList(locales) {
    151  // Step 1.
    152  if (locales === undefined) {
    153    return [];
    154  }
    155 
    156  // Step 3 (and the remaining steps).
    157  var tag = intl_ValidateAndCanonicalizeLanguageTag(locales, false);
    158  if (tag !== null) {
    159    assert(
    160      typeof tag === "string",
    161      "intl_ValidateAndCanonicalizeLanguageTag returns a string value"
    162    );
    163    return [tag];
    164  }
    165 
    166  // Step 2.
    167  var seen = [];
    168 
    169  // Step 4.
    170  var O = ToObject(locales);
    171 
    172  // Step 5.
    173  var len = ToLength(O.length);
    174 
    175  // Step 6.
    176  var k = 0;
    177 
    178  // Step 7.
    179  while (k < len) {
    180    // Steps 7.a-c.
    181    if (k in O) {
    182      // Step 7.c.i.
    183      var kValue = O[k];
    184 
    185      // Step 7.c.ii.
    186      if (!(typeof kValue === "string" || IsObject(kValue))) {
    187        ThrowTypeError(JSMSG_INVALID_LOCALES_ELEMENT);
    188      }
    189 
    190      // Steps 7.c.iii-iv.
    191      var tag = intl_ValidateAndCanonicalizeLanguageTag(kValue, true);
    192      assert(
    193        typeof tag === "string",
    194        "ValidateAndCanonicalizeLanguageTag returns a string value"
    195      );
    196 
    197      // Step 7.c.v.
    198      if (callFunction(std_Array_indexOf, seen, tag) === -1) {
    199        DefineDataProperty(seen, seen.length, tag);
    200      }
    201    }
    202 
    203    // Step 7.d.
    204    k++;
    205  }
    206 
    207  // Step 8.
    208  return seen;
    209 }
    210 
    211 /**
    212 * Compares a BCP 47 language tag against the locales in availableLocales
    213 * and returns the best available match. Uses the fallback
    214 * mechanism of RFC 4647, section 3.4.
    215 *
    216 * Spec: ECMAScript Internationalization API Specification, 9.2.2.
    217 * Spec: RFC 4647, section 3.4.
    218 */
    219 function BestAvailableLocale(availableLocales, locale) {
    220  return intl_BestAvailableLocale(availableLocales, locale, intl_DefaultLocale());
    221 }
    222 
    223 /**
    224 * Identical to BestAvailableLocale, but does not consider the default locale
    225 * during computation.
    226 */
    227 function BestAvailableLocaleIgnoringDefault(availableLocales, locale) {
    228  return intl_BestAvailableLocale(availableLocales, locale, null);
    229 }
    230 
    231 /**
    232 * Compares a BCP 47 language priority list against the set of locales in
    233 * availableLocales and determines the best available language to meet the
    234 * request. Options specified through Unicode extension subsequences are
    235 * ignored in the lookup, but information about such subsequences is returned
    236 * separately.
    237 *
    238 * This variant is based on the Lookup algorithm of RFC 4647 section 3.4.
    239 *
    240 * Spec: ECMAScript Internationalization API Specification, 9.2.3.
    241 * Spec: RFC 4647, section 3.4.
    242 */
    243 function LookupMatcher(availableLocales, requestedLocales) {
    244  // Step 1.
    245  var result = NEW_RECORD();
    246 
    247  // Step 2.
    248  for (var i = 0; i < requestedLocales.length; i++) {
    249    var locale = requestedLocales[i];
    250 
    251    // Step 2.a.
    252    var noExtensionsLocale = removeUnicodeExtensions(locale);
    253 
    254    // Step 2.b.
    255    var availableLocale = BestAvailableLocale(
    256      availableLocales,
    257      noExtensionsLocale
    258    );
    259 
    260    // Step 2.c.
    261    if (availableLocale !== undefined) {
    262      // Step 2.c.i.
    263      result.locale = availableLocale;
    264 
    265      // Step 2.c.ii.
    266      if (locale !== noExtensionsLocale) {
    267        result.extension = getUnicodeExtensions(locale);
    268      }
    269 
    270      // Step 2.c.iii.
    271      return result;
    272    }
    273  }
    274 
    275  // Steps 3-4.
    276  result.locale = intl_DefaultLocale();
    277 
    278  // Step 5.
    279  return result;
    280 }
    281 
    282 /**
    283 * Compares a BCP 47 language priority list against the set of locales in
    284 * availableLocales and determines the best available language to meet the
    285 * request. Options specified through Unicode extension subsequences are
    286 * ignored in the lookup, but information about such subsequences is returned
    287 * separately.
    288 *
    289 * Spec: ECMAScript Internationalization API Specification, 9.2.4.
    290 */
    291 function BestFitMatcher(availableLocales, requestedLocales) {
    292  // this implementation doesn't have anything better
    293  return LookupMatcher(availableLocales, requestedLocales);
    294 }
    295 
    296 /**
    297 * Returns the Unicode extension value subtags for the requested key subtag.
    298 *
    299 * Spec: ECMAScript Internationalization API Specification, 9.2.5.
    300 */
    301 function UnicodeExtensionValue(extension, key) {
    302  assert(typeof extension === "string", "extension is a string value");
    303  assert(
    304    callFunction(std_String_startsWith, extension, "-u-") &&
    305      getUnicodeExtensions("und" + extension) === extension,
    306    "extension is a Unicode extension subtag"
    307  );
    308  assert(typeof key === "string", "key is a string value");
    309 
    310  // Step 1.
    311  assert(key.length === 2, "key is a Unicode extension key subtag");
    312 
    313  // Step 2.
    314  var size = extension.length;
    315 
    316  // Step 3.
    317  var searchValue = "-" + key + "-";
    318 
    319  // Step 4.
    320  var pos = callFunction(std_String_indexOf, extension, searchValue);
    321 
    322  // Step 5.
    323  if (pos !== -1) {
    324    // Step 5.a.
    325    var start = pos + 4;
    326 
    327    // Step 5.b.
    328    var end = start;
    329 
    330    // Step 5.c.
    331    var k = start;
    332 
    333    // Steps 5.d-e.
    334    while (true) {
    335      // Step 5.e.i.
    336      var e = callFunction(std_String_indexOf, extension, "-", k);
    337 
    338      // Step 5.e.ii.
    339      var len = e === -1 ? size - k : e - k;
    340 
    341      // Step 5.e.iii.
    342      if (len === 2) {
    343        break;
    344      }
    345 
    346      // Step 5.e.iv.
    347      if (e === -1) {
    348        end = size;
    349        break;
    350      }
    351 
    352      // Step 5.e.v.
    353      end = e;
    354      k = e + 1;
    355    }
    356 
    357    // Step 5.f.
    358    return callFunction(String_substring, extension, start, end);
    359  }
    360 
    361  // Step 6.
    362  searchValue = "-" + key;
    363 
    364  // Steps 7-8.
    365  if (callFunction(std_String_endsWith, extension, searchValue)) {
    366    return "";
    367  }
    368 
    369  // Step 9 (implicit).
    370 }
    371 
    372 /**
    373 * Compares a BCP 47 language priority list against availableLocales and
    374 * determines the best available language to meet the request. Options specified
    375 * through Unicode extension subsequences are negotiated separately, taking the
    376 * caller's relevant extensions and locale data as well as client-provided
    377 * options into consideration.
    378 *
    379 * Spec: ECMAScript Internationalization API Specification, 9.2.6.
    380 */
    381 function ResolveLocale(
    382  availableLocales,
    383  requestedLocales,
    384  options,
    385  relevantExtensionKeys,
    386  localeData
    387 ) {
    388  // Steps 1-3.
    389  var matcher = options.localeMatcher;
    390  var r =
    391    matcher === "lookup"
    392      ? LookupMatcher(availableLocales, requestedLocales)
    393      : BestFitMatcher(availableLocales, requestedLocales);
    394 
    395  // Step 4.
    396  var foundLocale = r.locale;
    397  var extension = r.extension;
    398 
    399  // Step 5.
    400  var result = NEW_RECORD();
    401 
    402  // Step 6.
    403  result.dataLocale = foundLocale;
    404 
    405  // Step 7.
    406  var supportedExtension = "-u";
    407 
    408  // In this implementation, localeData is a function, not an object.
    409  var localeDataProvider = localeData();
    410 
    411  // Step 8.
    412  for (var i = 0; i < relevantExtensionKeys.length; i++) {
    413    var key = relevantExtensionKeys[i];
    414 
    415    // Steps 8.a-h (The locale data is only computed when needed).
    416    var keyLocaleData = undefined;
    417    var value = undefined;
    418 
    419    // Locale tag may override.
    420 
    421    // Step 8.g.
    422    var supportedExtensionAddition = "";
    423 
    424    // Step 8.h.
    425    if (extension !== undefined) {
    426      // Step 8.h.i.
    427      var requestedValue = UnicodeExtensionValue(extension, key);
    428 
    429      // Step 8.h.ii.
    430      if (requestedValue !== undefined) {
    431        // Steps 8.a-d.
    432        keyLocaleData = callFunction(
    433          localeDataProvider[key],
    434          null,
    435          foundLocale
    436        );
    437 
    438        // Step 8.h.ii.1.
    439        if (requestedValue !== "") {
    440          // Step 8.h.ii.1.a.
    441          if (
    442            callFunction(std_Array_indexOf, keyLocaleData, requestedValue) !==
    443            -1
    444          ) {
    445            value = requestedValue;
    446            supportedExtensionAddition = "-" + key + "-" + value;
    447          }
    448        } else {
    449          // Step 8.h.ii.2.
    450 
    451          // According to the LDML spec, if there's no type value,
    452          // and true is an allowed value, it's used.
    453          if (callFunction(std_Array_indexOf, keyLocaleData, "true") !== -1) {
    454            value = "true";
    455            supportedExtensionAddition = "-" + key;
    456          }
    457        }
    458      }
    459    }
    460 
    461    // Options override all.
    462 
    463    // Step 8.i.i.
    464    var optionsValue = options[key];
    465 
    466    // Step 8.i.ii.
    467    assert(
    468      typeof optionsValue === "string" ||
    469        optionsValue === undefined ||
    470        optionsValue === null,
    471      "unexpected type for options value"
    472    );
    473 
    474    // Steps 8.i, 8.i.iii.1.
    475    if (optionsValue !== undefined && optionsValue !== value) {
    476      // Steps 8.a-d.
    477      if (keyLocaleData === undefined) {
    478        keyLocaleData = callFunction(
    479          localeDataProvider[key],
    480          null,
    481          foundLocale
    482        );
    483      }
    484 
    485      // Step 8.i.iii.
    486      if (callFunction(std_Array_indexOf, keyLocaleData, optionsValue) !== -1) {
    487        value = optionsValue;
    488        supportedExtensionAddition = "";
    489      }
    490    }
    491 
    492    // Locale data provides default value.
    493    if (value === undefined) {
    494      // Steps 8.a-f.
    495      value =
    496        keyLocaleData === undefined
    497          ? callFunction(localeDataProvider.default[key], null, foundLocale)
    498          : keyLocaleData[0];
    499    }
    500 
    501    // Step 8.j.
    502    assert(
    503      typeof value === "string" || value === null,
    504      "unexpected locale data value"
    505    );
    506    result[key] = value;
    507 
    508    // Step 8.k.
    509    supportedExtension += supportedExtensionAddition;
    510  }
    511 
    512  // Step 9.
    513  if (supportedExtension.length > 2) {
    514    foundLocale = addUnicodeExtension(foundLocale, supportedExtension);
    515  }
    516 
    517  // Step 10.
    518  result.locale = foundLocale;
    519 
    520  // Step 11.
    521  return result;
    522 }
    523 
    524 /**
    525 * Adds a Unicode extension subtag to a locale.
    526 *
    527 * Spec: ECMAScript Internationalization API Specification, 9.2.6.
    528 */
    529 function addUnicodeExtension(locale, extension) {
    530  assert(typeof locale === "string", "locale is a string value");
    531  assert(
    532    !callFunction(std_String_startsWith, locale, "x-"),
    533    "unexpected privateuse-only locale"
    534  );
    535  assert(
    536    startOfUnicodeExtensions(locale) < 0,
    537    "Unicode extension subtag already present in locale"
    538  );
    539 
    540  assert(typeof extension === "string", "extension is a string value");
    541  assert(
    542    callFunction(std_String_startsWith, extension, "-u-") &&
    543      getUnicodeExtensions("und" + extension) === extension,
    544    "extension is a Unicode extension subtag"
    545  );
    546 
    547  // Step 9.a.
    548  var privateIndex = callFunction(std_String_indexOf, locale, "-x-");
    549 
    550  // Steps 9.b-c.
    551  if (privateIndex === -1) {
    552    locale += extension;
    553  } else {
    554    var preExtension = callFunction(String_substring, locale, 0, privateIndex);
    555    var postExtension = callFunction(String_substring, locale, privateIndex);
    556    locale = preExtension + extension + postExtension;
    557  }
    558 
    559  // Steps 9.d-e (Step 9.e is not required in this implementation, because we don't canonicalize
    560  // Unicode extension subtags).
    561  assertIsValidAndCanonicalLanguageTag(locale, "locale after concatenation");
    562 
    563  return locale;
    564 }
    565 
    566 /**
    567 * Extracts a property value from the provided options object, converts it to
    568 * the required type, checks whether it is one of a list of allowed values,
    569 * and fills in a fallback value if necessary.
    570 *
    571 * Spec: ECMAScript Internationalization API Specification, 9.2.10.
    572 */
    573 function GetOption(options, property, type, values, fallback) {
    574  // Step 1.
    575  var value = options[property];
    576 
    577  // Step 2.
    578  if (value !== undefined) {
    579    // Steps 2.a-c.
    580    if (type === "boolean") {
    581      value = TO_BOOLEAN(value);
    582    } else if (type === "string") {
    583      value = ToString(value);
    584    } else {
    585      assert(false, "GetOption");
    586    }
    587 
    588    // Step 2.d.
    589    if (
    590      values !== undefined &&
    591      callFunction(std_Array_indexOf, values, value) === -1
    592    ) {
    593      ThrowRangeError(JSMSG_INVALID_OPTION_VALUE, property, `"${value}"`);
    594    }
    595 
    596    // Step 2.e.
    597    return value;
    598  }
    599 
    600  // Step 3.
    601  return fallback;
    602 }
    603 
    604 /**
    605 * Extracts a property value from the provided options object, converts it to
    606 * a boolean or string, checks whether it is one of a list of allowed values,
    607 * and fills in a fallback value if necessary.
    608 */
    609 function GetStringOrBooleanOption(
    610  options,
    611  property,
    612  stringValues,
    613  fallback
    614 ) {
    615  assert(IsObject(stringValues), "GetStringOrBooleanOption");
    616 
    617  // Step 1.
    618  var value = options[property];
    619 
    620  // Step 2.
    621  if (value === undefined) {
    622    return fallback;
    623  }
    624 
    625  // Step 3.
    626  if (value === true) {
    627    return true;
    628  }
    629 
    630  // Steps 4-5.
    631  if (!value) {
    632    return false;
    633  }
    634 
    635  // Step 6.
    636  value = ToString(value);
    637 
    638  // Step 7.
    639  if (callFunction(std_Array_indexOf, stringValues, value) === -1) {
    640    ThrowRangeError(JSMSG_INVALID_OPTION_VALUE, property, `"${value}"`);
    641  }
    642 
    643  // Step 8.
    644  return value;
    645 }
    646 
    647 /**
    648 * The abstract operation DefaultNumberOption converts value to a Number value,
    649 * checks whether it is in the allowed range, and fills in a fallback value if
    650 * necessary.
    651 *
    652 * Spec: ECMAScript Internationalization API Specification, 9.2.11.
    653 */
    654 function DefaultNumberOption(value, minimum, maximum, fallback) {
    655  assert(
    656    typeof minimum === "number" && (minimum | 0) === minimum,
    657    "DefaultNumberOption"
    658  );
    659  assert(
    660    typeof maximum === "number" && (maximum | 0) === maximum,
    661    "DefaultNumberOption"
    662  );
    663  assert(
    664    fallback === undefined ||
    665      (typeof fallback === "number" && (fallback | 0) === fallback),
    666    "DefaultNumberOption"
    667  );
    668  assert(
    669    fallback === undefined || (minimum <= fallback && fallback <= maximum),
    670    "DefaultNumberOption"
    671  );
    672 
    673  // Step 1.
    674  if (value === undefined) {
    675    return fallback;
    676  }
    677 
    678  // Step 2.
    679  value = TO_NUMBER(value);
    680 
    681  // Step 3.
    682  if (Number_isNaN(value) || value < minimum || value > maximum) {
    683    ThrowRangeError(JSMSG_INVALID_DIGITS_VALUE, value);
    684  }
    685 
    686  // Step 4.
    687  // Apply bitwise-or to convert -0 to +0 per ES2017, 5.2 and to ensure the
    688  // result is an int32 value.
    689  return std_Math_floor(value) | 0;
    690 }
    691 
    692 /**
    693 * Extracts a property value from the provided options object, converts it to a
    694 * Number value, checks whether it is in the allowed range, and fills in a
    695 * fallback value if necessary.
    696 *
    697 * Spec: ECMAScript Internationalization API Specification, 9.2.12.
    698 */
    699 function GetNumberOption(options, property, minimum, maximum, fallback) {
    700  // Steps 1-2.
    701  return DefaultNumberOption(options[property], minimum, maximum, fallback);
    702 }
    703 
    704 // Symbols in the self-hosting compartment can't be cloned, use a separate
    705 // object to hold the actual symbol value.
    706 // TODO: Can we add support to clone symbols?
    707 var intlFallbackSymbolHolder = { value: undefined };
    708 
    709 /**
    710 * The [[FallbackSymbol]] symbol of the %Intl% intrinsic object.
    711 *
    712 * This symbol is used to implement the legacy constructor semantics for
    713 * Intl.DateTimeFormat and Intl.NumberFormat.
    714 */
    715 function intlFallbackSymbol() {
    716  var fallbackSymbol = intlFallbackSymbolHolder.value;
    717  if (!fallbackSymbol) {
    718    var Symbol = GetBuiltinConstructor("Symbol");
    719    fallbackSymbol = Symbol("IntlLegacyConstructedSymbol");
    720    intlFallbackSymbolHolder.value = fallbackSymbol;
    721  }
    722  return fallbackSymbol;
    723 }
    724 
    725 /**
    726 * Initializes the INTL_INTERNALS_OBJECT_SLOT of the given object.
    727 */
    728 function initializeIntlObject(obj, type, lazyData) {
    729  assert(IsObject(obj), "Non-object passed to initializeIntlObject");
    730  assert(
    731    (type === "Collator" && intl_GuardToCollator(obj) !== null) ||
    732      (type === "DateTimeFormat" && intl_GuardToDateTimeFormat(obj) !== null) ||
    733      (type === "DisplayNames" && intl_GuardToDisplayNames(obj) !== null) ||
    734      (type === "DurationFormat" && intl_GuardToDurationFormat(obj) !== null) ||
    735      (type === "ListFormat" && intl_GuardToListFormat(obj) !== null) ||
    736      (type === "NumberFormat" && intl_GuardToNumberFormat(obj) !== null) ||
    737      (type === "PluralRules" && intl_GuardToPluralRules(obj) !== null) ||
    738      (type === "RelativeTimeFormat" &&
    739        intl_GuardToRelativeTimeFormat(obj) !== null) ||
    740      (type === "Segmenter" && intl_GuardToSegmenter(obj) !== null),
    741    "type must match the object's class"
    742  );
    743  assert(IsObject(lazyData), "non-object lazy data");
    744 
    745  // The meaning of an internals object for an object |obj| is as follows.
    746  //
    747  // The .type property indicates the type of Intl object that |obj| is. It
    748  // must be one of:
    749  // - Collator
    750  // - DateTimeFormat
    751  // - DisplayNames
    752  // - DurationFormat
    753  // - ListFormat
    754  // - NumberFormat
    755  // - PluralRules
    756  // - RelativeTimeFormat
    757  // - Segmenter
    758  //
    759  // The .lazyData property stores information needed to compute -- without
    760  // observable side effects -- the actual internal Intl properties of
    761  // |obj|.  If it is non-null, then the actual internal properties haven't
    762  // been computed, and .lazyData must be processed by
    763  // |setInternalProperties| before internal Intl property values are
    764  // available.  If it is null, then the .internalProps property contains an
    765  // object whose properties are the internal Intl properties of |obj|.
    766 
    767  var internals = std_Object_create(null);
    768  internals.type = type;
    769  internals.lazyData = lazyData;
    770  internals.internalProps = null;
    771 
    772  assert(
    773    UnsafeGetReservedSlot(obj, INTL_INTERNALS_OBJECT_SLOT) === undefined,
    774    "Internal slot already initialized?"
    775  );
    776  UnsafeSetReservedSlot(obj, INTL_INTERNALS_OBJECT_SLOT, internals);
    777 }
    778 
    779 /**
    780 * Set the internal properties object for an |internals| object previously
    781 * associated with lazy data.
    782 */
    783 function setInternalProperties(internals, internalProps) {
    784  assert(IsObject(internals.lazyData), "lazy data must exist already");
    785  assert(IsObject(internalProps), "internalProps argument should be an object");
    786 
    787  // Set in reverse order so that the .lazyData nulling is a barrier.
    788  internals.internalProps = internalProps;
    789  internals.lazyData = null;
    790 }
    791 
    792 /**
    793 * Get the existing internal properties out of a non-newborn |internals|, or
    794 * null if none have been computed.
    795 */
    796 function maybeInternalProperties(internals) {
    797  assert(IsObject(internals), "non-object passed to maybeInternalProperties");
    798  var lazyData = internals.lazyData;
    799  if (lazyData) {
    800    return null;
    801  }
    802  assert(
    803    IsObject(internals.internalProps),
    804    "missing lazy data and computed internals"
    805  );
    806  return internals.internalProps;
    807 }
    808 
    809 /**
    810 * Return |obj|'s internals object (*not* the object holding its internal
    811 * properties!), with structure specified above.
    812 *
    813 * Spec: ECMAScript Internationalization API Specification, 10.3.
    814 * Spec: ECMAScript Internationalization API Specification, 11.3.
    815 * Spec: ECMAScript Internationalization API Specification, 12.3.
    816 */
    817 function getIntlObjectInternals(obj) {
    818  assert(IsObject(obj), "getIntlObjectInternals called with non-Object");
    819  assert(
    820    intl_GuardToCollator(obj) !== null ||
    821      intl_GuardToDateTimeFormat(obj) !== null ||
    822      intl_GuardToDisplayNames(obj) !== null ||
    823      intl_GuardToDurationFormat(obj) !== null ||
    824      intl_GuardToListFormat(obj) !== null ||
    825      intl_GuardToNumberFormat(obj) !== null ||
    826      intl_GuardToPluralRules(obj) !== null ||
    827      intl_GuardToRelativeTimeFormat(obj) !== null ||
    828      intl_GuardToSegmenter(obj) !== null,
    829    "getIntlObjectInternals called with non-Intl object"
    830  );
    831 
    832  var internals = UnsafeGetReservedSlot(obj, INTL_INTERNALS_OBJECT_SLOT);
    833 
    834  assert(IsObject(internals), "internals not an object");
    835  assert(hasOwn("type", internals), "missing type");
    836  assert(
    837    (internals.type === "Collator" && intl_GuardToCollator(obj) !== null) ||
    838      (internals.type === "DateTimeFormat" &&
    839        intl_GuardToDateTimeFormat(obj) !== null) ||
    840      (internals.type === "DisplayNames" &&
    841        intl_GuardToDisplayNames(obj) !== null) ||
    842      (internals.type === "DurationFormat" &&
    843        intl_GuardToDurationFormat(obj) !== null) ||
    844      (internals.type === "ListFormat" &&
    845        intl_GuardToListFormat(obj) !== null) ||
    846      (internals.type === "NumberFormat" &&
    847        intl_GuardToNumberFormat(obj) !== null) ||
    848      (internals.type === "PluralRules" &&
    849        intl_GuardToPluralRules(obj) !== null) ||
    850      (internals.type === "RelativeTimeFormat" &&
    851        intl_GuardToRelativeTimeFormat(obj) !== null) ||
    852      (internals.type === "Segmenter" &&
    853        intl_GuardToSegmenter(obj) !== null),
    854    "type must match the object's class"
    855  );
    856  assert(hasOwn("lazyData", internals), "missing lazyData");
    857  assert(hasOwn("internalProps", internals), "missing internalProps");
    858 
    859  return internals;
    860 }
    861 
    862 /**
    863 * Get the internal properties of known-Intl object |obj|.  For use only by
    864 * C++ code that knows what it's doing!
    865 */
    866 function getInternals(obj) {
    867  var internals = getIntlObjectInternals(obj);
    868 
    869  // If internal properties have already been computed, use them.
    870  var internalProps = maybeInternalProperties(internals);
    871  if (internalProps) {
    872    return internalProps;
    873  }
    874 
    875  // Otherwise it's time to fully create them.
    876  var type = internals.type;
    877  if (type === "Collator") {
    878    internalProps = resolveCollatorInternals(internals.lazyData);
    879  } else if (type === "DateTimeFormat") {
    880    internalProps = resolveDateTimeFormatInternals(internals.lazyData);
    881  } else if (type === "DisplayNames") {
    882    internalProps = resolveDisplayNamesInternals(internals.lazyData);
    883  } else if (type === "DurationFormat") {
    884    internalProps = resolveDurationFormatInternals(internals.lazyData);
    885  } else if (type === "ListFormat") {
    886    internalProps = resolveListFormatInternals(internals.lazyData);
    887  } else if (type === "NumberFormat") {
    888    internalProps = resolveNumberFormatInternals(internals.lazyData);
    889  } else if (type === "PluralRules") {
    890    internalProps = resolvePluralRulesInternals(internals.lazyData);
    891  } else if (type === "RelativeTimeFormat") {
    892    internalProps = resolveRelativeTimeFormatInternals(internals.lazyData);
    893  } else {
    894    assert(type === "Segmenter", "unexpected Intl type");
    895    internalProps = resolveSegmenterInternals(internals.lazyData);
    896  }
    897  setInternalProperties(internals, internalProps);
    898  return internalProps;
    899 }