json codec: bug fixes after removing id
diff --git a/lib/json_info_codec.dart b/lib/json_info_codec.dart
index aad6b5a..91ef7f0 100644
--- a/lib/json_info_codec.dart
+++ b/lib/json_info_codec.dart
@@ -5,8 +5,9 @@
 /// Converters and codecs for converting between JSON and [Info] classes.
 part of dart2js_info.info;
 
-List<String> _toSortedSerializedIds(Iterable<Info> infos, Map<Info, Id> ids) =>
-    infos.map((i) => ids[i].serializedId).toList()..sort(compareNatural);
+List<String> _toSortedSerializedIds(
+        Iterable<Info> infos, Id Function(Info) getId) =>
+    infos.map((i) => getId(i).serializedId).toList()..sort(compareNatural);
 
 // TODO(sigmund): add unit tests.
 class JsonToAllInfoConverter extends Converter<Map<String, dynamic>, AllInfo> {
@@ -325,7 +326,9 @@
   List<CodeSpan> parseCode(dynamic json) {
     // backwards compatibility with format 5.1:
     if (json is String) {
-      return [new CodeSpan(outputUnit: null, start: -1, end: -1, text: json)];
+      return [
+        new CodeSpan(outputUnit: null, start: null, end: null, text: json)
+      ];
     }
 
     if (json is List) {
@@ -346,32 +349,40 @@
 class AllInfoToJsonConverter extends Converter<AllInfo, Map>
     implements InfoVisitor<Map> {
   final Map<Info, Id> ids = new HashMap<Info, Id>();
-  final Set<int> usedIds = new Set<int>();
+  final Set<String> usedIds = new Set<String>();
 
   Id idFor(Info info) {
     var serializedId = ids[info];
     if (serializedId != null) return serializedId;
 
-    assert(info is LibraryInfo ||
-        info is ConstantInfo ||
-        info is OutputUnitInfo ||
-        info.parent != null);
+    assert(
+        info is LibraryInfo ||
+            info is ConstantInfo ||
+            info is OutputUnitInfo ||
+            info.parent != null,
+        "$info");
 
-    int id;
+    String id;
     if (info is ConstantInfo) {
       // No name and no parent, so `longName` isn't helpful
       assert(info.name == null);
       assert(info.parent == null);
       assert(info.code != null);
       // Instead, use the content of the code.
-      id = info.code.first.text.hashCode;
+      id = info.code.first.text ?? "_";
     } else {
-      id = longName(info, useLibraryUri: true, forId: true).hashCode;
+      id = longName(info, useLibraryUri: true, forId: true);
+      if (info is FieldInfo || info is FunctionInfo || info is ClosureInfo) {
+        id = "${id}_${info.size}";
+      }
     }
-    while (!usedIds.add(id)) {
-      id++;
-    }
-    serializedId = new Id(info.kind, id);
+    int suffix = 0;
+    String candidateId;
+    do {
+      candidateId = id + (suffix == 0 ? '' : '.$suffix');
+      suffix++;
+    } while (!usedIds.add(candidateId));
+    serializedId = new Id(info.kind, candidateId);
     return ids[info] = serializedId;
   }
 
@@ -426,7 +437,7 @@
   Map _visitAllInfoDependencies(AllInfo allInfo) {
     var map = new SplayTreeMap<String, List>(compareNatural);
     allInfo.dependencies.forEach((k, v) {
-      map[idFor(k).serializedId] = _toSortedSerializedIds(v, ids);
+      map[idFor(k).serializedId] = _toSortedSerializedIds(v, idFor);
     });
     return map;
   }
@@ -449,7 +460,7 @@
 
   Map visitProgram(ProgramInfo info) {
     return {
-      'entrypoint': idFor(info.entrypoint),
+      'entrypoint': idFor(info.entrypoint).serializedId,
       'size': info.size,
       'dart2jsVersion': info.dart2jsVersion,
       'compilationMoment': '${info.compilationMoment}',
@@ -492,7 +503,7 @@
               info.classes,
               info.typedefs
             ].expand((i) => i),
-            ids),
+            idFor),
         'canonicalUri': '${info.uri}',
       });
   }
@@ -503,14 +514,14 @@
         // TODO(sigmund): change format, include only when abstract is true.
         'modifiers': {'abstract': info.isAbstract},
         'children': _toSortedSerializedIds(
-            [info.fields, info.functions].expand((i) => i), ids)
+            [info.fields, info.functions].expand((i) => i), idFor)
       });
   }
 
   Map visitField(FieldInfo info) {
     var result = _visitBasicInfo(info)
       ..addAll(<String, Object>{
-        'children': _toSortedSerializedIds(info.closures, ids),
+        'children': _toSortedSerializedIds(info.closures, idFor),
         'inferredType': info.inferredType,
         'code': _serializeCode(info.code),
         'type': info.type,
@@ -571,7 +582,7 @@
   Map visitFunction(FunctionInfo info) {
     return _visitBasicInfo(info)
       ..addAll(<String, Object>{
-        'children': _toSortedSerializedIds(info.closures, ids),
+        'children': _toSortedSerializedIds(info.closures, idFor),
         'modifiers': _visitFunctionModifiers(info.modifiers),
         'returnType': info.returnType,
         'inferredReturnType': info.inferredReturnType,
@@ -600,7 +611,9 @@
   List<Object> _serializeCode(List<CodeSpan> code) {
     return code
         .map<Object>((c) => {
-              'outputUnit': idFor(c.outputUnit),
+              'outputUnit': c.outputUnit != null
+                  ? idFor(c.outputUnit).serializedId
+                  : null,
               'start': c.start,
               'end': c.end,
               'text': c.text,
@@ -616,9 +629,9 @@
 
 class Id {
   final InfoKind kind;
-  final int id;
+  final String id;
 
   Id(this.kind, this.id);
 
-  String get serializedId => '$kind/$id';
+  String get serializedId => '${kindToString(kind)}/$id';
 }