Bug 1175173 - Don't box primitive this values in {Object, Array}.prototype.toLocaleString. r=till
authorTom Schuster <evilpies@gmail.com>
Thu, 13 Aug 2015 23:19:50 +0200
changeset 257732 47e0a4c1d10980f7a61ce25341134a8716d418d3
parent 257731 26b37ffc80f3c6b263990803364762f3b8a35de5
child 257733 283c4c10df55bd006d6377fe8786525e45e9d0e8
push id29226
push userryanvm@gmail.com
push dateFri, 14 Aug 2015 13:01:14 +0000
treeherdermozilla-central@1b2402247429 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill
bugs1175173
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1175173 - Don't box primitive this values in {Object, Array}.prototype.toLocaleString. r=till
js/src/builtin/Object.cpp
js/src/builtin/Object.js
js/src/jsarray.cpp
js/src/tests/ecma_6/Array/toLocaleString.js
js/src/tests/ecma_6/Object/toLocaleString.js
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -356,33 +356,16 @@ js::obj_toString(JSContext* cx, unsigned
     /* Steps 4-5. */
     JSString* str = JS_BasicObjectToString(cx, obj);
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
 
-/* ES5 15.2.4.3. */
-static bool
-obj_toLocaleString(JSContext* cx, unsigned argc, Value* vp)
-{
-    JS_CHECK_RECURSION(cx, return false);
-
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    /* Step 1. */
-    RootedObject obj(cx, ToObject(cx, args.thisv()));
-    if (!obj)
-        return false;
-
-    /* Steps 2-4. */
-    RootedId id(cx, NameToId(cx->names().toString));
-    return obj->callMethod(cx, id, 0, nullptr, args.rval());
-}
 
 bool
 js::obj_valueOf(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
@@ -994,17 +977,17 @@ ProtoSetter(JSContext* cx, unsigned argc
     return true;
 }
 
 static const JSFunctionSpec object_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str,             obj_toSource,                0,0),
 #endif
     JS_FN(js_toString_str,             obj_toString,                0,0),
-    JS_FN(js_toLocaleString_str,       obj_toLocaleString,          0,0),
+    JS_SELF_HOSTED_FN(js_toLocaleString_str, "Object_toLocaleString", 0,JSPROP_DEFINE_LATE),
     JS_FN(js_valueOf_str,              obj_valueOf,                 0,0),
 #if JS_HAS_OBJ_WATCHPOINT
     JS_FN(js_watch_str,                obj_watch,                   2,0),
     JS_FN(js_unwatch_str,              obj_unwatch,                 1,0),
 #endif
     JS_FN(js_hasOwnProperty_str,       obj_hasOwnProperty,          1,0),
     JS_FN(js_isPrototypeOf_str,        obj_isPrototypeOf,           1,0),
     JS_FN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable,    1,0),
--- a/js/src/builtin/Object.js
+++ b/js/src/builtin/Object.js
@@ -46,16 +46,25 @@ function ObjectGetPrototypeOf(obj) {
     return std_Reflect_getPrototypeOf(ToObject(obj));
 }
 
 /* ES6 draft rev 32 (2015 Feb 2) 19.1.2.11. */
 function ObjectIsExtensible(obj) {
     return IsObject(obj) && std_Reflect_isExtensible(obj);
 }
 
+/* ES2015 19.1.3.5 Object.prototype.toLocaleString */
+function Object_toLocaleString() {
+    // Step 1.
+    var O = this;
+
+    // Step 2.
+    return O.toString();
+}
+
 function ObjectDefineSetter(name, setter) {
     var object;
     if (this === null || this === undefined)
         object = global;
     else
         object = ToObject(this);
 
     if (!IsCallable(setter))
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1016,21 +1016,20 @@ ArrayJoinKernel(JSContext* cx, Separator
             if (!CheckForInterrupt(cx))
                 return false;
 
             bool hole;
             if (!GetElement(cx, obj, i, &hole, &v))
                 return false;
             if (!hole && !v.isNullOrUndefined()) {
                 if (Locale) {
-                    JSObject* robj = ToObject(cx, v);
-                    if (!robj)
+                    RootedValue fun(cx);
+                    if (!GetProperty(cx, v, cx->names().toLocaleString, &fun))
                         return false;
-                    RootedId id(cx, NameToId(cx->names().toLocaleString));
-                    if (!robj->callMethod(cx, id, 0, nullptr, &v))
+                    if (!Invoke(cx, v, fun, 0, nullptr, &v))
                         return false;
                 }
                 if (!ValueToStringBuffer(cx, v, sb))
                     return false;
             }
 
             if (++i != length && !sepOp(cx, sb))
                 return false;
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/toLocaleString.js
@@ -0,0 +1,16 @@
+"use strict";
+
+Object.defineProperty(String.prototype, "toLocaleString", {
+    get() {
+        // Congratulations! You probably fixed primitive-this getters.
+        // Change "object" to "string".
+        assertEq(typeof this, "object");
+
+        return function() { return typeof this; };
+    }
+})
+
+assertEq(["test"].toLocaleString(), "string");
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Object/toLocaleString.js
@@ -0,0 +1,15 @@
+"use strict";
+
+Object.defineProperty(String.prototype, "toString", {
+    get() {
+        // Congratulations! You probably fixed primitive-this getters.
+        // Change "object" to "string".
+        assertEq(typeof this, "object");
+
+        return function() { return typeof this; };
+    }
+})
+assertEq(Object.prototype.toLocaleString.call("test"), "string");
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);