Add CodeSpan to represent code sections in dump info

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f9a4cf4..65d6f80 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,14 @@
   properties were only used for serialization and deserialization. Those values
   are now computed during the serialization process instead.
 
+* Added `CodeSpan` - a representation of code regions referring to output files.
+  This will be used to transition to a lighterweight dump-info that doesn't
+  embed code snippets (since they are duplicated with the output program).
+ 
+  Encoder produces a new format for code-spans, but for a transitional period
+  the decoder is still backwards compatible (filling in just the `text` in
+  `CodeSpan` where the json contained a String).
+
 ## 0.5.17
 
 * Make `live_code_size_analysis` print library URIs and not library names.
diff --git a/lib/info.dart b/lib/info.dart
index a3cb639..62a2de9 100644
--- a/lib/info.dart
+++ b/lib/info.dart
@@ -120,14 +120,14 @@
   /// Major version indicating breaking changes in the format. A new version
   /// means that an old deserialization algorithm will not work with the new
   /// format.
-  final int version = 5;
+  final int version = 6;
 
   /// Minor version indicating non-breaking changes in the format. A change in
   /// this version number means that the json parsing in this library from a
   /// previous will continue to work after the change. This is typically
   /// increased when adding new entries to the file format.
   // Note: the dump-info.viewer app was written using a json parser version 3.2.
-  final int minorVersion = 1;
+  final int minorVersion = 0;
 
   AllInfo();
 
@@ -249,11 +249,28 @@
   T accept<T>(InfoVisitor<T> visitor) => visitor.visitClass(this);
 }
 
+/// Details about generated code spans.
+class CodeSpan {
+  /// File where the code was generated.
+  OutputUnitInfo outputUnit;
+
+  /// Start offset in the generated file.
+  int start;
+
+  /// end offset in the generated file.
+  int end;
+
+  /// The actual code.
+  String text;
+
+  CodeSpan({this.outputUnit, this.start, this.end, this.text});
+}
+
 /// Information about a constant value.
 // TODO(sigmund): add dependency data for ConstantInfo
 class ConstantInfo extends BasicInfo {
   /// The actual generated code for the constant.
-  String code;
+  List<CodeSpan> code;
 
   // TODO(sigmund): Add coverage support to constants?
   ConstantInfo({int size: 0, this.code, OutputUnitInfo outputUnit})
@@ -276,7 +293,7 @@
   List<ClosureInfo> closures;
 
   /// The actual generated code for the field.
-  String code;
+  List<CodeSpan> code;
 
   /// Whether this corresponds to a const field declaration.
   bool isConst;
@@ -350,7 +367,7 @@
   int inlinedCount;
 
   /// The actual generated code.
-  String code;
+  List<CodeSpan> code;
 
   /// Measurements collected for this function.
   Measurements measurements;
diff --git a/lib/json_info_codec.dart b/lib/json_info_codec.dart
index ec0e6c1..aad6b5a 100644
--- a/lib/json_info_codec.dart
+++ b/lib/json_info_codec.dart
@@ -144,7 +144,7 @@
       ..size = json['size']
       ..type = json['type']
       ..inferredType = json['inferredType']
-      ..code = json['code']
+      ..code = parseCode(json['code'])
       ..isConst = json['const'] ?? false
       ..initializer = parseId(json['initializer'])
       ..closures = (json['children'] as List)
@@ -155,7 +155,7 @@
   ConstantInfo parseConstant(Map json) {
     ConstantInfo result = parseId(json['id']);
     return result
-      ..code = json['code']
+      ..code = parseCode(json['code'])
       ..size = json['size']
       ..outputUnit = parseId(json['outputUnit']);
   }
@@ -243,7 +243,7 @@
       ..inferredReturnType = json['inferredReturnType']
       ..parameters =
           (json['parameters'] as List).map((p) => parseParameter(p)).toList()
-      ..code = json['code']
+      ..code = parseCode(json['code'])
       ..sideEffects = json['sideEffects']
       ..modifiers =
           parseModifiers(new Map<String, bool>.from(json['modifiers']))
@@ -321,6 +321,26 @@
       assert(false);
     });
   }
+
+  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)];
+    }
+
+    if (json is List) {
+      return json.map((dynamic value) {
+        Map<String, dynamic> jsonCode = value;
+        return new CodeSpan(
+            outputUnit: parseId(jsonCode['outputUnit']),
+            start: jsonCode['start'],
+            end: jsonCode['end'],
+            text: jsonCode['text']);
+      }).toList();
+    }
+
+    return [];
+  }
 }
 
 class AllInfoToJsonConverter extends Converter<AllInfo, Map>
@@ -344,7 +364,7 @@
       assert(info.parent == null);
       assert(info.code != null);
       // Instead, use the content of the code.
-      id = info.code.hashCode;
+      id = info.code.first.text.hashCode;
     } else {
       id = longName(info, useLibraryUri: true, forId: true).hashCode;
     }
@@ -492,7 +512,7 @@
       ..addAll(<String, Object>{
         'children': _toSortedSerializedIds(info.closures, ids),
         'inferredType': info.inferredType,
-        'code': info.code,
+        'code': _serializeCode(info.code),
         'type': info.type,
       });
     if (info.isConst) {
@@ -504,8 +524,8 @@
     return result;
   }
 
-  Map visitConstant(ConstantInfo info) =>
-      _visitBasicInfo(info)..addAll(<String, Object>{'code': info.code});
+  Map visitConstant(ConstantInfo info) => _visitBasicInfo(info)
+    ..addAll(<String, Object>{'code': _serializeCode(info.code)});
 
   // TODO(sigmund): exclude false values (requires bumping the format version):
   //     var res = <String, bool>{};
@@ -559,7 +579,7 @@
             info.parameters.map((p) => _visitParameterInfo(p)).toList(),
         'sideEffects': info.sideEffects,
         'inlinedCount': info.inlinedCount,
-        'code': info.code,
+        'code': _serializeCode(info.code),
         'type': info.type,
         'measurements': _visitMeasurements(info.measurements),
         // Note: version 3.2 of dump-info serializes `uses` in a section called
@@ -576,6 +596,17 @@
 
   visitOutput(OutputUnitInfo info) =>
       _visitBasicInfo(info)..['imports'] = info.imports;
+
+  List<Object> _serializeCode(List<CodeSpan> code) {
+    return code
+        .map<Object>((c) => {
+              'outputUnit': idFor(c.outputUnit),
+              'start': c.start,
+              'end': c.end,
+              'text': c.text,
+            })
+        .toList();
+  }
 }
 
 class AllInfoJsonCodec extends Codec<AllInfo, Map> {
diff --git a/lib/proto_info_codec.dart b/lib/proto_info_codec.dart
index db9b6a0..f0a6b6a 100644
--- a/lib/proto_info_codec.dart
+++ b/lib/proto_info_codec.dart
@@ -44,7 +44,7 @@
       assert(info.parent == null);
       assert(info.code != null);
       // Instead, use the content of the code.
-      id = info.code.hashCode;
+      id = info.code.first.text.hashCode;
     } else {
       id = longName(info, useLibraryUri: true, forId: true).hashCode;
     }
@@ -146,7 +146,7 @@
     }
 
     if (info.code != null) {
-      proto.code = info.code;
+      proto.code = info.code.map((c) => c.text).join('\n');
     }
 
     if (info.sideEffects != null) {
@@ -171,7 +171,7 @@
       ..isConst = info.isConst;
 
     if (info.code != null) {
-      proto.code = info.code;
+      proto.code = info.code.map((c) => c.text).join('\n');
     }
 
     if (info.initializer != null) {
@@ -185,7 +185,7 @@
   }
 
   static ConstantInfoPB _convertToConstantInfoPB(ConstantInfo info) {
-    return new ConstantInfoPB()..code = info.code;
+    return new ConstantInfoPB()..code = info.code.map((c) => c.text).join('\n');
   }
 
   static OutputUnitInfoPB _convertToOutputUnitInfoPB(OutputUnitInfo info) {