OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013, 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 library dart._js_mirrors; | |
6 | |
7 import 'dart:_js_embedded_names' show | |
8 JsGetName, | |
9 ALL_CLASSES, | |
10 LAZIES, | |
11 LIBRARIES, | |
12 STATICS, | |
13 TYPE_INFORMATION, | |
14 TYPEDEF_PREDICATE_PROPERTY_NAME, | |
15 TYPEDEF_TYPE_PROPERTY_NAME; | |
16 | |
17 import 'dart:collection' show | |
18 UnmodifiableListView, | |
19 UnmodifiableMapView; | |
20 | |
21 import 'dart:mirrors'; | |
22 | |
23 import 'dart:_foreign_helper' show | |
24 JS, | |
25 JS_GET_FLAG, | |
26 JS_CURRENT_ISOLATE, | |
27 JS_CURRENT_ISOLATE_CONTEXT, | |
28 JS_EMBEDDED_GLOBAL, | |
29 JS_GET_NAME, | |
30 JS_TYPEDEF_TAG, | |
31 JS_FUNCTION_TYPE_RETURN_TYPE_TAG, | |
32 JS_FUNCTION_TYPE_VOID_RETURN_TAG, | |
33 JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG, | |
34 JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG, | |
35 JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG; | |
36 | |
37 | |
38 import 'dart:_internal' as _symbol_dev; | |
39 | |
40 import 'dart:_js_helper' show | |
41 BoundClosure, | |
42 CachedInvocation, | |
43 Closure, | |
44 JSInvocationMirror, | |
45 JsCache, | |
46 Null, | |
47 Primitives, | |
48 ReflectionInfo, | |
49 RuntimeError, | |
50 TearOffClosure, | |
51 TypeVariable, | |
52 UnimplementedNoSuchMethodError, | |
53 createRuntimeType, | |
54 createUnmangledInvocationMirror, | |
55 getMangledTypeName, | |
56 getMetadata, | |
57 getType, | |
58 getRuntimeType, | |
59 isDartFunctionType, | |
60 runtimeTypeToString, | |
61 setRuntimeTypeInfo, | |
62 throwInvalidReflectionError, | |
63 TypeImpl, | |
64 deferredLoadHook; | |
65 | |
66 import 'dart:_interceptors' show | |
67 Interceptor, | |
68 JSArray, | |
69 JSExtendableArray, | |
70 getInterceptor; | |
71 | |
72 import 'dart:_js_names'; | |
73 | |
74 const String METHODS_WITH_OPTIONAL_ARGUMENTS = r'$methodsWithOptionalArguments'; | |
75 | |
76 bool hasReflectableProperty(var jsFunction) { | |
77 return JS('bool', '# in #', JS_GET_NAME(JsGetName.REFLECTABLE), jsFunction); | |
78 } | |
79 | |
80 /// No-op method that is called to inform the compiler that tree-shaking needs | |
81 /// to be disabled. | |
82 disableTreeShaking() => preserveNames(); | |
83 | |
84 /// No-op method that is called to inform the compiler that metadata must be | |
85 /// preserved at runtime. | |
86 preserveMetadata() {} | |
87 | |
88 /// No-op method that is called to inform the compiler that the compiler must | |
89 /// preserve the URIs. | |
90 preserveUris() {} | |
91 | |
92 /// No-op method that is called to inform the compiler that the compiler must | |
93 /// preserve the library names. | |
94 preserveLibraryNames() {} | |
95 | |
96 String getName(Symbol symbol) { | |
97 preserveNames(); | |
98 return n(symbol); | |
99 } | |
100 | |
101 class JsMirrorSystem implements MirrorSystem { | |
102 UnmodifiableMapView<Uri, LibraryMirror> _cachedLibraries; | |
103 | |
104 final IsolateMirror isolate = new JsIsolateMirror(); | |
105 | |
106 JsTypeMirror get dynamicType => _dynamicType; | |
107 JsTypeMirror get voidType => _voidType; | |
108 | |
109 static final JsTypeMirror _dynamicType = | |
110 new JsTypeMirror(const Symbol('dynamic')); | |
111 static final JsTypeMirror _voidType = new JsTypeMirror(const Symbol('void')); | |
112 | |
113 static Map<String, List<LibraryMirror>> _librariesByName; | |
114 | |
115 // Will be set to `true` when we have installed a hook on [deferredLoadHook] | |
116 // to avoid installing it multiple times. | |
117 static bool _hasInstalledDeferredLoadHook = false; | |
118 | |
119 static Map<String, List<LibraryMirror>> get librariesByName { | |
120 if (_librariesByName == null) { | |
121 _librariesByName = computeLibrariesByName(); | |
122 if (!_hasInstalledDeferredLoadHook) { | |
123 _hasInstalledDeferredLoadHook = true; | |
124 // After a deferred import has been loaded new libraries might have | |
125 // been created, so in the hook we erase _librariesByName, so it will be | |
126 // recomputed on the next access. | |
127 deferredLoadHook = () => _librariesByName = null; | |
128 } | |
129 } | |
130 return _librariesByName; | |
131 } | |
132 | |
133 Map<Uri, LibraryMirror> get libraries { | |
134 if (_cachedLibraries != null) return _cachedLibraries; | |
135 Map<Uri, LibraryMirror> result = new Map(); | |
136 for (List<LibraryMirror> list in librariesByName.values) { | |
137 for (LibraryMirror library in list) { | |
138 result[library.uri] = library; | |
139 } | |
140 } | |
141 return _cachedLibraries = | |
142 new UnmodifiableMapView<Uri, LibraryMirror>(result); | |
143 } | |
144 | |
145 LibraryMirror findLibrary(Symbol libraryName) { | |
146 return librariesByName[n(libraryName)].single; | |
147 } | |
148 | |
149 static Map<String, List<LibraryMirror>> computeLibrariesByName() { | |
150 disableTreeShaking(); | |
151 var result = new Map<String, List<LibraryMirror>>(); | |
152 var jsLibraries = JS_EMBEDDED_GLOBAL('JSExtendableArray|Null', LIBRARIES); | |
153 if (jsLibraries == null) return result; | |
154 for (List data in jsLibraries) { | |
155 String name = data[0]; | |
156 String uriString = data[1]; | |
157 Uri uri; | |
158 // The Uri has been compiled out. Create a URI from the simple name. | |
159 if (uriString != "") { | |
160 uri = Uri.parse(uriString); | |
161 } else { | |
162 uri = new Uri(scheme: 'https', | |
163 host: 'dartlang.org', | |
164 path: 'dart2js-stripped-uri', | |
165 queryParameters: { 'lib': name }); | |
166 } | |
167 List<String> classes = data[2]; | |
168 List<String> functions = data[3]; | |
169 var metadataFunction = data[4]; | |
170 var fields = data[5]; | |
171 bool isRoot = data[6]; | |
172 var globalObject = data[7]; | |
173 List metadata = (metadataFunction == null) | |
174 ? const [] : JS('List', '#()', metadataFunction); | |
175 var libraries = result.putIfAbsent(name, () => <LibraryMirror>[]); | |
176 libraries.add( | |
177 new JsLibraryMirror( | |
178 s(name), uri, classes, functions, metadata, fields, isRoot, | |
179 globalObject)); | |
180 } | |
181 return result; | |
182 } | |
183 } | |
184 | |
185 abstract class JsMirror implements Mirror { | |
186 const JsMirror(); | |
187 | |
188 String get _prettyName; | |
189 | |
190 String toString() => _prettyName; | |
191 | |
192 // TODO(ahe): Remove this method from the API. | |
193 MirrorSystem get mirrors => currentJsMirrorSystem; | |
194 | |
195 _getField(JsMirror receiver) { | |
196 throw new UnimplementedError(); | |
197 } | |
198 | |
199 void _setField(JsMirror receiver, Object arg) { | |
200 throw new UnimplementedError(); | |
201 } | |
202 | |
203 _loadField(String name) { | |
204 throw new UnimplementedError(); | |
205 } | |
206 | |
207 void _storeField(String name, Object arg) { | |
208 throw new UnimplementedError(); | |
209 } | |
210 } | |
211 | |
212 // This class is somewhat silly in the current implementation. | |
213 class JsIsolateMirror extends JsMirror implements IsolateMirror { | |
214 final _isolateContext = JS_CURRENT_ISOLATE_CONTEXT(); | |
215 | |
216 String get _prettyName => 'Isolate'; | |
217 | |
218 String get debugName { | |
219 String id = _isolateContext == null ? 'X' : _isolateContext.id.toString(); | |
220 // Using name similar to what the VM uses. | |
221 return '${n(rootLibrary.simpleName)}-$id'; | |
222 } | |
223 | |
224 bool get isCurrent => JS_CURRENT_ISOLATE_CONTEXT() == _isolateContext; | |
225 | |
226 LibraryMirror get rootLibrary { | |
227 return currentJsMirrorSystem.libraries.values.firstWhere( | |
228 (JsLibraryMirror library) => library._isRoot); | |
229 } | |
230 } | |
231 | |
232 abstract class JsDeclarationMirror extends JsMirror | |
233 implements DeclarationMirror { | |
234 final Symbol simpleName; | |
235 | |
236 const JsDeclarationMirror(this.simpleName); | |
237 | |
238 Symbol get qualifiedName => computeQualifiedName(owner, simpleName); | |
239 | |
240 bool get isPrivate => n(simpleName).startsWith('_'); | |
241 | |
242 bool get isTopLevel => owner != null && owner is LibraryMirror; | |
243 | |
244 // TODO(ahe): This should use qualifiedName. | |
245 String toString() => "$_prettyName on '${n(simpleName)}'"; | |
246 | |
247 List<JsMethodMirror> get _methods { | |
248 throw new RuntimeError('Should not call _methods'); | |
249 } | |
250 | |
251 _invoke(List positionalArguments, Map<Symbol, dynamic> namedArguments) { | |
252 throw new RuntimeError('Should not call _invoke'); | |
253 } | |
254 | |
255 // TODO(ahe): Implement this. | |
256 SourceLocation get location => throw new UnimplementedError(); | |
257 } | |
258 | |
259 class JsTypeVariableMirror extends JsTypeMirror implements TypeVariableMirror { | |
260 final DeclarationMirror owner; | |
261 final TypeVariable _typeVariable; | |
262 final int _metadataIndex; | |
263 TypeMirror _cachedUpperBound; | |
264 | |
265 JsTypeVariableMirror(TypeVariable typeVariable, this.owner, | |
266 this._metadataIndex) | |
267 : this._typeVariable = typeVariable, | |
268 super(s(typeVariable.name)); | |
269 | |
270 bool operator ==(other) { | |
271 return (other is JsTypeVariableMirror && | |
272 simpleName == other.simpleName && | |
273 owner == other.owner); | |
274 } | |
275 | |
276 int get hashCode { | |
277 int code = 0x3FFFFFFF & (JsTypeVariableMirror).hashCode; | |
278 code ^= 17 * simpleName.hashCode; | |
279 code ^= 19 * owner.hashCode; | |
280 return code; | |
281 } | |
282 | |
283 String get _prettyName => 'TypeVariableMirror'; | |
284 | |
285 bool get isTopLevel => false; | |
286 bool get isStatic => false; | |
287 | |
288 TypeMirror get upperBound { | |
289 if (_cachedUpperBound != null) return _cachedUpperBound; | |
290 return _cachedUpperBound = typeMirrorFromRuntimeTypeRepresentation( | |
291 owner, getType(_typeVariable.bound)); | |
292 } | |
293 | |
294 bool isSubtypeOf(TypeMirror other) => throw new UnimplementedError(); | |
295 bool isAssignableTo(TypeMirror other) => throw new UnimplementedError(); | |
296 | |
297 _asRuntimeType() => _metadataIndex; | |
298 } | |
299 | |
300 class JsTypeMirror extends JsDeclarationMirror implements TypeMirror { | |
301 JsTypeMirror(Symbol simpleName) | |
302 : super(simpleName); | |
303 | |
304 String get _prettyName => 'TypeMirror'; | |
305 | |
306 DeclarationMirror get owner => null; | |
307 | |
308 // TODO(ahe): Doesn't match the specification, see http://dartbug.com/11569. | |
309 bool get isTopLevel => true; | |
310 | |
311 // TODO(ahe): Implement these. | |
312 List<InstanceMirror> get metadata => throw new UnimplementedError(); | |
313 | |
314 bool get hasReflectedType => false; | |
315 Type get reflectedType { | |
316 throw new UnsupportedError("This type does not support reflectedType"); | |
317 } | |
318 | |
319 List<TypeVariableMirror> get typeVariables => const <TypeVariableMirror>[]; | |
320 List<TypeMirror> get typeArguments => const <TypeMirror>[]; | |
321 | |
322 bool get isOriginalDeclaration => true; | |
323 TypeMirror get originalDeclaration => this; | |
324 | |
325 bool isSubtypeOf(TypeMirror other) => throw new UnimplementedError(); | |
326 bool isAssignableTo(TypeMirror other) => throw new UnimplementedError(); | |
327 | |
328 _asRuntimeType() { | |
329 if (this == JsMirrorSystem._dynamicType) return null; | |
330 if (this == JsMirrorSystem._voidType) return null; | |
331 throw new RuntimeError('Should not call _asRuntimeType'); | |
332 } | |
333 } | |
334 | |
335 class JsLibraryMirror extends JsDeclarationMirror with JsObjectMirror | |
336 implements LibraryMirror { | |
337 final Uri _uri; | |
338 final List<String> _classes; | |
339 final List<String> _functions; | |
340 final List _metadata; | |
341 final String _compactFieldSpecification; | |
342 final bool _isRoot; | |
343 final _globalObject; | |
344 List<JsMethodMirror> _cachedFunctionMirrors; | |
345 List<VariableMirror> _cachedFields; | |
346 UnmodifiableMapView<Symbol, ClassMirror> _cachedClasses; | |
347 UnmodifiableMapView<Symbol, MethodMirror> _cachedFunctions; | |
348 UnmodifiableMapView<Symbol, MethodMirror> _cachedGetters; | |
349 UnmodifiableMapView<Symbol, MethodMirror> _cachedSetters; | |
350 UnmodifiableMapView<Symbol, VariableMirror> _cachedVariables; | |
351 UnmodifiableMapView<Symbol, Mirror> _cachedMembers; | |
352 UnmodifiableMapView<Symbol, DeclarationMirror> _cachedDeclarations; | |
353 UnmodifiableListView<InstanceMirror> _cachedMetadata; | |
354 | |
355 JsLibraryMirror(Symbol simpleName, | |
356 this._uri, | |
357 this._classes, | |
358 this._functions, | |
359 this._metadata, | |
360 this._compactFieldSpecification, | |
361 this._isRoot, | |
362 this._globalObject) | |
363 : super(simpleName) { | |
364 preserveLibraryNames(); | |
365 } | |
366 | |
367 String get _prettyName => 'LibraryMirror'; | |
368 | |
369 Uri get uri { | |
370 preserveUris(); | |
371 return _uri; | |
372 } | |
373 | |
374 Symbol get qualifiedName => simpleName; | |
375 | |
376 List<JsMethodMirror> get _methods => _functionMirrors; | |
377 | |
378 Map<Symbol, ClassMirror> get __classes { | |
379 if (_cachedClasses != null) return _cachedClasses; | |
380 var result = new Map(); | |
381 for (String className in _classes) { | |
382 var cls = reflectClassByMangledName(className); | |
383 if (cls is ClassMirror) { | |
384 cls = cls.originalDeclaration; | |
385 } | |
386 if (cls is JsClassMirror) { | |
387 result[cls.simpleName] = cls; | |
388 cls._owner = this; | |
389 } else if (cls is JsTypedefMirror) { | |
390 result[cls.simpleName] = cls; | |
391 } | |
392 } | |
393 return _cachedClasses = | |
394 new UnmodifiableMapView<Symbol, ClassMirror>(result); | |
395 } | |
396 | |
397 InstanceMirror setField(Symbol fieldName, Object arg) { | |
398 String name = n(fieldName); | |
399 if (name.endsWith('=')) throw new ArgumentError(''); | |
400 var mirror = __functions[s('$name=')]; | |
401 if (mirror == null) mirror = __variables[fieldName]; | |
402 if (mirror == null) { | |
403 throw new NoSuchStaticMethodError.method( | |
404 null, setterSymbol(fieldName), [arg], null); | |
405 } | |
406 mirror._setField(this, arg); | |
407 return reflect(arg); | |
408 } | |
409 | |
410 InstanceMirror getField(Symbol fieldName) { | |
411 JsMirror mirror = __members[fieldName]; | |
412 if (mirror == null) { | |
413 throw new NoSuchStaticMethodError.method(null, fieldName, [], null); | |
414 } | |
415 if (mirror is! MethodMirror) return reflect(mirror._getField(this)); | |
416 JsMethodMirror methodMirror = mirror; | |
417 if (methodMirror.isGetter) return reflect(mirror._getField(this)); | |
418 assert(methodMirror.isRegularMethod); | |
419 var getter = JS("", "#['\$getter']", methodMirror._jsFunction); | |
420 if (getter == null) throw new UnimplementedError(); | |
421 return reflect(JS("", "#()", getter)); | |
422 } | |
423 | |
424 InstanceMirror invoke(Symbol memberName, | |
425 List positionalArguments, | |
426 [Map<Symbol, dynamic> namedArguments]) { | |
427 if (namedArguments != null && !namedArguments.isEmpty) { | |
428 throw new UnsupportedError('Named arguments are not implemented.'); | |
429 } | |
430 JsDeclarationMirror mirror = __members[memberName]; | |
431 | |
432 if (mirror is JsMethodMirror && !mirror.canInvokeReflectively()) { | |
433 throwInvalidReflectionError(n(memberName)); | |
434 } | |
435 if (mirror == null || mirror is JsMethodMirror && mirror.isSetter) { | |
436 throw new NoSuchStaticMethodError.method( | |
437 null, memberName, positionalArguments, namedArguments); | |
438 } | |
439 if (mirror is JsMethodMirror && !mirror.isGetter) { | |
440 return reflect(mirror._invoke(positionalArguments, namedArguments)); | |
441 } | |
442 return getField(memberName) | |
443 .invoke(#call, positionalArguments, namedArguments); | |
444 } | |
445 | |
446 _loadField(String name) { | |
447 // TODO(ahe): What about lazily initialized fields? See | |
448 // [JsClassMirror.getField]. | |
449 | |
450 // '$' (JS_CURRENT_ISOLATE()) stores state which is read directly, so we | |
451 // shouldn't use [_globalObject] here. | |
452 assert(JS('bool', '# in #', name, JS_CURRENT_ISOLATE())); | |
453 return JS('', '#[#]', JS_CURRENT_ISOLATE(), name); | |
454 } | |
455 | |
456 void _storeField(String name, Object arg) { | |
457 // '$' (JS_CURRENT_ISOLATE()) stores state which is stored directly, so we | |
458 // shouldn't use [_globalObject] here. | |
459 assert(JS('bool', '# in #', name, JS_CURRENT_ISOLATE())); | |
460 JS('void', '#[#] = #', JS_CURRENT_ISOLATE(), name, arg); | |
461 } | |
462 | |
463 List<JsMethodMirror> get _functionMirrors { | |
464 if (_cachedFunctionMirrors != null) return _cachedFunctionMirrors; | |
465 var result = new List<JsMethodMirror>(); | |
466 for (int i = 0; i < _functions.length; i++) { | |
467 String name = _functions[i]; | |
468 var jsFunction = JS('', '#[#]', _globalObject, name); | |
469 String unmangledName = mangledGlobalNames[name]; | |
470 if (unmangledName == null || | |
471 JS('bool', "!!#['\$getterStub']", jsFunction)) { | |
472 // If there is no unmangledName, [jsFunction] is either a synthetic | |
473 // implementation detail, or something that is excluded | |
474 // by @MirrorsUsed. | |
475 // If it has a getterStub property it is a synthetic stub. | |
476 // TODO(floitsch): Remove the getterStub hack. | |
477 continue; | |
478 } | |
479 bool isConstructor = unmangledName.startsWith('new '); | |
480 bool isStatic = !isConstructor; // Top-level functions are static, but | |
481 // constructors are not. | |
482 if (isConstructor) { | |
483 unmangledName = unmangledName.substring(4).replaceAll(r'$', '.'); | |
484 } | |
485 JsMethodMirror mirror = | |
486 new JsMethodMirror.fromUnmangledName( | |
487 unmangledName, jsFunction, isStatic, isConstructor); | |
488 result.add(mirror); | |
489 mirror._owner = this; | |
490 } | |
491 return _cachedFunctionMirrors = result; | |
492 } | |
493 | |
494 List<VariableMirror> get _fields { | |
495 if (_cachedFields != null) return _cachedFields; | |
496 var result = <VariableMirror>[]; | |
497 parseCompactFieldSpecification( | |
498 this, _compactFieldSpecification, true, result); | |
499 return _cachedFields = result; | |
500 } | |
501 | |
502 Map<Symbol, MethodMirror> get __functions { | |
503 if (_cachedFunctions != null) return _cachedFunctions; | |
504 var result = new Map(); | |
505 for (JsMethodMirror mirror in _functionMirrors) { | |
506 if (!mirror.isConstructor) result[mirror.simpleName] = mirror; | |
507 } | |
508 return _cachedFunctions = | |
509 new UnmodifiableMapView<Symbol, MethodMirror>(result); | |
510 } | |
511 | |
512 Map<Symbol, MethodMirror> get __getters { | |
513 if (_cachedGetters != null) return _cachedGetters; | |
514 var result = new Map(); | |
515 // TODO(ahe): Implement this. | |
516 return _cachedGetters = | |
517 new UnmodifiableMapView<Symbol, MethodMirror>(result); | |
518 } | |
519 | |
520 Map<Symbol, MethodMirror> get __setters { | |
521 if (_cachedSetters != null) return _cachedSetters; | |
522 var result = new Map(); | |
523 // TODO(ahe): Implement this. | |
524 return _cachedSetters = | |
525 new UnmodifiableMapView<Symbol, MethodMirror>(result); | |
526 } | |
527 | |
528 Map<Symbol, VariableMirror> get __variables { | |
529 if (_cachedVariables != null) return _cachedVariables; | |
530 var result = new Map(); | |
531 for (JsVariableMirror mirror in _fields) { | |
532 result[mirror.simpleName] = mirror; | |
533 } | |
534 return _cachedVariables = | |
535 new UnmodifiableMapView<Symbol, VariableMirror>(result); | |
536 } | |
537 | |
538 Map<Symbol, Mirror> get __members { | |
539 if (_cachedMembers != null) return _cachedMembers; | |
540 Map<Symbol, Mirror> result = new Map.from(__classes); | |
541 addToResult(Symbol key, Mirror value) { | |
542 result[key] = value; | |
543 } | |
544 __functions.forEach(addToResult); | |
545 __getters.forEach(addToResult); | |
546 __setters.forEach(addToResult); | |
547 __variables.forEach(addToResult); | |
548 return _cachedMembers = new UnmodifiableMapView<Symbol, Mirror>(result); | |
549 } | |
550 | |
551 Map<Symbol, DeclarationMirror> get declarations { | |
552 if (_cachedDeclarations != null) return _cachedDeclarations; | |
553 var result = new Map<Symbol, DeclarationMirror>(); | |
554 addToResult(Symbol key, Mirror value) { | |
555 result[key] = value; | |
556 } | |
557 __members.forEach(addToResult); | |
558 return _cachedDeclarations = | |
559 new UnmodifiableMapView<Symbol, DeclarationMirror>(result); | |
560 } | |
561 | |
562 List<InstanceMirror> get metadata { | |
563 if (_cachedMetadata != null) return _cachedMetadata; | |
564 preserveMetadata(); | |
565 return _cachedMetadata = | |
566 new UnmodifiableListView<InstanceMirror>(_metadata.map(reflect)); | |
567 } | |
568 | |
569 // TODO(ahe): Test this getter. | |
570 DeclarationMirror get owner => null; | |
571 | |
572 List<LibraryDependencyMirror> get libraryDependencies | |
573 => throw new UnimplementedError(); | |
574 } | |
575 | |
576 String n(Symbol symbol) => _symbol_dev.Symbol.getName(symbol); | |
577 | |
578 Symbol s(String name) { | |
579 if (name == null) return null; | |
580 return new _symbol_dev.Symbol.unvalidated(name); | |
581 } | |
582 | |
583 Symbol setterSymbol(Symbol symbol) => s("${n(symbol)}="); | |
584 | |
585 final JsMirrorSystem currentJsMirrorSystem = new JsMirrorSystem(); | |
586 | |
587 InstanceMirror reflect(Object reflectee) { | |
588 if (reflectee is Closure) { | |
589 return new JsClosureMirror(reflectee); | |
590 } else { | |
591 return new JsInstanceMirror(reflectee); | |
592 } | |
593 } | |
594 | |
595 TypeMirror reflectType(Type key) { | |
596 return reflectClassByMangledName(getMangledTypeName(key)); | |
597 } | |
598 | |
599 TypeMirror reflectClassByMangledName(String mangledName) { | |
600 String unmangledName = mangledGlobalNames[mangledName]; | |
601 if (mangledName == 'dynamic') return JsMirrorSystem._dynamicType; | |
602 if (mangledName == 'void') return JsMirrorSystem._voidType; | |
603 if (unmangledName == null) unmangledName = mangledName; | |
604 return reflectClassByName(s(unmangledName), mangledName); | |
605 } | |
606 | |
607 var classMirrors; | |
608 | |
609 TypeMirror reflectClassByName(Symbol symbol, String mangledName) { | |
610 if (classMirrors == null) classMirrors = JsCache.allocate(); | |
611 var mirror = JsCache.fetch(classMirrors, mangledName); | |
612 if (mirror != null) return mirror; | |
613 disableTreeShaking(); | |
614 int typeArgIndex = mangledName.indexOf("<"); | |
615 if (typeArgIndex != -1) { | |
616 TypeMirror originalDeclaration = | |
617 reflectClassByMangledName(mangledName.substring(0, typeArgIndex)) | |
618 .originalDeclaration; | |
619 if (originalDeclaration is JsTypedefMirror) { | |
620 throw new UnimplementedError(); | |
621 } | |
622 mirror = new JsTypeBoundClassMirror(originalDeclaration, | |
623 // Remove the angle brackets enclosing the type arguments. | |
624 mangledName.substring(typeArgIndex + 1, mangledName.length - 1)); | |
625 JsCache.update(classMirrors, mangledName, mirror); | |
626 return mirror; | |
627 } | |
628 var allClasses = JS_EMBEDDED_GLOBAL('', ALL_CLASSES); | |
629 var constructor = JS('var', '#[#]', allClasses, mangledName); | |
630 if (constructor == null) { | |
631 // Probably an intercepted class. | |
632 // TODO(ahe): How to handle intercepted classes? | |
633 throw new UnsupportedError('Cannot find class for: ${n(symbol)}'); | |
634 } | |
635 var descriptor = JS('', '#["@"]', constructor); | |
636 var fields; | |
637 var fieldsMetadata; | |
638 if (descriptor == null) { | |
639 // This is a native class, or an intercepted class. | |
640 // TODO(ahe): Preserve descriptor for such classes. | |
641 } else if (JS('bool', '# in #', | |
642 TYPEDEF_PREDICATE_PROPERTY_NAME, descriptor)) { | |
643 // Typedefs are represented as normal classes with two special properties: | |
644 // TYPEDEF_PREDICATE_PROPERTY_NAME and TYPEDEF_TYPE_PROPERTY_NAME. | |
645 // For example: | |
646 // MyTypedef: { | |
647 // "^": "Object;", | |
648 // $typedefType: 58, | |
649 // $$isTypedef: true | |
650 // } | |
651 // The typedefType is the index into the metadata table. | |
652 int index = JS('int', '#[#]', descriptor, TYPEDEF_TYPE_PROPERTY_NAME); | |
653 mirror = new JsTypedefMirror(symbol, mangledName, getType(index)); | |
654 } else { | |
655 fields = JS('', '#[#]', descriptor, | |
656 JS_GET_NAME(JsGetName.CLASS_DESCRIPTOR_PROPERTY)); | |
657 if (fields is List) { | |
658 fieldsMetadata = fields.getRange(1, fields.length).toList(); | |
659 fields = fields[0]; | |
660 } | |
661 if (fields is! String) { | |
662 // TODO(ahe): This is CSP mode. Find a way to determine the | |
663 // fields of this class. | |
664 fields = ''; | |
665 } | |
666 } | |
667 | |
668 if (mirror == null) { | |
669 var superclassName = fields.split(';')[0]; | |
670 var mixins = superclassName.split('+'); | |
671 if (mixins.length > 1 && mangledGlobalNames[mangledName] == null) { | |
672 mirror = reflectMixinApplication(mixins, mangledName); | |
673 } else { | |
674 ClassMirror classMirror = new JsClassMirror( | |
675 symbol, mangledName, constructor, fields, fieldsMetadata); | |
676 List typeVariables = | |
677 JS('JSExtendableArray|Null', '#.prototype["<>"]', constructor); | |
678 if (typeVariables == null || typeVariables.length == 0) { | |
679 mirror = classMirror; | |
680 } else { | |
681 String typeArguments = 'dynamic'; | |
682 for (int i = 1; i < typeVariables.length; i++) { | |
683 typeArguments += ',dynamic'; | |
684 } | |
685 mirror = new JsTypeBoundClassMirror(classMirror, typeArguments); | |
686 } | |
687 } | |
688 } | |
689 | |
690 JsCache.update(classMirrors, mangledName, mirror); | |
691 return mirror; | |
692 } | |
693 | |
694 Map<Symbol, MethodMirror> filterMethods(List<MethodMirror> methods) { | |
695 var result = new Map(); | |
696 for (JsMethodMirror method in methods) { | |
697 if (!method.isConstructor && !method.isGetter && !method.isSetter) { | |
698 result[method.simpleName] = method; | |
699 } | |
700 } | |
701 return result; | |
702 } | |
703 | |
704 Map<Symbol, MethodMirror> filterConstructors(methods) { | |
705 var result = new Map(); | |
706 for (JsMethodMirror method in methods) { | |
707 if (method.isConstructor) { | |
708 result[method.simpleName] = method; | |
709 } | |
710 } | |
711 return result; | |
712 } | |
713 | |
714 Map<Symbol, MethodMirror> filterGetters(List<MethodMirror> methods, | |
715 Map<Symbol, VariableMirror> fields) { | |
716 var result = new Map(); | |
717 for (JsMethodMirror method in methods) { | |
718 if (method.isGetter) { | |
719 | |
720 // TODO(ahe): This is a hack to remove getters corresponding to a field. | |
721 if (fields[method.simpleName] != null) continue; | |
722 | |
723 result[method.simpleName] = method; | |
724 } | |
725 } | |
726 return result; | |
727 } | |
728 | |
729 Map<Symbol, MethodMirror> filterSetters(List<MethodMirror> methods, | |
730 Map<Symbol, VariableMirror> fields) { | |
731 var result = new Map(); | |
732 for (JsMethodMirror method in methods) { | |
733 if (method.isSetter) { | |
734 | |
735 // TODO(ahe): This is a hack to remove setters corresponding to a field. | |
736 String name = n(method.simpleName); | |
737 name = name.substring(0, name.length - 1); // Remove '='. | |
738 if (fields[s(name)] != null) continue; | |
739 | |
740 result[method.simpleName] = method; | |
741 } | |
742 } | |
743 return result; | |
744 } | |
745 | |
746 Map<Symbol, Mirror> filterMembers(List<MethodMirror> methods, | |
747 Map<Symbol, VariableMirror> variables) { | |
748 Map<Symbol, Mirror> result = new Map.from(variables); | |
749 for (JsMethodMirror method in methods) { | |
750 if (method.isSetter) { | |
751 String name = n(method.simpleName); | |
752 name = name.substring(0, name.length - 1); | |
753 // Filter-out setters corresponding to variables. | |
754 if (result[s(name)] is VariableMirror) continue; | |
755 } | |
756 // Constructors aren't 'members'. | |
757 if (method.isConstructor) continue; | |
758 // Filter out synthetic tear-off stubs | |
759 if (JS('bool', r'!!#.$getterStub', method._jsFunction)) continue; | |
760 // Use putIfAbsent to filter-out getters corresponding to variables. | |
761 result.putIfAbsent(method.simpleName, () => method); | |
762 } | |
763 return result; | |
764 } | |
765 | |
766 int counter = 0; | |
767 | |
768 ClassMirror reflectMixinApplication(mixinNames, String mangledName) { | |
769 disableTreeShaking(); | |
770 var mixins = []; | |
771 for (String mangledName in mixinNames) { | |
772 mixins.add(reflectClassByMangledName(mangledName)); | |
773 } | |
774 var it = mixins.iterator; | |
775 it.moveNext(); | |
776 var superclass = it.current; | |
777 while (it.moveNext()) { | |
778 superclass = new JsMixinApplication(superclass, it.current, mangledName); | |
779 } | |
780 return superclass; | |
781 } | |
782 | |
783 class JsMixinApplication extends JsTypeMirror with JsObjectMirror | |
784 implements ClassMirror { | |
785 final ClassMirror superclass; | |
786 final ClassMirror mixin; | |
787 Symbol _cachedSimpleName; | |
788 Map<Symbol, MethodMirror> _cachedInstanceMembers; | |
789 | |
790 JsMixinApplication(ClassMirror superclass, ClassMirror mixin, | |
791 String mangledName) | |
792 : this.superclass = superclass, | |
793 this.mixin = mixin, | |
794 super(s(mangledName)); | |
795 | |
796 String get _prettyName => 'ClassMirror'; | |
797 | |
798 Symbol get simpleName { | |
799 if (_cachedSimpleName != null) return _cachedSimpleName; | |
800 String superName = n(superclass.qualifiedName); | |
801 return _cachedSimpleName = (superName.contains(' with ')) | |
802 ? s('$superName, ${n(mixin.qualifiedName)}') | |
803 : s('$superName with ${n(mixin.qualifiedName)}'); | |
804 } | |
805 | |
806 Symbol get qualifiedName => simpleName; | |
807 | |
808 // TODO(ahe): Remove this method, only here to silence warning. | |
809 get _mixin => mixin; | |
810 | |
811 Map<Symbol, Mirror> get __members => _mixin.__members; | |
812 | |
813 Map<Symbol, MethodMirror> get __methods => _mixin.__methods; | |
814 | |
815 Map<Symbol, MethodMirror> get __getters => _mixin.__getters; | |
816 | |
817 Map<Symbol, MethodMirror> get __setters => _mixin.__setters; | |
818 | |
819 Map<Symbol, VariableMirror> get __variables => _mixin.__variables; | |
820 | |
821 Map<Symbol, DeclarationMirror> get declarations => mixin.declarations; | |
822 | |
823 Map<Symbol, MethodMirror> get instanceMembers { | |
824 if (_cachedInstanceMembers == null) { | |
825 var result = new Map<Symbol, MethodMirror>(); | |
826 if (superclass != null) { | |
827 result.addAll(superclass.instanceMembers); | |
828 } | |
829 result.addAll(mixin.instanceMembers); | |
830 _cachedInstanceMembers = result; | |
831 } | |
832 return _cachedInstanceMembers; | |
833 } | |
834 | |
835 Map<Symbol, MethodMirror> get staticMembers => mixin.staticMembers; | |
836 | |
837 _asRuntimeType() => null; | |
838 | |
839 InstanceMirror invoke( | |
840 Symbol memberName, | |
841 List positionalArguments, | |
842 [Map<Symbol,dynamic> namedArguments]) { | |
843 throw new NoSuchStaticMethodError.method( | |
844 null, memberName, positionalArguments, namedArguments); | |
845 } | |
846 | |
847 InstanceMirror getField(Symbol fieldName) { | |
848 throw new NoSuchStaticMethodError.method(null, fieldName, null, null); | |
849 } | |
850 | |
851 InstanceMirror setField(Symbol fieldName, Object arg) { | |
852 throw new NoSuchStaticMethodError.method( | |
853 null, setterSymbol(fieldName), [arg], null); | |
854 } | |
855 | |
856 List<ClassMirror> get superinterfaces => [mixin]; | |
857 | |
858 Map<Symbol, MethodMirror> get __constructors => _mixin.__constructors; | |
859 | |
860 InstanceMirror newInstance( | |
861 Symbol constructorName, | |
862 List positionalArguments, | |
863 [Map<Symbol,dynamic> namedArguments]) { | |
864 throw new UnsupportedError( | |
865 "Can't instantiate mixin application '${n(qualifiedName)}'"); | |
866 } | |
867 | |
868 bool get isOriginalDeclaration => true; | |
869 | |
870 ClassMirror get originalDeclaration => this; | |
871 | |
872 // TODO(ahe): Implement this. | |
873 List<TypeVariableMirror> get typeVariables { | |
874 throw new UnimplementedError(); | |
875 } | |
876 | |
877 List<TypeMirror> get typeArguments => const <TypeMirror>[]; | |
878 | |
879 bool get isAbstract => throw new UnimplementedError(); | |
880 | |
881 bool get isEnum => throw new UnimplementedError(); | |
882 | |
883 bool isSubclassOf(ClassMirror other) { | |
884 superclass.isSubclassOf(other) || mixin.isSubclassOf(other); | |
885 } | |
886 | |
887 bool isSubtypeOf(TypeMirror other) => throw new UnimplementedError(); | |
888 | |
889 bool isAssignableTo(TypeMirror other) => throw new UnimplementedError(); | |
890 } | |
891 | |
892 abstract class JsObjectMirror implements ObjectMirror { | |
893 } | |
894 | |
895 class JsInstanceMirror extends JsObjectMirror implements InstanceMirror { | |
896 final reflectee; | |
897 | |
898 JsInstanceMirror(this.reflectee); | |
899 | |
900 bool get hasReflectee => true; | |
901 | |
902 ClassMirror get type { | |
903 // The spec guarantees that `null` is the singleton instance of the `Null` | |
904 // class. | |
905 if (reflectee == null) return reflectClass(Null); | |
906 return reflectType(getRuntimeType(reflectee)); | |
907 } | |
908 | |
909 InstanceMirror invoke(Symbol memberName, | |
910 List positionalArguments, | |
911 [Map<Symbol,dynamic> namedArguments]) { | |
912 if (namedArguments == null) namedArguments = const {}; | |
913 // We can safely pass positionalArguments to _invoke as it will wrap it in | |
914 // a JSArray if needed. | |
915 return _invoke(memberName, JSInvocationMirror.METHOD, | |
916 positionalArguments, namedArguments); | |
917 } | |
918 | |
919 InstanceMirror _invokeMethodWithNamedArguments( | |
920 String reflectiveName, | |
921 List positionalArguments, Map<Symbol,dynamic> namedArguments) { | |
922 assert(namedArguments.isNotEmpty); | |
923 var interceptor = getInterceptor(reflectee); | |
924 | |
925 var jsFunction = JS('', '#[#]', interceptor, reflectiveName); | |
926 if (jsFunction == null) { | |
927 // TODO(ahe): Invoke noSuchMethod. | |
928 throw new UnimplementedNoSuchMethodError( | |
929 'Invoking noSuchMethod with named arguments not implemented'); | |
930 } | |
931 ReflectionInfo info = new ReflectionInfo(jsFunction); | |
932 if (jsFunction == null) { | |
933 // TODO(ahe): Invoke noSuchMethod. | |
934 throw new UnimplementedNoSuchMethodError( | |
935 'Invoking noSuchMethod with named arguments not implemented'); | |
936 } | |
937 | |
938 positionalArguments = new List.from(positionalArguments); | |
939 // Check the number of positional arguments is valid. | |
940 if (info.requiredParameterCount != positionalArguments.length) { | |
941 // TODO(ahe): Invoke noSuchMethod. | |
942 throw new UnimplementedNoSuchMethodError( | |
943 'Invoking noSuchMethod with named arguments not implemented'); | |
944 } | |
945 var defaultArguments = new Map(); | |
946 for (int i = 0; i < info.optionalParameterCount; i++) { | |
947 var parameterName = info.parameterName(i + info.requiredParameterCount); | |
948 var defaultValue = | |
949 getMetadata(info.defaultValue(i + info.requiredParameterCount)); | |
950 defaultArguments[parameterName] = defaultValue; | |
951 } | |
952 namedArguments.forEach((Symbol symbol, value) { | |
953 String parameter = n(symbol); | |
954 if (defaultArguments.containsKey(parameter)) { | |
955 defaultArguments[parameter] = value; | |
956 } else { | |
957 // Extraneous named argument. | |
958 // TODO(ahe): Invoke noSuchMethod. | |
959 throw new UnimplementedNoSuchMethodError( | |
960 'Invoking noSuchMethod with named arguments not implemented'); | |
961 } | |
962 }); | |
963 positionalArguments.addAll(defaultArguments.values); | |
964 // TODO(ahe): Handle intercepted methods. | |
965 return reflect( | |
966 JS('', '#.apply(#, #)', jsFunction, reflectee, positionalArguments)); | |
967 } | |
968 | |
969 /// Grabs hold of the class-specific invocation cache for the reflectee. | |
970 /// All reflectees with the same class share the same cache. The cache | |
971 /// maps reflective names to cached invocation objects with enough decoded | |
972 /// reflective information to know how to to invoke a specific member. | |
973 get _classInvocationCache { | |
974 String cacheName = Primitives.mirrorInvokeCacheName; | |
975 var cacheHolder = (reflectee == null) ? getInterceptor(null) : reflectee; | |
976 var cache = JS('', r'#.constructor[#]', cacheHolder, cacheName); | |
977 if (cache == null) { | |
978 cache = JsCache.allocate(); | |
979 JS('void', r'#.constructor[#] = #', cacheHolder, cacheName, cache); | |
980 } | |
981 return cache; | |
982 } | |
983 | |
984 String _computeReflectiveName(Symbol symbolName, int type, | |
985 List positionalArguments, | |
986 Map<Symbol, dynamic> namedArguments) { | |
987 String name = n(symbolName); | |
988 switch (type) { | |
989 case JSInvocationMirror.GETTER: return name; | |
990 case JSInvocationMirror.SETTER: return '$name='; | |
991 case JSInvocationMirror.METHOD: | |
992 if (namedArguments.isNotEmpty) return '$name*'; | |
993 int nbArgs = positionalArguments.length as int; | |
994 return "$name:$nbArgs"; | |
995 } | |
996 throw new RuntimeError("Could not compute reflective name for $name"); | |
997 } | |
998 | |
999 /** | |
1000 * Returns a `CachedInvocation` or `CachedNoSuchMethodInvocation` for the | |
1001 * given member. | |
1002 * | |
1003 * Caches the result. | |
1004 */ | |
1005 _getCachedInvocation(Symbol name, int type, String reflectiveName, | |
1006 List positionalArguments, Map<Symbol,dynamic> namedArguments) { | |
1007 | |
1008 var cache = _classInvocationCache; | |
1009 var cacheEntry = JsCache.fetch(cache, reflectiveName); | |
1010 var result; | |
1011 if (cacheEntry == null) { | |
1012 disableTreeShaking(); | |
1013 String mangledName = reflectiveNames[reflectiveName]; | |
1014 List<String> argumentNames = const []; | |
1015 | |
1016 // TODO(ahe): We don't need to create an invocation mirror here. The | |
1017 // logic from JSInvocationMirror.getCachedInvocation could easily be | |
1018 // inlined here. | |
1019 Invocation invocation = createUnmangledInvocationMirror( | |
1020 name, mangledName, type, positionalArguments, argumentNames); | |
1021 | |
1022 cacheEntry = | |
1023 JSInvocationMirror.getCachedInvocation(invocation, reflectee); | |
1024 JsCache.update(cache, reflectiveName, cacheEntry); | |
1025 } | |
1026 return cacheEntry; | |
1027 } | |
1028 | |
1029 bool _isReflectable(CachedInvocation cachedInvocation) { | |
1030 // TODO(floitsch): tear-off closure does not guarantee that the | |
1031 // function is reflectable. | |
1032 var method = cachedInvocation.jsFunction; | |
1033 return hasReflectableProperty(method) || reflectee is TearOffClosure; | |
1034 } | |
1035 | |
1036 /// Invoke the member specified through name and type on the reflectee. | |
1037 /// As a side-effect, this populates the class-specific invocation cache | |
1038 /// for the reflectee. | |
1039 InstanceMirror _invoke(Symbol name, | |
1040 int type, | |
1041 List positionalArguments, | |
1042 Map<Symbol,dynamic> namedArguments) { | |
1043 String reflectiveName = | |
1044 _computeReflectiveName(name, type, positionalArguments, namedArguments); | |
1045 | |
1046 if (namedArguments.isNotEmpty) { | |
1047 // TODO(floitsch): first, make sure it's not a getter. | |
1048 return _invokeMethodWithNamedArguments( | |
1049 reflectiveName, positionalArguments, namedArguments); | |
1050 } | |
1051 var cacheEntry = _getCachedInvocation( | |
1052 name, type, reflectiveName, positionalArguments, namedArguments); | |
1053 | |
1054 if (cacheEntry.isNoSuchMethod || !_isReflectable(cacheEntry)) { | |
1055 // Could be that we want to invoke a getter, or get a method. | |
1056 if (type == JSInvocationMirror.METHOD && _instanceFieldExists(name)) { | |
1057 return getField(name).invoke( | |
1058 #call, positionalArguments, namedArguments); | |
1059 } | |
1060 | |
1061 if (type == JSInvocationMirror.SETTER) { | |
1062 // For setters we report the setter name "field=". | |
1063 name = s("${n(name)}="); | |
1064 } | |
1065 | |
1066 if (!cacheEntry.isNoSuchMethod) { | |
1067 // Not reflectable. | |
1068 throwInvalidReflectionError(reflectiveName); | |
1069 } | |
1070 | |
1071 String mangledName = reflectiveNames[reflectiveName]; | |
1072 // TODO(ahe): Get the argument names. | |
1073 List<String> argumentNames = []; | |
1074 Invocation invocation = createUnmangledInvocationMirror( | |
1075 name, mangledName, type, positionalArguments, argumentNames); | |
1076 return reflect(cacheEntry.invokeOn(reflectee, invocation)); | |
1077 } else { | |
1078 return reflect(cacheEntry.invokeOn(reflectee, positionalArguments)); | |
1079 } | |
1080 } | |
1081 | |
1082 InstanceMirror setField(Symbol fieldName, Object arg) { | |
1083 _invoke(fieldName, JSInvocationMirror.SETTER, [arg], const {}); | |
1084 return reflect(arg); | |
1085 } | |
1086 | |
1087 // JS helpers for getField optimizations. | |
1088 static bool isUndefined(x) | |
1089 => JS('bool', 'typeof # == "undefined"', x); | |
1090 static bool isMissingCache(x) | |
1091 => JS('bool', 'typeof # == "number"', x); | |
1092 static bool isMissingProbe(Symbol symbol) | |
1093 => JS('bool', 'typeof #.\$p == "undefined"', symbol); | |
1094 static bool isEvalAllowed() | |
1095 => !JS_GET_FLAG("USE_CONTENT_SECURITY_POLICY"); | |
1096 | |
1097 | |
1098 /// The getter cache is lazily allocated after a couple | |
1099 /// of invocations of [InstanceMirror.getField]. The delay is | |
1100 /// used to avoid too aggressive caching and dynamic function | |
1101 /// generation for rarely used mirrors. The cache is specific to | |
1102 /// this [InstanceMirror] and maps reflective names to functions | |
1103 /// that will invoke the corresponding getter on the reflectee. | |
1104 /// The reflectee is passed to the function as the first argument | |
1105 /// to avoid the overhead of fetching it from this mirror repeatedly. | |
1106 /// The cache is lazily initialized to a JS object so we can | |
1107 /// benefit from "map transitions" in the underlying JavaScript | |
1108 /// engine to speed up cache probing. | |
1109 var _getterCache = 4; | |
1110 | |
1111 bool _instanceFieldExists(Symbol name) { | |
1112 int getterType = JSInvocationMirror.GETTER; | |
1113 String getterName = | |
1114 _computeReflectiveName(name, getterType, const [], const {}); | |
1115 var getterCacheEntry = _getCachedInvocation( | |
1116 name, getterType, getterName, const [], const {}); | |
1117 return !getterCacheEntry.isNoSuchMethod && !getterCacheEntry.isGetterStub; | |
1118 } | |
1119 | |
1120 InstanceMirror getField(Symbol fieldName) { | |
1121 FASTPATH: { | |
1122 var cache = _getterCache; | |
1123 if (isMissingCache(cache) || isMissingProbe(fieldName)) break FASTPATH; | |
1124 // If the [fieldName] has an associated probe function, we can use | |
1125 // it to read from the getter cache specific to this [InstanceMirror]. | |
1126 var getter = JS('', '#.\$p(#)', fieldName, cache); | |
1127 if (isUndefined(getter)) break FASTPATH; | |
1128 // Call the getter passing the reflectee as the first argument. | |
1129 var value = JS('', '#(#)', getter, reflectee); | |
1130 // The getter has an associate cache of the last [InstanceMirror] | |
1131 // returned to avoid repeated invocations of [reflect]. To validate | |
1132 // the cache, we check that the value returned by the getter is the | |
1133 // same value as last time. | |
1134 if (JS('bool', '# === #.v', value, getter)) { | |
1135 return JS('InstanceMirror', '#.m', getter); | |
1136 } else { | |
1137 var result = reflect(value); | |
1138 JS('void', '#.v = #', getter, value); | |
1139 JS('void', '#.m = #', getter, result); | |
1140 return result; | |
1141 } | |
1142 } | |
1143 return _getFieldSlow(fieldName); | |
1144 } | |
1145 | |
1146 InstanceMirror _getFieldSlow(Symbol fieldName) { | |
1147 // First do the slow-case getter invocation. As a side-effect of this, | |
1148 // the invocation cache is filled in so we can query it afterwards. | |
1149 var result = | |
1150 _invoke(fieldName, JSInvocationMirror.GETTER, const [], const {}); | |
1151 String name = n(fieldName); | |
1152 var cacheEntry = JsCache.fetch(_classInvocationCache, name); | |
1153 if (cacheEntry.isNoSuchMethod) { | |
1154 return result; | |
1155 } | |
1156 | |
1157 // Make sure we have a getter cache in this [InstanceMirror]. | |
1158 var cache = _getterCache; | |
1159 if (isMissingCache(cache)) { | |
1160 if ((_getterCache = --cache) != 0) return result; | |
1161 cache = _getterCache = JS('=Object', 'Object.create(null)'); | |
1162 } | |
1163 | |
1164 // Make sure that symbol [fieldName] has a cache probing function ($p). | |
1165 bool useEval = isEvalAllowed(); | |
1166 if (isMissingProbe(fieldName)) { | |
1167 var probe = _newProbeFn(name, useEval); | |
1168 JS('void', '#.\$p = #', fieldName, probe); | |
1169 } | |
1170 | |
1171 // Create a new getter function and install it in the cache. | |
1172 var mangledName = cacheEntry.mangledName; | |
1173 var getter = (cacheEntry.isIntercepted) | |
1174 ? _newInterceptedGetterFn(mangledName, useEval) | |
1175 : _newGetterFn(mangledName, useEval); | |
1176 JS('void', '#[#] = #', cache, name, getter); | |
1177 | |
1178 // Initialize the last value (v) and last mirror (m) on the | |
1179 // newly generated getter to be a sentinel value that is hard | |
1180 // to get hold of through user code. | |
1181 JS('void', '#.v = #.m = #', getter, getter, cache); | |
1182 | |
1183 // Return the result of the slow-path getter invocation. | |
1184 return result; | |
1185 } | |
1186 | |
1187 _newProbeFn(String id, bool useEval) { | |
1188 if (useEval) { | |
1189 String body = "return c.$id;"; | |
1190 return JS('', 'new Function("c", #)', body); | |
1191 } else { | |
1192 return JS('', '(function(n){return(function(c){return c[n]})})(#)', id); | |
1193 } | |
1194 } | |
1195 | |
1196 _newGetterFn(String name, bool useEval) { | |
1197 if (!useEval) return _newGetterNoEvalFn(name); | |
1198 // We use a comment that associates the generated function with the | |
1199 // class of the reflectee. This makes it more likely that the underlying | |
1200 // JavaScript engine will only share the generated code for accessors on the | |
1201 // same class (through caching of eval'ed code). This makes the | |
1202 // generated call to the getter - e.g. o.get$foo() - much more likely | |
1203 // to be monomorphic and inlineable. | |
1204 String className = JS('String', '#.constructor.name', reflectee); | |
1205 String body = "/* $className */ return o.$name();"; | |
1206 return JS('', 'new Function("o", #)', body); | |
1207 } | |
1208 | |
1209 _newGetterNoEvalFn(n) => JS('', | |
1210 '(function(n){return(function(o){return o[n]()})})(#)', n); | |
1211 | |
1212 _newInterceptedGetterFn(String name, bool useEval) { | |
1213 var object = reflectee; | |
1214 // It is possible that the interceptor for a given object is the object | |
1215 // itself, so it is important not to share the code that captures the | |
1216 // interceptor between multiple different instances of [InstanceMirror]. | |
1217 var interceptor = getInterceptor(object); | |
1218 if (!useEval) return _newInterceptGetterNoEvalFn(name, interceptor); | |
1219 String className = JS('String', '#.constructor.name', interceptor); | |
1220 String functionName = '$className\$$name'; | |
1221 String body = | |
1222 ' function $functionName(o){return i.$name(o)}' | |
1223 ' return $functionName;'; | |
1224 return JS('', '(new Function("i", #))(#)', body, interceptor); | |
1225 } | |
1226 | |
1227 _newInterceptGetterNoEvalFn(n, i) => JS('', | |
1228 '(function(n,i){return(function(o){return i[n](o)})})(#,#)', n, i); | |
1229 | |
1230 delegate(Invocation invocation) { | |
1231 return JSInvocationMirror.invokeFromMirror(invocation, reflectee); | |
1232 } | |
1233 | |
1234 operator ==(other) { | |
1235 return other is JsInstanceMirror && | |
1236 identical(reflectee, other.reflectee); | |
1237 } | |
1238 | |
1239 int get hashCode { | |
1240 // Avoid hash collisions with the reflectee. This constant is in Smi range | |
1241 // and happens to be the inner padding from RFC 2104. | |
1242 return identityHashCode(reflectee) ^ 0x36363636; | |
1243 } | |
1244 | |
1245 String toString() => 'InstanceMirror on ${Error.safeToString(reflectee)}'; | |
1246 | |
1247 // TODO(ahe): Remove this method from the API. | |
1248 MirrorSystem get mirrors => currentJsMirrorSystem; | |
1249 } | |
1250 | |
1251 /** | |
1252 * ClassMirror for generic classes where the type parameters are bound. | |
1253 * | |
1254 * [typeArguments] will return a list of the type arguments, in constrast | |
1255 * to JsCLassMirror that returns an empty list since it represents original | |
1256 * declarations and classes that are not generic. | |
1257 */ | |
1258 class JsTypeBoundClassMirror extends JsDeclarationMirror | |
1259 implements ClassMirror { | |
1260 final JsClassMirror _class; | |
1261 | |
1262 /** | |
1263 * When instantiated this field will hold a string representing the list of | |
1264 * type arguments for the class, i.e. what is inside the outermost angle | |
1265 * brackets. Then, when get typeArguments is called the first time, the string | |
1266 * is parsed into the actual list of TypeMirrors, and stored in | |
1267 * [_cachedTypeArguments]. Due to type substitution of, for instance, | |
1268 * superclasses the mangled name of the class and hence this string is needed | |
1269 * after [_cachedTypeArguments] has been computed. | |
1270 * | |
1271 * If an integer is encountered as a type argument, it represents the type | |
1272 * variable at the corresponding entry in [emitter.globalMetadata]. | |
1273 */ | |
1274 String _typeArguments; | |
1275 | |
1276 UnmodifiableListView<TypeMirror> _cachedTypeArguments; | |
1277 UnmodifiableMapView<Symbol, DeclarationMirror> _cachedDeclarations; | |
1278 UnmodifiableMapView<Symbol, DeclarationMirror> _cachedMembers; | |
1279 UnmodifiableMapView<Symbol, MethodMirror> _cachedConstructors; | |
1280 Map<Symbol, VariableMirror> _cachedVariables; | |
1281 Map<Symbol, MethodMirror> _cachedGetters; | |
1282 Map<Symbol, MethodMirror> _cachedSetters; | |
1283 Map<Symbol, MethodMirror> _cachedMethodsMap; | |
1284 List<JsMethodMirror> _cachedMethods; | |
1285 ClassMirror _superclass; | |
1286 List<ClassMirror> _cachedSuperinterfaces; | |
1287 Map<Symbol, MethodMirror> _cachedInstanceMembers; | |
1288 Map<Symbol, MethodMirror> _cachedStaticMembers; | |
1289 | |
1290 JsTypeBoundClassMirror(JsClassMirror originalDeclaration, this._typeArguments) | |
1291 : _class = originalDeclaration, | |
1292 super(originalDeclaration.simpleName); | |
1293 | |
1294 String get _prettyName => 'ClassMirror'; | |
1295 | |
1296 String toString() { | |
1297 String result = '$_prettyName on ${n(simpleName)}'; | |
1298 if (typeArguments != null) { | |
1299 result = "$result<${typeArguments.join(', ')}>"; | |
1300 } | |
1301 return result; | |
1302 } | |
1303 | |
1304 String get _mangledName { | |
1305 for (TypeMirror typeArgument in typeArguments) { | |
1306 if (typeArgument != JsMirrorSystem._dynamicType) { | |
1307 return '${_class._mangledName}<$_typeArguments>'; | |
1308 } | |
1309 } | |
1310 // When all type arguments are dynamic, the canonical representation is to | |
1311 // drop them. | |
1312 return _class._mangledName; | |
1313 } | |
1314 | |
1315 List<TypeVariableMirror> get typeVariables => _class.typeVariables; | |
1316 | |
1317 List<TypeMirror> get typeArguments { | |
1318 if (_cachedTypeArguments != null) return _cachedTypeArguments; | |
1319 List result = new List(); | |
1320 | |
1321 addTypeArgument(String typeArgument) { | |
1322 int parsedIndex = int.parse(typeArgument, onError: (_) => -1); | |
1323 if (parsedIndex == -1) { | |
1324 result.add(reflectClassByMangledName(typeArgument.trim())); | |
1325 } else { | |
1326 TypeVariable typeVariable = getMetadata(parsedIndex); | |
1327 TypeMirror owner = reflectClass(typeVariable.owner); | |
1328 TypeVariableMirror typeMirror = | |
1329 new JsTypeVariableMirror(typeVariable, owner, parsedIndex); | |
1330 result.add(typeMirror); | |
1331 } | |
1332 } | |
1333 | |
1334 if (_typeArguments.indexOf('<') == -1) { | |
1335 _typeArguments.split(',').forEach((t) => addTypeArgument(t)); | |
1336 } else { | |
1337 int level = 0; | |
1338 String currentTypeArgument = ''; | |
1339 | |
1340 for (int i = 0; i < _typeArguments.length; i++) { | |
1341 var character = _typeArguments[i]; | |
1342 if (character == ' ') { | |
1343 continue; | |
1344 } else if (character == '<') { | |
1345 currentTypeArgument += character; | |
1346 level++; | |
1347 } else if (character == '>') { | |
1348 currentTypeArgument += character; | |
1349 level--; | |
1350 } else if (character == ',') { | |
1351 if (level > 0) { | |
1352 currentTypeArgument += character; | |
1353 } else { | |
1354 addTypeArgument(currentTypeArgument); | |
1355 currentTypeArgument = ''; | |
1356 } | |
1357 } else { | |
1358 currentTypeArgument += character; | |
1359 } | |
1360 } | |
1361 addTypeArgument(currentTypeArgument); | |
1362 } | |
1363 return _cachedTypeArguments = new UnmodifiableListView(result); | |
1364 } | |
1365 | |
1366 List<JsMethodMirror> get _methods { | |
1367 if (_cachedMethods != null) return _cachedMethods; | |
1368 return _cachedMethods =_class._getMethodsWithOwner(this); | |
1369 } | |
1370 | |
1371 Map<Symbol, MethodMirror> get __methods { | |
1372 if (_cachedMethodsMap != null) return _cachedMethodsMap; | |
1373 return _cachedMethodsMap = new UnmodifiableMapView<Symbol, MethodMirror>( | |
1374 filterMethods(_methods)); | |
1375 } | |
1376 | |
1377 Map<Symbol, MethodMirror> get __constructors { | |
1378 if (_cachedConstructors != null) return _cachedConstructors; | |
1379 return _cachedConstructors = | |
1380 new UnmodifiableMapView<Symbol, MethodMirror>( | |
1381 filterConstructors(_methods)); | |
1382 } | |
1383 | |
1384 Map<Symbol, MethodMirror> get __getters { | |
1385 if (_cachedGetters != null) return _cachedGetters; | |
1386 return _cachedGetters = new UnmodifiableMapView<Symbol, MethodMirror>( | |
1387 filterGetters(_methods, __variables)); | |
1388 } | |
1389 | |
1390 Map<Symbol, MethodMirror> get __setters { | |
1391 if (_cachedSetters != null) return _cachedSetters; | |
1392 return _cachedSetters = new UnmodifiableMapView<Symbol, MethodMirror>( | |
1393 filterSetters(_methods, __variables)); | |
1394 } | |
1395 | |
1396 Map<Symbol, VariableMirror> get __variables { | |
1397 if (_cachedVariables != null) return _cachedVariables; | |
1398 var result = new Map(); | |
1399 for (JsVariableMirror mirror in _class._getFieldsWithOwner(this)) { | |
1400 result[mirror.simpleName] = mirror; | |
1401 } | |
1402 return _cachedVariables = | |
1403 new UnmodifiableMapView<Symbol, VariableMirror>(result); | |
1404 } | |
1405 | |
1406 Map<Symbol, DeclarationMirror> get __members { | |
1407 if (_cachedMembers != null) return _cachedMembers; | |
1408 return _cachedMembers = new UnmodifiableMapView<Symbol, DeclarationMirror>( | |
1409 filterMembers(_methods, __variables)); | |
1410 } | |
1411 | |
1412 Map<Symbol, DeclarationMirror> get declarations { | |
1413 if (_cachedDeclarations != null) return _cachedDeclarations; | |
1414 Map<Symbol, DeclarationMirror> result = | |
1415 new Map<Symbol, DeclarationMirror>(); | |
1416 result.addAll(__members); | |
1417 result.addAll(__constructors); | |
1418 typeVariables.forEach((tv) => result[tv.simpleName] = tv); | |
1419 return _cachedDeclarations = | |
1420 new UnmodifiableMapView<Symbol, DeclarationMirror>(result); | |
1421 } | |
1422 | |
1423 Map<Symbol, MethodMirror> get staticMembers { | |
1424 if (_cachedStaticMembers == null) { | |
1425 var result = new Map<Symbol, MethodMirror>(); | |
1426 declarations.values.forEach((decl) { | |
1427 if (decl is MethodMirror && decl.isStatic && !decl.isConstructor) { | |
1428 result[decl.simpleName] = decl; | |
1429 } | |
1430 if (decl is VariableMirror && decl.isStatic) { | |
1431 var getterName = decl.simpleName; | |
1432 result[getterName] = new JsSyntheticAccessor( | |
1433 this, getterName, true, true, false, decl); | |
1434 if (!decl.isFinal) { | |
1435 var setterName = setterSymbol(decl.simpleName); | |
1436 result[setterName] = new JsSyntheticAccessor( | |
1437 this, setterName, false, true, false, decl); | |
1438 } | |
1439 } | |
1440 }); | |
1441 _cachedStaticMembers = result; | |
1442 } | |
1443 return _cachedStaticMembers; | |
1444 } | |
1445 | |
1446 Map<Symbol, MethodMirror> get instanceMembers { | |
1447 if (_cachedInstanceMembers == null) { | |
1448 var result = new Map<Symbol, MethodMirror>(); | |
1449 if (superclass != null) { | |
1450 result.addAll(superclass.instanceMembers); | |
1451 } | |
1452 declarations.values.forEach((decl) { | |
1453 if (decl is MethodMirror && !decl.isStatic && | |
1454 !decl.isConstructor && !decl.isAbstract) { | |
1455 result[decl.simpleName] = decl; | |
1456 } | |
1457 if (decl is VariableMirror && !decl.isStatic) { | |
1458 var getterName = decl.simpleName; | |
1459 result[getterName] = new JsSyntheticAccessor( | |
1460 this, getterName, true, false, false, decl); | |
1461 if (!decl.isFinal) { | |
1462 var setterName = setterSymbol(decl.simpleName); | |
1463 result[setterName] = new JsSyntheticAccessor( | |
1464 this, setterName, false, false, false, decl); | |
1465 } | |
1466 } | |
1467 }); | |
1468 _cachedInstanceMembers = result; | |
1469 } | |
1470 return _cachedInstanceMembers; | |
1471 } | |
1472 | |
1473 InstanceMirror setField(Symbol fieldName, Object arg) { | |
1474 return _class.setField(fieldName, arg); | |
1475 } | |
1476 | |
1477 InstanceMirror getField(Symbol fieldName) => _class.getField(fieldName); | |
1478 | |
1479 InstanceMirror newInstance(Symbol constructorName, | |
1480 List positionalArguments, | |
1481 [Map<Symbol, dynamic> namedArguments]) { | |
1482 var instance = _class._getInvokedInstance(constructorName, | |
1483 positionalArguments, | |
1484 namedArguments); | |
1485 return reflect(setRuntimeTypeInfo( | |
1486 instance, typeArguments.map((t) => t._asRuntimeType()).toList())); | |
1487 } | |
1488 | |
1489 _asRuntimeType() { | |
1490 return [_class._jsConstructor].addAll( | |
1491 typeArguments.map((t) => t._asRuntimeType())); | |
1492 } | |
1493 | |
1494 JsLibraryMirror get owner => _class.owner; | |
1495 | |
1496 List<InstanceMirror> get metadata => _class.metadata; | |
1497 | |
1498 ClassMirror get superclass { | |
1499 if (_superclass != null) return _superclass; | |
1500 | |
1501 var typeInformationContainer = JS_EMBEDDED_GLOBAL('', TYPE_INFORMATION); | |
1502 List<int> typeInformation = | |
1503 JS('List|Null', '#[#]', typeInformationContainer, _class._mangledName); | |
1504 assert(typeInformation != null); | |
1505 var type = getType(typeInformation[0]); | |
1506 return _superclass = typeMirrorFromRuntimeTypeRepresentation(this, type); | |
1507 } | |
1508 | |
1509 InstanceMirror invoke(Symbol memberName, | |
1510 List positionalArguments, | |
1511 [Map<Symbol,dynamic> namedArguments]) { | |
1512 return _class.invoke(memberName, positionalArguments, namedArguments); | |
1513 } | |
1514 | |
1515 bool get isOriginalDeclaration => false; | |
1516 | |
1517 ClassMirror get originalDeclaration => _class; | |
1518 | |
1519 List<ClassMirror> get superinterfaces { | |
1520 if (_cachedSuperinterfaces != null) return _cachedSuperinterfaces; | |
1521 return _cachedSuperinterfaces = _class._getSuperinterfacesWithOwner(this); | |
1522 } | |
1523 | |
1524 bool get isPrivate => _class.isPrivate; | |
1525 | |
1526 bool get isTopLevel => _class.isTopLevel; | |
1527 | |
1528 bool get isAbstract => _class.isAbstract; | |
1529 | |
1530 bool get isEnum => _class.isEnum; | |
1531 | |
1532 bool isSubclassOf(ClassMirror other) => _class.isSubclassOf(other); | |
1533 | |
1534 SourceLocation get location => _class.location; | |
1535 | |
1536 MirrorSystem get mirrors => _class.mirrors; | |
1537 | |
1538 Symbol get qualifiedName => _class.qualifiedName; | |
1539 | |
1540 bool get hasReflectedType => true; | |
1541 | |
1542 Type get reflectedType => createRuntimeType(_mangledName); | |
1543 | |
1544 Symbol get simpleName => _class.simpleName; | |
1545 | |
1546 // TODO(ahe): Implement this. | |
1547 ClassMirror get mixin => throw new UnimplementedError(); | |
1548 | |
1549 bool isSubtypeOf(TypeMirror other) => throw new UnimplementedError(); | |
1550 | |
1551 bool isAssignableTo(TypeMirror other) => throw new UnimplementedError(); | |
1552 } | |
1553 | |
1554 class JsSyntheticAccessor implements MethodMirror { | |
1555 final DeclarationMirror owner; | |
1556 final Symbol simpleName; | |
1557 final bool isGetter; | |
1558 final bool isStatic; | |
1559 final bool isTopLevel; | |
1560 final _target; /// The field or type that introduces the synthetic accessor. | |
1561 | |
1562 JsSyntheticAccessor(this.owner, | |
1563 this.simpleName, | |
1564 this.isGetter, | |
1565 this.isStatic, | |
1566 this.isTopLevel, | |
1567 this._target); | |
1568 | |
1569 bool get isSynthetic => true; | |
1570 bool get isRegularMethod => false; | |
1571 bool get isOperator => false; | |
1572 bool get isConstructor => false; | |
1573 bool get isConstConstructor => false; | |
1574 bool get isGenerativeConstructor => false; | |
1575 bool get isFactoryConstructor => false; | |
1576 bool get isRedirectingConstructor => false; | |
1577 bool get isAbstract => false; | |
1578 | |
1579 bool get isSetter => !isGetter; | |
1580 bool get isPrivate => n(simpleName).startsWith('_'); | |
1581 | |
1582 Symbol get qualifiedName => computeQualifiedName(owner, simpleName); | |
1583 Symbol get constructorName => const Symbol(''); | |
1584 | |
1585 TypeMirror get returnType => _target.type; | |
1586 List<ParameterMirror> get parameters { | |
1587 if (isGetter) return const []; | |
1588 return new UnmodifiableListView( | |
1589 [new JsSyntheticSetterParameter(this, this._target)]); | |
1590 } | |
1591 | |
1592 List<InstanceMirror> get metadata => const []; | |
1593 String get source => null; | |
1594 SourceLocation get location => throw new UnimplementedError(); | |
1595 } | |
1596 | |
1597 class JsSyntheticSetterParameter implements ParameterMirror { | |
1598 final DeclarationMirror owner; | |
1599 final VariableMirror _target; | |
1600 | |
1601 JsSyntheticSetterParameter(this.owner, this._target); | |
1602 | |
1603 Symbol get simpleName => _target.simpleName; | |
1604 Symbol get qualifiedName => computeQualifiedName(owner, simpleName); | |
1605 TypeMirror get type => _target.type; | |
1606 | |
1607 bool get isOptional => false; | |
1608 bool get isNamed => false; | |
1609 bool get isStatic => false; | |
1610 bool get isTopLevel => false; | |
1611 bool get isConst => false; | |
1612 bool get isFinal => true; | |
1613 bool get isPrivate => false; | |
1614 bool get hasDefaultValue => false; | |
1615 InstanceMirror get defaultValue => null; | |
1616 List<InstanceMirror> get metadata => const []; | |
1617 SourceLocation get location => throw new UnimplementedError(); | |
1618 } | |
1619 | |
1620 class JsClassMirror extends JsTypeMirror with JsObjectMirror | |
1621 implements ClassMirror { | |
1622 final String _mangledName; | |
1623 final _jsConstructor; | |
1624 final String _fieldsDescriptor; | |
1625 final List _fieldsMetadata; | |
1626 final _jsConstructorCache = JsCache.allocate(); | |
1627 List _metadata; | |
1628 ClassMirror _superclass; | |
1629 List<JsMethodMirror> _cachedMethods; | |
1630 List<VariableMirror> _cachedFields; | |
1631 UnmodifiableMapView<Symbol, MethodMirror> _cachedConstructors; | |
1632 UnmodifiableMapView<Symbol, MethodMirror> _cachedMethodsMap; | |
1633 UnmodifiableMapView<Symbol, MethodMirror> _cachedGetters; | |
1634 UnmodifiableMapView<Symbol, MethodMirror> _cachedSetters; | |
1635 UnmodifiableMapView<Symbol, VariableMirror> _cachedVariables; | |
1636 UnmodifiableMapView<Symbol, Mirror> _cachedMembers; | |
1637 UnmodifiableMapView<Symbol, DeclarationMirror> _cachedDeclarations; | |
1638 UnmodifiableListView<InstanceMirror> _cachedMetadata; | |
1639 UnmodifiableListView<ClassMirror> _cachedSuperinterfaces; | |
1640 UnmodifiableListView<TypeVariableMirror> _cachedTypeVariables; | |
1641 Map<Symbol, MethodMirror> _cachedInstanceMembers; | |
1642 Map<Symbol, MethodMirror> _cachedStaticMembers; | |
1643 | |
1644 // Set as side-effect of accessing JsLibraryMirror.classes. | |
1645 JsLibraryMirror _owner; | |
1646 | |
1647 JsClassMirror(Symbol simpleName, | |
1648 this._mangledName, | |
1649 this._jsConstructor, | |
1650 this._fieldsDescriptor, | |
1651 this._fieldsMetadata) | |
1652 : super(simpleName); | |
1653 | |
1654 String get _prettyName => 'ClassMirror'; | |
1655 | |
1656 Map<Symbol, MethodMirror> get __constructors { | |
1657 if (_cachedConstructors != null) return _cachedConstructors; | |
1658 return _cachedConstructors = | |
1659 new UnmodifiableMapView<Symbol, MethodMirror>( | |
1660 filterConstructors(_methods)); | |
1661 } | |
1662 | |
1663 _asRuntimeType() { | |
1664 if (typeVariables.isEmpty) return _jsConstructor; | |
1665 var type = [_jsConstructor]; | |
1666 for (int i = 0; i < typeVariables.length; i ++) { | |
1667 type.add(JsMirrorSystem._dynamicType._asRuntimeType); | |
1668 } | |
1669 return type; | |
1670 } | |
1671 | |
1672 List<JsMethodMirror> _getMethodsWithOwner(DeclarationMirror methodOwner) { | |
1673 var prototype = JS('', '#.prototype', _jsConstructor); | |
1674 // The prototype might not have been processed yet, so do that now. | |
1675 JS('', '#[#]()', prototype, | |
1676 JS_GET_NAME(JsGetName.DEFERRED_ACTION_PROPERTY)); | |
1677 List<String> keys = extractKeys(prototype); | |
1678 var result = <JsMethodMirror>[]; | |
1679 for (String key in keys) { | |
1680 if (isReflectiveDataInPrototype(key)) continue; | |
1681 String simpleName = mangledNames[key]; | |
1682 // [simpleName] can be null if [key] represents an implementation | |
1683 // detail, for example, a bailout method, or runtime type support. | |
1684 // It might also be null if the user has limited what is reified for | |
1685 // reflection with metadata. | |
1686 if (simpleName == null) continue; | |
1687 var function = JS('', '#[#]', prototype, key); | |
1688 if (!isOrdinaryReflectableMethod(function)) continue; | |
1689 if (isAliasedSuperMethod(function, key)) continue; | |
1690 var mirror = | |
1691 new JsMethodMirror.fromUnmangledName( | |
1692 simpleName, function, false, false); | |
1693 result.add(mirror); | |
1694 mirror._owner = methodOwner; | |
1695 } | |
1696 | |
1697 var statics = JS_EMBEDDED_GLOBAL('', STATICS); | |
1698 keys = extractKeys(JS('', '#[#]', statics, _mangledName)); | |
1699 for (String mangledName in keys) { | |
1700 if (isReflectiveDataInPrototype(mangledName)) continue; | |
1701 String unmangledName = mangledName; | |
1702 var jsFunction = JS('', '#[#]', owner._globalObject, mangledName); | |
1703 | |
1704 bool isConstructor = false; | |
1705 if (hasReflectableProperty(jsFunction)) { | |
1706 String reflectionName = | |
1707 JS('String|Null', r'#.$reflectionName', jsFunction); | |
1708 if (reflectionName == null) continue; | |
1709 isConstructor = reflectionName.startsWith('new '); | |
1710 if (isConstructor) { | |
1711 reflectionName = reflectionName.substring(4).replaceAll(r'$', '.'); | |
1712 } | |
1713 unmangledName = reflectionName; | |
1714 } else { | |
1715 continue; | |
1716 } | |
1717 bool isStatic = !isConstructor; // Constructors are not static. | |
1718 JsMethodMirror mirror = | |
1719 new JsMethodMirror.fromUnmangledName( | |
1720 unmangledName, jsFunction, isStatic, isConstructor); | |
1721 result.add(mirror); | |
1722 mirror._owner = methodOwner; | |
1723 } | |
1724 | |
1725 return result; | |
1726 } | |
1727 | |
1728 List<JsMethodMirror> get _methods { | |
1729 if (_cachedMethods != null) return _cachedMethods; | |
1730 return _cachedMethods = _getMethodsWithOwner(this); | |
1731 } | |
1732 | |
1733 List<VariableMirror> _getFieldsWithOwner(DeclarationMirror fieldOwner) { | |
1734 var result = <VariableMirror>[]; | |
1735 | |
1736 var instanceFieldSpecfication = _fieldsDescriptor.split(';')[1]; | |
1737 if (_fieldsMetadata != null) { | |
1738 instanceFieldSpecfication = | |
1739 [instanceFieldSpecfication]..addAll(_fieldsMetadata); | |
1740 } | |
1741 parseCompactFieldSpecification( | |
1742 fieldOwner, instanceFieldSpecfication, false, result); | |
1743 | |
1744 var statics = JS_EMBEDDED_GLOBAL('', STATICS); | |
1745 var staticDescriptor = JS('', '#[#]', statics, _mangledName); | |
1746 if (staticDescriptor != null) { | |
1747 parseCompactFieldSpecification( | |
1748 fieldOwner, | |
1749 JS('', '#[#]', | |
1750 staticDescriptor, | |
1751 JS_GET_NAME(JsGetName.CLASS_DESCRIPTOR_PROPERTY)), | |
1752 true, result); | |
1753 } | |
1754 return result; | |
1755 } | |
1756 | |
1757 List<VariableMirror> get _fields { | |
1758 if (_cachedFields != null) return _cachedFields; | |
1759 return _cachedFields = _getFieldsWithOwner(this); | |
1760 } | |
1761 | |
1762 Map<Symbol, MethodMirror> get __methods { | |
1763 if (_cachedMethodsMap != null) return _cachedMethodsMap; | |
1764 return _cachedMethodsMap = | |
1765 new UnmodifiableMapView<Symbol, MethodMirror>(filterMethods(_methods)); | |
1766 } | |
1767 | |
1768 Map<Symbol, MethodMirror> get __getters { | |
1769 if (_cachedGetters != null) return _cachedGetters; | |
1770 return _cachedGetters = new UnmodifiableMapView<Symbol, MethodMirror>( | |
1771 filterGetters(_methods, __variables)); | |
1772 } | |
1773 | |
1774 Map<Symbol, MethodMirror> get __setters { | |
1775 if (_cachedSetters != null) return _cachedSetters; | |
1776 return _cachedSetters = new UnmodifiableMapView<Symbol, MethodMirror>( | |
1777 filterSetters(_methods, __variables)); | |
1778 } | |
1779 | |
1780 Map<Symbol, VariableMirror> get __variables { | |
1781 if (_cachedVariables != null) return _cachedVariables; | |
1782 var result = new Map(); | |
1783 for (JsVariableMirror mirror in _fields) { | |
1784 result[mirror.simpleName] = mirror; | |
1785 } | |
1786 return _cachedVariables = | |
1787 new UnmodifiableMapView<Symbol, VariableMirror>(result); | |
1788 } | |
1789 | |
1790 Map<Symbol, Mirror> get __members { | |
1791 if (_cachedMembers != null) return _cachedMembers; | |
1792 return _cachedMembers = new UnmodifiableMapView<Symbol, Mirror>( | |
1793 filterMembers(_methods, __variables)); | |
1794 } | |
1795 | |
1796 Map<Symbol, DeclarationMirror> get declarations { | |
1797 if (_cachedDeclarations != null) return _cachedDeclarations; | |
1798 var result = new Map<Symbol, DeclarationMirror>(); | |
1799 addToResult(Symbol key, Mirror value) { | |
1800 result[key] = value; | |
1801 } | |
1802 __members.forEach(addToResult); | |
1803 __constructors.forEach(addToResult); | |
1804 typeVariables.forEach((tv) => result[tv.simpleName] = tv); | |
1805 return _cachedDeclarations = | |
1806 new UnmodifiableMapView<Symbol, DeclarationMirror>(result); | |
1807 } | |
1808 | |
1809 Map<Symbol, MethodMirror> get staticMembers { | |
1810 if (_cachedStaticMembers == null) { | |
1811 var result = new Map<Symbol, MethodMirror>(); | |
1812 declarations.values.forEach((decl) { | |
1813 if (decl is MethodMirror && decl.isStatic && !decl.isConstructor) { | |
1814 result[decl.simpleName] = decl; | |
1815 } | |
1816 if (decl is VariableMirror && decl.isStatic) { | |
1817 var getterName = decl.simpleName; | |
1818 result[getterName] = new JsSyntheticAccessor( | |
1819 this, getterName, true, true, false, decl); | |
1820 if (!decl.isFinal) { | |
1821 var setterName = setterSymbol(decl.simpleName); | |
1822 result[setterName] = new JsSyntheticAccessor( | |
1823 this, setterName, false, true, false, decl); | |
1824 } | |
1825 } | |
1826 }); | |
1827 _cachedStaticMembers = result; | |
1828 } | |
1829 return _cachedStaticMembers; | |
1830 } | |
1831 | |
1832 Map<Symbol, MethodMirror> get instanceMembers { | |
1833 if (_cachedInstanceMembers == null) { | |
1834 var result = new Map<Symbol, MethodMirror>(); | |
1835 if (superclass != null) { | |
1836 result.addAll(superclass.instanceMembers); | |
1837 } | |
1838 declarations.values.forEach((decl) { | |
1839 if (decl is MethodMirror && !decl.isStatic && | |
1840 !decl.isConstructor && !decl.isAbstract) { | |
1841 result[decl.simpleName] = decl; | |
1842 } | |
1843 if (decl is VariableMirror && !decl.isStatic) { | |
1844 var getterName = decl.simpleName; | |
1845 result[getterName] = new JsSyntheticAccessor( | |
1846 this, getterName, true, false, false, decl); | |
1847 if (!decl.isFinal) { | |
1848 var setterName = setterSymbol(decl.simpleName); | |
1849 result[setterName] = new JsSyntheticAccessor( | |
1850 this, setterName, false, false, false, decl); | |
1851 } | |
1852 } | |
1853 }); | |
1854 _cachedInstanceMembers = result; | |
1855 } | |
1856 return _cachedInstanceMembers; | |
1857 } | |
1858 | |
1859 InstanceMirror setField(Symbol fieldName, Object arg) { | |
1860 JsVariableMirror mirror = __variables[fieldName]; | |
1861 if (mirror != null && mirror.isStatic && !mirror.isFinal) { | |
1862 // '$' (JS_CURRENT_ISOLATE()) stores state which is stored directly, so | |
1863 // we shouldn't use [JsLibraryMirror._globalObject] here. | |
1864 String jsName = mirror._jsName; | |
1865 if (!JS('bool', '# in #', jsName, JS_CURRENT_ISOLATE())) { | |
1866 throw new RuntimeError('Cannot find "$jsName" in current isolate.'); | |
1867 } | |
1868 JS('void', '#[#] = #', JS_CURRENT_ISOLATE(), jsName, arg); | |
1869 return reflect(arg); | |
1870 } | |
1871 Symbol setterName = setterSymbol(fieldName); | |
1872 if (mirror == null) { | |
1873 JsMethodMirror setter = __setters[setterName]; | |
1874 if (setter != null) { | |
1875 setter._invoke([arg], const {}); | |
1876 return reflect(arg); | |
1877 } | |
1878 } | |
1879 throw new NoSuchStaticMethodError.method(null, setterName, [arg], null); | |
1880 } | |
1881 | |
1882 bool _staticFieldExists(Symbol fieldName) { | |
1883 JsVariableMirror mirror = __variables[fieldName]; | |
1884 if (mirror != null) return mirror.isStatic; | |
1885 JsMethodMirror getter = __getters[fieldName]; | |
1886 return getter != null && getter.isStatic; | |
1887 } | |
1888 | |
1889 InstanceMirror getField(Symbol fieldName) { | |
1890 JsVariableMirror mirror = __variables[fieldName]; | |
1891 if (mirror != null && mirror.isStatic) { | |
1892 String jsName = mirror._jsName; | |
1893 // '$' (JS_CURRENT_ISOLATE()) stores state which is read directly, so | |
1894 // we shouldn't use [JsLibraryMirror._globalObject] here. | |
1895 if (!JS('bool', '# in #', jsName, JS_CURRENT_ISOLATE())) { | |
1896 throw new RuntimeError('Cannot find "$jsName" in current isolate.'); | |
1897 } | |
1898 var lazies = JS_EMBEDDED_GLOBAL('', LAZIES); | |
1899 if (JS('bool', '# in #', jsName, lazies)) { | |
1900 String getterName = JS('String', '#[#]', lazies, jsName); | |
1901 return reflect(JS('', '#[#]()', JS_CURRENT_ISOLATE(), getterName)); | |
1902 } else { | |
1903 return reflect(JS('', '#[#]', JS_CURRENT_ISOLATE(), jsName)); | |
1904 } | |
1905 } | |
1906 JsMethodMirror getter = __getters[fieldName]; | |
1907 if (getter != null && getter.isStatic) { | |
1908 return reflect(getter._invoke(const [], const {})); | |
1909 } | |
1910 // If the fieldName designates a static function we have to return | |
1911 // its closure. | |
1912 JsMethodMirror method = __methods[fieldName]; | |
1913 if (method != null && method.isStatic) { | |
1914 // We invoke the same getter that Dart code would execute. During | |
1915 // initialization we have stored that getter on the function (so that | |
1916 // we can find it more easily here). | |
1917 var getter = JS("", "#['\$getter']", method._jsFunction); | |
1918 if (getter == null) throw new UnimplementedError(); | |
1919 return reflect(JS("", "#()", getter)); | |
1920 } | |
1921 throw new NoSuchStaticMethodError.method(null, fieldName, null, null); | |
1922 } | |
1923 | |
1924 _getInvokedInstance(Symbol constructorName, | |
1925 List positionalArguments, | |
1926 [Map<Symbol, dynamic> namedArguments]) { | |
1927 if (namedArguments != null && !namedArguments.isEmpty) { | |
1928 throw new UnsupportedError('Named arguments are not implemented.'); | |
1929 } | |
1930 JsMethodMirror mirror = | |
1931 JsCache.fetch(_jsConstructorCache, n(constructorName)); | |
1932 if (mirror == null) { | |
1933 mirror = __constructors.values.firstWhere( | |
1934 (m) => m.constructorName == constructorName, | |
1935 orElse: () { | |
1936 throw new NoSuchStaticMethodError.method( | |
1937 null, constructorName, positionalArguments, namedArguments); | |
1938 }); | |
1939 JsCache.update(_jsConstructorCache, n(constructorName), mirror); | |
1940 } | |
1941 return mirror._invoke(positionalArguments, namedArguments); | |
1942 } | |
1943 | |
1944 InstanceMirror newInstance(Symbol constructorName, | |
1945 List positionalArguments, | |
1946 [Map<Symbol, dynamic> namedArguments]) { | |
1947 return reflect(_getInvokedInstance(constructorName, | |
1948 positionalArguments, | |
1949 namedArguments)); | |
1950 } | |
1951 | |
1952 JsLibraryMirror get owner { | |
1953 if (_owner == null) { | |
1954 for (var list in JsMirrorSystem.librariesByName.values) { | |
1955 for (JsLibraryMirror library in list) { | |
1956 // This will set _owner field on all classes as a side | |
1957 // effect. This gives us a fast path to reflect on a | |
1958 // class without parsing reflection data. | |
1959 library.__classes; | |
1960 } | |
1961 } | |
1962 if (_owner == null) { | |
1963 throw new StateError('Class "${n(simpleName)}" has no owner'); | |
1964 } | |
1965 } | |
1966 return _owner; | |
1967 } | |
1968 | |
1969 List<InstanceMirror> get metadata { | |
1970 if (_cachedMetadata != null) return _cachedMetadata; | |
1971 if (_metadata == null) { | |
1972 _metadata = extractMetadata(JS('', '#.prototype', _jsConstructor)); | |
1973 } | |
1974 return _cachedMetadata = | |
1975 new UnmodifiableListView<InstanceMirror>(_metadata.map(reflect)); | |
1976 } | |
1977 | |
1978 ClassMirror get superclass { | |
1979 if (_superclass == null) { | |
1980 var typeInformationContainer = JS_EMBEDDED_GLOBAL('', TYPE_INFORMATION); | |
1981 List<int> typeInformation = | |
1982 JS('List|Null', '#[#]', typeInformationContainer, _mangledName); | |
1983 if (typeInformation != null) { | |
1984 var type = getType(typeInformation[0]); | |
1985 _superclass = typeMirrorFromRuntimeTypeRepresentation(this, type); | |
1986 } else { | |
1987 var superclassName = _fieldsDescriptor.split(';')[0].split(':')[0]; | |
1988 // TODO(zarah): Remove special handing of mixins. | |
1989 var mixins = superclassName.split('+'); | |
1990 if (mixins.length > 1) { | |
1991 if (mixins.length != 2) { | |
1992 throw new RuntimeError('Strange mixin: $_fieldsDescriptor'); | |
1993 } | |
1994 _superclass = reflectClassByMangledName(mixins[0]); | |
1995 } else { | |
1996 // Use _superclass == this to represent class with no superclass | |
1997 // (Object). | |
1998 _superclass = (superclassName == '') | |
1999 ? this : reflectClassByMangledName(superclassName); | |
2000 } | |
2001 } | |
2002 } | |
2003 return _superclass == this ? null : _superclass; | |
2004 } | |
2005 | |
2006 InstanceMirror invoke(Symbol memberName, | |
2007 List positionalArguments, | |
2008 [Map<Symbol,dynamic> namedArguments]) { | |
2009 // Mirror API gotcha: Calling [invoke] on a ClassMirror means invoke a | |
2010 // static method. | |
2011 | |
2012 if (namedArguments != null && !namedArguments.isEmpty) { | |
2013 throw new UnsupportedError('Named arguments are not implemented.'); | |
2014 } | |
2015 JsMethodMirror mirror = __methods[memberName]; | |
2016 | |
2017 if (mirror == null && _staticFieldExists(memberName)) { | |
2018 return getField(memberName) | |
2019 .invoke(#call, positionalArguments, namedArguments); | |
2020 } | |
2021 if (mirror == null || !mirror.isStatic) { | |
2022 throw new NoSuchStaticMethodError.method( | |
2023 null, memberName, positionalArguments, namedArguments); | |
2024 } | |
2025 if (!mirror.canInvokeReflectively()) { | |
2026 throwInvalidReflectionError(n(memberName)); | |
2027 } | |
2028 return reflect(mirror._invoke(positionalArguments, namedArguments)); | |
2029 } | |
2030 | |
2031 bool get isOriginalDeclaration => true; | |
2032 | |
2033 ClassMirror get originalDeclaration => this; | |
2034 | |
2035 List<ClassMirror> _getSuperinterfacesWithOwner(DeclarationMirror owner) { | |
2036 var typeInformationContainer = JS_EMBEDDED_GLOBAL('', TYPE_INFORMATION); | |
2037 List<int> typeInformation = | |
2038 JS('List|Null', '#[#]', typeInformationContainer, _mangledName); | |
2039 List<ClassMirror> result = const <ClassMirror>[]; | |
2040 if (typeInformation != null) { | |
2041 ClassMirror lookupType(int i) { | |
2042 var type = getType(i); | |
2043 return typeMirrorFromRuntimeTypeRepresentation(owner, type); | |
2044 } | |
2045 | |
2046 //We skip the first since it is the supertype. | |
2047 result = typeInformation.skip(1).map(lookupType).toList(); | |
2048 } | |
2049 | |
2050 return new UnmodifiableListView<ClassMirror>(result); | |
2051 } | |
2052 | |
2053 List<ClassMirror> get superinterfaces { | |
2054 if (_cachedSuperinterfaces != null) return _cachedSuperinterfaces; | |
2055 return _cachedSuperinterfaces = _getSuperinterfacesWithOwner(this); | |
2056 } | |
2057 | |
2058 List<TypeVariableMirror> get typeVariables { | |
2059 if (_cachedTypeVariables != null) return _cachedTypeVariables; | |
2060 List result = new List(); | |
2061 List typeVariables = | |
2062 JS('JSExtendableArray|Null', '#.prototype["<>"]', _jsConstructor); | |
2063 if (typeVariables == null) return result; | |
2064 for (int i = 0; i < typeVariables.length; i++) { | |
2065 TypeVariable typeVariable = getMetadata(typeVariables[i]); | |
2066 result.add(new JsTypeVariableMirror(typeVariable, this, | |
2067 typeVariables[i])); | |
2068 } | |
2069 return _cachedTypeVariables = new UnmodifiableListView(result); | |
2070 } | |
2071 | |
2072 List<TypeMirror> get typeArguments => const <TypeMirror>[]; | |
2073 | |
2074 bool get hasReflectedType => typeVariables.length == 0; | |
2075 | |
2076 Type get reflectedType { | |
2077 if (!hasReflectedType) { | |
2078 throw new UnsupportedError( | |
2079 "Declarations of generics have no reflected type"); | |
2080 } | |
2081 return createRuntimeType(_mangledName); | |
2082 } | |
2083 | |
2084 // TODO(ahe): Implement this. | |
2085 ClassMirror get mixin => throw new UnimplementedError(); | |
2086 | |
2087 bool get isAbstract => throw new UnimplementedError(); | |
2088 | |
2089 bool get isEnum => throw new UnimplementedError(); | |
2090 | |
2091 bool isSubclassOf(ClassMirror other) { | |
2092 if (other is! ClassMirror) { | |
2093 throw new ArgumentError(other); | |
2094 } | |
2095 if (other is JsFunctionTypeMirror) { | |
2096 return false; | |
2097 } if (other is JsClassMirror && | |
2098 JS('bool', '# == #', other._jsConstructor, _jsConstructor)) { | |
2099 return true; | |
2100 } else if (superclass == null) { | |
2101 return false; | |
2102 } else { | |
2103 return superclass.isSubclassOf(other); | |
2104 } | |
2105 } | |
2106 } | |
2107 | |
2108 class JsVariableMirror extends JsDeclarationMirror implements VariableMirror { | |
2109 | |
2110 // TODO(ahe): The values in these fields are virtually untested. | |
2111 final String _jsName; | |
2112 final bool isFinal; | |
2113 final bool isStatic; | |
2114 final _metadataFunction; | |
2115 final DeclarationMirror _owner; | |
2116 final int _type; | |
2117 List _metadata; | |
2118 | |
2119 JsVariableMirror(Symbol simpleName, | |
2120 this._jsName, | |
2121 this._type, | |
2122 this.isFinal, | |
2123 this.isStatic, | |
2124 this._metadataFunction, | |
2125 this._owner) | |
2126 : super(simpleName); | |
2127 | |
2128 factory JsVariableMirror.from(String descriptor, | |
2129 metadataFunction, | |
2130 JsDeclarationMirror owner, | |
2131 bool isStatic) { | |
2132 List<String> fieldInformation = descriptor.split('-'); | |
2133 if (fieldInformation.length == 1) { | |
2134 // The field is not available for reflection. | |
2135 // TODO(ahe): Should return an unreflectable field. | |
2136 return null; | |
2137 } | |
2138 | |
2139 String field = fieldInformation[0]; | |
2140 int length = field.length; | |
2141 var code = fieldCode(field.codeUnitAt(length - 1)); | |
2142 bool isFinal = false; | |
2143 if (code == 0) return null; // Inherited field. | |
2144 bool hasGetter = (code & 3) != 0; | |
2145 bool hasSetter = (code >> 2) != 0; | |
2146 isFinal = !hasSetter; | |
2147 length--; | |
2148 String jsName; | |
2149 String accessorName = jsName = field.substring(0, length); | |
2150 int divider = field.indexOf(':'); | |
2151 if (divider > 0) { | |
2152 accessorName = accessorName.substring(0, divider); | |
2153 jsName = field.substring(divider + 1); | |
2154 } | |
2155 var unmangledName; | |
2156 if (isStatic) { | |
2157 unmangledName = mangledGlobalNames[accessorName]; | |
2158 } else { | |
2159 String getterPrefix = JS_GET_NAME(JsGetName.GETTER_PREFIX); | |
2160 unmangledName = mangledNames['$getterPrefix$accessorName']; | |
2161 } | |
2162 if (unmangledName == null) unmangledName = accessorName; | |
2163 if (!hasSetter) { | |
2164 // TODO(ahe): This is a hack to handle checked setters in checked mode. | |
2165 var setterName = s('$unmangledName='); | |
2166 for (JsMethodMirror method in owner._methods) { | |
2167 if (method.simpleName == setterName) { | |
2168 isFinal = false; | |
2169 break; | |
2170 } | |
2171 } | |
2172 } | |
2173 int type = int.parse(fieldInformation[1], onError: (_) => null); | |
2174 return new JsVariableMirror(s(unmangledName), | |
2175 jsName, | |
2176 type, | |
2177 isFinal, | |
2178 isStatic, | |
2179 metadataFunction, | |
2180 owner); | |
2181 } | |
2182 | |
2183 String get _prettyName => 'VariableMirror'; | |
2184 | |
2185 TypeMirror get type { | |
2186 return typeMirrorFromRuntimeTypeRepresentation(owner, getType(_type)); | |
2187 } | |
2188 | |
2189 DeclarationMirror get owner => _owner; | |
2190 | |
2191 List<InstanceMirror> get metadata { | |
2192 preserveMetadata(); | |
2193 if (_metadata == null) { | |
2194 _metadata = (_metadataFunction == null) | |
2195 ? const [] : JS('', '#()', _metadataFunction); | |
2196 } | |
2197 return _metadata.map(reflect).toList(); | |
2198 } | |
2199 | |
2200 static int fieldCode(int code) { | |
2201 if (code >= 60 && code <= 64) return code - 59; | |
2202 if (code >= 123 && code <= 126) return code - 117; | |
2203 if (code >= 37 && code <= 43) return code - 27; | |
2204 return 0; | |
2205 } | |
2206 | |
2207 _getField(JsMirror receiver) => receiver._loadField(_jsName); | |
2208 | |
2209 void _setField(JsMirror receiver, Object arg) { | |
2210 if (isFinal) { | |
2211 // TODO(floitsch): when the field is non-static we don't want to have | |
2212 // a mirror as receiver. | |
2213 if (isStatic) { | |
2214 throw new NoSuchStaticMethodError.method( | |
2215 null, setterSymbol(simpleName), [arg], null); | |
2216 } | |
2217 throw new NoSuchMethodError(this, setterSymbol(simpleName), [arg], null); | |
2218 } | |
2219 receiver._storeField(_jsName, arg); | |
2220 } | |
2221 | |
2222 // TODO(ahe): Implement this method. | |
2223 bool get isConst => throw new UnimplementedError(); | |
2224 } | |
2225 | |
2226 class JsClosureMirror extends JsInstanceMirror implements ClosureMirror { | |
2227 JsClosureMirror(reflectee) | |
2228 : super(reflectee); | |
2229 | |
2230 MethodMirror get function { | |
2231 String cacheName = Primitives.mirrorFunctionCacheName; | |
2232 JsMethodMirror cachedFunction; | |
2233 // TODO(ahe): Restore caching. | |
2234 //= JS('JsMethodMirror|Null', r'#.constructor[#]', reflectee, cacheName); | |
2235 if (cachedFunction != null) return cachedFunction; | |
2236 disableTreeShaking(); | |
2237 // TODO(ahe): What about optional parameters (named or not). | |
2238 String callPrefix = "${JS_GET_NAME(JsGetName.CALL_PREFIX)}\$"; | |
2239 var extractCallName = JS('', r''' | |
2240 function(reflectee) { | |
2241 var properties = Object.keys(reflectee.constructor.prototype); | |
2242 for (var i = 0; i < properties.length; i++) { | |
2243 var property = properties[i]; | |
2244 if (# == property.substring(0, #) && | |
2245 property[#] >= '0' && | |
2246 property[#] <= '9') return property; | |
2247 } | |
2248 return null; | |
2249 } | |
2250 ''', callPrefix, callPrefix.length, callPrefix.length, callPrefix.length); | |
2251 String callName = JS('String|Null', '#(#)', extractCallName, reflectee); | |
2252 if (callName == null) { | |
2253 throw new RuntimeError('Cannot find callName on "$reflectee"'); | |
2254 } | |
2255 // TODO(floitsch): What about optional parameters? | |
2256 int parameterCount = int.parse(callName.split(r'$')[1]); | |
2257 if (reflectee is BoundClosure) { | |
2258 var target = BoundClosure.targetOf(reflectee); | |
2259 var self = BoundClosure.selfOf(reflectee); | |
2260 var name = mangledNames[BoundClosure.nameOf(reflectee)]; | |
2261 if (name == null) { | |
2262 throwInvalidReflectionError(name); | |
2263 } | |
2264 cachedFunction = new JsMethodMirror.fromUnmangledName( | |
2265 name, target, false, false); | |
2266 } else { | |
2267 bool isStatic = true; // TODO(ahe): Compute isStatic correctly. | |
2268 var jsFunction = JS('', '#[#]', reflectee, callName); | |
2269 var dummyOptionalParameterCount = 0; | |
2270 cachedFunction = new JsMethodMirror( | |
2271 s(callName), jsFunction, parameterCount, dummyOptionalParameterCount, | |
2272 false, false, isStatic, false, false); | |
2273 } | |
2274 JS('void', r'#.constructor[#] = #', reflectee, cacheName, cachedFunction); | |
2275 return cachedFunction; | |
2276 } | |
2277 | |
2278 InstanceMirror apply(List positionalArguments, | |
2279 [Map<Symbol, dynamic> namedArguments]) { | |
2280 return reflect( | |
2281 Function.apply(reflectee, positionalArguments, namedArguments)); | |
2282 } | |
2283 | |
2284 String toString() => "ClosureMirror on '${Error.safeToString(reflectee)}'"; | |
2285 | |
2286 // TODO(ahe): Implement this method. | |
2287 String get source => throw new UnimplementedError(); | |
2288 } | |
2289 | |
2290 class JsMethodMirror extends JsDeclarationMirror implements MethodMirror { | |
2291 final _jsFunction; | |
2292 final int _requiredParameterCount; | |
2293 final int _optionalParameterCount; | |
2294 final bool isGetter; | |
2295 final bool isSetter; | |
2296 final bool isStatic; | |
2297 final bool isConstructor; | |
2298 final bool isOperator; | |
2299 DeclarationMirror _owner; | |
2300 List _metadata; | |
2301 TypeMirror _returnType; | |
2302 UnmodifiableListView<ParameterMirror> _parameters; | |
2303 | |
2304 JsMethodMirror(Symbol simpleName, | |
2305 this._jsFunction, | |
2306 this._requiredParameterCount, | |
2307 this._optionalParameterCount, | |
2308 this.isGetter, | |
2309 this.isSetter, | |
2310 this.isStatic, | |
2311 this.isConstructor, | |
2312 this.isOperator) | |
2313 : super(simpleName); | |
2314 | |
2315 factory JsMethodMirror.fromUnmangledName(String name, | |
2316 jsFunction, | |
2317 bool isStatic, | |
2318 bool isConstructor) { | |
2319 List<String> info = name.split(':'); | |
2320 name = info[0]; | |
2321 bool isOperator = isOperatorName(name); | |
2322 bool isSetter = !isOperator && name.endsWith('='); | |
2323 int requiredParameterCount = 0; | |
2324 int optionalParameterCount = 0; | |
2325 bool isGetter = false; | |
2326 if (info.length == 1) { | |
2327 if (isSetter) { | |
2328 requiredParameterCount = 1; | |
2329 } else { | |
2330 isGetter = true; | |
2331 requiredParameterCount = 0; | |
2332 } | |
2333 } else { | |
2334 ReflectionInfo reflectionInfo = new ReflectionInfo(jsFunction); | |
2335 requiredParameterCount = reflectionInfo.requiredParameterCount; | |
2336 optionalParameterCount = reflectionInfo.optionalParameterCount; | |
2337 assert(int.parse(info[1]) == requiredParameterCount | |
2338 + optionalParameterCount); | |
2339 } | |
2340 return new JsMethodMirror( | |
2341 s(name), jsFunction, requiredParameterCount, optionalParameterCount, | |
2342 isGetter, isSetter, isStatic, isConstructor, isOperator); | |
2343 } | |
2344 | |
2345 String get _prettyName => 'MethodMirror'; | |
2346 | |
2347 int get _parameterCount => _requiredParameterCount + _optionalParameterCount; | |
2348 | |
2349 List<ParameterMirror> get parameters { | |
2350 if (_parameters != null) return _parameters; | |
2351 metadata; // Compute _parameters as a side-effect of extracting metadata. | |
2352 return _parameters; | |
2353 } | |
2354 | |
2355 bool canInvokeReflectively() { | |
2356 return hasReflectableProperty(_jsFunction); | |
2357 } | |
2358 | |
2359 DeclarationMirror get owner => _owner; | |
2360 | |
2361 TypeMirror get returnType { | |
2362 metadata; // Compute _returnType as a side-effect of extracting metadata. | |
2363 return _returnType; | |
2364 } | |
2365 | |
2366 List<InstanceMirror> get metadata { | |
2367 if (_metadata == null) { | |
2368 var raw = extractMetadata(_jsFunction); | |
2369 var formals = new List(_parameterCount); | |
2370 ReflectionInfo info = new ReflectionInfo(_jsFunction); | |
2371 if (info != null) { | |
2372 assert(_parameterCount | |
2373 == info.requiredParameterCount + info.optionalParameterCount); | |
2374 var functionType = info.functionType; | |
2375 var type; | |
2376 if (functionType is int) { | |
2377 type = new JsFunctionTypeMirror(info.computeFunctionRti(null), this); | |
2378 assert(_parameterCount == type.parameters.length); | |
2379 } else if (isTopLevel) { | |
2380 type = new JsFunctionTypeMirror(info.computeFunctionRti(null), owner); | |
2381 } else { | |
2382 TypeMirror ownerType = owner; | |
2383 JsClassMirror ownerClass = ownerType.originalDeclaration; | |
2384 type = new JsFunctionTypeMirror( | |
2385 info.computeFunctionRti(ownerClass._jsConstructor), | |
2386 owner); | |
2387 } | |
2388 // Constructors aren't reified with their return type. | |
2389 if (isConstructor) { | |
2390 _returnType = owner; | |
2391 } else { | |
2392 _returnType = type.returnType; | |
2393 } | |
2394 int i = 0; | |
2395 bool isNamed = info.areOptionalParametersNamed; | |
2396 for (JsParameterMirror parameter in type.parameters) { | |
2397 var name = info.parameterName(i); | |
2398 List<int> annotations = info.parameterMetadataAnnotations(i); | |
2399 var p; | |
2400 if (i < info.requiredParameterCount) { | |
2401 p = new JsParameterMirror(name, this, parameter._type, | |
2402 metadataList: annotations); | |
2403 } else { | |
2404 var defaultValue = info.defaultValue(i); | |
2405 p = new JsParameterMirror( | |
2406 name, this, parameter._type, metadataList: annotations, | |
2407 isOptional: true, isNamed: isNamed, defaultValue: defaultValue); | |
2408 } | |
2409 formals[i++] = p; | |
2410 } | |
2411 } | |
2412 _parameters = new UnmodifiableListView<ParameterMirror>(formals); | |
2413 _metadata = new UnmodifiableListView(raw.map(reflect)); | |
2414 } | |
2415 return _metadata; | |
2416 } | |
2417 | |
2418 Symbol get constructorName { | |
2419 // TODO(ahe): I believe it is more appropriate to throw an exception or | |
2420 // return null. | |
2421 if (!isConstructor) return const Symbol(''); | |
2422 String name = n(simpleName); | |
2423 int index = name.indexOf('.'); | |
2424 if (index == -1) return const Symbol(''); | |
2425 return s(name.substring(index + 1)); | |
2426 } | |
2427 | |
2428 _invoke(List positionalArguments, Map<Symbol, dynamic> namedArguments) { | |
2429 if (namedArguments != null && !namedArguments.isEmpty) { | |
2430 throw new UnsupportedError('Named arguments are not implemented.'); | |
2431 } | |
2432 if (!isStatic && !isConstructor) { | |
2433 throw new RuntimeError('Cannot invoke instance method without receiver.'); | |
2434 } | |
2435 int positionalLength = positionalArguments.length; | |
2436 if (positionalLength < _requiredParameterCount || | |
2437 positionalLength > _parameterCount || | |
2438 _jsFunction == null) { | |
2439 // TODO(ahe): What receiver to use? | |
2440 throw new NoSuchMethodError( | |
2441 owner, simpleName, positionalArguments, namedArguments); | |
2442 } | |
2443 if (positionalLength < _parameterCount) { | |
2444 // Fill up with default values. | |
2445 // Make a copy so we don't modify the input. | |
2446 positionalArguments = positionalArguments.toList(); | |
2447 for (int i = positionalLength; i < parameters.length; i++) { | |
2448 JsParameterMirror parameter = parameters[i]; | |
2449 positionalArguments.add(parameter.defaultValue.reflectee); | |
2450 } | |
2451 } | |
2452 // Using JS_CURRENT_ISOLATE() ('$') here is actually correct, although | |
2453 // _jsFunction may not be a property of '$', most static functions do not | |
2454 // care who their receiver is. But to lazy getters, it is important that | |
2455 // 'this' is '$'. | |
2456 return JS('', r'#.apply(#, #)', _jsFunction, JS_CURRENT_ISOLATE(), | |
2457 new List.from(positionalArguments)); | |
2458 } | |
2459 | |
2460 _getField(JsMirror receiver) { | |
2461 if (isGetter) { | |
2462 return _invoke([], null); | |
2463 } else { | |
2464 // TODO(ahe): Closurize method. | |
2465 throw new UnimplementedError('getField on $receiver'); | |
2466 } | |
2467 } | |
2468 | |
2469 _setField(JsMirror receiver, Object arg) { | |
2470 if (isSetter) { | |
2471 return _invoke([arg], null); | |
2472 } else { | |
2473 throw new NoSuchMethodError(this, setterSymbol(simpleName), [], null); | |
2474 } | |
2475 } | |
2476 | |
2477 // Abstract methods are tree-shaken away. | |
2478 bool get isAbstract => false; | |
2479 | |
2480 // TODO(ahe, 14633): This might not be true for all cases. | |
2481 bool get isSynthetic => false; | |
2482 | |
2483 // TODO(ahe): Test this. | |
2484 bool get isRegularMethod => !isGetter && !isSetter && !isConstructor; | |
2485 | |
2486 // TODO(ahe): Implement this method. | |
2487 bool get isConstConstructor => throw new UnimplementedError(); | |
2488 | |
2489 // TODO(ahe): Implement this method. | |
2490 bool get isGenerativeConstructor => throw new UnimplementedError(); | |
2491 | |
2492 // TODO(ahe): Implement this method. | |
2493 bool get isRedirectingConstructor => throw new UnimplementedError(); | |
2494 | |
2495 // TODO(ahe): Implement this method. | |
2496 bool get isFactoryConstructor => throw new UnimplementedError(); | |
2497 | |
2498 // TODO(ahe): Implement this method. | |
2499 String get source => throw new UnimplementedError(); | |
2500 } | |
2501 | |
2502 class JsParameterMirror extends JsDeclarationMirror implements ParameterMirror { | |
2503 final DeclarationMirror owner; | |
2504 // A JS object representing the type. | |
2505 final _type; | |
2506 | |
2507 final bool isOptional; | |
2508 | |
2509 final bool isNamed; | |
2510 | |
2511 final int _defaultValue; | |
2512 | |
2513 final List<int> metadataList; | |
2514 | |
2515 JsParameterMirror(String unmangledName, | |
2516 this.owner, | |
2517 this._type, | |
2518 {this.metadataList: const <int>[], | |
2519 this.isOptional: false, | |
2520 this.isNamed: false, | |
2521 defaultValue}) | |
2522 : _defaultValue = defaultValue, | |
2523 super(s(unmangledName)); | |
2524 | |
2525 String get _prettyName => 'ParameterMirror'; | |
2526 | |
2527 TypeMirror get type { | |
2528 return typeMirrorFromRuntimeTypeRepresentation(owner, _type); | |
2529 } | |
2530 | |
2531 // Only true for static fields, never for a parameter. | |
2532 bool get isStatic => false; | |
2533 | |
2534 // TODO(ahe): Implement this. | |
2535 bool get isFinal => false; | |
2536 | |
2537 // TODO(ahe): Implement this. | |
2538 bool get isConst => false; | |
2539 | |
2540 bool get hasDefaultValue => _defaultValue != null; | |
2541 | |
2542 get defaultValue { | |
2543 return hasDefaultValue ? reflect(getMetadata(_defaultValue)) : null; | |
2544 } | |
2545 | |
2546 List<InstanceMirror> get metadata { | |
2547 preserveMetadata(); | |
2548 return metadataList.map((int i) => reflect(getMetadata(i))).toList(); | |
2549 } | |
2550 } | |
2551 | |
2552 class JsTypedefMirror extends JsDeclarationMirror implements TypedefMirror { | |
2553 final String _mangledName; | |
2554 JsFunctionTypeMirror referent; | |
2555 | |
2556 JsTypedefMirror(Symbol simpleName, this._mangledName, _typeData) | |
2557 : super(simpleName) { | |
2558 referent = new JsFunctionTypeMirror(_typeData, this); | |
2559 } | |
2560 | |
2561 JsFunctionTypeMirror get value => referent; | |
2562 | |
2563 String get _prettyName => 'TypedefMirror'; | |
2564 | |
2565 bool get hasReflectedType => throw new UnimplementedError(); | |
2566 | |
2567 Type get reflectedType => createRuntimeType(_mangledName); | |
2568 | |
2569 // TODO(floitsch): Implement this method. | |
2570 List<TypeVariableMirror> get typeVariables => throw new UnimplementedError(); | |
2571 | |
2572 // TODO(floitsch): Implement this method. | |
2573 List<TypeMirror> get typeArguments => throw new UnimplementedError(); | |
2574 | |
2575 bool get isOriginalDeclaration => true; | |
2576 | |
2577 TypeMirror get originalDeclaration => this; | |
2578 | |
2579 // TODO(floitsch): Implement this method. | |
2580 DeclarationMirror get owner => throw new UnimplementedError(); | |
2581 | |
2582 // TODO(ahe): Implement this method. | |
2583 List<InstanceMirror> get metadata => throw new UnimplementedError(); | |
2584 | |
2585 bool isSubtypeOf(TypeMirror other) => throw new UnimplementedError(); | |
2586 bool isAssignableTo(TypeMirror other) => throw new UnimplementedError(); | |
2587 } | |
2588 | |
2589 // TODO(ahe): Remove this class when API is updated. | |
2590 class BrokenClassMirror { | |
2591 bool get hasReflectedType => throw new UnimplementedError(); | |
2592 Type get reflectedType => throw new UnimplementedError(); | |
2593 ClassMirror get superclass => throw new UnimplementedError(); | |
2594 List<ClassMirror> get superinterfaces => throw new UnimplementedError(); | |
2595 Map<Symbol, DeclarationMirror> get declarations | |
2596 => throw new UnimplementedError(); | |
2597 Map<Symbol, MethodMirror> get instanceMembers | |
2598 => throw new UnimplementedError(); | |
2599 Map<Symbol, MethodMirror> get staticMembers => throw new UnimplementedError(); | |
2600 ClassMirror get mixin => throw new UnimplementedError(); | |
2601 InstanceMirror newInstance( | |
2602 Symbol constructorName, | |
2603 List positionalArguments, | |
2604 [Map<Symbol, dynamic> namedArguments]) => throw new UnimplementedError(); | |
2605 InstanceMirror invoke(Symbol memberName, | |
2606 List positionalArguments, | |
2607 [Map<Symbol, dynamic> namedArguments]) | |
2608 => throw new UnimplementedError(); | |
2609 InstanceMirror getField(Symbol fieldName) => throw new UnimplementedError(); | |
2610 InstanceMirror setField(Symbol fieldName, Object value) | |
2611 => throw new UnimplementedError(); | |
2612 List<TypeVariableMirror> get typeVariables => throw new UnimplementedError(); | |
2613 List<TypeMirror> get typeArguments => throw new UnimplementedError(); | |
2614 TypeMirror get originalDeclaration => throw new UnimplementedError(); | |
2615 Symbol get simpleName => throw new UnimplementedError(); | |
2616 Symbol get qualifiedName => throw new UnimplementedError(); | |
2617 bool get isPrivate => throw new UnimplementedError(); | |
2618 bool get isTopLevel => throw new UnimplementedError(); | |
2619 SourceLocation get location => throw new UnimplementedError(); | |
2620 List<InstanceMirror> get metadata => throw new UnimplementedError(); | |
2621 } | |
2622 | |
2623 class JsFunctionTypeMirror extends BrokenClassMirror | |
2624 implements FunctionTypeMirror { | |
2625 final _typeData; | |
2626 String _cachedToString; | |
2627 TypeMirror _cachedReturnType; | |
2628 UnmodifiableListView<ParameterMirror> _cachedParameters; | |
2629 DeclarationMirror owner; | |
2630 | |
2631 JsFunctionTypeMirror(this._typeData, this.owner); | |
2632 | |
2633 bool get _hasReturnType { | |
2634 return JS('bool', '# in #', | |
2635 JS_GET_NAME(JsGetName.FUNCTION_TYPE_RETURN_TYPE_TAG), _typeData); | |
2636 } | |
2637 | |
2638 get _returnType { | |
2639 return JS('', '#[#]', _typeData, | |
2640 JS_GET_NAME(JsGetName.FUNCTION_TYPE_RETURN_TYPE_TAG)); | |
2641 } | |
2642 | |
2643 bool get _isVoid { | |
2644 return JS('bool', '!!#[#]', _typeData, | |
2645 JS_GET_NAME(JsGetName.FUNCTION_TYPE_VOID_RETURN_TAG)); | |
2646 } | |
2647 | |
2648 bool get _hasArguments { | |
2649 return JS('bool', '# in #', | |
2650 JS_GET_NAME(JsGetName.FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG), | |
2651 _typeData); | |
2652 } | |
2653 List get _arguments { | |
2654 return JS('JSExtendableArray', '#[#]', | |
2655 _typeData, | |
2656 JS_GET_NAME(JsGetName.FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG)); | |
2657 } | |
2658 | |
2659 bool get _hasOptionalArguments { | |
2660 return JS('bool', '# in #', | |
2661 JS_GET_NAME(JsGetName.FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG), | |
2662 _typeData); | |
2663 } | |
2664 List get _optionalArguments { | |
2665 return JS('JSExtendableArray', '#[#]', | |
2666 _typeData, | |
2667 JS_GET_NAME(JsGetName.FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG)); | |
2668 } | |
2669 | |
2670 bool get _hasNamedArguments { | |
2671 return JS('bool', '# in #', | |
2672 JS_GET_NAME(JsGetName.FUNCTION_TYPE_NAMED_PARAMETERS_TAG), | |
2673 _typeData); | |
2674 } | |
2675 get _namedArguments { | |
2676 return JS('=Object', '#[#]', | |
2677 _typeData, | |
2678 JS_GET_NAME(JsGetName.FUNCTION_TYPE_NAMED_PARAMETERS_TAG)); | |
2679 } | |
2680 | |
2681 bool get isOriginalDeclaration => true; | |
2682 | |
2683 bool get isAbstract => false; | |
2684 | |
2685 bool get isEnum => false; | |
2686 | |
2687 TypeMirror get returnType { | |
2688 if (_cachedReturnType != null) return _cachedReturnType; | |
2689 if (_isVoid) return _cachedReturnType = JsMirrorSystem._voidType; | |
2690 if (!_hasReturnType) return _cachedReturnType = JsMirrorSystem._dynamicType; | |
2691 return _cachedReturnType = | |
2692 typeMirrorFromRuntimeTypeRepresentation(owner, _returnType); | |
2693 } | |
2694 | |
2695 List<ParameterMirror> get parameters { | |
2696 if (_cachedParameters != null) return _cachedParameters; | |
2697 List result = []; | |
2698 int parameterCount = 0; | |
2699 if (_hasArguments) { | |
2700 for (var type in _arguments) { | |
2701 result.add( | |
2702 new JsParameterMirror('argument${parameterCount++}', this, type)); | |
2703 } | |
2704 } | |
2705 if (_hasOptionalArguments) { | |
2706 for (var type in _optionalArguments) { | |
2707 result.add( | |
2708 new JsParameterMirror('argument${parameterCount++}', this, type)); | |
2709 } | |
2710 } | |
2711 if (_hasNamedArguments) { | |
2712 for (var name in extractKeys(_namedArguments)) { | |
2713 var type = JS('', '#[#]', _namedArguments, name); | |
2714 result.add(new JsParameterMirror(name, this, type)); | |
2715 } | |
2716 } | |
2717 return _cachedParameters = new UnmodifiableListView<ParameterMirror>( | |
2718 result); | |
2719 } | |
2720 | |
2721 String _unmangleIfPreserved(String mangled) { | |
2722 String result = unmangleGlobalNameIfPreservedAnyways(mangled); | |
2723 if (result != null) return result; | |
2724 return mangled; | |
2725 } | |
2726 | |
2727 String toString() { | |
2728 if (_cachedToString != null) return _cachedToString; | |
2729 var s = "FunctionTypeMirror on '("; | |
2730 var sep = ''; | |
2731 if (_hasArguments) { | |
2732 for (var argument in _arguments) { | |
2733 s += sep; | |
2734 s += _unmangleIfPreserved(runtimeTypeToString(argument)); | |
2735 sep = ', '; | |
2736 } | |
2737 } | |
2738 if (_hasOptionalArguments) { | |
2739 s += '$sep['; | |
2740 sep = ''; | |
2741 for (var argument in _optionalArguments) { | |
2742 s += sep; | |
2743 s += _unmangleIfPreserved(runtimeTypeToString(argument)); | |
2744 sep = ', '; | |
2745 } | |
2746 s += ']'; | |
2747 } | |
2748 if (_hasNamedArguments) { | |
2749 s += '$sep{'; | |
2750 sep = ''; | |
2751 for (var name in extractKeys(_namedArguments)) { | |
2752 s += sep; | |
2753 s += '$name: '; | |
2754 s += _unmangleIfPreserved( | |
2755 runtimeTypeToString(JS('', '#[#]', _namedArguments, name))); | |
2756 sep = ', '; | |
2757 } | |
2758 s += '}'; | |
2759 } | |
2760 s += ') -> '; | |
2761 if (_isVoid) { | |
2762 s += 'void'; | |
2763 } else if (_hasReturnType) { | |
2764 s += _unmangleIfPreserved(runtimeTypeToString(_returnType)); | |
2765 } else { | |
2766 s += 'dynamic'; | |
2767 } | |
2768 return _cachedToString = "$s'"; | |
2769 } | |
2770 | |
2771 bool isSubclassOf(ClassMirror other) => false; | |
2772 | |
2773 bool isSubtypeOf(TypeMirror other) => throw new UnimplementedError(); | |
2774 | |
2775 bool isAssignableTo(TypeMirror other) => throw new UnimplementedError(); | |
2776 | |
2777 // TODO(ahe): Implement this method. | |
2778 MethodMirror get callMethod => throw new UnimplementedError(); | |
2779 } | |
2780 | |
2781 int findTypeVariableIndex(List<TypeVariableMirror> typeVariables, String name) { | |
2782 for (int i = 0; i < typeVariables.length; i++) { | |
2783 if (typeVariables[i].simpleName == s(name)) { | |
2784 return i; | |
2785 } | |
2786 } | |
2787 throw new ArgumentError('Type variable not present in list.'); | |
2788 } | |
2789 | |
2790 TypeMirror typeMirrorFromRuntimeTypeRepresentation( | |
2791 DeclarationMirror owner, | |
2792 var /*int|List|JsFunction|TypeImpl*/ type) { | |
2793 // TODO(ahe): This method might benefit from using convertRtiToRuntimeType | |
2794 // instead of working on strings. | |
2795 ClassMirror ownerClass; | |
2796 DeclarationMirror context = owner; | |
2797 while (context != null) { | |
2798 if (context is ClassMirror) { | |
2799 ownerClass = context; | |
2800 break; | |
2801 } | |
2802 // TODO(ahe): Get type parameters and arguments from typedefs. | |
2803 if (context is TypedefMirror) break; | |
2804 context = context.owner; | |
2805 } | |
2806 | |
2807 String representation; | |
2808 if (type == null) { | |
2809 return JsMirrorSystem._dynamicType; | |
2810 } else if (type is TypeImpl) { | |
2811 return reflectType(type); | |
2812 } else if (ownerClass == null) { | |
2813 representation = runtimeTypeToString(type); | |
2814 } else if (ownerClass.isOriginalDeclaration) { | |
2815 if (type is num) { | |
2816 // [type] represents a type variable so in the context of an original | |
2817 // declaration the corresponding type variable should be returned. | |
2818 TypeVariable typeVariable = getMetadata(type); | |
2819 List<TypeVariableMirror> typeVariables = ownerClass.typeVariables; | |
2820 int index = findTypeVariableIndex(typeVariables, typeVariable.name); | |
2821 return typeVariables[index]; | |
2822 } else { | |
2823 // Nested type variables will be retrieved lazily (the integer | |
2824 // representation is kept in the string) so they are not processed here. | |
2825 representation = runtimeTypeToString(type); | |
2826 } | |
2827 } else { | |
2828 TypeMirror getTypeArgument(int index) { | |
2829 TypeVariable typeVariable = getMetadata(index); | |
2830 int variableIndex = | |
2831 findTypeVariableIndex(ownerClass.typeVariables, typeVariable.name); | |
2832 return ownerClass.typeArguments[variableIndex]; | |
2833 } | |
2834 | |
2835 if (type is num) { | |
2836 // [type] represents a type variable used as type argument for example | |
2837 // the type argument of Bar: class Foo<T> extends Bar<T> {} | |
2838 TypeMirror typeArgument = getTypeArgument(type); | |
2839 if (typeArgument is JsTypeVariableMirror) | |
2840 return typeArgument; | |
2841 } | |
2842 String substituteTypeVariable(int index) { | |
2843 var typeArgument = getTypeArgument(index); | |
2844 if (typeArgument is JsTypeVariableMirror) { | |
2845 return '${typeArgument._metadataIndex}'; | |
2846 } | |
2847 if (typeArgument is! JsClassMirror && | |
2848 typeArgument is! JsTypeBoundClassMirror) { | |
2849 if (typeArgument == JsMirrorSystem._dynamicType) { | |
2850 return 'dynamic'; | |
2851 } else if (typeArgument == JsMirrorSystem._voidType) { | |
2852 return 'void'; | |
2853 } else { | |
2854 // TODO(ahe): This case shouldn't happen. | |
2855 return 'dynamic'; | |
2856 } | |
2857 } | |
2858 return typeArgument._mangledName; | |
2859 } | |
2860 representation = | |
2861 runtimeTypeToString(type, onTypeVariable: substituteTypeVariable); | |
2862 } | |
2863 if (representation != null) { | |
2864 return reflectClassByMangledName( | |
2865 getMangledTypeName(createRuntimeType(representation))); | |
2866 } | |
2867 String typedefPropertyName = JS_GET_NAME(JsGetName.TYPEDEF_TAG); | |
2868 if (type != null && JS('', '#[#]', type, typedefPropertyName) != null) { | |
2869 return typeMirrorFromRuntimeTypeRepresentation( | |
2870 owner, JS('', '#[#]', type, typedefPropertyName)); | |
2871 } else if (type != null && isDartFunctionType(type)) { | |
2872 return new JsFunctionTypeMirror(type, owner); | |
2873 } | |
2874 return reflectClass(Function); | |
2875 } | |
2876 | |
2877 Symbol computeQualifiedName(DeclarationMirror owner, Symbol simpleName) { | |
2878 if (owner == null) return simpleName; | |
2879 String ownerName = n(owner.qualifiedName); | |
2880 return s('$ownerName.${n(simpleName)}'); | |
2881 } | |
2882 | |
2883 List extractMetadata(victim) { | |
2884 preserveMetadata(); | |
2885 var metadataFunction; | |
2886 if (JS('bool', 'Object.prototype.hasOwnProperty.call(#, "@")', victim)) { | |
2887 metadataFunction = JS('', '#["@"]', victim); | |
2888 } | |
2889 if (metadataFunction != null) return JS('', '#()', metadataFunction); | |
2890 if (JS('bool', 'typeof # != "function"', victim)) return const []; | |
2891 if (JS('bool', '# in #', r'$metadataIndex', victim)) { | |
2892 return JSArray.markFixedList( | |
2893 JS('JSExtendableArray', | |
2894 r'#.$reflectionInfo.splice(#.$metadataIndex)', victim, victim)) | |
2895 .map((int i) => getMetadata(i)).toList(); | |
2896 } | |
2897 return const []; | |
2898 } | |
2899 | |
2900 void parseCompactFieldSpecification( | |
2901 JsDeclarationMirror owner, | |
2902 fieldSpecification, | |
2903 bool isStatic, | |
2904 List<Mirror> result) { | |
2905 List fieldsMetadata = null; | |
2906 List<String> fields; | |
2907 if (fieldSpecification is List) { | |
2908 fields = splitFields(fieldSpecification[0], ','); | |
2909 fieldsMetadata = fieldSpecification.sublist(1); | |
2910 } else if (fieldSpecification is String) { | |
2911 fields = splitFields(fieldSpecification, ','); | |
2912 } else { | |
2913 fields = []; | |
2914 } | |
2915 int fieldNumber = 0; | |
2916 for (String field in fields) { | |
2917 var metadata; | |
2918 if (fieldsMetadata != null) { | |
2919 metadata = fieldsMetadata[fieldNumber++]; | |
2920 } | |
2921 var mirror = new JsVariableMirror.from(field, metadata, owner, isStatic); | |
2922 if (mirror != null) { | |
2923 result.add(mirror); | |
2924 } | |
2925 } | |
2926 } | |
2927 | |
2928 /// Similar to [String.split], but returns an empty list if [string] is empty. | |
2929 List<String> splitFields(String string, Pattern pattern) { | |
2930 if (string.isEmpty) return <String>[]; | |
2931 return string.split(pattern); | |
2932 } | |
2933 | |
2934 bool isOperatorName(String name) { | |
2935 switch (name) { | |
2936 case '==': | |
2937 case '[]': | |
2938 case '*': | |
2939 case '/': | |
2940 case '%': | |
2941 case '~/': | |
2942 case '+': | |
2943 case '<<': | |
2944 case '>>': | |
2945 case '>=': | |
2946 case '>': | |
2947 case '<=': | |
2948 case '<': | |
2949 case '&': | |
2950 case '^': | |
2951 case '|': | |
2952 case '-': | |
2953 case 'unary-': | |
2954 case '[]=': | |
2955 case '~': | |
2956 return true; | |
2957 default: | |
2958 return false; | |
2959 } | |
2960 } | |
2961 | |
2962 /// Returns true if the key represent ancillary reflection data, that is, not a | |
2963 /// method. | |
2964 bool isReflectiveDataInPrototype(String key) { | |
2965 if (key == JS_GET_NAME(JsGetName.CLASS_DESCRIPTOR_PROPERTY) || | |
2966 key == METHODS_WITH_OPTIONAL_ARGUMENTS) { | |
2967 return true; | |
2968 } | |
2969 String firstChar = key[0]; | |
2970 return firstChar == '*' || firstChar == '+'; | |
2971 } | |
2972 | |
2973 /// Returns `true` if [jsFunction] is an ordinary reflectable method and | |
2974 /// not a (potentially reflectable) stub or otherwise non-reflectable method. | |
2975 bool isOrdinaryReflectableMethod(var jsFunction) { | |
2976 return JS('bool', r'#.$reflectable === 1', jsFunction); | |
2977 } | |
2978 | |
2979 /// Returns true if [key] is only an aliased entry for [function] in the | |
2980 /// prototype. | |
2981 bool isAliasedSuperMethod(var jsFunction, String key) { | |
2982 var stubName = JS('String|Null', r'#.$stubName', jsFunction); | |
2983 return stubName != null && key != stubName; | |
2984 } | |
2985 | |
2986 class NoSuchStaticMethodError extends Error implements NoSuchMethodError { | |
2987 static const int MISSING_CONSTRUCTOR = 0; | |
2988 static const int MISSING_METHOD = 1; | |
2989 final ClassMirror _cls; | |
2990 final Symbol _name; | |
2991 final List _positionalArguments; | |
2992 final Map<Symbol, dynamic> _namedArguments; | |
2993 final int _kind; | |
2994 | |
2995 NoSuchStaticMethodError.missingConstructor( | |
2996 this._cls, | |
2997 this._name, | |
2998 this._positionalArguments, | |
2999 this._namedArguments) | |
3000 : _kind = MISSING_CONSTRUCTOR; | |
3001 | |
3002 /// If the given class is `null` the static method/getter/setter is top-level. | |
3003 NoSuchStaticMethodError.method( | |
3004 this._cls, | |
3005 this._name, | |
3006 this._positionalArguments, | |
3007 this._namedArguments) | |
3008 : _kind = MISSING_METHOD; | |
3009 | |
3010 String toString() { | |
3011 // TODO(floitsch): show arguments. | |
3012 switch(_kind) { | |
3013 case MISSING_CONSTRUCTOR: | |
3014 return | |
3015 "NoSuchMethodError: No constructor named '${n(_name)}' in class" | |
3016 " '${n(_cls.qualifiedName)}'."; | |
3017 case MISSING_METHOD: | |
3018 if (_cls == null) { | |
3019 return "NoSuchMethodError: No top-level method named '${n(_name)}'."; | |
3020 } | |
3021 return "NoSuchMethodError: No static method named '${n(_name)}' in" | |
3022 " class '${n(_cls.qualifiedName)}'"; | |
3023 default: | |
3024 return 'NoSuchMethodError'; | |
3025 } | |
3026 } | |
3027 } | |
3028 | |
3029 Symbol getSymbol(String name, LibraryMirror library) { | |
3030 if (_isPublicSymbol(name)) { | |
3031 return new _symbol_dev.Symbol.validated(name); | |
3032 } | |
3033 if (library == null) { | |
3034 throw new ArgumentError( | |
3035 "Library required for private symbol name: $name"); | |
3036 } | |
3037 if (!_symbol_dev.Symbol.isValidSymbol(name)) { | |
3038 throw new ArgumentError("Not a valid symbol name: $name"); | |
3039 } | |
3040 throw new UnimplementedError( | |
3041 "MirrorSystem.getSymbol not implemented for private names"); | |
3042 } | |
3043 | |
3044 bool _isPublicSymbol(String name) { | |
3045 // A symbol is public if it doesn't start with '_' and it doesn't | |
3046 // have a part (following a '.') that starts with '_'. | |
3047 const int UNDERSCORE = 0x5f; | |
3048 if (name.isEmpty) return true; | |
3049 int index = -1; | |
3050 do { | |
3051 if (name.codeUnitAt(index + 1) == UNDERSCORE) return false; | |
3052 index = name.indexOf('.', index + 1); | |
3053 } while (index >= 0 && index + 1 < name.length); | |
3054 return true; | |
3055 } | |
OLD | NEW |