Bug 1252924 - SIMD.*.prototype.valueOf. r=waldo
authorJakob Olesen <jolesen@mozilla.com>
Mon, 14 Mar 2016 09:50:15 -0700
changeset 288600 716358ed5dd8f090fb9cda2f55a55bd9cad64fb4
parent 288599 43ef35e84e99ef2105be6101c4aacf3d7cd94d29
child 288601 583f746e9e55311a6f586e1b5bc67ff059ec55fe
push id18174
push usercbook@mozilla.com
push dateTue, 15 Mar 2016 09:44:58 +0000
treeherderfx-team@dd0baa33759d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswaldo
bugs1252924, 1099216
milestone48.0a1
Bug 1252924 - SIMD.*.prototype.valueOf. r=waldo When SIMD objects have proper value semantics, this method will just return |this| after a type check. Until then, do as the polyfill and override valueOf to throw a TypeError so that ToNumber() can't convert a SIMD value to a number. Fix wrong code in asm.js/testBug1099216.js. This code was using Float32x4(x) instead of Float32x4.check(x) as an argument coercion. MozReview-Commit-ID: H9zaEfoqs2J
js/src/builtin/SIMD.cpp
js/src/builtin/TypedObject.js
js/src/jit-test/tests/asm.js/testBug1099216.js
js/src/js.msg
js/src/tests/ecma_7/SIMD/toString.js
js/src/vm/Xdr.h
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -214,16 +214,17 @@ static const JSFunctionSpec TypeDescript
     JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
     JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
     JS_FS_END
 };
 
 // Shared TypedObject methods for all SIMD types.
 static const JSFunctionSpec SimdTypedObjectMethods[] = {
     JS_SELF_HOSTED_FN("toString", "SimdToString", 0, 0),
+    JS_SELF_HOSTED_FN("valueOf", "SimdValueOf", 0, 0),
     JS_SELF_HOSTED_FN("toSource", "SimdToSource", 0, 0),
     JS_FS_END
 };
 
 // Provide JSJitInfo structs for those types that are supported by Ion.
 // The controlling SIMD type is encoded as the InlinableNative primary opcode.
 // The SimdOperation within the type is encoded in the .depth field.
 //
--- a/js/src/builtin/TypedObject.js
+++ b/js/src/builtin/TypedObject.js
@@ -685,16 +685,34 @@ function SimdTypeToLength(type) {
   case JS_SIMDTYPEREPR_BOOL64X2:
     return 2;
   }
 
   assert(false, "Unhandled type constant");
   return undefined;
 }
 
+// This implements SIMD.*.prototype.valueOf().
+// Once we have proper value semantics for SIMD types, this function should just
+// perform a type check and return this.
+// For now, throw a TypeError unconditionally since valueOf() was probably
+// called from ToNumber() which is supposed to throw when attempting to convert
+// a SIMD value to a number.
+function SimdValueOf() {
+  if (!IsObject(this) || !ObjectIsTypedObject(this))
+    ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "SIMD", "valueOf", typeof this);
+
+  var descr = TypedObjectTypeDescr(this);
+
+  if (DESCR_KIND(descr) != JS_TYPEREPR_SIMD_KIND)
+    ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "SIMD", "valueOf", typeof this);
+
+  ThrowTypeError(JSMSG_SIMD_TO_NUMBER);
+}
+
 function SimdToSource() {
   if (!IsObject(this) || !ObjectIsTypedObject(this))
     ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "SIMD.*", "toSource", typeof this);
 
   var descr = TypedObjectTypeDescr(this);
 
   if (DESCR_KIND(descr) != JS_TYPEREPR_SIMD_KIND)
     ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "SIMD.*", "toSource", typeof this);
--- a/js/src/jit-test/tests/asm.js/testBug1099216.js
+++ b/js/src/jit-test/tests/asm.js/testBug1099216.js
@@ -2,57 +2,60 @@ if (typeof SIMD === 'undefined' || !isSi
     print("won't run tests as simd extensions aren't activated yet");
     quit(0);
 }
 
 (function(global) {
     "use asm";
     var frd = global.Math.fround;
     var fx4 = global.SIMD.Float32x4;
+    var fc4 = fx4.check;
     var fsp = fx4.splat;
     function s(){}
-    function d(x){x=fx4(x);}
+    function d(x){x=fc4(x);}
     function e() {
         var x = frd(0);
         x = frd(x / x);
         s();
         d(fsp(x));
     }
     return e;
 })(this)();
 
 (function(m) {
     "use asm"
     var k = m.SIMD.Bool32x4
     var g = m.SIMD.Int32x4
+    var gc = g.check;
     var h = g.select
     function f() {
         var x = k(0, 0, 0, 0)
         var y = g(1, 2, 3, 4)
-        return g(h(x, y, y))
+        return gc(h(x, y, y))
     }
     return f;
 })(this)();
 
 t = (function(global) {
     "use asm"
     var toF = global.Math.fround
     var f4 = global.SIMD.Float32x4
+    var f4c = f4.check
     function p(x, y, width, value, max_iterations) {
         x = x | 0
         y = y | 0
         width = width | 0
         value = value | 0
         max_iterations = max_iterations | 0
     }
     function m(xf, yf, yd, max_iterations) {
         xf = toF(xf)
         yf = toF(yf)
         yd = toF(yd)
         max_iterations = max_iterations | 0
         var _ = f4(0, 0, 0, 0), c_im4 = f4(0, 0, 0, 0)
         c_im4 = f4(yf, yd, yd, yf)
-        return f4(c_im4);
+        return f4c(c_im4);
     }
     return {p:p,m:m};
 })(this)
 t.p();
 t.m();
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -466,16 +466,17 @@ MSG_DEF(JSMSG_NO_SUCH_SELF_HOSTED_PROP,1
 // Typed object / SIMD
 MSG_DEF(JSMSG_INVALID_PROTOTYPE,       0, JSEXN_TYPEERR, "prototype field is not an object")
 MSG_DEF(JSMSG_TYPEDOBJECT_BAD_ARGS,    0, JSEXN_TYPEERR, "invalid arguments")
 MSG_DEF(JSMSG_TYPEDOBJECT_BINARYARRAY_BAD_INDEX, 0, JSEXN_RANGEERR, "invalid or out-of-range index")
 MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED, 0, JSEXN_TYPEERR, "handle unattached")
 MSG_DEF(JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD_ARGS, 0, JSEXN_RANGEERR, "invalid field descriptor")
 MSG_DEF(JSMSG_TYPEDOBJECT_TOO_BIG,     0, JSEXN_ERR, "Type is too large to allocate")
 MSG_DEF(JSMSG_SIMD_FAILED_CONVERSION,  0, JSEXN_RANGEERR, "SIMD conversion loses precision")
+MSG_DEF(JSMSG_SIMD_TO_NUMBER,          0, JSEXN_TYPEERR, "can't convert SIMD value to number")
 
 // Typed array
 MSG_DEF(JSMSG_BAD_INDEX,               0, JSEXN_RANGEERR, "invalid or out-of-range index")
 MSG_DEF(JSMSG_NON_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected ArrayBuffer, but species constructor returned non-ArrayBuffer")
 MSG_DEF(JSMSG_SAME_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected different ArrayBuffer, but species constructor returned same ArrayBuffer")
 MSG_DEF(JSMSG_SHORT_ARRAY_BUFFER_RETURNED, 2, JSEXN_TYPEERR, "expected ArrayBuffer with at least {0} bytes, but species constructor returns ArrayBuffer with {1} bytes")
 MSG_DEF(JSMSG_TYPED_ARRAY_BAD_ARGS,    0, JSEXN_TYPEERR, "invalid arguments")
 MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG,1, JSEXN_RANGEERR, "argument {0} must be >= 0")
--- a/js/src/tests/ecma_7/SIMD/toString.js
+++ b/js/src/tests/ecma_7/SIMD/toString.js
@@ -8,57 +8,83 @@ function test() {
     // Polyfill check should show that we already have a toString.
     assertEq(Float32x4.prototype.hasOwnProperty("toString"), true);
 
     // This toString method type checks its argument.
     var ts = Float32x4.prototype.toString;
     assertThrowsInstanceOf(() => ts.call(5), TypeError);
     assertThrowsInstanceOf(() => ts.call({}), TypeError);
 
+    // Can't convert SIMD objects to numbers.
+    assertThrowsInstanceOf(() => +f, TypeError);
+    assertThrowsInstanceOf(() => f.valueOf(), TypeError);
+
     var Float64x2 = SIMD.Float64x2;
     var f = Float64x2(11, 22);
     assertEq(f.toString(), "SIMD.Float64x2(11, 22)");
+    assertThrowsInstanceOf(() => +f, TypeError);
+    assertThrowsInstanceOf(() => f.valueOf(), TypeError);
 
     var Int8x16 = SIMD.Int8x16;
     var f = Int8x16(11, 22, 33, 44, -11, -22, -33, -44, 1, 2, 3, 4, -1, -2, -3, -4);
     assertEq(f.toString(), "SIMD.Int8x16(11, 22, 33, 44, -11, -22, -33, -44, 1, 2, 3, 4, -1, -2, -3, -4)");
+    assertThrowsInstanceOf(() => +f, TypeError);
+    assertThrowsInstanceOf(() => f.valueOf(), TypeError);
 
     var Int16x8 = SIMD.Int16x8;
     var f = Int16x8(11, 22, 33, 44, -11, -22, -33, -44);
     assertEq(f.toString(), "SIMD.Int16x8(11, 22, 33, 44, -11, -22, -33, -44)");
+    assertThrowsInstanceOf(() => +f, TypeError);
+    assertThrowsInstanceOf(() => f.valueOf(), TypeError);
 
     var Int32x4 = SIMD.Int32x4;
     var f = Int32x4(11, 22, 33, 44);
     assertEq(f.toString(), "SIMD.Int32x4(11, 22, 33, 44)");
+    assertThrowsInstanceOf(() => +f, TypeError);
+    assertThrowsInstanceOf(() => f.valueOf(), TypeError);
 
     var Uint8x16 = SIMD.Uint8x16;
     var f = Uint8x16(11, 22, 33, 44, 245, 234, 223, 212, 1, 2, 3, 4, 255, 254, 0, 250);
     assertEq(f.toString(), "SIMD.Uint8x16(11, 22, 33, 44, 245, 234, 223, 212, 1, 2, 3, 4, 255, 254, 0, 250)");
+    assertThrowsInstanceOf(() => +f, TypeError);
+    assertThrowsInstanceOf(() => f.valueOf(), TypeError);
 
     var Uint16x8 = SIMD.Uint16x8;
     var f = Uint16x8(11, 22, 33, 44, 65535, 65534, 65533, 65532);
     assertEq(f.toString(), "SIMD.Uint16x8(11, 22, 33, 44, 65535, 65534, 65533, 65532)");
+    assertThrowsInstanceOf(() => +f, TypeError);
+    assertThrowsInstanceOf(() => f.valueOf(), TypeError);
 
     var Uint32x4 = SIMD.Uint32x4;
     var f = Uint32x4(11, 22, 4294967295, 4294967294);
     assertEq(f.toString(), "SIMD.Uint32x4(11, 22, 4294967295, 4294967294)");
+    assertThrowsInstanceOf(() => +f, TypeError);
+    assertThrowsInstanceOf(() => f.valueOf(), TypeError);
 
     var Bool8x16 = SIMD.Bool8x16;
     var f = Bool8x16(true, true, false, false, false, true, true, false, true, true, true, true, false, false, false, false);
     assertEq(f.toString(), "SIMD.Bool8x16(true, true, false, false, false, true, true, false, true, true, true, true, false, false, false, false)");
+    assertThrowsInstanceOf(() => +f, TypeError);
+    assertThrowsInstanceOf(() => f.valueOf(), TypeError);
 
     var Bool16x8 = SIMD.Bool16x8;
     var f = Bool16x8(true, true, false, false, true, false, false, true);
     assertEq(f.toString(), "SIMD.Bool16x8(true, true, false, false, true, false, false, true)");
+    assertThrowsInstanceOf(() => +f, TypeError);
+    assertThrowsInstanceOf(() => f.valueOf(), TypeError);
 
     var Bool32x4 = SIMD.Bool32x4;
     var f = Bool32x4(true, true, false, false);
     assertEq(f.toString(), "SIMD.Bool32x4(true, true, false, false)");
+    assertThrowsInstanceOf(() => +f, TypeError);
+    assertThrowsInstanceOf(() => f.valueOf(), TypeError);
 
     var Bool64x2 = SIMD.Bool64x2;
     var f = Bool64x2(true, false);
     assertEq(f.toString(), "SIMD.Bool64x2(true, false)");
+    assertThrowsInstanceOf(() => +f, TypeError);
+    assertThrowsInstanceOf(() => f.valueOf(), TypeError);
 
     if (typeof reportCompare === "function")
         reportCompare(true, true);
 }
 
 test();
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -26,21 +26,21 @@ namespace js {
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  *
  * (If you're wondering, 0xb973c0de is used because it looks like "bytecode".)
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 352;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 353;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
-static_assert(JSErr_Limit == 419,
+static_assert(JSErr_Limit == 420,
               "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
               "removed MSG_DEFs from js.msg, you should increment "
               "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
               "expected JSErr_Limit value.");
 
 class XDRBuffer {
   public:
     explicit XDRBuffer(JSContext* cx)