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 }