[INTL] Implement Date.prototype.toLocaleDateString in ECMA-402
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 20 Jan 2016 01:07:34 +0000 (01:07 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 20 Jan 2016 01:07:34 +0000 (01:07 +0000)
https://bugs.webkit.org/show_bug.cgi?id=147612

Patch by Andy VanWagoner <thetalecrafter@gmail.com> on 2016-01-19
Reviewed by Benjamin Poulain.

Source/JavaScriptCore:

Implement toLocaleDateString in builtin JavaScript. Remove comments with
spec steps, and instead link to the new HTML version of the spec.

Avoids creating an extra empty object in the prototype chain of the options
object in ToDateTimeOptions. The version used in toLocaleString was updated
to match as well.

* builtins/DatePrototype.js:
(toLocaleString.toDateTimeOptionsAnyAll):
(toLocaleString):
(toLocaleDateString.toDateTimeOptionsDateDate):
(toLocaleDateString):
* runtime/DatePrototype.cpp:
(JSC::DatePrototype::finishCreation):

LayoutTests:

Added tests for toLocaleDateString.

* js/date-toLocaleString-expected.txt:
* js/script-tests/date-toLocaleString.js:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@195330 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/js/date-toLocaleString-expected.txt
LayoutTests/js/script-tests/date-toLocaleString.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/builtins/DatePrototype.js
Source/JavaScriptCore/runtime/DatePrototype.cpp

index dee362f..e7f71e1 100644 (file)
@@ -1,3 +1,15 @@
+2016-01-19  Andy VanWagoner  <thetalecrafter@gmail.com>
+
+        [INTL] Implement Date.prototype.toLocaleDateString in ECMA-402
+        https://bugs.webkit.org/show_bug.cgi?id=147612
+
+        Reviewed by Benjamin Poulain.
+
+        Added tests for toLocaleDateString.
+
+        * js/date-toLocaleString-expected.txt:
+        * js/script-tests/date-toLocaleString.js:
+
 2016-01-19  Brady Eidson  <beidson@apple.com>
 
         Modern IDB: Split all storage/indexeddb/modern tests into separate HTML + JS format.
index 02ccf80..a471c61 100644 (file)
@@ -26,6 +26,29 @@ PASS new Date(0).toLocaleString('en', { timeZone: 'UTC' }) is "1/1/1970, 12:00:0
 PASS new Date(0).toLocaleString('en', null) threw exception TypeError: null is not an object.
 PASS new Date(0).toLocaleString('en', { timeZone: 'UTC', hour:'numeric', minute:'2-digit' }) is "12:00 AM"
 PASS new Date(0).toLocaleString('en', { timeZone: 'UTC', year:'numeric', month:'long' }) is "January 1970"
+PASS Date.prototype.toLocaleDateString.length is 0
+PASS Object.getOwnPropertyDescriptor(Date.prototype, 'toLocaleDateString').enumerable is false
+PASS Object.getOwnPropertyDescriptor(Date.prototype, 'toLocaleDateString').configurable is true
+PASS Object.getOwnPropertyDescriptor(Date.prototype, 'toLocaleDateString').writable is true
+PASS Date.prototype.toLocaleDateString.call(new Date) did not throw exception.
+PASS Date.prototype.toLocaleDateString.call() threw exception TypeError: Type error.
+PASS Date.prototype.toLocaleDateString.call(undefined) threw exception TypeError: Type error.
+PASS Date.prototype.toLocaleDateString.call(null) threw exception TypeError: Type error.
+PASS Date.prototype.toLocaleDateString.call(0) threw exception TypeError: Type error.
+PASS Date.prototype.toLocaleDateString.call(NaN) threw exception TypeError: Type error.
+PASS Date.prototype.toLocaleDateString.call(Infinity) threw exception TypeError: Type error.
+PASS Date.prototype.toLocaleDateString.call('1') threw exception TypeError: Type error.
+PASS Date.prototype.toLocaleDateString.call({}) threw exception TypeError: Type error.
+PASS Date.prototype.toLocaleDateString.call([]) threw exception TypeError: Type error.
+PASS Date.prototype.toLocaleDateString.call(Symbol()) threw exception TypeError: Type error.
+PASS typeof new Date().toLocaleDateString() === 'string' is true
+PASS new Date(NaN).toLocaleDateString() is "Invalid Date"
+PASS new Date().toLocaleDateString('i') threw exception RangeError: invalid language tag: i.
+PASS new Date(0).toLocaleDateString('zh-Hans-CN-u-nu-hanidec', { timeZone: 'UTC' }) is "一九七〇/一/一"
+PASS new Date(0).toLocaleDateString('en', { timeZone: 'UTC' }) is "1/1/1970"
+PASS new Date(0).toLocaleDateString('en', null) threw exception TypeError: null is not an object.
+PASS new Date(0).toLocaleDateString('en', { timeZone: 'UTC', hour:'numeric', minute:'2-digit' }) is "1/1/1970, 12:00 AM"
+PASS new Date(0).toLocaleDateString('en', { timeZone: 'UTC', year:'numeric', month:'long' }) is "January 1970"
 PASS successfullyParsed is true
 
 TEST COMPLETE
index bc4f1e7..77113f5 100644 (file)
@@ -35,3 +35,40 @@ shouldThrow("new Date(0).toLocaleString('en', null)", "'TypeError: null is not a
 shouldBeEqualToString("new Date(0).toLocaleString('en', { timeZone: 'UTC', hour:'numeric', minute:'2-digit' })", "12:00 AM");
 shouldBeEqualToString("new Date(0).toLocaleString('en', { timeZone: 'UTC', year:'numeric', month:'long' })", "January 1970");
 
+// Test toLocaleDateString ()
+shouldBe("Date.prototype.toLocaleDateString.length", "0");
+shouldBeFalse("Object.getOwnPropertyDescriptor(Date.prototype, 'toLocaleDateString').enumerable");
+shouldBeTrue("Object.getOwnPropertyDescriptor(Date.prototype, 'toLocaleDateString').configurable");
+shouldBeTrue("Object.getOwnPropertyDescriptor(Date.prototype, 'toLocaleDateString').writable");
+
+// Test thisTimeValue abrupt completion.
+shouldNotThrow("Date.prototype.toLocaleDateString.call(new Date)");
+shouldThrow("Date.prototype.toLocaleDateString.call()");
+shouldThrow("Date.prototype.toLocaleDateString.call(undefined)");
+shouldThrow("Date.prototype.toLocaleDateString.call(null)");
+shouldThrow("Date.prototype.toLocaleDateString.call(0)");
+shouldThrow("Date.prototype.toLocaleDateString.call(NaN)");
+shouldThrow("Date.prototype.toLocaleDateString.call(Infinity)");
+shouldThrow("Date.prototype.toLocaleDateString.call('1')");
+shouldThrow("Date.prototype.toLocaleDateString.call({})");
+shouldThrow("Date.prototype.toLocaleDateString.call([])");
+shouldThrow("Date.prototype.toLocaleDateString.call(Symbol())");
+
+shouldBeTrue("typeof new Date().toLocaleDateString() === 'string'");
+
+shouldBeEqualToString("new Date(NaN).toLocaleDateString()", "Invalid Date");
+
+// Test for DateTimeFormat behavior.
+// Test that locale parameter is passed through properly.
+shouldThrow("new Date().toLocaleDateString('i')");
+shouldBeEqualToString("new Date(0).toLocaleDateString('zh-Hans-CN-u-nu-hanidec', { timeZone: 'UTC' })", "一九七〇/一/一");
+
+// Defaults to mdy
+shouldBeEqualToString("new Date(0).toLocaleDateString('en', { timeZone: 'UTC' })", "1/1/1970");
+
+// Test that options parameter is passed through properly.
+shouldThrow("new Date(0).toLocaleDateString('en', null)", "'TypeError: null is not an object'");
+// Adds mdy if no date formats specified.
+shouldBeEqualToString("new Date(0).toLocaleDateString('en', { timeZone: 'UTC', hour:'numeric', minute:'2-digit' })", "1/1/1970, 12:00 AM");
+// If any date formats specified, just use them.
+shouldBeEqualToString("new Date(0).toLocaleDateString('en', { timeZone: 'UTC', year:'numeric', month:'long' })", "January 1970");
index 24013b2..24496fc 100644 (file)
@@ -1,3 +1,25 @@
+2016-01-19  Andy VanWagoner  <thetalecrafter@gmail.com>
+
+        [INTL] Implement Date.prototype.toLocaleDateString in ECMA-402
+        https://bugs.webkit.org/show_bug.cgi?id=147612
+
+        Reviewed by Benjamin Poulain.
+
+        Implement toLocaleDateString in builtin JavaScript. Remove comments with
+        spec steps, and instead link to the new HTML version of the spec.
+
+        Avoids creating an extra empty object in the prototype chain of the options
+        object in ToDateTimeOptions. The version used in toLocaleString was updated
+        to match as well.
+
+        * builtins/DatePrototype.js:
+        (toLocaleString.toDateTimeOptionsAnyAll):
+        (toLocaleString):
+        (toLocaleDateString.toDateTimeOptionsDateDate):
+        (toLocaleDateString):
+        * runtime/DatePrototype.cpp:
+        (JSC::DatePrototype::finishCreation):
+
 2016-01-19  Benjamin Poulain  <bpoulain@apple.com>
 
         [JSC] fixSpillSlotZDef() crashes on ARM64
index 6601a23..683c1d3 100644 (file)
@@ -31,54 +31,31 @@ function toLocaleString(/* locales, options */)
 
     function toDateTimeOptionsAnyAll(opts)
     {
-        // ToDateTimeOptions abstract operation (ECMA-402 2.0)
-        // http://ecma-international.org/publications/standards/Ecma-402.htm
+        // ToDateTimeOptions(options, "any", "all")
+        // http://www.ecma-international.org/ecma-402/2.0/#sec-InitializeDateTimeFormat
 
-        // 1. If options is undefined, then let options be null, else let options be ToObject(options).
-        // 2. ReturnIfAbrupt(options).
-        var optObj;
+        var options;
         if (opts === undefined)
-            optObj = null;
+            options = null;
         else if (opts === null)
             throw new @TypeError("null is not an object");
         else
-            optObj = @Object(opts);
-
-        // 3. Let options be ObjectCreate(options).
-        var options = @Object.create(optObj);
-
-        // 4. Let needDefaults be true.
-        // 5. If required is "date" or "any",
-        // a. For each of the property names "weekday", "year", "month", "day":
-        // i. Let prop be the property name.
-        // ii. Let value be Get(options, prop).
-        // iii. ReturnIfAbrupt(value).
-        // iv. If value is not undefined, then let needDefaults be false.
-        // 6. If required is "time" or "any",
-        // a. For each of the property names "hour", "minute", "second":
-        // i. Let prop be the property name.
-        // ii. Let value be Get(options, prop).
-        // iii. ReturnIfAbrupt(value).
-        // iv. If value is not undefined, then let needDefaults be false.
-        // Check optObj instead of options to reduce lookups up the prototype chain.
-        var needsDefaults = !optObj || (
-            optObj.weekday === undefined &&
-            optObj.year === undefined &&
-            optObj.month === undefined &&
-            optObj.day === undefined &&
-            optObj.hour === undefined &&
-            optObj.minute === undefined &&
-            optObj.second === undefined
+            options = @Object(opts);
+
+        // Check original instead of descendant to reduce lookups up the prototype chain.
+        var needsDefaults = !options || (
+            options.weekday === undefined &&
+            options.year === undefined &&
+            options.month === undefined &&
+            options.day === undefined &&
+            options.hour === undefined &&
+            options.minute === undefined &&
+            options.second === undefined
         );
 
-        // 7. If needDefaults is true and defaults is either "date" or "all", then a. For each of the property names "year", "month", "day":
-        // i. Let status be CreateDatePropertyOrThrow(options, prop, "numeric").
-        // ii. ReturnIfAbrupt(status).
-        // 8. If needDefaults is true and defaults is either "time" or "all", then
-        // a. For each of the property names "hour", "minute", "second":
-        // i. Let status be CreateDatePropertyOrThrow(options, prop, "numeric").
-        // ii. ReturnIfAbrupt(status).
+        // Only create descendant if it will have own properties.
         if (needsDefaults) {
+            options = @Object.create(options)
             options.year = "numeric";
             options.month = "numeric";
             options.day = "numeric";
@@ -92,25 +69,65 @@ function toLocaleString(/* locales, options */)
     }
 
     // 13.3.1 Date.prototype.toLocaleString ([locales [, options ]]) (ECMA-402 2.0)
-    // http://ecma-international.org/publications/standards/Ecma-402.htm
+    // http://www.ecma-international.org/ecma-402/2.0/#sec-Date.prototype.toLocaleString
 
-    // 1. Let x be thisTimeValue(this value).
-    // 2. ReturnIfAbrupt(x).
     var value = @thisTimeValue.@call(this);
-
-    // 3. If x is NaN, return "Invalid Date".
     if (@isNaN(value))
         return "Invalid Date";
 
-    // 4. Let options be ToDateTimeOptions(options, "any", "all").
-    // 5. ReturnIfAbrupt(options).
     var options = toDateTimeOptionsAnyAll(arguments[1]);
-
-    // 6. Let dateFormat be Construct(%DateTimeFormat%, «locales, options»).
-    // 7. ReturnIfAbrupt(dateFormat).
     var locales = arguments[0];
+
     var dateFormat = new @DateTimeFormat(locales, options);
+    return dateFormat.format(value);
+}
+
+function toLocaleDateString(/* locales, options */)
+{
+    "use strict";
+
+    function toDateTimeOptionsDateDate(opts)
+    {
+        // ToDateTimeOptions(options, "date", "date")
+        // http://www.ecma-international.org/ecma-402/2.0/#sec-InitializeDateTimeFormat
+
+        var options;
+        if (opts === undefined)
+            options = null;
+        else if (opts === null)
+            throw new @TypeError("null is not an object");
+        else
+            options = @Object(opts);
+
+        // Check original instead of descendant to reduce lookups up the prototype chain.
+        var needsDefaults = !options || (
+            options.weekday === undefined &&
+            options.year === undefined &&
+            options.month === undefined &&
+            options.day === undefined
+        );
 
-    // 8. Return FormatDateTime(dateFormat, x).
+        // Only create descendant if it will have own properties.
+        if (needsDefaults) {
+            options = @Object.create(options)
+            options.year = "numeric";
+            options.month = "numeric";
+            options.day = "numeric";
+        }
+
+        return options;
+    }
+
+    // 13.3.2 Date.prototype.toLocaleDateString ([locales [, options ]]) (ECMA-402 2.0)
+    // http://www.ecma-international.org/ecma-402/2.0/#sec-Date.prototype.toLocaleDateString
+
+    var value = @thisTimeValue.@call(this);
+    if (@isNaN(value))
+        return "Invalid Date";
+
+    var options = toDateTimeOptionsDateDate(arguments[1]);
+    var locales = arguments[0];
+
+    var dateFormat = new @DateTimeFormat(locales, options);
     return dateFormat.format(value);
 }
index f5e8684..45af7e0 100644 (file)
@@ -493,6 +493,7 @@ void DatePrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
 
 #if ENABLE(INTL)
     JSC_BUILTIN_FUNCTION("toLocaleString", datePrototypeToLocaleStringCodeGenerator, DontEnum);
+    JSC_BUILTIN_FUNCTION("toLocaleDateString", datePrototypeToLocaleDateStringCodeGenerator, DontEnum);
 #else
     UNUSED_PARAM(globalObject);
 #endif // ENABLE(INTL)