Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(53)

Side by Side Diff: pkg/smoke/lib/mirrors.dart

Issue 169913004: Adding package:smoke. This CL includes the intial APIs, a mirror-based (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 /// Implementation of the smoke services using mirrors.
6 library smoke.mirrors;
7
8 import 'dart:mirrors';
9 import 'package:smoke/smoke.dart';
10 import 'package:logging/logging.dart';
11 import 'src/common.dart';
12
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
15 /// 'smoke.mirrors' in your override arguments.
16 useMirrors() {
17 configure(new ReflectiveObjectAccessorService(),
18 new ReflectiveTypeInspectorService(),
19 new ReflectiveSymbolConverterService());
20 }
21
22 var _logger = new Logger('smoke.mirrors');
23
24
25 /// Implements [ObjectAccessorService] using mirrors.
26 class ReflectiveObjectAccessorService implements ObjectAccessorService {
27 read(Object object, Symbol name) {
28 var decl = getDeclaration(object.runtimeType, name);
29 if (decl != null && decl.isMethod) {
30 // TODO(sigmund,jmesserly): remove this once dartbug.com/13002 is fixed.
31 return new _MethodClosure(object, name);
32 } else {
33 return reflect(object).getField(name).reflectee;
34 }
35 }
36
37 void write(Object object, Symbol name, value) {
38 reflect(object).setField(name, value);
39 }
40
41 invoke(receiver, Symbol methodName, List args,
42 {Map namedArgs, bool adjust: false}) {
43 var receiverMirror;
44 var method;
45 if (receiver is Type) {
46 receiverMirror = reflectType(receiver);
47 method = receiverMirror.declarations[methodName];
48 } else {
49 receiverMirror = reflect(receiver);
50 method = _findMethod(receiverMirror.type, methodName);
51 }
52 if (method != null && adjust) {
53 var required = 0;
54 var optional = 0;
55 for (var p in method.parameters) {
56 if (p.isOptional) {
57 if (!p.isNamed) optional++;
58 } else {
59 required++;
60 }
61 }
62 args = adjustList(args, required, required + optional);
63 }
64 return receiverMirror.invoke(methodName, args, namedArgs).reflectee;
65 }
66 }
67
68 /// Implements [TypeInspectorService] using mirrors.
69 class ReflectiveTypeInspectorService implements TypeInspectorService {
70 bool hasGetter(Type type, Symbol name) {
71 var mirror = reflectType(type);
72 if (mirror is! ClassMirror) return false;
73 while (mirror != _objectType) {
74 final members = mirror.declarations;
75 if (members.containsKey(name)) return true;
76 mirror = _safeSuperclass(mirror);
77 }
78 return false;
79 }
80
81 bool hasSetter(Type type, Symbol name) {
82 var mirror = reflectType(type);
83 if (mirror is! ClassMirror) return false;
84 var setterName = _setterName(name);
85 while (mirror != _objectType) {
86 final members = mirror.declarations;
87 var declaration = members[name];
88 if (declaration is VariableMirror && !declaration.isFinal) return true;
89 if (members.containsKey(setterName)) return true;
90 mirror = _safeSuperclass(mirror);
91 }
92 return false;
93 }
94
95 bool hasInstanceMethod(Type type, Symbol name) {
96 var mirror = reflectType(type);
97 if (mirror is! ClassMirror) return false;
98 while (mirror != _objectType) {
99 final m = mirror.declarations[name];
100 if (m is MethodMirror && m.isRegularMethod && !m.isStatic) return true;
101 mirror = _safeSuperclass(mirror);
102 }
103 return false;
104 }
105
106 bool hasStaticMethod(Type type, Symbol name) {
107 var mirror = reflectType(type);
108 if (mirror is! ClassMirror) return false;
109 final m = mirror.declarations[name];
110 return m is MethodMirror && m.isRegularMethod && m.isStatic;
111 }
112
113 Declaration getDeclaration(Type type, Symbol name) {
114 var mirror = reflectType(type);
115 if (mirror is! ClassMirror) return null;
116
117 var declaration;
118 while (mirror != _objectType) {
119 final members = mirror.declarations;
120 if (members.containsKey(name)) {
121 declaration = members[name];
122 break;
123 }
124 mirror = _safeSuperclass(mirror);
125 }
126 if (declaration == null) {
127 _logger.severe("declaration doesn't exists ($type.$name).");
128 return null;
129 }
130 return new _MirrorDeclaration(mirror, declaration);
131 }
132
133 List<Declaration> query(Type type, QueryOptions options) {
134 var mirror = reflectType(type);
135 if (mirror is! ClassMirror) return null;
136 return _query(mirror, options);
137 }
138
139 List<Declaration> _query(ClassMirror cls, QueryOptions options) {
140 var result = (!options.includeInherited || cls.superclass == _objectType)
141 ? [] : _query(cls.superclass, options);
142 for (var member in cls.declarations.values) {
143 if (member is! VariableMirror && member is! MethodMirror) continue;
144 if (member.isStatic || member.isPrivate) continue;
145 var name = member.simpleName;
146 bool isMethod = false;
147 if (member is VariableMirror) {
148 if (!options.includeProperties) continue;
149 if (options.excludeFinal && member.isFinal) continue;
150 }
151
152 // TODO(sigmund): what if we have a setter but no getter?
153 if (member is MethodMirror && member.isSetter) continue;
154 if (member is MethodMirror && member.isConstructor) continue;
155
156 if (member is MethodMirror && member.isGetter) {
157 if (!options.includeProperties) continue;
158 if (options.excludeFinal && !_hasSetter(cls, member)) continue;
159 }
160
161 if (member is MethodMirror && member.isRegularMethod) {
162 if (!options.includeMethods) continue;
163 isMethod = true;
164 }
165
166 var annotations =
167 member.metadata.map((m) => m.reflectee).toList();
168 if (options.withAnnotations != null &&
169 !matchesAnnotation(annotations, options.withAnnotations)) {
170 continue;
171 }
172
173 // TODO(sigmund): should we cache parts of this declaration so we don't
174 // compute them twice? For example, this chould be `new Declaration(name,
175 // type, ...)` and we could reuse what we computed above to implement the
176 // query filtering. Note, when I tried to eagerly compute everything, I
177 // run into trouble with type (`type = _toType(member.type)`), dart2js
178 // failed when the underlying types had type-arguments (see
179 // dartbug.com/16925).
180 result.add(new _MirrorDeclaration(cls, member));
181 }
182
183 return result;
184 }
185 }
186
187 /// Implements [SymbolConverterService] using mirrors.
188 class ReflectiveSymbolConverterService implements SymbolConverterService {
189 String symbolToName(Symbol symbol) => MirrorSystem.getName(symbol);
190 Symbol nameToSymbol(String name) => new Symbol(name);
191 }
192
193
194 // TODO(jmesserly): workaround for:
195 // https://code.google.com/p/dart/issues/detail?id=10029
196 Symbol _setterName(Symbol getter) =>
197 new Symbol('${MirrorSystem.getName(getter)}=');
198
199
200 ClassMirror _safeSuperclass(ClassMirror type) {
201 try {
202 return type.superclass;
203 } /*on UnsupportedError*/ catch (e) {
204 // Note: dart2js throws UnsupportedError when the type is not
205 // reflectable.
206 // TODO(jmesserly): dart2js also throws a NoSuchMethodError if the `type` is
207 // a bound generic, because they are not fully implemented. See
208 // https://code.google.com/p/dart/issues/detail?id=15573
Jennifer Messerly 2014/02/20 21:24:47 fyi -- this one has been fixed, I think we can now
Siggi Cherem (dart-lang) 2014/02/20 22:19:48 cool. good to know.
209 return _objectType;
210 }
211 }
212
213 MethodMirror _findMethod(ClassMirror type, Symbol name) {
214 do {
215 var member = type.declarations[name];
216 if (member is MethodMirror) return member;
217 type = type.superclass;
218 } while (type != null);
219 }
220
221 // When recursively looking for symbols up the type-hierarchy it's generally a
222 // good idea to stop at Object, since we know it doesn't have what we want.
223 // TODO(jmesserly): This is also a workaround for what appears to be a V8
224 // bug introduced between Chrome 31 and 32. After 32
225 // JsClassMirror.declarations on Object calls
226 // JsClassMirror.typeVariables, which tries to get the _jsConstructor's
227 // .prototype["<>"]. This ends up getting the "" property instead, maybe
228 // because "<>" doesn't exist, and gets ";" which then blows up because
229 // the code later on expects a List of ints.
230 final _objectType = reflectClass(Object);
231
232 bool _hasSetter(ClassMirror cls, MethodMirror getter) {
233 var setterName = new Symbol('${MirrorSystem.getName(getter.simpleName)}=');
Jennifer Messerly 2014/02/20 21:24:47 use _setterName here?
Siggi Cherem (dart-lang) 2014/02/20 22:19:48 Done.
234 var mirror = cls.declarations[setterName];
235 return mirror is MethodMirror && mirror.isSetter;
236 }
237
238 Type _toType(TypeMirror t) {
239 if (t is ClassMirror) return t.reflectedType;
240 if (t == null || t.qualifiedName != #dynamic) {
241 _logger.warning('unknown type ($t).');
242 }
243 return dynamic;
244 }
245
246 class _MirrorDeclaration implements Declaration {
247 final ClassMirror _cls;
248 final _original;
249
250 _MirrorDeclaration(this._cls, DeclarationMirror this._original);
251
252 Symbol get name => _original.simpleName;
253
254 /// Whether the symbol is a property (either this or [isMethod] is true).
255 bool get isProperty => _original is VariableMirror ||
256 (_original is MethodMirror && !_original.isRegularMethod);
257
258 /// Whether the symbol is a method (either this or [isProperty] is true)
259 bool get isMethod => !isProperty;
260
261 /// If this is a property, whether it's read only (final fields or properties
262 /// with no setter).
263 bool get isFinal =>
264 (_original is VariableMirror && _original.isFinal) ||
265 (_original is MethodMirror && _original.isGetter &&
266 !_hasSetter(_cls, _original));
267
268 /// If this is a property, it's declared type (including dynamic if it's not
269 /// declared). For methods, the returned type.
270 Type get type {
271 if (_original is MethodMirror && _original.isRegularMethod) {
272 return Function;
273 }
274 var typeMirror = _original is VariableMirror ? _original.type
275 : _original.returnType;
276 return _toType(typeMirror);
277 }
278
279 /// Whether this symbol is static.
280 bool get isStatic => _original.isStatic;
281
282 /// List of annotations in this declaration.
283 List get annotations => _original.metadata.map((a) => a.reflectee).toList();
284
285 String toString() {
286 return (new StringBuffer()
287 ..write('[declaration ')
288 ..write(name)
289 ..write(isProperty ? ' (property) ' : ' (method) ')
290 ..write(isFinal ? 'final ' : '')
291 ..write(isStatic ? 'static ' : '')
292 ..write(annotations)
293 ..write(']')).toString();
294 }
295 }
296
297 class _MethodClosure extends Function {
298 final receiver;
299 final Symbol methodName;
300
301 _MethodClosure(this.receiver, this.methodName);
302
303 // We shouldn't need to include [call] here, but we do to work around an
304 // analyzer bug (see dartbug.com/16078). Interestingly, if [call] is invoked
305 // with the wrong number of arguments, then noSuchMethod is anyways invoked
306 // instead.
307 call() => invoke(receiver, methodName, const []);
308
309 noSuchMethod(Invocation inv) {
310 if (inv.memberName != #call) return null;
311 return invoke(receiver, methodName,
312 inv.positionalArguments, namedArgs: inv.namedArguments);
313 }
314 }
OLDNEW
« pkg/smoke/README.md ('K') | « pkg/smoke/README.md ('k') | pkg/smoke/lib/smoke.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698