OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, 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 dart2js.mirrors; | |
6 | |
7 import 'dart:collection' show UnmodifiableListView, UnmodifiableMapView; | |
8 | |
9 import '../common.dart'; | |
10 import '../compiler.dart' show Compiler; | |
11 import '../constants/expressions.dart'; | |
12 import '../constants/values.dart'; | |
13 import '../dart_types.dart'; | |
14 import '../elements/elements.dart'; | |
15 import '../elements/modelx.dart' show LibraryElementX; | |
16 import '../resolution/scope.dart' show Scope; | |
17 import '../script.dart'; | |
18 import '../tokens/token.dart'; | |
19 import '../tokens/token_constants.dart' as Tokens; | |
20 import '../tree/tree.dart'; | |
21 import '../util/characters.dart' show $CR, $LF; | |
22 import '../util/util.dart' show Link; | |
23 import 'mirrors_util.dart'; | |
24 import 'source_mirrors.dart'; | |
25 | |
26 part 'dart2js_instance_mirrors.dart'; | |
27 part 'dart2js_library_mirror.dart'; | |
28 part 'dart2js_member_mirrors.dart'; | |
29 part 'dart2js_type_mirrors.dart'; | |
30 | |
31 //------------------------------------------------------------------------------ | |
32 // Utility types and functions for the dart2js mirror system | |
33 //------------------------------------------------------------------------------ | |
34 | |
35 bool _includeLibrary(Dart2JsLibraryMirror mirror) { | |
36 return const bool.fromEnvironment("list_all_libraries") || | |
37 !mirror._element.isInternalLibrary; | |
38 } | |
39 | |
40 bool _isPrivate(String name) { | |
41 return name.startsWith('_'); | |
42 } | |
43 | |
44 List<ParameterMirror> _parametersFromFunctionSignature( | |
45 Dart2JsDeclarationMirror owner, FunctionSignature signature) { | |
46 var parameters = <ParameterMirror>[]; | |
47 signature.requiredParameters.forEach((FormalElement parameter) { | |
48 parameters.add(new Dart2JsParameterMirror(owner, parameter, | |
49 isOptional: false, isNamed: false)); | |
50 }); | |
51 bool isNamed = signature.optionalParametersAreNamed; | |
52 signature.optionalParameters.forEach((FormalElement parameter) { | |
53 parameters.add(new Dart2JsParameterMirror(owner, parameter, | |
54 isOptional: true, isNamed: isNamed)); | |
55 }); | |
56 return parameters; | |
57 } | |
58 | |
59 MethodMirror _convertElementMethodToMethodMirror( | |
60 Dart2JsDeclarationMirror library, Element element) { | |
61 if (element is FunctionElement) { | |
62 return new Dart2JsMethodMirror(library, element); | |
63 } else { | |
64 return null; | |
65 } | |
66 } | |
67 | |
68 //------------------------------------------------------------------------------ | |
69 // Dart2Js specific extensions of mirror interfaces | |
70 //------------------------------------------------------------------------------ | |
71 | |
72 abstract class Dart2JsMirror implements Mirror { | |
73 Dart2JsMirrorSystem get mirrorSystem; | |
74 } | |
75 | |
76 abstract class Dart2JsDeclarationMirror extends Dart2JsMirror | |
77 implements DeclarationSourceMirror { | |
78 bool get isTopLevel => owner != null && owner is LibraryMirror; | |
79 | |
80 bool get isPrivate => _isPrivate(_simpleNameString); | |
81 | |
82 String get _simpleNameString; | |
83 | |
84 String get _qualifiedNameString { | |
85 var parent = owner; | |
86 if (parent is Dart2JsDeclarationMirror) { | |
87 return '${parent._qualifiedNameString}.${_simpleNameString}'; | |
88 } | |
89 assert(parent == null); | |
90 return _simpleNameString; | |
91 } | |
92 | |
93 Symbol get simpleName => symbolOf(_simpleNameString, getLibrary(this)); | |
94 | |
95 Symbol get qualifiedName => symbolOf(_qualifiedNameString, getLibrary(this)); | |
96 | |
97 DeclarationMirror lookupInScope(String name) => null; | |
98 | |
99 bool get isNameSynthetic => false; | |
100 | |
101 /// Returns the type mirror for [type] in the context of this declaration. | |
102 TypeMirror _getTypeMirror(DartType type) { | |
103 return mirrorSystem._convertTypeToTypeMirror(type); | |
104 } | |
105 | |
106 /// Returns a list of the declaration mirrorSystem for [element]. | |
107 Iterable<Dart2JsMemberMirror> _getDeclarationMirrors(Element element) { | |
108 if (element.isSynthesized) { | |
109 return const <Dart2JsMemberMirror>[]; | |
110 } else if (element is VariableElement) { | |
111 return <Dart2JsMemberMirror>[new Dart2JsFieldMirror(this, element)]; | |
112 } else if (element is FunctionElement) { | |
113 return <Dart2JsMemberMirror>[new Dart2JsMethodMirror(this, element)]; | |
114 } else if (element is AbstractFieldElement) { | |
115 var members = <Dart2JsMemberMirror>[]; | |
116 AbstractFieldElement field = element; | |
117 if (field.getter != null) { | |
118 members.add(new Dart2JsMethodMirror(this, field.getter)); | |
119 } | |
120 if (field.setter != null) { | |
121 members.add(new Dart2JsMethodMirror(this, field.setter)); | |
122 } | |
123 return members; | |
124 } | |
125 mirrorSystem.compiler.reporter.internalError( | |
126 element, "Unexpected member type $element ${element.kind}."); | |
127 return null; | |
128 } | |
129 } | |
130 | |
131 abstract class Dart2JsElementMirror extends Dart2JsDeclarationMirror { | |
132 final Dart2JsMirrorSystem mirrorSystem; | |
133 final Element _element; | |
134 List<InstanceMirror> _metadata; | |
135 | |
136 Dart2JsElementMirror(this.mirrorSystem, this._element) { | |
137 assert(mirrorSystem != null); | |
138 assert(_element != null); | |
139 } | |
140 | |
141 String get _simpleNameString => _element.name; | |
142 | |
143 /** | |
144 * Computes the first token for this declaration using the begin token of the | |
145 * element node or element position as indicator. | |
146 */ | |
147 Token getBeginToken() { | |
148 Element element = _element; | |
149 if (element is AstElement) { | |
150 Node node = element.node; | |
151 if (node != null) { | |
152 return node.getBeginToken(); | |
153 } | |
154 } | |
155 return element.position; | |
156 } | |
157 | |
158 /** | |
159 * Computes the last token for this declaration using the end token of the | |
160 * element node or element position as indicator. | |
161 */ | |
162 Token getEndToken() { | |
163 Element element = _element; | |
164 if (element is AstElement) { | |
165 Node node = element.node; | |
166 if (node != null) { | |
167 return node.getEndToken(); | |
168 } | |
169 } | |
170 return element.position; | |
171 } | |
172 | |
173 /** | |
174 * Returns the first token for the source of this declaration, including | |
175 * metadata annotations. | |
176 */ | |
177 Token getFirstToken() { | |
178 if (!_element.metadata.isEmpty) { | |
179 for (MetadataAnnotation metadata in _element.metadata) { | |
180 if (metadata.beginToken != null) { | |
181 return metadata.beginToken; | |
182 } | |
183 } | |
184 } | |
185 return getBeginToken(); | |
186 } | |
187 | |
188 Script getScript() => _element.compilationUnit.script; | |
189 | |
190 SourceLocation get location { | |
191 Token beginToken = getFirstToken(); | |
192 Script script = getScript(); | |
193 SourceSpan span; | |
194 if (beginToken == null) { | |
195 span = new SourceSpan(script.resourceUri, 0, 0); | |
196 } else { | |
197 Token endToken = getEndToken(); | |
198 span = | |
199 new SourceSpan.fromTokens(script.resourceUri, beginToken, endToken); | |
200 } | |
201 return new Dart2JsSourceLocation(script, span); | |
202 } | |
203 | |
204 String toString() => _element.toString(); | |
205 | |
206 void _appendCommentTokens(Token commentToken) { | |
207 while (commentToken != null && commentToken.kind == Tokens.COMMENT_TOKEN) { | |
208 _metadata.add( | |
209 new Dart2JsCommentInstanceMirror(mirrorSystem, commentToken.value)); | |
210 commentToken = commentToken.next; | |
211 } | |
212 } | |
213 | |
214 List<InstanceMirror> get metadata { | |
215 if (_metadata == null) { | |
216 _metadata = <InstanceMirror>[]; | |
217 for (MetadataAnnotation metadata in _element.metadata) { | |
218 _appendCommentTokens( | |
219 mirrorSystem.compiler.commentMap[metadata.beginToken]); | |
220 metadata.ensureResolved(mirrorSystem.compiler.resolution); | |
221 _metadata.add(_convertConstantToInstanceMirror( | |
222 mirrorSystem, | |
223 metadata.constant, | |
224 mirrorSystem.compiler.constants | |
225 .getConstantValue(metadata.constant))); | |
226 } | |
227 _appendCommentTokens(mirrorSystem.compiler.commentMap[getBeginToken()]); | |
228 } | |
229 // TODO(johnniwinther): Return an unmodifiable list instead. | |
230 return new List<InstanceMirror>.from(_metadata); | |
231 } | |
232 | |
233 DeclarationMirror lookupInScope(String name) { | |
234 // TODO(11653): Support lookup of constructors. | |
235 Scope scope = _element.buildScope(); | |
236 Element result; | |
237 int index = name.indexOf('.'); | |
238 if (index != -1) { | |
239 // Lookup [: prefix.id :]. | |
240 String prefix = name.substring(0, index); | |
241 String id = name.substring(index + 1); | |
242 result = scope.lookup(prefix); | |
243 if (result != null && result.isPrefix) { | |
244 PrefixElement prefix = result; | |
245 result = prefix.lookupLocalMember(id); | |
246 } else { | |
247 result = null; | |
248 } | |
249 } else { | |
250 // Lookup [: id :]. | |
251 result = scope.lookup(name); | |
252 } | |
253 if (result == null || result.isPrefix) return null; | |
254 return _convertElementToDeclarationMirror(mirrorSystem, result); | |
255 } | |
256 | |
257 bool operator ==(var other) { | |
258 if (identical(this, other)) return true; | |
259 if (other == null) return false; | |
260 if (other is! Dart2JsElementMirror) return false; | |
261 return _element == other._element && owner == other.owner; | |
262 } | |
263 | |
264 int get hashCode { | |
265 return 13 * _element.hashCode + 17 * owner.hashCode; | |
266 } | |
267 } | |
268 | |
269 //------------------------------------------------------------------------------ | |
270 // Mirror system implementation. | |
271 //------------------------------------------------------------------------------ | |
272 | |
273 class Dart2JsMirrorSystem extends MirrorSystem { | |
274 final Compiler compiler; | |
275 Map<LibraryElement, Dart2JsLibraryMirror> _libraryMap; | |
276 UnmodifiableMapView<Uri, LibraryMirror> _filteredLibraries; | |
277 | |
278 Dart2JsMirrorSystem(this.compiler); | |
279 | |
280 IsolateMirror get isolate => null; | |
281 | |
282 void _ensureLibraries() { | |
283 if (_filteredLibraries == null) { | |
284 var filteredLibs = new Map<Uri, LibraryMirror>(); | |
285 _libraryMap = new Map<LibraryElement, Dart2JsLibraryMirror>(); | |
286 compiler.libraryLoader.libraries.forEach((LibraryElement v) { | |
287 var mirror = new Dart2JsLibraryMirror(mirrorSystem, v); | |
288 if (_includeLibrary(mirror)) { | |
289 filteredLibs[mirror.uri] = mirror; | |
290 } | |
291 _libraryMap[v] = mirror; | |
292 }); | |
293 | |
294 _filteredLibraries = | |
295 new UnmodifiableMapView<Uri, LibraryMirror>(filteredLibs); | |
296 } | |
297 } | |
298 | |
299 Map<Uri, LibraryMirror> get libraries { | |
300 _ensureLibraries(); | |
301 return _filteredLibraries; | |
302 } | |
303 | |
304 Dart2JsLibraryMirror _getLibrary(LibraryElement element) => | |
305 _libraryMap[element]; | |
306 | |
307 Dart2JsMirrorSystem get mirrorSystem => this; | |
308 | |
309 TypeMirror get dynamicType => _convertTypeToTypeMirror(const DynamicType()); | |
310 | |
311 TypeMirror get voidType => _convertTypeToTypeMirror(const VoidType()); | |
312 | |
313 TypeMirror _convertTypeToTypeMirror(DartType type) { | |
314 assert(type != null); | |
315 if (type.treatAsDynamic) { | |
316 return new Dart2JsDynamicMirror(this, type); | |
317 } else if (type is InterfaceType) { | |
318 if (type.typeArguments.isEmpty) { | |
319 return _getTypeDeclarationMirror(type.element); | |
320 } else { | |
321 return new Dart2JsInterfaceTypeMirror(this, type); | |
322 } | |
323 } else if (type is TypeVariableType) { | |
324 return new Dart2JsTypeVariableMirror(this, type); | |
325 } else if (type is FunctionType) { | |
326 return new Dart2JsFunctionTypeMirror(this, type); | |
327 } else if (type is VoidType) { | |
328 return new Dart2JsVoidMirror(this, type); | |
329 } else if (type is TypedefType) { | |
330 if (type.typeArguments.isEmpty) { | |
331 return _getTypeDeclarationMirror(type.element); | |
332 } else { | |
333 return new Dart2JsTypedefMirror(this, type); | |
334 } | |
335 } | |
336 compiler.reporter.internalError( | |
337 type.element, "Unexpected type $type of kind ${type.kind}."); | |
338 return null; | |
339 } | |
340 | |
341 DeclarationMirror _getTypeDeclarationMirror(TypeDeclarationElement element) { | |
342 if (element.isClass) { | |
343 return new Dart2JsClassDeclarationMirror(this, element.thisType); | |
344 } else if (element.isTypedef) { | |
345 return new Dart2JsTypedefDeclarationMirror(this, element.thisType); | |
346 } | |
347 compiler.reporter.internalError(element, "Unexpected element $element."); | |
348 return null; | |
349 } | |
350 } | |
351 | |
352 abstract class ContainerMixin { | |
353 UnmodifiableMapView<Symbol, DeclarationMirror> _declarations; | |
354 | |
355 void _ensureDeclarations() { | |
356 if (_declarations == null) { | |
357 var declarations = <Symbol, DeclarationMirror>{}; | |
358 _forEachElement((Element element) { | |
359 for (DeclarationMirror mirror in _getDeclarationMirrors(element)) { | |
360 assert( | |
361 invariant(_element, !declarations.containsKey(mirror.simpleName), | |
362 message: "Declaration name '${nameOf(mirror)}' " | |
363 "is not unique in $_element.")); | |
364 declarations[mirror.simpleName] = mirror; | |
365 } | |
366 }); | |
367 _declarations = | |
368 new UnmodifiableMapView<Symbol, DeclarationMirror>(declarations); | |
369 } | |
370 } | |
371 | |
372 Element get _element; | |
373 | |
374 void _forEachElement(f(Element element)); | |
375 | |
376 Iterable<Dart2JsMemberMirror> _getDeclarationMirrors(Element element); | |
377 | |
378 Map<Symbol, DeclarationMirror> get declarations { | |
379 _ensureDeclarations(); | |
380 return _declarations; | |
381 } | |
382 } | |
383 | |
384 /** | |
385 * Converts [element] into its corresponding [DeclarationMirror], if any. | |
386 * | |
387 * If [element] is an [AbstractFieldElement] the mirror for its getter is | |
388 * returned or, if not present, the mirror for its setter. | |
389 */ | |
390 DeclarationMirror _convertElementToDeclarationMirror( | |
391 Dart2JsMirrorSystem system, Element element) { | |
392 if (element.isTypeVariable) { | |
393 TypeVariableElement typeVariable = element; | |
394 return new Dart2JsTypeVariableMirror(system, typeVariable.type); | |
395 } | |
396 | |
397 Dart2JsLibraryMirror library = system._libraryMap[element.library]; | |
398 if (element.isLibrary) return library; | |
399 if (element.isTypedef) { | |
400 TypedefElement typedefElement = element; | |
401 return new Dart2JsTypedefMirror.fromLibrary( | |
402 library, typedefElement.thisType); | |
403 } | |
404 | |
405 Dart2JsDeclarationMirror container = library; | |
406 if (element.enclosingClass != null) { | |
407 container = system._getTypeDeclarationMirror(element.enclosingClass); | |
408 } | |
409 if (element.isClass) return container; | |
410 if (element.isParameter) { | |
411 Dart2JsMethodMirror method = _convertElementMethodToMethodMirror( | |
412 container, element.outermostEnclosingMemberOrTopLevel); | |
413 // TODO(johnniwinther): Find the right info for [isOptional] and [isNamed]. | |
414 return new Dart2JsParameterMirror(method, element, | |
415 isOptional: false, isNamed: false); | |
416 } | |
417 Iterable<DeclarationMirror> members = | |
418 container._getDeclarationMirrors(element); | |
419 if (members.isEmpty) return null; | |
420 return members.first; | |
421 } | |
422 | |
423 /** | |
424 * Experimental API for accessing compilation units defined in a | |
425 * library. | |
426 */ | |
427 // TODO(ahe): Superclasses? Is this really a mirror? | |
428 class Dart2JsCompilationUnitMirror extends Dart2JsMirror with ContainerMixin { | |
429 final Dart2JsLibraryMirror _library; | |
430 final CompilationUnitElement _element; | |
431 | |
432 Dart2JsCompilationUnitMirror(this._element, this._library); | |
433 | |
434 Dart2JsMirrorSystem get mirrorSystem => _library.mirrorSystem; | |
435 | |
436 // TODO(johnniwinther): make sure that these are returned in declaration | |
437 // order. | |
438 void _forEachElement(f(Element element)) => _element.forEachLocalMember(f); | |
439 | |
440 Iterable<DeclarationMirror> _getDeclarationMirrors(Element element) => | |
441 _library._getDeclarationMirrors(element); | |
442 | |
443 Uri get uri => _element.script.resourceUri; | |
444 } | |
445 | |
446 /** | |
447 * Transitional class that allows access to features that have not yet | |
448 * made it to the mirror API. | |
449 * | |
450 * All API in this class is experimental. | |
451 */ | |
452 class BackDoor { | |
453 /// Return the compilation units comprising [library]. | |
454 static List<Mirror> compilationUnitsOf(Dart2JsLibraryMirror library) { | |
455 return library._element.compilationUnits | |
456 .mapToList((cu) => new Dart2JsCompilationUnitMirror(cu, library)); | |
457 } | |
458 | |
459 static Iterable<ConstantExpression> metadataSyntaxOf( | |
460 Dart2JsElementMirror declaration) { | |
461 return declaration._element.metadata.map((metadata) => metadata.constant); | |
462 } | |
463 | |
464 static ConstantExpression initializerSyntaxOf(Dart2JsFieldMirror variable) { | |
465 Compiler compiler = variable.mirrorSystem.compiler; | |
466 return variable._variable.constant; | |
467 } | |
468 | |
469 static ConstantExpression defaultValueSyntaxOf( | |
470 Dart2JsParameterMirror parameter) { | |
471 if (!parameter.hasDefaultValue) return null; | |
472 ParameterElement parameterElement = parameter._element; | |
473 Compiler compiler = parameter.mirrorSystem.compiler; | |
474 return parameterElement.constant; | |
475 } | |
476 | |
477 static Mirror getMirrorFromElement(Dart2JsMirror mirror, Element element) { | |
478 return _convertElementToDeclarationMirror(mirror.mirrorSystem, element); | |
479 } | |
480 } | |
OLD | NEW |