Modernize Intl constructors; using InternalFunction::createSubclassStructure
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 15 May 2016 21:11:27 +0000 (21:11 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 15 May 2016 21:11:27 +0000 (21:11 +0000)
https://bugs.webkit.org/show_bug.cgi?id=157082

Reviewed by Darin Adler.

Previously, Intl constructors retrieve "prototype" to inherit the "new.target".
At that time, this mis-assumed that getDirect() always returns meaningful JS value.
Actually, it returns an empty value if a property does not exist.

Instead of fixing this assertion, we now use InternalFunction::createSubclassStructure
in Intl constructors. It is modern and preferable way since it can cache the derived
structures in InternalFunction.

This patch also cleans up the workaround in Intl.NumberFormat and Intl.DateTimeFormat.
Those code are largely duplicate. This is now extracted into
constructIntlInstanceWithWorkaroundForLegacyIntlConstructor. This clean up does not
have any behavior changes. They are already tested in LayoutTests/js/intl-datetimeformat
and LayoutTests/js/intl-numberformat.

* JavaScriptCore.xcodeproj/project.pbxproj:
* runtime/IntlCollator.cpp:
(JSC::IntlCollator::create):
* runtime/IntlCollator.h:
* runtime/IntlCollatorConstructor.cpp:
(JSC::constructIntlCollator):
(JSC::callIntlCollator):
* runtime/IntlDateTimeFormat.cpp:
(JSC::IntlDateTimeFormat::create):
* runtime/IntlDateTimeFormat.h:
* runtime/IntlDateTimeFormatConstructor.cpp:
(JSC::constructIntlDateTimeFormat):
(JSC::callIntlDateTimeFormat):
* runtime/IntlDateTimeFormatPrototype.cpp:
(JSC::IntlDateTimeFormatPrototypeGetterFormat):
(JSC::IntlDateTimeFormatPrototypeFuncResolvedOptions):
* runtime/IntlNumberFormat.cpp:
(JSC::IntlNumberFormat::create):
* runtime/IntlNumberFormat.h:
* runtime/IntlNumberFormatConstructor.cpp:
(JSC::constructIntlNumberFormat):
(JSC::callIntlNumberFormat):
* runtime/IntlNumberFormatPrototype.cpp:
(JSC::IntlNumberFormatPrototypeGetterFormat):
(JSC::IntlNumberFormatPrototypeFuncResolvedOptions):
* runtime/IntlObjectInlines.h: Added.
(JSC::constructIntlInstanceWithWorkaroundForLegacyIntlConstructor):
* tests/stress/intl-constructors-with-proxy.js: Added.
(shouldBe):
(throw.new.Error.Empty):
(throw.new.Error):
(shouldBe.Empty):

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

15 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/runtime/IntlCollator.cpp
Source/JavaScriptCore/runtime/IntlCollator.h
Source/JavaScriptCore/runtime/IntlCollatorConstructor.cpp
Source/JavaScriptCore/runtime/IntlDateTimeFormat.cpp
Source/JavaScriptCore/runtime/IntlDateTimeFormat.h
Source/JavaScriptCore/runtime/IntlDateTimeFormatConstructor.cpp
Source/JavaScriptCore/runtime/IntlDateTimeFormatPrototype.cpp
Source/JavaScriptCore/runtime/IntlNumberFormat.cpp
Source/JavaScriptCore/runtime/IntlNumberFormat.h
Source/JavaScriptCore/runtime/IntlNumberFormatConstructor.cpp
Source/JavaScriptCore/runtime/IntlNumberFormatPrototype.cpp
Source/JavaScriptCore/runtime/IntlObjectInlines.h [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/intl-constructors-with-proxy.js [new file with mode: 0644]

index 68b25cc28b0529953c7ef0ce85c96c2f399acff8..5beaab2e25f8e7a6d48610a7eab2c866b37c75d4 100644 (file)
@@ -1,3 +1,57 @@
+2016-05-15  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        Modernize Intl constructors; using InternalFunction::createSubclassStructure
+        https://bugs.webkit.org/show_bug.cgi?id=157082
+
+        Reviewed by Darin Adler.
+
+        Previously, Intl constructors retrieve "prototype" to inherit the "new.target".
+        At that time, this mis-assumed that getDirect() always returns meaningful JS value.
+        Actually, it returns an empty value if a property does not exist.
+
+        Instead of fixing this assertion, we now use InternalFunction::createSubclassStructure
+        in Intl constructors. It is modern and preferable way since it can cache the derived
+        structures in InternalFunction.
+
+        This patch also cleans up the workaround in Intl.NumberFormat and Intl.DateTimeFormat.
+        Those code are largely duplicate. This is now extracted into
+        constructIntlInstanceWithWorkaroundForLegacyIntlConstructor. This clean up does not
+        have any behavior changes. They are already tested in LayoutTests/js/intl-datetimeformat
+        and LayoutTests/js/intl-numberformat.
+
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * runtime/IntlCollator.cpp:
+        (JSC::IntlCollator::create):
+        * runtime/IntlCollator.h:
+        * runtime/IntlCollatorConstructor.cpp:
+        (JSC::constructIntlCollator):
+        (JSC::callIntlCollator):
+        * runtime/IntlDateTimeFormat.cpp:
+        (JSC::IntlDateTimeFormat::create):
+        * runtime/IntlDateTimeFormat.h:
+        * runtime/IntlDateTimeFormatConstructor.cpp:
+        (JSC::constructIntlDateTimeFormat):
+        (JSC::callIntlDateTimeFormat):
+        * runtime/IntlDateTimeFormatPrototype.cpp:
+        (JSC::IntlDateTimeFormatPrototypeGetterFormat):
+        (JSC::IntlDateTimeFormatPrototypeFuncResolvedOptions):
+        * runtime/IntlNumberFormat.cpp:
+        (JSC::IntlNumberFormat::create):
+        * runtime/IntlNumberFormat.h:
+        * runtime/IntlNumberFormatConstructor.cpp:
+        (JSC::constructIntlNumberFormat):
+        (JSC::callIntlNumberFormat):
+        * runtime/IntlNumberFormatPrototype.cpp:
+        (JSC::IntlNumberFormatPrototypeGetterFormat):
+        (JSC::IntlNumberFormatPrototypeFuncResolvedOptions):
+        * runtime/IntlObjectInlines.h: Added.
+        (JSC::constructIntlInstanceWithWorkaroundForLegacyIntlConstructor):
+        * tests/stress/intl-constructors-with-proxy.js: Added.
+        (shouldBe):
+        (throw.new.Error.Empty):
+        (throw.new.Error):
+        (shouldBe.Empty):
+
 2016-05-14  Joseph Pecoraro  <pecoraro@apple.com>
 
         Remove LegacyProfiler
index 0f9dee2b62d49470148fe00b4efc1d9609f8f78d..e73cf231422ac21982748fe84796505726c793d3 100644 (file)
                705B41B01A6E501E00716757 /* SymbolObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 705B41A81A6E501E00716757 /* SymbolObject.h */; settings = {ATTRIBUTES = (Private, ); }; };
                705B41B11A6E501E00716757 /* SymbolPrototype.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 705B41A91A6E501E00716757 /* SymbolPrototype.cpp */; };
                705B41B21A6E501E00716757 /* SymbolPrototype.h in Headers */ = {isa = PBXBuildFile; fileRef = 705B41AA1A6E501E00716757 /* SymbolPrototype.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               708EBE241CE8F35800453146 /* IntlObjectInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 708EBE231CE8F35000453146 /* IntlObjectInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7094C4DE1AE439530041A2EE /* BytecodeIntrinsicRegistry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7094C4DC1AE439530041A2EE /* BytecodeIntrinsicRegistry.cpp */; };
                7094C4DF1AE439530041A2EE /* BytecodeIntrinsicRegistry.h in Headers */ = {isa = PBXBuildFile; fileRef = 7094C4DD1AE439530041A2EE /* BytecodeIntrinsicRegistry.h */; settings = {ATTRIBUTES = (Private, ); }; };
                709FB8671AE335C60039D069 /* JSWeakSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 709FB8611AE335C60039D069 /* JSWeakSet.cpp */; };
                705B41A81A6E501E00716757 /* SymbolObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SymbolObject.h; sourceTree = "<group>"; };
                705B41A91A6E501E00716757 /* SymbolPrototype.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SymbolPrototype.cpp; sourceTree = "<group>"; };
                705B41AA1A6E501E00716757 /* SymbolPrototype.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SymbolPrototype.h; sourceTree = "<group>"; };
+               708EBE231CE8F35000453146 /* IntlObjectInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IntlObjectInlines.h; sourceTree = "<group>"; };
                7094C4DC1AE439530041A2EE /* BytecodeIntrinsicRegistry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BytecodeIntrinsicRegistry.cpp; sourceTree = "<group>"; };
                7094C4DD1AE439530041A2EE /* BytecodeIntrinsicRegistry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BytecodeIntrinsicRegistry.h; sourceTree = "<group>"; };
                709FB8611AE335C60039D069 /* JSWeakSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSWeakSet.cpp; sourceTree = "<group>"; };
                7EF6E0BB0EB7A1EC0079AFAF /* runtime */ = {
                        isa = PBXGroup;
                        children = (
+                               708EBE231CE8F35000453146 /* IntlObjectInlines.h */,
                                DCF3D5641CD29468003D5C65 /* LazyClassStructure.cpp */,
                                DCF3D5651CD29468003D5C65 /* LazyClassStructure.h */,
                                DCF3D5661CD29468003D5C65 /* LazyClassStructureInlines.h */,
                                0FE050191AA9091100D33B33 /* GenericArguments.h in Headers */,
                                0FE0501A1AA9091100D33B33 /* GenericArgumentsInlines.h in Headers */,
                                FE3A06C01C11041A00390FDD /* JITRightShiftGenerator.h in Headers */,
+                               708EBE241CE8F35800453146 /* IntlObjectInlines.h in Headers */,
                                0FE0501B1AA9091100D33B33 /* GenericOffset.h in Headers */,
                                0F2B66E017B6B5AB00A7AE3F /* GenericTypedArrayView.h in Headers */,
                                0F2B66E117B6B5AB00A7AE3F /* GenericTypedArrayViewInlines.h in Headers */,
index 457eaabe3cdd2ae558c0552368d7e3b5502bd2f1..f7ca4d415e53976d10ba6f860ceb58688af46faa 100644 (file)
@@ -56,9 +56,9 @@ void IntlCollator::UCollatorDeleter::operator()(UCollator* collator) const
         ucol_close(collator);
 }
 
-IntlCollator* IntlCollator::create(VM& vm, IntlCollatorConstructor* constructor)
+IntlCollator* IntlCollator::create(VM& vm, Structure* structure)
 {
-    IntlCollator* format = new (NotNull, allocateCell<IntlCollator>(vm.heap)) IntlCollator(vm, constructor->collatorStructure());
+    IntlCollator* format = new (NotNull, allocateCell<IntlCollator>(vm.heap)) IntlCollator(vm, structure);
     format->finishCreation(vm);
     return format;
 }
index a7addf94815646924680b6e1a5f19d15158d7ecb..4c9455f50bc3d79a3646177ad6e67b35acee2b98 100644 (file)
@@ -41,7 +41,7 @@ class IntlCollator : public JSDestructibleObject {
 public:
     typedef JSDestructibleObject Base;
 
-    static IntlCollator* create(VM&, IntlCollatorConstructor*);
+    static IntlCollator* create(VM&, Structure*);
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
 
     DECLARE_INFO;
index 6438d6d53a017c50f25c1ed1a5cce927b2133e7d..5a128ed697ac2fcb96d9b32a72bd275535af9449 100644 (file)
@@ -88,27 +88,16 @@ static EncodedJSValue JSC_HOST_CALL constructIntlCollator(ExecState* state)
 {
     // 10.1.2 Intl.Collator ([locales [, options]]) (ECMA-402 2.0)
     // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget.
-    JSValue newTarget = state->newTarget();
-    if (newTarget.isUndefined())
-        newTarget = state->callee();
-
     // 2. Let collator be OrdinaryCreateFromConstructor(newTarget, %CollatorPrototype%).
-    VM& vm = state->vm();
-    IntlCollator* collator = IntlCollator::create(vm, jsCast<IntlCollatorConstructor*>(state->callee()));
-    if (collator && !jsDynamicCast<IntlCollatorConstructor*>(newTarget)) {
-        JSValue proto = asObject(newTarget)->getDirect(vm, vm.propertyNames->prototype);
-        asObject(collator)->setPrototype(vm, state, proto);
-        if (vm.exception())
-            return JSValue::encode(JSValue());
-    }
-
     // 3. ReturnIfAbrupt(collator).
+    Structure* structure = InternalFunction::createSubclassStructure(state, state->newTarget(), jsCast<IntlCollatorConstructor*>(state->callee())->collatorStructure());
+    if (state->hadException())
+        return JSValue::encode(jsUndefined());
+    IntlCollator* collator = IntlCollator::create(state->vm(), structure);
     ASSERT(collator);
 
     // 4. Return InitializeCollator(collator, locales, options).
-    JSValue locales = state->argument(0);
-    JSValue options = state->argument(1);
-    collator->initializeCollator(*state, locales, options);
+    collator->initializeCollator(*state, state->argument(0), state->argument(1));
     return JSValue::encode(collator);
 }
 
@@ -118,17 +107,19 @@ static EncodedJSValue JSC_HOST_CALL callIntlCollator(ExecState* state)
     // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget.
     // NewTarget is always undefined when called as a function.
 
-    // 2. Let collator be OrdinaryCreateFromConstructor(newTarget, %CollatorPrototype%).
     VM& vm = state->vm();
-    IntlCollator* collator = IntlCollator::create(vm, jsCast<IntlCollatorConstructor*>(state->callee()));
+    IntlCollatorConstructor* callee = jsCast<IntlCollatorConstructor*>(state->callee());
 
+    // FIXME: Collator does not get the workaround for ECMA-402 1.0 compatibility.
+    // https://bugs.webkit.org/show_bug.cgi?id=153679
+
+    // 2. Let collator be OrdinaryCreateFromConstructor(newTarget, %CollatorPrototype%).
     // 3. ReturnIfAbrupt(collator).
+    IntlCollator* collator = IntlCollator::create(vm, callee->collatorStructure());
     ASSERT(collator);
 
     // 4. Return InitializeCollator(collator, locales, options).
-    JSValue locales = state->argument(0);
-    JSValue options = state->argument(1);
-    collator->initializeCollator(*state, locales, options);
+    collator->initializeCollator(*state, state->argument(0), state->argument(1));
     return JSValue::encode(collator);
 }
 
index fc8828e1e8f6b36a9cc4d767fc8996968f595ca3..48389cd3b254b8a39ec1051851a9cc54eb79cce9 100644 (file)
@@ -56,9 +56,9 @@ void IntlDateTimeFormat::UDateFormatDeleter::operator()(UDateFormat* dateFormat)
         udat_close(dateFormat);
 }
 
-IntlDateTimeFormat* IntlDateTimeFormat::create(VM& vm, IntlDateTimeFormatConstructor* constructor)
+IntlDateTimeFormat* IntlDateTimeFormat::create(VM& vm, Structure* structure)
 {
-    IntlDateTimeFormat* format = new (NotNull, allocateCell<IntlDateTimeFormat>(vm.heap)) IntlDateTimeFormat(vm, constructor->dateTimeFormatStructure());
+    IntlDateTimeFormat* format = new (NotNull, allocateCell<IntlDateTimeFormat>(vm.heap)) IntlDateTimeFormat(vm, structure);
     format->finishCreation(vm);
     return format;
 }
index a25c1605cef73706925483564a29f0c81debb914..aa7fb2535b66487529fe5ca4b019c5f2a41f2525 100644 (file)
@@ -40,7 +40,7 @@ class IntlDateTimeFormat : public JSDestructibleObject {
 public:
     typedef JSDestructibleObject Base;
 
-    static IntlDateTimeFormat* create(VM&, IntlDateTimeFormatConstructor*);
+    static IntlDateTimeFormat* create(VM&, Structure*);
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
 
     DECLARE_INFO;
index 5b0e5f070d1ab5b2e9a945598f9ff3824f6e1aca..591255587512602ab3758ba085b40a88b317d15a 100644 (file)
@@ -32,6 +32,7 @@
 #include "IntlDateTimeFormat.h"
 #include "IntlDateTimeFormatPrototype.h"
 #include "IntlObject.h"
+#include "IntlObjectInlines.h"
 #include "JSCJSValueInlines.h"
 #include "JSCellInlines.h"
 #include "Lookup.h"
@@ -87,27 +88,16 @@ static EncodedJSValue JSC_HOST_CALL constructIntlDateTimeFormat(ExecState* state
 {
     // 12.1.2 Intl.DateTimeFormat ([locales [, options]]) (ECMA-402 2.0)
     // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget.
-    JSValue newTarget = state->newTarget();
-    if (newTarget.isUndefined())
-        newTarget = state->callee();
-
     // 2. Let dateTimeFormat be OrdinaryCreateFromConstructor(newTarget, %DateTimeFormatPrototype%).
-    VM& vm = state->vm();
-    IntlDateTimeFormat* dateTimeFormat = IntlDateTimeFormat::create(vm, jsCast<IntlDateTimeFormatConstructor*>(state->callee()));
-    if (dateTimeFormat && !jsDynamicCast<IntlDateTimeFormatConstructor*>(newTarget)) {
-        JSValue proto = asObject(newTarget)->getDirect(vm, vm.propertyNames->prototype);
-        asObject(dateTimeFormat)->setPrototype(vm, state, proto);
-        if (vm.exception())
-            return JSValue::encode(JSValue());
-    }
-
     // 3. ReturnIfAbrupt(dateTimeFormat).
+    Structure* structure = InternalFunction::createSubclassStructure(state, state->newTarget(), jsCast<IntlDateTimeFormatConstructor*>(state->callee())->dateTimeFormatStructure());
+    if (state->hadException())
+        return JSValue::encode(jsUndefined());
+    IntlDateTimeFormat* dateTimeFormat = IntlDateTimeFormat::create(state->vm(), structure);
     ASSERT(dateTimeFormat);
 
     // 4. Return InitializeDateTimeFormat(dateTimeFormat, locales, options).
-    JSValue locales = state->argument(0);
-    JSValue options = state->argument(1);
-    dateTimeFormat->initializeDateTimeFormat(*state, locales, options);
+    dateTimeFormat->initializeDateTimeFormat(*state, state->argument(0), state->argument(1));
     return JSValue::encode(dateTimeFormat);
 }
 
@@ -117,36 +107,20 @@ static EncodedJSValue JSC_HOST_CALL callIntlDateTimeFormat(ExecState* state)
     // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget.
     // NewTarget is always undefined when called as a function.
 
-    VM& vm = state->vm();
     IntlDateTimeFormatConstructor* callee = jsCast<IntlDateTimeFormatConstructor*>(state->callee());
 
     // FIXME: Workaround to provide compatibility with ECMA-402 1.0 call/apply patterns.
-    JSValue thisValue = state->thisValue();
-    IntlDateTimeFormat* dateTimeFormat = jsDynamicCast<IntlDateTimeFormat*>(thisValue);
-    if (!dateTimeFormat) {
-        JSValue prototype = callee->getDirect(vm, vm.propertyNames->prototype);
-        if (JSObject::defaultHasInstance(state, thisValue, prototype)) {
-            JSObject* thisObject = thisValue.toObject(state);
-            if (state->hadException())
-                return JSValue::encode(jsUndefined());
-
-            dateTimeFormat = IntlDateTimeFormat::create(vm, callee);
-            dateTimeFormat->initializeDateTimeFormat(*state, state->argument(0), state->argument(1));
-            if (state->hadException())
-                return JSValue::encode(jsUndefined());
-
-            thisObject->putDirect(vm, vm.propertyNames->intlSubstituteValuePrivateName, dateTimeFormat);
-            return JSValue::encode(thisValue);
-        }
-    }
-
-    // 2. Let dateTimeFormat be OrdinaryCreateFromConstructor(newTarget, %DateTimeFormatPrototype%).
-    // 3. ReturnIfAbrupt(dateTimeFormat).
-    dateTimeFormat = IntlDateTimeFormat::create(vm, callee);
-
-    // 4. Return InitializeDateTimeFormat(dateTimeFormat, locales, options).
-    dateTimeFormat->initializeDateTimeFormat(*state, state->argument(0), state->argument(1));
-    return JSValue::encode(dateTimeFormat);
+    // https://bugs.webkit.org/show_bug.cgi?id=153679
+    return JSValue::encode(constructIntlInstanceWithWorkaroundForLegacyIntlConstructor<IntlDateTimeFormat>(*state, state->thisValue(), callee, [&] (VM& vm) {
+        // 2. Let dateTimeFormat be OrdinaryCreateFromConstructor(newTarget, %DateTimeFormatPrototype%).
+        // 3. ReturnIfAbrupt(dateTimeFormat).
+        IntlDateTimeFormat* dateTimeFormat = IntlDateTimeFormat::create(vm, callee->dateTimeFormatStructure());
+        ASSERT(dateTimeFormat);
+
+        // 4. Return InitializeDateTimeFormat(dateTimeFormat, locales, options).
+        dateTimeFormat->initializeDateTimeFormat(*state, state->argument(0), state->argument(1));
+        return dateTimeFormat;
+    }));
 }
 
 ConstructType IntlDateTimeFormatConstructor::getConstructData(JSCell*, ConstructData& constructData)
index e683515ef54930d8155725e637a238430d87616a..93793827c89efd32e669b26af3d0b6298cf87ea0 100644 (file)
@@ -119,6 +119,7 @@ EncodedJSValue JSC_HOST_CALL IntlDateTimeFormatPrototypeGetterFormat(ExecState*
     IntlDateTimeFormat* dtf = jsDynamicCast<IntlDateTimeFormat*>(state->thisValue());
 
     // FIXME: Workaround to provide compatibility with ECMA-402 1.0 call/apply patterns.
+    // https://bugs.webkit.org/show_bug.cgi?id=153679
     if (!dtf)
         dtf = jsDynamicCast<IntlDateTimeFormat*>(state->thisValue().get(state, state->vm().propertyNames->intlSubstituteValuePrivateName));
 
@@ -155,6 +156,7 @@ EncodedJSValue JSC_HOST_CALL IntlDateTimeFormatPrototypeFuncResolvedOptions(Exec
     IntlDateTimeFormat* dateTimeFormat = jsDynamicCast<IntlDateTimeFormat*>(state->thisValue());
 
     // FIXME: Workaround to provide compatibility with ECMA-402 1.0 call/apply patterns.
+    // https://bugs.webkit.org/show_bug.cgi?id=153679
     if (!dateTimeFormat)
         dateTimeFormat = jsDynamicCast<IntlDateTimeFormat*>(state->thisValue().get(state, state->vm().propertyNames->intlSubstituteValuePrivateName));
 
index 06f7e95f4cc902f386a1c7c0f280d7b82f81ec60..ece5fa5b011bba40332e7d5119b1adf7e7a80190 100644 (file)
@@ -52,9 +52,9 @@ void IntlNumberFormat::UNumberFormatDeleter::operator()(UNumberFormat* numberFor
         unum_close(numberFormat);
 }
 
-IntlNumberFormat* IntlNumberFormat::create(VM& vm, IntlNumberFormatConstructor* constructor)
+IntlNumberFormat* IntlNumberFormat::create(VM& vm, Structure* structure)
 {
-    IntlNumberFormat* format = new (NotNull, allocateCell<IntlNumberFormat>(vm.heap)) IntlNumberFormat(vm, constructor->numberFormatStructure());
+    IntlNumberFormat* format = new (NotNull, allocateCell<IntlNumberFormat>(vm.heap)) IntlNumberFormat(vm, structure);
     format->finishCreation(vm);
     return format;
 }
index 0107b13799560005255a7b514f9e49130efa6469..238172b878d941e9df5d8e122f05c4dce13ad6ee 100644 (file)
@@ -40,7 +40,7 @@ class IntlNumberFormat : public JSDestructibleObject {
 public:
     typedef JSDestructibleObject Base;
 
-    static IntlNumberFormat* create(VM&, IntlNumberFormatConstructor*);
+    static IntlNumberFormat* create(VM&, Structure*);
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
 
     DECLARE_INFO;
index 0a11078fe564489ae320ff304cf4e9cf59546178..8fe544f2ab0a3d24064207dbaecc6e6c22ea9941 100644 (file)
@@ -32,6 +32,7 @@
 #include "IntlNumberFormat.h"
 #include "IntlNumberFormatPrototype.h"
 #include "IntlObject.h"
+#include "IntlObjectInlines.h"
 #include "JSCJSValueInlines.h"
 #include "JSCellInlines.h"
 #include "Lookup.h"
@@ -87,27 +88,16 @@ static EncodedJSValue JSC_HOST_CALL constructIntlNumberFormat(ExecState* state)
 {
     // 11.1.2 Intl.NumberFormat ([locales [, options]]) (ECMA-402 2.0)
     // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget.
-    JSValue newTarget = state->newTarget();
-    if (newTarget.isUndefined())
-        newTarget = state->callee();
-
     // 2. Let numberFormat be OrdinaryCreateFromConstructor(newTarget, %NumberFormatPrototype%).
-    VM& vm = state->vm();
-    IntlNumberFormat* numberFormat = IntlNumberFormat::create(vm, jsCast<IntlNumberFormatConstructor*>(state->callee()));
-    if (numberFormat && !jsDynamicCast<IntlNumberFormatConstructor*>(newTarget)) {
-        JSValue proto = asObject(newTarget)->getDirect(vm, vm.propertyNames->prototype);
-        asObject(numberFormat)->setPrototype(vm, state, proto);
-        if (vm.exception())
-            return JSValue::encode(JSValue());
-    }
-
     // 3. ReturnIfAbrupt(numberFormat).
+    Structure* structure = InternalFunction::createSubclassStructure(state, state->newTarget(), jsCast<IntlNumberFormatConstructor*>(state->callee())->numberFormatStructure());
+    if (state->hadException())
+        return JSValue::encode(jsUndefined());
+    IntlNumberFormat* numberFormat = IntlNumberFormat::create(state->vm(), structure);
     ASSERT(numberFormat);
 
     // 4. Return InitializeNumberFormat(numberFormat, locales, options).
-    JSValue locales = state->argument(0);
-    JSValue options = state->argument(1);
-    numberFormat->initializeNumberFormat(*state, locales, options);
+    numberFormat->initializeNumberFormat(*state, state->argument(0), state->argument(1));
     return JSValue::encode(numberFormat);
 }
 
@@ -117,36 +107,20 @@ static EncodedJSValue JSC_HOST_CALL callIntlNumberFormat(ExecState* state)
     // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget.
     // NewTarget is always undefined when called as a function.
 
-    // FIXME: Workaround to provide compatibility with ECMA-402 1.0 call/apply patterns.
-    VM& vm = state->vm();
     IntlNumberFormatConstructor* callee = jsCast<IntlNumberFormatConstructor*>(state->callee());
 
-    JSValue thisValue = state->thisValue();
-    IntlNumberFormat* numberFormat = jsDynamicCast<IntlNumberFormat*>(thisValue);
-    if (!numberFormat) {
-        JSValue prototype = callee->getDirect(vm, vm.propertyNames->prototype);
-        if (JSObject::defaultHasInstance(state, thisValue, prototype)) {
-            JSObject* thisObject = thisValue.toObject(state);
-            if (state->hadException())
-                return JSValue::encode(jsUndefined());
-
-            numberFormat = IntlNumberFormat::create(vm, callee);
-            numberFormat->initializeNumberFormat(*state, state->argument(0), state->argument(1));
-            if (state->hadException())
-                return JSValue::encode(jsUndefined());
-
-            thisObject->putDirect(vm, vm.propertyNames->intlSubstituteValuePrivateName, numberFormat);
-            return JSValue::encode(thisValue);
-        }
-    }
-
-    // 2. Let numberFormat be OrdinaryCreateFromConstructor(newTarget, %NumberFormatPrototype%).
-    // 3. ReturnIfAbrupt(numberFormat).
-    numberFormat = IntlNumberFormat::create(vm, callee);
-
-    // 4. Return InitializeNumberFormat(numberFormat, locales, options).
-    numberFormat->initializeNumberFormat(*state, state->argument(0), state->argument(1));
-    return JSValue::encode(numberFormat);
+    // FIXME: Workaround to provide compatibility with ECMA-402 1.0 call/apply patterns.
+    // https://bugs.webkit.org/show_bug.cgi?id=153679
+    return JSValue::encode(constructIntlInstanceWithWorkaroundForLegacyIntlConstructor<IntlNumberFormat>(*state, state->thisValue(), callee, [&] (VM& vm) {
+        // 2. Let numberFormat be OrdinaryCreateFromConstructor(newTarget, %NumberFormatPrototype%).
+        // 3. ReturnIfAbrupt(numberFormat).
+        IntlNumberFormat* numberFormat = IntlNumberFormat::create(vm, callee->numberFormatStructure());
+        ASSERT(numberFormat);
+
+        // 4. Return InitializeNumberFormat(numberFormat, locales, options).
+        numberFormat->initializeNumberFormat(*state, state->argument(0), state->argument(1));
+        return numberFormat;
+    }));
 }
 
 ConstructType IntlNumberFormatConstructor::getConstructData(JSCell*, ConstructData& constructData)
index 7cdbeb4a29ef1b91b29277a6a186e8b4d9ee6139..b878158190708c61e7c282a4766c20091963e563 100644 (file)
@@ -108,6 +108,7 @@ EncodedJSValue JSC_HOST_CALL IntlNumberFormatPrototypeGetterFormat(ExecState* st
     IntlNumberFormat* nf = jsDynamicCast<IntlNumberFormat*>(state->thisValue());
 
     // FIXME: Workaround to provide compatibility with ECMA-402 1.0 call/apply patterns.
+    // https://bugs.webkit.org/show_bug.cgi?id=153679
     if (!nf)
         nf = jsDynamicCast<IntlNumberFormat*>(state->thisValue().get(state, state->vm().propertyNames->intlSubstituteValuePrivateName));
 
@@ -143,6 +144,7 @@ EncodedJSValue JSC_HOST_CALL IntlNumberFormatPrototypeFuncResolvedOptions(ExecSt
     IntlNumberFormat* numberFormat = jsDynamicCast<IntlNumberFormat*>(state->thisValue());
 
     // FIXME: Workaround to provide compatibility with ECMA-402 1.0 call/apply patterns.
+    // https://bugs.webkit.org/show_bug.cgi?id=153679
     if (!numberFormat)
         numberFormat = jsDynamicCast<IntlNumberFormat*>(state->thisValue().get(state, state->vm().propertyNames->intlSubstituteValuePrivateName));
 
diff --git a/Source/JavaScriptCore/runtime/IntlObjectInlines.h b/Source/JavaScriptCore/runtime/IntlObjectInlines.h
new file mode 100644 (file)
index 0000000..129d985
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 Yusuke Suzuki <yusuke.suzuki@sslab.ics.keio.ac.jp>
+ *
+ * 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. AND ITS CONTRIBUTORS ``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 ITS 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.
+ */
+
+#pragma once
+
+#if ENABLE(INTL)
+
+#include "IntlObject.h"
+#include "JSObject.h"
+
+namespace JSC {
+
+template<typename IntlInstance, typename Constructor, typename Factory>
+JSValue constructIntlInstanceWithWorkaroundForLegacyIntlConstructor(ExecState& state, JSValue thisValue, Constructor* callee, Factory factory)
+{
+    // FIXME: Workaround to provide compatibility with ECMA-402 1.0 call/apply patterns.
+    // https://bugs.webkit.org/show_bug.cgi?id=153679
+    VM& vm = state.vm();
+    if (!jsDynamicCast<IntlInstance*>(thisValue)) {
+        JSValue prototype = callee->getDirect(vm, vm.propertyNames->prototype);
+        if (JSObject::defaultHasInstance(&state, thisValue, prototype)) {
+            JSObject* thisObject = thisValue.toObject(&state);
+            if (state.hadException())
+                return jsUndefined();
+
+            IntlInstance* instance = factory(vm);
+            if (state.hadException())
+                return jsUndefined();
+
+            thisObject->putDirect(vm, vm.propertyNames->intlSubstituteValuePrivateName, instance);
+            return thisObject;
+        }
+    }
+
+    return factory(vm);
+}
+
+} // namespace JSC
+
+#endif // ENABLE(INTL)
diff --git a/Source/JavaScriptCore/tests/stress/intl-constructors-with-proxy.js b/Source/JavaScriptCore/tests/stress/intl-constructors-with-proxy.js
new file mode 100644 (file)
index 0000000..c9b78ed
--- /dev/null
@@ -0,0 +1,36 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+{
+    function Empty() {
+    }
+    let proxy = new Proxy(Empty, {});
+
+    shouldBe(Reflect.construct(Intl.Collator, [], proxy) instanceof Empty, true);
+    shouldBe(Reflect.construct(Intl.Collator, [], proxy).__proto__, Empty.prototype);
+
+    shouldBe(Reflect.construct(Intl.NumberFormat, [], proxy) instanceof Empty, true);
+    shouldBe(Reflect.construct(Intl.NumberFormat, [], proxy).__proto__, Empty.prototype);
+
+    shouldBe(Reflect.construct(Intl.DateTimeFormat, [], proxy) instanceof Empty, true);
+    shouldBe(Reflect.construct(Intl.DateTimeFormat, [], proxy).__proto__, Empty.prototype);
+}
+
+{
+    function Empty() {
+    }
+    Empty.prototype = null;
+
+    let proxy = new Proxy(Empty, {});
+
+    shouldBe(Reflect.construct(Intl.Collator, [], proxy) instanceof Intl.Collator, true);
+    shouldBe(Reflect.construct(Intl.Collator, [], proxy).__proto__, Intl.Collator.prototype);
+
+    shouldBe(Reflect.construct(Intl.NumberFormat, [], proxy) instanceof Intl.NumberFormat, true);
+    shouldBe(Reflect.construct(Intl.NumberFormat, [], proxy).__proto__, Intl.NumberFormat.prototype);
+
+    shouldBe(Reflect.construct(Intl.DateTimeFormat, [], proxy) instanceof Intl.DateTimeFormat, true);
+    shouldBe(Reflect.construct(Intl.DateTimeFormat, [], proxy).__proto__, Intl.DateTimeFormat.prototype);
+}