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 JsonToInfoConverter 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 InfoToJsonConverter 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 JsonInfoCodec extends Codec<AllInfo, Map> { | |
Siggi Cherem (dart-lang)
2015/10/15 23:36:08
What are your thoughts on these alternative naming
Harry Terkelsen
2015/10/15 23:59:55
I see what you mean, "info" is not a very meaningf
| |
413 final Converter<AllInfo, Map> encoder = new InfoToJsonConverter(); | |
414 final Converter<Map, AllInfo> decoder = new JsonToInfoConverter(); | |
415 } | |
OLD | NEW |