adaptSink<T> static method
Adapt a sink that came from d4rt into a typed Sink<T>.
The contravariant twin of coerceStream, and it cannot be a
coercion. The interpreter erases type arguments, so
ChunkedConversionSink.withCallback(cb) evaluates to a native
ChunkedConversionSink<Object?>, and Dart generics are covariant:
Sink<Object?> is NOT a Sink<String>. Every
startChunkedConversion guard and cast in stdlib/convert therefore
rejected every sink a script could build, exactly as bind rejected
every stream before SCC68.
A Stream<T> is a PRODUCER — coerceStream maps its elements on the
way out, and there is something to map. A Sink<T> is a CONSUMER:
nothing has been produced yet, only a method that will be called with a
T later. So the repair is a forwarding wrapper that IS the demanded
Sink<T> and passes add/close down to the erased sink underneath.
An already-typed Sink<T> passes through by identity rather than being
wrapped — the same rule coerceStream follows, and for the same reason:
a wrapper around a sink that did not need one would make identical
false for a value the caller still holds.
A value that is not a Sink at all still throws — this widens nothing.
Implementation
static Sink<T> adaptSink<T>(Object? arg, String paramName) {
final value = arg is BridgedInstance ? arg.nativeObject : arg;
if (value is Sink<T>) return value;
if (value is! Sink) {
throw ArgumentD4rtException(
'Invalid parameter "$paramName": expected Sink<$T>, '
'got ${value.runtimeType}',
);
}
return _AdaptedSink<T>(value, paramName);
}