| 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 |