Skip to content

Commit 16acf73

Browse files
osa1kevmoo
andauthored
Fix JS value to Dart conversion when receiving from a web socket (dart-archive/web_socket_channel#298)
`MessageEvent` is a `package:web` type and `data` field is a JS value of type `JSAny?`. The receiving end of the `sink` is here: https://github.com/dart-lang/sdk/blob/26107a319a7503deafee404e3462644a873e2920/pkg/vm_service/lib/src/vm_service.dart#L1795 This code currently does not expect to see JS objects, so passing a `JSAny?` to the sink breaks it. The fix should be in the generating end rather than the receiving end: `JSAny?` is supposed to be an unboxed value, not a boxed Dart value. In an ideal world we shouldn't be able to pass it as a Dart object. So we call `dartify` and convert it to a Dart object. This fixes DevTools when compiled to Wasm. Thanks to @eyebrowsoffire and @mkustermann for help with debugging. Co-authored-by: Kevin Moore <[email protected]>
1 parent df37a53 commit 16acf73

File tree

2 files changed

+16
-5
lines changed

2 files changed

+16
-5
lines changed

pkgs/web_socket_channel/lib/html.dart

+7-3
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,17 @@ class HtmlWebSocketChannel extends StreamChannelMixin
127127
}
128128

129129
void _innerListen(MessageEvent event) {
130+
// Event data will be ArrayBuffer, Blob, or String.
130131
final eventData = event.data;
131-
Object? data;
132-
if (eventData.typeofEquals('object') &&
132+
final Object? data;
133+
if (eventData.typeofEquals('string')) {
134+
data = (eventData as JSString).toDart;
135+
} else if (eventData.typeofEquals('object') &&
133136
(eventData as JSObject).instanceOfString('ArrayBuffer')) {
134137
data = (eventData as JSArrayBuffer).toDart.asUint8List();
135138
} else {
136-
data = event.data;
139+
// Blobs are passed directly.
140+
data = eventData;
137141
}
138142
_controller.local.sink.add(data);
139143
}

pkgs/web_socket_channel/test/html_test.dart

+9-2
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,19 @@ import 'dart:js_interop';
1010
import 'dart:typed_data';
1111

1212
import 'package:async/async.dart';
13+
import 'package:stream_channel/stream_channel.dart';
1314
import 'package:test/test.dart';
1415
import 'package:web/helpers.dart' hide BinaryType;
1516
import 'package:web_socket_channel/html.dart';
1617
import 'package:web_socket_channel/src/web_helpers.dart';
1718
import 'package:web_socket_channel/web_socket_channel.dart';
1819

20+
extension on StreamChannel {
21+
/// Handles the Wasm case where the runtime type is actually [double] instead
22+
/// of the JS case where its [int].
23+
Future<int> get firstAsInt async => ((await stream.first) as num).toInt();
24+
}
25+
1926
void main() {
2027
late int port;
2128
setUpAll(() async {
@@ -35,7 +42,7 @@ void main() {
3542
}
3643
''', stayAlive: true);
3744

38-
port = await channel.stream.first as int;
45+
port = await channel.firstAsInt;
3946
});
4047

4148
test('communicates using an existing WebSocket', () async {
@@ -169,7 +176,7 @@ void main() {
169176
// TODO(nweiz): Make this channel use a port number that's guaranteed to be
170177
// invalid.
171178
final channel = HtmlWebSocketChannel.connect(
172-
'ws://localhost:${await serverChannel.stream.first}');
179+
'ws://localhost:${await serverChannel.firstAsInt}');
173180
expect(channel.ready, throwsA(isA<WebSocketChannelException>()));
174181
expect(channel.stream.toList(), throwsA(isA<WebSocketChannelException>()));
175182
});

0 commit comments

Comments
 (0)