Index: runtime/lib/convert_patch.dart |
diff --git a/pkg/json/lib/json.dart b/runtime/lib/convert_patch.dart |
similarity index 65% |
copy from pkg/json/lib/json.dart |
copy to runtime/lib/convert_patch.dart |
index 58b6c796d6135abbc02ed18dda51d1c6ed7a2e98..7a6b6673dd0cb013afd77e046dcc1d982a5d2914 100644 |
--- a/pkg/json/lib/json.dart |
+++ b/runtime/lib/convert_patch.dart |
@@ -1,131 +1,25 @@ |
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
// 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. |
-/** |
- * Utilities for encoding and decoding JSON (JavaScript Object Notation) data. |
- */ |
- |
-library json; |
- |
-// JSON parsing and serialization. |
- |
-/** |
- * Error thrown by JSON serialization if an object cannot be serialized. |
- * |
- * The [unsupportedObject] field holds that object that failed to be serialized. |
- * |
- * 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] will be null. |
- */ |
-class JsonUnsupportedObjectError extends Error { |
- /** The object that could not be serialized. */ |
- final unsupportedObject; |
- /** The exception thrown by object's [:toJson:] method, if any. */ |
- final cause; |
- |
- JsonUnsupportedObjectError(this.unsupportedObject, { this.cause }); |
- |
- String toString() { |
- if (cause != null) { |
- return "Calling toJson method on object failed."; |
- } else { |
- return "Object toJson method returns non-serializable value."; |
- } |
- } |
-} |
- |
- |
-/** |
- * Reports that an object could not be stringified due to cyclic references. |
- * |
- * An object that references itself cannot be serialized by [stringify]. |
- * 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); |
- String toString() => "Cyclic error in JSON stringify"; |
-} |
+// JSON conversion. |
- |
-/** |
- * Parses [json] and build the corresponding parsed JSON value. |
- * |
- * 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. |
- * |
- * The optional [reviver] function, if provided, is called once for each |
- * object or list property parsed. The arguments are the property name |
- * ([String]) or list index ([int]), and the value is the parsed value. |
- * The return value of the reviver will be used as the value of that property |
- * instead the parsed value. |
- * |
- * Throws [FormatException] if the input is not valid JSON text. |
- */ |
-parse(String json, [reviver(var key, var value)]) { |
- BuildJsonListener listener; |
+patch _parseJson(String json, reviver(var key, var value)) { |
+ _BuildJsonListener listener; |
if (reviver == null) { |
- listener = new BuildJsonListener(); |
+ listener = new _BuildJsonListener(); |
} else { |
- listener = new ReviverJsonListener(reviver); |
+ listener = new _ReviverJsonListener(reviver); |
} |
- new JsonParser(json, listener).parse(); |
+ new _JsonParser(json, listener).parse(); |
return listener.result; |
} |
-/** |
- * Serializes [object] into 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. |
- * |
- * If a value is any other type is attempted serialized, a "toJson()" method |
- * is invoked on the object and the result, which must be a directly |
- * serializable value, is serialized instead of the original value. |
- * |
- * If the object does not support this method, throws, or returns a |
- * value that is not directly serializable, a [JsonUnsupportedObjectError] |
- * exception is thrown. If the call throws (including the case where there |
- * is no nullary "toJson" method, the error is caught and stored in the |
- * [JsonUnsupportedObjectError]'s [:cause:] field. |
- * |
- * If a [List] or [Map] contains a reference to itself, directly or through |
- * other lists or maps, it cannot be serialized and a [JsonCyclicError] is |
- * thrown. |
- * |
- * Json Objects should not change during serialization. |
- * If an object is serialized more than once, [stringify] is allowed to cache |
- * the JSON text for it. I.e., if an object changes after it is first |
- * serialized, the new values may or may not be reflected in the result. |
- */ |
-String stringify(Object object) { |
- return _JsonStringifier.stringify(object); |
-} |
- |
-/** |
- * Serializes [object] into [output] stream. |
- * |
- * Performs the same operations as [stringify] but outputs the resulting |
- * string to an existing [StringSink] instead of creating a new [String]. |
- * |
- * If serialization fails by throwing, some data might have been added to |
- * [output], but it won't contain valid JSON text. |
- */ |
-void printOn(Object object, StringSink output) { |
- return _JsonStringifier.printOn(object, output); |
-} |
- |
//// Implementation /////////////////////////////////////////////////////////// |
// Simple API for JSON parsing. |
-abstract class JsonListener { |
+abstract class _JsonListener { |
void handleString(String value) {} |
void handleNumber(num value) {} |
void handleBool(bool value) {} |
@@ -147,7 +41,7 @@ abstract class JsonListener { |
* This is a simple stack-based object builder. It keeps the most recently |
* seen value in a variable, and uses it depending on the following event. |
*/ |
-class BuildJsonListener extends JsonListener { |
+class _BuildJsonListener extends _JsonListener { |
/** |
* Stack used to handle nested containers. |
* |
@@ -223,11 +117,9 @@ class BuildJsonListener extends JsonListener { |
} |
} |
-typedef _Reviver(var key, var value); |
- |
-class ReviverJsonListener extends BuildJsonListener { |
+class _ReviverJsonListener extends _BuildJsonListener { |
final _Reviver reviver; |
- ReviverJsonListener(reviver(key, value)) : this.reviver = reviver; |
+ _ReviverJsonListener(reviver(key, value)) : this.reviver = reviver; |
void arrayElement() { |
List list = currentContainer; |
@@ -245,7 +137,7 @@ class ReviverJsonListener extends BuildJsonListener { |
} |
} |
-class JsonParser { |
+class _JsonParser { |
// A simple non-recursive state-based parser for JSON. |
// |
// Literal values accepted in states ARRAY_EMPTY, ARRAY_COMMA, OBJECT_COLON |
@@ -337,8 +229,8 @@ class JsonParser { |
static const int RBRACE = 0x7d; |
final String source; |
- final JsonListener listener; |
- JsonParser(this.source, this.listener); |
+ final _JsonListener listener; |
+ _JsonParser(this.source, this.listener); |
/** Parses [source], or throws if it fails. */ |
void parse() { |
@@ -662,167 +554,3 @@ class JsonParser { |
throw new FormatException("Unexpected character at $position: $slice"); |
} |
} |
- |
- |
-class _JsonStringifier { |
- StringSink sink; |
- List<Object> seen; // TODO: that should be identity set. |
- |
- _JsonStringifier(this.sink) : seen = []; |
- |
- static String stringify(final object) { |
- StringBuffer output = new StringBuffer(); |
- _JsonStringifier stringifier = new _JsonStringifier(output); |
- stringifier.stringifyValue(object); |
- return output.toString(); |
- } |
- |
- static void printOn(final object, StringSink output) { |
- _JsonStringifier stringifier = new _JsonStringifier(output); |
- stringifier.stringifyValue(object); |
- } |
- |
- static String numberToString(num x) { |
- return x.toString(); |
- } |
- |
- // ('0' + x) or ('a' + x - 10) |
- static int hexDigit(int x) => x < 10 ? 48 + x : 87 + x; |
- |
- static void escape(StringSink sb, String s) { |
- final int length = s.length; |
- bool needsEscape = false; |
- final charCodes = new List<int>(); |
- for (int i = 0; i < length; i++) { |
- int charCode = s.codeUnitAt(i); |
- if (charCode < 32) { |
- needsEscape = true; |
- charCodes.add(JsonParser.BACKSLASH); |
- switch (charCode) { |
- case JsonParser.BACKSPACE: |
- charCodes.add(JsonParser.CHAR_b); |
- break; |
- case JsonParser.TAB: |
- charCodes.add(JsonParser.CHAR_t); |
- break; |
- case JsonParser.NEWLINE: |
- charCodes.add(JsonParser.CHAR_n); |
- break; |
- case JsonParser.FORM_FEED: |
- charCodes.add(JsonParser.CHAR_f); |
- break; |
- case JsonParser.CARRIAGE_RETURN: |
- charCodes.add(JsonParser.CHAR_r); |
- break; |
- default: |
- charCodes.add(JsonParser.CHAR_u); |
- charCodes.add(hexDigit((charCode >> 12) & 0xf)); |
- charCodes.add(hexDigit((charCode >> 8) & 0xf)); |
- charCodes.add(hexDigit((charCode >> 4) & 0xf)); |
- charCodes.add(hexDigit(charCode & 0xf)); |
- break; |
- } |
- } else if (charCode == JsonParser.QUOTE || |
- charCode == JsonParser.BACKSLASH) { |
- needsEscape = true; |
- charCodes.add(JsonParser.BACKSLASH); |
- charCodes.add(charCode); |
- } else { |
- charCodes.add(charCode); |
- } |
- } |
- sb.write(needsEscape ? new String.fromCharCodes(charCodes) : s); |
- } |
- |
- void checkCycle(final object) { |
- // TODO: use Iterables. |
- for (int i = 0; i < seen.length; i++) { |
- if (identical(seen[i], object)) { |
- throw new JsonCyclicError(object); |
- } |
- } |
- seen.add(object); |
- } |
- |
- void stringifyValue(final 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. |
- if (!stringifyJsonValue(object)) { |
- checkCycle(object); |
- try { |
- var customJson = object.toJson(); |
- if (!stringifyJsonValue(customJson)) { |
- throw new JsonUnsupportedObjectError(object); |
- } |
- seen.removeLast(); |
- } catch (e) { |
- throw new JsonUnsupportedObjectError(object, cause: e); |
- } |
- } |
- } |
- |
- /** |
- * Serializes 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]. |
- */ |
- bool stringifyJsonValue(final object) { |
- if (object is num) { |
- // TODO: use writeOn. |
- sink.write(numberToString(object)); |
- return true; |
- } else if (identical(object, true)) { |
- sink.write('true'); |
- return true; |
- } else if (identical(object, false)) { |
- sink.write('false'); |
- return true; |
- } else if (object == null) { |
- sink.write('null'); |
- return true; |
- } else if (object is String) { |
- sink.write('"'); |
- escape(sink, object); |
- sink.write('"'); |
- return true; |
- } else if (object is List) { |
- checkCycle(object); |
- List a = object; |
- sink.write('['); |
- if (a.length > 0) { |
- stringifyValue(a[0]); |
- // TODO: switch to Iterables. |
- for (int i = 1; i < a.length; i++) { |
- sink.write(','); |
- stringifyValue(a[i]); |
- } |
- } |
- sink.write(']'); |
- seen.removeLast(); |
- return true; |
- } else if (object is Map) { |
- checkCycle(object); |
- Map<String, Object> m = object; |
- sink.write('{'); |
- bool first = true; |
- m.forEach((String key, Object value) { |
- if (!first) { |
- sink.write(',"'); |
- } else { |
- sink.write('"'); |
- } |
- escape(sink, key); |
- sink.write('":'); |
- stringifyValue(value); |
- first = false; |
- }); |
- sink.write('}'); |
- seen.removeLast(); |
- return true; |
- } else { |
- return false; |
- } |
- } |
-} |