diff --git a/js/src/builtin/SIMD.cpp b/js/src/builtin/SIMD.cpp index 7219239b9f78..f01b7c99b946 100644 --- a/js/src/builtin/SIMD.cpp +++ b/js/src/builtin/SIMD.cpp @@ -219,6 +219,7 @@ static const JSFunctionSpec TypeDescriptorMethods[] = { // 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 }; diff --git a/js/src/builtin/TypedObject.js b/js/src/builtin/TypedObject.js index 9f92e61da29b..c4ddee486bce 100644 --- a/js/src/builtin/TypedObject.js +++ b/js/src/builtin/TypedObject.js @@ -690,6 +690,24 @@ function SimdTypeToLength(type) { 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); diff --git a/js/src/jit-test/tests/asm.js/testBug1099216.js b/js/src/jit-test/tests/asm.js/testBug1099216.js index 32b22f0fe1dc..3514f307d5a7 100644 --- a/js/src/jit-test/tests/asm.js/testBug1099216.js +++ b/js/src/jit-test/tests/asm.js/testBug1099216.js @@ -7,9 +7,10 @@ if (typeof SIMD === 'undefined' || !isSimdAvailable()) { "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); @@ -23,11 +24,12 @@ if (typeof SIMD === 'undefined' || !isSimdAvailable()) { "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)(); @@ -36,6 +38,7 @@ 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 @@ -50,7 +53,7 @@ t = (function(global) { 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) diff --git a/js/src/js.msg b/js/src/js.msg index 7617dd068aab..6a88de5fae12 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -471,6 +471,7 @@ MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED, 0, JSEXN_TYPEERR, "handle unattache 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") diff --git a/js/src/tests/ecma_7/SIMD/toString.js b/js/src/tests/ecma_7/SIMD/toString.js index 022346936a17..0f8bb7a9b280 100644 --- a/js/src/tests/ecma_7/SIMD/toString.js +++ b/js/src/tests/ecma_7/SIMD/toString.js @@ -13,49 +13,75 @@ function test() { 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); diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index 3cf17c222358..8855676ed3ba 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -31,11 +31,11 @@ namespace js { * * (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 "