Bug 1328386 - Part 1: Remove boilerplate code and add comments in Intl code. r=Waldo
authorAndré Bargull <andre.bargull@gmail.com>
Mon, 23 Jan 2017 08:33:27 -0800
changeset 466644 862bc2b2553e0a65ac080e49d4279f4faba4bc76
parent 466643 a50557f92553b859df03352edff970a97159aa24
child 466645 36ad679ec7dbdb6923ff0db88d0e9b720296527c
push id42948
push userbmo:gasolin@mozilla.com
push dateThu, 26 Jan 2017 07:49:21 +0000
reviewersWaldo
bugs1328386
milestone54.0a1
Bug 1328386 - Part 1: Remove boilerplate code and add comments in Intl code. r=Waldo
js/src/builtin/Date.js
js/src/builtin/Intl.cpp
js/src/builtin/Intl.h
js/src/builtin/Intl.js
--- a/js/src/builtin/Date.js
+++ b/js/src/builtin/Date.js
@@ -99,17 +99,17 @@ function Date_toLocaleString() {
         // locales and options.
         dateTimeFormat = GetCachedFormat("dateTimeFormat", "any", "all");
     } else {
         options = ToDateTimeOptions(options, "any", "all");
         dateTimeFormat = intl_DateTimeFormat(locales, options);
     }
 
     // Step 7.
-    return intl_FormatDateTime(dateTimeFormat, x, false);
+    return intl_FormatDateTime(dateTimeFormat, x, /* formatToParts = */ false);
 }
 
 
 /**
  * Format this Date object into a date string, using the locale and formatting
  * options provided.
  *
  * Spec: ECMAScript Language Specification, 5.1 edition, 15.9.5.6.
@@ -132,17 +132,17 @@ function Date_toLocaleDateString() {
         // locales and options.
         dateTimeFormat = GetCachedFormat("dateFormat", "date", "date");
     } else {
         options = ToDateTimeOptions(options, "date", "date");
         dateTimeFormat = intl_DateTimeFormat(locales, options);
     }
 
     // Step 7.
-    return intl_FormatDateTime(dateTimeFormat, x, false);
+    return intl_FormatDateTime(dateTimeFormat, x, /* formatToParts = */ false);
 }
 
 
 /**
  * Format this Date object into a time string, using the locale and formatting
  * options provided.
  *
  * Spec: ECMAScript Language Specification, 5.1 edition, 15.9.5.7.
@@ -165,10 +165,10 @@ function Date_toLocaleTimeString() {
         // locales and options.
         dateTimeFormat = GetCachedFormat("timeFormat", "time", "time");
     } else {
         options = ToDateTimeOptions(options, "time", "time");
         dateTimeFormat = intl_DateTimeFormat(locales, options);
     }
 
     // Step 7.
-    return intl_FormatDateTime(dateTimeFormat, x, false);
+    return intl_FormatDateTime(dateTimeFormat, x, /* formatToParts = */ false);
 }
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -36,16 +36,17 @@
 #include "unicode/unum.h"
 #include "unicode/unumsys.h"
 #include "unicode/upluralrules.h"
 #include "unicode/ustring.h"
 #endif
 #include "vm/DateTime.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
+#include "vm/SelfHosting.h"
 #include "vm/Stack.h"
 #include "vm/StringBuffer.h"
 #include "vm/Unicode.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 
@@ -792,31 +793,25 @@ uplrules_select(const UPluralRules *uplr
 
 
 /******************** Common to Intl constructors ********************/
 
 static bool
 IntlInitialize(JSContext* cx, HandleObject obj, Handle<PropertyName*> initializer,
                HandleValue locales, HandleValue options)
 {
-    RootedValue initializerValue(cx);
-    if (!GlobalObject::getIntrinsicValue(cx, cx->global(), initializer, &initializerValue))
-        return false;
-    MOZ_ASSERT(initializerValue.isObject());
-    MOZ_ASSERT(initializerValue.toObject().is<JSFunction>());
-
     FixedInvokeArgs<3> args(cx);
 
     args[0].setObject(*obj);
     args[1].set(locales);
     args[2].set(options);
 
     RootedValue thisv(cx, NullValue());
     RootedValue ignored(cx);
-    return js::Call(cx, initializerValue, thisv, args, &ignored);
+    return js::CallSelfHostedFunction(cx, initializer, thisv, args, &ignored);
 }
 
 static bool
 CreateDefaultOptions(JSContext* cx, MutableHandleValue defaultOptions)
 {
     RootedObject options(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr));
     if (!options)
         return false;
@@ -866,31 +861,22 @@ intl_availableLocales(JSContext* cx, Cou
 }
 
 /**
  * Returns the object holding the internal properties for obj.
  */
 static JSObject*
 GetInternals(JSContext* cx, HandleObject obj)
 {
-    RootedValue getInternalsValue(cx);
-    if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().getInternals,
-                                         &getInternalsValue))
-    {
-        return nullptr;
-    }
-    MOZ_ASSERT(getInternalsValue.isObject());
-    MOZ_ASSERT(getInternalsValue.toObject().is<JSFunction>());
-
     FixedInvokeArgs<1> args(cx);
 
     args[0].setObject(*obj);
 
     RootedValue v(cx, NullValue());
-    if (!js::Call(cx, getInternalsValue, v, args, &v))
+    if (!js::CallSelfHostedFunction(cx, cx->names().getInternals, v, args, &v))
         return nullptr;
 
     return &v.toObject();
 }
 
 static bool
 equal(const char* s1, const char* s2)
 {
@@ -930,16 +916,17 @@ class ScopedICUObject
         ptr_ = nullptr;
         return tmp;
     }
 };
 
 // The inline capacity we use for the char16_t Vectors.
 static const size_t INITIAL_CHAR_BUFFER_SIZE = 32;
 
+
 /******************** Collator ********************/
 
 static void collator_finalize(FreeOp* fop, JSObject* obj);
 
 static const uint32_t UCOLLATOR_SLOT = 0;
 static const uint32_t COLLATOR_SLOTS_COUNT = 1;
 
 static const ClassOps CollatorClassOps = {
@@ -3571,16 +3558,17 @@ js::intl_FormatDateTime(JSContext* cx, u
     if (!isDateTimeFormatInstance)
         udat_close(df);
     if (!success)
         return false;
     args.rval().set(result);
     return true;
 }
 
+
 /**************** PluralRules *****************/
 
 static void pluralRules_finalize(FreeOp* fop, JSObject* obj);
 
 static const uint32_t UPLURAL_RULES_SLOT = 0;
 static const uint32_t PLURAL_RULES_SLOTS_COUNT = 1;
 
 static const ClassOps PluralRulesClassOps = {
@@ -3687,19 +3675,16 @@ CreatePluralRulesPrototype(JSContext* cx
     ctor = global->createConstructor(cx, &PluralRules, cx->names().PluralRules, 0);
     if (!ctor)
         return nullptr;
 
     RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global,
                                                                     &PluralRulesClass));
     if (!proto)
         return nullptr;
-    MOZ_ASSERT(proto->getReservedSlot(UPLURAL_RULES_SLOT).isUndefined(),
-               "improperly creating PluralRules more than once for a single "
-               "global?");
     proto->setReservedSlot(UPLURAL_RULES_SLOT, PrivateValue(nullptr));
 
     if (!LinkConstructorAndPrototype(cx, ctor, proto))
         return nullptr;
 
     if (!JS_DefineFunctions(cx, ctor, pluralRules_static_methods))
         return nullptr;
 
@@ -3955,16 +3940,19 @@ js::intl_GetPluralCategories(JSContext* 
         if (!DefineElement(cx, res, i++, element))
             return false;
     } while (true);
 
     args.rval().setObject(*res);
     return true;
 }
 
+
+/******************** Intl ********************/
+
 bool
 js::intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
 
     JSAutoByteString locale(cx, args[0].toString());
     if (!locale)
@@ -4388,18 +4376,16 @@ js::intl_ComputeDisplayNames(JSContext* 
             return false;
     }
 
     // 6. Return result.
     args.rval().setObject(*result);
     return true;
 }
 
-/******************** Intl ********************/
-
 const Class js::IntlClass = {
     js_Object_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_Intl)
 };
 
 #if JS_HAS_TOSOURCE
 static bool
 intl_toSource(JSContext* cx, unsigned argc, Value* vp)
--- a/js/src/builtin/Intl.h
+++ b/js/src/builtin/Intl.h
@@ -257,17 +257,17 @@ extern MOZ_MUST_USE bool
 intl_numberingSystem(JSContext* cx, unsigned argc, Value* vp);
 
 /**
  * Returns a string representing the number x according to the effective
  * locale and the formatting options of the given NumberFormat.
  *
  * Spec: ECMAScript Internationalization API Specification, 11.3.2.
  *
- * Usage: formatted = intl_FormatNumber(numberFormat, x)
+ * Usage: formatted = intl_FormatNumber(numberFormat, x, formatToParts)
  */
 extern MOZ_MUST_USE bool
 intl_FormatNumber(JSContext* cx, unsigned argc, Value* vp);
 
 
 /******************** DateTimeFormat ********************/
 
 /**
@@ -353,21 +353,22 @@ intl_patternForSkeleton(JSContext* cx, u
 
 /**
  * Returns a String value representing x (which must be a Number value)
  * according to the effective locale and the formatting options of the
  * given DateTimeFormat.
  *
  * Spec: ECMAScript Internationalization API Specification, 12.3.2.
  *
- * Usage: formatted = intl_FormatDateTime(dateTimeFormat, x)
+ * Usage: formatted = intl_FormatDateTime(dateTimeFormat, x, formatToParts)
  */
 extern MOZ_MUST_USE bool
 intl_FormatDateTime(JSContext* cx, unsigned argc, Value* vp);
 
+
 /******************** PluralRules ********************/
 
 /**
  * Returns an object indicating the supported locales for plural rules
  * by having a true-valued property for each such locale with the
  * canonicalized language tag as the property name. The object has no
  * prototype.
  *
@@ -396,16 +397,19 @@ intl_SelectPluralRule(JSContext* cx, uns
  *
  * Example:
  *
  * intl_getPluralCategories('pl', 'cardinal'); // ['one', 'few', 'many', 'other']
  */
 extern MOZ_MUST_USE bool
 intl_GetPluralCategories(JSContext* cx, unsigned argc, Value* vp);
 
+
+/******************** Intl ********************/
+
 /**
  * Returns a plain object with calendar information for a single valid locale
  * (callers must perform this validation).  The object will have these
  * properties:
  *
  *   firstDayOfWeek
  *     an integer in the range 1=Sunday to 7=Saturday indicating the day
  *     considered the first day of the week in calendars, e.g. 1 for en-US,
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -1355,31 +1355,31 @@ function getIntlObjectInternals(obj, cla
  * C++ code that knows what it's doing!
  */
 function getInternals(obj)
 {
     assert(isInitializedIntlObject(obj), "for use only on guaranteed Intl objects");
 
     var internals = callFunction(std_WeakMap_get, internalsMap, obj);
 
-    assert(internals.type !== "partial", "must have been successfully initialized");
-    var lazyData = internals.lazyData;
-    if (!lazyData)
-        return internals.internalProps;
-
-    var internalProps;
+    // If internal properties have already been computed, use them.
+    var internalProps = maybeInternalProperties(internals);
+    if (internalProps)
+        return internalProps;
+
+    // Otherwise it's time to fully create them.
     var type = internals.type;
     if (type === "Collator")
-        internalProps = resolveCollatorInternals(lazyData)
+        internalProps = resolveCollatorInternals(internals.lazyData)
     else if (type === "DateTimeFormat")
-        internalProps = resolveDateTimeFormatInternals(lazyData)
-    else if (type === "PluralRules")
-        internalProps = resolvePluralRulesInternals(lazyData)
+        internalProps = resolveDateTimeFormatInternals(internals.lazyData)
+    else if (type === "NumberFormat")
+        internalProps = resolveNumberFormatInternals(internals.lazyData);
     else
-        internalProps = resolveNumberFormatInternals(lazyData);
+        internalProps = resolvePluralRulesInternals(internals.lazyData)
     setInternalProperties(internals, internalProps);
     return internalProps;
 }
 
 
 /********** Intl.Collator **********/
 
 
@@ -2792,17 +2792,17 @@ function dateTimeFormatLocaleData(locale
  * Spec: ECMAScript Internationalization API Specification, 12.3.2.
  */
 function dateTimeFormatFormatToBind() {
     // Steps 1.a.i-ii
     var date = arguments.length > 0 ? arguments[0] : undefined;
     var x = (date === undefined) ? std_Date_now() : ToNumber(date);
 
     // Step 1.a.iii.
-    return intl_FormatDateTime(this, x, false);
+    return intl_FormatDateTime(this, x, /* formatToParts = */ false);
 }
 
 /**
  * Returns a function bound to this DateTimeFormat that returns a String value
  * representing the result of calling ToNumber(date) according to the
  * effective locale and the formatting options of this DateTimeFormat.
  *
  * Spec: ECMAScript Internationalization API Specification, 12.3.2.
@@ -2831,17 +2831,17 @@ function Intl_DateTimeFormat_formatToPar
     // Check "this DateTimeFormat object" per introduction of section 12.3.
     getDateTimeFormatInternals(this, "formatToParts");
 
     // Steps 1.a.i-ii
     var date = arguments.length > 0 ? arguments[0] : undefined;
     var x = (date === undefined) ? std_Date_now() : ToNumber(date);
 
     // Step 1.a.iii.
-    return intl_FormatDateTime(this, x, true);
+    return intl_FormatDateTime(this, x, /* formatToParts = */ true);
 }
 
 
 /**
  * Returns the resolved options for a DateTimeFormat object.
  *
  * Spec: ECMAScript Internationalization API Specification, 12.3.3 and 12.4.
  */
@@ -2955,18 +2955,20 @@ function resolveICUPattern(pattern, resu
             if (c === "h" || c === "K")
                 _DefineDataProperty(result, "hour12", true);
             else if (c === "H" || c === "k")
                 _DefineDataProperty(result, "hour12", false);
         }
     }
 }
 
+
 /********** Intl.PluralRules **********/
 
+
 /**
  * PluralRules internal properties.
  *
  * Spec: ECMAScript 402 API, PluralRules, 1.3.3.
  */
 var pluralRulesInternalProperties = {
     _availableLocales: null,
     availableLocales: function()
@@ -3176,55 +3178,98 @@ function Intl_PluralRules_resolvedOption
         var p = optionalProperties[i];
         if (callFunction(std_Object_hasOwnProperty, internals, p))
             _DefineDataProperty(result, p, internals[p]);
     }
     return result;
 }
 
 
+/********** Intl **********/
+
+
+/**
+ * 8.2.1 Intl.getCanonicalLocales ( locales )
+ *
+ * ES2017 Intl draft rev 947aa9a0c853422824a0c9510d8f09be3eb416b9
+ */
 function Intl_getCanonicalLocales(locales) {
-  let codes = CanonicalizeLocaleList(locales);
-  let result = [];
-
-  let len = codes.length;
-  let k = 0;
-
-  while (k < len) {
-    _DefineDataProperty(result, k, codes[k]);
-    k++;
-  }
-  return result;
-}
-
-function Intl_getCalendarInfo(locales) {
-  const requestedLocales = CanonicalizeLocaleList(locales);
-
-  const DateTimeFormat = dateTimeFormatInternalProperties;
-  const localeData = DateTimeFormat.localeData;
-
-  const localeOpt = new Record();
-  localeOpt.localeMatcher = "best fit";
-
-  const r = ResolveLocale(callFunction(DateTimeFormat.availableLocales, DateTimeFormat),
-                          requestedLocales,
-                          localeOpt,
-                          DateTimeFormat.relevantExtensionKeys,
-                          localeData);
-
-  const result = intl_GetCalendarInfo(r.locale);
-  result.calendar = r.ca;
-  result.locale = r.locale;
-
-  return result;
+    // Step 1.
+    var localeList = CanonicalizeLocaleList(locales);
+
+    // Step 2 (Inlined CreateArrayFromList).
+    var array = [];
+
+    for (var n = 0, len = localeList.length; n < len; n++)
+        _DefineDataProperty(array, n, localeList[n]);
+
+    return array;
 }
 
 /**
- * This function is a custom method designed after Intl API, but currently
- * not part of the spec or spec proposal.
+ * This function is a custom function in the style of the standard Intl.*
+ * functions, that isn't part of any spec or proposal yet.
+ *
+ * Returns an object with the following properties:
+ *   locale:
+ *     The actual resolved locale.
+ *
+ *   calendar:
+ *     The default calendar of the resolved locale.
+ *
+ *   firstDayOfWeek:
+ *     The first day of the week for the resolved locale.
+ *
+ *   minDays:
+ *     The minimum number of days in a week for the resolved locale.
+ *
+ *   weekendStart:
+ *     The day considered the beginning of a weekend for the resolved locale.
+ *
+ *   weekendEnd:
+ *     The day considered the end of a weekend for the resolved locale.
+ *
+ * Days are encoded as integers in the range 1=Sunday to 7=Saturday.
+ */
+function Intl_getCalendarInfo(locales) {
+    // 1. Let requestLocales be ? CanonicalizeLocaleList(locales).
+    const requestedLocales = CanonicalizeLocaleList(locales);
+
+    const DateTimeFormat = dateTimeFormatInternalProperties;
+
+    // 2. Let localeData be %DateTimeFormat%.[[localeData]].
+    const localeData = DateTimeFormat.localeData;
+
+    // 3. Let localeOpt be a new Record.
+    const localeOpt = new Record();
+
+    // 4. Set localeOpt.[[localeMatcher]] to "best fit".
+    localeOpt.localeMatcher = "best fit";
+
+    // 5. Let r be ResolveLocale(%DateTimeFormat%.[[availableLocales]],
+    //    requestedLocales, localeOpt,
+    //    %DateTimeFormat%.[[relevantExtensionKeys]], localeData).
+    const r = ResolveLocale(callFunction(DateTimeFormat.availableLocales, DateTimeFormat),
+                            requestedLocales,
+                            localeOpt,
+                            DateTimeFormat.relevantExtensionKeys,
+                            localeData);
+
+    // 6. Let result be GetCalendarInfo(r.[[locale]]).
+    const result = intl_GetCalendarInfo(r.locale);
+    _DefineDataProperty(result, "calendar", r.ca);
+    _DefineDataProperty(result, "locale", r.locale);
+
+    // 7. Return result.
+    return result;
+}
+
+/**
+ * This function is a custom function in the style of the standard Intl.*
+ * functions, that isn't part of any spec or proposal yet.
  * We want to use it internally to retrieve translated values from CLDR in
  * order to ensure they're aligned with what Intl API returns.
  *
  * This API may one day be a foundation for an ECMA402 API spec proposal.
  *
  * The function takes two arguments - locales which is a list of locale strings
  * and options which is an object with two optional properties:
  *
@@ -3260,31 +3305,33 @@ function Intl_getDisplayNames(locales, o
         // a. Let options be ? ToObject(options).
         options = ToObject(options);
 
     const DateTimeFormat = dateTimeFormatInternalProperties;
 
     // 4. Let localeData be %DateTimeFormat%.[[localeData]].
     const localeData = DateTimeFormat.localeData;
 
-    // 5. Let opt be a new Record.
+    // 5. Let localeOpt be a new Record.
     const localeOpt = new Record();
+
     // 6. Set localeOpt.[[localeMatcher]] to "best fit".
     localeOpt.localeMatcher = "best fit";
 
     // 7. Let r be ResolveLocale(%DateTimeFormat%.[[availableLocales]], requestedLocales, localeOpt,
     //    %DateTimeFormat%.[[relevantExtensionKeys]], localeData).
     const r = ResolveLocale(callFunction(DateTimeFormat.availableLocales, DateTimeFormat),
                           requestedLocales,
                           localeOpt,
                           DateTimeFormat.relevantExtensionKeys,
                           localeData);
 
     // 8. Let style be ? GetOption(options, "style", "string", « "long", "short", "narrow" », "long").
     const style = GetOption(options, "style", "string", ["long", "short", "narrow"], "long");
+
     // 9. Let keys be ? Get(options, "keys").
     let keys = options.keys;
 
     // 10. If keys is undefined,
     if (keys === undefined) {
         // a. Let keys be ArrayCreate(0).
         keys = [];
     } else if (!IsObject(keys)) {
@@ -3293,18 +3340,20 @@ function Intl_getDisplayNames(locales, o
         ThrowTypeError(JSMSG_INVALID_KEYS_TYPE);
     }
 
     // 12. Let processedKeys be ArrayCreate(0).
     // (This really should be a List, but we use an Array here in order that
     // |intl_ComputeDisplayNames| may infallibly access the list's length via
     // |ArrayObject::length|.)
     let processedKeys = [];
+
     // 13. Let len be ? ToLength(? Get(keys, "length")).
     let len = ToLength(keys.length);
+
     // 14. Let i be 0.
     // 15. Repeat, while i < len
     for (let i = 0; i < len; i++) {
         // a. Let processedKey be ? ToString(? Get(keys, i)).
         // b. Perform ? CreateDataPropertyOrThrow(processedKeys, i, processedKey).
         callFunction(std_Array_push, processedKeys, ToString(keys[i]));
     }