Bug 1252924 - SIMD.*.prototype.valueOf. r=waldo
authorJakob Olesen <jolesen@mozilla.com>
Mon, 14 Mar 2016 09:50:15 -0700
changeset 288594 716358ed5dd8f090fb9cda2f55a55bd9cad64fb4
parent 288593 43ef35e84e99ef2105be6101c4aacf3d7cd94d29
child 288595 583f746e9e55311a6f586e1b5bc67ff059ec55fe
push id30087
push usercbook@mozilla.com
push dateTue, 15 Mar 2016 09:43:43 +0000
treeherdermozilla-central@5e14887312d4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswaldo
bugs1252924, 1099216
milestone48.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 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)