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