Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2016, 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 import 'dart:convert'; | |
| 6 import 'dart:mirrors'; | |
| 7 | |
| 8 import 'package:analyzer/src/generated/utilities_dart.dart'; | |
| 9 import 'package:analyzer/src/summary/base.dart'; | |
| 10 import 'package:analyzer/src/summary/idl.dart'; | |
| 11 | |
| 12 const int MAX_LINE_LENGTH = 80; | |
| 13 | |
| 14 /** | |
| 15 * Cache used to speed up [isEnum]. | |
| 16 */ | |
| 17 Map<Type, bool> _isEnumCache = <Type, bool>{}; | |
| 18 | |
| 19 /** | |
| 20 * Determine if the given [obj] has an enumerated type. | |
| 21 */ | |
| 22 bool isEnum(Object obj) { | |
| 23 return _isEnumCache.putIfAbsent( | |
| 24 obj.runtimeType, () => reflect(obj).type.isEnum); | |
| 25 } | |
| 26 | |
| 27 /** | |
| 28 * Decoded reprensentation of a part of a summary that occupies multiple lines | |
| 29 * of output. | |
| 30 */ | |
| 31 class BrokenEntity implements DecodedEntity { | |
| 32 final String opener; | |
| 33 final Map<String, DecodedEntity> parts; | |
| 34 final String closer; | |
| 35 | |
| 36 BrokenEntity(this.opener, this.parts, this.closer); | |
| 37 | |
| 38 @override | |
| 39 List<String> getLines() { | |
| 40 List<String> result = <String>[opener]; | |
| 41 bool first = true; | |
| 42 for (String key in parts.keys) { | |
| 43 if (first) { | |
| 44 first = false; | |
| 45 } else { | |
| 46 result[result.length - 1] += ','; | |
| 47 } | |
| 48 List<String> subResult = parts[key].getLines(); | |
| 49 subResult[0] = '$key: ${subResult[0]}'; | |
| 50 result.addAll(subResult.map((String s) => ' $s')); | |
| 51 } | |
| 52 result.add(closer); | |
| 53 return result; | |
| 54 } | |
| 55 } | |
| 56 | |
| 57 /** | |
| 58 * Decoded representation of a part of a summary. | |
| 59 */ | |
| 60 abstract class DecodedEntity { | |
| 61 /** | |
| 62 * Create a representation of a part of the summary that consists of a group | |
| 63 * of entities (represented by [parts]) contined between [opener] and | |
|
scheglov
2016/04/21 22:11:41
contined -> contained
Paul Berry
2016/04/21 22:31:08
Done.
| |
| 64 * [closers]. | |
|
scheglov
2016/04/21 22:11:41
[closers] -> [closer]
Paul Berry
2016/04/21 22:31:08
Done.
| |
| 65 * | |
| 66 * If [forceKeys] is `true`, the keys in [parts] will always be shown. If | |
| 67 * [forceKeys] is `false`, they keys will only be shown if the output is | |
| 68 * broken into multiple lines. | |
| 69 */ | |
| 70 factory DecodedEntity.group(String opener, Map<String, DecodedEntity> parts, | |
| 71 String closer, bool forceKeys) { | |
| 72 // Attempt to format the entity in a single line; if not bail out and | |
| 73 // construct a _BrokenEntity. | |
| 74 DecodedEntity bailout() => new BrokenEntity(opener, parts, closer); | |
| 75 String short = opener; | |
| 76 bool first = true; | |
| 77 for (String key in parts.keys) { | |
| 78 if (first) { | |
| 79 first = false; | |
| 80 } else { | |
| 81 short += ', '; | |
| 82 } | |
| 83 DecodedEntity value = parts[key]; | |
| 84 if (forceKeys) { | |
| 85 short += '$key: '; | |
| 86 } | |
| 87 if (value is UnbrokenEntity) { | |
| 88 short += value._s; | |
| 89 } else { | |
| 90 return bailout(); | |
| 91 } | |
| 92 if (short.length > MAX_LINE_LENGTH) { | |
| 93 return bailout(); | |
| 94 } | |
| 95 } | |
| 96 return new DecodedEntity.short(short + closer); | |
| 97 } | |
| 98 | |
| 99 /** | |
| 100 * Create a representation of a part of the summary that is represented by a | |
| 101 * single unbroken string. | |
| 102 */ | |
| 103 factory DecodedEntity.short(String s) = UnbrokenEntity; | |
| 104 | |
| 105 /** | |
| 106 * Format this entity into a sequence of strings (one per output line). | |
| 107 */ | |
| 108 List<String> getLines(); | |
| 109 } | |
| 110 | |
| 111 /** | |
| 112 * Wrapper around a [LinkedLibrary] and its constituent [UnlinkedUnit]s. | |
| 113 */ | |
| 114 class LibraryWrapper { | |
| 115 final LinkedLibrary _linked; | |
| 116 final List<UnlinkedUnit> _unlinked; | |
| 117 | |
| 118 LibraryWrapper(this._linked, this._unlinked); | |
| 119 } | |
| 120 | |
| 121 /** | |
| 122 * Wrapper around a [LinkedReference] and its corresponding [UnlinkedReference]. | |
| 123 */ | |
| 124 class ReferenceWrapper { | |
| 125 final LinkedReference _linked; | |
| 126 final UnlinkedReference _unlinked; | |
| 127 | |
| 128 ReferenceWrapper(this._linked, this._unlinked); | |
| 129 | |
| 130 String get name { | |
| 131 if (_linked != null && _linked.name.isNotEmpty) { | |
| 132 return _linked.name; | |
| 133 } else if (_unlinked != null && _unlinked.name.isNotEmpty) { | |
| 134 return _unlinked.name; | |
| 135 } else { | |
| 136 return '???'; | |
| 137 } | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 /** | |
| 142 * Instances of [SummaryInspector] are capable of traversing a summary and | |
| 143 * converting it to semi-human-readable output. | |
| 144 */ | |
| 145 class SummaryInspector { | |
| 146 /** | |
| 147 * The dependencies of the library currently being visited. | |
| 148 */ | |
| 149 List<LinkedDependency> _dependencies; | |
| 150 | |
| 151 /** | |
| 152 * The references of the unit currently being visited. | |
| 153 */ | |
| 154 List<ReferenceWrapper> _references; | |
| 155 | |
| 156 /** | |
| 157 * Decode the object [obj], which was reached by examining [key] inside | |
| 158 * another object. | |
| 159 */ | |
| 160 DecodedEntity decode(Object obj, String key) { | |
| 161 if (obj is PackageBundle) { | |
| 162 return decodePackageBundle(obj); | |
| 163 } | |
| 164 if (obj is LibraryWrapper) { | |
| 165 return decodeLibrary(obj); | |
| 166 } | |
| 167 if (obj is UnitWrapper) { | |
| 168 return decodeUnit(obj); | |
| 169 } | |
| 170 if (obj is ReferenceWrapper) { | |
| 171 return decodeReference(obj); | |
| 172 } | |
| 173 if (obj is DecodedEntity) { | |
| 174 return obj; | |
| 175 } | |
| 176 if (obj is SummaryClass) { | |
| 177 Map<String, Object> map = obj.toMap(); | |
| 178 return decodeMap(map); | |
| 179 } else if (obj is List) { | |
| 180 Map<String, DecodedEntity> parts = <String, DecodedEntity>{}; | |
| 181 for (int i = 0; i < obj.length; i++) { | |
| 182 parts[i.toString()] = decode(obj[i], key); | |
| 183 } | |
| 184 return new DecodedEntity.group('[', parts, ']', false); | |
| 185 } else if (obj is String) { | |
| 186 return new DecodedEntity.short(JSON.encode(obj)); | |
| 187 } else if (isEnum(obj)) { | |
| 188 return new DecodedEntity.short(obj.toString().split('.')[1]); | |
| 189 } else if (obj is int && | |
| 190 key == 'dependency' && | |
| 191 _dependencies != null && | |
| 192 obj < _dependencies.length) { | |
| 193 return new DecodedEntity.short('$obj (${_dependencies[obj].uri})'); | |
| 194 } else if (obj is int && | |
| 195 key == 'reference' && | |
| 196 _references != null && | |
| 197 obj < _references.length) { | |
| 198 return new DecodedEntity.short('$obj (${_references[obj].name})'); | |
| 199 } else { | |
| 200 return new DecodedEntity.short(obj.toString()); | |
| 201 } | |
| 202 } | |
| 203 | |
| 204 /** | |
| 205 * Decode the given [LibraryWrapper]. | |
| 206 */ | |
| 207 DecodedEntity decodeLibrary(LibraryWrapper obj) { | |
| 208 try { | |
| 209 LinkedLibrary linked = obj._linked; | |
| 210 List<UnlinkedUnit> unlinked = obj._unlinked; | |
| 211 _dependencies = linked.dependencies; | |
| 212 Map<String, Object> result = linked.toMap(); | |
| 213 result.remove('units'); | |
| 214 result['defining compilation unit'] = | |
| 215 new UnitWrapper(linked.units[0], unlinked[0]); | |
| 216 for (int i = 1; i < linked.units.length; i++) { | |
| 217 String partUri = unlinked[0].publicNamespace.parts[i - 1]; | |
| 218 result['part ${JSON.encode(partUri)}'] = | |
| 219 new UnitWrapper(linked.units[i], unlinked[i]); | |
| 220 } | |
| 221 return decodeMap(result); | |
| 222 } finally { | |
| 223 _dependencies = null; | |
| 224 } | |
| 225 } | |
| 226 | |
| 227 /** | |
| 228 * Decode the given [map]. | |
| 229 */ | |
| 230 DecodedEntity decodeMap(Map<String, Object> map) { | |
| 231 Map<String, DecodedEntity> parts = <String, DecodedEntity>{}; | |
| 232 map = reorderMap(map); | |
| 233 map.forEach((String key, Object value) { | |
| 234 if (value is String && value.isEmpty) { | |
| 235 return; | |
| 236 } | |
| 237 if (isEnum(value) && (value as dynamic).index == 0) { | |
| 238 return; | |
| 239 } | |
| 240 if (value is int && value == 0) { | |
| 241 return; | |
| 242 } | |
| 243 if (value is bool && value == false) { | |
| 244 return; | |
| 245 } | |
| 246 if (value == null) { | |
| 247 return; | |
| 248 } | |
| 249 if (value is List) { | |
| 250 if (value.isEmpty) { | |
| 251 return; | |
| 252 } | |
| 253 DecodedEntity entity = decode(value, key); | |
| 254 if (entity is BrokenEntity) { | |
| 255 for (int i = 0; i < value.length; i++) { | |
| 256 parts['$key[$i]'] = decode(value[i], key); | |
| 257 } | |
| 258 return; | |
| 259 } else { | |
| 260 parts[key] = entity; | |
| 261 } | |
| 262 } | |
| 263 parts[key] = decode(value, key); | |
| 264 }); | |
| 265 return new DecodedEntity.group('{', parts, '}', true); | |
| 266 } | |
| 267 | |
| 268 /** | |
| 269 * Decode the given [PackageBundle]. | |
| 270 */ | |
| 271 DecodedEntity decodePackageBundle(PackageBundle bundle) { | |
| 272 Map<String, UnlinkedUnit> units = <String, UnlinkedUnit>{}; | |
| 273 Set<String> seenUnits = new Set<String>(); | |
| 274 for (int i = 0; i < bundle.unlinkedUnits.length; i++) { | |
| 275 units[bundle.unlinkedUnitUris[i]] = bundle.unlinkedUnits[i]; | |
| 276 } | |
| 277 Map<String, Object> restOfMap = bundle.toMap(); | |
| 278 Map<String, Object> result = <String, Object>{}; | |
| 279 result['version'] = new DecodedEntity.short( | |
| 280 '${bundle.majorVersion}.${bundle.minorVersion}'); | |
| 281 restOfMap.remove('majorVersion'); | |
| 282 restOfMap.remove('minorVersion'); | |
| 283 for (int i = 0; i < bundle.linkedLibraries.length; i++) { | |
| 284 String libraryUriString = bundle.linkedLibraryUris[i]; | |
| 285 Uri libraryUri = Uri.parse(libraryUriString); | |
| 286 UnlinkedUnit unlinkedDefiningUnit = units[libraryUriString]; | |
| 287 seenUnits.add(libraryUriString); | |
| 288 List<UnlinkedUnit> libraryUnits = <UnlinkedUnit>[unlinkedDefiningUnit]; | |
| 289 LinkedLibrary linkedLibrary = bundle.linkedLibraries[i]; | |
| 290 for (int j = 1; j < linkedLibrary.units.length; j++) { | |
| 291 String partUriString = resolveRelativeUri(libraryUri, | |
| 292 Uri.parse(unlinkedDefiningUnit.publicNamespace.parts[j - 1])) | |
| 293 .toString(); | |
| 294 libraryUnits.add(units[partUriString]); | |
| 295 seenUnits.add(partUriString); | |
| 296 } | |
| 297 result['library ${JSON.encode(libraryUriString)}'] = | |
| 298 new LibraryWrapper(linkedLibrary, libraryUnits); | |
| 299 } | |
| 300 for (String uriString in units.keys) { | |
| 301 if (seenUnits.contains(uriString)) { | |
| 302 continue; | |
| 303 } | |
| 304 result['orphan unit ${JSON.encode(uriString)}'] = | |
| 305 new UnitWrapper(null, units[uriString]); | |
| 306 } | |
| 307 restOfMap.remove('linkedLibraries'); | |
| 308 restOfMap.remove('linkedLibraryUris'); | |
| 309 restOfMap.remove('unlinkedUnits'); | |
| 310 restOfMap.remove('unlinkedUnitUris'); | |
| 311 result.addAll(restOfMap); | |
| 312 return decodeMap(result); | |
| 313 } | |
| 314 | |
| 315 /** | |
| 316 * Decode the given [ReferenceWrapper]. | |
| 317 */ | |
| 318 DecodedEntity decodeReference(ReferenceWrapper obj) { | |
| 319 Map<String, Object> result = obj._unlinked != null | |
| 320 ? obj._unlinked.toMap() | |
| 321 : <String, Object>{'linkedOnly': true}; | |
| 322 if (obj._linked != null) { | |
| 323 mergeMaps(result, obj._linked.toMap()); | |
| 324 } | |
| 325 return decodeMap(result); | |
| 326 } | |
| 327 | |
| 328 /** | |
| 329 * Decode the given [UnitWrapper]. | |
| 330 */ | |
| 331 DecodedEntity decodeUnit(UnitWrapper obj) { | |
| 332 try { | |
| 333 LinkedUnit linked = obj._linked; | |
| 334 UnlinkedUnit unlinked = obj._unlinked; | |
| 335 Map<String, Object> unlinkedMap = unlinked.toMap(); | |
| 336 Map<String, Object> linkedMap = | |
| 337 linked != null ? linked.toMap() : <String, Object>{}; | |
| 338 Map<String, Object> result = <String, Object>{}; | |
| 339 List<ReferenceWrapper> references = <ReferenceWrapper>[]; | |
| 340 int numReferences = linked != null | |
| 341 ? linked.references.length | |
| 342 : unlinked.references.length; | |
| 343 for (int i = 0; i < numReferences; i++) { | |
| 344 references.add(new ReferenceWrapper( | |
| 345 linked != null ? linked.references[i] : null, | |
| 346 i < unlinked.references.length ? unlinked.references[i] : null)); | |
| 347 } | |
| 348 result['references'] = references; | |
| 349 _references = references; | |
| 350 unlinkedMap.remove('references'); | |
| 351 linkedMap.remove('references'); | |
| 352 linkedMap.forEach((String key, Object value) { | |
| 353 result['linked $key'] = value; | |
| 354 }); | |
| 355 unlinkedMap.forEach((String key, Object value) { | |
| 356 result[key] = value; | |
| 357 }); | |
| 358 return decodeMap(result); | |
| 359 } finally { | |
| 360 _references = null; | |
| 361 } | |
| 362 } | |
| 363 | |
| 364 /** | |
| 365 * Decode the given [PackageBundle] and dump it to a list of strings. | |
| 366 */ | |
| 367 List<String> dumpPackageBundle(PackageBundle bundle) { | |
| 368 DecodedEntity decoded = decode(bundle, 'PackageBundle'); | |
| 369 return decoded.getLines(); | |
| 370 } | |
| 371 | |
| 372 /** | |
| 373 * Merge the contents of [other] into [result], discarding empty entries. | |
| 374 */ | |
| 375 void mergeMaps(Map<String, Object> result, Map<String, Object> other) { | |
| 376 other.forEach((String key, Object value) { | |
| 377 if (value is String && value.isEmpty) { | |
| 378 return; | |
| 379 } | |
| 380 if (result.containsKey(key)) { | |
| 381 Object oldValue = result[key]; | |
| 382 if (oldValue is String && oldValue.isEmpty) { | |
| 383 result[key] = value; | |
| 384 } else { | |
| 385 throw new Exception( | |
| 386 'Duplicate values for $key: $oldValue and $value'); | |
| 387 } | |
| 388 } else { | |
| 389 result[key] = value; | |
| 390 } | |
| 391 }); | |
| 392 } | |
| 393 | |
| 394 /** | |
| 395 * Reorder [map] for more intuitive display. | |
| 396 */ | |
| 397 Map<String, Object> reorderMap(Map<String, Object> map) { | |
| 398 Map<String, Object> result = <String, Object>{}; | |
| 399 if (map.containsKey('name')) { | |
| 400 result['name'] = map['name']; | |
| 401 } | |
| 402 result.addAll(map); | |
| 403 return result; | |
| 404 } | |
| 405 } | |
| 406 | |
| 407 /** | |
| 408 * Decoded reprensentation of a part of a summary that occupies a single line of | |
| 409 * output. | |
| 410 */ | |
| 411 class UnbrokenEntity implements DecodedEntity { | |
| 412 final String _s; | |
| 413 | |
| 414 UnbrokenEntity(this._s); | |
| 415 | |
| 416 @override | |
| 417 List<String> getLines() => <String>[_s]; | |
| 418 } | |
| 419 | |
| 420 /** | |
| 421 * Wrapper around a [LinkedUnit] and its corresponding [UnlinkedUnit]. | |
| 422 */ | |
| 423 class UnitWrapper { | |
| 424 final LinkedUnit _linked; | |
| 425 final UnlinkedUnit _unlinked; | |
| 426 | |
| 427 UnitWrapper(this._linked, this._unlinked); | |
| 428 } | |
| OLD | NEW |