[dart2wasm] Start using WebAssembly.String imports

Refactor `JSStringImpl` to use the js-string-builtins API[1].

When the module `WebAssembly.String` is not available we define a
"polyfill" with the previous imports, so this change is backwards
compatible.

Also updates some of the methods to use avoid multiple `this.length`
calls (which do an FFI call), and use unchecked getters when possible.

A new library `dart:_error_utils` is introduced for faster range and
index checks when the length (or max value) is known to be positive
(e.g. when it's the length of a string).

For now only `JSStringImpl` and `operator []` and `operator []=` of
`JSArrayImpl` are updated to use the new range and index checks. Rest of
the libraries will be updated separately.

[1]: https://github.com/WebAssembly/js-string-builtins

CoreLibraryReviewExempt: dart2wasm specific library change.
Change-Id: I9436def0cfe59c631f6f4e15ea06cc18a47a738e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/335043
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Ömer Ağacan <omersa@google.com>
This commit is contained in:
Ömer Sinan Ağacan 2023-12-08 10:04:41 +00:00 committed by Commit Queue
parent d27b887fec
commit 7cceaebd49
13 changed files with 242 additions and 103 deletions

View file

@ -370,6 +370,11 @@ class Intrinsifier {
codeGen.wrap(node.arguments.positional[0], w.NumType.i64);
b.i64_le_u();
return boolType;
case "ltU":
codeGen.wrap(receiver, w.NumType.i64);
codeGen.wrap(node.arguments.positional[0], w.NumType.i64);
b.i64_lt_u();
return boolType;
default:
throw 'Unknown WasmI64 member $name';
}

View file

@ -95,6 +95,23 @@ const jsRuntimeBlobPart3 = r'''
return wrapped;
}
if (WebAssembly.String === undefined) {
console.log("WebAssembly.String is undefined, adding polyfill");
WebAssembly.String = {
"charCodeAt": (s, i) => s.charCodeAt(i),
"compare": (s1, s2) => {
if (s1 < s2) return -1;
if (s1 > s2) return 1;
return 0;
},
"concat": (s1, s2) => s1 + s2,
"equals": (s1, s2) => s1 === s2,
"fromCharCode": (i) => String.fromCharCode(i),
"length": (s) => s.length,
"substring": (s, a, b) => s.substring(a, b),
};
}
// Imports
const dart2wasm = {
''';

View file

@ -409,6 +409,7 @@ class Chrome extends Browser {
"--no-first-run",
"--use-mock-keychain",
"--user-data-dir=${userDir.path}",
"--js-flags=--experimental-wasm-imported-strings",
url,
];

View file

@ -604,6 +604,7 @@ class Dart2WasmCompilerConfiguration extends CompilerConfiguration {
final isD8 = runtimeConfiguration is D8RuntimeConfiguration;
return [
if (isD8) '--turboshaft-wasm',
if (isD8) '--experimental-wasm-imported-strings',
'pkg/dart2wasm/bin/run_wasm.js',
if (isD8) '--',
'${filename.substring(0, filename.lastIndexOf('.'))}.mjs',

View file

@ -36,6 +36,6 @@ if [[ $D8_OPTIONS ]]; then
fi
# Find the JS runtime based on the input wasm file.
exec "$D8_EXEC" --turboshaft-wasm \
exec "$D8_EXEC" --turboshaft-wasm --experimental-wasm-imported-strings \
"${EXTRA_D8_OPTIONS[@]}" \
"$SDK_DIR/pkg/dart2wasm/bin/run_wasm.js" -- "$(realpath -- "${1%.*}.mjs")" "$@"

View file

@ -0,0 +1,42 @@
// Copyright (c) 2023, 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:_wasm';
class IndexErrorUtils {
/// Same as [IndexError.check], but assumes that [length] is positive and
/// uses a single unsigned comparison. Always inlined.
@pragma("wasm:prefer-inline")
static void checkAssumePositiveLength(int index, int length) {
if (WasmI64.fromInt(length).leU(WasmI64.fromInt(index))) {
throw IndexError.withLength(index, length);
}
}
}
class RangeErrorUtils {
/// Same as `RangeError.checkValueInInterval(value, 0, maxValue)`, but
/// assumes that [maxValue] is positive and uses a single unsigned
/// comparison. Always inlined.
@pragma("wasm:prefer-inline")
static void checkValueBetweenZeroAndPositiveMax(int value, int maxValue) {
if (WasmI64.fromInt(maxValue).ltU(WasmI64.fromInt(value))) {
throw RangeError.range(value, 0, maxValue);
}
}
/// Same as [RangeError.checkValidRange], but assumes that [length] is
/// positive and does less checks. Error reporting is also slightly
/// different: when both [start] and [end] are negative, this reports [end]
/// instead of [start]. Always inlined.
@pragma("wasm:prefer-inline")
static void checkValidRangePositiveLength(int start, int end, int length) {
if (WasmI64.fromInt(length).ltU(WasmI64.fromInt(end))) {
throw RangeError.range(end, 0, length);
}
if (WasmI64.fromInt(end).ltU(WasmI64.fromInt(start))) {
throw RangeError.range(start, 0, end);
}
}
}

View file

@ -466,18 +466,30 @@ class JSArrayImpl implements List<JSAny?> {
'(a, l) => a.length = l', toExternRef, newLength.toJS.toExternRef);
}
@override
JSAny? operator [](int index) {
RangeError.checkValueInInterval(index, 0, length - 1);
return js.JSValue.boxT<JSAny?>(js.JS<WasmExternRef?>(
'(a, i) => a[i]', toExternRef, index.toJS.toExternRef));
}
@pragma("wasm:prefer-inline")
JSAny? _getUnchecked(int index) =>
js.JSValue.boxT<JSAny?>(js.JS<WasmExternRef?>(
'(a, i) => a[i]', toExternRef, index.toJS.toExternRef));
@override
@pragma("wasm:prefer-inline")
JSAny? operator [](int index) {
IndexErrorUtils.checkAssumePositiveLength(index, length);
return _getUnchecked(index);
}
@pragma("wasm:prefer-inline")
void _setUnchecked(int index, JSAny? value) => js.JS<void>(
'(a, i, v) => a[i] = v',
toExternRef,
index.toJS.toExternRef,
value.toExternRef);
@override
@pragma("wasm:prefer-inline")
void operator []=(int index, JSAny? value) {
RangeError.checkValueInInterval(index, 0, length - 1);
js.JS<void>('(a, i, v) => a[i] = v', toExternRef, index.toJS.toExternRef,
value.toExternRef);
IndexErrorUtils.checkAssumePositiveLength(index, length);
_setUnchecked(index, value);
}
@override

View file

@ -4,60 +4,71 @@
part of dart._js_types;
String _matchString(Match match) => match[0]!;
String _stringIdentity(String string) => string;
/// The box class for JS' `string` object. [JSStringImpl] is heavily based off
/// of `sdk/lib/_internal/js_runtime/lib/js_string.dart`. TODO(joshualitt): Add
/// `JSString` fastpaths for cases where `String` arguments are really
/// `JSString`.
final class JSStringImpl implements String {
final WasmExternRef? _ref;
JSStringImpl(this._ref);
@pragma("wasm:prefer-inline")
static String? box(WasmExternRef? ref) =>
js.isDartNull(ref) ? null : JSStringImpl(ref);
@pragma("wasm:prefer-inline")
WasmExternRef? get toExternRef => _ref;
@override
@pragma("wasm:prefer-inline")
int get length => _jsLength(toExternRef);
@override
@pragma("wasm:prefer-inline")
bool get isEmpty => length == 0;
@override
@pragma("wasm:prefer-inline")
bool get isNotEmpty => !isEmpty;
@pragma("wasm:entry-point")
static String interpolate(List<Object?> values) {
final array = JSArrayImpl.fromLength(values.length);
for (int i = 0; i < values.length; i++) {
final valuesLength = values.length;
final array = JSArrayImpl.fromLength(valuesLength);
for (int i = 0; i < valuesLength; i++) {
final o = values[i];
final s = o.toString();
final jsString =
s is JSStringImpl ? js.JSValue.boxT<JSAny?>(s.toExternRef) : s.toJS;
array[i] = jsString;
array._setUnchecked(i, jsString);
}
return JSStringImpl(
js.JS<WasmExternRef?>("a => a.join('')", array.toExternRef));
}
@override
@pragma("wasm:prefer-inline")
int codeUnitAt(int index) {
RangeError.checkValueInInterval(index, 0, length - 1);
return js
.JS<double>('(s, i) => s.charCodeAt(i)', toExternRef,
index.toDouble().toExternRef)
.toInt();
final length = this.length;
IndexErrorUtils.checkAssumePositiveLength(index, length);
return _codeUnitAtUnchecked(index);
}
@pragma("wasm:prefer-inline")
int _codeUnitAtUnchecked(int index) {
return _jsCharCodeAt(toExternRef, index);
}
@override
Iterable<Match> allMatches(String string, [int start = 0]) {
if (0 > start || start > string.length) {
throw new RangeError.range(start, 0, string.length);
}
final stringLength = string.length;
RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(start, stringLength);
return StringAllMatchesIterable(string, this, start);
}
@override
Match? matchAsPrefix(String string, [int start = 0]) {
if (start < 0 || start > string.length) {
throw new RangeError.range(start, 0, string.length);
}
if (start + length > string.length) return null;
final stringLength = string.length;
RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(start, stringLength);
final length = this.length;
if (start + length > stringLength) return null;
// TODO(lrn): See if this can be optimized.
for (int i = 0; i < length; i++) {
if (string.codeUnitAt(start + i) != codeUnitAt(i)) {
@ -70,8 +81,7 @@ final class JSStringImpl implements String {
@override
String operator +(String other) {
if (other is JSStringImpl) {
return JSStringImpl(js.JS<WasmExternRef?>(
'(a, b) => a + b', toExternRef, other.toExternRef));
return JSStringImpl(_jsConcat(toExternRef, other.toExternRef));
}
// TODO(joshualitt): Refactor `string_patch.dart` so we can directly
@ -81,7 +91,8 @@ final class JSStringImpl implements String {
@override
bool endsWith(String other) {
int otherLength = other.length;
final otherLength = other.length;
final length = this.length;
if (otherLength > length) return false;
return other == substring(length - otherLength);
}
@ -102,6 +113,7 @@ final class JSStringImpl implements String {
} else {
StringBuffer result = StringBuffer();
result.write(to);
final length = this.length;
for (int i = 0; i < length; i++) {
result.write(this[i]);
result.write(to);
@ -143,12 +155,13 @@ final class JSStringImpl implements String {
if (onMatch == null) onMatch = _matchString;
if (onNonMatch == null) onNonMatch = _stringIdentity;
if (from is String) {
int patternLength = from.length;
final patternLength = from.length;
if (patternLength == 0) {
// Pattern is the empty string.
StringBuffer buffer = StringBuffer();
int i = 0;
buffer.write(onNonMatch(""));
final length = this.length;
while (i < length) {
buffer.write(onMatch(StringMatch(i, this, "")));
// Special case to avoid splitting a surrogate pair.
@ -172,6 +185,7 @@ final class JSStringImpl implements String {
}
StringBuffer buffer = StringBuffer();
int startIndex = 0;
final length = this.length;
while (startIndex < length) {
int position = indexOf(from, startIndex);
if (position == -1) {
@ -212,7 +226,7 @@ final class JSStringImpl implements String {
@override
String replaceFirst(Pattern from, String to, [int startIndex = 0]) {
RangeError.checkValueInInterval(startIndex, 0, length, "startIndex");
RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(startIndex, length);
if (from is String) {
int index = indexOf(from, startIndex);
if (index < 0) return this;
@ -233,7 +247,7 @@ final class JSStringImpl implements String {
@override
String replaceFirstMapped(Pattern from, String replace(Match match),
[int startIndex = 0]) {
RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex");
RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(startIndex, length);
Iterator<Match> matches = from.allMatches(this, startIndex).iterator;
if (!matches.moveNext()) return this;
Match match = matches.current;
@ -292,15 +306,14 @@ final class JSStringImpl implements String {
@override
String replaceRange(int start, int? end, String replacement) {
final e = RangeError.checkValidRange(start, end, length);
return _replaceRange(start, e, replacement);
end ??= length;
RangeErrorUtils.checkValidRangePositiveLength(start, end, length);
return _replaceRange(start, end, replacement);
}
@override
bool startsWith(Pattern pattern, [int index = 0]) {
if (index < 0 || index > length) {
throw RangeError.range(index, 0, length);
}
RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(index, length);
if (pattern is String) {
final patternLength = pattern.length;
final endIndex = index + patternLength;
@ -312,9 +325,9 @@ final class JSStringImpl implements String {
@override
String substring(int start, [int? end]) {
end = RangeError.checkValidRange(start, end, this.length);
return JSStringImpl(js.JS<WasmExternRef?>('(o, s, i) => o.substring(s, i)',
toExternRef, start.toDouble(), end.toDouble()));
end ??= length;
RangeErrorUtils.checkValidRangePositiveLength(start, end, length);
return JSStringImpl(_jsSubstring(toExternRef, start, end));
}
@override
@ -398,9 +411,10 @@ final class JSStringImpl implements String {
/// Finds the index of the first non-whitespace character, or the
/// end of the string. Start looking at position [index].
static int _skipLeadingWhitespace(String string, int index) {
while (index < string.length) {
int codeUnit = string.codeUnitAt(index);
static int _skipLeadingWhitespace(JSStringImpl string, int index) {
final stringLength = string.length;
while (index < stringLength) {
int codeUnit = string._codeUnitAtUnchecked(index);
if (codeUnit != spaceCodeUnit &&
codeUnit != carriageReturnCodeUnit &&
!_isWhitespace(codeUnit)) {
@ -413,9 +427,9 @@ final class JSStringImpl implements String {
/// Finds the index after the last non-whitespace character, or 0.
/// Start looking at position [index - 1].
static int _skipTrailingWhitespace(String string, int index) {
static int _skipTrailingWhitespace(JSStringImpl string, int index) {
while (index > 0) {
int codeUnit = string.codeUnitAt(index - 1);
int codeUnit = string._codeUnitAtUnchecked(index - 1);
if (codeUnit != spaceCodeUnit &&
codeUnit != carriageReturnCodeUnit &&
!_isWhitespace(codeUnit)) {
@ -426,7 +440,7 @@ final class JSStringImpl implements String {
return index;
}
// Dart2Wasm can't use JavaScript trim directly,
// dart2wasm can't use JavaScript trim directly,
// because JavaScript does not trim
// the NEXT LINE (NEL) character (0x85).
@override
@ -435,26 +449,27 @@ final class JSStringImpl implements String {
// either end of the string.
final result =
JSStringImpl(js.JS<WasmExternRef?>('s => s.trim()', toExternRef));
if (result.length == 0) return result;
int firstCode = result.codeUnitAt(0);
final resultLength = result.length;
if (resultLength == 0) return result;
int firstCode = result._codeUnitAtUnchecked(0);
int startIndex = 0;
if (firstCode == nelCodeUnit) {
startIndex = _skipLeadingWhitespace(result, 1);
if (startIndex == result.length) return "";
if (startIndex == resultLength) return "";
}
int endIndex = result.length;
int endIndex = resultLength;
// We know that there is at least one character that is non-whitespace.
// Therefore we don't need to verify that endIndex > startIndex.
int lastCode = result.codeUnitAt(endIndex - 1);
if (lastCode == nelCodeUnit) {
endIndex = _skipTrailingWhitespace(result, endIndex - 1);
}
if (startIndex == 0 && endIndex == result.length) return result;
if (startIndex == 0 && endIndex == resultLength) return result;
return substring(startIndex, endIndex);
}
// Dart2Wasm can't use JavaScript trimLeft directly because it does not trim
// dart2wasm can't use JavaScript trimLeft directly because it does not trim
// the NEXT LINE character (0x85).
@override
String trimLeft() {
@ -463,17 +478,18 @@ final class JSStringImpl implements String {
int startIndex = 0;
final result =
JSStringImpl(js.JS<WasmExternRef?>('s => s.trimLeft()', toExternRef));
if (result.length == 0) return result;
int firstCode = result.codeUnitAt(0);
final resultLength = result.length;
if (resultLength == 0) return result;
int firstCode = result._codeUnitAtUnchecked(0);
if (firstCode == nelCodeUnit) {
startIndex = _skipLeadingWhitespace(result, 1);
}
if (startIndex == 0) return result;
if (startIndex == result.length) return "";
if (startIndex == resultLength) return "";
return result.substring(startIndex);
}
// Dart2Wasm can't use JavaScript trimRight directly because it does not trim
// dart2wasm can't use JavaScript trimRight directly because it does not trim
// the NEXT LINE character (0x85).
@override
String trimRight() {
@ -481,14 +497,15 @@ final class JSStringImpl implements String {
// string.
final result =
JSStringImpl(js.JS<WasmExternRef?>('s => s.trimRight()', toExternRef));
int endIndex = result.length;
final resultLength = result.length;
int endIndex = resultLength;
if (endIndex == 0) return result;
int lastCode = result.codeUnitAt(endIndex - 1);
if (lastCode == nelCodeUnit) {
endIndex = _skipTrailingWhitespace(result, endIndex - 1);
}
if (endIndex == result.length) return result;
if (endIndex == resultLength) return result;
if (endIndex == 0) return "";
return result.substring(0, endIndex);
}
@ -503,16 +520,16 @@ final class JSStringImpl implements String {
@override
String padLeft(int width, [String padding = ' ']) {
int delta = width - this.length;
int delta = width - length;
if (delta <= 0) return this;
return padding * delta + this;
return (padding * delta) + this;
}
@override
String padRight(int width, [String padding = ' ']) {
int delta = width - this.length;
int delta = width - length;
if (delta <= 0) return this;
return this + padding * delta;
return this + (padding * delta);
}
@override
@ -528,9 +545,8 @@ final class JSStringImpl implements String {
@override
int indexOf(Pattern pattern, [int start = 0]) {
if (start < 0 || start > this.length) {
throw RangeError.range(start, 0, this.length);
}
final length = this.length;
RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(start, length);
if (pattern is JSStringImpl) {
return _jsIndexOf(pattern.toExternRef, start);
} else if (pattern is String) {
@ -539,7 +555,7 @@ final class JSStringImpl implements String {
Match? match = js.firstMatchAfter(pattern, this, start);
return (match == null) ? -1 : match.start;
} else {
for (int i = start; i <= this.length; i++) {
for (int i = start; i <= length; i++) {
if (pattern.matchAsPrefix(this, i) != null) return i;
}
return -1;
@ -553,10 +569,11 @@ final class JSStringImpl implements String {
@override
int lastIndexOf(Pattern pattern, [int? start]) {
final length = this.length;
if (start == null) {
start = length;
} else if (start < 0 || start > this.length) {
throw RangeError.range(start, 0, this.length);
} else {
RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(start, length);
}
if (pattern is JSStringImpl) {
if (start + pattern.length > length) {
@ -577,9 +594,8 @@ final class JSStringImpl implements String {
@override
bool contains(Pattern other, [int startIndex = 0]) {
if (startIndex < 0 || startIndex > this.length) {
throw RangeError.range(startIndex, 0, this.length);
}
final length = this.length;
RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(startIndex, length);
if (other is String) {
return indexOf(other, startIndex) >= 0;
} else if (other is js.JSSyntaxRegExp) {
@ -589,31 +605,24 @@ final class JSStringImpl implements String {
}
}
@override
bool get isEmpty => length == 0;
@override
bool get isNotEmpty => !isEmpty;
/// This must be kept in sync with `StringBase.hashCode` in string_patch.dart.
/// TODO(joshualitt): Find some way to cache the hash code.
@override
int get hashCode {
int hash = 0;
final length = this.length;
for (int i = 0; i < length; i++) {
hash = stringCombineHashes(hash, codeUnitAt(i));
hash = stringCombineHashes(hash, _codeUnitAtUnchecked(i));
}
return stringFinalizeHash(hash);
}
@override
int get length => js.JS<double>('s => s.length', toExternRef).toInt();
@override
@pragma("wasm:prefer-inline")
String operator [](int index) {
RangeError.checkValueInInterval(index, 0, length - 1);
return JSStringImpl(js.JS<WasmExternRef?>(
'(s, i) => s[i]', toExternRef, index.toDouble().toExternRef));
final length = this.length;
IndexErrorUtils.checkAssumePositiveLength(index, length);
return JSStringImpl(_jsFromCharCode(_codeUnitAtUnchecked(index)));
}
@override
@ -621,25 +630,34 @@ final class JSStringImpl implements String {
if (identical(this, other)) {
return true;
}
if (other is JSStringImpl && length == other.length) {
return js.areEqualInJS(toExternRef, other.toExternRef);
} else if (other is String && length == other.length) {
if (other is JSStringImpl) {
return _jsEquals(toExternRef, other.toExternRef);
}
final length = this.length;
if (other is String && length == other.length) {
for (int i = 0; i < length; i++) {
if (codeUnitAt(i) != other.codeUnitAt(i)) {
if (_codeUnitAtUnchecked(i) != other.codeUnitAt(i)) {
return false;
}
}
return true;
}
return false;
}
@override
int compareTo(String other) {
int otherLength = other.length;
int len = (length < otherLength) ? length : otherLength;
if (other is JSStringImpl) {
return _jsCompare(toExternRef, other.toExternRef);
}
final otherLength = other.length;
final length = this.length;
final len = (length < otherLength) ? length : otherLength;
for (int i = 0; i < len; i++) {
int thisCodeUnit = this.codeUnitAt(i);
int thisCodeUnit = _codeUnitAtUnchecked(i);
int otherCodeUnit = other.codeUnitAt(i);
if (thisCodeUnit < otherCodeUnit) {
return -1;
@ -657,10 +675,10 @@ final class JSStringImpl implements String {
String toString() => js.stringify(toExternRef);
int firstNonWhitespace() {
final len = this.length;
final length = this.length;
int first = 0;
for (; first < len; first++) {
if (!_isWhitespace(this.codeUnitAt(first))) {
for (; first < length; first++) {
if (!_isWhitespace(_codeUnitAtUnchecked(first))) {
break;
}
}
@ -668,9 +686,9 @@ final class JSStringImpl implements String {
}
int lastNonWhitespace() {
int last = this.length - 1;
int last = length - 1;
for (; last >= 0; last--) {
if (!_isWhitespace(this.codeUnitAt(last))) {
if (!_isWhitespace(_codeUnitAtUnchecked(last))) {
break;
}
}
@ -678,6 +696,10 @@ final class JSStringImpl implements String {
}
}
String _matchString(Match match) => match[0]!;
String _stringIdentity(String string) => string;
@pragma("wasm:export", "\$jsStringToJSStringImpl")
JSStringImpl _jsStringToJSStringImpl(WasmExternRef? string) =>
JSStringImpl(string);
@ -688,3 +710,34 @@ WasmExternRef? _jsStringFromJSStringImpl(JSStringImpl string) =>
bool _jsIdentical(WasmExternRef? ref1, WasmExternRef? ref2) =>
js.JS<bool>('Object.is', ref1, ref2);
@pragma("wasm:prefer-inline")
int _jsCharCodeAt(WasmExternRef? stringRef, int index) => js
.JS<WasmI32>(
'WebAssembly.String.charCodeAt', stringRef, WasmI32.fromInt(index))
.toIntUnsigned();
WasmExternRef _jsConcat(WasmExternRef? s1, WasmExternRef? s2) =>
js.JS<WasmExternRef>('WebAssembly.String.concat', s1, s2);
@pragma("wasm:prefer-inline")
WasmExternRef _jsSubstring(
WasmExternRef? stringRef, int startIndex, int endIndex) =>
js.JS<WasmExternRef>('WebAssembly.String.substring', stringRef,
WasmI32.fromInt(startIndex), WasmI32.fromInt(endIndex));
@pragma("wasm:prefer-inline")
int _jsLength(WasmExternRef? stringRef) =>
js.JS<WasmI32>('WebAssembly.String.length', stringRef).toIntUnsigned();
@pragma("wasm:prefer-inline")
bool _jsEquals(WasmExternRef? s1, WasmExternRef? s2) =>
js.JS<WasmI32>('WebAssembly.String.equals', s1, s2).toBool();
@pragma("wasm:prefer-inline")
int _jsCompare(WasmExternRef? s1, WasmExternRef? s2) =>
js.JS<WasmI32>('WebAssembly.String.compare', s1, s2).toIntSigned();
@pragma("wasm:prefer-inline")
WasmExternRef _jsFromCharCode(int charCode) => js.JS<WasmExternRef>(
'WebAssembly.String.fromCharCode', WasmI32.fromInt(charCode));

View file

@ -13,6 +13,7 @@
/// change in the future.
library dart._js_types;
import 'dart:_error_utils';
import 'dart:_internal';
import 'dart:_js_helper' as js;
import 'dart:_string_helper';

View file

@ -156,6 +156,9 @@ class WasmI64 extends _WasmInt {
/// `i64.le_u`.
external bool leU(WasmI64 other);
/// `i64.lt_u`.
external bool ltU(WasmI64 other);
}
/// The Wasm `f32` type.

View file

@ -306,7 +306,6 @@ class RangeError extends ArgumentError {
/// name and message text of the thrown error.
///
/// Returns [value] if it is in the interval.
@pragma("wasm:entry-point")
static int checkValueInInterval(int value, int minValue, int maxValue,
[String? name, String? message]) {
if (value < minValue || value > maxValue) {

View file

@ -239,6 +239,9 @@
"_internal/wasm/lib/int_common_patch.dart"
]
},
"_error_utils": {
"uri": "_internal/wasm/lib/error_utils.dart"
},
"_http": {
"uri": "_http/http.dart"
},

View file

@ -202,6 +202,8 @@ wasm_common:
- _internal/wasm/lib/boxed_double.dart
- _internal/wasm/lib/boxed_int.dart
- _internal/wasm/lib/int_common_patch.dart
_error_utils:
uri: _internal/wasm/lib/error_utils.dart
_http:
uri: _http/http.dart
_internal: