| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2014, 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 library docgen.model_helpers; | |
| 6 | |
| 7 import 'dart:collection'; | |
| 8 | |
| 9 import 'package:compiler/src/constants/expressions.dart'; | |
| 10 | |
| 11 import '../exports/dart2js_mirrors.dart' as dart2js_mirrors; | |
| 12 import '../exports/mirrors_util.dart' as dart2js_util; | |
| 13 import '../exports/source_mirrors.dart'; | |
| 14 import '../exports/libraries.dart'; | |
| 15 | |
| 16 import '../library_helpers.dart' show includePrivateMembers; | |
| 17 import '../package_helpers.dart'; | |
| 18 | |
| 19 import 'annotation.dart'; | |
| 20 import 'generic.dart'; | |
| 21 import 'indexable.dart'; | |
| 22 import 'library.dart'; | |
| 23 import 'method.dart'; | |
| 24 import 'parameter.dart'; | |
| 25 import 'variable.dart'; | |
| 26 | |
| 27 String getLibraryDocName(LibraryMirror mirror) { | |
| 28 var dotsFixed = dart2js_util.qualifiedNameOf(mirror).replaceAll('.', '-'); | |
| 29 if (dotsFixed.startsWith('dart-dom-')) { | |
| 30 return dotsFixed.replaceFirst("dart-dom-", "dart:"); | |
| 31 } else if (dotsFixed.startsWith("dart-")) { | |
| 32 return dotsFixed.replaceFirst("dart-", "dart:"); | |
| 33 } else { | |
| 34 return dotsFixed; | |
| 35 } | |
| 36 } | |
| 37 | |
| 38 /// Expand the method map [mapToExpand] into a more detailed map that | |
| 39 /// separates out setters, getters, constructors, operators, and methods. | |
| 40 Map expandMethodMap(Map<String, Method> mapToExpand) => { | |
| 41 'setters': recurseMap(_filterMap(mapToExpand, | |
| 42 (key, val) => val.mirror.isSetter)), | |
| 43 'getters': recurseMap(_filterMap(mapToExpand, | |
| 44 (key, val) => val.mirror.isGetter)), | |
| 45 'constructors': recurseMap(_filterMap(mapToExpand, | |
| 46 (key, val) => val.mirror.isConstructor)), | |
| 47 'operators': recurseMap(_filterMap(mapToExpand, | |
| 48 (key, val) => val.mirror.isOperator)), | |
| 49 'methods': recurseMap(_filterMap(mapToExpand, | |
| 50 (key, val) => val.mirror.isRegularMethod && !val.mirror.isOperator)) | |
| 51 }; | |
| 52 | |
| 53 String getDefaultValue(ParameterMirror mirror) { | |
| 54 if (!mirror.hasDefaultValue) return null; | |
| 55 return '${mirror.defaultValue}'; | |
| 56 } | |
| 57 | |
| 58 /// Returns a list of meta annotations assocated with a mirror. | |
| 59 List<Annotation> createAnnotations(DeclarationMirror mirror, | |
| 60 Library owningLibrary) { | |
| 61 var annotations = []; | |
| 62 var info = new AnnotationInfo(mirror, owningLibrary); | |
| 63 for (var expr in dart2js_mirrors.BackDoor.metadataSyntaxOf(mirror)) { | |
| 64 var docgenAnnotation = expr.accept(const AnnotationCreator(), info); | |
| 65 if (docgenAnnotation != null && | |
| 66 !_SKIPPED_ANNOTATIONS.contains( | |
| 67 dart2js_util.qualifiedNameOf(docgenAnnotation.mirror))) { | |
| 68 annotations.add(docgenAnnotation); | |
| 69 } | |
| 70 } | |
| 71 return annotations; | |
| 72 } | |
| 73 | |
| 74 class AnnotationInfo { | |
| 75 final Mirror mirror; | |
| 76 final Library owningLibrary; | |
| 77 | |
| 78 AnnotationInfo(this.mirror, this.owningLibrary); | |
| 79 } | |
| 80 | |
| 81 class AnnotationCreator | |
| 82 extends ConstantExpressionVisitor<Annotation, AnnotationInfo> { | |
| 83 | |
| 84 const AnnotationCreator(); | |
| 85 | |
| 86 Annotation createAnnotation(var element, | |
| 87 AnnotationInfo context, | |
| 88 [List<String> parameters = const <String>[]]) { | |
| 89 var mirror = | |
| 90 dart2js_mirrors.BackDoor.getMirrorFromElement(context.mirror, element); | |
| 91 if (mirror != null) { | |
| 92 return new Annotation(context.owningLibrary, mirror, parameters); | |
| 93 } | |
| 94 return null; | |
| 95 } | |
| 96 | |
| 97 @override | |
| 98 Annotation visitBinary(BinaryConstantExpression exp, | |
| 99 AnnotationInfo context) { | |
| 100 return null; | |
| 101 } | |
| 102 | |
| 103 @override | |
| 104 Annotation visitIdentical(IdenticalConstantExpression exp, | |
| 105 AnnotationInfo context) { | |
| 106 return null; | |
| 107 } | |
| 108 | |
| 109 @override | |
| 110 Annotation visitConcatenate(ConcatenateConstantExpression exp, | |
| 111 AnnotationInfo context) { | |
| 112 return null; | |
| 113 } | |
| 114 | |
| 115 @override | |
| 116 Annotation visitConditional(ConditionalConstantExpression exp, | |
| 117 AnnotationInfo context) { | |
| 118 return null; | |
| 119 } | |
| 120 | |
| 121 @override | |
| 122 Annotation visitConstructed(ConstructedConstantExpression exp, | |
| 123 AnnotationInfo context) { | |
| 124 return createAnnotation(exp.target, context, | |
| 125 exp.arguments.map((a) => a.getText()).toList()); | |
| 126 } | |
| 127 | |
| 128 @override | |
| 129 Annotation visitFunction(FunctionConstantExpression exp, | |
| 130 AnnotationInfo context) { | |
| 131 return createAnnotation(exp.element, context); | |
| 132 } | |
| 133 | |
| 134 @override | |
| 135 Annotation visitList(ListConstantExpression exp, | |
| 136 AnnotationInfo context) { | |
| 137 return null; | |
| 138 } | |
| 139 | |
| 140 @override | |
| 141 Annotation visitMap(MapConstantExpression exp, | |
| 142 AnnotationInfo context) { | |
| 143 return null; | |
| 144 } | |
| 145 | |
| 146 @override | |
| 147 Annotation visitSymbol(SymbolConstantExpression exp, | |
| 148 AnnotationInfo context) { | |
| 149 return null; | |
| 150 } | |
| 151 | |
| 152 @override | |
| 153 Annotation visitType(TypeConstantExpression exp, | |
| 154 AnnotationInfo context) { | |
| 155 return null; | |
| 156 } | |
| 157 | |
| 158 @override | |
| 159 Annotation visitUnary(UnaryConstantExpression exp, | |
| 160 AnnotationInfo context) { | |
| 161 return null; | |
| 162 } | |
| 163 | |
| 164 @override | |
| 165 Annotation visitVariable(VariableConstantExpression exp, | |
| 166 AnnotationInfo context) { | |
| 167 return createAnnotation(exp.element, context); | |
| 168 } | |
| 169 | |
| 170 @override | |
| 171 Annotation visitBool(BoolConstantExpression exp, | |
| 172 AnnotationInfo context) { | |
| 173 return null; | |
| 174 } | |
| 175 | |
| 176 @override | |
| 177 Annotation visitBoolFromEnvironment(BoolFromEnvironmentConstantExpression exp, | |
| 178 AnnotationInfo context) { | |
| 179 return null; | |
| 180 } | |
| 181 | |
| 182 @override | |
| 183 Annotation visitDouble(DoubleConstantExpression exp, | |
| 184 AnnotationInfo context) { | |
| 185 return null; | |
| 186 } | |
| 187 | |
| 188 @override | |
| 189 Annotation visitInt(IntConstantExpression exp, | |
| 190 AnnotationInfo context) { | |
| 191 return null; | |
| 192 } | |
| 193 | |
| 194 @override | |
| 195 Annotation visitIntFromEnvironment(IntFromEnvironmentConstantExpression exp, | |
| 196 AnnotationInfo context) { | |
| 197 return null; | |
| 198 } | |
| 199 | |
| 200 @override | |
| 201 Annotation visitNamed(NamedArgumentReference exp, | |
| 202 AnnotationInfo context) { | |
| 203 return null; | |
| 204 } | |
| 205 | |
| 206 @override | |
| 207 Annotation visitNull(NullConstantExpression exp, | |
| 208 AnnotationInfo context) { | |
| 209 return null; | |
| 210 } | |
| 211 | |
| 212 @override | |
| 213 Annotation visitPositional(PositionalArgumentReference exp, | |
| 214 AnnotationInfo context) { | |
| 215 return null; | |
| 216 } | |
| 217 | |
| 218 @override | |
| 219 Annotation visitString(StringConstantExpression exp, | |
| 220 AnnotationInfo context) { | |
| 221 return null; | |
| 222 } | |
| 223 | |
| 224 @override | |
| 225 Annotation visitStringFromEnvironment( | |
| 226 StringFromEnvironmentConstantExpression exp, | |
| 227 AnnotationInfo context) { | |
| 228 return null; | |
| 229 } | |
| 230 | |
| 231 @override | |
| 232 Annotation visitStringLength(StringLengthConstantExpression exp, | |
| 233 AnnotationInfo context) { | |
| 234 return null; | |
| 235 } | |
| 236 | |
| 237 @override | |
| 238 Annotation visitDeferred(DeferredConstantExpression exp, | |
| 239 AnnotationInfo context) { | |
| 240 return exp.expression.accept(this, context); | |
| 241 } | |
| 242 } | |
| 243 | |
| 244 /// A declaration is private if itself is private, or the owner is private. | |
| 245 bool isHidden(DeclarationSourceMirror mirror) { | |
| 246 if (mirror is LibraryMirror) { | |
| 247 return _isLibraryPrivate(mirror); | |
| 248 } else if (mirror.owner is LibraryMirror) { | |
| 249 return (mirror.isPrivate || _isLibraryPrivate(mirror.owner) || | |
| 250 mirror.isNameSynthetic); | |
| 251 } else { | |
| 252 return (mirror.isPrivate || isHidden(mirror.owner) || | |
| 253 mirror.isNameSynthetic); | |
| 254 } | |
| 255 } | |
| 256 | |
| 257 /// Transforms the map by calling toMap on each value in it. | |
| 258 Map recurseMap(Map inputMap) { | |
| 259 var outputMap = new LinkedHashMap(); | |
| 260 inputMap.forEach((key, value) { | |
| 261 if (value is Map) { | |
| 262 outputMap[key] = recurseMap(value); | |
| 263 } else { | |
| 264 outputMap[key] = value.toMap(); | |
| 265 } | |
| 266 }); | |
| 267 return outputMap; | |
| 268 } | |
| 269 | |
| 270 /// Read a pubspec and return the library name given a [LibraryMirror]. | |
| 271 String getPackageName(LibraryMirror mirror) { | |
| 272 if (mirror.uri.scheme != 'file') return ''; | |
| 273 var rootdir = getPackageDirectory(mirror); | |
| 274 if (rootdir == null) return ''; | |
| 275 return packageNameFor(rootdir); | |
| 276 } | |
| 277 | |
| 278 | |
| 279 /// Helper that maps [mirrors] to their simple name in map. | |
| 280 Map addAll(Map map, Iterable<DeclarationMirror> mirrors) { | |
| 281 for (var mirror in mirrors) { | |
| 282 map[dart2js_util.nameOf(mirror)] = mirror; | |
| 283 } | |
| 284 return map; | |
| 285 } | |
| 286 | |
| 287 /// For the given library determine what items (if any) are exported. | |
| 288 /// | |
| 289 /// Returns a Map with three keys: "classes", "methods", and "variables" the | |
| 290 /// values of which point to a map of exported name identifiers with values | |
| 291 /// corresponding to the actual DeclarationMirror. | |
| 292 Map<String, Map<String, DeclarationMirror>> calcExportedItems( | |
| 293 LibrarySourceMirror library, Map visited) { | |
| 294 var exports = {}; | |
| 295 visited[library] = exports; | |
| 296 exports['classes'] = new SplayTreeMap(); | |
| 297 exports['methods'] = new SplayTreeMap(); | |
| 298 exports['variables'] = new SplayTreeMap(); | |
| 299 | |
| 300 // Determine the classes, variables and methods that are exported for a | |
| 301 // specific dependency. | |
| 302 void _populateExports(LibraryDependencyMirror export, bool showExport) { | |
| 303 if (visited[export.targetLibrary] != null) return; | |
| 304 var transitiveExports = calcExportedItems(export.targetLibrary, visited); | |
| 305 exports['classes'].addAll(transitiveExports['classes']); | |
| 306 exports['methods'].addAll(transitiveExports['methods']); | |
| 307 exports['variables'].addAll(transitiveExports['variables']); | |
| 308 // If there is a show in the export, add only the show items to the | |
| 309 // library. Ex: "export foo show bar" | |
| 310 // Otherwise, add all items, and then remove the hidden ones. | |
| 311 // Ex: "export foo hide bar" | |
| 312 | |
| 313 if (!showExport) { | |
| 314 // Add all items, and then remove the hidden ones. | |
| 315 // Ex: "export foo hide bar" | |
| 316 addAll(exports['classes'], | |
| 317 dart2js_util.typesOf(export.targetLibrary.declarations)); | |
| 318 addAll(exports['methods'], | |
| 319 export.targetLibrary.declarations.values.where( | |
| 320 (mirror) => mirror is MethodMirror)); | |
| 321 addAll(exports['variables'], | |
| 322 dart2js_util.variablesOf(export.targetLibrary.declarations)); | |
| 323 } | |
| 324 for (CombinatorMirror combinator in export.combinators) { | |
| 325 for (String identifier in combinator.identifiers) { | |
| 326 var librarySourceMirror = | |
| 327 export.targetLibrary as DeclarationSourceMirror; | |
| 328 var declaration = librarySourceMirror.lookupInScope(identifier); | |
| 329 if (declaration == null) { | |
| 330 // Technically this should be a bug, but some of our packages | |
| 331 // (such as the polymer package) are curently broken in this | |
| 332 // way, so we just produce a warning. | |
| 333 print('Warning identifier $identifier not found in library ' | |
| 334 '${dart2js_util.qualifiedNameOf(export.targetLibrary)}'); | |
| 335 } else { | |
| 336 var subMap = exports['classes']; | |
| 337 if (declaration is MethodMirror) { | |
| 338 subMap = exports['methods']; | |
| 339 } else if (declaration is VariableMirror) { | |
| 340 subMap = exports['variables']; | |
| 341 } | |
| 342 if (showExport) { | |
| 343 subMap[identifier] = declaration; | |
| 344 } else { | |
| 345 subMap.remove(identifier); | |
| 346 } | |
| 347 } | |
| 348 } | |
| 349 } | |
| 350 } | |
| 351 | |
| 352 Iterable<LibraryDependencyMirror> exportList = | |
| 353 library.libraryDependencies.where((lib) => lib.isExport); | |
| 354 for (LibraryDependencyMirror export in exportList) { | |
| 355 _populateExports(export, | |
| 356 export.combinators.any((combinator) => combinator.isShow)); | |
| 357 } | |
| 358 return exports; | |
| 359 } | |
| 360 | |
| 361 | |
| 362 /// Returns a map of [Variable] objects constructed from [mirrorMap]. | |
| 363 /// The optional parameter [containingLibrary] is contains data for variables | |
| 364 /// defined at the top level of a library (potentially for exporting | |
| 365 /// purposes). | |
| 366 Map<String, Variable> createVariables(Iterable<VariableMirror> mirrors, | |
| 367 Indexable owner) { | |
| 368 var data = new SplayTreeMap<String, Variable>(); | |
| 369 // TODO(janicejl): When map to map feature is created, replace the below | |
| 370 // with a filter. Issue(#9590). | |
| 371 mirrors.forEach((dart2js_mirrors.Dart2JsFieldMirror mirror) { | |
| 372 if (includePrivateMembers || !isHidden(mirror)) { | |
| 373 var mirrorName = dart2js_util.nameOf(mirror); | |
| 374 data[mirrorName] = new Variable(mirrorName, mirror, owner); | |
| 375 } | |
| 376 }); | |
| 377 return data; | |
| 378 } | |
| 379 | |
| 380 /// Returns a map of [Method] objects constructed from [mirrorMap]. | |
| 381 /// The optional parameter [containingLibrary] is contains data for variables | |
| 382 /// defined at the top level of a library (potentially for exporting | |
| 383 /// purposes). | |
| 384 Map<String, Method> createMethods(Iterable<MethodMirror> mirrors, | |
| 385 Indexable owner) { | |
| 386 var group = new SplayTreeMap<String, Method>(); | |
| 387 mirrors.forEach((MethodMirror mirror) { | |
| 388 if (includePrivateMembers || !mirror.isPrivate) { | |
| 389 group[dart2js_util.nameOf(mirror)] = new Method(mirror, owner); | |
| 390 } | |
| 391 }); | |
| 392 return group; | |
| 393 } | |
| 394 | |
| 395 /// Returns a map of [Parameter] objects constructed from [mirrorList]. | |
| 396 Map<String, Parameter> createParameters(List<ParameterMirror> mirrorList, | |
| 397 Indexable owner) { | |
| 398 var data = {}; | |
| 399 mirrorList.forEach((ParameterMirror mirror) { | |
| 400 data[dart2js_util.nameOf(mirror)] = | |
| 401 new Parameter(mirror, owner.owningLibrary); | |
| 402 }); | |
| 403 return data; | |
| 404 } | |
| 405 | |
| 406 /// Returns a map of [Generic] objects constructed from the class mirror. | |
| 407 Map<String, Generic> createGenerics(TypeMirror mirror) { | |
| 408 return new Map.fromIterable(mirror.typeVariables, | |
| 409 key: (e) => dart2js_util.nameOf(e), | |
| 410 value: (e) => new Generic(e)); | |
| 411 } | |
| 412 | |
| 413 Map _filterMap(Map map, bool test(k, v)) { | |
| 414 var exported = new SplayTreeMap(); | |
| 415 map.forEach((key, value) { | |
| 416 if (test(key, value)) exported[key] = value; | |
| 417 }); | |
| 418 return exported; | |
| 419 } | |
| 420 | |
| 421 /// Annotations that we do not display in the viewer. | |
| 422 const List<String> _SKIPPED_ANNOTATIONS = const [ | |
| 423 'metadata.DocsEditable', '_js_helper.JSName', '_js_helper.Creates', | |
| 424 '_js_helper.Returns' | |
| 425 ]; | |
| 426 | |
| 427 /// Returns true if a library name starts with an underscore, and false | |
| 428 /// otherwise. | |
| 429 /// | |
| 430 /// An example that starts with _ is _js_helper. | |
| 431 /// An example that contains ._ is dart._collection.dev | |
| 432 bool _isLibraryPrivate(dart2js_mirrors.Dart2JsLibraryMirror mirror) { | |
| 433 // This method is needed because LibraryMirror.isPrivate returns `false` all | |
| 434 // the time. | |
| 435 var sdkLibrary = LIBRARIES[dart2js_util.nameOf(mirror)]; | |
| 436 if (sdkLibrary != null) { | |
| 437 return !sdkLibrary.documented; | |
| 438 } else if (dart2js_util.nameOf(mirror).startsWith('_') || dart2js_util.nameOf( | |
| 439 mirror).contains('._')) { | |
| 440 return true; | |
| 441 } | |
| 442 return false; | |
| 443 } | |
| OLD | NEW |