[dart2wasm] Fix bug in JS typed data .sublist() implementations
Issue https://github.com/flutter/flutter/issues/179853 Change-Id: I97ce8d7177dfdd612714fafbc2a6b13555d9bba1 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/468300 Reviewed-by: Ömer Ağacan <omersa@google.com> Commit-Queue: Martin Kustermann <kustermann@google.com>
This commit is contained in:
committed by
Commit Queue
parent
10373c30e8
commit
d38e30d65a
@@ -891,14 +891,12 @@ final class JSUint8ArrayImpl extends JSIntegerArrayBase
|
||||
|
||||
@override
|
||||
JSUint8ArrayImpl sublist(int start, [int? end]) {
|
||||
final newOffset = offsetInBytes + start;
|
||||
final newEnd = RangeErrorUtils.checkValidRange(
|
||||
newOffset,
|
||||
end,
|
||||
lengthInBytes,
|
||||
final newEnd = RangeErrorUtils.checkValidRange(start, end, length);
|
||||
final newOffsetInBytes = offsetInBytes + start;
|
||||
final newLengthInBytes = newEnd - start;
|
||||
return JSUint8ArrayImpl._(
|
||||
buffer.cloneAsDataView(newOffsetInBytes, newLengthInBytes),
|
||||
);
|
||||
final newLength = newEnd - newOffset;
|
||||
return JSUint8ArrayImpl._(buffer.cloneAsDataView(newOffset, newLength));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -999,14 +997,12 @@ final class JSInt8ArrayImpl extends JSIntegerArrayBase
|
||||
|
||||
@override
|
||||
JSInt8ArrayImpl sublist(int start, [int? end]) {
|
||||
final newOffset = offsetInBytes + start;
|
||||
final newEnd = RangeErrorUtils.checkValidRange(
|
||||
newOffset,
|
||||
end,
|
||||
lengthInBytes,
|
||||
final newEnd = RangeErrorUtils.checkValidRange(start, end, length);
|
||||
final newOffsetInBytes = offsetInBytes + start;
|
||||
final newLengthInBytes = newEnd - start;
|
||||
return JSInt8ArrayImpl._(
|
||||
buffer.cloneAsDataView(newOffsetInBytes, newLengthInBytes),
|
||||
);
|
||||
final newLength = newEnd - newOffset;
|
||||
return JSInt8ArrayImpl._(buffer.cloneAsDataView(newOffset, newLength));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1109,15 +1105,11 @@ final class JSUint8ClampedArrayImpl extends JSIntegerArrayBase
|
||||
|
||||
@override
|
||||
JSUint8ClampedArrayImpl sublist(int start, [int? end]) {
|
||||
final newOffset = offsetInBytes + start;
|
||||
final newEnd = RangeErrorUtils.checkValidRange(
|
||||
newOffset,
|
||||
end,
|
||||
lengthInBytes,
|
||||
);
|
||||
final newLength = newEnd - newOffset;
|
||||
final newEnd = RangeErrorUtils.checkValidRange(start, end, length);
|
||||
final newOffsetInBytes = offsetInBytes + start;
|
||||
final newLengthInBytes = newEnd - start;
|
||||
return JSUint8ClampedArrayImpl._(
|
||||
buffer.cloneAsDataView(newOffset, newLength),
|
||||
buffer.cloneAsDataView(newOffsetInBytes, newLengthInBytes),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1211,15 +1203,12 @@ final class JSUint16ArrayImpl extends JSIntegerArrayBase
|
||||
|
||||
@override
|
||||
JSUint16ArrayImpl sublist(int start, [int? end]) {
|
||||
final int newOffset = offsetInBytes + (start * 2);
|
||||
final int newEnd = end == null ? lengthInBytes : end * 2;
|
||||
final int newLength = newEnd - newOffset;
|
||||
RangeErrorUtils.checkValidRange(
|
||||
newOffset ~/ 2,
|
||||
newEnd ~/ 2,
|
||||
lengthInBytes ~/ 2,
|
||||
final newEnd = RangeErrorUtils.checkValidRange(start, end, length);
|
||||
final newOffsetInBytes = offsetInBytes + 2 * start;
|
||||
final newLengthInBytes = 2 * (newEnd - start);
|
||||
return JSUint16ArrayImpl._(
|
||||
buffer.cloneAsDataView(newOffsetInBytes, newLengthInBytes),
|
||||
);
|
||||
return JSUint16ArrayImpl._(buffer.cloneAsDataView(newOffset, newLength));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1327,15 +1316,12 @@ final class JSInt16ArrayImpl extends JSIntegerArrayBase
|
||||
|
||||
@override
|
||||
JSInt16ArrayImpl sublist(int start, [int? end]) {
|
||||
final int newOffset = offsetInBytes + (start * 2);
|
||||
final int newEnd = end == null ? lengthInBytes : end * 2;
|
||||
final int newLength = newEnd - newOffset;
|
||||
RangeErrorUtils.checkValidRange(
|
||||
newOffset ~/ 2,
|
||||
newEnd ~/ 2,
|
||||
lengthInBytes ~/ 2,
|
||||
final newEnd = RangeErrorUtils.checkValidRange(start, end, length);
|
||||
final newOffsetInBytes = offsetInBytes + 2 * start;
|
||||
final newLengthInBytes = 2 * (newEnd - start);
|
||||
return JSInt16ArrayImpl._(
|
||||
buffer.cloneAsDataView(newOffsetInBytes, newLengthInBytes),
|
||||
);
|
||||
return JSInt16ArrayImpl._(buffer.cloneAsDataView(newOffset, newLength));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1443,15 +1429,12 @@ final class JSUint32ArrayImpl extends JSIntegerArrayBase
|
||||
|
||||
@override
|
||||
JSUint32ArrayImpl sublist(int start, [int? end]) {
|
||||
final int newOffset = offsetInBytes + (start * 4);
|
||||
final int newEnd = end == null ? lengthInBytes : end * 4;
|
||||
final int newLength = newEnd - newOffset;
|
||||
RangeErrorUtils.checkValidRange(
|
||||
newOffset ~/ 4,
|
||||
newEnd ~/ 4,
|
||||
lengthInBytes ~/ 4,
|
||||
final newEnd = RangeErrorUtils.checkValidRange(start, end, length);
|
||||
final newOffsetInBytes = offsetInBytes + 4 * start;
|
||||
final newLengthInBytes = 4 * (newEnd - start);
|
||||
return JSUint32ArrayImpl._(
|
||||
buffer.cloneAsDataView(newOffsetInBytes, newLengthInBytes),
|
||||
);
|
||||
return JSUint32ArrayImpl._(buffer.cloneAsDataView(newOffset, newLength));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1559,15 +1542,12 @@ final class JSInt32ArrayImpl extends JSIntegerArrayBase
|
||||
|
||||
@override
|
||||
JSInt32ArrayImpl sublist(int start, [int? end]) {
|
||||
final int newOffset = offsetInBytes + (start * 4);
|
||||
final int newEnd = end == null ? lengthInBytes : end * 4;
|
||||
final int newLength = newEnd - newOffset;
|
||||
RangeErrorUtils.checkValidRange(
|
||||
newOffset ~/ 4,
|
||||
newEnd ~/ 4,
|
||||
lengthInBytes ~/ 4,
|
||||
final newEnd = RangeErrorUtils.checkValidRange(start, end, length);
|
||||
final newOffsetInBytes = offsetInBytes + 4 * start;
|
||||
final newLengthInBytes = 4 * (newEnd - start);
|
||||
return JSInt32ArrayImpl._(
|
||||
buffer.cloneAsDataView(newOffsetInBytes, newLengthInBytes),
|
||||
);
|
||||
return JSInt32ArrayImpl._(buffer.cloneAsDataView(newOffset, newLength));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1756,15 +1736,12 @@ final class JSBigUint64ArrayImpl extends JSIntegerArrayBase
|
||||
|
||||
@override
|
||||
JSBigUint64ArrayImpl sublist(int start, [int? end]) {
|
||||
final int newOffset = offsetInBytes + (start * 8);
|
||||
final int newEnd = end == null ? lengthInBytes : end * 8;
|
||||
final int newLength = newEnd - newOffset;
|
||||
RangeErrorUtils.checkValidRange(
|
||||
newOffset ~/ 8,
|
||||
newEnd ~/ 8,
|
||||
lengthInBytes ~/ 8,
|
||||
final newEnd = RangeErrorUtils.checkValidRange(start, end, length);
|
||||
final newOffsetInBytes = offsetInBytes + 8 * start;
|
||||
final newLengthInBytes = 8 * (newEnd - start);
|
||||
return JSBigUint64ArrayImpl._(
|
||||
buffer.cloneAsDataView(newOffsetInBytes, newLengthInBytes),
|
||||
);
|
||||
return JSBigUint64ArrayImpl._(buffer.cloneAsDataView(newOffset, newLength));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1860,15 +1837,12 @@ final class JSBigInt64ArrayImpl extends JSIntegerArrayBase
|
||||
|
||||
@override
|
||||
JSBigInt64ArrayImpl sublist(int start, [int? end]) {
|
||||
final int newOffset = offsetInBytes + (start * 8);
|
||||
final int newEnd = end == null ? lengthInBytes : end * 8;
|
||||
final int newLength = newEnd - newOffset;
|
||||
RangeErrorUtils.checkValidRange(
|
||||
newOffset ~/ 8,
|
||||
newEnd ~/ 8,
|
||||
lengthInBytes ~/ 8,
|
||||
final newEnd = RangeErrorUtils.checkValidRange(start, end, length);
|
||||
final newOffsetInBytes = offsetInBytes + 8 * start;
|
||||
final newLengthInBytes = 8 * (newEnd - start);
|
||||
return JSBigInt64ArrayImpl._(
|
||||
buffer.cloneAsDataView(newOffsetInBytes, newLengthInBytes),
|
||||
);
|
||||
return JSBigInt64ArrayImpl._(buffer.cloneAsDataView(newOffset, newLength));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -2321,15 +2295,12 @@ final class JSFloat32ArrayImpl extends JSFloatArrayBase
|
||||
|
||||
@override
|
||||
JSFloat32ArrayImpl sublist(int start, [int? end]) {
|
||||
final int newOffset = offsetInBytes + (start * 4);
|
||||
final int newEnd = end == null ? lengthInBytes : end * 4;
|
||||
final int newLength = newEnd - newOffset;
|
||||
RangeErrorUtils.checkValidRange(
|
||||
newOffset ~/ 4,
|
||||
newEnd ~/ 4,
|
||||
lengthInBytes ~/ 4,
|
||||
final newEnd = RangeErrorUtils.checkValidRange(start, end, length);
|
||||
final newOffsetInBytes = offsetInBytes + 4 * start;
|
||||
final newLengthInBytes = 4 * (newEnd - start);
|
||||
return JSFloat32ArrayImpl._(
|
||||
buffer.cloneAsDataView(newOffsetInBytes, newLengthInBytes),
|
||||
);
|
||||
return JSFloat32ArrayImpl._(buffer.cloneAsDataView(newOffset, newLength));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -2438,15 +2409,12 @@ final class JSFloat64ArrayImpl extends JSFloatArrayBase
|
||||
|
||||
@override
|
||||
JSFloat64ArrayImpl sublist(int start, [int? end]) {
|
||||
final int newOffset = offsetInBytes + (start * 8);
|
||||
final int newEnd = end == null ? lengthInBytes : end * 8;
|
||||
final int newLength = newEnd - newOffset;
|
||||
RangeErrorUtils.checkValidRange(
|
||||
newOffset ~/ 8,
|
||||
newEnd ~/ 8,
|
||||
lengthInBytes ~/ 8,
|
||||
final newEnd = RangeErrorUtils.checkValidRange(start, end, length);
|
||||
final newOffsetInBytes = offsetInBytes + 8 * start;
|
||||
final newLengthInBytes = 8 * (newEnd - start);
|
||||
return JSFloat64ArrayImpl._(
|
||||
buffer.cloneAsDataView(newOffsetInBytes, newLengthInBytes),
|
||||
);
|
||||
return JSFloat64ArrayImpl._(buffer.cloneAsDataView(newOffset, newLength));
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:js_interop';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
void main() {
|
||||
const size = 128;
|
||||
for (final buffer in createBuffers(128)) {
|
||||
Expect.equals(size, buffer.lengthInBytes);
|
||||
initData(buffer, size);
|
||||
forEachTypedData((viewConstructor, lengthFun, sublistFun, elementSize) {
|
||||
final sizeInElements = size ~/ elementSize;
|
||||
|
||||
final view = viewConstructor(
|
||||
buffer,
|
||||
/*offsetInBytes=*/ elementSize,
|
||||
/*length=*/ sizeInElements - 2,
|
||||
);
|
||||
print(
|
||||
'view: ${view.runtimeType} offsetInBytes:${view.offsetInBytes} len=${lengthFun(view)}',
|
||||
);
|
||||
Expect.equals(elementSize, view.offsetInBytes);
|
||||
Expect.equals(sizeInElements - 2, lengthFun(view));
|
||||
Expect.equals((sizeInElements - 2) * elementSize, view.lengthInBytes);
|
||||
verifyData(view, elementSize, size - 2 * elementSize);
|
||||
|
||||
// This creates a new list.
|
||||
final sublist = sublistFun(
|
||||
view,
|
||||
/*start=*/ 1,
|
||||
/*end=*/ sizeInElements - 3,
|
||||
);
|
||||
Expect.equals(0, sublist.offsetInBytes);
|
||||
Expect.equals(sizeInElements - 4, lengthFun(sublist));
|
||||
Expect.equals((sizeInElements - 4) * elementSize, sublist.lengthInBytes);
|
||||
|
||||
verifyData(sublist, 2 * elementSize, size - 4 * elementSize);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void forEachTypedData(
|
||||
void Function(
|
||||
TypedData Function(ByteBuffer, [int, int?]),
|
||||
int Function(TypedData),
|
||||
TypedData Function(TypedData, int, int?),
|
||||
int,
|
||||
)
|
||||
fun,
|
||||
) {
|
||||
fun(
|
||||
Uint8List.view,
|
||||
(td) => (td as Uint8List).length,
|
||||
(td, start, end) => (td as Uint8List).sublist(start, end),
|
||||
1,
|
||||
);
|
||||
fun(
|
||||
Int8List.view,
|
||||
(td) => (td as Int8List).length,
|
||||
(td, start, end) => (td as Int8List).sublist(start, end),
|
||||
1,
|
||||
);
|
||||
fun(
|
||||
Uint8ClampedList.view,
|
||||
(td) => (td as Uint8ClampedList).length,
|
||||
(td, start, end) => (td as Uint8ClampedList).sublist(start, end),
|
||||
1,
|
||||
);
|
||||
fun(
|
||||
Uint16List.view,
|
||||
(td) => (td as Uint16List).length,
|
||||
(td, start, end) => (td as Uint16List).sublist(start, end),
|
||||
2,
|
||||
);
|
||||
fun(
|
||||
Int16List.view,
|
||||
(td) => (td as Int16List).length,
|
||||
(td, start, end) => (td as Int16List).sublist(start, end),
|
||||
2,
|
||||
);
|
||||
fun(
|
||||
Uint32List.view,
|
||||
(td) => (td as Uint32List).length,
|
||||
(td, start, end) => (td as Uint32List).sublist(start, end),
|
||||
4,
|
||||
);
|
||||
fun(
|
||||
Int32List.view,
|
||||
(td) => (td as Int32List).length,
|
||||
(td, start, end) => (td as Int32List).sublist(start, end),
|
||||
4,
|
||||
);
|
||||
fun(
|
||||
Float32List.view,
|
||||
(td) => (td as Float32List).length,
|
||||
(td, start, end) => (td as Float32List).sublist(start, end),
|
||||
4,
|
||||
);
|
||||
fun(
|
||||
Float64List.view,
|
||||
(td) => (td as Float64List).length,
|
||||
(td, start, end) => (td as Float64List).sublist(start, end),
|
||||
8,
|
||||
);
|
||||
}
|
||||
|
||||
List<ByteBuffer> createBuffers(int size) {
|
||||
return [
|
||||
// JS backed.
|
||||
JSArrayBuffer(size).toDart,
|
||||
JSUint8Array.withLength(size).toDart.buffer,
|
||||
JSInt8Array.withLength(size).toDart.buffer,
|
||||
JSUint8ClampedArray.withLength(size).toDart.buffer,
|
||||
JSUint16Array.withLength(size ~/ 2).toDart.buffer,
|
||||
JSInt16Array.withLength(size ~/ 2).toDart.buffer,
|
||||
JSUint32Array.withLength(size ~/ 4).toDart.buffer,
|
||||
JSInt32Array.withLength(size ~/ 4).toDart.buffer,
|
||||
JSFloat32Array.withLength(size ~/ 4).toDart.buffer,
|
||||
JSFloat64Array.withLength(size ~/ 8).toDart.buffer,
|
||||
|
||||
// Dart backed.
|
||||
Uint8List(size).buffer,
|
||||
Int8List(size).buffer,
|
||||
Uint8ClampedList(size).buffer,
|
||||
Uint16List(size ~/ 2).buffer,
|
||||
Int16List(size ~/ 2).buffer,
|
||||
Uint32List(size ~/ 4).buffer,
|
||||
Int32List(size ~/ 4).buffer,
|
||||
Int64List(size ~/ 8).buffer,
|
||||
Float32List(size ~/ 4).buffer,
|
||||
Float64List(size ~/ 8).buffer,
|
||||
Float32x4List(size ~/ 16).buffer,
|
||||
Float64x2List(size ~/ 16).buffer,
|
||||
];
|
||||
}
|
||||
|
||||
void initData(ByteBuffer buffer, int length) {
|
||||
final td = buffer.asUint8List();
|
||||
for (int i = 0; i < length; i++) {
|
||||
td[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
void verifyData(TypedData typedData, int offset, int length) {
|
||||
final td = typedData.buffer.asUint8List(typedData.offsetInBytes);
|
||||
print('verifyData($offset, $length) ');
|
||||
for (int i = 0; i < length; i++) {
|
||||
Expect.equals(offset + i, td[i]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user