OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 /// Converters and codecs for converting between JSON and [Info] classes. |
| 6 part of dart2js_info.info; |
| 7 |
| 8 // TODO(sigmund): add unit tests. |
| 9 class JsonToAllInfoConverter extends Converter<Map, AllInfo> { |
| 10 Map<String, Info> registry; |
| 11 |
| 12 AllInfo convert(Map json) { |
| 13 registry = <String, Info>{}; |
| 14 |
| 15 var result = new AllInfo(); |
| 16 var elements = json['elements']; |
| 17 result.libraries.addAll(elements['library'].values.map(parseLibrary)); |
| 18 result.classes.addAll(elements['class'].values.map(parseClass)); |
| 19 result.functions.addAll(elements['function'].values.map(parseFunction)); |
| 20 result.fields.addAll(elements['field'].values.map(parseField)); |
| 21 result.typedefs.addAll(elements['typedef'].values.map(parseTypedef)); |
| 22 |
| 23 // TODO(sigmund): remove null check on next breaking version |
| 24 var constants = elements['constant']; |
| 25 if (constants != null) { |
| 26 result.constants.addAll(constants.values.map(parseConstant)); |
| 27 } |
| 28 |
| 29 var idMap = {}; |
| 30 for (var f in result.functions) { |
| 31 idMap[f.serializedId] = f; |
| 32 } |
| 33 for (var f in result.fields) { |
| 34 idMap[f.serializedId] = f; |
| 35 } |
| 36 |
| 37 json['holding'].forEach((k, deps) { |
| 38 var src = idMap[k]; |
| 39 assert(src != null); |
| 40 for (var dep in deps) { |
| 41 var target = idMap[dep['id']]; |
| 42 assert(target != null); |
| 43 src.uses.add(new DependencyInfo(target, dep['mask'])); |
| 44 } |
| 45 }); |
| 46 |
| 47 json['dependencies']?.forEach((k, deps) { |
| 48 result.dependencies[idMap[k]] = deps.map((d) => idMap[d]).toList(); |
| 49 }); |
| 50 |
| 51 result.program = parseProgram(json['program']); |
| 52 // todo: version, etc |
| 53 return result; |
| 54 } |
| 55 |
| 56 LibraryInfo parseLibrary(Map json) { |
| 57 LibraryInfo result = parseId(json['id']); |
| 58 result |
| 59 ..name = json['name'] |
| 60 ..uri = Uri.parse(json['canonicalUri']) |
| 61 ..outputUnit = parseId(json['outputUnit']) |
| 62 ..size = json['size']; |
| 63 for (var child in json['children'].map(parseId)) { |
| 64 if (child is FunctionInfo) { |
| 65 result.topLevelFunctions.add(child); |
| 66 } else if (child is FieldInfo) { |
| 67 result.topLevelVariables.add(child); |
| 68 } else if (child is ClassInfo) { |
| 69 result.classes.add(child); |
| 70 } else { |
| 71 assert(child is TypedefInfo); |
| 72 result.typedefs.add(child); |
| 73 } |
| 74 } |
| 75 return result; |
| 76 } |
| 77 |
| 78 ClassInfo parseClass(Map json) { |
| 79 ClassInfo result = parseId(json['id']); |
| 80 result |
| 81 ..name = json['name'] |
| 82 ..parent = parseId(json['parent']) |
| 83 ..outputUnit = parseId(json['outputUnit']) |
| 84 ..size = json['size'] |
| 85 ..isAbstract = json['modifiers']['abstract'] == true; |
| 86 assert(result is ClassInfo); |
| 87 for (var child in json['children'].map(parseId)) { |
| 88 if (child is FunctionInfo) { |
| 89 result.functions.add(child); |
| 90 } else { |
| 91 assert(child is FieldInfo); |
| 92 result.fields.add(child); |
| 93 } |
| 94 } |
| 95 return result; |
| 96 } |
| 97 |
| 98 FieldInfo parseField(Map json) { |
| 99 FieldInfo result = parseId(json['id']); |
| 100 return result |
| 101 ..name = json['name'] |
| 102 ..parent = parseId(json['parent']) |
| 103 ..coverageId = json['coverageId'] |
| 104 ..outputUnit = parseId(json['outputUnit']) |
| 105 ..size = json['size'] |
| 106 ..type = json['type'] |
| 107 ..inferredType = json['inferredType'] |
| 108 ..code = json['code'] |
| 109 ..isConst = json['const'] ?? false |
| 110 ..initializer = parseId(json['initializer']) |
| 111 ..closures = json['children'].map(parseId).toList(); |
| 112 } |
| 113 |
| 114 ConstantInfo parseConstant(Map json) { |
| 115 ConstantInfo result = parseId(json['id']); |
| 116 return result |
| 117 ..code = json['code'] |
| 118 ..size = json['size']; |
| 119 } |
| 120 |
| 121 TypedefInfo parseTypedef(Map json) { |
| 122 TypedefInfo result = parseId(json['id']); |
| 123 return result |
| 124 ..name = json['name'] |
| 125 ..parent = parseId(json['parent']) |
| 126 ..type = json['type'] |
| 127 ..size = 0; |
| 128 } |
| 129 |
| 130 ProgramInfo parseProgram(Map json) => new ProgramInfo() |
| 131 ..size = json['size'] |
| 132 ..entrypoint = parseId(json['entrypoint']); |
| 133 |
| 134 FunctionInfo parseFunction(Map json) { |
| 135 FunctionInfo result = parseId(json['id']); |
| 136 return result |
| 137 ..name = json['name'] |
| 138 ..parent = parseId(json['parent']) |
| 139 ..coverageId = json['coverageId'] |
| 140 ..outputUnit = parseId(json['outputUnit']) |
| 141 ..size = json['size'] |
| 142 ..type = json['type'] |
| 143 ..returnType = json['returnType'] |
| 144 ..inferredReturnType = json['inferredReturnType'] |
| 145 ..parameters = json['parameters'].map(parseParameter).toList() |
| 146 ..code = json['code'] |
| 147 ..sideEffects = json['sideEffects'] |
| 148 ..modifiers = parseModifiers(json['modifiers']) |
| 149 ..closures = json['children'].map(parseId).toList() |
| 150 ..measurements = parseMeasurements(json['measurements']); |
| 151 } |
| 152 |
| 153 ParameterInfo parseParameter(Map json) => |
| 154 new ParameterInfo(json['name'], json['type'], json['declaredType']); |
| 155 |
| 156 Measurements parseMeasurements(Map json) { |
| 157 if (json == null) return null; |
| 158 var uri = json['sourceFile']; |
| 159 var res = new Measurements(uri == null ? null : Uri.parse(uri)); |
| 160 for (var key in json.keys) { |
| 161 var value = json[key]; |
| 162 if (value == null) continue; |
| 163 if (key == 'entries') { |
| 164 value.forEach((metricName, entries) { |
| 165 var metric = new Metric.fromName(metricName); |
| 166 for (var i = 0; i < entries.length; i += 2) { |
| 167 res.record(metric, entries[i], entries[i + 1]); |
| 168 } |
| 169 }); |
| 170 } else { |
| 171 res.counters[new Metric.fromName(key)] = value; |
| 172 } |
| 173 } |
| 174 return res; |
| 175 } |
| 176 |
| 177 FunctionModifiers parseModifiers(Map<String, bool> json) { |
| 178 return new FunctionModifiers( |
| 179 isStatic: json['static'] == true, |
| 180 isConst: json['const'] == true, |
| 181 isFactory: json['factory'] == true, |
| 182 isExternal: json['external'] == true); |
| 183 } |
| 184 |
| 185 Info parseId(String serializedId) => registry.putIfAbsent(serializedId, () { |
| 186 if (serializedId == null) { |
| 187 return null; |
| 188 } else if (serializedId.startsWith('function/')) { |
| 189 return new FunctionInfo._(serializedId); |
| 190 } else if (serializedId.startsWith('library/')) { |
| 191 return new LibraryInfo._(serializedId); |
| 192 } else if (serializedId.startsWith('class/')) { |
| 193 return new ClassInfo._(serializedId); |
| 194 } else if (serializedId.startsWith('field/')) { |
| 195 return new FieldInfo._(serializedId); |
| 196 } else if (serializedId.startsWith('constant/')) { |
| 197 return new ConstantInfo._(serializedId); |
| 198 } else if (serializedId.startsWith('typedef/')) { |
| 199 return new TypedefInfo._(serializedId); |
| 200 } else if (serializedId.startsWith('outputUnit/')) { |
| 201 return new OutputUnitInfo._(serializedId); |
| 202 } |
| 203 assert(false); |
| 204 }); |
| 205 } |
| 206 |
| 207 class AllInfoToJsonConverter extends Converter<AllInfo, Map> |
| 208 implements InfoVisitor<Map> { |
| 209 Map convert(AllInfo info) => info.accept(this); |
| 210 |
| 211 Map _visitList(List<Info> infos) { |
| 212 var map = <String, Map>{}; |
| 213 for (var info in infos) { |
| 214 map['${info.id}'] = info.accept(this); |
| 215 } |
| 216 return map; |
| 217 } |
| 218 |
| 219 Map _visitAllInfoElements(AllInfo info) { |
| 220 var jsonLibraries = _visitList(info.libraries); |
| 221 var jsonClasses = _visitList(info.classes); |
| 222 var jsonFunctions = _visitList(info.functions); |
| 223 var jsonTypedefs = _visitList(info.typedefs); |
| 224 var jsonFields = _visitList(info.fields); |
| 225 var jsonConstants = _visitList(info.constants); |
| 226 return { |
| 227 'library': jsonLibraries, |
| 228 'class': jsonClasses, |
| 229 'function': jsonFunctions, |
| 230 'typedef': jsonTypedefs, |
| 231 'field': jsonFields, |
| 232 'constant': jsonConstants |
| 233 }; |
| 234 } |
| 235 |
| 236 Map _visitDependencyInfo(DependencyInfo info) => |
| 237 {'id': info.target.serializedId, 'mask': info.mask}; |
| 238 |
| 239 Map _visitAllInfoHolding(AllInfo allInfo) { |
| 240 var map = <String, List>{}; |
| 241 void helper(CodeInfo info) { |
| 242 if (info.uses.isEmpty) return; |
| 243 map[info.serializedId] = |
| 244 info.uses.map((u) => _visitDependencyInfo(u)).toList(); |
| 245 } |
| 246 allInfo.functions.forEach(helper); |
| 247 allInfo.fields.forEach(helper); |
| 248 return map; |
| 249 } |
| 250 |
| 251 Map _visitAllInfoDependencies(AllInfo allInfo) { |
| 252 var map = <String, List>{}; |
| 253 allInfo.dependencies.forEach((k, v) { |
| 254 map[k.serializedId] = v.map((i) => i.serializedId).toList(); |
| 255 }); |
| 256 return map; |
| 257 } |
| 258 |
| 259 Map visitAll(AllInfo info) { |
| 260 var elements = _visitAllInfoElements(info); |
| 261 var jsonHolding = _visitAllInfoHolding(info); |
| 262 var jsonDependencies = _visitAllInfoDependencies(info); |
| 263 return { |
| 264 'elements': elements, |
| 265 'holding': jsonHolding, |
| 266 'dependencies': jsonDependencies, |
| 267 'outputUnits': info.outputUnits.map((u) => u.accept(this)).toList(), |
| 268 'dump_version': info.version, |
| 269 'deferredFiles': info.deferredFiles, |
| 270 'dump_minor_version': '${info.minorVersion}', |
| 271 'program': info.program.accept(this) |
| 272 }; |
| 273 } |
| 274 |
| 275 Map visitProgram(ProgramInfo info) { |
| 276 return { |
| 277 'entrypoint': info.entrypoint.serializedId, |
| 278 'size': info.size, |
| 279 'dart2jsVersion': info.dart2jsVersion, |
| 280 'compilationMoment': '${info.compilationMoment}', |
| 281 'compilationDuration': '${info.compilationDuration}', |
| 282 'toJsonDuration': info.toJsonDuration, |
| 283 'dumpInfoDuration': '${info.dumpInfoDuration}', |
| 284 'noSuchMethodEnabled': info.noSuchMethodEnabled, |
| 285 'minified': info.minified, |
| 286 }; |
| 287 } |
| 288 |
| 289 Map _visitBasicInfo(BasicInfo info) { |
| 290 var res = { |
| 291 'id': info.serializedId, |
| 292 'kind': _kindToString(info.kind), |
| 293 'name': info.name, |
| 294 'size': info.size, |
| 295 }; |
| 296 // TODO(sigmund): Omit this also when outputUnit.id == 0 (most code is in |
| 297 // the main output unit by default). |
| 298 if (info.outputUnit != null) res['outputUnit'] = |
| 299 info.outputUnit.serializedId; |
| 300 if (info.coverageId != null) res['coverageId'] = info.coverageId; |
| 301 if (info.parent != null) res['parent'] = info.parent.serializedId; |
| 302 return res; |
| 303 } |
| 304 |
| 305 Map visitLibrary(LibraryInfo info) { |
| 306 return _visitBasicInfo(info) |
| 307 ..addAll({ |
| 308 'children': [] |
| 309 ..addAll(info.topLevelFunctions.map((f) => f.serializedId)) |
| 310 ..addAll(info.topLevelVariables.map((v) => v.serializedId)) |
| 311 ..addAll(info.classes.map((c) => c.serializedId)) |
| 312 ..addAll(info.typedefs.map((t) => t.serializedId)), |
| 313 'canonicalUri': '${info.uri}', |
| 314 }); |
| 315 } |
| 316 |
| 317 Map visitClass(ClassInfo info) { |
| 318 return _visitBasicInfo(info) |
| 319 ..addAll({ |
| 320 // TODO(sigmund): change format, include only when abstract is true. |
| 321 'modifiers': {'abstract': info.isAbstract}, |
| 322 'children': [] |
| 323 ..addAll(info.fields.map((f) => f.serializedId)) |
| 324 ..addAll(info.functions.map((m) => m.serializedId)) |
| 325 }); |
| 326 } |
| 327 |
| 328 Map visitField(FieldInfo info) { |
| 329 var result = _visitBasicInfo(info) |
| 330 ..addAll({ |
| 331 'children': info.closures.map((i) => i.serializedId).toList(), |
| 332 'inferredType': info.inferredType, |
| 333 'code': info.code, |
| 334 'type': info.type, |
| 335 }); |
| 336 if (info.isConst) { |
| 337 result['const'] = true; |
| 338 if (info.initializer != null) result['initializer'] = |
| 339 info.initializer.serializedId; |
| 340 } |
| 341 return result; |
| 342 } |
| 343 |
| 344 Map visitConstant(ConstantInfo info) => |
| 345 _visitBasicInfo(info)..addAll({'code': info.code}); |
| 346 |
| 347 // TODO(sigmund): exclude false values (requires bumping the format version): |
| 348 // var res = <String, bool>{}; |
| 349 // if (isStatic) res['static'] = true; |
| 350 // if (isConst) res['const'] = true; |
| 351 // if (isFactory) res['factory'] = true; |
| 352 // if (isExternal) res['external'] = true; |
| 353 // return res; |
| 354 Map _visitFunctionModifiers(FunctionModifiers mods) => { |
| 355 'static': mods.isStatic, |
| 356 'const': mods.isConst, |
| 357 'factory': mods.isFactory, |
| 358 'external': mods.isExternal, |
| 359 }; |
| 360 |
| 361 Map _visitParameterInfo(ParameterInfo info) => |
| 362 {'name': info.name, 'type': info.type, 'declaredType': info.declaredType}; |
| 363 |
| 364 String _visitMetric(Metric metric) => metric.name; |
| 365 |
| 366 Map _visitMeasurements(Measurements measurements) { |
| 367 if (measurements == null) return null; |
| 368 var jsonEntries = <String, List<Map>>{}; |
| 369 measurements.entries.forEach((metric, values) { |
| 370 jsonEntries[_visitMetric(metric)] = |
| 371 values.expand((e) => [e.begin, e.end]).toList(); |
| 372 }); |
| 373 var json = {'entries': jsonEntries}; |
| 374 // TODO(sigmund): encode uri as an offset of the URIs available in the parts |
| 375 // of the library info. |
| 376 if (measurements.uri != null) json['sourceFile'] = '${measurements.uri}'; |
| 377 if (measurements.counters[Metric.functions] != null) { |
| 378 json[_visitMetric(Metric.functions)] = |
| 379 measurements.counters[Metric.functions]; |
| 380 } |
| 381 if (measurements.counters[Metric.reachableFunctions] != null) { |
| 382 json[_visitMetric(Metric.reachableFunctions)] = |
| 383 measurements.counters[Metric.reachableFunctions]; |
| 384 } |
| 385 return json; |
| 386 } |
| 387 |
| 388 Map visitFunction(FunctionInfo info) { |
| 389 return _visitBasicInfo(info) |
| 390 ..addAll({ |
| 391 'children': info.closures.map((i) => i.serializedId).toList(), |
| 392 'modifiers': _visitFunctionModifiers(info.modifiers), |
| 393 'returnType': info.returnType, |
| 394 'inferredReturnType': info.inferredReturnType, |
| 395 'parameters': |
| 396 info.parameters.map((p) => _visitParameterInfo(p)).toList(), |
| 397 'sideEffects': info.sideEffects, |
| 398 'inlinedCount': info.inlinedCount, |
| 399 'code': info.code, |
| 400 'type': info.type, |
| 401 'measurements': _visitMeasurements(info.measurements), |
| 402 // Note: version 3.2 of dump-info serializes `uses` in a section called |
| 403 // `holding` at the top-level. |
| 404 }); |
| 405 } |
| 406 |
| 407 visitTypedef(TypedefInfo info) => _visitBasicInfo(info)..['type'] = info.type; |
| 408 |
| 409 visitOutput(OutputUnitInfo info) => _visitBasicInfo(info); |
| 410 } |
| 411 |
| 412 class AllInfoJsonCodec extends Codec<AllInfo, Map> { |
| 413 final Converter<AllInfo, Map> encoder = new AllInfoToJsonConverter(); |
| 414 final Converter<Map, AllInfo> decoder = new JsonToAllInfoConverter(); |
| 415 } |
OLD | NEW |