Bug 1405015 - Don't perform legacy unwrapping for Intl objects when calling formatToParts. r=gandalf
authorAndré Bargull <andre.bargull@gmail.com>
Mon, 02 Oct 2017 18:54:28 +0200
changeset 426796 a41ebebed7fc788294c7f53e45919e10d3ada969
parent 426795 add5e417d0528189831b5d7431f6e264c3dc3ecf
child 426797 8787753154117930a310005472e340f30255dd2b
push id97
push userfmarier@mozilla.com
push dateSat, 14 Oct 2017 01:12:59 +0000
reviewersgandalf
bugs1405015
milestone58.0a1
Bug 1405015 - Don't perform legacy unwrapping for Intl objects when calling formatToParts. r=gandalf
js/src/builtin/Intl.js
js/src/tests/Intl/DateTimeFormat/unwrapping.js
js/src/tests/Intl/NumberFormat/unwrapping.js
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -2365,18 +2365,24 @@ function Intl_NumberFormat_format_get() 
 
     // Step 5.
     return internals.boundFormat;
 }
 _SetCanonicalName(Intl_NumberFormat_format_get, "get format");
 
 
 function Intl_NumberFormat_formatToParts(value) {
-    // Steps 1-3.
-    var nf = UnwrapNumberFormat(this, "formatToParts");
+    // Step 1.
+    var nf = this;
+
+    // Steps 2-3.
+    if (!IsObject(nf) || !IsNumberFormat(nf)) {
+        ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "NumberFormat", "formatToParts",
+                       "NumberFormat");
+    }
 
     // Ensure the NumberFormat internals are resolved.
     getNumberFormatInternals(nf);
 
     // Step 4.
     var x = ToNumber(value);
 
     // Step 5.
@@ -3066,18 +3072,24 @@ function Intl_DateTimeFormat_format_get(
 }
 _SetCanonicalName(Intl_DateTimeFormat_format_get, "get format");
 
 
 /**
  * Intl.DateTimeFormat.prototype.formatToParts ( date )
  */
 function Intl_DateTimeFormat_formatToParts(date) {
-    // Steps 1-3.
-    var dtf = UnwrapDateTimeFormat(this, "formatToParts");
+    // Step 1.
+    var dtf = this;
+
+    // Steps 2-3.
+    if (!IsObject(dtf) || !IsDateTimeFormat(dtf)) {
+        ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "DateTimeFormat", "formatToParts",
+                       "DateTimeFormat");
+    }
 
     // Ensure the DateTimeFormat internals are resolved.
     getDateTimeFormatInternals(dtf);
 
     // Steps 4-5.
     var x = (date === undefined) ? std_Date_now() : ToNumber(date);
 
     // Step 6.
--- a/js/src/tests/Intl/DateTimeFormat/unwrapping.js
+++ b/js/src/tests/Intl/DateTimeFormat/unwrapping.js
@@ -1,16 +1,25 @@
 // |reftest| skip-if(!this.hasOwnProperty("Intl"))
 
 // Test UnwrapDateTimeFormat operation.
 
 const dateTimeFormatFunctions = [];
-dateTimeFormatFunctions.push(Intl.DateTimeFormat.prototype.resolvedOptions);
-dateTimeFormatFunctions.push(Object.getOwnPropertyDescriptor(Intl.DateTimeFormat.prototype, "format").get);
-dateTimeFormatFunctions.push(Intl.DateTimeFormat.prototype.formatToParts);
+dateTimeFormatFunctions.push({
+    function: Intl.DateTimeFormat.prototype.resolvedOptions,
+    unwrap: true,
+});
+dateTimeFormatFunctions.push({
+    function: Object.getOwnPropertyDescriptor(Intl.DateTimeFormat.prototype, "format").get,
+    unwrap: true,
+});
+dateTimeFormatFunctions.push({
+    function: Intl.DateTimeFormat.prototype.formatToParts,
+    unwrap: false,
+});
 
 function IsConstructor(o) {
   try {
     new (new Proxy(o, {construct: () => ({})}));
     return true;
   } catch (e) {
     return false;
   }
@@ -53,17 +62,17 @@ function thisValues(C) {
         // Object inheriting from an Intl constructor prototype.
         ...intlConstructors.map(ctor => Object.create(ctor.prototype)),
     ];
 }
 
 const intlFallbackSymbol = Object.getOwnPropertySymbols(Intl.DateTimeFormat.call(Object.create(Intl.DateTimeFormat.prototype)))[0];
 
 // Test Intl.DateTimeFormat.prototype methods.
-for (let dateTimeFormatFunction of dateTimeFormatFunctions) {
+for (let {function: dateTimeFormatFunction, unwrap} of dateTimeFormatFunctions) {
     // Test a TypeError is thrown when the this-value isn't an initialized
     // Intl.DateTimeFormat instance.
     for (let thisValue of thisValues(Intl.DateTimeFormat)) {
         assertThrowsInstanceOf(() => dateTimeFormatFunction.call(thisValue), TypeError);
     }
 
     // And test no error is thrown for initialized Intl.DateTimeFormat instances.
     for (let thisValue of intlObjects(Intl.DateTimeFormat)) {
@@ -74,20 +83,25 @@ for (let dateTimeFormatFunction of dateT
     for (let thisValue of thisValues(Intl.DateTimeFormat)) {
         assertThrowsInstanceOf(() => dateTimeFormatFunction.call({
             __proto__: Intl.DateTimeFormat.prototype,
             [intlFallbackSymbol]: thisValue,
         }), TypeError);
     }
 
     for (let thisValue of intlObjects(Intl.DateTimeFormat)) {
-        dateTimeFormatFunction.call({
+        let obj = {
             __proto__: Intl.DateTimeFormat.prototype,
             [intlFallbackSymbol]: thisValue,
-        });
+        };
+        if (unwrap) {
+            dateTimeFormatFunction.call(obj);
+        } else {
+            assertThrowsInstanceOf(() => dateTimeFormatFunction.call(obj), TypeError);
+        }
     }
 
     // Ensure [[FallbackSymbol]] isn't retrieved for Intl.DateTimeFormat instances.
     for (let thisValue of intlObjects(Intl.DateTimeFormat)) {
         Object.defineProperty(thisValue, intlFallbackSymbol, {
             get() { assertEq(false, true); }
         });
         dateTimeFormatFunction.call(thisValue);
@@ -121,18 +135,18 @@ for (let dateTimeFormatFunction of dateT
                 return null;
             }, configurable: true
         });
 
         assertThrowsInstanceOf(() => dateTimeFormatFunction.call(thisValue), TypeError);
 
         delete Intl.DateTimeFormat[Symbol.hasInstance];
 
-        assertEq(hasInstanceCalled, true);
-        assertEq(symbolGetterCalled, true);
+        assertEq(hasInstanceCalled, unwrap);
+        assertEq(symbolGetterCalled, unwrap);
     }
 
     // Test with primitive values.
     for (let thisValue of thisValues(Intl.DateTimeFormat).filter(IsPrimitive)) {
         // Ensure @@hasInstance is not called.
         Object.defineProperty(Intl.DateTimeFormat, Symbol.hasInstance, {
             value() { assertEq(true, false); }, configurable: true
         });
@@ -172,45 +186,31 @@ for (let dateTimeFormatFunction of dateT
     for (let number of [0, Date.now(), -Date.now()]) {
         let expected = dateTimeFormat.format(number);
         assertEq(thisValue.format(number), expected);
         assertEq(thisValue[intlFallbackSymbol].format(number), expected);
         assertEq(fakeObj.format(number), expected);
     }
 }
 
-// Test formatToParts() returns the correct result for objects initialized as Intl.DateTimeFormat instances.
-if ("formatToParts" in Intl.DateTimeFormat.prototype) {
-    // An actual Intl.DateTimeFormat instance.
-    let dateTimeFormat = new Intl.DateTimeFormat();
+// Ensure formatToParts() doesn't use the fallback semantics.
+{
+    let formatToParts = Intl.DateTimeFormat.prototype.formatToParts;
 
     // An object initialized as a DateTimeFormat instance.
     let thisValue = Object.create(Intl.DateTimeFormat.prototype);
     Intl.DateTimeFormat.call(thisValue);
+    assertThrowsInstanceOf(() => formatToParts.call(thisValue), TypeError);
 
     // Object with [[FallbackSymbol]] set to DateTimeFormat instance.
     let fakeObj = {
         __proto__: Intl.DateTimeFormat.prototype,
-        [intlFallbackSymbol]: dateTimeFormat,
+        [intlFallbackSymbol]: new Intl.DateTimeFormat(),
     };
-
-    function assertEqParts(actual, expected) {
-        assertEq(actual.length, expected.length, "parts count mismatch");
-        for (var i = 0; i < expected.length; i++) {
-            assertEq(actual[i].type, expected[i].type, "type mismatch at " + i);
-            assertEq(actual[i].value, expected[i].value, "value mismatch at " + i);
-        }
-    }
-
-    for (let number of [0, Date.now(), -Date.now()]) {
-        let expected = dateTimeFormat.formatToParts(number);
-        assertEqParts(thisValue.formatToParts(number), expected);
-        assertEqParts(thisValue[intlFallbackSymbol].formatToParts(number), expected);
-        assertEqParts(fakeObj.formatToParts(number), expected);
-    }
+    assertThrowsInstanceOf(() => formatToParts.call(fakeObj), TypeError);
 }
 
 // Test resolvedOptions() returns the same results.
 {
     // An actual Intl.DateTimeFormat instance.
     let dateTimeFormat = new Intl.DateTimeFormat();
 
     // An object initialized as a DateTimeFormat instance.
--- a/js/src/tests/Intl/NumberFormat/unwrapping.js
+++ b/js/src/tests/Intl/NumberFormat/unwrapping.js
@@ -1,18 +1,25 @@
 // |reftest| skip-if(!this.hasOwnProperty("Intl"))
 
 // Test UnwrapNumberFormat operation.
 
 const numberFormatFunctions = [];
-numberFormatFunctions.push(Intl.NumberFormat.prototype.resolvedOptions);
-numberFormatFunctions.push(Object.getOwnPropertyDescriptor(Intl.NumberFormat.prototype, "format").get);
-// "formatToParts" isn't yet enabled by default.
-if ("formatToParts" in Intl.NumberFormat.prototype)
-    numberFormatFunctions.push(Intl.NumberFormat.prototype.formatToParts);
+numberFormatFunctions.push({
+    function: Intl.NumberFormat.prototype.resolvedOptions,
+    unwrap: true,
+});
+numberFormatFunctions.push({
+    function: Object.getOwnPropertyDescriptor(Intl.NumberFormat.prototype, "format").get,
+    unwrap: true,
+});
+numberFormatFunctions.push({
+    function: Intl.NumberFormat.prototype.formatToParts,
+    unwrap: false,
+});
 
 function IsConstructor(o) {
   try {
     new (new Proxy(o, {construct: () => ({})}));
     return true;
   } catch (e) {
     return false;
   }
@@ -55,17 +62,17 @@ function thisValues(C) {
         // Object inheriting from an Intl constructor prototype.
         ...intlConstructors.map(ctor => Object.create(ctor.prototype)),
     ];
 }
 
 const intlFallbackSymbol = Object.getOwnPropertySymbols(Intl.NumberFormat.call(Object.create(Intl.NumberFormat.prototype)))[0];
 
 // Test Intl.NumberFormat.prototype methods.
-for (let numberFormatFunction of numberFormatFunctions) {
+for (let {function: numberFormatFunction, unwrap} of numberFormatFunctions) {
     // Test a TypeError is thrown when the this-value isn't an initialized
     // Intl.NumberFormat instance.
     for (let thisValue of thisValues(Intl.NumberFormat)) {
         assertThrowsInstanceOf(() => numberFormatFunction.call(thisValue), TypeError);
     }
 
     // And test no error is thrown for initialized Intl.NumberFormat instances.
     for (let thisValue of intlObjects(Intl.NumberFormat)) {
@@ -76,20 +83,25 @@ for (let numberFormatFunction of numberF
     for (let thisValue of thisValues(Intl.NumberFormat)) {
         assertThrowsInstanceOf(() => numberFormatFunction.call({
             __proto__: Intl.NumberFormat.prototype,
             [intlFallbackSymbol]: thisValue,
         }), TypeError);
     }
 
     for (let thisValue of intlObjects(Intl.NumberFormat)) {
-        numberFormatFunction.call({
+        let obj = {
             __proto__: Intl.NumberFormat.prototype,
             [intlFallbackSymbol]: thisValue,
-        });
+        };
+        if (unwrap) {
+            numberFormatFunction.call(obj);
+        } else {
+            assertThrowsInstanceOf(() => numberFormatFunction.call(obj), TypeError);
+        }
     }
 
     // Ensure [[FallbackSymbol]] isn't retrieved for Intl.NumberFormat instances.
     for (let thisValue of intlObjects(Intl.NumberFormat)) {
         Object.defineProperty(thisValue, intlFallbackSymbol, {
             get() { assertEq(false, true); }
         });
         numberFormatFunction.call(thisValue);
@@ -123,18 +135,18 @@ for (let numberFormatFunction of numberF
                 return null;
             }, configurable: true
         });
 
         assertThrowsInstanceOf(() => numberFormatFunction.call(thisValue), TypeError);
 
         delete Intl.NumberFormat[Symbol.hasInstance];
 
-        assertEq(hasInstanceCalled, true);
-        assertEq(symbolGetterCalled, true);
+        assertEq(hasInstanceCalled, unwrap);
+        assertEq(symbolGetterCalled, unwrap);
     }
 
     // Test with primitive values.
     for (let thisValue of thisValues(Intl.NumberFormat).filter(IsPrimitive)) {
         // Ensure @@hasInstance is not called.
         Object.defineProperty(Intl.NumberFormat, Symbol.hasInstance, {
             value() { assertEq(true, false); }, configurable: true
         });
@@ -174,45 +186,31 @@ for (let numberFormatFunction of numberF
     for (let number of [0, 1, 1.5, Infinity, NaN]) {
         let expected = numberFormat.format(number);
         assertEq(thisValue.format(number), expected);
         assertEq(thisValue[intlFallbackSymbol].format(number), expected);
         assertEq(fakeObj.format(number), expected);
     }
 }
 
-// Test formatToParts() returns the correct result for objects initialized as Intl.NumberFormat instances.
-if ("formatToParts" in Intl.NumberFormat.prototype) {
-    // An actual Intl.NumberFormat instance.
-    let numberFormat = new Intl.NumberFormat();
+// Ensure formatToParts() doesn't use the fallback semantics.
+{
+    let formatToParts = Intl.NumberFormat.prototype.formatToParts;
 
     // An object initialized as a NumberFormat instance.
     let thisValue = Object.create(Intl.NumberFormat.prototype);
     Intl.NumberFormat.call(thisValue);
+    assertThrowsInstanceOf(() => formatToParts.call(thisValue), TypeError);
 
     // Object with [[FallbackSymbol]] set to NumberFormat instance.
     let fakeObj = {
         __proto__: Intl.NumberFormat.prototype,
-        [intlFallbackSymbol]: numberFormat,
+        [intlFallbackSymbol]: new Intl.NumberFormat(),
     };
-
-    function assertEqParts(actual, expected) {
-        assertEq(actual.length, expected.length, "parts count mismatch");
-        for (var i = 0; i < expected.length; i++) {
-            assertEq(actual[i].type, expected[i].type, "type mismatch at " + i);
-            assertEq(actual[i].value, expected[i].value, "value mismatch at " + i);
-        }
-    }
-
-    for (let number of [0, 1, 1.5, Infinity, NaN]) {
-        let expected = numberFormat.formatToParts(number);
-        assertEqParts(thisValue.formatToParts(number), expected);
-        assertEqParts(thisValue[intlFallbackSymbol].formatToParts(number), expected);
-        assertEqParts(fakeObj.formatToParts(number), expected);
-    }
+    assertThrowsInstanceOf(() => formatToParts.call(fakeObj), TypeError);
 }
 
 // Test resolvedOptions() returns the same results.
 {
     // An actual Intl.NumberFormat instance.
     let numberFormat = new Intl.NumberFormat();
 
     // An object initialized as a NumberFormat instance.