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 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
69 | 69 |
70 /// 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 |
71 /// query on [type] that matches [options]. Also adds symbols, getters, and | 71 /// query on [type] that matches [options]. Also adds symbols, getters, and |
72 /// setters if [includeAccessors] is true. If [results] is not null, it will | 72 /// setters if [includeAccessors] is true. If [results] is not null, it will |
73 /// be filled up with the members that match the query. | 73 /// be filled up with the members that match the query. |
74 void runQuery(ClassElement type, QueryOptions options, | 74 void runQuery(ClassElement type, QueryOptions options, |
75 {bool includeAccessors: true, List results}) { | 75 {bool includeAccessors: true, List results}) { |
76 if (type.type.isObject) return; // We don't include Object in query results. | 76 if (type.type.isObject) return; // We don't include Object in query results. |
77 var id = _typeFor(type); | 77 var id = _typeFor(type); |
78 var parent = type.supertype != null ? type.supertype.element : null; | 78 var parent = type.supertype != null ? type.supertype.element : null; |
79 if (options.includeInherited && parent != null && | 79 if (options.includeInherited && |
| 80 parent != null && |
80 parent != options.includeUpTo) { | 81 parent != options.includeUpTo) { |
81 lookupParent(type); | 82 lookupParent(type); |
82 runQuery(parent, options, includeAccessors: includeAccessors); | 83 runQuery(parent, options, includeAccessors: includeAccessors); |
83 var parentId = _typeFor(parent); | 84 var parentId = _typeFor(parent); |
84 for (var m in type.mixins) { | 85 for (var m in type.mixins) { |
85 var mixinClass = m.element; | 86 var mixinClass = m.element; |
86 var mixinId = _mixins[parentId][mixinClass]; | 87 var mixinId = _mixins[parentId][mixinClass]; |
87 _runQueryInternal( | 88 _runQueryInternal( |
88 mixinClass, mixinId, options, includeAccessors, results); | 89 mixinClass, mixinId, options, includeAccessors, results); |
89 parentId = mixinId; | 90 parentId = mixinId; |
90 } | 91 } |
91 } | 92 } |
92 _runQueryInternal(type, id, options, includeAccessors, results); | 93 _runQueryInternal(type, id, options, includeAccessors, results); |
93 } | 94 } |
94 | 95 |
95 /// Helper for [runQuery]. This runs the query only on a specific [type], | 96 /// Helper for [runQuery]. This runs the query only on a specific [type], |
96 /// which could be a class or a mixin labeled by [id]. | 97 /// which could be a class or a mixin labeled by [id]. |
97 // TODO(sigmund): currently we materialize mixins in smoke/static.dart, | 98 // TODO(sigmund): currently we materialize mixins in smoke/static.dart, |
98 // we should consider to include the mixin declaration information directly, | 99 // we should consider to include the mixin declaration information directly, |
99 // and remove the duplication we have for mixins today. | 100 // and remove the duplication we have for mixins today. |
100 void _runQueryInternal(ClassElement type, TypeIdentifier id, | 101 void _runQueryInternal(ClassElement type, TypeIdentifier id, |
101 QueryOptions options, bool includeAccessors, List results) { | 102 QueryOptions options, bool includeAccessors, List results) { |
102 | |
103 skipBecauseOfAnnotations(Element e) { | 103 skipBecauseOfAnnotations(Element e) { |
104 if (options.withAnnotations == null) return false; | 104 if (options.withAnnotations == null) return false; |
105 return !_matchesAnnotation(e.metadata, options.withAnnotations); | 105 return !_matchesAnnotation(e.metadata, options.withAnnotations); |
106 } | 106 } |
107 | 107 |
108 if (options.includeFields) { | 108 if (options.includeFields) { |
109 for (var f in type.fields) { | 109 for (var f in type.fields) { |
110 if (f.isStatic) continue; | 110 if (f.isStatic) continue; |
111 if (f.isSynthetic) continue; // exclude getters | 111 if (f.isSynthetic) continue; // exclude getters |
112 if (options.excludeFinal && f.isFinal) continue; | 112 if (options.excludeFinal && f.isFinal) continue; |
113 var name = f.displayName; | 113 var name = f.displayName; |
114 if (options.matches != null && !options.matches(name)) continue; | 114 if (options.matches != null && !options.matches(name)) continue; |
115 if (skipBecauseOfAnnotations(f)) continue; | 115 if (skipBecauseOfAnnotations(f)) continue; |
116 if (results != null) results.add(f); | 116 if (results != null) results.add(f); |
117 generator.addDeclaration(id, name, _typeFor(f.type.element), | 117 generator.addDeclaration(id, name, _typeFor(f.type.element), |
118 isField: true, isFinal: f.isFinal, | 118 isField: true, |
| 119 isFinal: f.isFinal, |
119 annotations: _copyAnnotations(f)); | 120 annotations: _copyAnnotations(f)); |
120 if (includeAccessors) _addAccessors(name, !f.isFinal); | 121 if (includeAccessors) _addAccessors(name, !f.isFinal); |
121 } | 122 } |
122 } | 123 } |
123 | 124 |
124 if (options.includeProperties) { | 125 if (options.includeProperties) { |
125 for (var a in type.accessors) { | 126 for (var a in type.accessors) { |
126 if (a is! PropertyAccessorElement) continue; | 127 if (a is! PropertyAccessorElement) continue; |
127 if (a.isStatic || !a.isGetter) continue; | 128 if (a.isStatic || !a.isGetter) continue; |
128 var v = a.variable; | 129 var v = a.variable; |
129 if (v is FieldElement && !v.isSynthetic) continue; // exclude fields | 130 if (v is FieldElement && !v.isSynthetic) continue; // exclude fields |
130 if (options.excludeFinal && v.isFinal) continue; | 131 if (options.excludeFinal && v.isFinal) continue; |
131 var name = v.displayName; | 132 var name = v.displayName; |
132 if (options.matches != null && !options.matches(name)) continue; | 133 if (options.matches != null && !options.matches(name)) continue; |
133 if (skipBecauseOfAnnotations(a)) continue; | 134 if (skipBecauseOfAnnotations(a)) continue; |
134 if (results != null) results.add(a); | 135 if (results != null) results.add(a); |
135 generator.addDeclaration(id, name, _typeFor(a.type.returnType.element), | 136 generator.addDeclaration(id, name, _typeFor(a.type.returnType.element), |
136 isProperty: true, isFinal: v.isFinal, | 137 isProperty: true, |
| 138 isFinal: v.isFinal, |
137 annotations: _copyAnnotations(a)); | 139 annotations: _copyAnnotations(a)); |
138 if (includeAccessors) _addAccessors(name, !v.isFinal); | 140 if (includeAccessors) _addAccessors(name, !v.isFinal); |
139 } | 141 } |
140 } | 142 } |
141 | 143 |
142 if (options.includeMethods) { | 144 if (options.includeMethods) { |
143 for (var m in type.methods) { | 145 for (var m in type.methods) { |
144 if (m.isStatic) continue; | 146 if (m.isStatic) continue; |
145 var name = m.displayName; | 147 var name = m.displayName; |
146 if (options.matches != null && !options.matches(name)) continue; | 148 if (options.matches != null && !options.matches(name)) continue; |
147 if (skipBecauseOfAnnotations(m)) continue; | 149 if (skipBecauseOfAnnotations(m)) continue; |
148 if (results != null) results.add(m); | 150 if (results != null) results.add(m); |
149 generator.addDeclaration(id, name, | 151 generator.addDeclaration( |
150 new TypeIdentifier('dart:core', 'Function'), isMethod: true, | 152 id, name, new TypeIdentifier('dart:core', 'Function'), |
151 annotations: _copyAnnotations(m)); | 153 isMethod: true, annotations: _copyAnnotations(m)); |
152 if (includeAccessors) _addAccessors(name, false); | 154 if (includeAccessors) _addAccessors(name, false); |
153 } | 155 } |
154 } | 156 } |
155 } | 157 } |
156 | 158 |
157 /// Adds the declaration of [name] if it was found in [type]. If [recursive] | 159 /// Adds the declaration of [name] if it was found in [type]. If [recursive] |
158 /// is true, then we continue looking up [name] in the parent classes until we | 160 /// is true, then we continue looking up [name] in the parent classes until we |
159 /// find it or we reach [includeUpTo] or Object. Returns whether the | 161 /// find it or we reach [includeUpTo] or Object. Returns whether the |
160 /// declaration was found. When a declaration is found, add also a symbol, | 162 /// declaration was found. When a declaration is found, add also a symbol, |
161 /// getter, and setter if [includeAccessors] is true. | 163 /// getter, and setter if [includeAccessors] is true. |
162 bool lookupMember(ClassElement type, String name, {bool recursive: false, | 164 bool lookupMember(ClassElement type, String name, {bool recursive: false, |
163 bool includeAccessors: true, ClassElement includeUpTo}) => | 165 bool includeAccessors: true, ClassElement includeUpTo}) => |
164 _lookupMemberInternal(type, _typeFor(type), name, recursive, | 166 _lookupMemberInternal( |
165 includeAccessors, includeUpTo); | 167 type, _typeFor(type), name, recursive, includeAccessors, includeUpTo); |
166 | 168 |
167 /// Helper for [lookupMember] that walks up the type hierarchy including mixin | 169 /// Helper for [lookupMember] that walks up the type hierarchy including mixin |
168 /// classes. | 170 /// classes. |
169 bool _lookupMemberInternal(ClassElement type, TypeIdentifier id, String name, | 171 bool _lookupMemberInternal(ClassElement type, TypeIdentifier id, String name, |
170 bool recursive, bool includeAccessors, ClassElement includeUpTo) { | 172 bool recursive, bool includeAccessors, ClassElement includeUpTo) { |
171 // Exclude members from [Object]. | 173 // Exclude members from [Object]. |
172 if (type.type.isObject) return false; | 174 if (type.type.isObject) return false; |
173 generator.addEmptyDeclaration(id); | 175 generator.addEmptyDeclaration(id); |
174 for (var f in type.fields) { | 176 for (var f in type.fields) { |
175 if (f.displayName != name) continue; | 177 if (f.displayName != name) continue; |
176 if (f.isSynthetic) continue; // exclude getters | 178 if (f.isSynthetic) continue; // exclude getters |
177 generator.addDeclaration(id, name, | 179 generator.addDeclaration(id, name, _typeFor(f.type.element), |
178 _typeFor(f.type.element), isField: true, isFinal: f.isFinal, | 180 isField: true, |
179 isStatic: f.isStatic, annotations: _copyAnnotations(f)); | 181 isFinal: f.isFinal, |
| 182 isStatic: f.isStatic, |
| 183 annotations: _copyAnnotations(f)); |
180 if (includeAccessors && !f.isStatic) _addAccessors(name, !f.isFinal); | 184 if (includeAccessors && !f.isStatic) _addAccessors(name, !f.isFinal); |
181 return true; | 185 return true; |
182 } | 186 } |
183 | 187 |
184 for (var a in type.accessors) { | 188 for (var a in type.accessors) { |
185 if (a is! PropertyAccessorElement) continue; | 189 if (a is! PropertyAccessorElement) continue; |
186 // TODO(sigmund): support setters without getters. | 190 // TODO(sigmund): support setters without getters. |
187 if (!a.isGetter) continue; | 191 if (!a.isGetter) continue; |
188 if (a.displayName != name) continue; | 192 if (a.displayName != name) continue; |
189 var v = a.variable; | 193 var v = a.variable; |
190 if (v is FieldElement && !v.isSynthetic) continue; // exclude fields | 194 if (v is FieldElement && !v.isSynthetic) continue; // exclude fields |
191 generator.addDeclaration(id, name, | 195 generator.addDeclaration(id, name, _typeFor(a.type.returnType.element), |
192 _typeFor(a.type.returnType.element), isProperty: true, | 196 isProperty: true, |
193 isFinal: v.isFinal, isStatic: a.isStatic, | 197 isFinal: v.isFinal, |
| 198 isStatic: a.isStatic, |
194 annotations: _copyAnnotations(a)); | 199 annotations: _copyAnnotations(a)); |
195 if (includeAccessors && !v.isStatic) _addAccessors(name, !v.isFinal); | 200 if (includeAccessors && !v.isStatic) _addAccessors(name, !v.isFinal); |
196 return true; | 201 return true; |
197 } | 202 } |
198 | 203 |
199 for (var m in type.methods) { | 204 for (var m in type.methods) { |
200 if (m.displayName != name) continue; | 205 if (m.displayName != name) continue; |
201 generator.addDeclaration(id, name, | 206 generator.addDeclaration( |
202 new TypeIdentifier('dart:core', 'Function'), isMethod: true, | 207 id, name, new TypeIdentifier('dart:core', 'Function'), |
203 isStatic: m.isStatic, annotations: _copyAnnotations(m)); | 208 isMethod: true, |
| 209 isStatic: m.isStatic, |
| 210 annotations: _copyAnnotations(m)); |
204 if (includeAccessors) { | 211 if (includeAccessors) { |
205 if (m.isStatic) { | 212 if (m.isStatic) { |
206 generator.addStaticMethod(id, name); | 213 generator.addStaticMethod(id, name); |
207 generator.addSymbol(name); | 214 generator.addSymbol(name); |
208 } else { | 215 } else { |
209 _addAccessors(name, false); | 216 _addAccessors(name, false); |
210 } | 217 } |
211 } | 218 } |
212 return true; | 219 return true; |
213 } | 220 } |
214 | 221 |
215 if (recursive) { | 222 if (recursive) { |
216 lookupParent(type); | 223 lookupParent(type); |
217 var parent = type.supertype != null ? type.supertype.element : null; | 224 var parent = type.supertype != null ? type.supertype.element : null; |
218 if (parent == null || parent == includeUpTo) return false; | 225 if (parent == null || parent == includeUpTo) return false; |
219 var parentId = _typeFor(parent); | 226 var parentId = _typeFor(parent); |
220 for (var m in type.mixins) { | 227 for (var m in type.mixins) { |
221 var mixinClass = m.element; | 228 var mixinClass = m.element; |
222 var mixinId = _mixins[parentId][mixinClass]; | 229 var mixinId = _mixins[parentId][mixinClass]; |
223 if (_lookupMemberInternal(mixinClass, mixinId, name, false, | 230 if (_lookupMemberInternal( |
224 includeAccessors, includeUpTo)) { | 231 mixinClass, mixinId, name, false, includeAccessors, includeUpTo)) { |
225 return true; | 232 return true; |
226 } | 233 } |
227 parentId = mixinId; | 234 parentId = mixinId; |
228 } | 235 } |
229 return _lookupMemberInternal(parent, parentId, name, true, | 236 return _lookupMemberInternal( |
230 includeAccessors, includeUpTo); | 237 parent, parentId, name, true, includeAccessors, includeUpTo); |
231 } | 238 } |
232 return false; | 239 return false; |
233 } | 240 } |
234 | 241 |
235 /// Add information so smoke can invoke the static method [type].[name]. | 242 /// Add information so smoke can invoke the static method [type].[name]. |
236 void addStaticMethod(ClassElement type, String name) { | 243 void addStaticMethod(ClassElement type, String name) { |
237 generator.addStaticMethod(_typeFor(type), name); | 244 generator.addStaticMethod(_typeFor(type), name); |
238 } | 245 } |
239 | 246 |
240 /// Adds [name] as a symbol, a getter, and optionally a setter in [generator]. | 247 /// Adds [name] as a symbol, a getter, and optionally a setter in [generator]. |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
284 | 291 |
285 throw new UnsupportedError('unsupported annotation $annotation'); | 292 throw new UnsupportedError('unsupported annotation $annotation'); |
286 } | 293 } |
287 | 294 |
288 /// Converts [expression] into a [ConstExpression]. | 295 /// Converts [expression] into a [ConstExpression]. |
289 ConstExpression _convertExpression(Expression expression) { | 296 ConstExpression _convertExpression(Expression expression) { |
290 if (expression is StringLiteral) { | 297 if (expression is StringLiteral) { |
291 return new ConstExpression.string(expression.stringValue); | 298 return new ConstExpression.string(expression.stringValue); |
292 } | 299 } |
293 | 300 |
294 if (expression is BooleanLiteral || expression is DoubleLiteral || | 301 if (expression is BooleanLiteral || |
295 expression is IntegerLiteral || expression is NullLiteral) { | 302 expression is DoubleLiteral || |
| 303 expression is IntegerLiteral || |
| 304 expression is NullLiteral) { |
296 return new CodeAsConstExpression("${(expression as dynamic).value}"); | 305 return new CodeAsConstExpression("${(expression as dynamic).value}"); |
297 } | 306 } |
298 | 307 |
299 if (expression is Identifier) { | 308 if (expression is Identifier) { |
300 var element = expression.bestElement; | 309 var element = expression.bestElement; |
301 if (element == null || !element.isPublic) { | 310 if (element == null || !element.isPublic) { |
302 throw new UnsupportedError('private constants are not supported'); | 311 throw new UnsupportedError('private constants are not supported'); |
303 } | 312 } |
304 | 313 |
305 var url = importUrlFor(element.library); | 314 var url = importUrlFor(element.library); |
(...skipping 15 matching lines...) Expand all Loading... |
321 throw new UnimplementedError('expression convertion not implemented in ' | 330 throw new UnimplementedError('expression convertion not implemented in ' |
322 'smoke.codegen.recorder (${expression.runtimeType} $expression)'); | 331 'smoke.codegen.recorder (${expression.runtimeType} $expression)'); |
323 } | 332 } |
324 } | 333 } |
325 | 334 |
326 /// Returns whether [metadata] contains any annotation that is either equal to | 335 /// Returns whether [metadata] contains any annotation that is either equal to |
327 /// an annotation in [queryAnnotations] or whose type is a subclass of a type | 336 /// an annotation in [queryAnnotations] or whose type is a subclass of a type |
328 /// listed in [queryAnnotations]. This is equivalent to the check done in | 337 /// listed in [queryAnnotations]. This is equivalent to the check done in |
329 /// `src/common.dart#matchesAnnotation`, except that this is applied to | 338 /// `src/common.dart#matchesAnnotation`, except that this is applied to |
330 /// static metadata as it was provided by the analyzer. | 339 /// static metadata as it was provided by the analyzer. |
331 bool _matchesAnnotation(Iterable<ElementAnnotation> metadata, | 340 bool _matchesAnnotation( |
332 Iterable<Element> queryAnnotations) { | 341 Iterable<ElementAnnotation> metadata, Iterable<Element> queryAnnotations) { |
333 for (var meta in metadata) { | 342 for (var meta in metadata) { |
334 var element = meta.element; | 343 var element = meta.element; |
335 var exp; | 344 var exp; |
336 var type; | 345 var type; |
337 if (element is PropertyAccessorElement) { | 346 if (element is PropertyAccessorElement) { |
338 exp = element.variable; | 347 exp = element.variable; |
339 type = exp.evaluationResult.value.type; | 348 type = exp.evaluationResult.value.type; |
340 } else if (element is ConstructorElement) { | 349 } else if (element is ConstructorElement) { |
341 exp = element; | 350 exp = element; |
342 type = element.enclosingElement.type; | 351 type = element.enclosingElement.type; |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
379 | 388 |
380 /// If [withAnnotation] is not null, then it should be a list of types, so | 389 /// If [withAnnotation] is not null, then it should be a list of types, so |
381 /// only symbols that are annotated with instances of those types are | 390 /// only symbols that are annotated with instances of those types are |
382 /// included. | 391 /// included. |
383 final List<Element> withAnnotations; | 392 final List<Element> withAnnotations; |
384 | 393 |
385 /// If [matches] is not null, then only those fields, properties, or methods | 394 /// If [matches] is not null, then only those fields, properties, or methods |
386 /// that match will be included. | 395 /// that match will be included. |
387 final NameMatcher matches; | 396 final NameMatcher matches; |
388 | 397 |
389 const QueryOptions({ | 398 const QueryOptions({this.includeFields: true, this.includeProperties: true, |
390 this.includeFields: true, | 399 this.includeInherited: true, this.includeUpTo: null, |
391 this.includeProperties: true, | 400 this.excludeFinal: false, this.includeMethods: false, |
392 this.includeInherited: true, | 401 this.withAnnotations: null, this.matches: null}); |
393 this.includeUpTo: null, | |
394 this.excludeFinal: false, | |
395 this.includeMethods: false, | |
396 this.withAnnotations: null, | |
397 this.matches: null}); | |
398 } | 402 } |
399 | 403 |
400 /// Predicate that tells whether [name] should be included in query results. | 404 /// Predicate that tells whether [name] should be included in query results. |
401 typedef bool NameMatcher(String name); | 405 typedef bool NameMatcher(String name); |
OLD | NEW |