Commit 186bfbb1 authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

[runtime] Fix TypedArray slice when sharing the underlying buffer

According to the spec the copy step is defined iteratively and with
@@species we can create a TypedArray which shares the buffer with the
receiver which in turn prevents us from using memcpy.

Bug: v8:6223

Change-Id: If1bad085ea1d022bf3fb2cffc81645b2f7f56346
Reviewed-on: https://chromium-review.googlesource.com/471409Reviewed-by: 's avatarPeter Marshall <petermarshall@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#44520}
parent 4fad53a5
......@@ -3063,10 +3063,19 @@ class TypedElementsAccessor
BackingStore::cast(result_array->elements());
DCHECK_LE(count, result_elements->length());
uint8_t* src = static_cast<uint8_t*>(src_elements->DataPtr());
uint8_t* src =
static_cast<uint8_t*>(src_elements->DataPtr()) + start * element_size;
uint8_t* result = static_cast<uint8_t*>(result_elements->DataPtr());
std::memcpy(result, src + start * element_size, count * element_size);
if (array->buffer() != result_array->buffer()) {
std::memcpy(result, src, count * element_size);
} else {
// The spec defines the copy-step iteratively, which means that we
// cannot use memcpy if the buffer is shared.
uint8_t* end = src + count * element_size;
while (src < end) {
*result++ = *src++;
}
}
return result_array;
}
......
......@@ -57,9 +57,7 @@ for (var constructor of typedArrayConstructors) {
assertEquals(7, array.slice(0, 100).length);
// Does not permit being called on other types
assertThrows(function () {
constructor.prototype.slice.call([], 0, 0);
}, TypeError);
assertThrows(() => constructor.prototype.slice.call([], 0, 0), TypeError);
// Check that elements are copied properly in slice
array = new constructor([1, 2, 3, 4]);
......@@ -82,14 +80,48 @@ for (var constructor of typedArrayConstructors) {
// Check that the result array is properly created by checking species
for (var constructor1 of typedArrayConstructors) {
for (var constructor2 of typedArrayConstructors) {
class MyTypedArray2 extends constructor1 {
static get[Symbol.species]() {
return constructor2;
}
testCustomSubclass(constructor1, constructor2);
}
}
function testCustomSubclass(superClass, speciesClass) {
// Simple subclass that has another TypedArray as species
class CustomTypedArray extends superClass {
static get[Symbol.species]() {
return speciesClass;
}
}
// 16 entries.
let exampleArray = [-1.0, 0, 1.1, 255, 256, 0xFFFFFFFF, 2**50, NaN];
let customArray = new CustomTypedArray(exampleArray);
let basicArray = new superClass(exampleArray);
assertEquals(new speciesClass(basicArray), customArray.slice(),
superClass.name + ' -> ' + speciesClass.name);
// Custom constructor with shared buffer.
exampleArray = new Array(64).fill(0).map((v,i) => i);
let filledBuffer = new Uint8Array(exampleArray).buffer;
// Create a view for the begining of the buffer.
let customArray2 = new superClass(filledBuffer, 0, 3);
customArray2.constructor = {
[Symbol.species]: function(length) {
let bytes_per_element = speciesClass.BYTES_PER_ELEMENT;
// Reuse the same buffer for the custom species constructor.
// Skip the first BYTES_PER_ELEMENT bytes of the buffer.
return new speciesClass(filledBuffer, bytes_per_element, length);
}
var arr = new MyTypedArray2([-1.0, 0, 1.1, 255, 256]);
var arr2 = new constructor1([-1.0, 0, 1.1, 255, 256]);
assertEquals(new constructor2(arr2), arr.slice(),
constructor1.name + ' -> ' + constructor2.name);
};
// Since slice is defined iteratively, the species created new array uses the
// same underlying buffer shifted by one element. Hence the first value is
// copied over and over again.
let convertedCopy = Array.from(customArray2);
let firstValue = convertedCopy[0];
assertEquals(firstValue, customArray2[0]);
let sliceResult = customArray2.slice();
if (superClass == speciesClass) {
assertEquals(new Array(3).fill(firstValue), Array.from(customArray2));
assertEquals(new Array(3).fill(firstValue), Array.from(sliceResult));
}
assertEquals(3, customArray2.length);
assertEquals(3, sliceResult.length);
}
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
var ab = new Int8Array(20).map((v, i) => i).buffer;
var ta = new Int8Array(ab, 0, 10);
var seen_length = -1;
ta.constructor = {
[Symbol.species]: function(len) {
seen_length = len;
return new Int8Array(ab, 1, len);
}
};
assertEquals(-1, seen_length);
assertArrayEquals([0,1,2,3,4,5,6,7,8,9], ta);
var tb = ta.slice();
assertEquals(10, seen_length);
assertArrayEquals([0,0,0,0,0,0,0,0,0,0], ta);
assertArrayEquals([0,0,0,0,0,0,0,0,0,0], tb);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment