@@ -48,48 +48,76 @@ using namespace PlatformAgnostic::ICUHelpers;
4848
4949#endif // INTL_ICU
5050
51- // NOTE(jahorto): Keep these enums in sync with those by the same name in Intl.js
52- // These enums are used by both WinGlob- and ICU-backed Intl
53- enum class NumberFormatStyle
54- {
55- Decimal, // Intl.NumberFormat(locale, { style: "decimal" }); // aka in our code as "number"
56- Percent, // Intl.NumberFormat(locale, { style: "percent" });
57- Currency, // Intl.NumberFormat(locale, { style: "currency", ... });
58-
59- Max,
60- Default = Decimal,
61- };
62-
63- enum class NumberFormatCurrencyDisplay
64- {
65- Symbol, // Intl.NumberFormat(locale, { style: "currency", currencyDisplay: "symbol" }); // e.g. "$" or "US$" depeding on locale
66- Code, // Intl.NumberFormat(locale, { style: "currency", currencyDisplay: "code" }); // e.g. "USD"
67- Name, // Intl.NumberFormat(locale, { style: "currency", currencyDisplay: "name" }); // e.g. "US dollar"
68-
69- Max,
70- Default = Symbol,
51+ // The following macros allow the key-value pairs to be C++ enums as well as JS objects
52+ // in Intl.js. When adding a new macro, follow the same format as the _VALUES macros below,
53+ // and add your new _VALUES macro to PROJECTED_ENUMS along with the name of the enum.
54+ // NOTE: make sure the last VALUE macro has the highest integer value, since the C++ enum's ::Max
55+ // value is added to the end of the C++ enum definition as an increment of the previous value.
56+ // The ::Max value is used in a defensive assert, and we want to make sure its always 1 greater
57+ // than the highest valid value.
58+
59+ #define NUMBERFORMATSTYLE_VALUES (VALUE ) \
60+ VALUE (Default, default_, 0 ) \
61+ VALUE(Decimal, decimal, 0 ) \
62+ VALUE(Percent, percent, 1 ) \
63+ VALUE(Currency, currency, 2 )
64+
65+ #define NUMBERFORMATCURRENCYDISPLAY_VALUES (VALUE ) \
66+ VALUE (Default, default_, 0 ) \
67+ VALUE(Symbol, symbol, 0 ) \
68+ VALUE(Code, code, 1 ) \
69+ VALUE(Name, name, 2 )
70+
71+ #define COLLATORSENSITIVITY_VALUES (VALUE ) \
72+ VALUE (Default, default_, 3 ) \
73+ VALUE(Base, base, 0 ) \
74+ VALUE(Accent, accent, 1 ) \
75+ VALUE(Case, case_, 2 ) \
76+ VALUE(Variant, variant, 3 )
77+
78+ #define COLLATORCASEFIRST_VALUES (VALUE ) \
79+ VALUE (Default, default_, 2 ) \
80+ VALUE(Upper, upper, 0 ) \
81+ VALUE(Lower, lower, 1 ) \
82+ VALUE(False, false_, 2 )
83+
84+ // LocaleDataKind intentionally has no Default value
85+ #define LOCALEDATAKIND_VALUES (VALUE ) \
86+ VALUE (Collation, co, 0 ) \
87+ VALUE(CaseFirst, kf, 1 ) \
88+ VALUE(Numeric, kn, 2 ) \
89+ VALUE(Calendar, ca, 3 ) \
90+ VALUE(NumberingSystem, nu, 4 ) \
91+ VALUE(HourCycle, hc, 5 )
92+
93+ // BuiltInFunctionID intentionally has no Default value
94+ #define BUILTINFUNCTIONID_VALUES (VALUE ) \
95+ VALUE (DateToLocaleString, DateToLocaleString, 0 ) \
96+ VALUE(DateToLocaleDateString, DateToLocaleDateString, 1 ) \
97+ VALUE(DateToLocaleTimeString, DateToLocaleTimeString, 2 ) \
98+ VALUE(NumberToLocaleString, NumberToLocaleString, 3 ) \
99+ VALUE(StringLocaleCompare, StringLocaleCompare, 4 )
100+
101+ #define ENUM_VALUE (enumName, propId, value ) enumName = value,
102+ #define PROJECTED_ENUM (ClassName, VALUES ) \
103+ enum class ClassName \
104+ { \
105+ VALUES (ENUM_VALUE) \
106+ Max \
71107};
72108
73- enum class CollatorSensitivity
74- {
75- Base,
76- Accent,
77- Case,
78- Variant,
109+ #define PROJECTED_ENUMS (PROJECT ) \
110+ PROJECT (LocaleDataKind, LOCALEDATAKIND_VALUES) \
111+ PROJECT(CollatorCaseFirst, COLLATORCASEFIRST_VALUES) \
112+ PROJECT(CollatorSensitivity, COLLATORSENSITIVITY_VALUES) \
113+ PROJECT(NumberFormatCurrencyDisplay, NUMBERFORMATCURRENCYDISPLAY_VALUES) \
114+ PROJECT(NumberFormatStyle, NUMBERFORMATSTYLE_VALUES) \
115+ PROJECT(BuiltInFunctionID, BUILTINFUNCTIONID_VALUES)
79116
80- Max,
81- Default = Variant,
82- };
117+ PROJECTED_ENUMS(PROJECTED_ENUM)
83118
84- enum class CollatorCaseFirst
85- {
86- Upper,
87- Lower,
88- False,
89-
90- Max,
91- Default = False,
92- };
119+ #undef PROJECTED_ENUM
120+ #undef ENUM_VALUE
93121
94122#pragma warning(push)
95123#pragma warning(disable:4309) // truncation of constant value
@@ -431,7 +459,7 @@ namespace Js
431459 template <size_t N>
432460 static void LangtagToLocaleID (_In_ JavascriptString *langtag, _Out_ char (&localeID)[N])
433461 {
434- LangtagToLocaleID (langtag->GetSz (), langtag->GetLength (), localeID);
462+ LangtagToLocaleID (langtag->GetString (), langtag->GetLength (), localeID);
435463 }
436464
437465 template <typename Callback>
@@ -533,22 +561,46 @@ namespace Js
533561
534562 bool IntlEngineInterfaceExtensionObject::InitializeIntlNativeInterfaces (DynamicObject* intlNativeInterfaces, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode)
535563 {
536- typeHandler->Convert (intlNativeInterfaces, mode, 16 );
564+ int initSlotCapacity = 0 ;
565+
566+ // automatically get the initSlotCapacity from everything we are about to add to intlNativeInterfaces
567+ #define INTL_ENTRY (id, func ) initSlotCapacity++;
568+ #include " IntlExtensionObjectBuiltIns.h"
569+ #undef INTL_ENTRY
570+
571+ #define PROJECTED_ENUM (ClassName, VALUES ) initSlotCapacity++;
572+ PROJECTED_ENUMS (PROJECTED_ENUM)
573+ #undef PROJECTED_ENUM
574+
575+ // add capacity for platform.winglob and platform.FallbackSymbol
576+ initSlotCapacity += 2 ;
577+
578+ typeHandler->Convert (intlNativeInterfaces, mode, initSlotCapacity);
537579
538580 ScriptContext* scriptContext = intlNativeInterfaces->GetScriptContext ();
539581 JavascriptLibrary* library = scriptContext->GetLibrary ();
540582
541583// gives each entrypoint a property ID on the intlNativeInterfaces library object
542- #ifdef INTL_ENTRY
543- #undef INTL_ENTRY
544- #endif
545- #define INTL_ENTRY (id, func ) \
546- library->AddFunctionToLibraryObject (intlNativeInterfaces, Js::PropertyIds::##id, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_##func, 1 );
584+ #define INTL_ENTRY (id, func ) library->AddFunctionToLibraryObject (intlNativeInterfaces, Js::PropertyIds::##id, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_##func, 1 );
547585#include " IntlExtensionObjectBuiltIns.h"
548586#undef INTL_ENTRY
549587
550588 library->AddMember (intlNativeInterfaces, PropertyIds::FallbackSymbol, library->CreateSymbol (BuiltInPropertyRecords::_intlFallbackSymbol));
551589
590+ DynamicObject * enumObj = nullptr ;
591+
592+ // Projects the exact layout of our C++ enums into Intl.js so that we dont have to remember to keep them in sync
593+ #define ENUM_VALUE (enumName, propId, value ) library->AddMember (enumObj, PropertyIds::##propId, JavascriptNumber::ToVar(value, scriptContext));
594+ #define PROJECTED_ENUM (ClassName, VALUES ) \
595+ enumObj = library->CreateObject (); \
596+ VALUES (ENUM_VALUE) \
597+ library->AddMember (intlNativeInterfaces, PropertyIds::##ClassName, enumObj); \
598+
599+ PROJECTED_ENUMS (PROJECTED_ENUM)
600+
601+ #undef PROJECTED_ENUM
602+ #undef ENUM_VALUE
603+
552604#if INTL_WINGLOB
553605 library->AddMember (intlNativeInterfaces, Js::PropertyIds::winglob, library->GetTrue ());
554606#else
@@ -1049,15 +1101,8 @@ DEFINE_ISXLOCALEAVAILABLE(DTF, udat)
10491101DEFINE_ISXLOCALEAVAILABLE(PR, uloc)
10501102
10511103#ifdef INTL_ICU
1052- enum class LocaleDataKind
1053- {
1054- Collation,
1055- CaseFirst,
1056- Numeric,
1057- Calendar,
1058- NumberingSystem,
1059- HourCycle
1060- };
1104+
1105+
10611106#endif
10621107
10631108 Var IntlEngineInterfaceExtensionObject::EntryIntl_GetLocaleData (RecyclableObject* function, CallInfo callInfo, ...)
@@ -1087,11 +1132,13 @@ DEFINE_ISXLOCALEAVAILABLE(PR, uloc)
10871132 if (kind == LocaleDataKind::Collation)
10881133 {
10891134 ScopedUEnumeration collations (ucol_getKeywordValuesForLocale (" collation" , localeID, false , &status));
1090- ICU_ASSERT (status, true );
1135+ int collationsCount = uenum_count (collations, &status);
1136+
1137+ // we expect collationsCount to have at least "standard" and "search" in it
1138+ ICU_ASSERT (status, collationsCount > 2 );
10911139
10921140 // the return array can't include "standard" and "search", but must have its first element be null (count - 2 + 1) [#sec-intl-collator-internal-slots]
1093- ret = library->CreateArray (uenum_count (collations, &status) - 1 );
1094- ICU_ASSERT (status, true );
1141+ ret = library->CreateArray (collationsCount - 1 );
10951142 ret->SetItem (0 , library->GetNull (), flag);
10961143
10971144 int collationLen = 0 ;
@@ -1111,7 +1158,7 @@ DEFINE_ISXLOCALEAVAILABLE(PR, uloc)
11111158 const size_t unicodeCollationLen = strlen (unicodeCollation);
11121159
11131160 // we only need strlen(unicodeCollation) + 1 char16s because unicodeCollation will always be ASCII (funnily enough)
1114- char16 *unicodeCollation16 = RecyclerNewArrayLeaf (scriptContext->GetRecycler (), char16, strlen (unicodeCollation) + 1 );
1161+ char16 *unicodeCollation16 = RecyclerNewArrayLeaf (scriptContext->GetRecycler (), char16, unicodeCollationLen + 1 );
11151162 charcount_t unicodeCollation16Len = 0 ;
11161163 HRESULT hr = utf8::NarrowStringToWideNoAlloc (
11171164 unicodeCollation,
@@ -1836,10 +1883,18 @@ DEFINE_ISXLOCALEAVAILABLE(PR, uloc)
18361883 bool ignorePunctuation = AssertBooleanProperty (state, PropertyIds::ignorePunctuation);
18371884 bool numeric = AssertBooleanProperty (state, PropertyIds::numeric);
18381885 CollatorCaseFirst caseFirst = AssertEnumProperty<CollatorCaseFirst>(state, PropertyIds::caseFirstEnum);
1886+ JavascriptString *usage = AssertStringProperty (state, PropertyIds::usage);
18391887
18401888 char localeID[ULOC_FULLNAME_CAPACITY] = { 0 };
18411889 LangtagToLocaleID (langtag, localeID);
18421890
1891+ const char16 searchString[] = _u (" search" );
1892+ if (usage->BufferEquals (searchString, _countof (searchString) - 1 )) // minus the null terminator
1893+ {
1894+ uloc_setKeywordValue (" collation" , " search" , localeID, _countof (localeID), &status);
1895+ ICU_ASSERT (status, true );
1896+ }
1897+
18431898 coll = FinalizableUCollator::New (scriptContext->GetRecycler (), ucol_open (localeID, &status));
18441899 ICU_ASSERT (status, true );
18451900
@@ -3114,45 +3169,34 @@ DEFINE_ISXLOCALEAVAILABLE(PR, uloc)
31143169 */
31153170 Var IntlEngineInterfaceExtensionObject::EntryIntl_RegisterBuiltInFunction (RecyclableObject* function, CallInfo callInfo, ...)
31163171 {
3117- // Don't put this in a header or add it to the namespace even in this file. Keep it to the minimum scope needed.
3118- enum class IntlBuiltInFunctionID : int32 {
3119- Min = 0 ,
3120- DateToLocaleString = Min,
3121- DateToLocaleDateString,
3122- DateToLocaleTimeString,
3123- NumberToLocaleString,
3124- StringLocaleCompare,
3125- Max
3126- };
3127-
31283172 EngineInterfaceObject_CommonFunctionProlog (function, callInfo);
31293173
31303174 // This function will only be used during the construction of the Intl object, hence Asserts are in place.
31313175 Assert (args.Info .Count >= 3 && JavascriptFunction::Is (args.Values [1 ]) && TaggedInt::Is (args.Values [2 ]));
31323176
31333177 JavascriptFunction *func = JavascriptFunction::FromVar (args.Values [1 ]);
31343178 int32 id = TaggedInt::ToInt32 (args.Values [2 ]);
3135- Assert (id >= (int32)IntlBuiltInFunctionID::Min && id < (int32)IntlBuiltInFunctionID ::Max);
3179+ Assert (id >= 0 && id < (int32)BuiltInFunctionID ::Max);
31363180
31373181 EngineInterfaceObject* nativeEngineInterfaceObj = scriptContext->GetLibrary ()->GetEngineInterfaceObject ();
31383182 IntlEngineInterfaceExtensionObject* extensionObject = static_cast <IntlEngineInterfaceExtensionObject*>(nativeEngineInterfaceObj->GetEngineExtension (EngineInterfaceExtensionKind_Intl));
31393183
3140- IntlBuiltInFunctionID functionID = static_cast <IntlBuiltInFunctionID >(id);
3184+ BuiltInFunctionID functionID = static_cast <BuiltInFunctionID >(id);
31413185 switch (functionID)
31423186 {
3143- case IntlBuiltInFunctionID ::DateToLocaleString:
3187+ case BuiltInFunctionID ::DateToLocaleString:
31443188 extensionObject->dateToLocaleString = func;
31453189 break ;
3146- case IntlBuiltInFunctionID ::DateToLocaleDateString:
3190+ case BuiltInFunctionID ::DateToLocaleDateString:
31473191 extensionObject->dateToLocaleDateString = func;
31483192 break ;
3149- case IntlBuiltInFunctionID ::DateToLocaleTimeString:
3193+ case BuiltInFunctionID ::DateToLocaleTimeString:
31503194 extensionObject->dateToLocaleTimeString = func;
31513195 break ;
3152- case IntlBuiltInFunctionID ::NumberToLocaleString:
3196+ case BuiltInFunctionID ::NumberToLocaleString:
31533197 extensionObject->numberToLocaleString = func;
31543198 break ;
3155- case IntlBuiltInFunctionID ::StringLocaleCompare:
3199+ case BuiltInFunctionID ::StringLocaleCompare:
31563200 extensionObject->stringLocaleCompare = func;
31573201 break ;
31583202 default :
0 commit comments