OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /// Records accesses to Dart program declarations and generates code that will | 5 /// Records accesses to Dart program declarations and generates code that will |
6 /// allow to do the same accesses at runtime using `package:smoke/static.dart`. | 6 /// allow to do the same accesses at runtime using `package:smoke/static.dart`. |
7 /// Internally, this library relies on the `analyzer` to extract data from the | 7 /// Internally, this library relies on the `analyzer` to extract data from the |
8 /// program, and then uses [SmokeCodeGenerator] to produce the code needed by | 8 /// program, and then uses [SmokeCodeGenerator] to produce the code needed by |
9 /// the smoke system. | 9 /// the smoke system. |
10 /// | 10 /// |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
56 }); | 56 }); |
57 if (!baseType.type.isObject) generator.addParent(mixinId, baseId); | 57 if (!baseType.type.isObject) generator.addParent(mixinId, baseId); |
58 baseType = mixinType; | 58 baseType = mixinType; |
59 baseId = mixinId; | 59 baseId = mixinId; |
60 _mixins.putIfAbsent(mixinId, () => {}); | 60 _mixins.putIfAbsent(mixinId, () => {}); |
61 } | 61 } |
62 } | 62 } |
63 if (!baseType.type.isObject) generator.addParent(_typeFor(type), baseId); | 63 if (!baseType.type.isObject) generator.addParent(_typeFor(type), baseId); |
64 } | 64 } |
65 | 65 |
66 TypeIdentifier _typeFor(ClassElement type) => | 66 TypeIdentifier _typeFor(Element type) => new TypeIdentifier( |
67 new TypeIdentifier(importUrlFor(type.library), type.displayName); | 67 type.library == null ? 'dart:core' : importUrlFor(type.library), |
| 68 type.displayName); |
68 | 69 |
69 /// Adds any declaration and superclass information that is needed to answer a | 70 /// Adds any declaration and superclass information that is needed to answer a |
70 /// query on [type] that matches [options]. | 71 /// query on [type] that matches [options]. Also adds symbols, getters, and |
71 void runQuery(ClassElement type, QueryOptions options) { | 72 /// setters if [includeAccessors] is true. |
| 73 void runQuery(ClassElement type, QueryOptions options, |
| 74 {bool includeAccessors: true}) { |
72 if (type.type.isObject) return; // We don't include Object in query results. | 75 if (type.type.isObject) return; // We don't include Object in query results. |
73 var id = _typeFor(type); | 76 var id = _typeFor(type); |
74 var parent = type.supertype != null ? type.supertype.element : null; | 77 var parent = type.supertype != null ? type.supertype.element : null; |
75 if (options.includeInherited && parent != null && | 78 if (options.includeInherited && parent != null && |
76 parent != options.includeUpTo) { | 79 parent != options.includeUpTo) { |
77 lookupParent(type); | 80 lookupParent(type); |
78 runQuery(parent, options); | 81 runQuery(parent, options, includeAccessors: includeAccessors); |
79 var parentId = _typeFor(parent); | 82 var parentId = _typeFor(parent); |
80 for (var m in type.mixins) { | 83 for (var m in type.mixins) { |
81 var mixinClass = m.element; | 84 var mixinClass = m.element; |
82 var mixinId = _mixins[parentId][mixinClass]; | 85 var mixinId = _mixins[parentId][mixinClass]; |
83 _runQueryInternal(mixinClass, mixinId, options); | 86 _runQueryInternal(mixinClass, mixinId, options, includeAccessors); |
84 parentId = mixinId; | 87 parentId = mixinId; |
85 } | 88 } |
86 } | 89 } |
87 _runQueryInternal(type, id, options); | 90 _runQueryInternal(type, id, options, includeAccessors); |
88 } | 91 } |
89 | 92 |
90 /// Helper for [runQuery]. This runs the query only on a specific [type], | 93 /// Helper for [runQuery]. This runs the query only on a specific [type], |
91 /// which could be a class or a mixin labeled by [id]. | 94 /// which could be a class or a mixin labeled by [id]. |
92 // TODO(sigmund): currently we materialize mixins in smoke/static.dart, | 95 // TODO(sigmund): currently we materialize mixins in smoke/static.dart, |
93 // we should consider to include the mixin declaration information directly, | 96 // we should consider to include the mixin declaration information directly, |
94 // and remove the duplication we have for mixins today. | 97 // and remove the duplication we have for mixins today. |
95 void _runQueryInternal(ClassElement type, TypeIdentifier id, | 98 void _runQueryInternal(ClassElement type, TypeIdentifier id, |
96 QueryOptions options) { | 99 QueryOptions options, bool includeAccessors) { |
97 | 100 |
98 skipBecauseOfAnnotations(Element e) { | 101 skipBecauseOfAnnotations(Element e) { |
99 if (options.withAnnotations == null) return false; | 102 if (options.withAnnotations == null) return false; |
100 return !_matchesAnnotation(e.metadata, options.withAnnotations); | 103 return !_matchesAnnotation(e.metadata, options.withAnnotations); |
101 } | 104 } |
102 | 105 |
103 if (options.includeFields) { | 106 if (options.includeFields) { |
104 for (var f in type.fields) { | 107 for (var f in type.fields) { |
105 if (f.isStatic) continue; | 108 if (f.isStatic) continue; |
106 if (f.isSynthetic) continue; // exclude getters | 109 if (f.isSynthetic) continue; // exclude getters |
107 if (options.excludeFinal && f.isFinal) continue; | 110 if (options.excludeFinal && f.isFinal) continue; |
| 111 var name = f.displayName; |
| 112 if (options.matches != null && !options.matches(name)) continue; |
108 if (skipBecauseOfAnnotations(f)) continue; | 113 if (skipBecauseOfAnnotations(f)) continue; |
109 generator.addDeclaration(id, f.displayName, _typeFor(f.type.element), | 114 generator.addDeclaration(id, name, _typeFor(f.type.element), |
110 isField: true, isFinal: f.isFinal, | 115 isField: true, isFinal: f.isFinal, |
111 annotations: _copyAnnotations(f)); | 116 annotations: _copyAnnotations(f)); |
| 117 if (includeAccessors) _addAccessors(name, !f.isFinal); |
112 } | 118 } |
113 } | 119 } |
114 | 120 |
115 if (options.includeProperties) { | 121 if (options.includeProperties) { |
116 for (var a in type.accessors) { | 122 for (var a in type.accessors) { |
117 if (a is! PropertyAccessorElement) continue; | 123 if (a is! PropertyAccessorElement) continue; |
118 if (a.isStatic || !a.isGetter) continue; | 124 if (a.isStatic || !a.isGetter) continue; |
119 var v = a.variable; | 125 var v = a.variable; |
120 if (v is FieldElement && !v.isSynthetic) continue; // exclude fields | 126 if (v is FieldElement && !v.isSynthetic) continue; // exclude fields |
121 if (options.excludeFinal && v.isFinal) continue; | 127 if (options.excludeFinal && v.isFinal) continue; |
122 if (skipBecauseOfAnnotations(v)) continue; | 128 var name = v.displayName; |
123 generator.addDeclaration(id, v.displayName, _typeFor(v.type.element), | 129 if (options.matches != null && !options.matches(name)) continue; |
| 130 if (skipBecauseOfAnnotations(a)) continue; |
| 131 generator.addDeclaration(id, name, _typeFor(a.type.returnType.element), |
124 isProperty: true, isFinal: v.isFinal, | 132 isProperty: true, isFinal: v.isFinal, |
125 annotations: _copyAnnotations(a)); | 133 annotations: _copyAnnotations(a)); |
| 134 if (includeAccessors) _addAccessors(name, !v.isFinal); |
126 } | 135 } |
127 } | 136 } |
128 | 137 |
129 if (options.includeMethods) { | 138 if (options.includeMethods) { |
130 for (var m in type.methods) { | 139 for (var m in type.methods) { |
131 if (m.isStatic) continue; | 140 if (m.isStatic) continue; |
| 141 var name = m.displayName; |
| 142 if (options.matches != null && !options.matches(name)) continue; |
132 if (skipBecauseOfAnnotations(m)) continue; | 143 if (skipBecauseOfAnnotations(m)) continue; |
133 generator.addDeclaration(id, m.displayName, | 144 generator.addDeclaration(id, name, |
134 new TypeIdentifier('dart:core', 'Function'), isMethod: true, | 145 new TypeIdentifier('dart:core', 'Function'), isMethod: true, |
135 annotations: _copyAnnotations(m)); | 146 annotations: _copyAnnotations(m)); |
| 147 if (includeAccessors) _addAccessors(name, false); |
136 } | 148 } |
137 } | 149 } |
138 } | 150 } |
139 | 151 |
140 /// Adds the declaration of [name] if it was found in [type]. If [recursive] | 152 /// Adds the declaration of [name] if it was found in [type]. If [recursive] |
141 /// is true, then we continue looking up [name] in the parent classes until we | 153 /// is true, then we continue looking up [name] in the parent classes until we |
142 /// find it or we reach Object. | 154 /// find it or we reach Object. Returns whether the declaration was found. |
143 void lookupMember(ClassElement type, String name, {bool recursive: false}) { | 155 /// When a declaration is found, add also a symbol, getter, and setter if |
144 _lookupMemberInternal(type, _typeFor(type), name, recursive); | 156 /// [includeAccessors] is true. |
145 } | 157 bool lookupMember(ClassElement type, String name, {bool recursive: false, |
| 158 bool includeAccessors: true}) => |
| 159 _lookupMemberInternal(type, _typeFor(type), name, recursive, |
| 160 includeAccessors); |
146 | 161 |
147 /// Helper for [lookupMember] that walks up the type hierarchy including mixin | 162 /// Helper for [lookupMember] that walks up the type hierarchy including mixin |
148 /// classes. | 163 /// classes. |
149 bool _lookupMemberInternal(ClassElement type, TypeIdentifier id, String name, | 164 bool _lookupMemberInternal(ClassElement type, TypeIdentifier id, String name, |
150 bool recursive) { | 165 bool recursive, bool includeAccessors) { |
151 // Exclude members from [Object]. | 166 // Exclude members from [Object]. |
152 if (type.type.isObject) return false; | 167 if (type.type.isObject) return false; |
153 generator.addEmptyDeclaration(id); | 168 generator.addEmptyDeclaration(id); |
154 for (var f in type.fields) { | 169 for (var f in type.fields) { |
155 if (f.displayName != name) continue; | 170 if (f.displayName != name) continue; |
156 if (f.isSynthetic) continue; // exclude getters | 171 if (f.isSynthetic) continue; // exclude getters |
157 generator.addDeclaration(id, f.displayName, | 172 generator.addDeclaration(id, name, |
158 _typeFor(f.type.element), isField: true, isFinal: f.isFinal, | 173 _typeFor(f.type.element), isField: true, isFinal: f.isFinal, |
159 isStatic: f.isStatic, annotations: _copyAnnotations(f)); | 174 isStatic: f.isStatic, annotations: _copyAnnotations(f)); |
| 175 if (includeAccessors) _addAccessors(name, !f.isFinal); |
160 return true; | 176 return true; |
161 } | 177 } |
162 | 178 |
163 for (var a in type.accessors) { | 179 for (var a in type.accessors) { |
164 if (a is! PropertyAccessorElement) continue; | 180 if (a is! PropertyAccessorElement) continue; |
165 // TODO(sigmund): support setters without getters. | 181 // TODO(sigmund): support setters without getters. |
166 if (!a.isGetter) continue; | 182 if (!a.isGetter) continue; |
167 if (a.displayName != name) continue; | 183 if (a.displayName != name) continue; |
168 var v = a.variable; | 184 var v = a.variable; |
169 if (v is FieldElement && !v.isSynthetic) continue; // exclude fields | 185 if (v is FieldElement && !v.isSynthetic) continue; // exclude fields |
170 generator.addDeclaration(id, v.displayName, | 186 generator.addDeclaration(id, name, |
171 _typeFor(v.type.element), isProperty: true, isFinal: v.isFinal, | 187 _typeFor(a.type.returnType.element), isProperty: true, |
172 isStatic: v.isStatic, annotations: _copyAnnotations(a)); | 188 isFinal: v.isFinal, isStatic: a.isStatic, |
| 189 annotations: _copyAnnotations(a)); |
| 190 if (includeAccessors) _addAccessors(name, !v.isFinal); |
173 return true; | 191 return true; |
174 } | 192 } |
175 | 193 |
176 for (var m in type.methods) { | 194 for (var m in type.methods) { |
177 if (m.displayName != name) continue; | 195 if (m.displayName != name) continue; |
178 generator.addDeclaration(id, m.displayName, | 196 generator.addDeclaration(id, name, |
179 new TypeIdentifier('dart:core', 'Function'), isMethod: true, | 197 new TypeIdentifier('dart:core', 'Function'), isMethod: true, |
180 isStatic: m.isStatic, annotations: _copyAnnotations(m)); | 198 isStatic: m.isStatic, annotations: _copyAnnotations(m)); |
| 199 if (includeAccessors) _addAccessors(name, false); |
181 return true; | 200 return true; |
182 } | 201 } |
183 | 202 |
184 if (recursive) { | 203 if (recursive) { |
185 lookupParent(type); | 204 lookupParent(type); |
186 var parent = type.supertype != null ? type.supertype.element : null; | 205 var parent = type.supertype != null ? type.supertype.element : null; |
187 if (parent == null) return false; | 206 if (parent == null) return false; |
188 var parentId = _typeFor(parent); | 207 var parentId = _typeFor(parent); |
189 for (var m in type.mixins) { | 208 for (var m in type.mixins) { |
190 var mixinClass = m.element; | 209 var mixinClass = m.element; |
191 var mixinId = _mixins[parentId][mixinClass]; | 210 var mixinId = _mixins[parentId][mixinClass]; |
192 if (_lookupMemberInternal(mixinClass, mixinId, name, false)) { | 211 if (_lookupMemberInternal(mixinClass, mixinId, name, false, |
| 212 includeAccessors)) { |
193 return true; | 213 return true; |
194 } | 214 } |
195 parentId = mixinId; | 215 parentId = mixinId; |
196 } | 216 } |
197 return _lookupMemberInternal(parent, parentId, name, true); | 217 return _lookupMemberInternal(parent, parentId, name, true, |
| 218 includeAccessors); |
198 } | 219 } |
199 return false; | 220 return false; |
200 } | 221 } |
201 | 222 |
202 | 223 |
| 224 /// Adds [name] as a symbol, a getter, and optionally a setter in [generator]. |
| 225 _addAccessors(String name, bool includeSetter) { |
| 226 generator.addSymbol(name); |
| 227 generator.addGetter(name); |
| 228 if (includeSetter) generator.addSetter(name); |
| 229 } |
| 230 |
203 /// Copy metadata associated with the declaration of [target]. | 231 /// Copy metadata associated with the declaration of [target]. |
204 List<ConstExpression> _copyAnnotations(Element target) { | 232 List<ConstExpression> _copyAnnotations(Element target) { |
205 var node = target.node; | 233 var node = target.node; |
206 // [node] is the initialization expression, we walk up to get to the actual | 234 // [node] is the initialization expression, we walk up to get to the actual |
207 // member declaration where the metadata is attached to. | 235 // member declaration where the metadata is attached to. |
208 while (node is! ClassMember) node = node.parent; | 236 while (node is! ClassMember) node = node.parent; |
209 return node.metadata.map(_convertAnnotation).toList(); | 237 return node.metadata.map(_convertAnnotation).toList(); |
210 } | 238 } |
211 | 239 |
212 /// Converts annotations into [ConstExpression]s supported by the codegen | 240 /// Converts annotations into [ConstExpression]s supported by the codegen |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
331 final bool excludeFinal; | 359 final bool excludeFinal; |
332 | 360 |
333 /// Whether to include methods (default is false). | 361 /// Whether to include methods (default is false). |
334 final bool includeMethods; | 362 final bool includeMethods; |
335 | 363 |
336 /// If [withAnnotation] is not null, then it should be a list of types, so | 364 /// If [withAnnotation] is not null, then it should be a list of types, so |
337 /// only symbols that are annotated with instances of those types are | 365 /// only symbols that are annotated with instances of those types are |
338 /// included. | 366 /// included. |
339 final List<Element> withAnnotations; | 367 final List<Element> withAnnotations; |
340 | 368 |
| 369 /// If [matches] is not null, then only those fields, properties, or methods |
| 370 /// that match will be included. |
| 371 final NameMatcher matches; |
| 372 |
341 const QueryOptions({ | 373 const QueryOptions({ |
342 this.includeFields: true, | 374 this.includeFields: true, |
343 this.includeProperties: true, | 375 this.includeProperties: true, |
344 this.includeInherited: true, | 376 this.includeInherited: true, |
345 this.includeUpTo: null, | 377 this.includeUpTo: null, |
346 this.excludeFinal: false, | 378 this.excludeFinal: false, |
347 this.includeMethods: false, | 379 this.includeMethods: false, |
348 this.withAnnotations: null}); | 380 this.withAnnotations: null, |
| 381 this.matches: null}); |
349 } | 382 } |
| 383 |
| 384 /// Predicate that tells whether [name] should be included in query results. |
| 385 typedef bool NameMatcher(String name); |
OLD | NEW |