Skip to content

Commit b987205

Browse files
committed
Project C++ enums to Intl.js rather than hardcoding them and keeping them in sync
1 parent f54b1f2 commit b987205

File tree

3 files changed

+100
-116
lines changed

3 files changed

+100
-116
lines changed

lib/Runtime/Base/JnDirectFields.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,10 +519,30 @@ ENTRY(tagPublicLibraryCode)
519519
ENTRY(winglob)
520520
ENTRY(platform)
521521
ENTRY(formatToParts)
522-
ENTRY(FallbackSymbol)
522+
ENTRY(co)
523+
ENTRY(kn)
524+
ENTRY(kf)
525+
ENTRY(nu)
526+
ENTRY(ca)
527+
ENTRY(hc)
528+
ENTRY(upper)
529+
ENTRY(lower)
530+
ENTRY(base)
531+
ENTRY(accent)
532+
ENTRY2(case_, _u("case"))
533+
ENTRY(variant)
534+
ENTRY(code)
535+
ENTRY(decimal)
536+
ENTRY(percent)
537+
ENTRY(NumberFormatStyle)
538+
ENTRY(NumberFormatCurrencyDisplay)
539+
ENTRY(CollatorSensitivity)
540+
ENTRY(CollatorCaseFirst)
541+
ENTRY(LocaleDataKind)
523542

524543
// This symbol is not part of the regular Symbol API and is only used in rare circumstances in Intl.js for backwards compatibility
525544
// with the Intl v1 spec. It is visible to the user only using Object.getOwnPropertySymbols(Intl.NumberFormat.call(new Intl.NumberFormat())).
545+
ENTRY(FallbackSymbol)
526546
ENTRY_SYMBOL(_intlFallbackSymbol, _u("Intl.FallbackSymbol"))
527547

528548
ENTRY(NumberFormat)

lib/Runtime/Library/InJavascript/Intl.js

Lines changed: 6 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -165,41 +165,6 @@
165165
assert(test, err) { return test ? undefined : platform.raiseAssert(err || new Error("Assert failed")); }
166166
};
167167

168-
// Keep these "enums" in sync with lib/Runtime/PlatformAgnostic/Intl.h
169-
const CollatorSensitivity = bare({
170-
base: 0,
171-
accent: 1,
172-
case: 2,
173-
variant: 3,
174-
DEFAULT: 3
175-
});
176-
const CollatorCaseFirst = bare({
177-
upper: 0,
178-
lower: 1,
179-
false: 2,
180-
DEFAULT: 2
181-
});
182-
const NumberFormatStyle = bare({
183-
DEFAULT: 0, // "decimal" is the default
184-
DECIMAL: 0, // Intl.NumberFormat(locale, { style: "decimal" }); // aka in our code as "number"
185-
PERCENT: 1, // Intl.NumberFormat(locale, { style: "percent" });
186-
CURRENCY: 2, // Intl.NumberFormat(locale, { style: "currency", ... });
187-
});
188-
const NumberFormatCurrencyDisplay = bare({
189-
DEFAULT: 0, // "symbol" is the default
190-
SYMBOL: 0, // Intl.NumberFormat(locale, { style: "currency", currencyDisplay: "symbol" }); // e.g. "$" or "US$" depeding on locale
191-
CODE: 1, // Intl.NumberFormat(locale, { style: "currency", currencyDisplay: "code" }); // e.g. "USD"
192-
NAME: 2, // Intl.NumberFormat(locale, { style: "currency", currencyDisplay: "name" }); // e.g. "US dollar"
193-
});
194-
195-
const toEnum = function (enumObject, key) {
196-
if (!key || typeof key !== "string") {
197-
return enumObject.DEFAULT;
198-
} else {
199-
return enumObject[key];
200-
}
201-
}
202-
203168
// When this file was originally written, it assumed Windows Globalization semantics.
204169
// Throughout the transition to ICU, we tried to share as much code as possible between WinGlob and ICU.
205170
// However, because ICU has different semantics and our ICU-based implementation tries to match a newer
@@ -294,33 +259,6 @@
294259
}
295260
};
296261

297-
/**
298-
* Returns an array of acceptable values for a given key in a given locale. It is expected that
299-
* locale is one that has already been validated by platform.is*LocaleAvailable and key is limited
300-
* to the [[RelevantExtensionKeys]] of Collator, NumberFormat, and DateTimeFormat.
301-
*
302-
* ECMA402: #sec-internal-slots ([[SortLocaleData]], [[SearchLocaleData]], and [[LocaleData]])
303-
*
304-
* @param {String} key a unicode extension key like "co", "ca", etc
305-
* @param {String} locale the locale for which to get the given key's data
306-
* @returns {String[]}
307-
*/
308-
const getKeyLocaleData = function (key, locale) {
309-
// NOTE: keep this enum in sync with `enum class LocaleDataKind` in IntlEngineInterfaceExtensionObject.cpp
310-
const LocaleDataKind = {
311-
co: 0,
312-
kf: 1,
313-
kn: 2,
314-
ca: 3,
315-
nu: 4,
316-
hc: 5,
317-
};
318-
319-
const keyLocaleData = platform.getLocaleData(LocaleDataKind[key], locale);
320-
321-
return keyLocaleData;
322-
};
323-
324262
/**
325263
* Determines which locale (or fallback) to use of an array of locales.
326264
*
@@ -424,7 +362,7 @@
424362
const result = bare({ dataLocale: foundLocale });
425363
let supportedExtension = "-u";
426364
_.forEach(relevantExtensionKeys, function (key) {
427-
const keyLocaleData = getKeyLocaleData(key, foundLocale);
365+
const keyLocaleData = platform.getLocaleData(platform.LocaleDataKind[key], foundLocale);
428366
let value = keyLocaleData[0];
429367
let supportedExtensionAddition = "";
430368
if (r.extension) {
@@ -956,10 +894,10 @@
956894
collator.collation = r.co === null ? "default" : r.co;
957895
collator.numeric = r.kn === "true";
958896
collator.caseFirst = r.kf;
959-
collator.caseFirstEnum = toEnum(CollatorCaseFirst, collator.caseFirst);
897+
collator.caseFirstEnum = platform.CollatorCaseFirst[collator.caseFirst];
960898

961899
collator.sensitivity = GetOption(options, "sensitivity", "string", ["base", "accent", "case", "variant"], "variant");
962-
collator.sensitivityEnum = toEnum(CollatorSensitivity, collator.sensitivity);
900+
collator.sensitivityEnum = platform.CollatorSensitivity[collator.sensitivity];
963901

964902
collator.ignorePunctuation = GetOption(options, "ignorePunctuation", "boolean", undefined, false);
965903

@@ -1132,7 +1070,7 @@
11321070

11331071
const style = GetOption(options, "style", "string", ["decimal", "percent", "currency"], "decimal");
11341072
nf.style = style;
1135-
nf.formatterToUse = toEnum(NumberFormatStyle, _.toUpperCase(style));
1073+
nf.formatterToUse = platform.NumberFormatStyle[style];
11361074
const useCurrency = style === "currency";
11371075

11381076
let currency = GetOption(options, "currency", "string", undefined, undefined);
@@ -1151,8 +1089,8 @@
11511089

11521090
let currencyDisplay = GetOption(options, "currencyDisplay", "string", ["code", "symbol", "name"], "symbol");
11531091
if (useCurrency) {
1154-
nf.currencyDisplay = currencyDisplay
1155-
nf.currencyDisplayToUse = toEnum(NumberFormatCurrencyDisplay, _.toUpperCase(currencyDisplay));
1092+
nf.currencyDisplay = currencyDisplay;
1093+
nf.currencyDisplayToUse = platform.NumberFormatCurrencyDisplay[currencyDisplay];
11561094
}
11571095

11581096
let mnfdDefault, mxfdDefault;

lib/Runtime/Library/IntlEngineInterfaceExtensionObject.cpp

Lines changed: 73 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -48,48 +48,67 @@ 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+
#define ENUM_VALUE(enumName, propId, value) enumName = value,
94+
#define PROJECTED_ENUM(ClassName, VALUES) \
95+
enum class ClassName \
96+
{ \
97+
VALUES(ENUM_VALUE) \
98+
Max \
7199
};
72100

73-
enum class CollatorSensitivity
74-
{
75-
Base,
76-
Accent,
77-
Case,
78-
Variant,
101+
#define PROJECTED_ENUMS(PROJECT) \
102+
PROJECT(LocaleDataKind, LOCALEDATAKIND_VALUES) \
103+
PROJECT(CollatorCaseFirst, COLLATORCASEFIRST_VALUES) \
104+
PROJECT(CollatorSensitivity, COLLATORSENSITIVITY_VALUES) \
105+
PROJECT(NumberFormatCurrencyDisplay, NUMBERFORMATCURRENCYDISPLAY_VALUES) \
106+
PROJECT(NumberFormatStyle, NUMBERFORMATSTYLE_VALUES)
79107

80-
Max,
81-
Default = Variant,
82-
};
108+
PROJECTED_ENUMS(PROJECTED_ENUM)
83109

84-
enum class CollatorCaseFirst
85-
{
86-
Upper,
87-
Lower,
88-
False,
89-
90-
Max,
91-
Default = False,
92-
};
110+
#undef PROJECTED_ENUM
111+
#undef ENUM_VALUE
93112

94113
#pragma warning(push)
95114
#pragma warning(disable:4309) // truncation of constant value
@@ -549,6 +568,20 @@ namespace Js
549568

550569
library->AddMember(intlNativeInterfaces, PropertyIds::FallbackSymbol, library->CreateSymbol(BuiltInPropertyRecords::_intlFallbackSymbol));
551570

571+
DynamicObject * enumObj = nullptr;
572+
573+
// Projects the exact layout of our C++ enums into Intl.js so that we dont have to remember to keep them in sync
574+
#define ENUM_VALUE(enumName, unicodeKey, value) library->AddMember(enumObj, PropertyIds::##unicodeKey, JavascriptNumber::ToVar(value, scriptContext));
575+
#define PROJECTED_ENUM(ClassName, VALUES) \
576+
enumObj = library->CreateObject(); \
577+
VALUES(ENUM_VALUE) \
578+
library->AddMember(intlNativeInterfaces, PropertyIds::##ClassName, enumObj); \
579+
580+
PROJECTED_ENUMS(PROJECTED_ENUM)
581+
582+
#undef PROJECTED_ENUM
583+
#undef ENUM_VALUE
584+
552585
#if INTL_WINGLOB
553586
library->AddMember(intlNativeInterfaces, Js::PropertyIds::winglob, library->GetTrue());
554587
#else
@@ -1049,15 +1082,8 @@ DEFINE_ISXLOCALEAVAILABLE(DTF, udat)
10491082
DEFINE_ISXLOCALEAVAILABLE(PR, uloc)
10501083

10511084
#ifdef INTL_ICU
1052-
enum class LocaleDataKind
1053-
{
1054-
Collation,
1055-
CaseFirst,
1056-
Numeric,
1057-
Calendar,
1058-
NumberingSystem,
1059-
HourCycle
1060-
};
1085+
1086+
10611087
#endif
10621088

10631089
Var IntlEngineInterfaceExtensionObject::EntryIntl_GetLocaleData(RecyclableObject* function, CallInfo callInfo, ...)

0 commit comments

Comments
 (0)