-
Notifications
You must be signed in to change notification settings - Fork 34
Conversation
…nullability result
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I enabled strong-mode
for implicit casts to find all the places with them
it seems that only 3 places with potentially unsafe casts left (all highlighted in next comments)
_handler!(message)
SendPort.send
singleCompletePort
with dynamic generic parameters
@@ -49,7 +49,7 @@ class _MultiplexRawReceivePort implements RawReceivePort { | |||
SendPort get sendPort => _multiplexer._createSendPort(_id); | |||
|
|||
void _invokeHandler(message) { | |||
_handler(message); | |||
_handler!(message); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it seems to be breaking change to call handler conditionally, but leaving it like this seems to be a mistake too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is the only place with force unwrap besides 2 valid operator[]
on maps in registry.dart
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the current behavior is wrong. Also, it's not considered a breaking change to change the behavior of something which throws an Error
, so changing it is fine.
I think _handle?.call(message)
would be correct. The alternative is to have a default handler of (_){}
.
test('Value', () { | ||
var completer = Completer.sync(); | ||
var p = singleCallbackPort(completer.complete); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a removal of duplicated test
test('FutureValueWithoutTimeout invalid null', () { | ||
return expectLater(singleResponseFutureWithoutTimeout<int>((SendPort p) { | ||
p.send(null); | ||
}), throwsA(isA<TypeError>())); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is impossible to check statically since SendPort.send
expects Object?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, ports are inherently untyped. Primarily because they are able to send values between isolates that don't necessarily share types.
We have to get the value to the other side of the port connection in order to check it against the original type argument.
test('TimeoutFirst with invalid null', () { | ||
final completer = Completer<int>.sync(); | ||
|
||
/// example of incomplete generic parameters promotion. | ||
/// same code with [singleCompletePort<int, dynamic>] is a compile time error | ||
final p = singleCompletePort( | ||
completer, | ||
timeout: _ms * 100, | ||
onTimeout: () => null, | ||
); | ||
Timer(_ms * 500, () => p.send(42)); | ||
return expectLater(completer.future, throwsA(isA<TypeError>())); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this seems to be related to generic types promotion with incomplete types
callback
arg is required to promote second generic parameter, so both parameters are handled as dynamic
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems likely, yes.
Not sure if this will ever be merged. I released your branch as a project |
@2ZeroSix I've found a bug in the isolates version of the code is there a github repo for it that I can push a patch to? ../../../.pub-cache/hosted/pub.dartlang.org/isolates-3.0.3+7/lib/isolate_runner.dart:85:36: Warning: Operand of null-aware operation '??' has type 'bool' which excludes null. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks very thorough. There are a few subtle potentially-bugs, but otherwise very solid work.
I'm half torn towards rewriting the API to avoid the "you can omit the value if it's null
" places where it'll now be an error to pass null
with a non-nullable type argument.
It's probably better to launch this as null safe first, then make a 3.0.0 version afterwards.
@@ -53,7 +55,7 @@ class EchoHttpListener implements HttpListener { | |||
static final _id = Isolate.current.hashCode; | |||
final SendPort _counter; | |||
|
|||
StreamSubscription _subscription; | |||
late StreamSubscription _subscription; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer to not use late
unless necessary. I'd just use StreamSubscription?
here.
int port = args[0]; | ||
HttpListener listener = args[1]; | ||
final port = args[0] as int; | ||
final listener = args[1] as HttpListener; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code doesn't use final
, so I wouldn't change that.
I'd change the argument to List<Object?>
instead of (implicit) List<dynamic>
while here.
@@ -61,8 +61,8 @@ class IsolateRunner implements Runner { | |||
isolate.setErrorsFatal(false); | |||
var pingChannel = SingleResponseChannel(); | |||
isolate.ping(pingChannel.port); | |||
var commandPort = await channel.result; | |||
var result = IsolateRunner(isolate, commandPort); | |||
final commandPort = await channel.result as SendPort; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, the style here is not to use final
. Just use var
.
@@ -150,7 +151,7 @@ class IsolateRunner implements Runner { | |||
/// Calling pause more than once with the same `resumeCapability` | |||
/// has no further effect. Only a single call to [resume] is needed | |||
/// to resume the isolate. | |||
void pause([Capability resumeCapability]) { | |||
void pause([Capability? resumeCapability]) { | |||
resumeCapability ??= isolate.pauseCapability; | |||
isolate.pause(resumeCapability); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably throw here if resumeCapability
is still null
. That, or return the result of isolate.pause
. Or have a default resumeCapability
it uses if necessary.
As written, it pauses in a way which cannot be resumed if isolate.pauseCapability
is null
. I'll look into this.
resumeCapability ??= isolate.pauseCapability; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just do:
if (resumeCapability == null) return;
@@ -22,259 +22,335 @@ void main() { | |||
|
|||
void testSingleCallbackPort() { | |||
test('Value', () { | |||
var completer = Completer.sync(); | |||
var p = singleCallbackPort(completer.complete); | |||
final completer = Completer.sync(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to change var
to final
.
test('TimeoutFirst with invalid null', () { | ||
final completer = Completer<int>.sync(); | ||
|
||
/// example of incomplete generic parameters promotion. | ||
/// same code with [singleCompletePort<int, dynamic>] is a compile time error | ||
final p = singleCompletePort( | ||
completer, | ||
timeout: _ms * 100, | ||
onTimeout: () => null, | ||
); | ||
Timer(_ms * 500, () => p.send(42)); | ||
return expectLater(completer.future, throwsA(isA<TypeError>())); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems likely, yes.
test('TimeoutFirst with invalid null', () { | ||
final completer = Completer<int>.sync(); | ||
|
||
/// example of incomplete generic parameters promotion. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Format comments as sentences, so capital first letter and trailing .
.
test('FutureValueWithoutTimeout invalid null', () { | ||
return expectLater(singleResponseFutureWithoutTimeout<int>((SendPort p) { | ||
p.send(null); | ||
}), throwsA(isA<TypeError>())); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, ports are inherently untyped. Primarily because they are able to send values between isolates that don't necessarily share types.
We have to get the value to the other side of the port connection in order to check it against the original type argument.
@override | ||
bool operator ==(Object other) => other is Element && id == other.id; | ||
bool operator ==(Object? other) => other is Element && id == other.id; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be (Object other)
. A null
value can't reach operator==
.
FYI |
@2ZeroSix any eta on getting this merged? |
Hi, any updates in this PR ? Would be great if this could get merged or reviewed ! |
Thanks @2ZeroSix I'm waiting for this too. |
@2ZeroSix Can you add me as a collaborator in your repo? I can push this forward as far as it can. We're currently waiting it for too long. :) |
Also I saw @irhn said is investigating something like #45 (comment) , any result? |
@@ -122,7 +122,7 @@ class Registry<T> { | |||
_timeout = timeout; | |||
|
|||
_RegistryCache get _cache { | |||
_RegistryCache cache = _caches[this]; | |||
var cache = _caches[this] as _RegistryCache?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This entire getter could be
_RegistryCache get _cache => _caches[this] ??= _RegistryCache();
if _caches
is declared as Expando<_RegistryCache>
.
..[0] = v1 | ||
..[1] = v2 | ||
..[2] = v3; | ||
|
||
/// Create a four-element fixed-length list. | ||
List<Object> list4(Object v1, Object v2, Object v3, Object v4) => | ||
List<Object?> list4(Object? v1, Object? v2, Object? v3, Object? v4) => | ||
List.filled(4, null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could possibly optimize these a little by doing:
List.filled(4, v1)
..[2] = v2
..[3] = v3
..[4] = v4;
and avoid one assignment. (Not a big save, but a save!)./
I've addressed all suggestions in this PR to #51 . FYI 😃 |
Closes #43
then
clauses without return:then<Null>
fixes
Null is not a subtype of Future<Never>
PriorityQueue
from package:collection to get rid of nullable_LoadBalancerEntry
everywhere