[INTL] Use @thisNumberValue instead of `instanceof @Number`
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 19 Apr 2016 13:34:02 +0000 (13:34 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 19 Apr 2016 13:34:02 +0000 (13:34 +0000)
https://bugs.webkit.org/show_bug.cgi?id=156680

Reviewed by Saam Barati.

Source/JavaScriptCore:

Use @thisNumberValue instead of `instanceof @Number`.
`instanceof @Number` is not enough;
For example, given 2 realms, the object created in one realm does not
inherit the Number of another realm.
Another example is that the object which does not inherit Number.

```
var number = new Number(42);
number.__proto__ = null;
```

* builtins/NumberPrototype.js:
(toLocaleString):
* runtime/CommonIdentifiers.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
* runtime/NumberPrototype.cpp:
(JSC::numberProtoFuncValueOf):
* runtime/NumberPrototype.h:
* tests/stress/number-to-locale-string-should-accept-strange-number-objects.js: Added.
(shouldBe):

LayoutTests:

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

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

LayoutTests/ChangeLog
LayoutTests/js/number-toLocaleString-expected.txt
LayoutTests/js/script-tests/number-toLocaleString.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/builtins/NumberPrototype.js
Source/JavaScriptCore/runtime/CommonIdentifiers.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/NumberPrototype.cpp
Source/JavaScriptCore/runtime/NumberPrototype.h
Source/JavaScriptCore/tests/stress/number-to-locale-string-should-accept-strange-number-objects.js [new file with mode: 0644]

index 47c3082..46950dd 100644 (file)
@@ -1,3 +1,13 @@
+2016-04-19  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [INTL] Use @thisNumberValue instead of `instanceof @Number`
+        https://bugs.webkit.org/show_bug.cgi?id=156680
+
+        Reviewed by Saam Barati.
+
+        * js/number-toLocaleString-expected.txt:
+        * js/script-tests/number-toLocaleString.js:
+
 2016-04-19  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r199712.
index 4b0e823..bfc9d0a 100644 (file)
@@ -11,12 +11,12 @@ PASS Number.prototype.toLocaleString.call(0) did not throw exception.
 PASS Number.prototype.toLocaleString.call(NaN) did not throw exception.
 PASS Number.prototype.toLocaleString.call(Infinity) did not throw exception.
 PASS Number.prototype.toLocaleString.call(new Number) did not throw exception.
-PASS Number.prototype.toLocaleString.call() threw exception TypeError: Number.prototype.toLocaleString called on incompatible undefined.
-PASS Number.prototype.toLocaleString.call(undefined) threw exception TypeError: Number.prototype.toLocaleString called on incompatible undefined.
-PASS Number.prototype.toLocaleString.call(null) threw exception TypeError: Number.prototype.toLocaleString called on incompatible object.
-PASS Number.prototype.toLocaleString.call('1') threw exception TypeError: Number.prototype.toLocaleString called on incompatible string.
-PASS Number.prototype.toLocaleString.call([]) threw exception TypeError: Number.prototype.toLocaleString called on incompatible object.
-PASS Number.prototype.toLocaleString.call(Symbol()) threw exception TypeError: Number.prototype.toLocaleString called on incompatible symbol.
+PASS Number.prototype.toLocaleString.call() threw exception TypeError: thisNumberValue called on incompatible undefined.
+PASS Number.prototype.toLocaleString.call(undefined) threw exception TypeError: thisNumberValue called on incompatible undefined.
+PASS Number.prototype.toLocaleString.call(null) threw exception TypeError: thisNumberValue called on incompatible object.
+PASS Number.prototype.toLocaleString.call('1') threw exception TypeError: thisNumberValue called on incompatible string.
+PASS Number.prototype.toLocaleString.call([]) threw exception TypeError: thisNumberValue called on incompatible object.
+PASS Number.prototype.toLocaleString.call(Symbol()) threw exception TypeError: thisNumberValue called on incompatible symbol.
 PASS (0).toLocaleString() is "0"
 PASS new Number(1).toLocaleString() is "1"
 PASS (0).toLocaleString('i') threw exception RangeError: invalid language tag: i.
index 382b7cb..cc3bb12 100644 (file)
@@ -11,12 +11,12 @@ shouldNotThrow("Number.prototype.toLocaleString.call(0)");
 shouldNotThrow("Number.prototype.toLocaleString.call(NaN)");
 shouldNotThrow("Number.prototype.toLocaleString.call(Infinity)");
 shouldNotThrow("Number.prototype.toLocaleString.call(new Number)");
-shouldThrow("Number.prototype.toLocaleString.call()", "'TypeError: Number.prototype.toLocaleString called on incompatible undefined'");
-shouldThrow("Number.prototype.toLocaleString.call(undefined)", "'TypeError: Number.prototype.toLocaleString called on incompatible undefined'");
-shouldThrow("Number.prototype.toLocaleString.call(null)", "'TypeError: Number.prototype.toLocaleString called on incompatible object'");
-shouldThrow("Number.prototype.toLocaleString.call('1')", "'TypeError: Number.prototype.toLocaleString called on incompatible string'");
-shouldThrow("Number.prototype.toLocaleString.call([])", "'TypeError: Number.prototype.toLocaleString called on incompatible object'");
-shouldThrow("Number.prototype.toLocaleString.call(Symbol())", "'TypeError: Number.prototype.toLocaleString called on incompatible symbol'");
+shouldThrow("Number.prototype.toLocaleString.call()", "'TypeError: thisNumberValue called on incompatible undefined'");
+shouldThrow("Number.prototype.toLocaleString.call(undefined)", "'TypeError: thisNumberValue called on incompatible undefined'");
+shouldThrow("Number.prototype.toLocaleString.call(null)", "'TypeError: thisNumberValue called on incompatible object'");
+shouldThrow("Number.prototype.toLocaleString.call('1')", "'TypeError: thisNumberValue called on incompatible string'");
+shouldThrow("Number.prototype.toLocaleString.call([])", "'TypeError: thisNumberValue called on incompatible object'");
+shouldThrow("Number.prototype.toLocaleString.call(Symbol())", "'TypeError: thisNumberValue called on incompatible symbol'");
 
 shouldBeEqualToString("(0).toLocaleString()", "0");
 shouldBeEqualToString("new Number(1).toLocaleString()", "1");
index e127c83..bd87ad6 100644 (file)
@@ -1,3 +1,32 @@
+2016-04-19  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [INTL] Use @thisNumberValue instead of `instanceof @Number`
+        https://bugs.webkit.org/show_bug.cgi?id=156680
+
+        Reviewed by Saam Barati.
+
+        Use @thisNumberValue instead of `instanceof @Number`.
+        `instanceof @Number` is not enough;
+        For example, given 2 realms, the object created in one realm does not
+        inherit the Number of another realm.
+        Another example is that the object which does not inherit Number.
+
+        ```
+        var number = new Number(42);
+        number.__proto__ = null;
+        ```
+
+        * builtins/NumberPrototype.js:
+        (toLocaleString):
+        * runtime/CommonIdentifiers.h:
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init):
+        * runtime/NumberPrototype.cpp:
+        (JSC::numberProtoFuncValueOf):
+        * runtime/NumberPrototype.h:
+        * tests/stress/number-to-locale-string-should-accept-strange-number-objects.js: Added.
+        (shouldBe):
+
 2016-04-19  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r199712.
index 49a1a8b..43790ad 100644 (file)
@@ -34,9 +34,7 @@ function toLocaleString(/* locales, options */)
 
     // 1. Let x be thisNumberValue(this value).
     // 2. ReturnIfAbrupt(x).
-    var number = this;
-    if (!(typeof number === "number" || number instanceof @Number))
-        throw new @TypeError("Number.prototype.toLocaleString called on incompatible " + typeof number);
+    var number = @thisNumberValue.@call(this);
 
     // 3. Let numberFormat be Construct(%NumberFormat%, Ā«locales, optionsĀ»).
     // 4. ReturnIfAbrupt(numberFormat).
index 72fe164..7403aed 100644 (file)
     macro(NumberFormat) \
     macro(intlSubstituteValue) \
     macro(thisTimeValue) \
+    macro(thisNumberValue) \
     macro(newTargetLocal) \
     macro(derivedConstructor) \
     macro(isBoundFunction) \
index dfa5d85..2d6e53e 100644 (file)
@@ -540,6 +540,7 @@ 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);
     JSFunction* privateFuncIsArrayConstructor = JSFunction::create(vm, this, 0, String(), arrayConstructorPrivateFuncIsArrayConstructor, IsArrayConstructorIntrinsic);
     JSFunction* privateFuncIsJSArray = JSFunction::create(vm, this, 0, String(), arrayProtoPrivateFuncIsJSArray, IsJSArrayIntrinsic);
     JSFunction* privateFuncConcatMemcpy = JSFunction::create(vm, this, 0, String(), arrayProtoPrivateFuncConcatMemcpy);
@@ -608,6 +609,7 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
         GlobalPropertyInfo(vm.propertyNames->MapPrivateName, mapConstructor, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().generatorResumePrivateName(), JSFunction::createBuiltinFunction(vm, generatorPrototypeGeneratorResumeCodeGenerator(vm), this), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().thisTimeValuePrivateName(), privateFuncThisTimeValue, DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->builtinNames().thisNumberValuePrivateName(), privateFuncThisNumberValue, DontEnum | DontDelete | ReadOnly),
 #if ENABLE(INTL)
         GlobalPropertyInfo(vm.propertyNames->builtinNames().CollatorPrivateName(), intl->getDirect(vm, vm.propertyNames->Collator), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().DateTimeFormatPrivateName(), intl->getDirect(vm, vm.propertyNames->DateTimeFormat), DontEnum | DontDelete | ReadOnly),
index 592932d..c255810 100644 (file)
@@ -45,7 +45,6 @@ namespace JSC {
 
 static EncodedJSValue JSC_HOST_CALL numberProtoFuncToString(ExecState*);
 static EncodedJSValue JSC_HOST_CALL numberProtoFuncToLocaleString(ExecState*);
-static EncodedJSValue JSC_HOST_CALL numberProtoFuncValueOf(ExecState*);
 static EncodedJSValue JSC_HOST_CALL numberProtoFuncToFixed(ExecState*);
 static EncodedJSValue JSC_HOST_CALL numberProtoFuncToExponential(ExecState*);
 static EncodedJSValue JSC_HOST_CALL numberProtoFuncToPrecision(ExecState*);
@@ -528,8 +527,9 @@ EncodedJSValue JSC_HOST_CALL numberProtoFuncToLocaleString(ExecState* exec)
 EncodedJSValue JSC_HOST_CALL numberProtoFuncValueOf(ExecState* exec)
 {
     double x;
-    if (!toThisNumber(exec->thisValue(), x))
-        return throwVMTypeError(exec);
+    JSValue thisValue = exec->thisValue();
+    if (!toThisNumber(thisValue, x))
+        return throwVMTypeError(exec, WTF::makeString("thisNumberValue called on incompatible ", jsCast<JSString*>(jsTypeStringForValue(exec, thisValue))->value(exec)));
     return JSValue::encode(jsNumber(x));
 }
 
index 45acd8f..d8f9a68 100644 (file)
@@ -52,6 +52,8 @@ private:
     static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
 };
 
+EncodedJSValue JSC_HOST_CALL numberProtoFuncValueOf(ExecState*);
+
 } // namespace JSC
 
 #endif // NumberPrototype_h
diff --git a/Source/JavaScriptCore/tests/stress/number-to-locale-string-should-accept-strange-number-objects.js b/Source/JavaScriptCore/tests/stress/number-to-locale-string-should-accept-strange-number-objects.js
new file mode 100644 (file)
index 0000000..aa99cc4
--- /dev/null
@@ -0,0 +1,11 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+var otherRealm = createGlobalObject();
+shouldBe(otherRealm.Number.prototype.toLocaleString.call(new Number(42)), `42`)
+
+var numberObject = new Number(42);
+numberObject.__proto__ = null;
+shouldBe(Number.prototype.toLocaleString.call(numberObject), `42`)