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 /// Implementation of the smoke services using mirrors. | 5 /// Implementation of the smoke services using mirrors. |
6 library smoke.mirrors; | 6 library smoke.mirrors; |
7 | 7 |
8 import 'dart:mirrors'; | 8 import 'dart:mirrors'; |
9 import 'package:smoke/smoke.dart'; | 9 import 'package:smoke/smoke.dart'; |
10 import 'package:logging/logging.dart'; | 10 import 'package:logging/logging.dart'; |
11 import 'src/common.dart'; | 11 import 'src/common.dart'; |
12 | 12 |
13 /// Set up the smoke package to use a mirror-based implementation. To tune what | 13 /// Set up the smoke package to use a mirror-based implementation. To tune what |
14 /// is preserved by `dart:mirrors`, use a @MirrorsUsed annotation and include | 14 /// is preserved by `dart:mirrors`, use a @MirrorsUsed annotation and include |
15 /// 'smoke.mirrors' in your override arguments. | 15 /// 'smoke.mirrors' in your override arguments. |
16 useMirrors() { | 16 useMirrors() { |
17 configure(new ReflectiveObjectAccessorService(), | 17 configure(new ReflectiveObjectAccessorService(), |
18 new ReflectiveTypeInspectorService(), | 18 new ReflectiveTypeInspectorService(), |
19 new ReflectiveSymbolConverterService()); | 19 new ReflectiveSymbolConverterService()); |
20 } | 20 } |
21 | 21 |
22 var _logger = new Logger('smoke.mirrors'); | 22 var _logger = new Logger('smoke.mirrors'); |
23 | 23 |
24 | |
25 /// Implements [ObjectAccessorService] using mirrors. | 24 /// Implements [ObjectAccessorService] using mirrors. |
26 class ReflectiveObjectAccessorService implements ObjectAccessorService { | 25 class ReflectiveObjectAccessorService implements ObjectAccessorService { |
27 read(Object object, Symbol name) => reflect(object).getField(name).reflectee; | 26 read(Object object, Symbol name) => reflect(object).getField(name).reflectee; |
28 | 27 |
29 void write(Object object, Symbol name, value) { | 28 void write(Object object, Symbol name, value) { |
30 reflect(object).setField(name, value); | 29 reflect(object).setField(name, value); |
31 } | 30 } |
32 | 31 |
33 invoke(receiver, Symbol methodName, List args, | 32 invoke(receiver, Symbol methodName, List args, |
34 {Map namedArgs, bool adjust: false}) { | 33 {Map namedArgs, bool adjust: false}) { |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
135 return new _MirrorDeclaration(mirror, declaration); | 134 return new _MirrorDeclaration(mirror, declaration); |
136 } | 135 } |
137 | 136 |
138 List<Declaration> query(Type type, QueryOptions options) { | 137 List<Declaration> query(Type type, QueryOptions options) { |
139 var mirror = reflectType(type); | 138 var mirror = reflectType(type); |
140 if (mirror is! ClassMirror) return null; | 139 if (mirror is! ClassMirror) return null; |
141 return _query(mirror, options); | 140 return _query(mirror, options); |
142 } | 141 } |
143 | 142 |
144 List<Declaration> _query(ClassMirror cls, QueryOptions options) { | 143 List<Declaration> _query(ClassMirror cls, QueryOptions options) { |
145 final visitParent = options.includeInherited && cls.superclass != null && | 144 final visitParent = options.includeInherited && cls.superclass != null && |
146 // TODO(sigmund): use _toType(cls.superclass) != options.includeUpTo | 145 // TODO(sigmund): use _toType(cls.superclass) != options.includeUpTo |
147 // when dartbug.com/16925 gets fixed (_toType fails in dart2js if | 146 // when dartbug.com/16925 gets fixed (_toType fails in dart2js if |
148 // applied to classes with type-arguments). | 147 // applied to classes with type-arguments). |
149 cls.superclass != reflectClass(options.includeUpTo); | 148 cls.superclass != reflectClass(options.includeUpTo); |
150 var result = visitParent ? _query(cls.superclass, options) : []; | 149 var result = visitParent ? _query(cls.superclass, options) : []; |
151 for (var member in cls.declarations.values) { | 150 for (var member in cls.declarations.values) { |
152 if (member is! VariableMirror && member is! MethodMirror) continue; | 151 if (member is! VariableMirror && member is! MethodMirror) continue; |
153 if (member.isStatic || member.isPrivate) continue; | 152 if (member.isStatic || member.isPrivate) continue; |
154 var name = member.simpleName; | 153 var name = member.simpleName; |
155 if (member is VariableMirror) { | 154 if (member is VariableMirror) { |
156 if (!options.includeFields) continue; | 155 if (!options.includeFields) continue; |
157 if (options.excludeFinal && member.isFinal) continue; | 156 if (options.excludeFinal && member.isFinal) continue; |
158 } | 157 } |
159 | 158 |
160 // TODO(sigmund): what if we have a setter but no getter? | 159 // TODO(sigmund): what if we have a setter but no getter? |
161 if (member is MethodMirror && member.isSetter) continue; | 160 if (member is MethodMirror && member.isSetter) continue; |
162 if (member is MethodMirror && member.isConstructor) continue; | 161 if (member is MethodMirror && member.isConstructor) continue; |
163 | 162 |
164 if (member is MethodMirror && member.isGetter) { | 163 if (member is MethodMirror && member.isGetter) { |
165 if (!options.includeProperties) continue; | 164 if (!options.includeProperties) continue; |
166 if (options.excludeFinal && !_hasSetter(cls, member)) continue; | 165 if (options.excludeFinal && !_hasSetter(cls, member)) continue; |
167 } | 166 } |
168 | 167 |
169 if (member is MethodMirror && member.isRegularMethod) { | 168 if (member is MethodMirror && member.isRegularMethod) { |
170 if (!options.includeMethods) continue; | 169 if (!options.includeMethods) continue; |
171 } | 170 } |
172 | 171 |
173 if (options.matches != null && !options.matches(name)) continue; | 172 if (options.matches != null && !options.matches(name)) continue; |
174 | 173 |
175 var annotations = | 174 var annotations = member.metadata.map((m) => m.reflectee).toList(); |
176 member.metadata.map((m) => m.reflectee).toList(); | |
177 if (options.withAnnotations != null && | 175 if (options.withAnnotations != null && |
178 !matchesAnnotation(annotations, options.withAnnotations)) { | 176 !matchesAnnotation(annotations, options.withAnnotations)) { |
179 continue; | 177 continue; |
180 } | 178 } |
181 | 179 |
182 // TODO(sigmund): should we cache parts of this declaration so we don't | 180 // TODO(sigmund): should we cache parts of this declaration so we don't |
183 // compute them twice? For example, this chould be `new Declaration(name, | 181 // compute them twice? For example, this chould be `new Declaration(name, |
184 // type, ...)` and we could reuse what we computed above to implement the | 182 // type, ...)` and we could reuse what we computed above to implement the |
185 // query filtering. Note, when I tried to eagerly compute everything, I | 183 // query filtering. Note, when I tried to eagerly compute everything, I |
186 // run into trouble with type (`type = _toType(member.type)`), dart2js | 184 // run into trouble with type (`type = _toType(member.type)`), dart2js |
187 // failed when the underlying types had type-arguments (see | 185 // failed when the underlying types had type-arguments (see |
188 // dartbug.com/16925). | 186 // dartbug.com/16925). |
189 result.add(new _MirrorDeclaration(cls, member)); | 187 result.add(new _MirrorDeclaration(cls, member)); |
190 } | 188 } |
191 | 189 |
192 return result; | 190 return result; |
193 } | 191 } |
194 } | 192 } |
195 | 193 |
196 /// Implements [SymbolConverterService] using mirrors. | 194 /// Implements [SymbolConverterService] using mirrors. |
197 class ReflectiveSymbolConverterService implements SymbolConverterService { | 195 class ReflectiveSymbolConverterService implements SymbolConverterService { |
198 String symbolToName(Symbol symbol) => MirrorSystem.getName(symbol); | 196 String symbolToName(Symbol symbol) => MirrorSystem.getName(symbol); |
199 Symbol nameToSymbol(String name) => new Symbol(name); | 197 Symbol nameToSymbol(String name) => new Symbol(name); |
200 } | 198 } |
201 | 199 |
202 | |
203 // TODO(jmesserly): workaround for: | 200 // TODO(jmesserly): workaround for: |
204 // https://code.google.com/p/dart/issues/detail?id=10029 | 201 // https://code.google.com/p/dart/issues/detail?id=10029 |
205 Symbol _setterName(Symbol getter) => | 202 Symbol _setterName(Symbol getter) => |
206 new Symbol('${MirrorSystem.getName(getter)}='); | 203 new Symbol('${MirrorSystem.getName(getter)}='); |
207 | 204 |
208 | |
209 ClassMirror _safeSuperclass(ClassMirror type) { | 205 ClassMirror _safeSuperclass(ClassMirror type) { |
210 try { | 206 try { |
211 var t = type.superclass; | 207 var t = type.superclass; |
212 // TODO(sigmund): workaround for darbug.com/17779. | 208 // TODO(sigmund): workaround for darbug.com/17779. |
213 // Interceptor is leaked by dart2js. It has the same methods as Object | 209 // Interceptor is leaked by dart2js. It has the same methods as Object |
214 // (including noSuchMethod), and our code above assumes that it doesn't | 210 // (including noSuchMethod), and our code above assumes that it doesn't |
215 // exist. Most queries exclude Object, so they should exclude Interceptor | 211 // exist. Most queries exclude Object, so they should exclude Interceptor |
216 // too. We don't check for t.simpleName == #Interceptor because depending on | 212 // too. We don't check for t.simpleName == #Interceptor because depending on |
217 // dart2js optimizations it may be #Interceptor or #num/Interceptor. | 213 // dart2js optimizations it may be #Interceptor or #num/Interceptor. |
218 // Checking for a private library seems to reliably filter this out. | 214 // Checking for a private library seems to reliably filter this out. |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
273 | 269 |
274 bool get isField => _original is VariableMirror; | 270 bool get isField => _original is VariableMirror; |
275 | 271 |
276 bool get isProperty => | 272 bool get isProperty => |
277 _original is MethodMirror && !_original.isRegularMethod; | 273 _original is MethodMirror && !_original.isRegularMethod; |
278 | 274 |
279 bool get isMethod => !isField && !isProperty; | 275 bool get isMethod => !isField && !isProperty; |
280 | 276 |
281 /// If this is a property, whether it's read only (final fields or properties | 277 /// If this is a property, whether it's read only (final fields or properties |
282 /// with no setter). | 278 /// with no setter). |
283 bool get isFinal => | 279 bool get isFinal => (_original is VariableMirror && _original.isFinal) || |
284 (_original is VariableMirror && _original.isFinal) || | 280 (_original is MethodMirror && |
285 (_original is MethodMirror && _original.isGetter && | 281 _original.isGetter && |
286 !_hasSetter(_cls, _original)); | 282 !_hasSetter(_cls, _original)); |
287 | 283 |
288 /// If this is a property, it's declared type (including dynamic if it's not | 284 /// If this is a property, it's declared type (including dynamic if it's not |
289 /// declared). For methods, the returned type. | 285 /// declared). For methods, the returned type. |
290 Type get type { | 286 Type get type { |
291 if (_original is MethodMirror && _original.isRegularMethod) { | 287 if (_original is MethodMirror && _original.isRegularMethod) { |
292 return Function; | 288 return Function; |
293 } | 289 } |
294 var typeMirror = _original is VariableMirror ? _original.type | 290 var typeMirror = |
295 : _original.returnType; | 291 _original is VariableMirror ? _original.type : _original.returnType; |
296 return _toType(typeMirror); | 292 return _toType(typeMirror); |
297 } | 293 } |
298 | 294 |
299 /// Whether this symbol is static. | 295 /// Whether this symbol is static. |
300 bool get isStatic => _original.isStatic; | 296 bool get isStatic => _original.isStatic; |
301 | 297 |
302 /// List of annotations in this declaration. | 298 /// List of annotations in this declaration. |
303 List get annotations => _original.metadata.map((a) => a.reflectee).toList(); | 299 List get annotations => _original.metadata.map((a) => a.reflectee).toList(); |
304 | 300 |
305 int get hashCode => name.hashCode; | 301 int get hashCode => name.hashCode; |
306 operator ==(other) => other is Declaration && name == other.name && | 302 operator ==(other) => other is Declaration && |
307 kind == other.kind && isFinal == other.isFinal && | 303 name == other.name && |
308 type == other.type && isStatic == other.isStatic && | 304 kind == other.kind && |
| 305 isFinal == other.isFinal && |
| 306 type == other.type && |
| 307 isStatic == other.isStatic && |
309 compareLists(annotations, other.annotations); | 308 compareLists(annotations, other.annotations); |
310 String toString() => (new StringBuffer() | 309 String toString() => (new StringBuffer() |
311 ..write('(mirror-based-declaration ') | 310 ..write('(mirror-based-declaration ') |
312 ..write(name) | 311 ..write(name) |
313 ..write(isField ? ' (field) ' | 312 ..write( |
314 : (isProperty ? ' (property) ' : ' (method) ')) | 313 isField ? ' (field) ' : (isProperty ? ' (property) ' : ' (method) ')) |
315 ..write(isFinal ? 'final ' : '') | 314 ..write(isFinal ? 'final ' : '') |
316 ..write(isStatic ? 'static ' : '') | 315 ..write(isStatic ? 'static ' : '') |
317 ..write(annotations) | 316 ..write(annotations) |
318 ..write(')')).toString(); | 317 ..write(')')).toString(); |
319 } | 318 } |
OLD | NEW |