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 68b25cc..5beaab2 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 0f9dee2..e73cf23 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 457eaab..f7ca4d4 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 a7addf9..4c9455f 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 6438d6d..5a128ed 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 fc8828e..48389cd 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 a25c160..aa7fb25 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 5b0e5f0..5912555 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 e683515..9379382 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 06f7e95..ece5fa5 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 0107b13..238172b 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 0a11078..8fe544f 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 7cdbeb4..b878158 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);
+}