diff --git a/js/src/tests/jstests.list b/js/src/tests/jstests.list index 8090c73d1b2e8..2e5300342b849 100644 --- a/js/src/tests/jstests.list +++ b/js/src/tests/jstests.list @@ -609,9 +609,6 @@ skip script test262/built-ins/RegExp/named-groups/non-unicode-property-names-val # https://bugzilla.mozilla.org/show_bug.cgi?id=1761989 skip script test262/built-ins/TypedArrayConstructors/ctors/no-species.js -# https://bugzilla.mozilla.org/show_bug.cgi?id=1763605 -skip script test262/built-ins/TypedArray/prototype/set/array-arg-targetbuffer-detached-on-get-src-value-no-throw.js - # https://bugzilla.mozilla.org/show_bug.cgi?id=1763606 skip script test262/built-ins/TypedArray/prototype/sort/sort-tonumber.js diff --git a/js/src/tests/non262/TypedArray/set-detached.js b/js/src/tests/non262/TypedArray/set-detached.js index 56f9631daf7b5..4f9226c608e1e 100644 --- a/js/src/tests/non262/TypedArray/set-detached.js +++ b/js/src/tests/non262/TypedArray/set-detached.js @@ -119,7 +119,7 @@ if (typeof detachArrayBuffer === "function") { } } - // Test a TypeError is thrown when the typed array is detached and + // Test no TypeError is thrown when the typed array is detached and // srcLength > 0. for (let {typedArray, buffer} of createTypedArrays()) { let source = { @@ -130,8 +130,11 @@ if (typeof detachArrayBuffer === "function") { } } }; - let err = typedArray.length === 0 ? RangeError : TypeError; - assertThrowsInstanceOf(() => typedArray.set(source), err); + if (typedArray.length === 0) { + assertThrowsInstanceOf(() => typedArray.set(source), RangeError); + } else { + typedArray.set(source); + } } // Same as above, but with side-effect when executing Get(src, "0"). @@ -182,13 +185,17 @@ if (typeof detachArrayBuffer === "function") { } } }); - let err = typedArray.length === 0 ? RangeError : TypeError; - assertThrowsInstanceOf(() => typedArray.set(source), err); + if (typedArray.length === 0) { + assertThrowsInstanceOf(() => typedArray.set(source), RangeError); + } else { + typedArray.set(source); + } } // Side-effects when getting the source elements detach the buffer. Also - // ensure other elements aren't accessed. + // ensure other elements are accessed. for (let {typedArray, buffer} of createTypedArrays()) { + let accessed = false; let source = Object.defineProperties([], { 0: { get() { @@ -198,12 +205,19 @@ if (typeof detachArrayBuffer === "function") { }, 1: { get() { - throw new Error("Unexpected access"); + assertEq(accessed, false); + accessed = true; + return 2; } } }); - let err = typedArray.length <= 1 ? RangeError : TypeError; - assertThrowsInstanceOf(() => typedArray.set(source), err); + if (typedArray.length <= 1) { + assertThrowsInstanceOf(() => typedArray.set(source), RangeError); + } else { + assertEq(accessed, false); + typedArray.set(source); + assertEq(accessed, true); + } } // Side-effects when converting the source elements detach the buffer. @@ -214,13 +228,17 @@ if (typeof detachArrayBuffer === "function") { return 1; } }]; - let err = typedArray.length === 0 ? RangeError : TypeError; - assertThrowsInstanceOf(() => typedArray.set(source), err); + if (typedArray.length === 0) { + assertThrowsInstanceOf(() => typedArray.set(source), RangeError); + } else { + typedArray.set(source); + } } // Side-effects when converting the source elements detach the buffer. Also - // ensure other elements aren't accessed. + // ensure other elements are accessed. for (let {typedArray, buffer} of createTypedArrays()) { + let accessed = false; let source = [{ valueOf() { detachArrayBuffer(buffer); @@ -228,11 +246,18 @@ if (typeof detachArrayBuffer === "function") { } }, { valueOf() { - throw new Error("Unexpected access"); + assertEq(accessed, false); + accessed = true; + return 2; } }]; - let err = typedArray.length <= 1 ? RangeError : TypeError; - assertThrowsInstanceOf(() => typedArray.set(source), err); + if (typedArray.length <= 1) { + assertThrowsInstanceOf(() => typedArray.set(source), RangeError); + } else { + assertEq(accessed, false); + typedArray.set(source); + assertEq(accessed, true); + } } } diff --git a/js/src/tests/non262/extensions/TypedArray-set-object-funky-length-detaches.js b/js/src/tests/non262/extensions/TypedArray-set-object-funky-length-detaches.js index 0c18796a3949b..66e539b99de2f 100644 --- a/js/src/tests/non262/extensions/TypedArray-set-object-funky-length-detaches.js +++ b/js/src/tests/non262/extensions/TypedArray-set-object-funky-length-detaches.js @@ -44,17 +44,7 @@ ctors.forEach(function(TypedArray) { } }; - var passed = false; - try - { - ta.set(arraylike, 0x1234); - } - catch (e) - { - passed = true; - } - - assertEq(passed, true); + ta.set(arraylike, 0x1234); }); /******************************************************************************/ diff --git a/js/src/tests/non262/extensions/typedarray-set-neutering.js b/js/src/tests/non262/extensions/typedarray-set-neutering.js index a0778a371e28c..23df8158c590c 100644 --- a/js/src/tests/non262/extensions/typedarray-set-neutering.js +++ b/js/src/tests/non262/extensions/typedarray-set-neutering.js @@ -35,7 +35,7 @@ Object.defineProperty(src, 4, { } }); -assertThrowsInstanceOf(() => a.set(src), TypeError); +a.set(src); /******************************************************************************/ diff --git a/js/src/vm/TypedArrayObject-inl.h b/js/src/vm/TypedArrayObject-inl.h index f7dd928b22307..b8631667c27f2 100644 --- a/js/src/vm/TypedArrayObject-inl.h +++ b/js/src/vm/TypedArrayObject-inl.h @@ -412,12 +412,15 @@ class ElementSpecific { size_t offset = 0) { MOZ_ASSERT(target->type() == TypeIDOfType::id, "target type and NativeType must match"); - MOZ_ASSERT(!target->hasDetachedBuffer(), "target isn't detached"); MOZ_ASSERT(!source->is(), "use setFromTypedArray instead of this method"); + MOZ_ASSERT_IF(target->hasDetachedBuffer(), target->length() == 0); + MOZ_ASSERT_IF(!target->hasDetachedBuffer(), offset <= target->length()); + MOZ_ASSERT_IF(!target->hasDetachedBuffer(), + len <= target->length() - offset); size_t i = 0; - if (source->is()) { + if (source->is() && !target->hasDetachedBuffer()) { // Attempt fast-path infallible conversion of dense elements up to // the first potentially side-effectful lookup or conversion. size_t bound = std::min( @@ -459,11 +462,14 @@ class ElementSpecific { return false; } - len = std::min(len, target->length()); - if (i >= len) { - break; + // Ignore out-of-bounds writes, but still execute getElement/valueToNative + // because of observable side-effects. + if (offset + i >= target->length()) { + continue; } + MOZ_ASSERT(!target->hasDetachedBuffer()); + // Compute every iteration in case getElement/valueToNative // detaches the underlying array buffer or GC moves the data. SharedMem dest = diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index c65572b2e0a20..cdaa2aadb5b05 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -1729,27 +1729,6 @@ static bool SetTypedArrayFromArrayLike(JSContext* cx, // Steps 8-9. if (srcLength > 0) { - // GetLengthProperty in step 16 can lead to the execution of user code - // which may detach the buffer. Handle this case here to ensure - // SetFromNonTypedArray is never called with a detached buffer. We still - // need to execute steps 21.a-b for their possible side-effects. - if (target->hasDetachedBuffer()) { - // Steps 21.a-b. - RootedValue v(cx); - if (!GetElement(cx, src, src, 0, &v)) { - return false; - } - - if (!target->convertForSideEffect(cx, v)) { - return false; - } - - // Step 21.c. - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_TYPED_ARRAY_DETACHED); - return false; - } - switch (target->type()) { #define SET_FROM_NON_TYPED_ARRAY(_, T, N) \ case Scalar::N: \ @@ -1761,15 +1740,6 @@ static bool SetTypedArrayFromArrayLike(JSContext* cx, default: MOZ_CRASH("Unsupported TypedArray type"); } - - // Step 21.c. - // SetFromNonTypedArray doesn't throw when the array buffer gets - // detached. - if (target->hasDetachedBuffer()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_TYPED_ARRAY_DETACHED); - return false; - } } // Step 10.