| Index: tool/input_sdk/lib/convert/json.dart
|
| diff --git a/tool/input_sdk/lib/convert/json.dart b/tool/input_sdk/lib/convert/json.dart
|
| index 083d26e3c35e0f824872a38c0c1dd635d71ad45b..e80a9868fab20b1c881dc54f198c90467ac8c569 100644
|
| --- a/tool/input_sdk/lib/convert/json.dart
|
| +++ b/tool/input_sdk/lib/convert/json.dart
|
| @@ -9,10 +9,10 @@ part of dart.convert;
|
| *
|
| * The [unsupportedObject] field holds that object that failed to be serialized.
|
| *
|
| - * If an object isn't directly serializable, the serializer calls the 'toJson'
|
| + * If an object isn't directly serializable, the serializer calls the `toJson`
|
| * method on the object. If that call fails, the error will be stored in the
|
| * [cause] field. If the call returns an object that isn't directly
|
| - * serializable, the [cause] is be null.
|
| + * serializable, the [cause] is null.
|
| */
|
| class JsonUnsupportedObjectError extends Error {
|
| /** The object that could not be serialized. */
|
| @@ -65,6 +65,11 @@ typedef _ToEncodable(var o);
|
| /**
|
| * A [JsonCodec] encodes JSON objects to strings and decodes strings to
|
| * JSON objects.
|
| + *
|
| + * Examples:
|
| + *
|
| + * var encoded = JSON.encode([1, 2, { "a": null }]);
|
| + * var decoded = JSON.decode('["foo", { "bar": 499 }]');
|
| */
|
| class JsonCodec extends Codec<Object, String> {
|
| final _Reviver _reviver;
|
| @@ -73,24 +78,22 @@ class JsonCodec extends Codec<Object, String> {
|
| /**
|
| * Creates a `JsonCodec` with the given reviver and encoding function.
|
| *
|
| - * The [reviver] function is called during decoding. It is invoked
|
| - * once for each object or list property that has been parsed.
|
| - * 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.
|
| + * The [reviver] function is called during decoding. It is invoked once for
|
| + * each object or list property that has been parsed.
|
| + * 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.
|
| *
|
| * If [reviver] is omitted, it defaults to returning the value argument.
|
| *
|
| * The [toEncodable] function is used during encoding. It is invoked for
|
| - * values that are not directly encodable to a JSON1toE
|
| - * string (a value that is not a number, boolean, string, null, list or a map
|
| - * with string keys). The function must return an object that is directly
|
| - * encodable. The elements of a returned list and values of a returned map
|
| - * do not need be directly encodable, and if they aren't, `toEncodable` will
|
| - * be used on them as well.
|
| - * Please notice that it is possible to cause an infinite recursive
|
| - * regress in this way, by effectively creating an infinite data structure
|
| - * through repeated call to `toEncodable`.
|
| + * values that are not directly encodable to a string (a value that is not a
|
| + * number, boolean, string, null, list or a map with string keys). The
|
| + * function must return an object that is directly encodable. The elements of
|
| + * a returned list and values of a returned map do not need to be directly
|
| + * encodable, and if they aren't, `toEncodable` will be used on them as well.
|
| + * Please notice that it is possible to cause an infinite recursive regress
|
| + * in this way, by effectively creating an infinite data structure through
|
| + * repeated call to `toEncodable`.
|
| *
|
| * If [toEncodable] is omitted, it defaults to a function that returns the
|
| * result of calling `.toJson()` on the unencodable object.
|
| @@ -156,7 +159,7 @@ class JsonCodec extends Codec<Object, String> {
|
| /**
|
| * This class converts JSON objects to strings.
|
| */
|
| -class JsonEncoder extends Converter<Object, String> {
|
| +class JsonEncoder extends ChunkedConverter<Object, String, Object, String> {
|
| /**
|
| * The string used for indention.
|
| *
|
| @@ -215,14 +218,14 @@ class JsonEncoder extends Converter<Object, String> {
|
| * Converts [object] to a JSON [String].
|
| *
|
| * Directly serializable values are [num], [String], [bool], and [Null], as
|
| - * well as some [List] and [Map] values.
|
| - * For [List], the elements must all be serializable.
|
| - * For [Map], the keys must be [String] and the values must be serializable.
|
| + * well as some [List] and [Map] values. For [List], the elements must all be
|
| + * serializable. For [Map], the keys must be [String] and the values must be
|
| + * serializable.
|
| *
|
| - * If a value is any other type is attempted serialized, the conversion
|
| - * function provided in the constructor is invoked with the object as argument
|
| - * and the result, which must be a directly serializable value,
|
| - * is serialized instead of the original value.
|
| + * If a value of any other type is attempted to be serialized, the
|
| + * `toEncodable` function provided in the constructor is called with the value
|
| + * as argument. The result, which must be a directly serializable value, is
|
| + * serialized instead of the original value.
|
| *
|
| * If the conversion throws, or returns a value that is not directly
|
| * serializable, a [JsonUnsupportedObjectError] exception is thrown.
|
| @@ -262,7 +265,7 @@ class JsonEncoder extends Converter<Object, String> {
|
| return new _JsonEncoderSink(sink, _toEncodable, indent);
|
| }
|
|
|
| - // Override the base-classes bind, to provide a better type.
|
| + // Override the base class's bind, to provide a better type.
|
| Stream<String> bind(Stream<Object> stream) => super.bind(stream);
|
|
|
| Converter<Object, dynamic> fuse(Converter<String, dynamic> other) {
|
| @@ -280,7 +283,8 @@ class JsonEncoder extends Converter<Object, String> {
|
| * 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
|
| + ChunkedConverter<Object, List<int>, Object, List<int>> {
|
| /** Default buffer size used by the JSON-to-UTF-8 encoder. */
|
| static const int DEFAULT_BUFFER_SIZE = 256;
|
| /** Indentation used in pretty-print mode, `null` if not pretty. */
|
| @@ -299,20 +303,21 @@ class JsonUtf8Encoder extends Converter<Object, List<int>> {
|
| * If `indent` contains characters that are not valid JSON whitespace
|
| * characters, the result will not be valid JSON. JSON whitespace characters
|
| * are space (U+0020), tab (U+0009), line feed (U+000a) and carriage return
|
| - * (U+000d) (ECMA 404).
|
| + * (U+000d) ([ECMA
|
| + * 404](http://www.ecma-international.org/publications/standards/Ecma-404.htm)).
|
| *
|
| * The [bufferSize] is the size of the internal buffers used to collect
|
| * UTF-8 code units.
|
| * If using [startChunkedConversion], it will be the size of the chunks.
|
| *
|
| - * The JSON encoder handles numbers, strings, booleans, null, lists and
|
| - * maps directly.
|
| + * The JSON encoder handles numbers, strings, booleans, null, lists and maps
|
| + * directly.
|
| *
|
| - * Any other object is attempted converted by [toEncodable] to an
|
| - * object that is of one of the convertible types.
|
| + * Any other object is attempted converted by [toEncodable] to an object that
|
| + * is of one of the convertible types.
|
| *
|
| - * If [toEncodable] is omitted, it defaults to calling `.toJson()` on
|
| - * the object.
|
| + * If [toEncodable] is omitted, it defaults to calling `.toJson()` on the
|
| + * object.
|
| */
|
| JsonUtf8Encoder([String indent,
|
| toEncodable(Object object),
|
| @@ -386,7 +391,7 @@ class JsonUtf8Encoder extends Converter<Object, List<int>> {
|
| _indent, _bufferSize);
|
| }
|
|
|
| - // Override the base-classes bind, to provide a better type.
|
| + // Override the base class's bind, to provide a better type.
|
| Stream<List<int>> bind(Stream<Object> stream) {
|
| return super.bind(stream);
|
| }
|
| @@ -469,7 +474,7 @@ class _JsonUtf8EncoderSink extends ChunkedConversionSink<Object> {
|
| /**
|
| * This class parses JSON strings and builds the corresponding objects.
|
| */
|
| -class JsonDecoder extends Converter<String, Object> {
|
| +class JsonDecoder extends ChunkedConverter<String, Object, String, Object> {
|
| final _Reviver _reviver;
|
| /**
|
| * Constructs a new JsonDecoder.
|
| @@ -482,8 +487,8 @@ class JsonDecoder extends Converter<String, Object> {
|
| * Converts the given JSON-string [input] to its corresponding object.
|
| *
|
| * Parsed JSON values are of the types [num], [String], [bool], [Null],
|
| - * [List]s of parsed JSON values or [Map]s from [String] to parsed
|
| - * JSON values.
|
| + * [List]s of parsed JSON values or [Map]s from [String] to parsed JSON
|
| + * values.
|
| *
|
| * If `this` was initialized with a reviver, then the parsing operation
|
| * invokes the reviver on every object or list property that has been parsed.
|
| @@ -496,14 +501,13 @@ class JsonDecoder extends Converter<String, Object> {
|
| dynamic convert(String input) => _parseJson(input, _reviver);
|
|
|
| /**
|
| - * Starts a conversion from a chunked JSON string to its corresponding
|
| - * 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);
|
|
|
| - // Override the base-classes bind, to provide a better type.
|
| + // Override the base class's bind, to provide a better type.
|
| Stream<Object> bind(Stream<String> stream) => super.bind(stream);
|
| }
|
|
|
| @@ -613,9 +617,8 @@ abstract class _JsonStringifier {
|
| /**
|
| * Check if an encountered object is already being traversed.
|
| *
|
| - * Records the object if it isn't already seen.
|
| - * Should have a matching call to [_removeSeen] when the object
|
| - * is no longer being traversed.
|
| + * 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) {
|
| for (int i = 0; i < _seen.length; i++) {
|
| @@ -627,7 +630,7 @@ abstract class _JsonStringifier {
|
| }
|
|
|
| /**
|
| - * Removes object from the list of currently traversed objects.
|
| + * Remove [object] from the list of currently traversed objects.
|
| *
|
| * Should be called in the opposite order of the matching [_checkCycle]
|
| * calls.
|
| @@ -639,10 +642,10 @@ abstract class _JsonStringifier {
|
| }
|
|
|
| /**
|
| - * Writes an object.
|
| + * Write an object.
|
| *
|
| - * If the object isn't directly encodable, the [_toEncodable] function
|
| - * gets one chance to return a replacement which is encodable.
|
| + * If [object] isn't directly encodable, the [_toEncodable] function gets one
|
| + * chance to return a replacement which is encodable.
|
| */
|
| void writeObject(object) {
|
| // Tries stringifying object directly. If it's not a simple value, List or
|
| @@ -662,7 +665,7 @@ abstract class _JsonStringifier {
|
| }
|
|
|
| /**
|
| - * Serializes a [num], [String], [bool], [Null], [List] or [Map] value.
|
| + * Serialize a [num], [String], [bool], [Null], [List] or [Map] value.
|
| *
|
| * 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].
|
| @@ -693,15 +696,16 @@ abstract class _JsonStringifier {
|
| return true;
|
| } else if (object is Map) {
|
| _checkCycle(object);
|
| - writeMap(object);
|
| + // writeMap can fail if keys are not all strings.
|
| + var success = writeMap(object);
|
| _removeSeen(object);
|
| - return true;
|
| + return success;
|
| } else {
|
| return false;
|
| }
|
| }
|
|
|
| - /** Serializes a [List]. */
|
| + /** Serialize a [List]. */
|
| void writeList(List list) {
|
| writeString('[');
|
| if (list.length > 0) {
|
| @@ -714,18 +718,34 @@ abstract class _JsonStringifier {
|
| writeString(']');
|
| }
|
|
|
| - /** Serializes a [Map]. */
|
| - void writeMap(Map<String, Object> map) {
|
| + /** Serialize a [Map]. */
|
| + bool writeMap(Map<String, Object> map) {
|
| + if (map.isEmpty) {
|
| + writeString("{}");
|
| + return true;
|
| + }
|
| + List keyValueList = new List(map.length * 2);
|
| + int i = 0;
|
| + bool allStringKeys = true;
|
| + map.forEach((key, value) {
|
| + if (key is! String) {
|
| + allStringKeys = false;
|
| + }
|
| + keyValueList[i++] = key;
|
| + keyValueList[i++] = value;
|
| + });
|
| + if (!allStringKeys) return false;
|
| writeString('{');
|
| String separator = '"';
|
| - map.forEach((String key, value) {
|
| + for (int i = 0; i < keyValueList.length; i += 2) {
|
| writeString(separator);
|
| separator = ',"';
|
| - writeStringContent(key);
|
| + writeStringContent(keyValueList[i]);
|
| writeString('":');
|
| - writeObject(value);
|
| - });
|
| + writeObject(keyValueList[i + 1]);
|
| + }
|
| writeString('}');
|
| + return true;
|
| }
|
| }
|
|
|
| @@ -763,29 +783,39 @@ abstract class _JsonPrettyPrintMixin implements _JsonStringifier {
|
| }
|
| }
|
|
|
| - void writeMap(Map map) {
|
| + bool writeMap(Map map) {
|
| if (map.isEmpty) {
|
| - writeString('{}');
|
| - } else {
|
| - writeString('{\n');
|
| - _indentLevel++;
|
| - bool first = true;
|
| - map.forEach((String key, Object value) {
|
| - if (!first) {
|
| - writeString(",\n");
|
| - }
|
| - writeIndentation(_indentLevel);
|
| - writeString('"');
|
| - writeStringContent(key);
|
| - writeString('": ');
|
| - writeObject(value);
|
| - first = false;
|
| - });
|
| - writeString('\n');
|
| - _indentLevel--;
|
| + writeString("{}");
|
| + return true;
|
| + }
|
| + List keyValueList = new List(map.length * 2);
|
| + int i = 0;
|
| + bool allStringKeys = true;
|
| + map.forEach((key, value) {
|
| + if (key is! String) {
|
| + allStringKeys = false;
|
| + }
|
| + keyValueList[i++] = key;
|
| + keyValueList[i++] = value;
|
| + });
|
| + if (!allStringKeys) return false;
|
| + writeString('{\n');
|
| + _indentLevel++;
|
| + String separator = "";
|
| + for (int i = 0; i < keyValueList.length; i += 2) {
|
| + writeString(separator);
|
| + separator = ",\n";
|
| writeIndentation(_indentLevel);
|
| - writeString('}');
|
| + writeString('"');
|
| + writeStringContent(keyValueList[i]);
|
| + writeString('": ');
|
| + writeObject(keyValueList[i + 1]);
|
| }
|
| + writeString('\n');
|
| + _indentLevel--;
|
| + writeIndentation(_indentLevel);
|
| + writeString('}');
|
| + return true;
|
| }
|
| }
|
|
|
| @@ -873,19 +903,18 @@ class _JsonUtf8Stringifier extends _JsonStringifier {
|
| _JsonUtf8Stringifier(toEncodable, int bufferSize, this.addChunk)
|
| : this.bufferSize = bufferSize,
|
| buffer = new Uint8List(bufferSize),
|
| - super(toEncodable)
|
| - ;
|
| + super(toEncodable);
|
|
|
| /**
|
| * Convert [object] to UTF-8 encoded JSON.
|
| *
|
| * Calls [addChunk] with slices of UTF-8 code units.
|
| * These will typically have size [bufferSize], but may be shorter.
|
| - * The buffers are not reused, so the [addChunk] call may keep and reuse
|
| - * the chunks.
|
| + * The buffers are not reused, so the [addChunk] call may keep and reuse the
|
| + * chunks.
|
| *
|
| - * If [indent] is non-`null`, the result will be "pretty-printed" with
|
| - * extra newlines and indentation, using [indent] as the indentation.
|
| + * 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,
|
|
|