Migrate dart:convert to NNBD.

Initial port of dart:convert to be statically NNBD clean with the
current analyzer implementation, using some assumptions about
how the rest of the platform libraries will be migrated.

Change-Id: Iad96c684dc7a2c1315ee6a3090ccd7b6d0f71c58
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/122173
Reviewed-by: Bob Nystrom <rnystrom@google.com>
Reviewed-by: Lasse R.H. Nielsen <lrn@google.com>
This commit is contained in:
Leaf Petersen 2019-10-28 23:58:11 +00:00
parent fe3aa126a2
commit 225df822b6
14 changed files with 204 additions and 176 deletions

View file

@ -2,8 +2,6 @@
// 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.
// @dart = 2.5
part of dart.convert;
/// An instance of the default implementation of the [AsciiCodec].
@ -50,9 +48,8 @@ class AsciiCodec extends Encoding {
///
/// If [allowInvalid] is not provided, it defaults to the value used to create
/// this [AsciiCodec].
String decode(List<int> bytes, {bool allowInvalid}) {
allowInvalid ??= _allowInvalid;
if (allowInvalid) {
String decode(List<int> bytes, {bool? allowInvalid}) {
if (allowInvalid ?? _allowInvalid) {
return const AsciiDecoder(allowInvalid: true).convert(bytes);
} else {
return const AsciiDecoder(allowInvalid: false).convert(bytes);
@ -77,9 +74,13 @@ class _UnicodeSubsetEncoder extends Converter<String, List<int>> {
///
/// If [start] and [end] are provided, only the substring
/// `string.substring(start, end)` is used as input to the conversion.
Uint8List convert(String string, [int start = 0, int end]) {
Uint8List convert(String string, [int start = 0, int? end]) {
var stringLength = string.length;
end = RangeError.checkValidRange(start, end, stringLength);
// TODO(38725): Remove workaround when assignment promotion is implemented
if (end == null) {
throw RangeError("Invalid range");
}
var length = end - start;
var result = Uint8List(length);
for (var i = 0; i < length; i++) {
@ -164,11 +165,12 @@ abstract class _UnicodeSubsetDecoder extends Converter<List<int>, String> {
///
/// If [start] and [end] are provided, only the sub-list of bytes from
/// `start` to `end` (`end` not inclusive) is used as input to the conversion.
String convert(List<int> bytes, [int start = 0, int end]) {
var byteCount = bytes.length;
RangeError.checkValidRange(start, end, byteCount);
end ??= byteCount;
String convert(List<int> bytes, [int start = 0, int? end]) {
end = RangeError.checkValidRange(start, end, bytes.length);
// TODO(38725): Remove workaround when assignment promotion is implemented
if (end == null) {
throw RangeError("Invalid range");
}
for (var i = start; i < end; i++) {
var byte = bytes[i];
if ((byte & ~_subsetMask) != 0) {

View file

@ -2,8 +2,6 @@
// 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.
// @dart = 2.5
part of dart.convert;
/// A [base64](https://tools.ietf.org/html/rfc4648) encoder and decoder.
@ -37,18 +35,18 @@ const Base64Codec base64Url = Base64Codec.urlSafe();
/// Encodes [bytes] using [base64](https://tools.ietf.org/html/rfc4648) encoding.
///
/// Shorthand for [base64.encode]. Useful if a local variable shadows the global
/// Shorthand for `base64.encode(bytes)`. Useful if a local variable shadows the global
/// [base64] constant.
String base64Encode(List<int> bytes) => base64.encode(bytes);
/// Encodes [bytes] using [base64url](https://tools.ietf.org/html/rfc4648) encoding.
///
/// Shorthand for [base64url.encode].
/// Shorthand for `base64url.encode(bytes)`.
String base64UrlEncode(List<int> bytes) => base64Url.encode(bytes);
/// Decodes [base64](https://tools.ietf.org/html/rfc4648) or [base64url](https://tools.ietf.org/html/rfc4648) encoded bytes.
///
/// Shorthand for [base64.decode]. Useful if a local variable shadows the
/// Shorthand for `base64.decode(bytes)`. Useful if a local variable shadows the
/// global [base64] constant.
Uint8List base64Decode(String source) => base64.decode(source);
@ -95,11 +93,15 @@ class Base64Codec extends Codec<List<int>, String> {
/// * Validate that existing padding (trailing `=` characters) is correct.
/// * If no padding exists, add correct padding if necessary and possible.
/// * Validate that the length is correct (a multiple of four).
String normalize(String source, [int start = 0, int end]) {
String normalize(String source, [int start = 0, int? end]) {
end = RangeError.checkValidRange(start, end, source.length);
// TODO(38725): Remove workaround when assignment promotion is implemented
if (end == null) {
throw RangeError("Invalid range");
}
const percent = 0x25;
const equals = 0x3d;
StringBuffer buffer;
StringBuffer? buffer;
var sliceStart = start;
var alphabet = _Base64Encoder._base64Alphabet;
var inverseAlphabet = _Base64Decoder._inverseAlphabet;
@ -142,9 +144,9 @@ class Base64Codec extends Codec<List<int>, String> {
if (originalChar == equals) continue;
}
if (value != _Base64Decoder._invalid) {
buffer ??= StringBuffer();
buffer.write(source.substring(sliceStart, sliceEnd));
buffer.writeCharCode(char);
(buffer ??= StringBuffer())
..write(source.substring(sliceStart, sliceEnd))
..writeCharCode(char);
sliceStart = i;
continue;
}
@ -233,7 +235,7 @@ class Base64Encoder extends Converter<List<int>, String> {
String convert(List<int> input) {
if (input.isEmpty) return "";
var encoder = _Base64Encoder(_urlSafe);
var buffer = encoder.encode(input, 0, input.length, true);
var buffer = encoder.encode(input, 0, input.length, true)!;
return String.fromCharCodes(buffer);
}
@ -302,10 +304,10 @@ class _Base64Encoder {
/// with the necessary padding.
///
/// Returns `null` if there is no output.
Uint8List encode(List<int> bytes, int start, int end, bool isLast) {
Uint8List? encode(List<int> bytes, int start, int end, bool isLast) {
assert(0 <= start);
assert(start <= end);
assert(bytes == null || end <= bytes.length);
assert(end <= bytes.length);
var length = end - start;
var count = _stateCount(_state);
@ -395,16 +397,21 @@ class _BufferCachingBase64Encoder extends _Base64Encoder {
///
/// When the buffer isn't released to the sink, only used to create another
/// value (a string), the buffer can be reused between chunks.
Uint8List bufferCache;
Uint8List? bufferCache;
_BufferCachingBase64Encoder(bool urlSafe) : super(urlSafe);
Uint8List createBuffer(int bufferLength) {
if (bufferCache == null || bufferCache.length < bufferLength) {
bufferCache = Uint8List(bufferLength);
Uint8List? buffer = bufferCache;
if (buffer == null || buffer.length < bufferLength) {
bufferCache = buffer = Uint8List(bufferLength);
}
// TODO(38725): Remove workaround when assignment promotion is implemented
if (buffer == null) {
throw "unreachable";
}
// Return a view of the buffer, so it has the requested length.
return Uint8List.view(bufferCache.buffer, 0, bufferLength);
return Uint8List.view(buffer.buffer, 0, bufferLength);
}
}
@ -414,7 +421,7 @@ abstract class _Base64EncoderSink extends ByteConversionSinkBase {
}
void close() {
_add(null, 0, 0, true);
_add(const [], 0, 0, true);
}
void addSlice(List<int> source, int start, int end, bool isLast) {
@ -480,11 +487,15 @@ class Base64Decoder extends Converter<String, List<int>> {
/// The returned [Uint8List] contains exactly the decoded bytes,
/// so the [Uint8List.length] is precisely the number of decoded bytes.
/// The [Uint8List.buffer] may be larger than the decoded bytes.
Uint8List convert(String input, [int start = 0, int end]) {
Uint8List convert(String input, [int start = 0, int? end]) {
end = RangeError.checkValidRange(start, end, input.length);
// TODO(38725): Remove workaround when assignment promotion is implemented
if (end == null) {
throw RangeError("Invalid range");
}
if (start == end) return Uint8List(0);
var decoder = _Base64Decoder();
var buffer = decoder.decode(input, start, end);
var buffer = decoder.decode(input, start, end)!;
decoder.close(input, end);
return buffer;
}
@ -594,7 +605,7 @@ class _Base64Decoder {
/// Returns a [Uint8List] with the decoded bytes.
/// If a previous call had an incomplete four-character block, the bits from
/// those are included in decoding
Uint8List decode(String input, int start, int end) {
Uint8List? decode(String input, int start, int end) {
assert(0 <= start);
assert(start <= end);
assert(end <= input.length);
@ -604,12 +615,14 @@ class _Base64Decoder {
}
if (start == end) return Uint8List(0);
var buffer = _allocateBuffer(input, start, end, _state);
_state = decodeChunk(input, start, end, buffer, 0, _state);
if (buffer.length > 0) {
_state = decodeChunk(input, start, end, buffer, 0, _state);
}
return buffer;
}
/// Checks that [_state] represents a valid decoding.
void close(String input, int end) {
void close(String? input, int? end) {
if (_state < _encodePaddingState(0)) {
throw FormatException("Missing padding character", input, end);
}
@ -693,6 +706,8 @@ class _Base64Decoder {
throw FormatException("Invalid character", input, i);
}
static Uint8List _emptyBuffer = Uint8List(0);
/// Allocates a buffer with room for the decoding of a substring of [input].
///
/// Includes room for the characters in [state], and handles padding correctly.
@ -712,7 +727,7 @@ class _Base64Decoder {
if (bufferLength > 0) return Uint8List(bufferLength);
// If the input plus state is less than four characters, and it's not
// at the end of input, no buffer is needed.
return null;
return _emptyBuffer;
}
/// Returns the position of the start of padding at the end of the input.
@ -844,7 +859,7 @@ class _Base64DecoderSink extends StringConversionSinkBase {
}
void addSlice(String string, int start, int end, bool isLast) {
end = RangeError.checkValidRange(start, end, string.length);
RangeError.checkValidRange(start, end, string.length);
if (start == end) return;
var buffer = _decoder.decode(string, start, end);
if (buffer != null) _sink.add(buffer);

View file

@ -2,8 +2,6 @@
// 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.
// @dart = 2.5
part of dart.convert;
/// The [ByteConversionSink] provides an interface for converters to

View file

@ -2,8 +2,6 @@
// 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.
// @dart = 2.5
part of dart.convert;
/// A [ChunkedConversionSink] is used to transmit data more efficiently between
@ -74,7 +72,7 @@ class _ConverterStreamEventSink<S, T> implements EventSink<S> {
_chunkedSink.add(o);
}
void addError(Object error, [StackTrace stackTrace]) {
void addError(Object error, [StackTrace? stackTrace]) {
_eventSink.addError(error, stackTrace);
}

View file

@ -2,8 +2,6 @@
// 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.
// @dart = 2.5
part of dart.convert;
/// A [Codec] encodes and (if supported) decodes data.

View file

@ -2,8 +2,6 @@
// 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.
// @dart = 2.5
///
/// Encoders and decoders for converting between different data representations,
/// including JSON and UTF-8.

View file

@ -2,8 +2,6 @@
// 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.
// @dart = 2.5
part of dart.convert;
/// A [Converter] converts data from one representation into another.

View file

@ -2,8 +2,6 @@
// 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.
// @dart = 2.5
part of dart.convert;
/// Open-ended Encoding enum.
@ -74,10 +72,9 @@ abstract class Encoding extends Codec<String, List<int>> {
///
/// The [name] passed is case insensitive.
///
/// If character set is not supported [:null:] is returned.
static Encoding getByName(String name) {
/// If character set is not supported `null` is returned.
static Encoding? getByName(String? name) {
if (name == null) return null;
name = name.toLowerCase();
return _nameToEncoding[name];
return _nameToEncoding[name.toLowerCase()];
}
}

View file

@ -2,8 +2,6 @@
// 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.
// @dart = 2.5
part of dart.convert;
/// A `String` converter that converts characters to HTML entities.
@ -160,11 +158,11 @@ class HtmlEscape extends Converter<String, String> {
///
/// Returns `null` if no changes were necessary, otherwise returns
/// the converted string.
String _convert(String text, int start, int end) {
StringBuffer result;
String? _convert(String text, int start, int end) {
StringBuffer? result;
for (var i = start; i < end; i++) {
var ch = text[i];
String replacement;
String? replacement;
switch (ch) {
case '&':
replacement = '&amp;';
@ -187,6 +185,11 @@ class HtmlEscape extends Converter<String, String> {
}
if (replacement != null) {
result ??= StringBuffer();
// TODO(38725): Remove workaround when assignment promotion is
// implemented
if (result == null) {
throw "unreachable";
}
if (i > start) result.write(text.substring(start, i));
result.write(replacement);
start = i + 1;

View file

@ -2,8 +2,6 @@
// 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.
// @dart = 2.5
part of dart.convert;
/// Error thrown by JSON serialization if an object cannot be serialized.
@ -16,15 +14,15 @@ part of dart.convert;
/// serializable, the [cause] is null.
class JsonUnsupportedObjectError extends Error {
/// The object that could not be serialized.
final Object unsupportedObject;
final Object? unsupportedObject;
/// The exception thrown when trying to convert the object.
final Object cause;
final Object? cause;
/// The partial result of the conversion, up until the error happened.
///
/// May be null.
final String partialResult;
final String? partialResult;
JsonUnsupportedObjectError(this.unsupportedObject,
{this.cause, this.partialResult});
@ -48,7 +46,7 @@ class JsonUnsupportedObjectError extends Error {
/// When the cycle is detected, a [JsonCyclicError] is thrown.
class JsonCyclicError extends JsonUnsupportedObjectError {
/// The first object that was detected as part of a cycle.
JsonCyclicError(Object object) : super(object);
JsonCyclicError(Object? object) : super(object);
String toString() => "Cyclic error in JSON stringify";
}
@ -76,9 +74,10 @@ const JsonCodec json = JsonCodec();
/// If [toEncodable] is omitted, it defaults to a function that returns the
/// result of calling `.toJson()` on the unencodable object.
///
/// Shorthand for [json.encode]. Useful if a local variable shadows the global
/// Shorthand for `json.encode`. Useful if a local variable shadows the global
/// [json] constant.
String jsonEncode(Object object, {Object toEncodable(Object nonEncodable)}) =>
String jsonEncode(Object? object,
{Object? toEncodable(Object? nonEncodable)?}) =>
json.encode(object, toEncodable: toEncodable);
/// Parses the string and returns the resulting Json object.
@ -90,9 +89,10 @@ String jsonEncode(Object object, {Object toEncodable(Object nonEncodable)}) =>
///
/// The default [reviver] (when not provided) is the identity function.
///
/// Shorthand for [json.decode]. Useful if a local variable shadows the global
/// Shorthand for `json.decode`. Useful if a local variable shadows the global
/// [json] constant.
dynamic jsonDecode(String source, {Object reviver(Object key, Object value)}) =>
dynamic jsonDecode(String source,
{Object? reviver(Object? key, Object? value)?}) =>
json.decode(source, reviver: reviver);
/// A [JsonCodec] encodes JSON objects to strings and decodes strings to
@ -102,9 +102,9 @@ dynamic jsonDecode(String source, {Object reviver(Object key, Object value)}) =>
///
/// var encoded = json.encode([1, 2, { "a": null }]);
/// var decoded = json.decode('["foo", { "bar": 499 }]');
class JsonCodec extends Codec<Object, String> {
final Function(Object key, Object value) _reviver;
final Function(dynamic) _toEncodable;
class JsonCodec extends Codec<Object?, String> {
final Object? Function(Object? key, Object? value)? _reviver;
final Object? Function(dynamic)? _toEncodable;
/// Creates a `JsonCodec` with the given reviver and encoding function.
///
@ -127,7 +127,9 @@ class JsonCodec extends Codec<Object, String> {
///
/// If [toEncodable] is omitted, it defaults to a function that returns the
/// result of calling `.toJson()` on the unencodable object.
const JsonCodec({reviver(Object key, Object value), toEncodable(var object)})
const JsonCodec(
{Object? reviver(Object? key, Object? value)?,
Object? toEncodable(dynamic object)?})
: _reviver = reviver,
_toEncodable = toEncodable;
@ -137,7 +139,7 @@ class JsonCodec extends Codec<Object, String> {
/// that has been parsed during decoding. The `key` argument is either the
/// integer list index for a list property, the string map key for object
/// properties, or `null` for the final result.
JsonCodec.withReviver(reviver(Object key, Object value))
JsonCodec.withReviver(dynamic reviver(Object? key, Object? value))
: this(reviver: reviver);
/// Parses the string and returns the resulting Json object.
@ -148,7 +150,8 @@ class JsonCodec extends Codec<Object, String> {
/// properties, or `null` for the final result.
///
/// The default [reviver] (when not provided) is the identity function.
dynamic decode(String source, {reviver(Object key, Object value)}) {
dynamic decode(String source,
{Object? reviver(Object? key, Object? value)?}) {
reviver ??= _reviver;
if (reviver == null) return decoder.convert(source);
return JsonDecoder(reviver).convert(source);
@ -163,7 +166,7 @@ class JsonCodec extends Codec<Object, String> {
///
/// If [toEncodable] is omitted, it defaults to a function that returns the
/// result of calling `.toJson()` on the unencodable object.
String encode(Object value, {toEncodable(object)}) {
String encode(Object? value, {Object? toEncodable(dynamic object)?}) {
toEncodable ??= _toEncodable;
if (toEncodable == null) return encoder.convert(value);
return JsonEncoder(toEncodable).convert(value);
@ -181,18 +184,18 @@ class JsonCodec extends Codec<Object, String> {
}
/// This class converts JSON objects to strings.
class JsonEncoder extends Converter<Object, String> {
class JsonEncoder extends Converter<Object?, String> {
/// The string used for indention.
///
/// When generating multi-line output, this string is inserted once at the
/// beginning of each indented line for each level of indentation.
///
/// If `null`, the output is encoded as a single line.
final String indent;
final String? indent;
/// Function called on non-encodable objects to return a replacement
/// encodable object that will be encoded in the orignal's place.
final Function(dynamic) _toEncodable;
final Object? Function(dynamic)? _toEncodable;
/// Creates a JSON encoder.
///
@ -204,7 +207,7 @@ class JsonEncoder extends Converter<Object, String> {
///
/// If [toEncodable] is omitted, it defaults to calling `.toJson()` on
/// the object.
const JsonEncoder([toEncodable(object)])
const JsonEncoder([Object? toEncodable(dynamic object)?])
: indent = null,
_toEncodable = toEncodable;
@ -224,7 +227,8 @@ class JsonEncoder extends Converter<Object, String> {
///
/// If [toEncodable] is omitted, it defaults to calling `.toJson()` on
/// the object.
const JsonEncoder.withIndent(this.indent, [toEncodable(object)])
const JsonEncoder.withIndent(this.indent,
[Object? toEncodable(dynamic object)?])
: _toEncodable = toEncodable;
/// Converts [object] to a JSON [String].
@ -253,7 +257,7 @@ class JsonEncoder extends Converter<Object, String> {
/// If an object is serialized more than once, [convert] may cache the text
/// for it. In other words, if the content of an object changes after it is
/// first serialized, the new values may not be reflected in the result.
String convert(Object object) =>
String convert(Object? object) =>
_JsonStringStringifier.stringify(object, _toEncodable, indent);
/// Starts a chunked conversion.
@ -263,7 +267,7 @@ class JsonEncoder extends Converter<Object, String> {
///
/// Returns a chunked-conversion sink that accepts at most one object. It is
/// an error to invoke `add` more than once on the returned sink.
ChunkedConversionSink<Object> startChunkedConversion(Sink<String> sink) {
ChunkedConversionSink<Object?> startChunkedConversion(Sink<String> sink) {
if (sink is _Utf8EncoderSink) {
return _JsonUtf8EncoderSink(
sink._sink,
@ -278,15 +282,15 @@ class JsonEncoder extends Converter<Object, String> {
}
// Override the base class's bind, to provide a better type.
Stream<String> bind(Stream<Object> stream) => super.bind(stream);
Stream<String> bind(Stream<Object?> stream) => super.bind(stream);
Converter<Object, T> fuse<T>(Converter<String, T> other) {
if (other is Utf8Encoder && T is List<int>) {
Converter<Object?, T> fuse<T>(Converter<String, T> other) {
if (other is Utf8Encoder) {
// The instance check guarantees that `T` is (a subtype of) List<int>,
// but the static type system doesn't know that, and so we cast.
// Cast through dynamic to keep the cast implicit for builds using
// unchecked implicit casts.
return JsonUtf8Encoder(indent, _toEncodable) as dynamic;
return JsonUtf8Encoder(indent, _toEncodable) as Converter<Object?, T>;
}
return super.fuse<T>(other);
}
@ -297,17 +301,17 @@ class JsonEncoder extends Converter<Object, String> {
/// This encoder works equivalently to first converting the object to
/// a JSON string, and then UTF-8 encoding the string, but without
/// creating an intermediate string.
class JsonUtf8Encoder extends Converter<Object, List<int>> {
class JsonUtf8Encoder extends Converter<Object?, List<int>> {
/// Default buffer size used by the JSON-to-UTF-8 encoder.
static const int _defaultBufferSize = 256;
@deprecated
static const int DEFAULT_BUFFER_SIZE = _defaultBufferSize;
/// Indentation used in pretty-print mode, `null` if not pretty.
final List<int> _indent;
final List<int>? _indent;
/// Function called with each un-encodable object encountered.
final Function(dynamic) _toEncodable;
final Object? Function(dynamic)? _toEncodable;
/// UTF-8 buffer size.
final int _bufferSize;
@ -336,12 +340,12 @@ class JsonUtf8Encoder extends Converter<Object, List<int>> {
/// If [toEncodable] is omitted, it defaults to calling `.toJson()` on the
/// object.
JsonUtf8Encoder(
[String indent, toEncodable(object), int bufferSize = _defaultBufferSize])
[String? indent, dynamic toEncodable(dynamic object)?, int? bufferSize])
: _indent = _utf8Encode(indent),
_toEncodable = toEncodable,
_bufferSize = bufferSize;
_bufferSize = bufferSize ?? _defaultBufferSize;
static List<int> _utf8Encode(String string) {
static List<int>? _utf8Encode(String? string) {
if (string == null) return null;
if (string.isEmpty) return Uint8List(0);
checkAscii:
@ -355,7 +359,7 @@ class JsonUtf8Encoder extends Converter<Object, List<int>> {
}
/// Convert [object] into UTF-8 encoded JSON.
List<int> convert(Object object) {
List<int> convert(Object? object) {
var bytes = <List<int>>[];
// The `stringify` function always converts into chunks.
// Collect the chunks into the `bytes` list, then combine them afterwards.
@ -391,7 +395,7 @@ class JsonUtf8Encoder extends Converter<Object, List<int>> {
///
/// The argument [sink] will receive byte lists in sizes depending on the
/// `bufferSize` passed to the constructor when creating this encoder.
ChunkedConversionSink<Object> startChunkedConversion(Sink<List<int>> sink) {
ChunkedConversionSink<Object?> startChunkedConversion(Sink<List<int>> sink) {
ByteConversionSink byteSink;
if (sink is ByteConversionSink) {
byteSink = sink;
@ -402,7 +406,7 @@ class JsonUtf8Encoder extends Converter<Object, List<int>> {
}
// Override the base class's bind, to provide a better type.
Stream<List<int>> bind(Stream<Object> stream) {
Stream<List<int>> bind(Stream<Object?> stream) {
return super.bind(stream);
}
}
@ -410,9 +414,9 @@ class JsonUtf8Encoder extends Converter<Object, List<int>> {
/// Implements the chunked conversion from object to its JSON representation.
///
/// The sink only accepts one value, but will produce output in a chunked way.
class _JsonEncoderSink extends ChunkedConversionSink<Object> {
final String _indent;
final Function(dynamic) _toEncodable;
class _JsonEncoderSink extends ChunkedConversionSink<Object?> {
final String? _indent;
final Object? Function(dynamic)? _toEncodable;
final StringConversionSink _sink;
bool _isDone = false;
@ -423,7 +427,7 @@ class _JsonEncoderSink extends ChunkedConversionSink<Object> {
/// It is an error to invoke this method more than once on any instance. While
/// this makes the input effectively non-chunked the output will be generated
/// in a chunked way.
void add(Object o) {
void add(Object? o) {
if (_isDone) {
throw StateError("Only one call to add allowed");
}
@ -437,11 +441,11 @@ class _JsonEncoderSink extends ChunkedConversionSink<Object> {
}
/// Sink returned when starting a chunked conversion from object to bytes.
class _JsonUtf8EncoderSink extends ChunkedConversionSink<Object> {
class _JsonUtf8EncoderSink extends ChunkedConversionSink<Object?> {
/// The byte sink receiveing the encoded chunks.
final ByteConversionSink _sink;
final List<int> _indent;
final Function(dynamic) _toEncodable;
final List<int>? _indent;
final Object? Function(dynamic)? _toEncodable;
final int _bufferSize;
bool _isDone = false;
_JsonUtf8EncoderSink(
@ -452,7 +456,7 @@ class _JsonUtf8EncoderSink extends ChunkedConversionSink<Object> {
_sink.addSlice(chunk, start, end, false);
}
void add(Object object) {
void add(Object? object) {
if (_isDone) {
throw StateError("Only one call to add allowed");
}
@ -471,13 +475,14 @@ class _JsonUtf8EncoderSink extends ChunkedConversionSink<Object> {
}
/// This class parses JSON strings and builds the corresponding objects.
class JsonDecoder extends Converter<String, Object> {
final Function(Object key, Object value) _reviver;
class JsonDecoder extends Converter<String, Object?> {
final Object? Function(Object? key, Object? value)? _reviver;
/// Constructs a new JsonDecoder.
///
/// The [reviver] may be `null`.
const JsonDecoder([reviver(Object key, Object value)]) : _reviver = reviver;
const JsonDecoder([Object? reviver(Object? key, Object? value)?])
: _reviver = reviver;
/// Converts the given JSON-string [input] to its corresponding object.
///
@ -497,14 +502,14 @@ class JsonDecoder extends Converter<String, Object> {
/// Starts a conversion from a chunked JSON string to its corresponding object.
///
/// The output [sink] receives exactly one decoded element through `add`.
external StringConversionSink startChunkedConversion(Sink<Object> sink);
external StringConversionSink startChunkedConversion(Sink<Object?> sink);
// Override the base class's bind, to provide a better type.
Stream<Object> bind(Stream<String> stream) => super.bind(stream);
Stream<Object?> bind(Stream<String> stream) => super.bind(stream);
}
// Internal optimized JSON parsing implementation.
external _parseJson(String source, reviver(key, value));
external _parseJson(String source, reviver(key, value)?);
// Implementation of encoder/stringifier.
@ -537,10 +542,10 @@ abstract class _JsonStringifier {
/// Function called for each un-encodable object encountered.
final Function(dynamic) _toEncodable;
_JsonStringifier(toEncodable(o))
_JsonStringifier(dynamic toEncodable(dynamic o)?)
: _toEncodable = toEncodable ?? _defaultToEncodable;
String get _partialResult;
String? get _partialResult;
/// Append a string to the JSON output.
void writeString(String characters);
@ -610,7 +615,7 @@ abstract class _JsonStringifier {
///
/// Records the object if it isn't already seen. Should have a matching call to
/// [_removeSeen] when the object is no longer being traversed.
void _checkCycle(object) {
void _checkCycle(Object? object) {
for (var i = 0; i < _seen.length; i++) {
if (identical(object, _seen[i])) {
throw JsonCyclicError(object);
@ -623,7 +628,7 @@ abstract class _JsonStringifier {
///
/// Should be called in the opposite order of the matching [_checkCycle]
/// calls.
void _removeSeen(object) {
void _removeSeen(Object? object) {
assert(_seen.isNotEmpty);
assert(identical(_seen.last, object));
_seen.removeLast();
@ -633,7 +638,7 @@ abstract class _JsonStringifier {
///
/// If [object] isn't directly encodable, the [_toEncodable] function gets one
/// chance to return a replacement which is encodable.
void writeObject(object) {
void writeObject(Object? object) {
// Tries stringifying object directly. If it's not a simple value, List or
// Map, call toJson() to get a custom representation and try serializing
// that.
@ -655,7 +660,7 @@ abstract class _JsonStringifier {
///
/// Returns true if the value is one of these types, and false if not.
/// If a value is both a [List] and a [Map], it's serialized as a [List].
bool writeJsonValue(object) {
bool writeJsonValue(Object? object) {
if (object is num) {
if (!object.isFinite) return false;
writeNumber(object);
@ -691,7 +696,7 @@ abstract class _JsonStringifier {
}
/// Serialize a [List].
void writeList(List list) {
void writeList(List<Object?> list) {
writeString('[');
if (list.isNotEmpty) {
writeObject(list[0]);
@ -704,12 +709,12 @@ abstract class _JsonStringifier {
}
/// Serialize a [Map].
bool writeMap(Map map) {
bool writeMap(Map<Object?, Object?> map) {
if (map.isEmpty) {
writeString("{}");
return true;
}
var keyValueList = List(map.length * 2);
var keyValueList = List<Object?>(map.length * 2);
var i = 0;
var allStringKeys = true;
map.forEach((key, value) {
@ -725,7 +730,7 @@ abstract class _JsonStringifier {
for (var i = 0; i < keyValueList.length; i += 2) {
writeString(separator);
separator = ',"';
writeStringContent(keyValueList[i]);
writeStringContent(keyValueList[i] as String);
writeString('":');
writeObject(keyValueList[i + 1]);
}
@ -744,7 +749,7 @@ abstract class _JsonPrettyPrintMixin implements _JsonStringifier {
/// Add [indentLevel] indentations to the JSON output.
void writeIndentation(int indentLevel);
void writeList(List list) {
void writeList(List<Object?> list) {
if (list.isEmpty) {
writeString('[]');
} else {
@ -764,12 +769,12 @@ abstract class _JsonPrettyPrintMixin implements _JsonStringifier {
}
}
bool writeMap(Map map) {
bool writeMap(Map<Object?, Object?> map) {
if (map.isEmpty) {
writeString("{}");
return true;
}
var keyValueList = List(map.length * 2);
var keyValueList = List<Object?>(map.length * 2);
var i = 0;
var allStringKeys = true;
map.forEach((key, value) {
@ -788,7 +793,7 @@ abstract class _JsonPrettyPrintMixin implements _JsonStringifier {
separator = ",\n";
writeIndentation(_indentLevel);
writeString('"');
writeStringContent(keyValueList[i]);
writeStringContent(keyValueList[i] as String);
writeString('": ');
writeObject(keyValueList[i + 1]);
}
@ -804,7 +809,8 @@ abstract class _JsonPrettyPrintMixin implements _JsonStringifier {
class _JsonStringStringifier extends _JsonStringifier {
final StringSink _sink;
_JsonStringStringifier(this._sink, dynamic Function(dynamic) _toEncodable)
_JsonStringStringifier(
this._sink, dynamic Function(dynamic object)? _toEncodable)
: super(_toEncodable);
/// Convert object to a string.
@ -816,7 +822,8 @@ class _JsonStringStringifier extends _JsonStringifier {
/// with newlines and indentation. The `indent` string is added as indentation
/// for each indentation level. It should only contain valid JSON whitespace
/// characters (space, tab, carriage return or line feed).
static String stringify(object, toEncodable(o), String indent) {
static String stringify(
Object? object, dynamic toEncodable(dynamic object)?, String? indent) {
var output = StringBuffer();
printOn(object, output, toEncodable, indent);
return output.toString();
@ -825,8 +832,8 @@ class _JsonStringStringifier extends _JsonStringifier {
/// Convert object to a string, and write the result to the [output] sink.
///
/// The result is written piecemally to the sink.
static void printOn(
object, StringSink output, toEncodable(o), String indent) {
static void printOn(Object? object, StringSink output,
dynamic toEncodable(dynamic o)?, String? indent) {
_JsonStringifier stringifier;
if (indent == null) {
stringifier = _JsonStringStringifier(output, toEncodable);
@ -836,7 +843,7 @@ class _JsonStringStringifier extends _JsonStringifier {
stringifier.writeObject(object);
}
String get _partialResult => _sink is StringBuffer ? _sink.toString() : null;
String? get _partialResult => _sink is StringBuffer ? _sink.toString() : null;
void writeNumber(num number) {
_sink.write(number.toString());
@ -859,7 +866,8 @@ class _JsonStringStringifierPretty extends _JsonStringStringifier
with _JsonPrettyPrintMixin {
final String _indent;
_JsonStringStringifierPretty(StringSink sink, toEncodable(o), this._indent)
_JsonStringStringifierPretty(
StringSink sink, dynamic toEncodable(dynamic o)?, this._indent)
: super(sink, toEncodable);
void writeIndentation(int count) {
@ -877,7 +885,8 @@ class _JsonUtf8Stringifier extends _JsonStringifier {
Uint8List buffer;
int index = 0;
_JsonUtf8Stringifier(toEncodable(o), this.bufferSize, this.addChunk)
_JsonUtf8Stringifier(
dynamic toEncodable(dynamic o)?, this.bufferSize, this.addChunk)
: buffer = Uint8List(bufferSize),
super(toEncodable);
@ -890,8 +899,12 @@ class _JsonUtf8Stringifier extends _JsonStringifier {
///
/// If [indent] is non-`null`, the result will be "pretty-printed" with extra
/// newlines and indentation, using [indent] as the indentation.
static void stringify(Object object, List<int> indent, toEncodable(o),
int bufferSize, void addChunk(Uint8List chunk, int start, int end)) {
static void stringify(
Object? object,
List<int>? indent,
dynamic toEncodable(dynamic o)?,
int bufferSize,
void addChunk(Uint8List chunk, int start, int end)) {
_JsonUtf8Stringifier stringifier;
if (indent != null) {
stringifier =
@ -909,11 +922,11 @@ class _JsonUtf8Stringifier extends _JsonStringifier {
if (index > 0) {
addChunk(buffer, 0, index);
}
buffer = null;
buffer = Uint8List(0);
index = 0;
}
String get _partialResult => null;
String? get _partialResult => null;
void writeNumber(num number) {
writeAsciiString(number.toString());
@ -937,7 +950,7 @@ class _JsonUtf8Stringifier extends _JsonStringifier {
void writeStringSlice(String string, int start, int end) {
// TODO(lrn): Optimize by copying directly into buffer instead of going
// through writeCharCode/writeByte. Assumption is the most characters
// in starings are plain ASCII.
// in strings are plain ASCII.
for (var i = start; i < end; i++) {
var char = string.codeUnitAt(i);
if (char <= 0x7f) {
@ -1005,8 +1018,8 @@ class _JsonUtf8Stringifier extends _JsonStringifier {
class _JsonUtf8StringifierPretty extends _JsonUtf8Stringifier
with _JsonPrettyPrintMixin {
final List<int> indent;
_JsonUtf8StringifierPretty(toEncodable(o), this.indent, int bufferSize,
void addChunk(Uint8List buffer, int start, int end))
_JsonUtf8StringifierPretty(dynamic toEncodable(dynamic o)?, this.indent,
int bufferSize, void addChunk(Uint8List buffer, int start, int end))
: super(toEncodable, bufferSize, addChunk);
void writeIndentation(int count) {

View file

@ -2,8 +2,6 @@
// 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.
// @dart = 2.5
part of dart.convert;
/// An instance of the default implementation of the [Latin1Codec].
@ -49,9 +47,8 @@ class Latin1Codec extends Encoding {
///
/// If [allowInvalid] is not provided, it defaults to the value used to create
/// this [Latin1Codec].
String decode(List<int> bytes, {bool allowInvalid}) {
allowInvalid ??= _allowInvalid;
if (allowInvalid) {
String decode(List<int> bytes, {bool? allowInvalid}) {
if (allowInvalid ?? _allowInvalid) {
return const Latin1Decoder(allowInvalid: true).convert(bytes);
} else {
return const Latin1Decoder(allowInvalid: false).convert(bytes);
@ -102,11 +99,11 @@ class Latin1Decoder extends _UnicodeSubsetDecoder {
}
class _Latin1DecoderSink extends ByteConversionSinkBase {
StringConversionSink _sink;
StringConversionSink? _sink;
_Latin1DecoderSink(this._sink);
void close() {
_sink.close();
_sink!.close();
_sink = null;
}
@ -119,12 +116,12 @@ class _Latin1DecoderSink extends ByteConversionSinkBase {
// _sink.addSlice(source, start, end, isLast).
// The code below is an moderately stupid workaround until a real
// solution can be made.
_sink.add(String.fromCharCodes(source, start, end));
_sink!.add(String.fromCharCodes(source, start, end));
if (isLast) close();
}
void addSlice(List<int> source, int start, int end, bool isLast) {
end = RangeError.checkValidRange(start, end, source.length);
RangeError.checkValidRange(start, end, source.length);
if (start == end) return;
if (source is! Uint8List) {
// List may contain value outside of the 0..255 range. If so, throw.

View file

@ -2,8 +2,6 @@
// 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.
// @dart = 2.5
part of dart.convert;
// Character constants.
@ -27,8 +25,12 @@ class LineSplitter extends StreamTransformerBase<String, String> {
/// `lines.substring(start, end)`. The [start] and [end] values must
/// specify a valid sub-range of [lines]
/// (`0 <= start <= end <= lines.length`).
static Iterable<String> split(String lines, [int start = 0, int end]) sync* {
static Iterable<String> split(String lines, [int start = 0, int? end]) sync* {
end = RangeError.checkValidRange(start, end, lines.length);
// TODO(38725): Remove workaround when assignment promotion is implemented
if (end == null) {
throw RangeError("Invalid range");
}
var sliceStart = start;
var char = 0;
for (var i = start; i < end; i++) {
@ -92,7 +94,7 @@ class _LineSplitterSink extends StringConversionSinkBase {
///
/// If the previous slice ended in a line without a line terminator,
/// then the next slice may continue the line.
String _carry;
String? _carry;
/// Whether to skip a leading LF character from the next slice.
///
@ -112,9 +114,10 @@ class _LineSplitterSink extends StringConversionSinkBase {
if (isLast) close();
return;
}
if (_carry != null) {
String? carry = _carry;
if (carry != null) {
assert(!_skipLeadingLF);
chunk = _carry + chunk.substring(start, end);
chunk = carry + chunk.substring(start, end);
start = 0;
end = chunk.length;
_carry = null;
@ -130,7 +133,7 @@ class _LineSplitterSink extends StringConversionSinkBase {
void close() {
if (_carry != null) {
_sink.add(_carry);
_sink.add(_carry!);
_carry = null;
}
_sink.close();
@ -168,7 +171,7 @@ class _LineSplitterEventSink extends _LineSplitterSink
: _eventSink = eventSink,
super(StringConversionSink.from(eventSink));
void addError(Object o, [StackTrace stackTrace]) {
void addError(Object o, [StackTrace? stackTrace]) {
_eventSink.addError(o, stackTrace);
}
}

View file

@ -2,8 +2,6 @@
// 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.
// @dart = 2.5
part of dart.convert;
/// This class provides an interface for converters to
@ -101,8 +99,8 @@ class _ClosableStringSink implements ClosableStringSink {
class _StringConversionSinkAsStringSinkAdapter implements ClosableStringSink {
static const _MIN_STRING_SIZE = 16;
StringBuffer _buffer;
StringConversionSink _chunkedSink;
final StringBuffer _buffer;
final StringConversionSink _chunkedSink;
_StringConversionSinkAsStringSinkAdapter(this._chunkedSink)
: _buffer = StringBuffer();
@ -117,12 +115,12 @@ class _StringConversionSinkAsStringSinkAdapter implements ClosableStringSink {
if (_buffer.length > _MIN_STRING_SIZE) _flush();
}
void write(Object o) {
void write(Object? o) {
if (_buffer.isNotEmpty) _flush();
_chunkedSink.add(o.toString());
}
void writeln([Object o = ""]) {
void writeln([Object? o = ""]) {
_buffer.writeln(o);
if (_buffer.length > _MIN_STRING_SIZE) _flush();
}
@ -177,10 +175,11 @@ abstract class StringConversionSinkMixin implements StringConversionSink {
/// This class is a [StringConversionSink] that wraps a [StringSink].
class _StringSinkConversionSink<TStringSink extends StringSink>
extends StringConversionSinkBase {
TStringSink _stringSink;
final TStringSink _stringSink;
_StringSinkConversionSink(this._stringSink);
void close() {}
void addSlice(String str, int start, int end, bool isLast) {
if (start != 0 || end != str.length) {
for (var i = start; i < end; i++) {
@ -211,6 +210,7 @@ class _StringSinkConversionSink<TStringSink extends StringSink>
/// This class can be used to terminate a chunked conversion.
class _StringCallbackSink extends _StringSinkConversionSink<StringBuffer> {
final void Function(String) _callback;
_StringCallbackSink(this._callback) : super(StringBuffer());
void close() {
@ -253,16 +253,18 @@ class _StringAdapterSink extends StringConversionSinkBase {
}
/// Decodes UTF-8 code units and stores them in a [StringSink].
///
/// The `Sink` provided is closed when this sink is closed.
class _Utf8StringSinkAdapter extends ByteConversionSink {
final _Utf8Decoder _decoder;
final Sink _sink;
final Sink<Object?> _sink;
_Utf8StringSinkAdapter(this._sink, StringSink stringSink, bool allowMalformed)
: _decoder = _Utf8Decoder(stringSink, allowMalformed);
void close() {
_decoder.close();
if (_sink != null) _sink.close();
_sink.close();
}
void add(List<int> chunk) {

View file

@ -2,14 +2,12 @@
// 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.
// @dart = 2.5
part of dart.convert;
/// The Unicode Replacement character `U+FFFD` (<EFBFBD>).
const int unicodeReplacementCharacterRune = 0xFFFD;
/// The Unicode Byte Order Marker (BOM) character `U+FEFF`.
/// The Unicode Byte Order Marker (f) character `U+FEFF`.
const int unicodeBomCharacterRune = 0xFEFF;
/// An instance of the default implementation of the [Utf8Codec].
@ -56,9 +54,9 @@ class Utf8Codec extends Encoding {
///
/// If [allowMalformed] is not given, it defaults to the `allowMalformed` that
/// was used to instantiate `this`.
String decode(List<int> codeUnits, {bool allowMalformed}) {
allowMalformed ??= _allowMalformed;
return Utf8Decoder(allowMalformed: allowMalformed).convert(codeUnits);
String decode(List<int> codeUnits, {bool? allowMalformed}) {
return Utf8Decoder(allowMalformed: allowMalformed ?? _allowMalformed)
.convert(codeUnits);
}
Utf8Encoder get encoder => const Utf8Encoder();
@ -77,9 +75,13 @@ class Utf8Encoder extends Converter<String, List<int>> {
///
/// If [start] and [end] are provided, only the substring
/// `string.substring(start, end)` is converted.
Uint8List convert(String string, [int start = 0, int end]) {
Uint8List convert(String string, [int start = 0, int? end]) {
var stringLength = string.length;
end = RangeError.checkValidRange(start, end, stringLength);
// TODO(38725): Remove workaround when assignment promotion is implemented
if (end == null) {
throw RangeError("Invalid range");
}
var length = end - start;
if (length == 0) return Uint8List(0);
// Create a new encoder with a length that is guaranteed to be big enough.
@ -298,7 +300,7 @@ class Utf8Decoder extends Converter<List<int>, String> {
///
/// If the [codeUnits] start with the encoding of a
/// [unicodeBomCharacterRune], that character is discarded.
String convert(List<int> codeUnits, [int start = 0, int end]) {
String convert(List<int> codeUnits, [int start = 0, int? end]) {
// Allow the implementation to intercept and specialize based on the type
// of codeUnits.
var result = _convertIntercepted(_allowMalformed, codeUnits, start, end);
@ -308,6 +310,10 @@ class Utf8Decoder extends Converter<List<int>, String> {
var length = codeUnits.length;
end = RangeError.checkValidRange(start, end, length);
// TODO(38725): Remove workaround when assignment promotion is implemented
if (end == null) {
throw RangeError("Invalid range");
}
// Fast case for ASCII strings avoids StringBuffer/_Utf8Decoder.
int oneBytes = _scanOneByteCharacters(codeUnits, start, end);
@ -321,9 +327,9 @@ class Utf8Decoder extends Converter<List<int>, String> {
}
buffer = StringBuffer(firstPart);
isFirstCharacter = false;
} else {
buffer = StringBuffer();
}
buffer ??= StringBuffer();
var decoder = _Utf8Decoder(buffer, _allowMalformed);
decoder._isFirstCharacter = isFirstCharacter;
decoder.convert(codeUnits, start, end);
@ -350,8 +356,8 @@ class Utf8Decoder extends Converter<List<int>, String> {
external Converter<List<int>, T> fuse<T>(Converter<String, T> next);
external static String _convertIntercepted(
bool allowMalformed, List<int> codeUnits, int start, int end);
external static String? _convertIntercepted(
bool allowMalformed, List<int> codeUnits, int start, int? end);
}
// UTF-8 constants.
@ -409,7 +415,7 @@ class _Utf8Decoder {
///
/// The [source] and [offset] of the current position may be provided,
/// and are included in the exception if one is thrown.
void flush([List<int> source, int offset]) {
void flush([List<int>? source, int? offset]) {
if (hasPartialInput) {
if (!_allowMalformed) {
throw FormatException(