[JSC] Drop direct references to Intl constructors by rewriting Intl JS builtins in C++
authorysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 25 Feb 2019 18:32:18 +0000 (18:32 +0000)
committerysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 25 Feb 2019 18:32:18 +0000 (18:32 +0000)
https://bugs.webkit.org/show_bug.cgi?id=194976

Reviewed by Michael Saboff.

This patch paves the way to making IntlObject allocation lazy by removing direct references
to Intl constructors (Intl.Collator etc.) from builtin JS. To achieve that,

1. We implement String.prototype.toLocaleCompare and Number.prototype.toLocaleString in C++
   instead of JS builtins. Since these functions end up calling ICU C++ runtime, writing them in
   JS does not offer performance improvement.

2. We remove @DateTimeFormat constructor reference, and instead, exposing @dateTimeFormat function,
   which returns formatted string directly. We still have JS builtins for DateTimeFormat things
   because the initialization of its "options" JSObject involves many get_by_id / put_by_id things,
   which are efficient in JS. But we avoid exposing @DateTimeFormat directly, so that Intl constructors
   can be lazily allocated.

* CMakeLists.txt:
* DerivedSources-input.xcfilelist:
* DerivedSources.make:
* JavaScriptCore.xcodeproj/project.pbxproj:
* builtins/BuiltinNames.h:
* builtins/DatePrototype.js:
(toLocaleString):
(toLocaleDateString):
(toLocaleTimeString):
* builtins/NumberPrototype.js: Removed.
* builtins/StringPrototype.js:
(intrinsic.StringPrototypeReplaceIntrinsic.replace):
(globalPrivate.getDefaultCollator): Deleted.
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
(JSC::JSGlobalObject::visitChildren):
(JSC::JSGlobalObject::defaultCollator):
* runtime/JSGlobalObject.h:
* runtime/JSGlobalObjectFunctions.cpp:
(JSC::globalFuncDateTimeFormat):
* runtime/JSGlobalObjectFunctions.h:
* runtime/NumberPrototype.cpp:
(JSC::NumberPrototype::finishCreation):
(JSC::throwVMToThisNumberError):
(JSC::numberProtoFuncToExponential):
(JSC::numberProtoFuncToFixed):
(JSC::numberProtoFuncToPrecision):
(JSC::numberProtoFuncToString):
(JSC::numberProtoFuncToLocaleString):
(JSC::numberProtoFuncValueOf):
* runtime/StringPrototype.cpp:
(JSC::StringPrototype::finishCreation):
(JSC::stringProtoFuncLocaleCompare):

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

15 files changed:
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/DerivedSources-input.xcfilelist
Source/JavaScriptCore/DerivedSources.make
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/builtins/BuiltinNames.h
Source/JavaScriptCore/builtins/DatePrototype.js
Source/JavaScriptCore/builtins/NumberPrototype.js [deleted file]
Source/JavaScriptCore/builtins/StringPrototype.js
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h
Source/JavaScriptCore/runtime/NumberPrototype.cpp
Source/JavaScriptCore/runtime/StringPrototype.cpp

index 29d9a9f..f94d85b 100644 (file)
@@ -1188,7 +1188,6 @@ set(JavaScriptCore_BUILTINS_SOURCES
     ${JAVASCRIPTCORE_DIR}/builtins/MapPrototype.js
     ${JAVASCRIPTCORE_DIR}/builtins/ModuleLoader.js
     ${JAVASCRIPTCORE_DIR}/builtins/NumberConstructor.js
-    ${JAVASCRIPTCORE_DIR}/builtins/NumberPrototype.js
     ${JAVASCRIPTCORE_DIR}/builtins/ObjectConstructor.js
     ${JAVASCRIPTCORE_DIR}/builtins/PromiseConstructor.js
     ${JAVASCRIPTCORE_DIR}/builtins/PromiseOperations.js
index 802f9d2..8b2c8b9 100644 (file)
@@ -1,3 +1,57 @@
+2019-02-25  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        [JSC] Drop direct references to Intl constructors by rewriting Intl JS builtins in C++
+        https://bugs.webkit.org/show_bug.cgi?id=194976
+
+        Reviewed by Michael Saboff.
+
+        This patch paves the way to making IntlObject allocation lazy by removing direct references
+        to Intl constructors (Intl.Collator etc.) from builtin JS. To achieve that,
+
+        1. We implement String.prototype.toLocaleCompare and Number.prototype.toLocaleString in C++
+           instead of JS builtins. Since these functions end up calling ICU C++ runtime, writing them in
+           JS does not offer performance improvement.
+
+        2. We remove @DateTimeFormat constructor reference, and instead, exposing @dateTimeFormat function,
+           which returns formatted string directly. We still have JS builtins for DateTimeFormat things
+           because the initialization of its "options" JSObject involves many get_by_id / put_by_id things,
+           which are efficient in JS. But we avoid exposing @DateTimeFormat directly, so that Intl constructors
+           can be lazily allocated.
+
+        * CMakeLists.txt:
+        * DerivedSources-input.xcfilelist:
+        * DerivedSources.make:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * builtins/BuiltinNames.h:
+        * builtins/DatePrototype.js:
+        (toLocaleString):
+        (toLocaleDateString):
+        (toLocaleTimeString):
+        * builtins/NumberPrototype.js: Removed.
+        * builtins/StringPrototype.js:
+        (intrinsic.StringPrototypeReplaceIntrinsic.replace):
+        (globalPrivate.getDefaultCollator): Deleted.
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init):
+        (JSC::JSGlobalObject::visitChildren):
+        (JSC::JSGlobalObject::defaultCollator):
+        * runtime/JSGlobalObject.h:
+        * runtime/JSGlobalObjectFunctions.cpp:
+        (JSC::globalFuncDateTimeFormat):
+        * runtime/JSGlobalObjectFunctions.h:
+        * runtime/NumberPrototype.cpp:
+        (JSC::NumberPrototype::finishCreation):
+        (JSC::throwVMToThisNumberError):
+        (JSC::numberProtoFuncToExponential):
+        (JSC::numberProtoFuncToFixed):
+        (JSC::numberProtoFuncToPrecision):
+        (JSC::numberProtoFuncToString):
+        (JSC::numberProtoFuncToLocaleString):
+        (JSC::numberProtoFuncValueOf):
+        * runtime/StringPrototype.cpp:
+        (JSC::StringPrototype::finishCreation):
+        (JSC::stringProtoFuncLocaleCompare):
+
 2019-02-24  Devin Rousso  <drousso@apple.com>
 
         Web Inspector: Change the InspectorOverlay to use native rather than canvas
index c61d435..ec1f34f 100644 (file)
@@ -43,7 +43,6 @@ $(PROJECT_DIR)/builtins/MapIteratorPrototype.js
 $(PROJECT_DIR)/builtins/MapPrototype.js
 $(PROJECT_DIR)/builtins/ModuleLoader.js
 $(PROJECT_DIR)/builtins/NumberConstructor.js
-$(PROJECT_DIR)/builtins/NumberPrototype.js
 $(PROJECT_DIR)/builtins/ObjectConstructor.js
 $(PROJECT_DIR)/builtins/PromiseConstructor.js
 $(PROJECT_DIR)/builtins/PromiseOperations.js
index c066081..242e0cd 100644 (file)
@@ -105,7 +105,6 @@ JavaScriptCore_BUILTINS_SOURCES = \
     $(JavaScriptCore)/builtins/MapPrototype.js \
     $(JavaScriptCore)/builtins/ModuleLoader.js \
     $(JavaScriptCore)/builtins/NumberConstructor.js \
-    $(JavaScriptCore)/builtins/NumberPrototype.js \
     $(JavaScriptCore)/builtins/ObjectConstructor.js \
     $(JavaScriptCore)/builtins/PromiseConstructor.js \
     $(JavaScriptCore)/builtins/PromiseOperations.js \
index 7136290..1084330 100644 (file)
                        dstPath = testapiScripts;
                        dstSubfolderSpec = 16;
                        files = (
-                               52D13091221CE176009C836C /* foo.js in Copy Support Script */,
                                53C3D5E521ECE7720087FDFC /* basic.js in Copy Support Script */,
+                               52D13091221CE176009C836C /* foo.js in Copy Support Script */,
                                FECB8B2A1D25CB5A006F2463 /* testapi-function-overrides.js in Copy Support Script */,
                                5DBB151B131D0B310056AD36 /* testapi.js in Copy Support Script */,
                        );
                A1587D6C1B4DC14100D69849 /* IntlDateTimeFormatPrototype.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IntlDateTimeFormatPrototype.h; sourceTree = "<group>"; };
                A1587D731B4DC1C600D69849 /* IntlDateTimeFormatConstructor.lut.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IntlDateTimeFormatConstructor.lut.h; sourceTree = "<group>"; };
                A1587D741B4DC1C600D69849 /* IntlDateTimeFormatPrototype.lut.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IntlDateTimeFormatPrototype.lut.h; sourceTree = "<group>"; };
-               A15DE5C51C0FBF8D0089133D /* NumberPrototype.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = NumberPrototype.js; sourceTree = "<group>"; };
                A1712B3A11C7B212007A5315 /* RegExpCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegExpCache.cpp; sourceTree = "<group>"; };
                A1712B3E11C7B228007A5315 /* RegExpCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegExpCache.h; sourceTree = "<group>"; };
                A1712B4011C7B235007A5315 /* RegExpKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegExpKey.h; sourceTree = "<group>"; };
                                7035587C1C418419004BD7BF /* MapPrototype.js */,
                                E30677971B8BC6F5003F87F0 /* ModuleLoader.js */,
                                A52704861D027C8800354C37 /* NumberConstructor.js */,
-                               A15DE5C51C0FBF8D0089133D /* NumberPrototype.js */,
                                7CF9BC5C1B65D9B1009DB1EF /* ObjectConstructor.js */,
                                7CF9BC5E1B65D9B1009DB1EF /* PromiseConstructor.js */,
                                7CF9BC5D1B65D9B1009DB1EF /* PromiseOperations.js */,
                                5B70CFDE1DB69E6600EC23F9 /* JSAsyncFunction.h in Headers */,
                                8BC064891E1ABA6400B2B8CA /* JSAsyncGeneratorFunction.h in Headers */,
                                BC18C4180E16F5CD00B34460 /* JSBase.h in Headers */,
+                               79872C48221BBAF3008C6969 /* JSBaseInternal.h in Headers */,
                                140D17D70E8AD4A9000CD17D /* JSBasePrivate.h in Headers */,
                                868921C21F9C0CB7001159F6 /* JSBigInt.h in Headers */,
                                86FA9E92142BBB2E001773B7 /* JSBoundFunction.h in Headers */,
                                0FF42748158EBE91004CB9FF /* udis86_syn.h in Headers */,
                                0FF42749158EBE91004CB9FF /* udis86_types.h in Headers */,
                                A7E5AB391799E4B200D2833D /* UDis86Disassembler.h in Headers */,
-                               79872C48221BBAF3008C6969 /* JSBaseInternal.h in Headers */,
                                A7A8AF4117ADB5F3005AB174 /* Uint16Array.h in Headers */,
                                866739D313BFDE710023D87C /* Uint16WithFraction.h in Headers */,
                                A7A8AF4217ADB5F3005AB174 /* Uint32Array.h in Headers */,
index 39b704d..1f8260b 100644 (file)
@@ -126,13 +126,9 @@ namespace JSC {
     macro(asyncGeneratorQueueItemNext) \
     macro(asyncGeneratorQueueItemPrevious) \
     macro(generatorResumeMode) \
-    macro(Collator) \
-    macro(DateTimeFormat) \
-    macro(NumberFormat) \
-    macro(PluralRules) \
+    macro(dateTimeFormat) \
     macro(intlSubstituteValue) \
     macro(thisTimeValue) \
-    macro(thisNumberValue) \
     macro(newTargetLocal) \
     macro(derivedConstructor) \
     macro(isTypedArrayView) \
index 800ce6c..3be204d 100644 (file)
@@ -77,9 +77,7 @@ function toLocaleString(/* locales, options */)
 
     var options = toDateTimeOptionsAnyAll(@argument(1));
     var locales = @argument(0);
-
-    var dateFormat = new @DateTimeFormat(locales, options);
-    return dateFormat.format(value);
+    return @dateTimeFormat(locales, options, value);
 }
 
 function toLocaleDateString(/* locales, options */)
@@ -127,9 +125,7 @@ function toLocaleDateString(/* locales, options */)
 
     var options = toDateTimeOptionsDateDate(@argument(1));
     var locales = @argument(0);
-
-    var dateFormat = new @DateTimeFormat(locales, options);
-    return dateFormat.format(value);
+    return @dateTimeFormat(locales, options, value);
 }
 
 function toLocaleTimeString(/* locales, options */)
@@ -176,7 +172,5 @@ function toLocaleTimeString(/* locales, options */)
 
     var options = toDateTimeOptionsTimeTime(@argument(1));
     var locales = @argument(0);
-
-    var dateFormat = new @DateTimeFormat(locales, options);
-    return dateFormat.format(value);
+    return @dateTimeFormat(locales, options, value);
 }
diff --git a/Source/JavaScriptCore/builtins/NumberPrototype.js b/Source/JavaScriptCore/builtins/NumberPrototype.js
deleted file mode 100644 (file)
index c560a9b..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2015 Andy VanWagoner <andy@vanwagoner.family>.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-// @conditional=ENABLE(INTL)
-
-function toLocaleString(/* locales, options */)
-{
-    "use strict";
-
-    // 13.2.1 Number.prototype.toLocaleString ([locales [, options ]]) (ECMA-402 2.0)
-    // http://ecma-international.org/publications/standards/Ecma-402.htm
-
-    // 1. Let x be thisNumberValue(this value).
-    // 2. ReturnIfAbrupt(x).
-    var number = @thisNumberValue.@call(this);
-
-    // 3. Let numberFormat be Construct(%NumberFormat%, «locales, options»).
-    // 4. ReturnIfAbrupt(numberFormat).
-    var numberFormat = new @NumberFormat(@argument(0), @argument(1));
-
-    // 5. Return FormatNumber(numberFormat, x).
-    return numberFormat.format(number);
-}
index 80fc826..1ca93c5 100644 (file)
@@ -237,47 +237,6 @@ function replace(search, replace)
     let searchString = @toString(search);
     return thisString.@replaceUsingStringSearch(searchString, replace);
 }
-    
-@globalPrivate
-function getDefaultCollator()
-{
-    "use strict";
-
-    return @getDefaultCollator.collator || (@getDefaultCollator.collator = new @Collator());
-}
-    
-function localeCompare(that/*, locales, options */)
-{
-    "use strict";
-
-    // 13.1.1 String.prototype.localeCompare (that [, locales [, options ]]) (ECMA-402 2.0)
-    // http://ecma-international.org/publications/standards/Ecma-402.htm
-
-    // 1. Let O be RequireObjectCoercible(this value).
-    if (@isUndefinedOrNull(this))
-        @throwTypeError("String.prototype.localeCompare requires that |this| not be null or undefined");
-
-    // 2. Let S be ToString(O).
-    // 3. ReturnIfAbrupt(S).
-    var thisString = @toString(this);
-
-    // 4. Let That be ToString(that).
-    // 5. ReturnIfAbrupt(That).
-    var thatString = @toString(that);
-
-    // Avoid creating a new collator every time for defaults.
-    var locales = @argument(1);
-    var options = @argument(2);
-    if (locales === @undefined && options === @undefined)
-        return @getDefaultCollator().compare(thisString, thatString);
-
-    // 6. Let collator be Construct(%Collator%, «locales, options»).
-    // 7. ReturnIfAbrupt(collator).
-    var collator = new @Collator(locales, options);
-
-    // 8. Return CompareStrings(collator, S, That).
-    return collator.compare(thisString, thatString);
-}
 
 function search(regexp)
 {
index a91abd7..6386604 100644 (file)
 #include <wtf/RandomNumber.h>
 
 #if ENABLE(INTL)
+#include "IntlCollator.h"
 #include "IntlObject.h"
 #include <unicode/ucol.h>
 #include <unicode/udat.h>
@@ -830,7 +831,9 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
     JSFunction* privateFuncHasInstanceBoundFunction = JSFunction::create(vm, this, 0, String(), hasInstanceBoundFunction);
     JSFunction* privateFuncInstanceOf = JSFunction::create(vm, this, 0, String(), objectPrivateFuncInstanceOf);
     JSFunction* privateFuncThisTimeValue = JSFunction::create(vm, this, 0, String(), dateProtoFuncGetTime);
-    JSFunction* privateFuncThisNumberValue = JSFunction::create(vm, this, 0, String(), numberProtoFuncValueOf);
+#if ENABLE(INTL)
+    JSFunction* privateFuncDateTimeFormat = JSFunction::create(vm, this, 0, String(), globalFuncDateTimeFormat);
+#endif
     JSFunction* privateFuncIsArrayConstructor = JSFunction::create(vm, this, 0, String(), arrayConstructorPrivateFuncIsArrayConstructor);
     JSFunction* privateFuncIsArraySlow = JSFunction::create(vm, this, 0, String(), arrayConstructorPrivateFuncIsArraySlow);
     JSFunction* privateFuncConcatMemcpy = JSFunction::create(vm, this, 0, String(), arrayProtoPrivateFuncConcatMemcpy);
@@ -920,12 +923,8 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
         GlobalPropertyInfo(vm.propertyNames->builtinNames().InspectorInstrumentationPrivateName(), InspectorInstrumentationObject::create(vm, this, InspectorInstrumentationObject::createStructure(vm, this, m_objectPrototype.get())), PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().SetPrivateName(), setConstructor, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().thisTimeValuePrivateName(), privateFuncThisTimeValue, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
-        GlobalPropertyInfo(vm.propertyNames->builtinNames().thisNumberValuePrivateName(), privateFuncThisNumberValue, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
 #if ENABLE(INTL)
-        GlobalPropertyInfo(vm.propertyNames->builtinNames().CollatorPrivateName(), intl->getDirect(vm, vm.propertyNames->Collator), PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
-        GlobalPropertyInfo(vm.propertyNames->builtinNames().DateTimeFormatPrivateName(), intl->getDirect(vm, vm.propertyNames->DateTimeFormat), PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
-        GlobalPropertyInfo(vm.propertyNames->builtinNames().NumberFormatPrivateName(), intl->getDirect(vm, vm.propertyNames->NumberFormat), PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
-        GlobalPropertyInfo(vm.propertyNames->builtinNames().PluralRulesPrivateName(), intl->getDirect(vm, vm.propertyNames->PluralRules), PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->builtinNames().dateTimeFormatPrivateName(), privateFuncDateTimeFormat, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
 #endif // ENABLE(INTL)
 
         GlobalPropertyInfo(vm.propertyNames->builtinNames().isConstructorPrivateName(), JSFunction::create(vm, this, 1, String(), esSpecIsConstructor, NoIntrinsic), PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
@@ -1568,6 +1567,7 @@ void JSGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
 
 #if ENABLE(INTL)
     visitor.append(thisObject->m_intlObject);
+    visitor.append(thisObject->m_defaultCollator);
 #endif
     visitor.append(thisObject->m_nullGetterFunction);
     visitor.append(thisObject->m_nullSetterFunction);
@@ -1865,6 +1865,22 @@ const HashSet<String>& JSGlobalObject::intlPluralRulesAvailableLocales()
     }
     return m_intlPluralRulesAvailableLocales;
 }
+
+IntlCollator* JSGlobalObject::defaultCollator(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    if (m_defaultCollator)
+        return m_defaultCollator.get();
+
+    IntlCollator* collator = IntlCollator::create(vm, intlObject()->collatorStructure());
+    collator->initializeCollator(*exec, jsUndefined(), jsUndefined());
+    RETURN_IF_EXCEPTION(scope, nullptr);
+    m_defaultCollator.set(vm, this, collator);
+    return collator;
+}
+
 #endif // ENABLE(INTL)
 
 void JSGlobalObject::bumpGlobalLexicalBindingEpoch(VM& vm)
index befbd22..5612723 100644 (file)
@@ -83,6 +83,7 @@ class GlobalCodeBlock;
 class IndirectEvalExecutable;
 class InputCursor;
 class IntlObject;
+class IntlCollator;
 class JSArrayBuffer;
 class JSArrayBufferPrototype;
 class JSCallee;
@@ -276,6 +277,7 @@ public:
 
 #if ENABLE(INTL)
     WriteBarrier<IntlObject> m_intlObject;
+    WriteBarrier<IntlCollator> m_defaultCollator;
 #endif
     WriteBarrier<NullGetterFunction> m_nullGetterFunction;
     WriteBarrier<NullSetterFunction> m_nullSetterFunction;
@@ -587,6 +589,7 @@ public:
 
 #if ENABLE(INTL)
     IntlObject* intlObject() const { return m_intlObject.get(); }
+    IntlCollator* defaultCollator(ExecState*);
 #endif
 
     NullGetterFunction* nullGetterFunction() const { return m_nullGetterFunction.get(); }
index 17ee33f..4a6d819 100644 (file)
@@ -31,6 +31,8 @@
 #include "Exception.h"
 #include "IndirectEvalExecutable.h"
 #include "Interpreter.h"
+#include "IntlDateTimeFormat.h"
+#include "IntlObject.h"
 #include "JSCInlines.h"
 #include "JSFunction.h"
 #include "JSGlobalObject.h"
@@ -830,4 +832,20 @@ EncodedJSValue JSC_HOST_CALL globalFuncPropertyIsEnumerable(ExecState* exec)
     return JSValue::encode(jsBoolean(enumerable));
 }
 
+#if ENABLE(INTL)
+EncodedJSValue JSC_HOST_CALL globalFuncDateTimeFormat(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSGlobalObject* globalObject = exec->lexicalGlobalObject();
+    IntlDateTimeFormat* dateTimeFormat = IntlDateTimeFormat::create(vm, globalObject->intlObject()->dateTimeFormatStructure());
+    dateTimeFormat->initializeDateTimeFormat(*exec, exec->argument(0), exec->argument(1));
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    double value = exec->argument(2).toNumber(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    RELEASE_AND_RETURN(scope, JSValue::encode(dateTimeFormat->format(*exec, value)));
+}
+#endif
+
 } // namespace JSC
index c23eb9b..e775524 100644 (file)
@@ -55,6 +55,7 @@ EncodedJSValue JSC_HOST_CALL globalFuncBuiltinLog(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncBuiltinDescribe(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncImportModule(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncPropertyIsEnumerable(ExecState*);
+EncodedJSValue JSC_HOST_CALL globalFuncDateTimeFormat(ExecState*);
 
 double jsToNumber(StringView);
 
index c83783a..43c6e5c 100644 (file)
@@ -24,7 +24,8 @@
 
 #include "BigInteger.h"
 #include "Error.h"
-#include "JSCBuiltins.h"
+#include "IntlNumberFormat.h"
+#include "IntlObject.h"
 #include "JSCInlines.h"
 #include "JSFunction.h"
 #include "JSGlobalObject.h"
@@ -80,10 +81,6 @@ void NumberPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
     setInternalValue(vm, jsNumber(0));
 
     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toString, numberProtoFuncToString, static_cast<unsigned>(PropertyAttribute::DontEnum), 1, NumberPrototypeToStringIntrinsic);
-#if ENABLE(INTL)
-    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("toLocaleString", numberPrototypeToLocaleStringCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
-#endif // ENABLE(INTL)
-
     ASSERT(inherits(vm, info()));
 }
 
@@ -109,6 +106,13 @@ static ALWAYS_INLINE bool toThisNumber(VM& vm, JSValue thisValue, double& x)
     return false;
 }
 
+static ALWAYS_INLINE EncodedJSValue throwVMToThisNumberError(ExecState* exec, ThrowScope& scope, JSValue thisValue)
+{
+    auto typeString = asString(jsTypeStringForValue(exec->vm(), exec->lexicalGlobalObject(), thisValue))->value(exec);
+    scope.assertNoException();
+    return throwVMTypeError(exec, scope, WTF::makeString("thisNumberValue called on incompatible ", typeString));
+}
+
 static ALWAYS_INLINE bool getIntegerArgumentInRange(ExecState* exec, int low, int high, int& result, bool& isUndefined)
 {
     result = 0;
@@ -408,7 +412,7 @@ EncodedJSValue JSC_HOST_CALL numberProtoFuncToExponential(ExecState* exec)
 
     double x;
     if (!toThisNumber(vm, exec->thisValue(), x))
-        return throwVMTypeError(exec, scope);
+        return throwVMToThisNumberError(exec, scope, exec->thisValue());
 
     // Perform ToInteger on the argument before remaining steps.
     int decimalPlacesInExponent;
@@ -445,7 +449,7 @@ EncodedJSValue JSC_HOST_CALL numberProtoFuncToFixed(ExecState* exec)
 
     double x;
     if (!toThisNumber(vm, exec->thisValue(), x))
-        return throwVMTypeError(exec, scope);
+        return throwVMToThisNumberError(exec, scope, exec->thisValue());
 
     // Get the argument. 
     int decimalPlaces;
@@ -482,7 +486,7 @@ EncodedJSValue JSC_HOST_CALL numberProtoFuncToPrecision(ExecState* exec)
 
     double x;
     if (!toThisNumber(vm, exec->thisValue(), x))
-        return throwVMTypeError(exec, scope);
+        return throwVMToThisNumberError(exec, scope, exec->thisValue());
 
     // Perform ToInteger on the argument before remaining steps.
     int significantFigures;
@@ -579,7 +583,7 @@ EncodedJSValue JSC_HOST_CALL numberProtoFuncToString(ExecState* state)
 
     double doubleValue;
     if (!toThisNumber(vm, state->thisValue(), doubleValue))
-        return throwVMTypeError(state, scope);
+        return throwVMToThisNumberError(state, scope, state->thisValue());
 
     auto radix = extractToStringRadixArgument(state, state->argument(0), scope);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
@@ -594,9 +598,17 @@ EncodedJSValue JSC_HOST_CALL numberProtoFuncToLocaleString(ExecState* exec)
 
     double x;
     if (!toThisNumber(vm, exec->thisValue(), x))
-        return throwVMTypeError(exec, scope);
+        return throwVMToThisNumberError(exec, scope, exec->thisValue());
 
+#if ENABLE(INTL)
+    JSGlobalObject* globalObject = exec->lexicalGlobalObject();
+    IntlNumberFormat* numberFormat = IntlNumberFormat::create(vm, globalObject->intlObject()->numberFormatStructure());
+    numberFormat->initializeNumberFormat(*exec, exec->argument(0), exec->argument(1));
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    RELEASE_AND_RETURN(scope, JSValue::encode(numberFormat->formatNumber(*exec, x)));
+#else
     return JSValue::encode(jsNumber(x).toString(exec));
+#endif
 }
 
 EncodedJSValue JSC_HOST_CALL numberProtoFuncValueOf(ExecState* exec)
@@ -607,7 +619,7 @@ EncodedJSValue JSC_HOST_CALL numberProtoFuncValueOf(ExecState* exec)
     double x;
     JSValue thisValue = exec->thisValue();
     if (!toThisNumber(vm, thisValue, x))
-        return throwVMTypeError(exec, scope, WTF::makeString("thisNumberValue called on incompatible ", asString(jsTypeStringForValue(exec, thisValue))->value(exec)));
+        return throwVMToThisNumberError(exec, scope, exec->thisValue());
     return JSValue::encode(jsNumber(x));
 }
 
index 4361988..161f89c 100644 (file)
@@ -29,6 +29,7 @@
 #include "Error.h"
 #include "FrameTracers.h"
 #include "InterpreterInlines.h"
+#include "IntlCollator.h"
 #include "IntlObject.h"
 #include "JITCodeInlines.h"
 #include "JSArray.h"
@@ -143,12 +144,11 @@ void StringPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject, JSStr
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("substring", stringProtoFuncSubstring, static_cast<unsigned>(PropertyAttribute::DontEnum), 2);
     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("toLowerCase", stringProtoFuncToLowerCase, static_cast<unsigned>(PropertyAttribute::DontEnum), 0, StringPrototypeToLowerCaseIntrinsic);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toUpperCase", stringProtoFuncToUpperCase, static_cast<unsigned>(PropertyAttribute::DontEnum), 0);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("localeCompare", stringProtoFuncLocaleCompare, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
 #if ENABLE(INTL)
-    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("localeCompare", stringPrototypeLocaleCompareCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLocaleLowerCase", stringProtoFuncToLocaleLowerCase, static_cast<unsigned>(PropertyAttribute::DontEnum), 0);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLocaleUpperCase", stringProtoFuncToLocaleUpperCase, static_cast<unsigned>(PropertyAttribute::DontEnum), 0);
 #else
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("localeCompare", stringProtoFuncLocaleCompare, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLocaleLowerCase", stringProtoFuncToLowerCase, static_cast<unsigned>(PropertyAttribute::DontEnum), 0);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLocaleUpperCase", stringProtoFuncToUpperCase, static_cast<unsigned>(PropertyAttribute::DontEnum), 0);
 #endif
@@ -1486,19 +1486,45 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec)
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState* exec)
 {
+    // 13.1.1 String.prototype.localeCompare (that [, locales [, options ]]) (ECMA-402 2.0)
+    // http://ecma-international.org/publications/standards/Ecma-402.htm
+
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
+    // 1. Let O be RequireObjectCoercible(this value).
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec, scope);
-    String s = thisValue.toWTFString(exec);
+        return throwVMTypeError(exec, scope, "String.prototype.localeCompare requires that |this| not be null or undefined"_s);
+
+    // 2. Let S be ToString(O).
+    // 3. ReturnIfAbrupt(S).
+    String string = thisValue.toWTFString(exec);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
-    JSValue a0 = exec->argument(0);
-    String str = a0.toWTFString(exec);
+    // 4. Let That be ToString(that).
+    // 5. ReturnIfAbrupt(That).
+    JSValue thatValue = exec->argument(0);
+    String that = thatValue.toWTFString(exec);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
-    return JSValue::encode(jsNumber(Collator().collate(s, str)));
+
+#if ENABLE(INTL)
+    JSGlobalObject* globalObject = exec->lexicalGlobalObject();
+    JSValue locales = exec->argument(1);
+    JSValue options = exec->argument(2);
+    IntlCollator* collator = nullptr;
+    if (locales.isUndefined() && options.isUndefined()) {
+        collator = globalObject->defaultCollator(exec);
+        RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    } else {
+        collator = IntlCollator::create(vm, globalObject->intlObject()->collatorStructure());
+        collator->initializeCollator(*exec, locales, options);
+        RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    }
+    RELEASE_AND_RETURN(scope, JSValue::encode(collator->compareStrings(*exec, string, that)));
+#else
+    return JSValue::encode(jsNumber(Collator().collate(string, that)));
+#endif
 }
 
 #if ENABLE(INTL)