Bug 1403318 - Expose Intl.PluralRules. r?anba draft
authorZibi Braniecki <zbraniecki@mozilla.com>
Wed, 27 Sep 2017 19:16:10 +0200
changeset 673427 08d60a631041d297bcbb3729529251f2ef787eb1
parent 673329 97efdde466f18cf580fda9673cf4c38ee21fc7b7
child 673428 2ee3ef2f2df17f28fa13c97302dada8182663617
push id82575
push userbmo:gandalf@aviary.pl
push dateMon, 02 Oct 2017 15:47:25 +0000
reviewersanba
bugs1403318
milestone58.0a1
Bug 1403318 - Expose Intl.PluralRules. r?anba MozReview-Commit-ID: qxU4dWBO94
js/src/builtin/Intl.cpp
js/src/jit-test/tests/auto-regress/bug1334573.js
js/src/jsfriendapi.h
js/src/shell/js.cpp
js/src/tests/Intl/PluralRules/call.js
js/src/tests/Intl/PluralRules/construct-newtarget.js
js/src/tests/Intl/PluralRules/negativeZeroFractionDigits.js
js/src/tests/Intl/PluralRules/pluralrules.js
js/src/tests/Intl/PluralRules/resolvedOptions-overridden-species.js
js/src/tests/Intl/PluralRules/select.js
js/src/tests/Intl/PluralRules/supportedLocalesOf.js
js/src/tests/jstests.list
js/src/tests/test262-intl-extras.js
js/src/tests/test262-update.py
js/src/tests/test262/intl402/PluralRules/shell.js
js/src/vm/CommonPropertyNames.h
js/src/vm/GlobalObject.h
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -3576,46 +3576,16 @@ CreatePluralRulesPrototype(JSContext* cx
 
     RootedValue ctorValue(cx, ObjectValue(*ctor));
     if (!DefineDataProperty(cx, Intl, cx->names().PluralRules, ctorValue, 0))
         return nullptr;
 
     return proto;
 }
 
-/* static */ bool
-js::GlobalObject::addPluralRulesConstructor(JSContext* cx, HandleObject intl)
-{
-    Handle<GlobalObject*> global = cx->global();
-
-    {
-        const HeapSlot& slot = global->getReservedSlotRef(PLURAL_RULES_PROTO);
-        if (!slot.isUndefined()) {
-            MOZ_ASSERT(slot.isObject());
-            JS_ReportErrorASCII(cx,
-                                "the PluralRules constructor can't be added "
-                                "multiple times in the same global");
-            return false;
-        }
-    }
-
-    JSObject* pluralRulesProto = CreatePluralRulesPrototype(cx, intl, global);
-    if (!pluralRulesProto)
-        return false;
-
-    global->setReservedSlot(PLURAL_RULES_PROTO, ObjectValue(*pluralRulesProto));
-    return true;
-}
-
-bool
-js::AddPluralRulesConstructor(JSContext* cx, JS::Handle<JSObject*> intl)
-{
-    return GlobalObject::addPluralRulesConstructor(cx, intl);
-}
-
 bool
 js::intl_PluralRules_availableLocales(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 0);
 
     RootedValue result(cx);
     // We're going to use ULocale availableLocales as per ICU recommendation:
@@ -4377,16 +4347,19 @@ GlobalObject::initIntlObject(JSContext* 
     RootedObject dateTimeFormatProto(cx), dateTimeFormat(cx);
     dateTimeFormatProto = CreateDateTimeFormatPrototype(cx, intl, global, &dateTimeFormat, DateTimeFormatOptions::Standard);
     if (!dateTimeFormatProto)
         return false;
     RootedObject numberFormatProto(cx), numberFormat(cx);
     numberFormatProto = CreateNumberFormatPrototype(cx, intl, global, &numberFormat);
     if (!numberFormatProto)
         return false;
+    RootedObject pluralRulesProto(cx, CreatePluralRulesPrototype(cx, intl, global));
+    if (!pluralRulesProto)
+        return false;
 
     // The |Intl| object is fully set up now, so define the global property.
     RootedValue intlValue(cx, ObjectValue(*intl));
     if (!DefineDataProperty(cx, global, cx->names().Intl, intlValue, JSPROP_RESOLVING))
         return false;
 
     // Now that the |Intl| object is successfully added, we can OOM-safely fill
     // in all relevant reserved global slots.
@@ -4397,16 +4370,17 @@ GlobalObject::initIntlObject(JSContext* 
     // |String.prototype| we have |JSProto_*| that enables
     // |getPrototype(JSProto_*)|, but that has global-object-property-related
     // baggage we don't need or want, so we use one-off reserved slots.
     global->setReservedSlot(COLLATOR_PROTO, ObjectValue(*collatorProto));
     global->setReservedSlot(DATE_TIME_FORMAT, ObjectValue(*dateTimeFormat));
     global->setReservedSlot(DATE_TIME_FORMAT_PROTO, ObjectValue(*dateTimeFormatProto));
     global->setReservedSlot(NUMBER_FORMAT, ObjectValue(*numberFormat));
     global->setReservedSlot(NUMBER_FORMAT_PROTO, ObjectValue(*numberFormatProto));
+    global->setReservedSlot(PLURAL_RULES_PROTO, ObjectValue(*pluralRulesProto));
 
     // Also cache |Intl| to implement spec language that conditions behavior
     // based on values being equal to "the standard built-in |Intl| object".
     // Use |setConstructor| to correspond with |JSProto_Intl|.
     //
     // XXX We should possibly do a one-off reserved slot like above.
     global->setConstructor(JSProto_Intl, ObjectValue(*intl));
     return true;
deleted file mode 100644
--- a/js/src/jit-test/tests/auto-regress/bug1334573.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// |jit-test| error:Error
-
-if (this.Intl) {
-    addIntlExtras(Intl);
-    addIntlExtras(Intl);
-} else {
-    throw new Error();
-}
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -3057,22 +3057,16 @@ ToWindowProxyIfWindow(JSObject* obj)
 /**
  * If `obj` is a WindowProxy, get its associated Window (the compartment's
  * global), else return `obj`. This function is infallible and never returns
  * nullptr.
  */
 extern JS_FRIEND_API(JSObject*)
 ToWindowIfWindowProxy(JSObject* obj);
 
-// Create and add the Intl.PluralRules constructor function to the provided
-// object.  This function throws if called more than once per realm/global
-// object.
-extern bool
-AddPluralRulesConstructor(JSContext* cx, JS::Handle<JSObject*> intl);
-
 // Create and add the Intl.MozDateTimeFormat constructor function to the provided
 // object.
 //
 // This custom date/time formatter constructor gives users the ability
 // to specify a custom format pattern. This pattern is passed *directly*
 // to ICU with NO SYNTAX PARSING OR VALIDATION WHATSOEVER. ICU appears to
 // have a a modicum of testing of this, and it won't fall over completely
 // if passed bad input. But the current behavior is entirely under-specified
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -784,19 +784,16 @@ AddIntlExtras(JSContext* cx, unsigned ar
         JS_SELF_HOSTED_FN("getLocaleInfo", "Intl_getLocaleInfo", 1, 0),
         JS_SELF_HOSTED_FN("getDisplayNames", "Intl_getDisplayNames", 2, 0),
         JS_FS_END
     };
 
     if (!JS_DefineFunctions(cx, intl, funcs))
         return false;
 
-    if (!js::AddPluralRulesConstructor(cx, intl))
-        return false;
-
     if (!js::AddMozDateTimeFormatConstructor(cx, intl))
         return false;
 
     args.rval().setUndefined();
     return true;
 }
 #endif // ENABLE_INTL_API
 
@@ -6685,18 +6682,17 @@ static const JSFunctionSpecWithHelp shel
 
 #ifdef ENABLE_INTL_API
     JS_FN_HELP("addIntlExtras", AddIntlExtras, 1, 0,
 "addIntlExtras(obj)",
 "Adds various not-yet-standardized Intl functions as properties on the\n"
 "provided object (this should generally be Intl itself).  The added\n"
 "functions and their behavior are experimental: don't depend upon them\n"
 "unless you're willing to update your code if these experimental APIs change\n"
-"underneath you.  Calling this function more than once in a realm/global\n"
-"will throw."),
+"underneath you."),
 #endif // ENABLE_INTL_API
 
     JS_FS_HELP_END
 };
 
 static const JSFunctionSpecWithHelp fuzzing_unsafe_functions[] = {
     JS_FN_HELP("getSelfHostedValue", GetSelfHostedValue, 1, 0,
 "getSelfHostedValue()",
--- a/js/src/tests/Intl/PluralRules/call.js
+++ b/js/src/tests/Intl/PluralRules/call.js
@@ -1,11 +1,9 @@
-// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.hasOwnProperty("addIntlExtras"))
-
-addIntlExtras(Intl);
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
 
 function IsConstructor(o) {
   try {
     new (new Proxy(o, {construct: () => ({})}));
     return true;
   } catch (e) {
     return false;
   }
--- a/js/src/tests/Intl/PluralRules/construct-newtarget.js
+++ b/js/src/tests/Intl/PluralRules/construct-newtarget.js
@@ -1,11 +1,9 @@
-// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.hasOwnProperty("addIntlExtras"))
-
-addIntlExtras(Intl);
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
 
 // Test subclassing %Intl.PluralRules% works correctly.
 class MyPluralRules extends Intl.PluralRules {}
 
 var obj = new MyPluralRules();
 assertEq(obj instanceof MyPluralRules, true);
 assertEq(obj instanceof Intl.PluralRules, true);
 assertEq(Object.getPrototypeOf(obj), MyPluralRules.prototype);
--- a/js/src/tests/Intl/PluralRules/negativeZeroFractionDigits.js
+++ b/js/src/tests/Intl/PluralRules/negativeZeroFractionDigits.js
@@ -1,11 +1,9 @@
-// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.hasOwnProperty('addIntlExtras'))
-
-addIntlExtras(Intl);
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
 
 const optionsList = [
     {minimumFractionDigits: -0, maximumFractionDigits: -0},
     {minimumFractionDigits: -0, maximumFractionDigits: +0},
     {minimumFractionDigits: +0, maximumFractionDigits: -0},
     {minimumFractionDigits: +0, maximumFractionDigits: +0},
 ];
 
--- a/js/src/tests/Intl/PluralRules/pluralrules.js
+++ b/js/src/tests/Intl/PluralRules/pluralrules.js
@@ -1,16 +1,14 @@
-// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.hasOwnProperty('addIntlExtras'))
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
 
 // Tests the format function with a diverse set of locales and options.
 
 var pr;
 
-addIntlExtras(Intl);
-
 pr = new Intl.PluralRules("en-us");
 assertEq(pr.resolvedOptions().locale, "en-US");
 assertEq(pr.resolvedOptions().type, "cardinal");
 assertEq(pr.resolvedOptions().pluralCategories.length, 2);
 
 pr = new Intl.PluralRules("de", {type: 'cardinal'});
 assertEq(pr.resolvedOptions().pluralCategories.length, 2);
 
--- a/js/src/tests/Intl/PluralRules/resolvedOptions-overridden-species.js
+++ b/js/src/tests/Intl/PluralRules/resolvedOptions-overridden-species.js
@@ -1,14 +1,12 @@
-// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.hasOwnProperty("addIntlExtras"))
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
 
 // Tests the PluralRules.resolvedOptions function for overriden Array[Symbol.species].
 
-addIntlExtras(Intl);
-
 var pl = new Intl.PluralRules("de");
 
 Object.defineProperty(Array, Symbol.species, {
     value: function() {
         return new Proxy(["?"], {
             get(t, pk, r) {
                 return Reflect.get(t, pk, r);
             },
--- a/js/src/tests/Intl/PluralRules/select.js
+++ b/js/src/tests/Intl/PluralRules/select.js
@@ -1,16 +1,14 @@
-// |reftest| skip-if(!this.hasOwnProperty('Intl')||!this.hasOwnProperty('addIntlExtras'))
+// |reftest| skip-if(!this.hasOwnProperty('Intl'))
 
 // Tests the format function with a diverse set of locales and options.
 
 var pr;
 
-addIntlExtras(Intl);
-
 pr = new Intl.PluralRules("en-us");
 assertEq(pr.select(0), "other");
 assertEq(pr.select(0.5), "other");
 assertEq(pr.select(1.2), "other");
 assertEq(pr.select(1.5), "other");
 assertEq(pr.select(1.7), "other");
 assertEq(pr.select(-1), "one");
 assertEq(pr.select(1), "one");
--- a/js/src/tests/Intl/PluralRules/supportedLocalesOf.js
+++ b/js/src/tests/Intl/PluralRules/supportedLocalesOf.js
@@ -1,9 +1,9 @@
-// |reftest| skip-if(!this.hasOwnProperty('Intl')||!this.hasOwnProperty('addIntlExtras')||xulRuntime.shell)
+// |reftest| skip-if(!this.hasOwnProperty('Intl')||xulRuntime.shell)
 // -- test in browser only that ICU has locale data for all Mozilla languages
 
 // This array contains the locales that ICU supports in
 // number formatting whose languages Mozilla localizes Firefox into.
 // Current as of ICU 50.1.2 and Firefox March 2013.
 var locales = [
     "af",
     "af-NA",
@@ -357,15 +357,13 @@ var locales = [
     "zh-Hans-MO",
     "zh-Hans-SG",
     "zh-Hant",
     "zh-Hant-HK",
     "zh-Hant-MO",
     "zh-Hant-TW",
 ];
 
-addIntlExtras(Intl);
-
 const result = Intl.PluralRules.supportedLocalesOf(locales);
 
 assertEqArray(locales, result);
 
 reportCompare(0, 0, 'ok');
--- a/js/src/tests/jstests.list
+++ b/js/src/tests/jstests.list
@@ -1,19 +1,16 @@
 # Manifest entries for imported test suites whose individual test cases
 # we don't want to change.
 
 skip script ecma_6/String/normalize-generateddata-input.js # input data for other test
 
 # Skip intl402 tests when Intl isn't available.
 skip-if(!this.hasOwnProperty('Intl')) include test262/intl402/jstests.list
 
-# Skip Intl.PluralRules tests when the addIntlExtras helper isn't available.
-skip-if(!this.hasOwnProperty('addIntlExtras')) include test262/intl402/PluralRules/jstests.list
-
 # Skip built-ins/Simd tests when SIMD isn't available.
 skip-if(!this.hasOwnProperty('SIMD')) include test262/built-ins/Simd/jstests.list
 
 # Skip built-in/Atomics when Atomics isn't available
 skip-if(!this.hasOwnProperty('Atomics')) include test262/built-ins/Atomics/jstests.list
 
 # Skip built-in/SharedArrayBuffer
 skip-if(!this.hasOwnProperty('SharedArrayBuffer')) include test262/built-ins/SharedArrayBuffer/jstests.list
deleted file mode 100644
--- a/js/src/tests/test262-intl-extras.js
+++ /dev/null
@@ -1,14 +0,0 @@
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-// Call the shell helper to add experimental features to the Intl object.
-if (typeof addIntlExtras === "function") {
-    let intlExtras = {};
-    addIntlExtras(intlExtras);
-
-    Object.defineProperty(Intl, "PluralRules", {
-        value: intlExtras.PluralRules,
-        writable: true, enumerable: false, configurable: true
-    });
-}
--- a/js/src/tests/test262-update.py
+++ b/js/src/tests/test262-update.py
@@ -316,19 +316,16 @@ def process_test262(test262Dir, test262O
     explicitIncludes = {}
     explicitIncludes["intl402"] = ["testBuiltInObject.js"]
     explicitIncludes[os.path.join("built-ins", "DataView")] = ["byteConversionValues.js"]
     explicitIncludes[os.path.join("built-ins", "Promise")] = ["promiseHelper.js"]
     explicitIncludes[os.path.join("built-ins", "TypedArray")] = ["byteConversionValues.js",
         "detachArrayBuffer.js", "nans.js"]
     explicitIncludes[os.path.join("built-ins", "TypedArrays")] = ["detachArrayBuffer.js"]
 
-    # Intl.PluralRules isn't yet enabled by default.
-    localIncludesMap[os.path.join("intl402", "PluralRules")] = ["test262-intl-extras.js"]
-
     # Process all test directories recursively.
     for (dirPath, dirNames, fileNames) in os.walk(testDir):
         relPath = os.path.relpath(dirPath, testDir)
         if relPath == ".":
             continue
 
         # Skip creating a "prs" directory if it already exists
         if relPath != "prs" or not os.path.exists(os.path.join(test262OutDir, relPath)):
--- a/js/src/tests/test262/intl402/PluralRules/shell.js
+++ b/js/src/tests/test262/intl402/PluralRules/shell.js
@@ -1,15 +0,0 @@
-// file: test262-intl-extras.js
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-// Call the shell helper to add experimental features to the Intl object.
-if (typeof addIntlExtras === "function") {
-    let intlExtras = {};
-    addIntlExtras(intlExtras);
-
-    Object.defineProperty(Intl, "PluralRules", {
-        value: intlExtras.PluralRules,
-        writable: true, enumerable: false, configurable: true
-    });
-}
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -277,17 +277,16 @@
     macro(ownKeys, ownKeys, "ownKeys") \
     macro(Object_valueOf, Object_valueOf, "Object_valueOf") \
     macro(package, package, "package") \
     macro(parseFloat, parseFloat, "parseFloat") \
     macro(parseInt, parseInt, "parseInt") \
     macro(pattern, pattern, "pattern") \
     macro(pending, pending, "pending") \
     macro(PluralRules, PluralRules, "PluralRules") \
-    macro(PluralRulesSelect, PluralRulesSelect, "Intl_PluralRules_Select") \
     macro(percentSign, percentSign, "percentSign") \
     macro(plusSign, plusSign, "plusSign") \
     macro(public, public_, "public") \
     macro(pull, pull, "pull") \
     macro(preventExtensions, preventExtensions, "preventExtensions") \
     macro(private, private_, "private") \
     macro(promise, promise, "promise") \
     macro(propertyIsEnumerable, propertyIsEnumerable, "propertyIsEnumerable") \
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -773,17 +773,16 @@ class GlobalObject : public NativeObject
     static bool initAsyncGenerators(JSContext* cx, Handle<GlobalObject*> global);
 
     // Implemented in builtin/MapObject.cpp.
     static bool initMapIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
     static bool initSetIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
 
     // Implemented in Intl.cpp.
     static bool initIntlObject(JSContext* cx, Handle<GlobalObject*> global);
-    static bool addPluralRulesConstructor(JSContext* cx, HandleObject intl);
 
     // Implemented in builtin/ModuleObject.cpp
     static bool initModuleProto(JSContext* cx, Handle<GlobalObject*> global);
     static bool initImportEntryProto(JSContext* cx, Handle<GlobalObject*> global);
     static bool initExportEntryProto(JSContext* cx, Handle<GlobalObject*> global);
     static bool initRequestedModuleProto(JSContext* cx, Handle<GlobalObject*> global);
 
     // Implemented in builtin/TypedObject.cpp