[dart2wasm,standalone] Migrate everything except String
This migrates remaining dart2wasm patches to avoid js-interop outside of passing strings. Change-Id: I92a9bb6cb97305a51858901d27966c3bd0af8fc6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/491540 Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Martin Kustermann <kustermann@google.com>
This commit is contained in:
committed by
dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent
69b84908a5
commit
9514710249
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2026, 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:_error_utils";
|
||||
import "dart:_internal" show patch, unsafeCast;
|
||||
import "dart:_js_string_convert";
|
||||
import "dart:_js_types";
|
||||
import "dart:_js_helper" show JS, jsStringFromDartString, JSExternWrapperExt;
|
||||
import "dart:_object_helper";
|
||||
import "dart:_string";
|
||||
import "dart:_typed_data";
|
||||
import "dart:_wasm";
|
||||
|
||||
@patch
|
||||
class _Utf8Decoder {
|
||||
@patch
|
||||
String? _convertSingleFastPath(List<int> codeUnits, int start, int end) {
|
||||
if (codeUnits is JSUint8ArrayImpl) {
|
||||
return decodeUtf8JS(codeUnits, start, end, allowMalformed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@patch
|
||||
class _StringParser {
|
||||
@patch
|
||||
static WasmArray<WasmI16> stringToCharCodeArray(String string, int end) {
|
||||
final externRef = jsStringFromDartString(string).wrappedExternRef;
|
||||
final array = WasmArray<WasmI16>(end);
|
||||
if (string.length == end) {
|
||||
jsStringIntoCharCodeArray(externRef, array, 0.toWasmI32());
|
||||
} else {
|
||||
for (int i = 0; i < end; ++i) array.write(i, jsCharCodeAt(externRef, i));
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
// Assumes the given [string] is a valid float, so it can rely on the implicit
|
||||
// string to number conversion in JS using `+<string-of-number>`.
|
||||
@patch
|
||||
double _parseValidFloat(String string) =>
|
||||
JS<double>('(s) => +s', jsStringFromDartString(string).wrappedExternRef);
|
||||
|
||||
@patch
|
||||
String _stringFromCharCodeArray(WasmArray<WasmI16> array, int start, int end) {
|
||||
return JSStringImpl.fromCharCodeArray(array, start, end);
|
||||
}
|
||||
|
||||
@patch
|
||||
String _stringFromAsciiBytes(WasmArray<WasmI8> source, int start, int end) {
|
||||
return JSStringImpl.fromAsciiBytes(source, start, end);
|
||||
}
|
||||
@@ -7,14 +7,10 @@ import "dart:_compact_hash" show createMapFromStringKeyValueListUnsafe;
|
||||
import "dart:_error_utils";
|
||||
import "dart:_internal"
|
||||
show patch, POWERS_OF_TEN, unsafeCast, pushWasmArray, popWasmArray;
|
||||
import "dart:_js_string_convert";
|
||||
import "dart:_js_types";
|
||||
import "dart:_js_helper" show JS, jsStringFromDartString, JSExternWrapperExt;
|
||||
import "dart:_list"
|
||||
show GrowableList, WasmListBaseUnsafeExtensions, WasmListBase;
|
||||
import "dart:_string";
|
||||
import "dart:_string_helper";
|
||||
import "dart:_object_helper";
|
||||
import "dart:_typed_data";
|
||||
import "dart:_object_helper";
|
||||
import "dart:_wasm";
|
||||
@@ -26,7 +22,7 @@ dynamic _parseJson(
|
||||
Object? Function(Object? key, Object? value)? reviver,
|
||||
) {
|
||||
final listener = _JsonListener(reviver);
|
||||
final parser = _JSStringImplParser(listener);
|
||||
final parser = _StringParser(listener);
|
||||
parser.setNewChunk(unsafeCast<JSStringImpl>(source), source.length);
|
||||
parser.parse(0);
|
||||
parser.close();
|
||||
@@ -291,13 +287,15 @@ class _NumberBuffer {
|
||||
this.array = newArray;
|
||||
}
|
||||
|
||||
String getString() => JSStringImpl.fromAsciiBytes(array, 0, length);
|
||||
String getString() {
|
||||
return _stringFromAsciiBytes(array, 0, length);
|
||||
}
|
||||
|
||||
// TODO(lrn): See if parsing of numbers can be abstracted to something
|
||||
// not only working on strings, but also on char-code lists, without losing
|
||||
// performance.
|
||||
num parseNum() => num.parse(getString());
|
||||
double parseDouble() => _jsParseValidFloat(getString());
|
||||
double parseDouble() => _parseValidFloat(getString());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -696,9 +694,7 @@ mixin _ChunkedJsonParser<T> on _ChunkedJsonParserState {
|
||||
* built exactly during parsing.
|
||||
*/
|
||||
double parseDouble(int start, int end) {
|
||||
return _jsParseValidFloat(
|
||||
getString(start, end, 0x7f, _emptyCodeUnitsCache),
|
||||
);
|
||||
return _parseValidFloat(getString(start, end, 0x7f, _emptyCodeUnitsCache));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1659,34 +1655,32 @@ mixin _ChunkedJsonParser<T> on _ChunkedJsonParserState {
|
||||
}
|
||||
|
||||
/**
|
||||
* Chunked JSON parser that parses [JSStringImpl] chunks.
|
||||
* Chunked JSON parser that parses [String] chunks.
|
||||
*/
|
||||
class _JSStringImplParser extends _ChunkedJsonParserState
|
||||
with _ChunkedJsonParser<JSStringImpl> {
|
||||
class _StringParser extends _ChunkedJsonParserState
|
||||
with _ChunkedJsonParser<String> {
|
||||
@pragma('wasm:initialize-at-startup')
|
||||
static WasmArray<WasmI16> _emptyChunk = WasmArray<WasmI16>(0);
|
||||
|
||||
JSStringImpl _chunkAsString = unsafeCast<JSStringImpl>('');
|
||||
external static WasmArray<WasmI16> stringToCharCodeArray(
|
||||
String string,
|
||||
int end,
|
||||
);
|
||||
|
||||
String _chunkAsString = '';
|
||||
WasmArray<WasmI16> _chunkAsArray = _emptyChunk;
|
||||
int chunkEnd = 0;
|
||||
|
||||
_JSStringImplParser(_JsonListener listener) : super(listener);
|
||||
_StringParser(_JsonListener listener) : super(listener);
|
||||
|
||||
void setNewChunk(JSStringImpl string, int end) {
|
||||
final externRef = string.wrappedExternRef;
|
||||
final array = WasmArray<WasmI16>(end);
|
||||
if (string.length == end) {
|
||||
jsStringIntoCharCodeArray(externRef, array, 0.toWasmI32());
|
||||
} else {
|
||||
for (int i = 0; i < end; ++i) array.write(i, jsCharCodeAt(externRef, i));
|
||||
}
|
||||
void setNewChunk(String string, int end) {
|
||||
_chunkAsString = string;
|
||||
_chunkAsArray = array;
|
||||
_chunkAsArray = stringToCharCodeArray(string, end);
|
||||
chunkEnd = end;
|
||||
}
|
||||
|
||||
@override
|
||||
JSStringImpl get chunk => _chunkAsString;
|
||||
String get chunk => _chunkAsString;
|
||||
|
||||
@pragma('wasm:prefer-inline')
|
||||
bool get isUtf16Input => true;
|
||||
@@ -1708,13 +1702,7 @@ class _JSStringImplParser extends _ChunkedJsonParserState
|
||||
}
|
||||
|
||||
String getJsonObjectKeyString(int start, int end, int bits, int stringHash) {
|
||||
return _internJSString(
|
||||
_chunkAsString,
|
||||
_chunkAsArray,
|
||||
stringHash,
|
||||
start,
|
||||
end,
|
||||
);
|
||||
return _internString(_chunkAsString, _chunkAsArray, stringHash, start, end);
|
||||
}
|
||||
|
||||
void beginString() {
|
||||
@@ -1757,14 +1745,12 @@ class _JSStringImplParser extends _ChunkedJsonParserState
|
||||
}
|
||||
|
||||
double parseDouble(int start, int end) {
|
||||
return _jsParseValidFloat(
|
||||
getString(start, end, 0x7f, _emptyCodeUnitsCache),
|
||||
);
|
||||
return _parseValidFloat(getString(start, end, 0x7f, _emptyCodeUnitsCache));
|
||||
}
|
||||
|
||||
void close() {
|
||||
super.close();
|
||||
_jsStringInternCache.fill(0, null, _jsStringInternCacheSize);
|
||||
_stringInternCache.fill(0, null, _stringInternCacheSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1776,21 +1762,21 @@ const int _codeUnitsCacheSize = 512;
|
||||
@pragma("wasm:initialize-at-startup")
|
||||
final _codeUnitsCache = WasmArray<WasmI16>(_codeUnitsCacheSize);
|
||||
|
||||
const int _jsStringInternCacheSize = 512;
|
||||
const int _stringInternCacheSize = 512;
|
||||
@pragma("wasm:initialize-at-startup")
|
||||
final WasmArray<JSStringImpl?> _jsStringInternCache = WasmArray<JSStringImpl?>(
|
||||
_jsStringInternCacheSize,
|
||||
final WasmArray<String?> _stringInternCache = WasmArray<String?>(
|
||||
_stringInternCacheSize,
|
||||
);
|
||||
JSStringImpl _internJSString(
|
||||
JSStringImpl source,
|
||||
String _internString(
|
||||
String source,
|
||||
WasmArray<WasmI16> sourceAsArray,
|
||||
int stringHash,
|
||||
int start,
|
||||
int end,
|
||||
) {
|
||||
final length = end - start;
|
||||
final int index = stringHash & (_jsStringInternCacheSize - 1);
|
||||
final existing = _jsStringInternCache[index];
|
||||
final int index = stringHash & (_stringInternCacheSize - 1);
|
||||
final existing = _stringInternCache[index];
|
||||
|
||||
insert:
|
||||
{
|
||||
@@ -1810,23 +1796,21 @@ JSStringImpl _internJSString(
|
||||
}
|
||||
}
|
||||
|
||||
final result = unsafeCast<JSStringImpl>(
|
||||
source.substringUnchecked(start, end),
|
||||
);
|
||||
final result = source.substringUnchecked(start, end);
|
||||
setIdentityHashField(result, stringHash);
|
||||
_jsStringInternCache[index] = result;
|
||||
_stringInternCache[index] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
JSStringImpl _internJSStringFromAsciiSlice(
|
||||
String _internJSStringFromAsciiSlice(
|
||||
U8List source,
|
||||
int stringHash,
|
||||
int start,
|
||||
int end,
|
||||
) {
|
||||
final length = end - start;
|
||||
final int index = stringHash & (_jsStringInternCacheSize - 1);
|
||||
final existing = _jsStringInternCache[index];
|
||||
final int index = stringHash & (_stringInternCacheSize - 1);
|
||||
final existing = _stringInternCache[index];
|
||||
|
||||
insert:
|
||||
{
|
||||
@@ -1845,14 +1829,30 @@ JSStringImpl _internJSStringFromAsciiSlice(
|
||||
}
|
||||
}
|
||||
|
||||
final result = JSStringImpl.fromAsciiBytes(
|
||||
final result = _stringFromAsciiList(source, start, end);
|
||||
setIdentityHashField(result, stringHash);
|
||||
_stringInternCache[index] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
external String _stringFromCharCodeArray(
|
||||
WasmArray<WasmI16> array,
|
||||
int start,
|
||||
int end,
|
||||
);
|
||||
|
||||
external String _stringFromAsciiBytes(
|
||||
WasmArray<WasmI8> source,
|
||||
int start,
|
||||
int end,
|
||||
);
|
||||
|
||||
String _stringFromAsciiList(U8List source, int start, int end) {
|
||||
return _stringFromAsciiBytes(
|
||||
source.data,
|
||||
source.offsetInElements + start,
|
||||
source.offsetInElements + end,
|
||||
);
|
||||
setIdentityHashField(result, stringHash);
|
||||
_jsStringInternCache[index] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
@patch
|
||||
@@ -1871,7 +1871,7 @@ class JsonDecoder {
|
||||
class _JsonStringDecoderSink extends StringConversionSinkBase {
|
||||
final _JsonListener _listener;
|
||||
|
||||
late final _JSStringImplParser _stringParser;
|
||||
late final _StringParser _stringParser;
|
||||
|
||||
final Object? Function(Object? key, Object? value)? _reviver;
|
||||
|
||||
@@ -1879,12 +1879,12 @@ class _JsonStringDecoderSink extends StringConversionSinkBase {
|
||||
|
||||
_JsonStringDecoderSink(this._reviver, this._sink)
|
||||
: _listener = _JsonListener(_reviver) {
|
||||
_stringParser = _JSStringImplParser(_listener);
|
||||
_stringParser = _StringParser(_listener);
|
||||
}
|
||||
|
||||
void addSlice(String chunk, int start, int end, bool isLast) {
|
||||
final parser = _stringParser;
|
||||
parser.setNewChunk(unsafeCast<JSStringImpl>(chunk), end);
|
||||
parser.setNewChunk(chunk, end);
|
||||
parser.parse(start);
|
||||
if (isLast) parser.close();
|
||||
}
|
||||
@@ -1989,7 +1989,7 @@ class _JsonUtf8Parser extends _ChunkedJsonParserState
|
||||
addStringSliceToString(decoder.convertChunked(chunk, start, end));
|
||||
}
|
||||
|
||||
JSStringImpl _stringFromBytesOrCache(
|
||||
String _stringFromBytesOrCache(
|
||||
int start,
|
||||
int end,
|
||||
WasmArray<WasmI16> codeUnitsCache,
|
||||
@@ -2004,13 +2004,9 @@ class _JsonUtf8Parser extends _ChunkedJsonParserState
|
||||
chunk.offsetInElements + end,
|
||||
),
|
||||
);
|
||||
return JSStringImpl.fromCharCodeArray(codeUnitsCache, 0, length);
|
||||
return _stringFromCharCodeArray(codeUnitsCache, 0, length);
|
||||
}
|
||||
return JSStringImpl.fromAsciiBytes(
|
||||
chunk.data,
|
||||
chunk.offsetInElements + start,
|
||||
chunk.offsetInElements + end,
|
||||
);
|
||||
return _stringFromAsciiList(chunk, start, end);
|
||||
}
|
||||
|
||||
void addStringSliceToString(String string) {
|
||||
@@ -2040,12 +2036,8 @@ class _JsonUtf8Parser extends _ChunkedJsonParserState
|
||||
}
|
||||
|
||||
double parseDouble(int start, int end) {
|
||||
final result = JSStringImpl.fromAsciiBytes(
|
||||
chunk.data,
|
||||
chunk.offsetInElements + start,
|
||||
chunk.offsetInElements + end,
|
||||
);
|
||||
return _jsParseValidFloat(result);
|
||||
final string = _stringFromAsciiList(chunk, start, end);
|
||||
return _parseValidFloat(string);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2179,6 +2171,12 @@ class _Utf8Decoder {
|
||||
82, 82, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98,
|
||||
]);
|
||||
|
||||
external String? _convertSingleFastPath(
|
||||
List<int> codeUnits,
|
||||
int start,
|
||||
int end,
|
||||
);
|
||||
|
||||
/// Reset the decoder to a state where it is ready to decode a new string but
|
||||
/// will not skip a leading BOM. Used by the fused UTF-8 / JSON decoder.
|
||||
void reset() {
|
||||
@@ -2214,14 +2212,9 @@ class _Utf8Decoder {
|
||||
);
|
||||
if (start == end) return "";
|
||||
|
||||
if (codeUnits is JSUint8ArrayImpl) {
|
||||
JSStringImpl? decoded = decodeUtf8JS(
|
||||
codeUnits,
|
||||
start,
|
||||
end,
|
||||
allowMalformed,
|
||||
);
|
||||
if (decoded != null) return decoded;
|
||||
if (_convertSingleFastPath(codeUnits, start, end) case final converted?) {
|
||||
// Platform-specific fast path
|
||||
return converted;
|
||||
}
|
||||
|
||||
final WasmArray<WasmI8> bytes;
|
||||
@@ -2581,7 +2574,7 @@ class _Utf8Decoder {
|
||||
return true;
|
||||
})(),
|
||||
);
|
||||
return JSStringImpl.fromCharCodeArray(_characterArray, 0, size);
|
||||
return _stringFromCharCodeArray(_characterArray, 0, size);
|
||||
}
|
||||
|
||||
String decode8(WasmArray<WasmI8> bytes, int start, int end, int size) {
|
||||
@@ -2628,7 +2621,7 @@ class _Utf8Decoder {
|
||||
_characterArray.write(j++, byte & 0xFF);
|
||||
}
|
||||
assert(j == size);
|
||||
return JSStringImpl.fromCharCodeArray(_characterArray, 0, size);
|
||||
return _stringFromCharCodeArray(_characterArray, 0, size);
|
||||
}
|
||||
|
||||
// This table is the Wasm array version of `_Utf8Decoder.transitionTable`,
|
||||
@@ -2727,7 +2720,7 @@ class _Utf8Decoder {
|
||||
_charOrIndex = char;
|
||||
|
||||
assert(j == size);
|
||||
return JSStringImpl.fromCharCodeArray(_characterArray, 0, size);
|
||||
return _stringFromCharCodeArray(_characterArray, 0, size);
|
||||
}
|
||||
|
||||
String _decodeGeneral(
|
||||
@@ -2891,10 +2884,7 @@ WasmArray<WasmI8> _makeI8ArrayFromWasmI8ArrayBase(
|
||||
return bytes;
|
||||
}
|
||||
|
||||
// Assumes the given [string] is a valid float, so it can rely on the implicit
|
||||
// string to number conversion in JS using `+<string-of-number>`.
|
||||
double _jsParseValidFloat(String string) =>
|
||||
JS<double>('(s) => +s', jsStringFromDartString(string).wrappedExternRef);
|
||||
external double _parseValidFloat(String string);
|
||||
|
||||
const ImmutableWasmArray<BoxedInt> _intBoxes256 = ImmutableWasmArray.literal([
|
||||
0, 1, 2, 3, 4, 5, 6, 7, //
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2026, 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:_embedder" as embedder;
|
||||
import "dart:_internal" show patch, unsafeCast;
|
||||
import "dart:_js_helper" show jsStringFromDartString, JSExternWrapperExt;
|
||||
import "dart:_string";
|
||||
import "dart:_wasm";
|
||||
|
||||
@patch
|
||||
class _Utf8Decoder {
|
||||
@patch
|
||||
String? _convertSingleFastPath(List<int> codeUnits, int start, int end) {
|
||||
// Always decode utf-8 in Dart.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@patch
|
||||
class _StringParser {
|
||||
@patch
|
||||
static WasmArray<WasmI16> stringToCharCodeArray(String string, int end) {
|
||||
final externRef = unsafeCast<JSStringImpl>(string).wrappedExternRef;
|
||||
final array = WasmArray<WasmI16>(end);
|
||||
if (string.length == end) {
|
||||
jsStringIntoCharCodeArray(externRef, array, 0.toWasmI32());
|
||||
} else {
|
||||
for (int i = 0; i < end; ++i) array.write(i, jsCharCodeAt(externRef, i));
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
@patch
|
||||
double _parseValidFloat(String string) {
|
||||
return embedder
|
||||
.doubleParseInfallible(unsafeCast<JSStringImpl>(string).wrappedExternRef)
|
||||
.toDouble();
|
||||
}
|
||||
|
||||
@patch
|
||||
String _stringFromCharCodeArray(WasmArray<WasmI16> array, int start, int end) {
|
||||
return JSStringImpl.fromCharCodeArray(array, start, end);
|
||||
}
|
||||
|
||||
@patch
|
||||
String _stringFromAsciiBytes(WasmArray<WasmI8> source, int start, int end) {
|
||||
return JSStringImpl.fromAsciiBytes(source, start, end);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2026, 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.
|
||||
|
||||
final Set<int> _loaded = {};
|
||||
|
||||
// The standalone target doesn't support loading deferred libraries, dart2wasm
|
||||
// emits an error when `--enable-deferred-loading` is enabled with this target.
|
||||
// These methods are still referenced in the compiler, but we can simply make
|
||||
// them do nothing.
|
||||
|
||||
Future<void> loadLibraryFromLoadId(int loadId) {
|
||||
_loaded.add(loadId);
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
bool checkLibraryIsLoadedFromLoadId(int loadId) {
|
||||
if (_loaded.contains(loadId)) {
|
||||
return true;
|
||||
}
|
||||
throw DeferredLoadIdNotLoadedError();
|
||||
}
|
||||
|
||||
class DeferredLoadIdNotLoadedError extends Error implements NoSuchMethodError {
|
||||
DeferredLoadIdNotLoadedError();
|
||||
|
||||
String toString() {
|
||||
return 'Deferred loading is not available with dart2wasm standalone';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
// Copyright (c) 2026, 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:_embedder' as embedder;
|
||||
import 'dart:_internal' show patch;
|
||||
import 'dart:_js_helper' show jsStringFromDartString, JSExternWrapperExt;
|
||||
import 'dart:_wasm';
|
||||
import 'dart:async' show Zone;
|
||||
import 'dart:isolate';
|
||||
|
||||
@patch
|
||||
bool debugger({bool when = true, String? message}) {
|
||||
if (when) {
|
||||
embedder.debugger(
|
||||
message == null
|
||||
? WasmExternRef.nullRef
|
||||
: jsStringFromDartString(message).wrappedExternRef,
|
||||
);
|
||||
}
|
||||
|
||||
return when;
|
||||
}
|
||||
|
||||
@patch
|
||||
Object? inspect(Object? object) {
|
||||
embedder.inspect(object == null ? null : WasmAnyRef.fromObject(object));
|
||||
return object;
|
||||
}
|
||||
|
||||
@patch
|
||||
void log(
|
||||
String message, {
|
||||
DateTime? time,
|
||||
int? sequenceNumber,
|
||||
int level = 0,
|
||||
String name = '',
|
||||
Zone? zone,
|
||||
Object? error,
|
||||
StackTrace? stackTrace,
|
||||
}) {
|
||||
// TODO.
|
||||
}
|
||||
|
||||
@patch
|
||||
int get reachabilityBarrier => 0;
|
||||
|
||||
@patch
|
||||
abstract final class NativeRuntime {
|
||||
@patch
|
||||
static String? get buildId => null;
|
||||
|
||||
@patch
|
||||
static void writeHeapSnapshotToFile(String filepath) =>
|
||||
throw UnsupportedError(
|
||||
"Generating heap snapshots is not supported for WebAssembly.",
|
||||
);
|
||||
|
||||
@patch
|
||||
static void streamTimelineTo(
|
||||
TimelineRecorder recorder, {
|
||||
String? path,
|
||||
String streams = "Dart,GC,Compiler",
|
||||
bool enableProfiler = false,
|
||||
Duration samplingInterval = const Duration(microseconds: 1000),
|
||||
}) => throw UnsupportedError(
|
||||
"Streaming timelines is not supported for WebAssembly.",
|
||||
);
|
||||
|
||||
@patch
|
||||
static void stopStreamingTimeline() => throw UnsupportedError(
|
||||
"Streaming timelines is not supported for WebAssembly.",
|
||||
);
|
||||
}
|
||||
|
||||
@patch
|
||||
bool get extensionStreamHasListener => false;
|
||||
|
||||
final _extensions = <String, ServiceExtensionHandler>{};
|
||||
|
||||
@patch
|
||||
void _postEvent(String eventKind, String eventData) {
|
||||
// TODO.
|
||||
}
|
||||
|
||||
@patch
|
||||
ServiceExtensionHandler? _lookupExtension(String method) {
|
||||
return _extensions[method];
|
||||
}
|
||||
|
||||
@patch
|
||||
_registerExtension(String method, ServiceExtensionHandler handler) {
|
||||
_extensions[method] = handler;
|
||||
}
|
||||
|
||||
@patch
|
||||
void _getServerInfo(SendPort sendPort) {
|
||||
sendPort.send(null);
|
||||
}
|
||||
|
||||
@patch
|
||||
void _webServerControl(SendPort sendPort, bool enable, bool? silenceOutput) {
|
||||
sendPort.send(null);
|
||||
}
|
||||
|
||||
@patch
|
||||
int _getServiceMajorVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@patch
|
||||
int _getServiceMinorVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@patch
|
||||
String? _getIsolateIdFromSendPort(SendPort sendPort) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@patch
|
||||
String? _getObjectId(Object object) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@patch
|
||||
class UserTag {
|
||||
@patch
|
||||
factory UserTag(String label) = _FakeUserTag;
|
||||
|
||||
@patch
|
||||
static UserTag get defaultTag => _FakeUserTag._defaultTag;
|
||||
}
|
||||
|
||||
final class _FakeUserTag implements UserTag {
|
||||
static final _instances = <String, _FakeUserTag>{};
|
||||
|
||||
_FakeUserTag.real(this.label);
|
||||
|
||||
factory _FakeUserTag(String label) {
|
||||
// Canonicalize by name.
|
||||
var existingTag = _instances[label];
|
||||
if (existingTag != null) {
|
||||
return existingTag;
|
||||
}
|
||||
// Throw an exception if we've reached the maximum number of user tags.
|
||||
if (_instances.length == UserTag.maxUserTags) {
|
||||
throw UnsupportedError(
|
||||
'UserTag instance limit (${UserTag.maxUserTags}) reached.',
|
||||
);
|
||||
}
|
||||
return _instances[label] = _FakeUserTag.real(label);
|
||||
}
|
||||
|
||||
final String label;
|
||||
|
||||
UserTag makeCurrent() {
|
||||
var old = _currentTag;
|
||||
_currentTag = this;
|
||||
return old;
|
||||
}
|
||||
|
||||
static final UserTag _defaultTag = _FakeUserTag('Default');
|
||||
}
|
||||
|
||||
var _currentTag = _FakeUserTag._defaultTag;
|
||||
|
||||
@patch
|
||||
UserTag getCurrentTag() => _currentTag;
|
||||
|
||||
@patch
|
||||
bool _isDartStreamEnabled() => embedder.dartTimelineStreamEnabled().toBool();
|
||||
|
||||
int _taskId = 1;
|
||||
|
||||
@patch
|
||||
int _getNextTaskId() {
|
||||
return _taskId++;
|
||||
}
|
||||
|
||||
@patch
|
||||
int _getTraceClock() => embedder.monotonicClockTicks().toInt();
|
||||
|
||||
@patch
|
||||
void _reportTaskEvent(
|
||||
int taskId,
|
||||
int flowId,
|
||||
int type,
|
||||
String name,
|
||||
String argumentsAsJson,
|
||||
) {
|
||||
embedder.reportTaskEvent(
|
||||
WasmI32.fromInt(taskId),
|
||||
WasmI32.fromInt(flowId),
|
||||
WasmI32.fromInt(type),
|
||||
jsStringFromDartString(name).wrappedExternRef,
|
||||
jsStringFromDartString(argumentsAsJson).wrappedExternRef,
|
||||
);
|
||||
}
|
||||
@@ -186,6 +186,10 @@ external WasmExternRef? doubleTryParse(WasmExternRef? string);
|
||||
@pragma("wasm:import", "dart.tryParseResultGetDouble")
|
||||
external WasmF64 tryParseResultGetDouble(WasmExternRef? parseResult);
|
||||
|
||||
/// Parses a double from a string known to contain a a valid double.
|
||||
@pragma("wasm:import", "dart.doubleParseInfallible")
|
||||
external WasmF64 doubleParseInfallible(WasmExternRef? string);
|
||||
|
||||
/// The implementation of [int.toRadixString].
|
||||
///
|
||||
/// The SDK will only call this with radix values between 2 and 36 (inclusive).
|
||||
@@ -333,3 +337,53 @@ external WasmExternRef timeZoneNameForClampedSeconds(WasmI64 secondsSinceEpoch);
|
||||
external WasmI32 timeZoneOffsetInSecondsForClampedSeconds(
|
||||
WasmI64 secondsSinceEpoch,
|
||||
);
|
||||
|
||||
@pragma("wasm:import", "dart.mathPow")
|
||||
external WasmF64 mathPow(WasmF64 base, WasmF64 exponent);
|
||||
@pragma("wasm:import", "dart.mathAtan2")
|
||||
external WasmF64 mathAtan2(WasmF64 a, WasmF64 b);
|
||||
@pragma("wasm:import", "dart.mathSin")
|
||||
external WasmF64 mathSin(WasmF64 x);
|
||||
@pragma("wasm:import", "dart.mathCos")
|
||||
external WasmF64 mathCos(WasmF64 x);
|
||||
@pragma("wasm:import", "dart.mathTan")
|
||||
external WasmF64 mathTan(WasmF64 x);
|
||||
@pragma("wasm:import", "dart.mathAcos")
|
||||
external WasmF64 mathAcos(WasmF64 x);
|
||||
@pragma("wasm:import", "dart.mathAsin")
|
||||
external WasmF64 mathAsin(WasmF64 x);
|
||||
@pragma("wasm:import", "dart.mathAtan")
|
||||
external WasmF64 mathAtan(WasmF64 x);
|
||||
@pragma("wasm:import", "dart.mathExp")
|
||||
external WasmF64 mathExp(WasmF64 a);
|
||||
@pragma("wasm:import", "dart.mathLog")
|
||||
external WasmF64 mathLog(WasmF64 a);
|
||||
|
||||
@pragma("wasm:import", "dart.randomInt")
|
||||
external WasmI64 randomInt();
|
||||
@pragma("wasm:import", "dart.randomIntSecure")
|
||||
external WasmI64 randomIntSecure();
|
||||
|
||||
@pragma("wasm:import", "dart.print")
|
||||
external WasmVoid print(WasmExternRef? line);
|
||||
@pragma("wasm:import", "dart.jsonEncodeString")
|
||||
external WasmExternRef jsonEncodeString(WasmExternRef? line);
|
||||
|
||||
@pragma("wasm:import", "dart.debugger")
|
||||
external WasmVoid debugger(WasmExternRef? message);
|
||||
|
||||
/// Send a reference to [object] to any attached debuggers.
|
||||
@pragma("wasm:import", "dart.inspect")
|
||||
external WasmVoid inspect(WasmAnyRef? object);
|
||||
|
||||
@pragma("wasm:import", "dart.timelineStreamEnabled")
|
||||
external WasmI32 dartTimelineStreamEnabled();
|
||||
|
||||
@pragma("wasm:import", "dart.reportTaskEvent")
|
||||
external WasmI32 reportTaskEvent(
|
||||
WasmI32 taskId,
|
||||
WasmI32 flowId,
|
||||
WasmI32 type,
|
||||
WasmExternRef? name,
|
||||
WasmExternRef? argumentsAsJson,
|
||||
);
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2026, 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:_embedder' as embedder;
|
||||
import "dart:_js_helper" show jsStringFromDartString, JSExternWrapperExt;
|
||||
import 'dart:_string' show JSStringImpl;
|
||||
import 'dart:_wasm';
|
||||
|
||||
String jsonEncode(String object) => JSStringImpl.fromRef(
|
||||
embedder.jsonEncodeString(jsStringFromDartString(object).wrappedExternRef),
|
||||
);
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2026, 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:_string';
|
||||
import 'dart:_wasm';
|
||||
|
||||
@pragma("wasm:prefer-inline")
|
||||
void _invokeMainArg0(List<String> args, Function() mainMethod) {
|
||||
mainMethod();
|
||||
return;
|
||||
}
|
||||
|
||||
@pragma("wasm:prefer-inline")
|
||||
void _invokeMainArg1(List<String> args, Function(List<String>) mainMethod) {
|
||||
mainMethod(args);
|
||||
return;
|
||||
}
|
||||
|
||||
@pragma("wasm:prefer-inline")
|
||||
void _invokeMainArg2(
|
||||
List<String> args,
|
||||
Function(List<String>, Null) mainMethod,
|
||||
) {
|
||||
mainMethod(args, null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Will be patched in `pkg/dart2wasm/lib/compile.dart` right before TFA.
|
||||
external void _invokeMainInternal(List<String> args);
|
||||
|
||||
/// Used to invoke the `main` function from JS, printing any exceptions that
|
||||
/// escape.
|
||||
@pragma("wasm:export", "\$invokeMain")
|
||||
WasmVoid _invokeMain(WasmArray<WasmExternRef?> args) {
|
||||
try {
|
||||
final dartArgs = <String>[];
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
dartArgs.add(JSStringImpl.fromRefUnchecked(args[i]));
|
||||
}
|
||||
|
||||
_invokeMainInternal(dartArgs);
|
||||
return WasmVoid();
|
||||
} catch (e, s) {
|
||||
print(e);
|
||||
print(s);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright (c) 2026, 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:_error_utils";
|
||||
import 'dart:_embedder' as embedder;
|
||||
import "dart:_internal" show patch;
|
||||
import "dart:_wasm";
|
||||
|
||||
@patch
|
||||
double atan2(num a, num b) => embedder
|
||||
.mathAtan2(
|
||||
WasmF64.fromDouble(a.toDouble()),
|
||||
WasmF64.fromDouble(b.toDouble()),
|
||||
)
|
||||
.toDouble();
|
||||
@patch
|
||||
double sin(num radians) =>
|
||||
embedder.mathSin(WasmF64.fromDouble(radians.toDouble())).toDouble();
|
||||
@patch
|
||||
double cos(num radians) =>
|
||||
embedder.mathCos(WasmF64.fromDouble(radians.toDouble())).toDouble();
|
||||
@patch
|
||||
double tan(num radians) =>
|
||||
embedder.mathTan(WasmF64.fromDouble(radians.toDouble())).toDouble();
|
||||
@patch
|
||||
double acos(num x) =>
|
||||
embedder.mathAcos(WasmF64.fromDouble(x.toDouble())).toDouble();
|
||||
@patch
|
||||
double asin(num x) =>
|
||||
embedder.mathAsin(WasmF64.fromDouble(x.toDouble())).toDouble();
|
||||
@patch
|
||||
double atan(num x) =>
|
||||
embedder.mathAtan(WasmF64.fromDouble(x.toDouble())).toDouble();
|
||||
@patch
|
||||
double sqrt(num x) => x.toDouble().sqrt();
|
||||
@patch
|
||||
double exp(num x) =>
|
||||
embedder.mathExp(WasmF64.fromDouble(x.toDouble())).toDouble();
|
||||
@patch
|
||||
double log(num x) =>
|
||||
embedder.mathLog(WasmF64.fromDouble(x.toDouble())).toDouble();
|
||||
|
||||
double _doublePow(double base, double exponent) => embedder
|
||||
.mathPow(
|
||||
WasmF64.fromDouble(base.toDouble()),
|
||||
WasmF64.fromDouble(exponent.toDouble()),
|
||||
)
|
||||
.toDouble();
|
||||
|
||||
@patch
|
||||
class _Random {
|
||||
@patch
|
||||
static int _initialSeed() => embedder.randomInt().toInt();
|
||||
}
|
||||
|
||||
class _SecureRandom implements Random {
|
||||
_SecureRandom() {
|
||||
// Throw early in constructor if entropy source is not hooked up.
|
||||
_getBytes(1);
|
||||
}
|
||||
|
||||
// Return count bytes of entropy as an integer; count <= 8.
|
||||
int _getBytes(int count) {
|
||||
final random = embedder.randomIntSecure().toInt();
|
||||
return random.toUnsigned(64);
|
||||
}
|
||||
|
||||
int nextInt(int max) {
|
||||
RangeErrorUtils.checkValueInInterval(
|
||||
max,
|
||||
1,
|
||||
_POW2_32,
|
||||
"max",
|
||||
"Must be positive and <= 2^32",
|
||||
);
|
||||
final byteCount =
|
||||
((max - 1).bitLength + 7) >> 3; // Divide number of bits by 8, round up.
|
||||
if (byteCount == 0) {
|
||||
return 0; // Not random if max == 1.
|
||||
}
|
||||
var rnd;
|
||||
var result;
|
||||
do {
|
||||
rnd = _getBytes(byteCount);
|
||||
result = rnd % max;
|
||||
} while ((rnd - result + max) > (1 << (byteCount << 3)));
|
||||
return result;
|
||||
}
|
||||
|
||||
double nextDouble() {
|
||||
return (_getBytes(7) >> 3) / _POW2_53_D;
|
||||
}
|
||||
|
||||
bool nextBool() {
|
||||
return _getBytes(1).isEven;
|
||||
}
|
||||
|
||||
// Constants used by the algorithm.
|
||||
static const _POW2_32 = 1 << 32;
|
||||
static const _POW2_53_D = 1.0 * (1 << 53);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright (c) 2026, 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:_embedder' as embedder;
|
||||
import 'dart:_js_helper' show jsStringFromDartString, JSExternWrapperExt;
|
||||
|
||||
@patch
|
||||
void printToConsole(String line) =>
|
||||
embedder.print(jsStringFromDartString(line).wrappedExternRef);
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2026, 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:_internal' show patch;
|
||||
|
||||
// The standalone platform doesn't support interacting with JS typed array
|
||||
// objects.
|
||||
|
||||
@patch
|
||||
bool tryCopyExternalIntTypedData(
|
||||
Iterable<int> from,
|
||||
_IntListMixin to,
|
||||
int start,
|
||||
int skipCount,
|
||||
int count,
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@patch
|
||||
bool tryCopyExternalFloatTypedData(
|
||||
Iterable<double> from,
|
||||
_DoubleListMixin to,
|
||||
int start,
|
||||
int skipCount,
|
||||
int count,
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
+11
-9
@@ -168,7 +168,8 @@
|
||||
"convert": {
|
||||
"uri": "convert/convert.dart",
|
||||
"patches": [
|
||||
"_internal/wasm/lib/convert_patch.dart"
|
||||
"_internal/wasm/lib/convert_patch.dart",
|
||||
"_internal/wasm/lib/convert_js_patch.dart"
|
||||
]
|
||||
},
|
||||
"typed_data": {
|
||||
@@ -305,13 +306,14 @@
|
||||
"convert": {
|
||||
"uri": "convert/convert.dart",
|
||||
"patches": [
|
||||
"_internal/wasm/lib/convert_patch.dart"
|
||||
"_internal/wasm/lib/convert_patch.dart",
|
||||
"_internal/wasm_standalone/lib/convert_standalone_patch.dart"
|
||||
]
|
||||
},
|
||||
"developer": {
|
||||
"uri": "developer/developer.dart",
|
||||
"patches": [
|
||||
"_internal/js_runtime/lib/developer_patch.dart"
|
||||
"_internal/wasm_standalone/lib/developer_patch.dart"
|
||||
]
|
||||
},
|
||||
"typed_data": {
|
||||
@@ -337,7 +339,7 @@
|
||||
"_typed_data": {
|
||||
"uri": "_internal/wasm/lib/typed_data.dart",
|
||||
"patches": [
|
||||
"_internal/wasm/lib/typed_data_copy_from_js.dart"
|
||||
"_internal/wasm_standalone/lib/typed_data_copy_from_js.dart"
|
||||
]
|
||||
},
|
||||
"_js_helper": {
|
||||
@@ -350,10 +352,10 @@
|
||||
"uri": "internal/internal.dart",
|
||||
"patches": [
|
||||
"_internal/wasm/lib/internal_patch.dart",
|
||||
"_internal/wasm/lib/deferred_patch.dart",
|
||||
"_internal/wasm/lib/internal_json_encode_patch.dart",
|
||||
"_internal/wasm/lib/invoke_main_patch.dart",
|
||||
"_internal/wasm/lib/print_patch.dart",
|
||||
"_internal/wasm_standalone/lib/deferred_patch.dart",
|
||||
"_internal/wasm_standalone/lib/internal_json_encode_patch.dart",
|
||||
"_internal/wasm_standalone/lib/invoke_main_patch.dart",
|
||||
"_internal/wasm_standalone/lib/print_patch.dart",
|
||||
"_internal/vm_shared/lib/check_valid_weak_target_patch.dart"
|
||||
]
|
||||
},
|
||||
@@ -365,7 +367,7 @@
|
||||
"uri": "math/math.dart",
|
||||
"patches": [
|
||||
"_internal/wasm/lib/math_patch.dart",
|
||||
"_internal/wasm/lib/math_externs_patch.dart"
|
||||
"_internal/wasm_standalone/lib/math_externs_patch.dart"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+10
-8
@@ -156,6 +156,7 @@ wasm:
|
||||
uri: convert/convert.dart
|
||||
patches:
|
||||
- _internal/wasm/lib/convert_patch.dart
|
||||
- _internal/wasm/lib/convert_js_patch.dart
|
||||
typed_data:
|
||||
uri: typed_data/typed_data.dart
|
||||
patches:
|
||||
@@ -261,11 +262,12 @@ wasm_standalone:
|
||||
convert:
|
||||
uri: convert/convert.dart
|
||||
patches:
|
||||
- _internal/wasm/lib/convert_patch.dart # TODO(53884): Rewrite without JS interop in _internal/wasm_standalone
|
||||
- _internal/wasm/lib/convert_patch.dart
|
||||
- _internal/wasm_standalone/lib/convert_standalone_patch.dart
|
||||
developer:
|
||||
uri: developer/developer.dart
|
||||
patches:
|
||||
- _internal/js_runtime/lib/developer_patch.dart # TODO(53884): Rewrite without JS interop in _internal/wasm_standalone
|
||||
- _internal/wasm_standalone/lib/developer_patch.dart
|
||||
typed_data:
|
||||
uri: typed_data/typed_data.dart
|
||||
patches:
|
||||
@@ -284,7 +286,7 @@ wasm_standalone:
|
||||
_typed_data:
|
||||
uri: _internal/wasm/lib/typed_data.dart
|
||||
patches:
|
||||
- _internal/wasm/lib/typed_data_copy_from_js.dart # TODO(53884): Rewrite without JS interop in _internal/wasm_standalone
|
||||
- _internal/wasm_standalone/lib/typed_data_copy_from_js.dart
|
||||
_js_helper: # TODO(53884): Remove once other wasm_standalone patches no longer reference this
|
||||
uri: _internal/wasm/lib/js_helper.dart
|
||||
patches:
|
||||
@@ -293,10 +295,10 @@ wasm_standalone:
|
||||
uri: internal/internal.dart
|
||||
patches:
|
||||
- _internal/wasm/lib/internal_patch.dart
|
||||
- _internal/wasm/lib/deferred_patch.dart # TODO(53884): Rewrite without JS interop in _internal/wasm_standalone
|
||||
- _internal/wasm/lib/internal_json_encode_patch.dart # Needs to be migrated
|
||||
- _internal/wasm/lib/invoke_main_patch.dart # TODO(53884): Rewrite without JS interop in _internal/wasm_standalone
|
||||
- _internal/wasm/lib/print_patch.dart # TODO(53884): Rewrite without JS interop in _internal/wasm_standalone
|
||||
- _internal/wasm_standalone/lib/deferred_patch.dart
|
||||
- _internal/wasm_standalone/lib/internal_json_encode_patch.dart
|
||||
- _internal/wasm_standalone/lib/invoke_main_patch.dart
|
||||
- _internal/wasm_standalone/lib/print_patch.dart
|
||||
- _internal/vm_shared/lib/check_valid_weak_target_patch.dart
|
||||
_wasm:
|
||||
uri: _wasm/wasm_types.dart
|
||||
@@ -305,7 +307,7 @@ wasm_standalone:
|
||||
uri: math/math.dart
|
||||
patches:
|
||||
- _internal/wasm/lib/math_patch.dart
|
||||
- _internal/wasm/lib/math_externs_patch.dart # TODO(53884): Rewrite without JS interop in _internal/wasm_standalone
|
||||
- _internal/wasm_standalone/lib/math_externs_patch.dart
|
||||
|
||||
wasm_js_common:
|
||||
include:
|
||||
|
||||
Reference in New Issue
Block a user