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 |