[dart:js_interop] Add typed array/data buffer constructors
- Adds constructors for JSArrayBuffer, JSDataView, and
concrete typed array types e.g. JSInt8Array.
While JSDataView and the typed array types can also take in
a SharedArrayBuffer, we currently don't have a type that
represents that type. Therefore we keep it as JSArrayBuffer
and not JSObject as that would be too general.
Change-Id: Iea1e08a9a8225accc7fc3550f0f8fe88039a1eb5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/343940
Reviewed-by: Leaf Petersen <leafp@google.com>
Commit-Queue: Srujan Gaddam <srujzs@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Reviewed-by: Ömer Ağacan <omersa@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 71b7277..229537a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,13 @@
[#56065]: https://github.com/dart-lang/sdk/issues/56065
+### Libraries
+
+#### `dart:js_interop`
+
+- Added constructors for `JSArrayBuffer`, `JSDataView`, and concrete typed array
+ types e.g. `JSInt8Array`.
+
## 3.5.0
### Language
diff --git a/sdk/lib/js_interop/js_interop.dart b/sdk/lib/js_interop/js_interop.dart
index 5c3d1c3..23595f8 100644
--- a/sdk/lib/js_interop/js_interop.dart
+++ b/sdk/lib/js_interop/js_interop.dart
@@ -192,12 +192,19 @@
/// A JavaScript `ArrayBuffer`.
@JS('ArrayBuffer')
extension type JSArrayBuffer._(JSArrayBufferRepType _jsArrayBuffer)
- implements JSObject {}
+ implements JSObject {
+ /// Creates a JavaScript `ArrayBuffer` of size [length] using an optional
+ /// [options] JavaScript object that sets the `maxByteLength`.
+ external JSArrayBuffer(int length, [JSObject options]);
+}
/// A JavaScript `DataView`.
@JS('DataView')
-extension type JSDataView._(JSDataViewRepType _jsDataView)
- implements JSObject {}
+extension type JSDataView._(JSDataViewRepType _jsDataView) implements JSObject {
+ /// Creates a JavaScript `DataView` with [buffer] as its backing storage,
+ /// offset by [byteOffset] bytes, of size [byteLength].
+ external JSDataView(JSArrayBuffer buffer, [int byteOffset, int byteLength]);
+}
/// Abstract supertype of all JavaScript typed arrays.
extension type JSTypedArray._(JSTypedArrayRepType _jsTypedArray)
@@ -206,47 +213,138 @@
/// A JavaScript `Int8Array`.
@JS('Int8Array')
extension type JSInt8Array._(JSInt8ArrayRepType _jsInt8Array)
- implements JSTypedArray {}
+ implements JSTypedArray {
+ /// Creates a JavaScript `Int8Array` with [buffer] as its backing storage,
+ /// offset by [byteOffset] bytes, of size [length].
+ ///
+ /// If no [buffer] is provided, creates an empty `Int8Array`.
+ external JSInt8Array([JSArrayBuffer buffer, int byteOffset, int length]);
+
+ /// Creates a JavaScript `Int8Array` of size [length] whose elements are
+ /// initialized to 0.
+ external JSInt8Array.withLength(int length);
+}
/// A JavaScript `Uint8Array`.
@JS('Uint8Array')
extension type JSUint8Array._(JSUint8ArrayRepType _jsUint8Array)
- implements JSTypedArray {}
+ implements JSTypedArray {
+ /// Creates a JavaScript `Uint8Array` with [buffer] as its backing storage,
+ /// offset by [byteOffset] bytes, of size [length].
+ ///
+ /// If no [buffer] is provided, creates an empty `Uint8Array`.
+ external JSUint8Array([JSArrayBuffer buffer, int byteOffset, int length]);
+
+ /// Creates a JavaScript `Uint8Array` of size [length] whose elements are
+ /// initialized to 0.
+ external JSUint8Array.withLength(int length);
+}
/// A JavaScript `Uint8ClampedArray`.
@JS('Uint8ClampedArray')
extension type JSUint8ClampedArray._(
- JSUint8ClampedArrayRepType _jsUint8ClampedArray) implements JSTypedArray {}
+ JSUint8ClampedArrayRepType _jsUint8ClampedArray) implements JSTypedArray {
+ /// Creates a JavaScript `Uint8ClampedArray` with [buffer] as its backing
+ /// storage, offset by [byteOffset] bytes, of size [length].
+ ///
+ /// If no [buffer] is provided, creates an empty `Uint8ClampedArray`.
+ external JSUint8ClampedArray(
+ [JSArrayBuffer buffer, int byteOffset, int length]);
+
+ /// Creates a JavaScript `Uint8ClampedArray` of size [length] whose elements
+ /// are initialized to 0.
+ external JSUint8ClampedArray.withLength(int length);
+}
/// A JavaScript `Int16Array`.
@JS('Int16Array')
extension type JSInt16Array._(JSInt16ArrayRepType _jsInt16Array)
- implements JSTypedArray {}
+ implements JSTypedArray {
+ /// Creates a JavaScript `Int16Array` with [buffer] as its backing storage,
+ /// offset by [byteOffset] bytes, of size [length].
+ ///
+ /// If no [buffer] is provided, creates an empty `Int16Array`.
+ external JSInt16Array([JSArrayBuffer buffer, int byteOffset, int length]);
+
+ /// Creates a JavaScript `Int16Array` of size [length] whose elements are
+ /// initialized to 0.
+ external JSInt16Array.withLength(int length);
+}
/// A JavaScript `Uint16Array`.
@JS('Uint16Array')
extension type JSUint16Array._(JSUint16ArrayRepType _jsUint16Array)
- implements JSTypedArray {}
+ implements JSTypedArray {
+ /// Creates a JavaScript `Uint16Array` with [buffer] as its backing storage,
+ /// offset by [byteOffset] bytes, of size [length].
+ ///
+ /// If no [buffer] is provided, creates an empty `Uint16Array`.
+ external JSUint16Array([JSArrayBuffer buffer, int byteOffset, int length]);
+
+ /// Creates a JavaScript `Uint16Array` of size [length] whose elements are
+ /// initialized to 0.
+ external JSUint16Array.withLength(int length);
+}
/// A JavaScript `Int32Array`.
@JS('Int32Array')
extension type JSInt32Array._(JSInt32ArrayRepType _jsInt32Array)
- implements JSTypedArray {}
+ implements JSTypedArray {
+ /// Creates a JavaScript `Int32Array` with [buffer] as its backing storage,
+ /// offset by [byteOffset] bytes, of size [length].
+ ///
+ /// If no [buffer] is provided, creates an empty `Int32Array`.
+ external JSInt32Array([JSArrayBuffer buffer, int byteOffset, int length]);
+
+ /// Creates a JavaScript `Int32Array` of size [length] whose elements are
+ /// initialized to 0.
+ external JSInt32Array.withLength(int length);
+}
/// A JavaScript `Uint32Array`.
@JS('Uint32Array')
extension type JSUint32Array._(JSUint32ArrayRepType _jsUint32Array)
- implements JSTypedArray {}
+ implements JSTypedArray {
+ /// Creates a JavaScript `Uint32Array` with [buffer] as its backing storage,
+ /// offset by [byteOffset] bytes, of size [length].
+ ///
+ /// If no [buffer] is provided, creates an empty `Uint32Array`.
+ external JSUint32Array([JSArrayBuffer buffer, int byteOffset, int length]);
+
+ /// Creates a JavaScript `Uint32Array` of size [length] whose elements are
+ /// initialized to 0.
+ external JSUint32Array.withLength(int length);
+}
/// A JavaScript `Float32Array`.
@JS('Float32Array')
extension type JSFloat32Array._(JSFloat32ArrayRepType _jsFloat32Array)
- implements JSTypedArray {}
+ implements JSTypedArray {
+ /// Creates a JavaScript `Float32Array` with [buffer] as its backing storage,
+ /// offset by [byteOffset] bytes, of size [length].
+ ///
+ /// If no [buffer] is provided, creates an empty `Float32Array`.
+ external JSFloat32Array([JSArrayBuffer buffer, int byteOffset, int length]);
+
+ /// Creates a JavaScript `Float32Array` of size [length] whose elements are
+ /// initialized to 0.
+ external JSFloat32Array.withLength(int length);
+}
/// A JavaScript `Float64Array`.
@JS('Float64Array')
extension type JSFloat64Array._(JSFloat64ArrayRepType _jsFloat64Array)
- implements JSTypedArray {}
+ implements JSTypedArray {
+ /// Creates a JavaScript `Float64Array` with [buffer] as its backing storage,
+ /// offset by [byteOffset] bytes, of size [length].
+ ///
+ /// If no [buffer] is provided, creates an empty `Float64Array`.
+ external JSFloat64Array([JSArrayBuffer buffer, int byteOffset, int length]);
+
+ /// Creates a JavaScript `Float64Array` of size [length] whose elements are
+ /// initialized to 0.
+ external JSFloat64Array.withLength(int length);
+}
// The various JavaScript primitive types. Crucially, unlike the Dart type
// hierarchy, none of these types are subtypes of [JSObject]. They are just
diff --git a/tests/lib/js/static_interop_test/js_types_test.dart b/tests/lib/js/static_interop_test/js_types_test.dart
index 6d8a3b8..2c20a29 100644
--- a/tests/lib/js/static_interop_test/js_types_test.dart
+++ b/tests/lib/js/static_interop_test/js_types_test.dart
@@ -10,6 +10,7 @@
import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';
+// TODO(srujzs): Delete this import and replace all uses with expect.dart.
import 'package:expect/minitest.dart'; // ignore: deprecated_member_use_from_same_package
const isJSBackend = const bool.fromEnvironment('dart.library.html');
@@ -27,7 +28,7 @@
@staticInterop
class SimpleObject {}
-extension SimpleObjectExtension on SimpleObject {
+extension on SimpleObject {
external JSString get foo;
}
@@ -52,12 +53,30 @@
@JS()
external JSArrayBuffer buf;
+extension on JSArrayBuffer {
+ external int get byteLength;
+ external int get maxByteLength;
+}
+
@JS()
external JSDataView dat;
+extension on JSDataView {
+ external JSArrayBuffer get buffer;
+ external int get byteLength;
+ external int get byteOffset;
+}
+
@JS()
external JSTypedArray tar;
+extension on JSTypedArray {
+ external JSArrayBuffer get buffer;
+ external int get byteLength;
+ external int get byteOffset;
+ external int get length;
+}
+
@JS()
external JSInt8Array ai8;
@@ -234,22 +253,67 @@
expect(confuse(buf) is JSArrayBuffer, true);
ByteBuffer dartBuf = buf.toDart;
expect(dartBuf.asUint8List(), equals([0, 255, 0, 255]));
+ buf = JSArrayBuffer(5);
+ expect(buf.byteLength, 5);
+ buf = JSArrayBuffer(5, {'maxByteLength': 12}.jsify() as JSObject);
+ expect(buf.maxByteLength, 12);
// [DataView] <-> [ByteData]
- dat = Uint8List.fromList([0, 255, 0, 255]).buffer.asByteData().toJS;
+ final datBuf = Uint8List.fromList([0, 255, 0, 255]).buffer.toJS;
+ dat = datBuf.toDart.asByteData().toJS;
expect(dat is JSDataView, true);
expect(confuse(dat) is JSDataView, true);
ByteData dartDat = dat.toDart;
expect(dartDat.getUint8(0), 0);
expect(dartDat.getUint8(1), 255);
+ dat = JSDataView(datBuf);
+ expect(dat.buffer, datBuf);
+ final dat2 = JSDataView(datBuf, 1, 3);
+ expect(dat2.byteOffset, 1);
+ expect(dat2.byteLength, 3);
// [TypedArray]s <-> [TypedData]s
+ // Test common TypedArray constructors for different subtypes.
+ void testTypedArrayConstructors(
+ JSTypedArray Function(JSArrayBuffer) createFromBuffer,
+ JSTypedArray Function(JSArrayBuffer, int, int)
+ createFromBufferOffsetAndLength,
+ JSTypedArray Function(int) createFromLength,
+ int byteSize) {
+ var byteLength = 16;
+ final buf = JSArrayBuffer(byteLength);
+ var typedArray = createFromBuffer(buf);
+ expect(typedArray.buffer, buf);
+ expect(typedArray.byteLength, byteLength);
+ expect(typedArray.byteOffset, 0);
+ expect(typedArray.length, byteLength ~/ byteSize);
+
+ byteLength = 8;
+ typedArray =
+ createFromBufferOffsetAndLength(buf, 8, byteLength ~/ byteSize);
+ expect(typedArray.buffer, buf);
+ expect(typedArray.byteLength, byteLength);
+ expect(typedArray.byteOffset, 8);
+ expect(typedArray.length, byteLength ~/ byteSize);
+
+ typedArray = createFromLength(byteLength ~/ byteSize);
+ expect(typedArray.byteLength, byteLength);
+ expect(typedArray.byteOffset, 0);
+ expect(typedArray.length, byteLength ~/ byteSize);
+ }
+
// Int8
ai8 = Int8List.fromList([-128, 0, 127]).toJS;
expect(ai8 is JSInt8Array, true);
expect(confuse(ai8) is JSInt8Array, true);
Int8List dartAi8 = ai8.toDart;
expect(dartAi8, equals([-128, 0, 127]));
+ testTypedArrayConstructors(
+ (JSArrayBuffer obj) => JSInt8Array(obj),
+ (JSArrayBuffer obj, int byteOffset, int length) =>
+ JSInt8Array(obj, byteOffset, length),
+ (int length) => JSInt8Array.withLength(length),
+ 1);
// Uint8
au8 = Uint8List.fromList([-1, 0, 255, 256]).toJS;
@@ -257,6 +321,12 @@
expect(confuse(au8) is JSUint8Array, true);
Uint8List dartAu8 = au8.toDart;
expect(dartAu8, equals([255, 0, 255, 0]));
+ testTypedArrayConstructors(
+ (JSArrayBuffer obj) => JSUint8Array(obj),
+ (JSArrayBuffer obj, int byteOffset, int length) =>
+ JSUint8Array(obj, byteOffset, length),
+ (int length) => JSUint8Array.withLength(length),
+ 1);
// Uint8Clamped
ac8 = Uint8ClampedList.fromList([-1, 0, 255, 256]).toJS;
@@ -264,6 +334,12 @@
expect(confuse(ac8) is JSUint8ClampedArray, true);
Uint8ClampedList dartAc8 = ac8.toDart;
expect(dartAc8, equals([0, 0, 255, 255]));
+ testTypedArrayConstructors(
+ (JSArrayBuffer obj) => JSUint8ClampedArray(obj),
+ (JSArrayBuffer obj, int byteOffset, int length) =>
+ JSUint8ClampedArray(obj, byteOffset, length),
+ (int length) => JSUint8ClampedArray.withLength(length),
+ 1);
// Int16
ai16 = Int16List.fromList([-32769, -32768, 0, 32767, 32768]).toJS;
@@ -271,6 +347,12 @@
expect(confuse(ai16) is JSInt16Array, true);
Int16List dartAi16 = ai16.toDart;
expect(dartAi16, equals([32767, -32768, 0, 32767, -32768]));
+ testTypedArrayConstructors(
+ (JSArrayBuffer obj) => JSInt16Array(obj),
+ (JSArrayBuffer obj, int byteOffset, int length) =>
+ JSInt16Array(obj, byteOffset, length),
+ (int length) => JSInt16Array.withLength(length),
+ 2);
// Uint16
au16 = Uint16List.fromList([-1, 0, 65535, 65536]).toJS;
@@ -278,6 +360,12 @@
expect(confuse(au16) is JSUint16Array, true);
Uint16List dartAu16 = au16.toDart;
expect(dartAu16, equals([65535, 0, 65535, 0]));
+ testTypedArrayConstructors(
+ (JSArrayBuffer obj) => JSUint16Array(obj),
+ (JSArrayBuffer obj, int byteOffset, int length) =>
+ JSUint16Array(obj, byteOffset, length),
+ (int length) => JSUint16Array.withLength(length),
+ 2);
// Int32
ai32 = Int32List.fromList([-2147483648, 0, 2147483647]).toJS;
@@ -285,6 +373,12 @@
expect(confuse(ai32) is JSInt32Array, true);
Int32List dartAi32 = ai32.toDart;
expect(dartAi32, equals([-2147483648, 0, 2147483647]));
+ testTypedArrayConstructors(
+ (JSArrayBuffer obj) => JSInt32Array(obj),
+ (JSArrayBuffer obj, int byteOffset, int length) =>
+ JSInt32Array(obj, byteOffset, length),
+ (int length) => JSInt32Array.withLength(length),
+ 4);
// Uint32
au32 = Uint32List.fromList([-1, 0, 4294967295, 4294967296]).toJS;
@@ -292,6 +386,12 @@
expect(confuse(au32) is JSUint32Array, true);
Uint32List dartAu32 = au32.toDart;
expect(dartAu32, equals([4294967295, 0, 4294967295, 0]));
+ testTypedArrayConstructors(
+ (JSArrayBuffer obj) => JSUint32Array(obj),
+ (JSArrayBuffer obj, int byteOffset, int length) =>
+ JSUint32Array(obj, byteOffset, length),
+ (int length) => JSUint32Array.withLength(length),
+ 4);
// Float32
af32 = Float32List.fromList([-1000.488, -0.00001, 0.0001, 10004.888]).toJS;
@@ -300,6 +400,12 @@
Float32List dartAf32 = af32.toDart;
expect(dartAf32,
equals(Float32List.fromList([-1000.488, -0.00001, 0.0001, 10004.888])));
+ testTypedArrayConstructors(
+ (JSArrayBuffer obj) => JSFloat32Array(obj),
+ (JSArrayBuffer obj, int byteOffset, int length) =>
+ JSFloat32Array(obj, byteOffset, length),
+ (int length) => JSFloat32Array.withLength(length),
+ 4);
// Float64
af64 = Float64List.fromList([-1000.488, -0.00001, 0.0001, 10004.888]).toJS;
@@ -307,6 +413,12 @@
expect(confuse(af64) is JSFloat64Array, true);
Float64List dartAf64 = af64.toDart;
expect(dartAf64, equals([-1000.488, -0.00001, 0.0001, 10004.888]));
+ testTypedArrayConstructors(
+ (JSArrayBuffer obj) => JSFloat64Array(obj),
+ (JSArrayBuffer obj, int byteOffset, int length) =>
+ JSFloat64Array(obj, byteOffset, length),
+ (int length) => JSFloat64Array.withLength(length),
+ 8);
// [JSNumber] <-> [double]
nbr = 4.5.toJS;