| 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 /// Data collected by the dump-info task. |
| 6 library compiler.src.lib.info; |
| 7 |
| 8 // Note: this file intentionally doesn't import anything from the compiler. That |
| 9 // should make it easier for tools to depend on this library. The idea is that |
| 10 // by using this library, tools can consume the information in the same way it |
| 11 // is produced by the compiler. |
| 12 // TODO(sigmund): make this a proper public API (export this explicitly at the |
| 13 // lib folder level.) |
| 14 |
| 15 /// Common interface to many pieces of information generated by the compiler. |
| 16 abstract class Info { |
| 17 /// An identifier for the kind of information. |
| 18 String get kind; |
| 19 |
| 20 /// Name of the element associated with this info. |
| 21 String name; |
| 22 |
| 23 /// An id to uniquely identify this info among infos of the same [kind]. |
| 24 int get id; |
| 25 |
| 26 /// A globally unique id combining [kind] and [id] together. |
| 27 String get serializedId; |
| 28 |
| 29 /// Bytes used in the generated code for the corresponding element. |
| 30 int size; |
| 31 |
| 32 /// Serializes the information into a JSON format. |
| 33 // TODO(sigmund): refactor and put toJson outside the class, so we can have 2 |
| 34 // different serializer/deserializers at once. |
| 35 Map toJson(); |
| 36 } |
| 37 |
| 38 /// Common information used for most kind of elements. |
| 39 // TODO(sigmund): add more: |
| 40 // - inputSize: bytes used in the Dart source program |
| 41 abstract class BasicInfo implements Info { |
| 42 final String kind; |
| 43 final int id; |
| 44 int size; |
| 45 |
| 46 String get serializedId => '$kind/$id'; |
| 47 |
| 48 String name; |
| 49 |
| 50 /// If using deferred libraries, where the element associated with this info |
| 51 /// is generated. |
| 52 OutputUnitInfo outputUnit; |
| 53 |
| 54 BasicInfo(this.kind, this.id, this.name, this.outputUnit, this.size); |
| 55 |
| 56 Map toJson() { |
| 57 var res = {'id': serializedId, 'kind': kind, 'name': name, 'size': size}; |
| 58 // TODO(sigmund): omit this also when outputUnit.id == 0 |
| 59 // (most code is by default in the main output unit) |
| 60 if (outputUnit != null) res['outputUnit'] = outputUnit.serializedId; |
| 61 return res; |
| 62 } |
| 63 |
| 64 String toString() => '$serializedId $name [$size]'; |
| 65 } |
| 66 |
| 67 /// Info associated with elements containing executable code (like fields and |
| 68 /// methods) |
| 69 abstract class CodeInfo implements Info { |
| 70 /// How does this function or field depend on others. |
| 71 final List<DependencyInfo> uses = <DependencyInfo>[]; |
| 72 } |
| 73 |
| 74 |
| 75 /// The entire information produced while compiling a program. |
| 76 class AllInfo { |
| 77 /// Summary information about the program. |
| 78 ProgramInfo program; |
| 79 |
| 80 /// Information about each library processed by the compiler. |
| 81 List<LibraryInfo> libraries = <LibraryInfo>[]; |
| 82 |
| 83 /// Information about each function (includes methods and getters in any |
| 84 /// library) |
| 85 List<FunctionInfo> functions = <FunctionInfo>[]; |
| 86 |
| 87 /// Information about type defs in the program. |
| 88 List<TypedefInfo> typedefs = <TypedefInfo>[]; |
| 89 |
| 90 /// Information about each class (in any library). |
| 91 List<ClassInfo> classes = <ClassInfo>[]; |
| 92 |
| 93 /// Information about fields (in any class). |
| 94 List<FieldInfo> fields = <FieldInfo>[]; |
| 95 |
| 96 /// Information about output units (should be just one entry if not using |
| 97 /// deferred loading). |
| 98 List<OutputUnitInfo> outputUnits = <OutputUnitInfo>[]; |
| 99 |
| 100 /// Details about all deferred imports and what files would be loaded when the |
| 101 /// import is resolved. |
| 102 // TODO(sigmund): use a different format for dump-info. This currently emits |
| 103 // the same map that is created for the `--deferred-map` flag. |
| 104 Map<String, Map<String, dynamic>> deferredFiles; |
| 105 |
| 106 /// Major version indicating breaking changes in the format. A new version |
| 107 /// means that an old deserialization algorithm will not work with the new |
| 108 /// format. |
| 109 final int version = 3; |
| 110 |
| 111 /// Minor version indicating non-breaking changes in the format. A change in |
| 112 /// this version number means that the json parsing in this library from a |
| 113 /// previous will continue to work after the change. This is typically |
| 114 /// increased when adding new entries to the file format. |
| 115 // Note: the dump-info.viewer app was written using a json parser version 3.2. |
| 116 final int minorVersion = 3; |
| 117 |
| 118 AllInfo(); |
| 119 |
| 120 Map _listAsJsonMap(List<Info> list) { |
| 121 var map = <String, Map>{}; |
| 122 for (var info in list) { |
| 123 map['${info.id}'] = info.toJson(); |
| 124 } |
| 125 return map; |
| 126 } |
| 127 |
| 128 Map _extractHoldingInfo() { |
| 129 var map = <String, List>{}; |
| 130 void helper(CodeInfo info) { |
| 131 if (info.uses.isEmpty) return; |
| 132 map[info.serializedId] = info.uses.map((u) => u.toJson()).toList(); |
| 133 } |
| 134 functions.forEach(helper); |
| 135 fields.forEach(helper); |
| 136 return map; |
| 137 } |
| 138 |
| 139 // TODO(sigmund): implement fromJson |
| 140 Map toJson() => { |
| 141 'elements': { |
| 142 'library': _listAsJsonMap(libraries), |
| 143 'class': _listAsJsonMap(classes), |
| 144 'function': _listAsJsonMap(functions), |
| 145 'typedef': _listAsJsonMap(typedefs), |
| 146 'field': _listAsJsonMap(fields), |
| 147 }, |
| 148 'holding': _extractHoldingInfo(), |
| 149 'outputUnits': outputUnits.map((u) => u.toJson()).toList(), |
| 150 'dump_version': version, |
| 151 'deferredFiles': deferredFiles, |
| 152 'dump_minor_version': '$minorVersion', |
| 153 // TODO(sigmund): change viewer to accept an int? |
| 154 'program': program.toJson(), |
| 155 }; |
| 156 } |
| 157 |
| 158 class ProgramInfo { |
| 159 int size; |
| 160 String dart2jsVersion; |
| 161 DateTime compilationMoment; |
| 162 Duration compilationDuration; |
| 163 // TODO(sigmund): use Duration. |
| 164 int toJsonDuration; |
| 165 int dumpInfoDuration; |
| 166 bool noSuchMethodEnabled; |
| 167 bool minified; |
| 168 |
| 169 ProgramInfo( |
| 170 {this.size, |
| 171 this.dart2jsVersion, |
| 172 this.compilationMoment, |
| 173 this.compilationDuration, |
| 174 this.toJsonDuration, |
| 175 this.dumpInfoDuration, |
| 176 this.noSuchMethodEnabled, |
| 177 this.minified}); |
| 178 |
| 179 Map toJson() => { |
| 180 'size': size, |
| 181 'dart2jsVersion': dart2jsVersion, |
| 182 'compilationMoment': '$compilationMoment', |
| 183 'compilationDuration': '${compilationDuration}', |
| 184 'toJsonDuration': toJsonDuration, |
| 185 'dumpInfoDuration': '$dumpInfoDuration', |
| 186 'noSuchMethodEnabled': noSuchMethodEnabled, |
| 187 'minified': minified, |
| 188 }; |
| 189 } |
| 190 |
| 191 class LibraryInfo extends BasicInfo { |
| 192 Uri uri; |
| 193 final List<FunctionInfo> topLevelFunctions = <FunctionInfo>[]; |
| 194 final List<FieldInfo> topLevelVariables = <FieldInfo>[]; |
| 195 final List<ClassInfo> classes = <ClassInfo>[]; |
| 196 final List<TypedefInfo> typedefs = <TypedefInfo>[]; |
| 197 |
| 198 static int _id = 0; |
| 199 |
| 200 bool get isEmpty => |
| 201 topLevelFunctions.isEmpty && topLevelVariables.isEmpty && classes.isEmpty; |
| 202 |
| 203 LibraryInfo(String name, this.uri, OutputUnitInfo outputUnit, int size) |
| 204 : super('library', _id++, name, outputUnit, size); |
| 205 |
| 206 Map toJson() => super.toJson() |
| 207 ..addAll({ |
| 208 'children': [] |
| 209 ..addAll(topLevelFunctions.map((f) => f.serializedId)) |
| 210 ..addAll(topLevelVariables.map((v) => v.serializedId)) |
| 211 ..addAll(classes.map((c) => c.serializedId)) |
| 212 ..addAll(typedefs.map((t) => t.serializedId)), |
| 213 'canonicalUri': '$uri', |
| 214 }); |
| 215 } |
| 216 |
| 217 class OutputUnitInfo extends BasicInfo { |
| 218 static int _ids = 0; |
| 219 OutputUnitInfo(String name, int size) |
| 220 : super('outputUnit', _ids++, name, null, size); |
| 221 } |
| 222 |
| 223 class ClassInfo extends BasicInfo { |
| 224 bool isAbstract; |
| 225 |
| 226 // TODO(sigmund): split static vs instance vs closures |
| 227 final List<FunctionInfo> functions = <FunctionInfo>[]; |
| 228 final List<FieldInfo> fields = <FieldInfo>[]; |
| 229 static int _ids = 0; |
| 230 |
| 231 ClassInfo( |
| 232 {String name, this.isAbstract, OutputUnitInfo outputUnit, int size: 0}) |
| 233 : super('class', _ids++, name, outputUnit, size); |
| 234 |
| 235 Map toJson() => super.toJson() |
| 236 ..addAll({ |
| 237 // TODO(sigmund): change format, include only when abstract is true. |
| 238 'modifiers': {'abstract': isAbstract}, |
| 239 'children': [] |
| 240 ..addAll(fields.map((f) => f.serializedId)) |
| 241 ..addAll(functions.map((m) => m.serializedId)) |
| 242 }); |
| 243 } |
| 244 |
| 245 class FieldInfo extends BasicInfo with CodeInfo { |
| 246 String type; |
| 247 String inferredType; |
| 248 List<FunctionInfo> closures; |
| 249 String code; |
| 250 |
| 251 static int _ids = 0; |
| 252 FieldInfo( |
| 253 {String name, |
| 254 int size: 0, |
| 255 this.type, |
| 256 this.inferredType, |
| 257 this.closures, |
| 258 this.code, |
| 259 OutputUnitInfo outputUnit}) |
| 260 : super('field', _ids++, name, outputUnit, size); |
| 261 |
| 262 Map toJson() => super.toJson() |
| 263 ..addAll({ |
| 264 'children': closures.map((i) => i.serializedId).toList(), |
| 265 'inferredType': inferredType, |
| 266 'code': code, |
| 267 'type': type, |
| 268 }); |
| 269 } |
| 270 |
| 271 class TypedefInfo extends BasicInfo { |
| 272 String type; |
| 273 |
| 274 static int _ids = 0; |
| 275 TypedefInfo(String name, this.type, OutputUnitInfo outputUnit) |
| 276 : super('typedef', _ids++, name, outputUnit, 0); |
| 277 |
| 278 Map toJson() => super.toJson()..['type'] = '$type'; |
| 279 } |
| 280 |
| 281 class FunctionInfo extends BasicInfo with CodeInfo { |
| 282 static const int TOP_LEVEL_FUNCTION_KIND = 0; |
| 283 static const int CLOSURE_FUNCTION_KIND = 1; |
| 284 static const int METHOD_FUNCTION_KIND = 2; |
| 285 static const int CONSTRUCTOR_FUNCTION_KIND = 3; |
| 286 static int _ids = 0; |
| 287 |
| 288 /// Kind of function (top-level function, closure, method, or constructor). |
| 289 final int functionKind; |
| 290 |
| 291 /// Modifiers applied to this function. |
| 292 final FunctionModifiers modifiers; |
| 293 |
| 294 /// Nested closures that appear within the body of this function. |
| 295 List<FunctionInfo> closures; |
| 296 |
| 297 /// The type of this function. |
| 298 String type; |
| 299 |
| 300 /// The declared return type. |
| 301 String returnType; |
| 302 |
| 303 /// The inferred return type. |
| 304 String inferredReturnType; |
| 305 |
| 306 /// Name and type information for each parameter. |
| 307 List<ParameterInfo> parameters; |
| 308 |
| 309 /// Side-effects. |
| 310 // TODO(sigmund): serialize more precisely, not just a string representation. |
| 311 String sideEffects; |
| 312 |
| 313 /// How many function calls were inlined into this function. |
| 314 int inlinedCount; |
| 315 |
| 316 /// The actual generated code. |
| 317 String code; |
| 318 |
| 319 FunctionInfo( |
| 320 {String name, |
| 321 OutputUnitInfo outputUnit, |
| 322 int size: 0, |
| 323 this.functionKind, |
| 324 this.modifiers, |
| 325 this.closures, |
| 326 this.type, |
| 327 this.returnType, |
| 328 this.inferredReturnType, |
| 329 this.parameters, |
| 330 this.sideEffects, |
| 331 this.inlinedCount, |
| 332 this.code}) |
| 333 : super('function', _ids++, name, outputUnit, size); |
| 334 |
| 335 Map toJson() => super.toJson() |
| 336 ..addAll({ |
| 337 'children': closures.map((i) => i.serializedId).toList(), |
| 338 'modifiers': modifiers.toJson(), |
| 339 'returnType': returnType, |
| 340 'inferredReturnType': inferredReturnType, |
| 341 'parameters': parameters.map((p) => p.toJson()).toList(), |
| 342 'sideEffects': sideEffects, |
| 343 'inlinedCount': inlinedCount, |
| 344 'code': code, |
| 345 'type': type, |
| 346 // Note: version 3.2 of dump-info serializes `uses` in a section called |
| 347 // `holding` at the top-level. |
| 348 }); |
| 349 } |
| 350 |
| 351 /// Information about how a dependency is used. |
| 352 class DependencyInfo { |
| 353 /// The dependency, either a FunctionInfo or FieldInfo. |
| 354 final Info target; |
| 355 |
| 356 /// Either a selector mask indicating how this is used, or 'inlined'. |
| 357 // TODO(sigmund): split mask into an enum or something more precise to really |
| 358 // describe the dependencies in detail. |
| 359 final String mask; |
| 360 |
| 361 DependencyInfo(this.target, this.mask); |
| 362 |
| 363 Map toJson() => {'id': target.serializedId, 'mask': mask}; |
| 364 } |
| 365 |
| 366 /// Name and type information about a function parameter. |
| 367 class ParameterInfo { |
| 368 final String name; |
| 369 final String type; |
| 370 final String declaredType; |
| 371 |
| 372 ParameterInfo(this.name, this.type, this.declaredType); |
| 373 |
| 374 Map toJson() => {'name': name, 'type': type, 'declaredType': declaredType}; |
| 375 } |
| 376 |
| 377 /// Modifiers that may apply to methods. |
| 378 class FunctionModifiers { |
| 379 final bool isStatic; |
| 380 final bool isConst; |
| 381 final bool isFactory; |
| 382 final bool isExternal; |
| 383 |
| 384 FunctionModifiers( |
| 385 {this.isStatic: false, |
| 386 this.isConst: false, |
| 387 this.isFactory: false, |
| 388 this.isExternal: false}); |
| 389 |
| 390 // TODO(sigmund): exclude false values (requires bumping the format version): |
| 391 // Map toJson() { |
| 392 // var res = <String, bool>{}; |
| 393 // if (isStatic) res['static'] = true; |
| 394 // if (isConst) res['const'] = true; |
| 395 // if (isFactory) res['factory'] = true; |
| 396 // if (isExternal) res['external'] = true; |
| 397 // return res; |
| 398 // } |
| 399 Map toJson() => { |
| 400 'static': isStatic, |
| 401 'const': isConst, |
| 402 'factory': isFactory, |
| 403 'external': isExternal, |
| 404 }; |
| 405 } |
| OLD | NEW |