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, |