浏览代码

LibJS: Allow TypeArray to become detached in TypedArray.prototype.set

This is a normative change in the ECMA-262 spec. See:
https://github.com/tc39/ecma262/commit/4d570c4
Timothy Flynn 3 年之前
父节点
当前提交
4d0315099f

+ 20 - 48
Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp

@@ -759,75 +759,47 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::set)
         // 3. Let targetLength be target.[[ArrayLength]].
         // 3. Let targetLength be target.[[ArrayLength]].
         auto target_length = typed_array->array_length();
         auto target_length = typed_array->array_length();
 
 
-        // 4. Let targetName be the String value of target.[[TypedArrayName]].
-        // 5. Let targetElementSize be the Element Size value specified in Table 69 for targetName.
-        // 6. Let targetType be the Element Type value in Table 69 for targetName.
-        auto target_element_size = typed_array->element_size();
-
-        // 7. Let targetByteOffset be target.[[ByteOffset]].
-        auto target_byte_offset = typed_array->byte_offset();
-
-        // 8. Let src be ? ToObject(source).
+        // 4. Let src be ? ToObject(source).
         auto* src = TRY(source.to_object(global_object));
         auto* src = TRY(source.to_object(global_object));
 
 
-        // 9. Let srcLength be ? LengthOfArrayLike(src).
+        // 5. Let srcLength be ? LengthOfArrayLike(src).
         auto source_length = TRY(length_of_array_like(global_object, *src));
         auto source_length = TRY(length_of_array_like(global_object, *src));
 
 
-        // 10. If targetOffset is +∞, throw a RangeError exception.
+        // 6. If targetOffset is +∞, throw a RangeError exception.
         if (isinf(target_offset))
         if (isinf(target_offset))
             return vm.throw_completion<RangeError>(global_object, "Invalid target offset");
             return vm.throw_completion<RangeError>(global_object, "Invalid target offset");
 
 
-        // 11. If srcLength + targetOffset > targetLength, throw a RangeError exception.
+        // 7. If srcLength + targetOffset > targetLength, throw a RangeError exception.
         Checked<size_t> checked = source_length;
         Checked<size_t> checked = source_length;
         checked += static_cast<u32>(target_offset);
         checked += static_cast<u32>(target_offset);
         if (checked.has_overflow() || checked.value() > target_length)
         if (checked.has_overflow() || checked.value() > target_length)
             return vm.throw_completion<RangeError>(global_object, "Overflow or out of bounds in target length");
             return vm.throw_completion<RangeError>(global_object, "Overflow or out of bounds in target length");
 
 
-        // 12. Let targetByteIndex be targetOffset × targetElementSize + targetByteOffset.
-        Checked<size_t> checked_target_byte_index(static_cast<size_t>(target_offset));
-        checked_target_byte_index *= target_element_size;
-        checked_target_byte_index += target_byte_offset;
-        if (checked_target_byte_index.has_overflow())
-            return vm.throw_completion<RangeError>(global_object, "Overflow in target byte index");
-        auto target_byte_index = checked_target_byte_index.value();
-
-        // 13. Let k be 0.
-        auto k = 0;
-
-        // 14. Let limit be targetByteIndex + targetElementSize × srcLength.
-        Checked<size_t> checked_limit(source_length);
-        checked_limit *= target_element_size;
-        checked_limit += target_byte_index;
-        if (checked_limit.has_overflow())
-            return vm.throw_completion<RangeError>(global_object, "Overflow in target limit");
-        auto limit = checked_limit.value();
+        // 8. Let k be 0.
+        size_t k = 0;
 
 
-        // 15. Repeat, while targetByteIndex < limit,
-        while (target_byte_index < limit) {
+        // 9. Repeat, while k < srcLength,
+        while (k < source_length) {
             // a. Let Pk be ! ToString(𝔽(k)).
             // a. Let Pk be ! ToString(𝔽(k)).
             // b. Let value be ? Get(src, Pk).
             // b. Let value be ? Get(src, Pk).
             auto value = TRY(src->get(k));
             auto value = TRY(src->get(k));
 
 
-            // c. If target.[[ContentType]] is BigInt, set value to ? ToBigInt(value).
-            if (typed_array->content_type() == TypedArrayBase::ContentType::BigInt)
-                value = TRY(value.to_bigint(global_object));
-            // d. Otherwise, set value to ? ToNumber(value).
-            else
-                value = TRY(value.to_number(global_object));
-
-            // e. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception.
-            if (target_buffer->is_detached())
-                return vm.throw_completion<TypeError>(global_object, ErrorType::DetachedArrayBuffer);
+            // c. Let targetIndex be 𝔽(targetOffset + k).
+            CanonicalIndex target_index(CanonicalIndex::Type::Index, target_offset + k);
 
 
-            // f. Perform SetValueInBuffer(targetBuffer, targetByteIndex, targetType, value, true, Unordered).
-            typed_array->set_value_in_buffer(target_byte_index, value, ArrayBuffer::Unordered);
+            // d. Perform ? IntegerIndexedElementSet(target, targetIndex, value).
+            // FIXME: This is very awkward.
+#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
+    if (is<ClassName>(typed_array))                                                 \
+        TRY(integer_indexed_element_set<Type>(*typed_array, target_index, value));
+            JS_ENUMERATE_TYPED_ARRAYS
+#undef __JS_ENUMERATE
 
 
-            // g. Set k to k + 1.
+            // e. Set k to k + 1.
             ++k;
             ++k;
-
-            // h. Set targetByteIndex to targetByteIndex + targetElementSize.
-            target_byte_index += target_element_size;
         }
         }
+
+        // 10. Return unused.
     }
     }
 
 
     // 8. Return undefined.
     // 8. Return undefined.

+ 22 - 0
Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.prototype.set.js

@@ -82,6 +82,28 @@ test("length is 1", () => {
     });
     });
 });
 });
 
 
+test("detached buffer", () => {
+    TYPED_ARRAYS.forEach(({ array: T }) => {
+        let typedArray = new T(2);
+        typedArray[0] = 1;
+        typedArray[1] = 2;
+
+        let object = { length: 2 };
+
+        Object.defineProperty(object, 0, {
+            get: () => {
+                detachArrayBuffer(typedArray.buffer);
+            },
+        });
+
+        expect(() => {
+            typedArray.set(object);
+        }).not.toThrow();
+
+        expect(typedArray.length).toBe(0);
+    });
+});
+
 describe("errors", () => {
 describe("errors", () => {
     function argumentErrorTests(T) {
     function argumentErrorTests(T) {
         test(`requires at least one argument (${T.name})`, () => {
         test(`requires at least one argument (${T.name})`, () => {