Version 2.10.0-142.0.dev
Merge commit 'ac6095a7cc5098d4449db9eb7f2a41da6f76d00e' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 86ed829..5e44eee 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -1116,7 +1116,8 @@
Future<void> compute(CorrectionProducer producer) async {
producer.configure(context);
- var builder = ChangeBuilder(workspace: context.workspace);
+ var builder = ChangeBuilder(
+ workspace: context.workspace, eol: context.utils.endOfLine);
await producer.compute(builder);
_addFixFromBuilder(builder, producer.fixKind,
args: producer.fixArguments);
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_missing_overrides_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_missing_overrides_test.dart
index 6bb0fd9..5103e74 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_missing_overrides_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_missing_overrides_test.dart
@@ -197,6 +197,34 @@
''');
}
+ Future<void> test_lineEndings() async {
+ // TODO(dantup): Remove the need for this here, and have all of the tests
+ // test with CRLF when running on Windows.
+ final newlineWithoutCarriageReturn = RegExp(r'(?<!\r)\n');
+ String asCrLf(String input) =>
+ input.replaceAll(newlineWithoutCarriageReturn, '\r\n');
+ await resolveTestUnit(asCrLf('''
+class A {
+ void ma() {}
+}
+
+class B implements A {
+}
+'''));
+ await assertHasFix(asCrLf('''
+class A {
+ void ma() {}
+}
+
+class B implements A {
+ @override
+ void ma() {
+ // TODO: implement ma
+ }
+}
+'''));
+ }
+
Future<void> test_mergeToField_getterSetter() async {
await resolveTestUnit('''
class A {
diff --git a/pkg/analyzer/analysis_options.yaml b/pkg/analyzer/analysis_options.yaml
index 62f4670..b269720 100644
--- a/pkg/analyzer/analysis_options.yaml
+++ b/pkg/analyzer/analysis_options.yaml
@@ -28,6 +28,9 @@
rules:
- avoid_unused_constructor_parameters
- empty_statements
+ - iterable_contains_unrelated_type
+ - list_remove_unrelated_type
- prefer_typing_uninitialized_variables
- unnecessary_brace_in_string_interps
+ - unnecessary_overrides
- unnecessary_parenthesis
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index a469a99..ecf68fe 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -3321,11 +3321,6 @@
}
@override
- set accessors(List<PropertyAccessorElement> accessors) {
- super.accessors = accessors;
- }
-
- @override
List<InterfaceType> get allSupertypes => <InterfaceType>[supertype];
@override
@@ -3378,11 +3373,6 @@
}
@override
- set fields(List<FieldElement> fields) {
- super.fields = fields;
- }
-
- @override
bool get hasNonFinalField => false;
@override
@@ -3838,11 +3828,6 @@
ElementKind get kind => ElementKind.EXPORT;
@override
- set metadata(List<ElementAnnotation> metadata) {
- super.metadata = metadata;
- }
-
- @override
int get nameOffset {
if (linkedNode != null) {
return linkedContext.getDirectiveOffset(linkedNode);
@@ -4917,11 +4902,6 @@
ElementKind get kind => ElementKind.IMPORT;
@override
- set metadata(List<ElementAnnotation> metadata) {
- super.metadata = metadata;
- }
-
- @override
int get nameOffset {
if (linkedNode != null) {
return linkedContext.getDirectiveOffset(linkedNode);
@@ -4985,11 +4965,6 @@
}
@override
- set uri(String uri) {
- super.uri = uri;
- }
-
- @override
T accept<T>(ElementVisitor<T> visitor) => visitor.visitImportElement(this);
@override
@@ -6248,11 +6223,6 @@
return super.hasImplicitType;
}
- @override
- set hasImplicitType(bool hasImplicitType) {
- super.hasImplicitType = hasImplicitType;
- }
-
bool get hasInitializer {
return linkedNode != null && linkedContext.hasInitializer(linkedNode);
}
@@ -6406,11 +6376,6 @@
return super.hasImplicitType;
}
- @override
- set hasImplicitType(bool hasImplicitType) {
- super.hasImplicitType = hasImplicitType;
- }
-
/// True if this parameter inherits from a covariant parameter. This happens
/// when it overrides a method in a supertype that has a corresponding
/// covariant parameter.
diff --git a/pkg/analyzer/test/src/diagnostics/deprecated_member_use_test.dart b/pkg/analyzer/test/src/diagnostics/deprecated_member_use_test.dart
index 1a4ba96..f9f9e2e 100644
--- a/pkg/analyzer/test/src/diagnostics/deprecated_member_use_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/deprecated_member_use_test.dart
@@ -833,11 +833,6 @@
}
@override
- void setUp() {
- super.setUp();
- }
-
- @override
void verifyCreatedCollection() {
super.verifyCreatedCollection();
assertPackageBuildWorkspaceFor(testFilePath);
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
index c16d12f..ed00442 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
@@ -23,7 +23,7 @@
/// The end-of-line marker used in the file being edited, or `null` if the
/// default marker should be used.
- String eol;
+ final String eol;
/// A table mapping group ids to the associated linked edit groups.
final Map<String, LinkedEditGroup> _linkedEditGroups =
@@ -49,7 +49,8 @@
/// Initialize a newly created change builder. If the builder will be used to
/// create changes for Dart files, then either a [session] or a [workspace]
/// must be provided (but not both).
- ChangeBuilderImpl({AnalysisSession session, ChangeWorkspace workspace})
+ ChangeBuilderImpl(
+ {AnalysisSession session, ChangeWorkspace workspace, this.eol})
: assert(session == null || workspace == null),
workspace = workspace ?? _SingleSessionWorkspace(session);
diff --git a/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_core.dart b/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_core.dart
index 0d0914f..0d310b5 100644
--- a/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_core.dart
+++ b/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_core.dart
@@ -16,8 +16,10 @@
/// Initialize a newly created change builder. If the builder will be used to
/// create changes for Dart files, then either a [session] or a [workspace]
/// must be provided (but not both).
- factory ChangeBuilder({AnalysisSession session, ChangeWorkspace workspace}) =
- ChangeBuilderImpl;
+ factory ChangeBuilder(
+ {AnalysisSession session,
+ ChangeWorkspace workspace,
+ String eol}) = ChangeBuilderImpl;
/// Return the range of the selection for the change being built, or `null` if
/// there is no selection.
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index c31e1a2c..72c1210 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -4885,13 +4885,13 @@
OneByteString::InstanceSize(length),
is_canonical);
str->ptr()->length_ = Smi::New(length);
- uint32_t hash = 0;
+ StringHasher hasher;
for (intptr_t j = 0; j < length; j++) {
- uint8_t code_point = d->Read<uint8_t>();
- str->ptr()->data()[j] = code_point;
- hash = CombineHashes(hash, code_point);
+ uint8_t code_unit = d->Read<uint8_t>();
+ str->ptr()->data()[j] = code_unit;
+ hasher.Add(code_unit);
}
- String::SetCachedHash(str, FinalizeHash(hash, String::kHashBits));
+ String::SetCachedHash(str, hasher.Finalize());
}
}
};
@@ -4964,9 +4964,14 @@
TwoByteString::InstanceSize(length),
is_canonical);
str->ptr()->length_ = Smi::New(length);
- uint8_t* cdata = reinterpret_cast<uint8_t*>(str->ptr()->data());
- d->ReadBytes(cdata, length * 2);
- String::SetCachedHash(str, String::Hash(str));
+ StringHasher hasher;
+ for (intptr_t j = 0; j < length; j++) {
+ uint16_t code_unit = d->Read<uint8_t>();
+ code_unit = code_unit | (d->Read<uint8_t>() << 8);
+ str->ptr()->data()[j] = code_unit;
+ hasher.Add(code_unit);
+ }
+ String::SetCachedHash(str, hasher.Finalize());
}
}
};
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index e81f325..119901a 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -21734,25 +21734,6 @@
return buffer;
}
-// Synchronize with implementation in compiler (intrinsifier).
-class StringHasher : ValueObject {
- public:
- StringHasher() : hash_(0) {}
- void Add(int32_t ch) { hash_ = CombineHashes(hash_, ch); }
- void Add(const String& str, intptr_t begin_index, intptr_t len);
-
- // Return a non-zero hash of at most 'bits' bits.
- intptr_t Finalize(int bits) {
- ASSERT(1 <= bits && bits <= (kBitsPerWord - 1));
- hash_ = FinalizeHash(hash_, bits);
- ASSERT(hash_ <= static_cast<uint32_t>(kMaxInt32));
- return hash_;
- }
-
- private:
- uint32_t hash_;
-};
-
void StringHasher::Add(const String& str, intptr_t begin_index, intptr_t len) {
ASSERT(begin_index >= 0);
ASSERT(len >= 0);
@@ -21762,48 +21743,32 @@
}
if (str.IsOneByteString()) {
NoSafepointScope no_safepoint;
- uint8_t* str_addr = OneByteString::CharAddr(str, begin_index);
- for (intptr_t i = 0; i < len; i++) {
- Add(*str_addr);
- str_addr++;
- }
+ Add(OneByteString::CharAddr(str, begin_index), len);
+ } else if (str.IsExternalOneByteString()) {
+ NoSafepointScope no_safepoint;
+ Add(ExternalOneByteString::CharAddr(str, begin_index), len);
+ } else if (str.IsTwoByteString()) {
+ NoSafepointScope no_safepoint;
+ Add(TwoByteString::CharAddr(str, begin_index), len);
+ } else if (str.IsExternalOneByteString()) {
+ NoSafepointScope no_safepoint;
+ Add(ExternalTwoByteString::CharAddr(str, begin_index), len);
} else {
- String::CodePointIterator it(str, begin_index, len);
- while (it.Next()) {
- Add(it.Current());
- }
+ UNREACHABLE();
}
}
intptr_t String::Hash(const String& str, intptr_t begin_index, intptr_t len) {
StringHasher hasher;
hasher.Add(str, begin_index, len);
- return hasher.Finalize(kHashBits);
+ return hasher.Finalize();
}
intptr_t String::HashConcat(const String& str1, const String& str2) {
- intptr_t len1 = str1.Length();
- // Since String::Hash works at the code point (rune) level, a surrogate pair
- // that crosses the boundary between str1 and str2 must be composed.
- if (str1.IsTwoByteString() && Utf16::IsLeadSurrogate(str1.CharAt(len1 - 1))) {
- const String& temp = String::Handle(String::Concat(str1, str2));
- return temp.Hash();
- } else {
- StringHasher hasher;
- hasher.Add(str1, 0, len1);
- hasher.Add(str2, 0, str2.Length());
- return hasher.Finalize(kHashBits);
- }
-}
-
-template <typename T>
-static intptr_t HashImpl(const T* characters, intptr_t len) {
- ASSERT(len >= 0);
StringHasher hasher;
- for (intptr_t i = 0; i < len; i++) {
- hasher.Add(characters[i]);
- }
- return hasher.Finalize(String::kHashBits);
+ hasher.Add(str1, 0, str1.Length());
+ hasher.Add(str2, 0, str2.Length());
+ return hasher.Finalize();
}
intptr_t String::Hash(StringPtr raw) {
@@ -21833,24 +21798,21 @@
}
intptr_t String::Hash(const char* characters, intptr_t len) {
- return HashImpl(characters, len);
+ StringHasher hasher;
+ hasher.Add(reinterpret_cast<const uint8_t*>(characters), len);
+ return hasher.Finalize();
}
intptr_t String::Hash(const uint8_t* characters, intptr_t len) {
- return HashImpl(characters, len);
+ StringHasher hasher;
+ hasher.Add(characters, len);
+ return hasher.Finalize();
}
intptr_t String::Hash(const uint16_t* characters, intptr_t len) {
StringHasher hasher;
- intptr_t i = 0;
- while (i < len) {
- hasher.Add(Utf16::Next(characters, &i, len));
- }
- return hasher.Finalize(kHashBits);
-}
-
-intptr_t String::Hash(const int32_t* characters, intptr_t len) {
- return HashImpl(characters, len);
+ hasher.Add(characters, len);
+ return hasher.Finalize();
}
intptr_t String::CharSize() const {
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 46144d1b..b14b8e9 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -9065,6 +9065,32 @@
friend class Pass2Visitor; // Stack "handle"
};
+// Synchronize with implementation in compiler (intrinsifier).
+class StringHasher : ValueObject {
+ public:
+ StringHasher() : hash_(0) {}
+ void Add(uint16_t code_unit) { hash_ = CombineHashes(hash_, code_unit); }
+ void Add(const uint8_t* code_units, intptr_t len) {
+ while (len > 0) {
+ Add(*code_units);
+ code_units++;
+ len--;
+ }
+ }
+ void Add(const uint16_t* code_units, intptr_t len) {
+ while (len > 0) {
+ Add(LoadUnaligned(code_units));
+ code_units++;
+ len--;
+ }
+ }
+ void Add(const String& str, intptr_t begin_index, intptr_t len);
+ intptr_t Finalize() { return FinalizeHash(hash_, String::kHashBits); }
+
+ private:
+ uint32_t hash_;
+};
+
class OneByteString : public AllStatic {
public:
static uint16_t CharAt(const String& str, intptr_t index) {
@@ -9327,6 +9353,7 @@
friend class Class;
friend class String;
+ friend class StringHasher;
friend class SnapshotReader;
friend class Symbols;
};
@@ -9424,6 +9451,7 @@
friend class Class;
friend class String;
+ friend class StringHasher;
friend class SnapshotReader;
friend class Symbols;
friend class Utf8;
@@ -9518,6 +9546,7 @@
friend class Class;
friend class String;
+ friend class StringHasher;
friend class SnapshotReader;
friend class Symbols;
};
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 66adb2f..e1118e2 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -1338,7 +1338,7 @@
const String& clef = String::Handle(String::FromUTF16(clef_utf16, 2));
int32_t clef_utf32[] = {0x1D11E};
EXPECT(clef.Equals(clef_utf32, 1));
- intptr_t hash32 = String::Hash(clef_utf32, 1);
+ intptr_t hash32 = String::Hash(String::FromUTF32(clef_utf32, 1));
EXPECT_EQ(hash32, clef.Hash());
EXPECT_EQ(hash32, String::HashConcat(
String::Handle(String::FromUTF16(clef_utf16, 1)),
@@ -1817,7 +1817,8 @@
uint16_t char16[] = {'E', 'l', 'f'};
String& elf1 = String::Handle(Symbols::FromUTF16(thread, char16, 3));
int32_t char32[] = {'E', 'l', 'f'};
- String& elf2 = String::Handle(Symbols::FromUTF32(thread, char32, 3));
+ String& elf2 = String::Handle(
+ Symbols::New(thread, String::Handle(String::FromUTF32(char32, 3))));
EXPECT(elf1.IsSymbol());
EXPECT(elf2.IsSymbol());
EXPECT_EQ(elf1.raw(), Symbols::New(thread, "Elf"));
@@ -1832,13 +1833,14 @@
EXPECT_EQ(monkey.raw(), Symbols::New(thread, monkey_utf8));
int32_t kMonkeyFace = 0x1f435;
- String& monkey2 = String::Handle(Symbols::FromCharCode(thread, kMonkeyFace));
+ String& monkey2 = String::Handle(
+ Symbols::New(thread, String::Handle(String::FromUTF32(&kMonkeyFace, 1))));
EXPECT_EQ(monkey.raw(), monkey2.raw());
// Unicode cat face with tears of joy.
int32_t kCatFaceWithTearsOfJoy = 0x1f639;
- String& cat =
- String::Handle(Symbols::FromCharCode(thread, kCatFaceWithTearsOfJoy));
+ String& cat = String::Handle(Symbols::New(
+ thread, String::Handle(String::FromUTF32(&kCatFaceWithTearsOfJoy, 1))));
uint16_t cat_utf16[] = {0xd83d, 0xde39};
String& cat2 = String::Handle(Symbols::FromUTF16(thread, cat_utf16, 2));
diff --git a/runtime/vm/symbols.cc b/runtime/vm/symbols.cc
index 10424d1..4fb75a0 100644
--- a/runtime/vm/symbols.cc
+++ b/runtime/vm/symbols.cc
@@ -44,10 +44,6 @@
return String::FromUTF16(data, len, space);
}
-StringPtr StringFrom(const int32_t* data, intptr_t len, Heap::Space space) {
- return String::FromUTF32(data, len, space);
-}
-
template <typename CharType>
class CharArray {
public:
@@ -76,7 +72,6 @@
};
typedef CharArray<uint8_t> Latin1Array;
typedef CharArray<uint16_t> UTF16Array;
-typedef CharArray<int32_t> UTF32Array;
class StringSlice {
public:
@@ -481,12 +476,6 @@
return NewSymbol(thread, UTF16Array(utf16_array, len));
}
-StringPtr Symbols::FromUTF32(Thread* thread,
- const int32_t* utf32_array,
- intptr_t len) {
- return NewSymbol(thread, UTF32Array(utf32_array, len));
-}
-
StringPtr Symbols::FromConcat(Thread* thread,
const String& str1,
const String& str2) {
@@ -586,7 +575,7 @@
}
}
-// StringType can be StringSlice, ConcatString, or {Latin1,UTF16,UTF32}Array.
+// StringType can be StringSlice, ConcatString, or {Latin1,UTF16}Array.
template <typename StringType>
StringPtr Symbols::NewSymbol(Thread* thread, const StringType& str) {
REUSABLE_OBJECT_HANDLESCOPE(thread);
@@ -782,9 +771,9 @@
return Symbols::New(thread, buffer);
}
-StringPtr Symbols::FromCharCode(Thread* thread, int32_t char_code) {
+StringPtr Symbols::FromCharCode(Thread* thread, uint16_t char_code) {
if (char_code > kMaxOneCharCodeSymbol) {
- return FromUTF32(thread, &char_code, 1);
+ return FromUTF16(thread, &char_code, 1);
}
return predefined_[char_code];
}
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 80d6da1..df2a0f0 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -680,11 +680,6 @@
const uint16_t* utf16_array,
intptr_t len);
- // Creates a new Symbol from an array of UTF-32 encoded characters.
- static StringPtr FromUTF32(Thread* thread,
- const int32_t* utf32_array,
- intptr_t len);
-
static StringPtr New(Thread* thread, const String& str);
static StringPtr New(Thread* thread,
const String& str,
@@ -712,7 +707,7 @@
// Returns char* of predefined symbol.
static const char* Name(SymbolId symbol);
- static StringPtr FromCharCode(Thread* thread, int32_t char_code);
+ static StringPtr FromCharCode(Thread* thread, uint16_t char_code);
static StringPtr* PredefinedAddress() {
return reinterpret_cast<StringPtr*>(&predefined_);
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/tools/VERSION b/tools/VERSION
index 047c185..7869782 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 10
PATCH 0
-PRERELEASE 141
+PRERELEASE 142
PRERELEASE_PATCH 0
\ No newline at end of file