[dart2wasm] Use WasmI32 (instead of double) as representation of integers across interop boundary
Currently code uses `double` to represent array indices, array lengths
etc. This seems wrong because `double`s can loose precision. This is
also a high overhead.
Understandably one could use `double` and only start to loose precision
with more than 32 bits, but realistically string lenghts, array lengths
cannot exceed 31-bits (i.e. 2 GBs) in practice.
=> Use Wasmi32 to represent array indices, array lengths, bytes loaded
from integer arrays, ...
Change-Id: Ife540622c85118d890a8e487677db56e2ce06f5a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/372424
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
diff --git a/sdk/lib/_internal/wasm/lib/js_helper.dart b/sdk/lib/_internal/wasm/lib/js_helper.dart
index d2c21b1..67b06a8 100644
--- a/sdk/lib/_internal/wasm/lib/js_helper.dart
+++ b/sdk/lib/_internal/wasm/lib/js_helper.dart
@@ -142,8 +142,6 @@
}
// Convert to double to avoid converting to [BigInt] in the case of int64.
-WasmExternRef intToJSNumber(int i) => toJSNumber(i.toDouble())!;
-
WasmExternRef? getConstructorString(String constructor) =>
getPropertyRaw(globalThisRaw(), constructor.toExternRef);
@@ -231,15 +229,17 @@
WasmExternRef? toJSBoolean(bool b) => JS<WasmExternRef?>("b => !!b", b);
-double objectLength(WasmExternRef? o) => JS<double>("o => o.length", o);
+int objectLength(WasmExternRef? o) =>
+ JS<WasmI32>("o => o.length", o).toIntSigned();
-double byteLength(WasmExternRef? o) => JS<double>("o => o.byteLength", o);
+int byteLength(WasmExternRef? o) =>
+ JS<WasmI32>("o => o.byteLength", o).toIntSigned();
-double dataViewGetUint8(WasmExternRef? o, double i) =>
- JS<double>("(o, i) => o.getUint8(i)", o, i);
+int dataViewGetUint8(WasmExternRef? o, int i) =>
+ JS<WasmI32>("(o, i) => o.getUint8(i)", o, i.toWasmI32()).toIntSigned();
-WasmExternRef? objectReadIndex(WasmExternRef? o, double index) =>
- JS<WasmExternRef?>("(o, i) => o[i]", o, index);
+WasmExternRef? objectReadIndex(WasmExternRef? o, int index) =>
+ JS<WasmExternRef?>("(o, i) => o[i]", o, index.toWasmI32());
Function unwrapJSWrappedDartFunction(WasmExternRef? f) =>
JS<Function>("f => f.dartFunction", f);
@@ -272,14 +272,14 @@
WasmExternRef? jsFloat64ArrayFromDartFloat64List(Float64List l) =>
JS<WasmExternRef?>('l => arrayFromDartList(Float64Array, l)', l);
-WasmExternRef? jsDataViewFromDartByteData(ByteData data, double length) =>
+WasmExternRef? jsDataViewFromDartByteData(ByteData data, int length) =>
JS<WasmExternRef?>("""(data, length) => {
const view = new DataView(new ArrayBuffer(length));
for (let i = 0; i < length; i++) {
view.setUint8(i, dartInstance.exports.\$byteDataGetUint8(data, i));
}
return view;
- }""", data, length);
+ }""", data, length.toWasmI32());
WasmExternRef? jsArrayFromDartList(List<Object?> l) =>
JS<WasmExternRef?>('l => arrayFromDartList(Array, l)', l);
@@ -295,7 +295,7 @@
WasmExternRef? newArrayRaw() => JS<WasmExternRef?>('() => []');
WasmExternRef? newArrayFromLengthRaw(int length) =>
- JS<WasmExternRef?>('l => new Array(l)', length.toDouble());
+ JS<WasmExternRef?>('l => new Array(l)', length.toWasmI32());
WasmExternRef? globalThisRaw() => JS<WasmExternRef?>('() => globalThis');
@@ -345,7 +345,7 @@
WasmExternRef? jsArrayBufferFromDartByteBuffer(ByteBuffer buffer) {
ByteData byteData = ByteData.view(buffer);
WasmExternRef? dataView =
- jsDataViewFromDartByteData(byteData, byteData.lengthInBytes.toDouble());
+ jsDataViewFromDartByteData(byteData, byteData.lengthInBytes);
return getPropertyRaw(dataView, 'buffer'.toExternRef);
}
@@ -407,7 +407,7 @@
} else if (object is js_types.JSDataViewImpl) {
return object.toExternRef;
} else if (object is ByteData) {
- return jsDataViewFromDartByteData(object, object.lengthInBytes.toDouble());
+ return jsDataViewFromDartByteData(object, object.lengthInBytes);
} else if (object is List<Object?>) {
return jsArrayFromDartList(object);
} else if (object is num) {
@@ -506,27 +506,27 @@
int length = byteLength(ref).toInt();
ByteData data = ByteData(length);
for (int i = 0; i < length; i++) {
- data.setUint8(i, dataViewGetUint8(ref, i.toDouble()).toInt());
+ data.setUint8(i, dataViewGetUint8(ref, i));
}
return data;
}
List<double> jsFloatTypedArrayToDartFloatTypedData(
WasmExternRef? ref, List<double> makeTypedData(int size)) {
- int length = objectLength(ref).toInt();
+ int length = objectLength(ref);
List<double> list = makeTypedData(length);
for (int i = 0; i < length; i++) {
- list[i] = toDartNumber(objectReadIndex(ref, i.toDouble()));
+ list[i] = toDartNumber(objectReadIndex(ref, i));
}
return list;
}
List<int> jsIntTypedArrayToDartIntTypedData(
WasmExternRef? ref, List<int> makeTypedData(int size)) {
- int length = objectLength(ref).toInt();
+ int length = objectLength(ref);
List<int> list = makeTypedData(length);
for (int i = 0; i < length; i++) {
- list[i] = toDartNumber(objectReadIndex(ref, i.toDouble())).toInt();
+ list[i] = toDartNumber(objectReadIndex(ref, i)).toInt();
}
return list;
}
@@ -541,12 +541,10 @@
}
List<JSAny?> toDartListJSAny(WasmExternRef? ref) => List<JSAny?>.generate(
- objectLength(ref).round(),
- (int n) => JSValue(objectReadIndex(ref, n.toDouble())) as JSAny?);
+ objectLength(ref), (int n) => JSValue(objectReadIndex(ref, n)) as JSAny?);
List<Object?> toDartList(WasmExternRef? ref) => List<Object?>.generate(
- objectLength(ref).round(),
- (int n) => dartifyRaw(objectReadIndex(ref, n.toDouble())));
+ objectLength(ref), (int n) => dartifyRaw(objectReadIndex(ref, n)));
// These two trivial helpers are needed to work around an issue with tearing off
// functions that take / return [WasmExternRef].
@@ -596,12 +594,12 @@
/// Methods used by the wasm runtime.
@pragma("wasm:export", "\$listLength")
-double _listLength(List list) => list.length.toDouble();
+WasmI32 _listLength(List list) => list.length.toWasmI32();
@pragma("wasm:export", "\$listRead")
-WasmExternRef? _listRead(List<Object?> list, double index) =>
- jsifyRaw(list[index.toInt()]);
+WasmExternRef? _listRead(List<Object?> list, WasmI32 index) =>
+ jsifyRaw(list[index.toIntSigned()]);
@pragma("wasm:export", "\$byteDataGetUint8")
-double _byteDataGetUint8(ByteData byteData, double index) =>
- byteData.getUint8(index.toInt()).toDouble();
+WasmI32 _byteDataGetUint8(ByteData byteData, WasmI32 index) =>
+ byteData.getUint8(index.toIntSigned()).toWasmI32();
diff --git a/sdk/lib/_internal/wasm/lib/js_interop_patch.dart b/sdk/lib/_internal/wasm/lib/js_interop_patch.dart
index 14db7dd..a8b32b3 100644
--- a/sdk/lib/_internal/wasm/lib/js_interop_patch.dart
+++ b/sdk/lib/_internal/wasm/lib/js_interop_patch.dart
@@ -215,7 +215,7 @@
final t = this;
return JSDataView._(JSValue(t is js_types.JSDataViewImpl
? t.toExternRef
- : jsDataViewFromDartByteData(t, lengthInBytes.toDouble())));
+ : jsDataViewFromDartByteData(t, lengthInBytes)));
}
}
diff --git a/sdk/lib/_internal/wasm/lib/js_util_patch.dart b/sdk/lib/_internal/wasm/lib/js_util_patch.dart
index 11ea89e..f8d54dc 100644
--- a/sdk/lib/_internal/wasm/lib/js_util_patch.dart
+++ b/sdk/lib/_internal/wasm/lib/js_util_patch.dart
@@ -256,7 +256,7 @@
convertedObjects[o] = dartList;
final length = getProperty<double>(o, 'length').toInt();
for (int i = 0; i < length; i++) {
- dartList.add(convert(JSValue.box(objectReadIndex(ref, i.toDouble()))));
+ dartList.add(convert(JSValue.box(objectReadIndex(ref, i))));
}
return dartList;
} else {
diff --git a/sdk/lib/_internal/wasm/lib/string.dart b/sdk/lib/_internal/wasm/lib/string.dart
index ec6b650..2618720 100644
--- a/sdk/lib/_internal/wasm/lib/string.dart
+++ b/sdk/lib/_internal/wasm/lib/string.dart
@@ -1630,31 +1630,31 @@
// String accessors used to perform Dart<->JS string conversion
@pragma("wasm:export", "\$stringLength")
-double _stringLength(String string) {
- return string.length.toDouble();
+WasmI32 _stringLength(String string) {
+ return string.length.toWasmI32();
}
@pragma("wasm:export", "\$stringRead")
-double _stringRead(String string, double index) {
- return string.codeUnitAt(index.toInt()).toDouble();
+WasmI32 _stringRead(String string, WasmI32 index) {
+ return string.codeUnitAt(index.toIntSigned()).toWasmI32();
}
@pragma("wasm:export", "\$stringAllocate1")
-OneByteString _stringAllocate1(double length) {
- return OneByteString.withLength(length.toInt());
+OneByteString _stringAllocate1(WasmI32 length) {
+ return OneByteString.withLength(length.toIntSigned());
}
@pragma("wasm:export", "\$stringWrite1")
-void _stringWrite1(OneByteString string, double index, double codePoint) {
- writeIntoOneByteString(string, index.toInt(), codePoint.toInt());
+void _stringWrite1(OneByteString string, WasmI32 index, WasmI32 codePoint) {
+ writeIntoOneByteString(string, index.toIntSigned(), codePoint.toIntSigned());
}
@pragma("wasm:export", "\$stringAllocate2")
-TwoByteString _stringAllocate2(double length) {
- return TwoByteString.withLength(length.toInt());
+TwoByteString _stringAllocate2(WasmI32 length) {
+ return TwoByteString.withLength(length.toIntSigned());
}
@pragma("wasm:export", "\$stringWrite2")
-void _stringWrite2(TwoByteString string, double index, double codePoint) {
- writeIntoTwoByteString(string, index.toInt(), codePoint.toInt());
+void _stringWrite2(TwoByteString string, WasmI32 index, WasmI32 codePoint) {
+ writeIntoTwoByteString(string, index.toIntSigned(), codePoint.toIntSigned());
}