Version 2.10.0-110.5.beta
* Cherry-pick 53895b22707d986776db19d4c953d9d3b0178401 to beta
* Cherry-pick 433fcd09d91677ec9a747506e2095f7c708a0672 to beta
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index 284c144..8a9fa88 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -867,16 +867,21 @@
var savedTopLevelClass = _classEmittingExtends;
_classEmittingExtends = c;
- // Unroll mixins.
+ // Refers to 'S' in `class C extends S`. Set this to null to avoid
+ // referencing deferred supertypes in _emitClassConstructor's JS output.
+ js_ast.Expression baseClass;
+
if (shouldDefer(supertype)) {
deferredSupertypes.add(runtimeStatement('setBaseClass(#, #)', [
getBaseClass(isMixinAliasClass(c) ? 0 : mixins.length),
emitDeferredType(supertype),
]));
+ // Refers to 'supertype' without any type arguments.
supertype =
_coreTypes.rawType(supertype.classNode, _currentLibrary.nonNullable);
+ } else {
+ baseClass = emitClassRef(supertype);
}
- var baseClass = emitClassRef(supertype);
if (isMixinAliasClass(c)) {
// Given `class C = Object with M [implements I1, I2 ...];`
diff --git a/sdk/lib/_internal/vm/bin/socket_patch.dart b/sdk/lib/_internal/vm/bin/socket_patch.dart
index 0d888f2..abd886f 100644
--- a/sdk/lib/_internal/vm/bin/socket_patch.dart
+++ b/sdk/lib/_internal/vm/bin/socket_patch.dart
@@ -576,255 +576,149 @@
}
static Future<ConnectionTask<_NativeSocket>> startConnect(
- dynamic host, int port, dynamic sourceAddress) async {
+ dynamic host, int port, dynamic sourceAddress) {
if (host is String) {
host = escapeLinkLocalAddress(host);
}
_throwOnBadPort(port);
- if (sourceAddress is String) {
- sourceAddress = InternetAddress(sourceAddress);
- } else if (sourceAddress != null && sourceAddress is! _InternetAddress) {
- throw ArgumentError(
- 'sourceAddress $sourceAddress must be either String or InternetAddress');
+ if (sourceAddress != null && sourceAddress is! _InternetAddress) {
+ if (sourceAddress is String) {
+ sourceAddress = new InternetAddress(sourceAddress);
+ }
}
+ return new Future.value(host).then((host) {
+ if (host is _InternetAddress) return [host];
+ return lookup(host).then((addresses) {
+ if (addresses.isEmpty) {
+ throw createError(null, "Failed host lookup: '$host'");
+ }
+ return addresses;
+ });
+ }).then((addresses) {
+ var completer = new Completer<_NativeSocket>();
+ var it = (addresses as List<InternetAddress>).iterator;
+ var error = null;
+ var connecting = new HashMap();
- // The stream containing Internet addresses
- final stream = await _lookup(host);
- var streamSubscription;
- final completer = Completer<_NativeSocket>();
- // The first error that occurs.
- var error = null;
- // The map contains currently attempted connections.
- final connecting = HashMap<_NativeSocket, Timer>();
- // A queue contains Internet addresses that waits for connecting.
- // `stream` keeps pushing element into queue, and connectNext() consume
- // them.
- final queue = <InternetAddress>[];
- // The flag that stream of addresses is done.
- var streamClosed = false;
- // connectNext() will exhaust all elements from the queue. In the case of an
- // element taking so long that connectNext() has finished trying all
- // addresses, an explicit call to connectNext() is needed to restart the
- // process.
- var tryConnect = true;
-
- // It will try each element in the `queue` to establish the connection.
- // There can be multiple Internet addresses are under processing. Whenever
- // a connection is successfully established (by receiving a write event),
- // all pending connections stored in the `connecting` will be cancelled.
- //
- // 1. Check the status of `queue`, `completer` and `connecting`.
- // Return an error if all elements have been tried.
- // Reset tryConnect so that next element will be examined if `stream`
- // is still open.
- // 2. Create a socket.
- // Try next address by issuing another connectNext() if this failed and
- // keep the first error.
- // 3. Register a timer, if socket creation succeeds, to try next address.
- // 4. Set up a handler for socket. It will cancel all other pending
- // connections and timers if it succeeds. Otherwise, keep the first
- // error and issue connectNext().
- void connectNext() {
- if (completer.isCompleted) return;
- if (queue.isEmpty) {
- if (streamClosed && connecting.isEmpty) {
- assert(error != null);
- completer.completeError(error);
- } else if (!streamClosed) {
- // If new addresses comes after all elements in the queue have been
- // processed, issue another connectNext().
- tryConnect = true;
- }
- return;
- }
- final _InternetAddress address = queue.removeAt(0) as _InternetAddress;
- var socket = _NativeSocket.normal(address);
- var result;
- if (sourceAddress == null) {
- if (address.type == InternetAddressType.unix) {
- result = socket.nativeCreateUnixDomainConnect(
- address.address, _Namespace._namespace);
- } else {
- result = socket.nativeCreateConnect(
- address._in_addr, port, address._scope_id);
- }
- } else {
- assert(sourceAddress is _InternetAddress);
- if (address.type == InternetAddressType.unix) {
- assert(sourceAddress.type == InternetAddressType.unix);
- result = socket.nativeCreateUnixDomainBindConnect(
- address.address, sourceAddress.address, _Namespace._namespace);
- } else {
- result = socket.nativeCreateBindConnect(address._in_addr, port,
- sourceAddress._in_addr, address._scope_id);
- }
- }
- if (result is OSError) {
- // Keep first error, if present.
- if (error == null) {
- int errorCode = result.errorCode;
- if (sourceAddress != null &&
- errorCode != null &&
- socket.isBindError(errorCode)) {
- error = createError(result, "Bind failed", sourceAddress);
- } else {
- error = createError(result, "Connection failed", address, port);
+ void connectNext() {
+ if (!it.moveNext()) {
+ if (connecting.isEmpty) {
+ assert(error != null);
+ completer.completeError(error);
}
- }
- connectNext();
- } else {
- // Query the local port for error messages.
- try {
- socket.port;
- } catch (e) {
- error ??= createError(e, "Connection failed", address, port);
- connectNext();
return;
}
- // Set up timer for when we should retry the next address
- // (if any).
- final duration =
- address.isLoopback ? _retryDurationLoopback : _retryDuration;
- final timer = Timer(duration, connectNext);
- setupResourceInfo(socket);
-
- connecting[socket] = timer;
- // Setup handlers for receiving the first write event which
- // indicate that the socket is fully connected.
- socket.setHandlers(write: () {
- timer.cancel();
- connecting.remove(socket);
- // From 'man 2 connect':
- // After select(2) indicates writability, use getsockopt(2) to read
- // the SO_ERROR option at level SOL_SOCKET to determine whether
- // connect() completed successfully (SO_ERROR is zero) or
- // unsuccessfully.
- OSError osError = socket.nativeGetError();
- if (osError.errorCode != 0) {
- socket.close();
- error ??= osError;
- // No timer is active for triggering next tryConnect(), do it
- // manually.
- if (connecting.isEmpty) connectNext();
- return;
+ final _InternetAddress address = it.current as _InternetAddress;
+ var socket = new _NativeSocket.normal(address);
+ var result;
+ if (sourceAddress == null) {
+ if (address.type == InternetAddressType.unix) {
+ result = socket.nativeCreateUnixDomainConnect(
+ address.address, _Namespace._namespace);
+ } else {
+ result = socket.nativeCreateConnect(
+ address._in_addr, port, address._scope_id);
}
- socket.setListening(read: false, write: false);
- completer.complete(socket);
- connecting.forEach((s, t) {
- t.cancel();
- s.close();
- s.setHandlers();
- s.setListening(read: false, write: false);
- });
- connecting.clear();
- }, error: (e, st) {
- timer.cancel();
- socket.close();
+ } else {
+ assert(sourceAddress is _InternetAddress);
+ if (address.type == InternetAddressType.unix) {
+ assert(sourceAddress.type == InternetAddressType.unix);
+ result = socket.nativeCreateUnixDomainBindConnect(
+ address.address, sourceAddress.address, _Namespace._namespace);
+ } else {
+ result = socket.nativeCreateBindConnect(address._in_addr, port,
+ sourceAddress._in_addr, address._scope_id);
+ }
+ }
+ if (result is OSError) {
// Keep first error, if present.
- error ??= e;
- connecting.remove(socket);
- if (connecting.isEmpty) connectNext();
- });
- socket.setListening(read: false, write: true);
- }
- }
-
- void onCancel() {
- connecting.forEach((s, t) {
- t.cancel();
- s.close();
- s.setHandlers();
- s.setListening(read: false, write: false);
- if (error == null) {
- error = createError(null,
- "Connection attempt cancelled, host: ${host}, port: ${port}");
- }
- });
- connecting.clear();
- if (!completer.isCompleted) {
- completer.completeError(error! as Object);
- }
- }
-
- // The stream is constructed in the _lookup() and should not emit the error.
- streamSubscription = stream.listen((address) {
- queue.add(address);
- if (tryConnect) {
- tryConnect = false;
- connectNext();
- }
- }, onError: (e) {
- streamSubscription.cancel();
- streamClosed = true;
- // The error comes from lookup() and we just rethrow the error.
- throw e;
- }, onDone: () {
- streamClosed = true;
- // In case that stream closes later than connectNext() exhausts all
- // Internet addresses, check whether an error is thrown.
- if (queue.isEmpty && connecting.isEmpty && !completer.isCompleted) {
- completer.completeError(error);
- }
- });
-
- return Future.value(
- ConnectionTask<_NativeSocket>._(completer.future, onCancel));
- }
-
- // The [host] should be either a String or an _InternetAddress.
- static Future<Stream> _lookup(host) async {
- if (host is _InternetAddress) {
- return Stream.value(host);
- } else {
- assert(host is String);
- // If host is an IPv4 or IPv6 literal, bypass the lookup.
- final inAddr = _InternetAddress._parse(host);
- if (inAddr != null && inAddr.length == _InternetAddress._IPv4AddrLength) {
- var addresses = await lookup(host, type: InternetAddressType.IPv4);
- if (addresses.isEmpty) {
- throw createError(null, "Failed host lookup: '$host'");
- }
- return Stream.fromIterable(addresses);
- }
- if (inAddr != null && inAddr.length == _InternetAddress._IPv6AddrLength) {
- var addresses = await lookup(host, type: InternetAddressType.IPv6);
- if (addresses.isEmpty) {
- throw createError(null, "Failed host lookup: '$host'");
- }
- return Stream.fromIterable(addresses);
- }
-
- if (Platform.isIOS) {
- return _concurrentLookup(host);
- } else {
- var addresses = await lookup(host, type: InternetAddressType.any);
- if (addresses.isEmpty) {
- throw createError(null, "Failed host lookup: '$host'");
- }
- return Stream.fromIterable(addresses);
- }
- }
- }
-
- static Stream<InternetAddress> _concurrentLookup(String host) {
- final controller = StreamController<InternetAddress>();
- // Lookup IPv4 and IPv6 concurrently
- Future.wait([
- for (final type in [InternetAddressType.IPv4, InternetAddressType.IPv6])
- lookup(host, type: type).then((list) {
- for (final address in list) {
- controller.add(address);
+ if (error == null) {
+ int errorCode = result.errorCode;
+ if (sourceAddress != null &&
+ errorCode != null &&
+ socket.isBindError(errorCode)) {
+ error = createError(result, "Bind failed", sourceAddress);
+ } else {
+ error = createError(result, "Connection failed", address, port);
+ }
}
- return list.isNotEmpty;
- })
- ]).then((successes) {
- if (!successes.contains(true)) {
- // Neither lookup found an address.
- throw createError(null, "Failed host lookup: '$host'");
+ connectNext();
+ } else {
+ // Query the local port for error messages.
+ try {
+ socket.port;
+ } catch (e) {
+ if (error == null) {
+ error = createError(e, "Connection failed", address, port);
+ }
+ connectNext();
+ }
+ // Set up timer for when we should retry the next address
+ // (if any).
+ var duration =
+ address.isLoopback ? _retryDurationLoopback : _retryDuration;
+ var timer = new Timer(duration, connectNext);
+ setupResourceInfo(socket);
+
+ connecting[socket] = timer;
+ // Setup handlers for receiving the first write event which
+ // indicate that the socket is fully connected.
+ socket.setHandlers(write: () {
+ timer.cancel();
+ connecting.remove(socket);
+ // From 'man 2 connect':
+ // After select(2) indicates writability, use getsockopt(2) to read
+ // the SO_ERROR option at level SOL_SOCKET to determine whether
+ // connect() completed successfully (SO_ERROR is zero) or
+ // unsuccessfully.
+ OSError osError = socket.nativeGetError();
+ if (osError.errorCode != 0) {
+ socket.close();
+ if (error == null) error = osError;
+ if (connecting.isEmpty) connectNext();
+ return;
+ }
+ socket.setListening(read: false, write: false);
+ completer.complete(socket);
+ connecting.forEach((s, t) {
+ t.cancel();
+ s.close();
+ s.setHandlers();
+ s.setListening(read: false, write: false);
+ });
+ connecting.clear();
+ }, error: (e, st) {
+ timer.cancel();
+ socket.close();
+ // Keep first error, if present.
+ if (error == null) error = e;
+ connecting.remove(socket);
+ if (connecting.isEmpty) connectNext();
+ });
+ socket.setListening(read: false, write: true);
+ }
}
- controller.close();
+
+ void onCancel() {
+ connecting.forEach((s, t) {
+ t.cancel();
+ s.close();
+ s.setHandlers();
+ s.setListening(read: false, write: false);
+ if (error == null) {
+ error = createError(null,
+ "Connection attempt cancelled, host: ${host}, port: ${port}");
+ }
+ });
+ connecting.clear();
+ if (!completer.isCompleted) {
+ completer.completeError(error);
+ }
+ }
+
+ connectNext();
+ return new ConnectionTask<_NativeSocket>._(completer.future, onCancel);
});
- return controller.stream;
}
static Future<_NativeSocket> connect(
diff --git a/tests/language/export/dual_export_triangle_a.dart b/tests/language/export/dual_export_triangle_a.dart
new file mode 100644
index 0000000..eb99e69
--- /dev/null
+++ b/tests/language/export/dual_export_triangle_a.dart
@@ -0,0 +1,3 @@
+import 'dual_export_triangle_b.dart';
+
+class A extends B {}
diff --git a/tests/language/export/dual_export_triangle_b.dart b/tests/language/export/dual_export_triangle_b.dart
new file mode 100644
index 0000000..c4d56e1
--- /dev/null
+++ b/tests/language/export/dual_export_triangle_b.dart
@@ -0,0 +1,3 @@
+import 'dual_export_triangle_c.dart';
+
+class B extends C {}
diff --git a/tests/language/export/dual_export_triangle_c.dart b/tests/language/export/dual_export_triangle_c.dart
new file mode 100644
index 0000000..705e6daa
--- /dev/null
+++ b/tests/language/export/dual_export_triangle_c.dart
@@ -0,0 +1,5 @@
+import 'dual_export_triangle_a.dart';
+
+class C0<T> {}
+
+class C extends C0<A> {}
diff --git a/tests/language/export/dual_export_triangle_entrypoint.dart b/tests/language/export/dual_export_triangle_entrypoint.dart
new file mode 100644
index 0000000..47d048d
--- /dev/null
+++ b/tests/language/export/dual_export_triangle_entrypoint.dart
@@ -0,0 +1,3 @@
+import 'dual_export_triangle_a.dart';
+
+class Entrypoint {}
diff --git a/tests/language/export/dual_export_triangle_test.dart b/tests/language/export/dual_export_triangle_test.dart
new file mode 100644
index 0000000..0d64571
--- /dev/null
+++ b/tests/language/export/dual_export_triangle_test.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2020, 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.
+
+// Test for cyclic export regression seen in: https://github.com/flutter/flutter/issues/64011
+
+// dual_export_triangle_entrypoint imports dual_export_triangle_a.
+// dual_export_triangle_a defines A (extends B).
+// dual_export_triangle_b defines B (extends C).
+// dual_export_triangle_c defines C (with type parameter A).
+// dual_export_triangle_test exports both dual_export_triangle_entrypoint and
+// either dual_export_triangle_b or dual_export_triangle_c.
+
+library dual_export_triangle_test;
+
+import 'dual_export_triangle_entrypoint.dart';
+
+export 'dual_export_triangle_entrypoint.dart';
+export 'dual_export_triangle_b.dart';
+
+main() {
+ print(Entrypoint());
+}
diff --git a/tools/VERSION b/tools/VERSION
index c3da8da..96d1078 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
MINOR 10
PATCH 0
PRERELEASE 110
-PRERELEASE_PATCH 3
+PRERELEASE_PATCH 5