| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library dart._js_mirrors; |
| |
| import 'dart:async'; |
| |
| import 'dart:collection' show |
| UnmodifiableListView, |
| UnmodifiableMapView; |
| |
| import 'dart:mirrors'; |
| |
| import 'dart:_foreign_helper' show |
| JS, |
| JS_CURRENT_ISOLATE, |
| JS_CURRENT_ISOLATE_CONTEXT, |
| JS_GET_NAME; |
| |
| import 'dart:_internal' as _symbol_dev; |
| |
| import 'dart:_js_helper' show |
| BoundClosure, |
| Closure, |
| JSInvocationMirror, |
| JsCache, |
| Null, |
| Primitives, |
| ReflectionInfo, |
| RuntimeError, |
| TypeVariable, |
| UnimplementedNoSuchMethodError, |
| createRuntimeType, |
| createUnmangledInvocationMirror, |
| getMangledTypeName, |
| getMetadata, |
| getRuntimeType, |
| hasReflectableProperty, |
| runtimeTypeToString, |
| setRuntimeTypeInfo, |
| throwInvalidReflectionError; |
| |
| import 'dart:_interceptors' show |
| Interceptor, |
| JSArray, |
| JSExtendableArray, |
| getInterceptor; |
| |
| import 'dart:_js_names'; |
| |
| const String METHODS_WITH_OPTIONAL_ARGUMENTS = r'$methodsWithOptionalArguments'; |
| |
| /// No-op method that is called to inform the compiler that tree-shaking needs |
| /// to be disabled. |
| disableTreeShaking() => preserveNames(); |
| |
| /// No-op method that is called to inform the compiler that metadata must be |
| /// preserved at runtime. |
| preserveMetadata() {} |
| |
| String getName(Symbol symbol) { |
| preserveNames(); |
| return n(symbol); |
| } |
| |
| class JsMirrorSystem implements MirrorSystem { |
| UnmodifiableMapView<Uri, LibraryMirror> _cachedLibraries; |
| |
| final IsolateMirror isolate = new JsIsolateMirror(); |
| |
| JsTypeMirror get dynamicType => _dynamicType; |
| JsTypeMirror get voidType => _voidType; |
| |
| static final JsTypeMirror _dynamicType = |
| new JsTypeMirror(const Symbol('dynamic')); |
| static final JsTypeMirror _voidType = new JsTypeMirror(const Symbol('void')); |
| |
| static final Map<String, List<LibraryMirror>> librariesByName = |
| computeLibrariesByName(); |
| |
| Map<Uri, LibraryMirror> get libraries { |
| if (_cachedLibraries != null) return _cachedLibraries; |
| Map<Uri, LibraryMirror> result = new Map(); |
| for (List<LibraryMirror> list in librariesByName.values) { |
| for (LibraryMirror library in list) { |
| result[library.uri] = library; |
| } |
| } |
| return _cachedLibraries = |
| new UnmodifiableMapView<Uri, LibraryMirror>(result); |
| } |
| |
| LibraryMirror findLibrary(Symbol libraryName) { |
| return librariesByName[n(libraryName)].single; |
| } |
| |
| static Map<String, List<LibraryMirror>> computeLibrariesByName() { |
| disableTreeShaking(); |
| var result = new Map<String, List<LibraryMirror>>(); |
| var jsLibraries = JS('JSExtendableArray|Null', 'init.libraries'); |
| if (jsLibraries == null) return result; |
| for (List data in jsLibraries) { |
| String name = data[0]; |
| Uri uri = Uri.parse(data[1]); |
| List<String> classes = data[2]; |
| List<String> functions = data[3]; |
| var metadataFunction = data[4]; |
| var fields = data[5]; |
| bool isRoot = data[6]; |
| var globalObject = data[7]; |
| List metadata = (metadataFunction == null) |
| ? const [] : JS('List', '#()', metadataFunction); |
| var libraries = result.putIfAbsent(name, () => <LibraryMirror>[]); |
| libraries.add( |
| new JsLibraryMirror( |
| s(name), uri, classes, functions, metadata, fields, isRoot, |
| globalObject)); |
| } |
| return result; |
| } |
| } |
| |
| abstract class JsMirror implements Mirror { |
| const JsMirror(); |
| |
| String get _prettyName; |
| |
| String toString() => _prettyName; |
| |
| // TODO(ahe): Remove this method from the API. |
| MirrorSystem get mirrors => currentJsMirrorSystem; |
| |
| _getField(JsMirror receiver) { |
| throw new UnimplementedError(); |
| } |
| |
| void _setField(JsMirror receiver, Object arg) { |
| throw new UnimplementedError(); |
| } |
| |
| _loadField(String name) { |
| throw new UnimplementedError(); |
| } |
| |
| void _storeField(String name, Object arg) { |
| throw new UnimplementedError(); |
| } |
| } |
| |
| // This class is somewhat silly in the current implementation. |
| class JsIsolateMirror extends JsMirror implements IsolateMirror { |
| final _isolateContext = JS_CURRENT_ISOLATE_CONTEXT(); |
| |
| String get _prettyName => 'Isolate'; |
| |
| String get debugName { |
| String id = _isolateContext == null ? 'X' : _isolateContext.id.toString(); |
| // Using name similar to what the VM uses. |
| return '${n(rootLibrary.simpleName)}-$id'; |
| } |
| |
| bool get isCurrent => JS_CURRENT_ISOLATE_CONTEXT() == _isolateContext; |
| |
| LibraryMirror get rootLibrary { |
| return currentJsMirrorSystem.libraries.values.firstWhere( |
| (JsLibraryMirror library) => library._isRoot); |
| } |
| } |
| |
| abstract class JsDeclarationMirror extends JsMirror |
| implements DeclarationMirror { |
| final Symbol simpleName; |
| |
| const JsDeclarationMirror(this.simpleName); |
| |
| Symbol get qualifiedName => computeQualifiedName(owner, simpleName); |
| |
| bool get isPrivate => n(simpleName).startsWith('_'); |
| |
| bool get isTopLevel => owner != null && owner is LibraryMirror; |
| |
| // TODO(ahe): This should use qualifiedName. |
| String toString() => "$_prettyName on '${n(simpleName)}'"; |
| |
| List<JsMethodMirror> get _methods { |
| throw new RuntimeError('Should not call _methods'); |
| } |
| |
| _invoke(List positionalArguments, Map<Symbol, dynamic> namedArguments) { |
| throw new RuntimeError('Should not call _invoke'); |
| } |
| |
| // TODO(ahe): Implement this. |
| SourceLocation get location => throw new UnimplementedError(); |
| } |
| |
| class JsTypeVariableMirror extends JsTypeMirror implements TypeVariableMirror { |
| final DeclarationMirror owner; |
| final TypeVariable _typeVariable; |
| final int _metadataIndex; |
| TypeMirror _cachedUpperBound; |
| |
| JsTypeVariableMirror(TypeVariable typeVariable, this.owner, |
| this._metadataIndex) |
| : this._typeVariable = typeVariable, |
| super(s(typeVariable.name)); |
| |
| bool operator ==(other) { |
| return (other is JsTypeVariableMirror && |
| simpleName == other.simpleName && |
| owner == other.owner); |
| } |
| |
| int get hashCode { |
| int code = 0x3FFFFFFF & (JsTypeVariableMirror).hashCode; |
| code ^= 17 * simpleName.hashCode; |
| code ^= 19 * owner.hashCode; |
| return code; |
| } |
| |
| String get _prettyName => 'TypeVariableMirror'; |
| |
| bool get isTopLevel => false; |
| bool get isStatic => false; |
| |
| TypeMirror get upperBound { |
| if (_cachedUpperBound != null) return _cachedUpperBound; |
| return _cachedUpperBound = typeMirrorFromRuntimeTypeRepresentation( |
| owner, getMetadata(_typeVariable.bound)); |
| } |
| |
| bool isSubtypeOf(TypeMirror other) => throw new UnimplementedError(); |
| bool isAssignableTo(TypeMirror other) => throw new UnimplementedError(); |
| |
| _asRuntimeType() => _metadataIndex; |
| } |
| |
| class JsTypeMirror extends JsDeclarationMirror implements TypeMirror { |
| JsTypeMirror(Symbol simpleName) |
| : super(simpleName); |
| |
| String get _prettyName => 'TypeMirror'; |
| |
| DeclarationMirror get owner => null; |
| |
| // TODO(ahe): Doesn't match the specification, see http://dartbug.com/11569. |
| bool get isTopLevel => true; |
| |
| // TODO(ahe): Implement these. |
| List<InstanceMirror> get metadata => throw new UnimplementedError(); |
| |
| bool get hasReflectedType => false; |
| Type get reflectedType { |
| throw new UnsupportedError("This type does not support reflectedType"); |
| } |
| |
| List<TypeVariableMirror> get typeVariables => const <TypeVariableMirror>[]; |
| List<TypeMirror> get typeArguments => const <TypeMirror>[]; |
| |
| bool get isOriginalDeclaration => true; |
| TypeMirror get originalDeclaration => this; |
| |
| bool isSubtypeOf(TypeMirror other) => throw new UnimplementedError(); |
| bool isAssignableTo(TypeMirror other) => throw new UnimplementedError(); |
| |
| _asRuntimeType() { |
| if (this == JsMirrorSystem._dynamicType) return null; |
| if (this == JsMirrorSystem._voidType) return null; |
| throw new RuntimeError('Should not call _asRuntimeType'); |
| } |
| } |
| |
| class JsLibraryMirror extends JsDeclarationMirror with JsObjectMirror |
| implements LibraryMirror { |
| final Uri uri; |
| final List<String> _classes; |
| final List<String> _functions; |
| final List _metadata; |
| final String _compactFieldSpecification; |
| final bool _isRoot; |
| final _globalObject; |
| List<JsMethodMirror> _cachedFunctionMirrors; |
| List<VariableMirror> _cachedFields; |
| UnmodifiableMapView<Symbol, ClassMirror> _cachedClasses; |
| UnmodifiableMapView<Symbol, MethodMirror> _cachedFunctions; |
| UnmodifiableMapView<Symbol, MethodMirror> _cachedGetters; |
| UnmodifiableMapView<Symbol, MethodMirror> _cachedSetters; |
| UnmodifiableMapView<Symbol, VariableMirror> _cachedVariables; |
| UnmodifiableMapView<Symbol, Mirror> _cachedMembers; |
| UnmodifiableMapView<Symbol, DeclarationMirror> _cachedDeclarations; |
| UnmodifiableListView<InstanceMirror> _cachedMetadata; |
| |
| JsLibraryMirror(Symbol simpleName, |
| this.uri, |
| this._classes, |
| this._functions, |
| this._metadata, |
| this._compactFieldSpecification, |
| this._isRoot, |
| this._globalObject) |
| : super(simpleName); |
| |
| String get _prettyName => 'LibraryMirror'; |
| |
| Symbol get qualifiedName => simpleName; |
| |
| List<JsMethodMirror> get _methods => _functionMirrors; |
| |
| Map<Symbol, ClassMirror> get __classes { |
| if (_cachedClasses != null) return _cachedClasses; |
| var result = new Map(); |
| for (String className in _classes) { |
| var cls = reflectClassByMangledName(className); |
| if (cls is ClassMirror) { |
| cls = cls.originalDeclaration; |
| if (cls is JsClassMirror) { |
| result[cls.simpleName] = cls; |
| cls._owner = this; |
| } |
| } |
| } |
| return _cachedClasses = |
| new UnmodifiableMapView<Symbol, ClassMirror>(result); |
| } |
| |
| InstanceMirror setField(Symbol fieldName, Object arg) { |
| String name = n(fieldName); |
| if (name.endsWith('=')) throw new ArgumentError(''); |
| var mirror = __functions[s('$name=')]; |
| if (mirror == null) mirror = __variables[fieldName]; |
| if (mirror == null) { |
| // TODO(ahe): What receiver to use? |
| throw new NoSuchMethodError(this, setterSymbol(fieldName), [arg], null); |
| } |
| mirror._setField(this, arg); |
| return reflect(arg); |
| } |
| |
| InstanceMirror getField(Symbol fieldName) { |
| JsMirror mirror = __members[fieldName]; |
| if (mirror == null) { |
| // TODO(ahe): What receiver to use? |
| throw new NoSuchMethodError(this, fieldName, [], null); |
| } |
| if (mirror is! MethodMirror) return reflect(mirror._getField(this)); |
| JsMethodMirror methodMirror = mirror; |
| if (methodMirror.isGetter) return reflect(mirror._getField(this)); |
| assert(methodMirror.isRegularMethod); |
| var getter = JS("", "#['\$getter']", methodMirror._jsFunction); |
| if (getter == null) throw new UnimplementedError(); |
| return reflect(JS("", "#()", getter)); |
| } |
| |
| InstanceMirror invoke(Symbol memberName, |
| List positionalArguments, |
| [Map<Symbol, dynamic> namedArguments]) { |
| if (namedArguments != null && !namedArguments.isEmpty) { |
| throw new UnsupportedError('Named arguments are not implemented.'); |
| } |
| JsDeclarationMirror mirror = __members[memberName]; |
| |
| if (mirror is JsMethodMirror && !mirror.canInvokeReflectively()) { |
| throwInvalidReflectionError(n(memberName)); |
| } |
| if (mirror == null || mirror is JsMethodMirror && mirror.isSetter) { |
| // TODO(ahe): What receiver to use? |
| throw new NoSuchMethodError( |
| this, memberName, positionalArguments, namedArguments); |
| } |
| if (mirror is JsMethodMirror && !mirror.isGetter) { |
| return reflect(mirror._invoke(positionalArguments, namedArguments)); |
| } |
| return getField(memberName) |
| .invoke(#call, positionalArguments, namedArguments); |
| } |
| |
| _loadField(String name) { |
| // TODO(ahe): What about lazily initialized fields? See |
| // [JsClassMirror.getField]. |
| |
| // '$' (JS_CURRENT_ISOLATE()) stores state which is read directly, so we |
| // shouldn't use [_globalObject] here. |
| assert(JS('bool', '# in #', name, JS_CURRENT_ISOLATE())); |
| return JS('', '#[#]', JS_CURRENT_ISOLATE(), name); |
| } |
| |
| void _storeField(String name, Object arg) { |
| // '$' (JS_CURRENT_ISOLATE()) stores state which is stored directly, so we |
| // shouldn't use [_globalObject] here. |
| assert(JS('bool', '# in #', name, JS_CURRENT_ISOLATE())); |
| JS('void', '#[#] = #', JS_CURRENT_ISOLATE(), name, arg); |
| } |
| |
| List<JsMethodMirror> get _functionMirrors { |
| if (_cachedFunctionMirrors != null) return _cachedFunctionMirrors; |
| var result = new List<JsMethodMirror>(); |
| for (int i = 0; i < _functions.length; i++) { |
| String name = _functions[i]; |
| var jsFunction = JS('', '#[#]', _globalObject, name); |
| String unmangledName = mangledGlobalNames[name]; |
| if (unmangledName == null || |
| JS('bool', "!!#['\$getterStub']", jsFunction)) { |
| // If there is no unmangledName, [jsFunction] is either a synthetic |
| // implementation detail, or something that is excluded |
| // by @MirrorsUsed. |
| // If it has a getterStub property it is a synthetic stub. |
| // TODO(floitsch): Remove the getterStub hack. |
| continue; |
| } |
| bool isConstructor = unmangledName.startsWith('new '); |
| bool isStatic = !isConstructor; // Top-level functions are static, but |
| // constructors are not. |
| if (isConstructor) { |
| unmangledName = unmangledName.substring(4).replaceAll(r'$', '.'); |
| } |
| JsMethodMirror mirror = |
| new JsMethodMirror.fromUnmangledName( |
| unmangledName, jsFunction, isStatic, isConstructor); |
| result.add(mirror); |
| mirror._owner = this; |
| } |
| return _cachedFunctionMirrors = result; |
| } |
| |
| List<VariableMirror> get _fields { |
| if (_cachedFields != null) return _cachedFields; |
| var result = <VariableMirror>[]; |
| parseCompactFieldSpecification( |
| this, _compactFieldSpecification, true, result); |
| return _cachedFields = result; |
| } |
| |
| Map<Symbol, MethodMirror> get __functions { |
| if (_cachedFunctions != null) return _cachedFunctions; |
| var result = new Map(); |
| for (JsMethodMirror mirror in _functionMirrors) { |
| if (!mirror.isConstructor) result[mirror.simpleName] = mirror; |
| } |
| return _cachedFunctions = |
| new UnmodifiableMapView<Symbol, MethodMirror>(result); |
| } |
| |
| Map<Symbol, MethodMirror> get __getters { |
| if (_cachedGetters != null) return _cachedGetters; |
| var result = new Map(); |
| // TODO(ahe): Implement this. |
| return _cachedGetters = |
| new UnmodifiableMapView<Symbol, MethodMirror>(result); |
| } |
| |
| Map<Symbol, MethodMirror> get __setters { |
| if (_cachedSetters != null) return _cachedSetters; |
| var result = new Map(); |
| // TODO(ahe): Implement this. |
| return _cachedSetters = |
| new UnmodifiableMapView<Symbol, MethodMirror>(result); |
| } |
| |
| Map<Symbol, VariableMirror> get __variables { |
| if (_cachedVariables != null) return _cachedVariables; |
| var result = new Map(); |
| for (JsVariableMirror mirror in _fields) { |
| result[mirror.simpleName] = mirror; |
| } |
| return _cachedVariables = |
| new UnmodifiableMapView<Symbol, VariableMirror>(result); |
| } |
| |
| Map<Symbol, Mirror> get __members { |
| if (_cachedMembers != null) return _cachedMembers; |
| Map<Symbol, Mirror> result = new Map.from(__classes); |
| addToResult(Symbol key, Mirror value) { |
| result[key] = value; |
| } |
| __functions.forEach(addToResult); |
| __getters.forEach(addToResult); |
| __setters.forEach(addToResult); |
| __variables.forEach(addToResult); |
| return _cachedMembers = new UnmodifiableMapView<Symbol, Mirror>(result); |
| } |
| |
| Map<Symbol, DeclarationMirror> get declarations { |
| if (_cachedDeclarations != null) return _cachedDeclarations; |
| var result = new Map<Symbol, DeclarationMirror>(); |
| addToResult(Symbol key, Mirror value) { |
| result[key] = value; |
| } |
| __members.forEach(addToResult); |
| return _cachedDeclarations = |
| new UnmodifiableMapView<Symbol, DeclarationMirror>(result); |
| } |
| |
| List<InstanceMirror> get metadata { |
| if (_cachedMetadata != null) return _cachedMetadata; |
| preserveMetadata(); |
| return _cachedMetadata = |
| new UnmodifiableListView<InstanceMirror>(_metadata.map(reflect)); |
| } |
| |
| // TODO(ahe): Test this getter. |
| DeclarationMirror get owner => null; |
| |
| List<LibraryDependencyMirror> get libraryDependencies |
| => throw new UnimplementedError(); |
| } |
| |
| String n(Symbol symbol) => _symbol_dev.Symbol.getName(symbol); |
| |
| Symbol s(String name) { |
| if (name == null) return null; |
| return new _symbol_dev.Symbol.unvalidated(name); |
| } |
| |
| Symbol setterSymbol(Symbol symbol) => s("${n(symbol)}="); |
| |
| final JsMirrorSystem currentJsMirrorSystem = new JsMirrorSystem(); |
| |
| InstanceMirror reflect(Object reflectee) { |
| if (reflectee is Closure) { |
| return new JsClosureMirror(reflectee); |
| } else { |
| return new JsInstanceMirror(reflectee); |
| } |
| } |
| |
| TypeMirror reflectType(Type key) { |
| return reflectClassByMangledName(getMangledTypeName(key)); |
| } |
| |
| TypeMirror reflectClassByMangledName(String mangledName) { |
| String unmangledName = mangledGlobalNames[mangledName]; |
| if (mangledName == 'dynamic') return JsMirrorSystem._dynamicType; |
| if (mangledName == 'void') return JsMirrorSystem._voidType; |
| if (unmangledName == null) unmangledName = mangledName; |
| return reflectClassByName(s(unmangledName), mangledName); |
| } |
| |
| var classMirrors; |
| |
| TypeMirror reflectClassByName(Symbol symbol, String mangledName) { |
| if (classMirrors == null) classMirrors = JsCache.allocate(); |
| var mirror = JsCache.fetch(classMirrors, mangledName); |
| if (mirror != null) return mirror; |
| disableTreeShaking(); |
| int typeArgIndex = mangledName.indexOf("<"); |
| if (typeArgIndex != -1) { |
| mirror = new JsTypeBoundClassMirror(reflectClassByMangledName( |
| mangledName.substring(0, typeArgIndex)).originalDeclaration, |
| // Remove the angle brackets enclosing the type arguments. |
| mangledName.substring(typeArgIndex + 1, mangledName.length - 1)); |
| JsCache.update(classMirrors, mangledName, mirror); |
| return mirror; |
| } |
| var constructor = JS('var', 'init.allClasses[#]', mangledName); |
| if (constructor == null) { |
| int index = JS('int|Null', 'init.functionAliases[#]', mangledName); |
| if (index != null) { |
| mirror = new JsTypedefMirror(symbol, mangledName, getMetadata(index)); |
| JsCache.update(classMirrors, mangledName, mirror); |
| return mirror; |
| } |
| // Probably an intercepted class. |
| // TODO(ahe): How to handle intercepted classes? |
| throw new UnsupportedError('Cannot find class for: ${n(symbol)}'); |
| } |
| var descriptor = JS('', '#["@"]', constructor); |
| var fields; |
| var fieldsMetadata; |
| if (descriptor == null) { |
| // This is a native class, or an intercepted class. |
| // TODO(ahe): Preserve descriptor for such classes. |
| } else { |
| fields = JS('', '#[#]', descriptor, |
| JS_GET_NAME('CLASS_DESCRIPTOR_PROPERTY')); |
| if (fields is List) { |
| fieldsMetadata = fields.getRange(1, fields.length).toList(); |
| fields = fields[0]; |
| } |
| if (fields is! String) { |
| // TODO(ahe): This is CSP mode. Find a way to determine the |
| // fields of this class. |
| fields = ''; |
| } |
| } |
| |
| var superclassName = fields.split(';')[0]; |
| var mixins = superclassName.split('+'); |
| if (mixins.length > 1 && mangledGlobalNames[mangledName] == null) { |
| mirror = reflectMixinApplication(mixins, mangledName); |
| } else { |
| ClassMirror classMirror = new JsClassMirror( |
| symbol, mangledName, constructor, fields, fieldsMetadata); |
| List typeVariables = |
| JS('JSExtendableArray|Null', '#.prototype["<>"]', constructor); |
| if (typeVariables == null || typeVariables.length == 0) { |
| mirror = classMirror; |
| } else { |
| String typeArguments = 'dynamic'; |
| for (int i = 1; i < typeVariables.length; i++) { |
| typeArguments += ',dynamic'; |
| } |
| mirror = new JsTypeBoundClassMirror(classMirror, typeArguments); |
| } |
| } |
| |
| JsCache.update(classMirrors, mangledName, mirror); |
| return mirror; |
| } |
| |
| Map<Symbol, MethodMirror> filterMethods(List<MethodMirror> methods) { |
| var result = new Map(); |
| for (JsMethodMirror method in methods) { |
| if (!method.isConstructor && !method.isGetter && !method.isSetter) { |
| result[method.simpleName] = method; |
| } |
| } |
| return result; |
| } |
| |
| Map<Symbol, MethodMirror> filterConstructors(methods) { |
| var result = new Map(); |
| for (JsMethodMirror method in methods) { |
| if (method.isConstructor) { |
| result[method.simpleName] = method; |
| } |
| } |
| return result; |
| } |
| |
| Map<Symbol, MethodMirror> filterGetters(List<MethodMirror> methods, |
| Map<Symbol, VariableMirror> fields) { |
| var result = new Map(); |
| for (JsMethodMirror method in methods) { |
| if (method.isGetter) { |
| |
| // TODO(ahe): This is a hack to remove getters corresponding to a field. |
| if (fields[method.simpleName] != null) continue; |
| |
| result[method.simpleName] = method; |
| } |
| } |
| return result; |
| } |
| |
| Map<Symbol, MethodMirror> filterSetters(List<MethodMirror> methods, |
| Map<Symbol, VariableMirror> fields) { |
| var result = new Map(); |
| for (JsMethodMirror method in methods) { |
| if (method.isSetter) { |
| |
| // TODO(ahe): This is a hack to remove setters corresponding to a field. |
| String name = n(method.simpleName); |
| name = name.substring(0, name.length - 1); // Remove '='. |
| if (fields[s(name)] != null) continue; |
| |
| result[method.simpleName] = method; |
| } |
| } |
| return result; |
| } |
| |
| Map<Symbol, Mirror> filterMembers(List<MethodMirror> methods, |
| Map<Symbol, VariableMirror> variables) { |
| Map<Symbol, Mirror> result = new Map.from(variables); |
| for (JsMethodMirror method in methods) { |
| if (method.isSetter) { |
| String name = n(method.simpleName); |
| name = name.substring(0, name.length - 1); |
| // Filter-out setters corresponding to variables. |
| if (result[s(name)] is VariableMirror) continue; |
| } |
| // Constructors aren't 'members'. |
| if (method.isConstructor) continue; |
| // Use putIfAbsent to filter-out getters corresponding to variables. |
| result.putIfAbsent(method.simpleName, () => method); |
| } |
| return result; |
| } |
| |
| int counter = 0; |
| |
| ClassMirror reflectMixinApplication(mixinNames, String mangledName) { |
| disableTreeShaking(); |
| var mixins = []; |
| for (String mangledName in mixinNames) { |
| mixins.add(reflectClassByMangledName(mangledName)); |
| } |
| var it = mixins.iterator; |
| it.moveNext(); |
| var superclass = it.current; |
| while (it.moveNext()) { |
| superclass = new JsMixinApplication(superclass, it.current, mangledName); |
| } |
| return superclass; |
| } |
| |
| class JsMixinApplication extends JsTypeMirror with JsObjectMirror |
| implements ClassMirror { |
| final ClassMirror superclass; |
| final ClassMirror mixin; |
| Symbol _cachedSimpleName; |
| Map<Symbol, MethodMirror> _cachedInstanceMembers; |
| |
| JsMixinApplication(ClassMirror superclass, ClassMirror mixin, |
| String mangledName) |
| : this.superclass = superclass, |
| this.mixin = mixin, |
| super(s(mangledName)); |
| |
| String get _prettyName => 'ClassMirror'; |
| |
| Symbol get simpleName { |
| if (_cachedSimpleName != null) return _cachedSimpleName; |
| String superName = n(superclass.qualifiedName); |
| return _cachedSimpleName = (superName.contains(' with ')) |
| ? s('$superName, ${n(mixin.qualifiedName)}') |
| : s('$superName with ${n(mixin.qualifiedName)}'); |
| } |
| |
| Symbol get qualifiedName => simpleName; |
| |
| // TODO(ahe): Remove this method, only here to silence warning. |
| get _mixin => mixin; |
| |
| Map<Symbol, Mirror> get __members => _mixin.__members; |
| |
| Map<Symbol, MethodMirror> get __methods => _mixin.__methods; |
| |
| Map<Symbol, MethodMirror> get __getters => _mixin.__getters; |
| |
| Map<Symbol, MethodMirror> get __setters => _mixin.__setters; |
| |
| Map<Symbol, VariableMirror> get __variables => _mixin.__variables; |
| |
| Map<Symbol, DeclarationMirror> get declarations => mixin.declarations; |
| |
| Map<Symbol, MethodMirror> get instanceMembers { |
| if (_cachedInstanceMembers == null) { |
| var result = new Map<Symbol, MethodMirror>(); |
| if (superclass != null) { |
| result.addAll(superclass.instanceMembers); |
| } |
| result.addAll(mixin.instanceMembers); |
| _cachedInstanceMembers = result; |
| } |
| return _cachedInstanceMembers; |
| } |
| |
| Map<Symbol, MethodMirror> get staticMembers => mixin.staticMembers; |
| |
| _asRuntimeType() => null; |
| |
| InstanceMirror invoke( |
| Symbol memberName, |
| List positionalArguments, |
| [Map<Symbol,dynamic> namedArguments]) { |
| // TODO(ahe): What receiver to use? |
| throw new NoSuchMethodError(this, memberName, |
| positionalArguments, namedArguments); |
| } |
| |
| InstanceMirror getField(Symbol fieldName) { |
| // TODO(ahe): What receiver to use? |
| throw new NoSuchMethodError(this, fieldName, null, null); |
| } |
| |
| InstanceMirror setField(Symbol fieldName, Object arg) { |
| // TODO(ahe): What receiver to use? |
| throw new NoSuchMethodError(this, setterSymbol(fieldName), [arg], null); |
| } |
| |
| List<ClassMirror> get superinterfaces => [mixin]; |
| |
| Map<Symbol, MethodMirror> get __constructors => _mixin.__constructors; |
| |
| InstanceMirror newInstance( |
| Symbol constructorName, |
| List positionalArguments, |
| [Map<Symbol,dynamic> namedArguments]) { |
| throw new UnsupportedError( |
| "Can't instantiate mixin application '${n(qualifiedName)}'"); |
| } |
| |
| bool get isOriginalDeclaration => true; |
| |
| ClassMirror get originalDeclaration => this; |
| |
| // TODO(ahe): Implement this. |
| List<TypeVariableMirror> get typeVariables { |
| throw new UnimplementedError(); |
| } |
| |
| List<TypeMirror> get typeArguments => const <TypeMirror>[]; |
| |
| bool get isAbstract => throw new UnimplementedError(); |
| |
| bool isSubclassOf(ClassMirror other) { |
| superclass.isSubclassOf(other) || mixin.isSubclassOf(other); |
| } |
| |
| bool isSubtypeOf(TypeMirror other) => throw new UnimplementedError(); |
| |
| bool isAssignableTo(TypeMirror other) => throw new UnimplementedError(); |
| } |
| |
| abstract class JsObjectMirror implements ObjectMirror { |
| } |
| |
| class JsInstanceMirror extends JsObjectMirror implements InstanceMirror { |
| final reflectee; |
| |
| JsInstanceMirror(this.reflectee); |
| |
| bool get hasReflectee => true; |
| |
| ClassMirror get type { |
| // The spec guarantees that `null` is the singleton instance of the `Null` |
| // class. |
| if (reflectee == null) return reflectClass(Null); |
| return reflectType(getRuntimeType(reflectee)); |
| } |
| |
| InstanceMirror invoke(Symbol memberName, |
| List positionalArguments, |
| [Map<Symbol,dynamic> namedArguments]) { |
| if (namedArguments == null) namedArguments = const {}; |
| // We can safely pass positionalArguments to _invoke as it will wrap it in |
| // a JSArray if needed. |
| return _invoke(memberName, JSInvocationMirror.METHOD, |
| positionalArguments, namedArguments); |
| } |
| |
| InstanceMirror _invokeMethodWithNamedArguments( |
| String reflectiveName, |
| List positionalArguments, Map<Symbol,dynamic> namedArguments) { |
| assert(namedArguments.isNotEmpty); |
| var interceptor = getInterceptor(reflectee); |
| |
| var jsFunction = JS('', '#[#]', interceptor, reflectiveName); |
| if (jsFunction == null) { |
| // TODO(ahe): Invoke noSuchMethod. |
| throw new UnimplementedNoSuchMethodError( |
| 'Invoking noSuchMethod with named arguments not implemented'); |
| } |
| ReflectionInfo info = new ReflectionInfo(jsFunction); |
| if (jsFunction == null) { |
| // TODO(ahe): Invoke noSuchMethod. |
| throw new UnimplementedNoSuchMethodError( |
| 'Invoking noSuchMethod with named arguments not implemented'); |
| } |
| |
| positionalArguments = new List.from(positionalArguments); |
| // Check the number of positional arguments is valid. |
| if (info.requiredParameterCount != positionalArguments.length) { |
| // TODO(ahe): Invoke noSuchMethod. |
| throw new UnimplementedNoSuchMethodError( |
| 'Invoking noSuchMethod with named arguments not implemented'); |
| } |
| var defaultArguments = new Map(); |
| for (int i = 0; i < info.optionalParameterCount; i++) { |
| var parameterName = info.parameterName(i + info.requiredParameterCount); |
| var defaultValue = |
| getMetadata(info.defaultValue(i + info.requiredParameterCount)); |
| defaultArguments[parameterName] = defaultValue; |
| } |
| namedArguments.forEach((Symbol symbol, value) { |
| String parameter = n(symbol); |
| if (defaultArguments.containsKey(parameter)) { |
| defaultArguments[parameter] = value; |
| } else { |
| // Extraneous named argument. |
| // TODO(ahe): Invoke noSuchMethod. |
| throw new UnimplementedNoSuchMethodError( |
| 'Invoking noSuchMethod with named arguments not implemented'); |
| } |
| }); |
| positionalArguments.addAll(defaultArguments.values); |
| // TODO(ahe): Handle intercepted methods. |
| return reflect( |
| JS('', '#.apply(#, #)', jsFunction, reflectee, positionalArguments)); |
| } |
| |
| /// Grabs hold of the class-specific invocation cache for the reflectee. |
| /// All reflectees with the same class share the same cache. The cache |
| /// maps reflective names to cached invocation objects with enough decoded |
| /// reflective information to know how to to invoke a specific member. |
| get _classInvocationCache { |
| String cacheName = Primitives.mirrorInvokeCacheName; |
| var cacheHolder = (reflectee == null) ? getInterceptor(null) : reflectee; |
| var cache = JS('', r'#.constructor[#]', cacheHolder, cacheName); |
| if (cache == null) { |
| cache = JsCache.allocate(); |
| JS('void', r'#.constructor[#] = #', cacheHolder, cacheName, cache); |
| } |
| return cache; |
| } |
| |
| String _computeReflectiveName(Symbol symbolName, int type, |
| List positionalArguments, |
| Map<Symbol, dynamic> namedArguments) { |
| String name = n(symbolName); |
| switch (type) { |
| case JSInvocationMirror.GETTER: return name; |
| case JSInvocationMirror.SETTER: return '$name='; |
| case JSInvocationMirror.METHOD: |
| if (namedArguments.isNotEmpty) return '$name*'; |
| int nbArgs = positionalArguments.length as int; |
| return "$name:$nbArgs:0"; |
| } |
| throw new RuntimeError("Could not compute reflective name for $name"); |
| } |
| |
| /** |
| * Returns a `CachedInvocation` or `CachedNoSuchMethodInvocation` for the |
| * given member. |
| * |
| * Caches the result. |
| */ |
| _getCachedInvocation(Symbol name, int type, String reflectiveName, |
| List positionalArguments, Map<Symbol,dynamic> namedArguments) { |
| |
| var cache = _classInvocationCache; |
| var cacheEntry = JsCache.fetch(cache, reflectiveName); |
| var result; |
| if (cacheEntry == null) { |
| disableTreeShaking(); |
| String mangledName = reflectiveNames[reflectiveName]; |
| List<String> argumentNames = const []; |
| |
| // TODO(ahe): We don't need to create an invocation mirror here. The |
| // logic from JSInvocationMirror.getCachedInvocation could easily be |
| // inlined here. |
| Invocation invocation = createUnmangledInvocationMirror( |
| name, mangledName, type, positionalArguments, argumentNames); |
| |
| cacheEntry = |
| JSInvocationMirror.getCachedInvocation(invocation, reflectee); |
| JsCache.update(cache, reflectiveName, cacheEntry); |
| } |
| return cacheEntry; |
| } |
| |
| /// Invoke the member specified through name and type on the reflectee. |
| /// As a side-effect, this populates the class-specific invocation cache |
| /// for the reflectee. |
| InstanceMirror _invoke(Symbol name, |
| int type, |
| List positionalArguments, |
| Map<Symbol,dynamic> namedArguments) { |
| String reflectiveName = |
| _computeReflectiveName(name, type, positionalArguments, namedArguments); |
| |
| if (namedArguments.isNotEmpty) { |
| // TODO(floitsch): first, make sure it's not a getter. |
| return _invokeMethodWithNamedArguments( |
| reflectiveName, positionalArguments, namedArguments); |
| } |
| var cacheEntry = _getCachedInvocation( |
| name, type, reflectiveName, positionalArguments, namedArguments); |
| |
| if (cacheEntry.isNoSuchMethod) { |
| // Could be that we want to invoke a getter, or get a method. |
| if (type == JSInvocationMirror.METHOD && _instanceFieldExists(name)) { |
| return getField(name).invoke( |
| #call, positionalArguments, namedArguments); |
| } |
| |
| if (type == JSInvocationMirror.SETTER) { |
| // For setters we report the setter name "field=". |
| name = s("${n(name)}="); |
| } |
| |
| String mangledName = reflectiveNames[reflectiveName]; |
| // TODO(ahe): Get the argument names. |
| List<String> argumentNames = []; |
| Invocation invocation = createUnmangledInvocationMirror( |
| name, mangledName, type, positionalArguments, argumentNames); |
| return reflect(cacheEntry.invokeOn(reflectee, invocation)); |
| } else { |
| return reflect(cacheEntry.invokeOn(reflectee, positionalArguments)); |
| } |
| } |
| |
| InstanceMirror setField(Symbol fieldName, Object arg) { |
| _invoke(fieldName, JSInvocationMirror.SETTER, [arg], const {}); |
| return reflect(arg); |
| } |
| |
| // JS helpers for getField optimizations. |
| static bool isUndefined(x) |
| => JS('bool', 'typeof # == "undefined"', x); |
| static bool isMissingCache(x) |
| => JS('bool', 'typeof # == "number"', x); |
| static bool isMissingProbe(Symbol symbol) |
| => JS('bool', 'typeof #.\$p == "undefined"', symbol); |
| static bool isEvalAllowed() |
| => JS('bool', 'typeof dart_precompiled != "function"'); |
| |
| |
| /// The getter cache is lazily allocated after a couple |
| /// of invocations of [InstanceMirror.getField]. The delay is |
| /// used to avoid too aggressive caching and dynamic function |
| /// generation for rarely used mirrors. The cache is specific to |
| /// this [InstanceMirror] and maps reflective names to functions |
| /// that will invoke the corresponding getter on the reflectee. |
| /// The reflectee is passed to the function as the first argument |
| /// to avoid the overhead of fetching it from this mirror repeatedly. |
| /// The cache is lazily initialized to a JS object so we can |
| /// benefit from "map transitions" in the underlying JavaScript |
| /// engine to speed up cache probing. |
| var _getterCache = 4; |
| |
| bool _instanceFieldExists(Symbol name) { |
| int getterType = JSInvocationMirror.GETTER; |
| String getterName = |
| _computeReflectiveName(name, getterType, const [], const {}); |
| var getterCacheEntry = _getCachedInvocation( |
| name, getterType, getterName, const [], const {}); |
| return !getterCacheEntry.isNoSuchMethod && !getterCacheEntry.isGetterStub; |
| } |
| |
| InstanceMirror getField(Symbol fieldName) { |
| // BUG(16400): This should be a labelled block, but that makes |
| // dart2js crash when merging locals information in the type |
| // inferencing implementation. |
| do { |
| var cache = _getterCache; |
| if (isMissingCache(cache) || isMissingProbe(fieldName)) break; |
| // If the [fieldName] has an associated probe function, we can use |
| // it to read from the getter cache specific to this [InstanceMirror]. |
| var getter = JS('', '#.\$p(#)', fieldName, cache); |
| if (isUndefined(getter)) break; |
| // Call the getter passing the reflectee as the first argument. |
| var value = JS('', '#(#)', getter, reflectee); |
| // The getter has an associate cache of the last [InstanceMirror] |
| // returned to avoid repeated invocations of [reflect]. To validate |
| // the cache, we check that the value returned by the getter is the |
| // same value as last time. |
| if (JS('bool', '# === #.v', value, getter)) { |
| return JS('InstanceMirror', '#.m', getter); |
| } else { |
| var result = reflect(value); |
| JS('void', '#.v = #', getter, value); |
| JS('void', '#.m = #', getter, result); |
| return result; |
| } |
| } while (false); |
| return _getFieldSlow(fieldName); |
| } |
| |
| InstanceMirror _getFieldSlow(Symbol fieldName) { |
| // First do the slow-case getter invocation. As a side-effect of this, |
| // the invocation cache is filled in so we can query it afterwards. |
| var result = |
| _invoke(fieldName, JSInvocationMirror.GETTER, const [], const {}); |
| String name = n(fieldName); |
| var cacheEntry = JsCache.fetch(_classInvocationCache, name); |
| if (cacheEntry.isNoSuchMethod) { |
| return result; |
| } |
| |
| // Make sure we have a getter cache in this [InstanceMirror]. |
| var cache = _getterCache; |
| if (isMissingCache(cache)) { |
| if ((_getterCache = --cache) != 0) return result; |
| cache = _getterCache = JS('=Object', 'Object.create(null)'); |
| } |
| |
| // Make sure that symbol [fieldName] has a cache probing function ($p). |
| bool useEval = isEvalAllowed(); |
| if (isMissingProbe(fieldName)) { |
| var probe = _newProbeFn(name, useEval); |
| JS('void', '#.\$p = #', fieldName, probe); |
| } |
| |
| // Create a new getter function and install it in the cache. |
| var mangledName = cacheEntry.mangledName; |
| var getter = (cacheEntry.isIntercepted) |
| ? _newInterceptedGetterFn(mangledName, useEval) |
| : _newGetterFn(mangledName, useEval); |
| JS('void', '#[#] = #', cache, name, getter); |
| |
| // Initialize the last value (v) and last mirror (m) on the |
| // newly generated getter to be a sentinel value that is hard |
| // to get hold of through user code. |
| JS('void', '#.v = #.m = #', getter, getter, cache); |
| |
| // Return the result of the slow-path getter invocation. |
| return result; |
| } |
| |
| _newProbeFn(String id, bool useEval) { |
| if (useEval) { |
| // We give the probe function a name to make it appear nicely in |
| // profiles and when debugging. The name also makes the source code |
| // for the function more "unique" so the underlying JavaScript |
| // engine is less likely to re-use an existing piece of generated |
| // code as the result of calling eval. In return, this leads to |
| // less polymorphic access in the probe function. |
| var body = "(function probe\$$id(c){return c.$id})"; |
| return JS('', '(function(b){return eval(b)})(#)', body); |
| } else { |
| return JS('', '(function(n){return(function(c){return c[n]})})(#)', id); |
| } |
| } |
| |
| _newGetterFn(String name, bool useEval) { |
| if (!useEval) return _newGetterNoEvalFn(name); |
| // We give the getter function a name that associates it with the |
| // class of the reflectee. This makes it easier to spot in profiles |
| // and when debugging, but it also means that the underlying JavaScript |
| // engine will only share the generated code for accessors on the |
| // same class (through caching of eval'ed code). This makes the |
| // generated call to the getter - e.g. o.get$foo() - much more likely |
| // to be monomorphic and inlineable. |
| String className = JS('String', '#.constructor.name', reflectee); |
| var body = "(function $className\$$name(o){return o.$name()})"; |
| return JS('', '(function(b){return eval(b)})(#)', body); |
| } |
| |
| _newGetterNoEvalFn(n) => JS('', |
| '(function(n){return(function(o){return o[n]()})})(#)', n); |
| |
| _newInterceptedGetterFn(String name, bool useEval) { |
| var object = reflectee; |
| // It is possible that the interceptor for a given object is the object |
| // itself, so it is important not to share the code that captures the |
| // interceptor between multiple different instances of [InstanceMirror]. |
| var interceptor = getInterceptor(object); |
| if (!useEval) return _newInterceptGetterNoEvalFn(name, interceptor); |
| String className = JS('String', '#.constructor.name', interceptor); |
| String functionName = '$className\$$name'; |
| var body = |
| '(function(i) {' |
| ' function $functionName(o){return i.$name(o)}' |
| ' return $functionName;' |
| '})'; |
| return JS('', '(function(b){return eval(b)})(#)(#)', body, interceptor); |
| } |
| |
| _newInterceptGetterNoEvalFn(n, i) => JS('', |
| '(function(n,i){return(function(o){return i[n](o)})})(#,#)', n, i); |
| |
| delegate(Invocation invocation) { |
| return JSInvocationMirror.invokeFromMirror(invocation, reflectee); |
| } |
| |
| operator ==(other) { |
| return other is JsInstanceMirror && |
| identical(reflectee, other.reflectee); |
| } |
| |
| int get hashCode { |
| // Avoid hash collisions with the reflectee. This constant is in Smi range |
| // and happens to be the inner padding from RFC 2104. |
| return identityHashCode(reflectee) ^ 0x36363636; |
| } |
| |
| String toString() => 'InstanceMirror on ${Error.safeToString(reflectee)}'; |
| |
| // TODO(ahe): Remove this method from the API. |
| MirrorSystem get mirrors => currentJsMirrorSystem; |
| } |
| |
| /** |
| * ClassMirror for generic classes where the type parameters are bound. |
| * |
| * [typeArguments] will return a list of the type arguments, in constrast |
| * to JsCLassMirror that returns an empty list since it represents original |
| * declarations and classes that are not generic. |
| */ |
| class JsTypeBoundClassMirror extends JsDeclarationMirror |
| implements ClassMirror { |
| final JsClassMirror _class; |
| |
| /** |
| * When instantiated this field will hold a string representing the list of |
| * type arguments for the class, i.e. what is inside the outermost angle |
| * brackets. Then, when get typeArguments is called the first time, the string |
| * is parsed into the actual list of TypeMirrors, and stored in |
| * [_cachedTypeArguments]. Due to type substitution of, for instance, |
| * superclasses the mangled name of the class and hence this string is needed |
| * after [_cachedTypeArguments] has been computed. |
| * |
| * If an integer is encountered as a type argument, it represents the type |
| * variable at the corresponding entry in [emitter.globalMetadata]. |
| */ |
| String _typeArguments; |
| |
| UnmodifiableListView<TypeMirror> _cachedTypeArguments; |
| UnmodifiableMapView<Symbol, DeclarationMirror> _cachedDeclarations; |
| UnmodifiableMapView<Symbol, DeclarationMirror> _cachedMembers; |
| UnmodifiableMapView<Symbol, MethodMirror> _cachedConstructors; |
| Map<Symbol, VariableMirror> _cachedVariables; |
| Map<Symbol, MethodMirror> _cachedGetters; |
| Map<Symbol, MethodMirror> _cachedSetters; |
| Map<Symbol, MethodMirror> _cachedMethodsMap; |
| List<JsMethodMirror> _cachedMethods; |
| ClassMirror _superclass; |
| List<ClassMirror> _cachedSuperinterfaces; |
| Map<Symbol, MethodMirror> _cachedInstanceMembers; |
| Map<Symbol, MethodMirror> _cachedStaticMembers; |
| |
| JsTypeBoundClassMirror(JsClassMirror originalDeclaration, this._typeArguments) |
| : _class = originalDeclaration, |
| super(originalDeclaration.simpleName); |
| |
| String get _prettyName => 'ClassMirror'; |
| |
| String toString() { |
| String result = '$_prettyName on ${n(simpleName)}'; |
| if (typeArguments != null) { |
| result = "$result<${typeArguments.join(', ')}>"; |
| } |
| return result; |
| } |
| |
| String get _mangledName { |
| for (TypeMirror typeArgument in typeArguments) { |
| if (typeArgument != JsMirrorSystem._dynamicType) { |
| return '${_class._mangledName}<$_typeArguments>'; |
| } |
| } |
| // When all type arguments are dynamic, the canonical representation is to |
| // drop them. |
| return _class._mangledName; |
| } |
| |
| List<TypeVariableMirror> get typeVariables => _class.typeVariables; |
| |
| List<TypeMirror> get typeArguments { |
| if (_cachedTypeArguments != null) return _cachedTypeArguments; |
| List result = new List(); |
| |
| addTypeArgument(String typeArgument) { |
| int parsedIndex = int.parse(typeArgument, onError: (_) => -1); |
| if (parsedIndex == -1) { |
| result.add(reflectClassByMangledName(typeArgument.trim())); |
| } else { |
| TypeVariable typeVariable = getMetadata(parsedIndex); |
| TypeMirror owner = reflectClass(typeVariable.owner); |
| TypeVariableMirror typeMirror = |
| new JsTypeVariableMirror(typeVariable, owner, parsedIndex); |
| result.add(typeMirror); |
| } |
| } |
| |
| if (_typeArguments.indexOf('<') == -1) { |
| _typeArguments.split(',').forEach((t) => addTypeArgument(t)); |
| } else { |
| int level = 0; |
| String currentTypeArgument = ''; |
| |
| for (int i = 0; i < _typeArguments.length; i++) { |
| var character = _typeArguments[i]; |
| if (character == ' ') { |
| continue; |
| } else if (character == '<') { |
| currentTypeArgument += character; |
| level++; |
| } else if (character == '>') { |
| currentTypeArgument += character; |
| level--; |
| } else if (character == ',') { |
| if (level > 0) { |
| currentTypeArgument += character; |
| } else { |
| addTypeArgument(currentTypeArgument); |
| currentTypeArgument = ''; |
| } |
| } else { |
| currentTypeArgument += character; |
| } |
| } |
| addTypeArgument(currentTypeArgument); |
| } |
| return _cachedTypeArguments = new UnmodifiableListView(result); |
| } |
| |
| List<JsMethodMirror> get _methods { |
| if (_cachedMethods != null) return _cachedMethods; |
| return _cachedMethods =_class._getMethodsWithOwner(this); |
| } |
| |
| Map<Symbol, MethodMirror> get __methods { |
| if (_cachedMethodsMap != null) return _cachedMethodsMap; |
| return _cachedMethodsMap = new UnmodifiableMapView<Symbol, MethodMirror>( |
| filterMethods(_methods)); |
| } |
| |
| Map<Symbol, MethodMirror> get __constructors { |
| if (_cachedConstructors != null) return _cachedConstructors; |
| return _cachedConstructors = |
| new UnmodifiableMapView<Symbol, MethodMirror>( |
| filterConstructors(_methods)); |
| } |
| |
| Map<Symbol, MethodMirror> get __getters { |
| if (_cachedGetters != null) return _cachedGetters; |
| return _cachedGetters = new UnmodifiableMapView<Symbol, MethodMirror>( |
| filterGetters(_methods, __variables)); |
| } |
| |
| Map<Symbol, MethodMirror> get __setters { |
| if (_cachedSetters != null) return _cachedSetters; |
| return _cachedSetters = new UnmodifiableMapView<Symbol, MethodMirror>( |
| filterSetters(_methods, __variables)); |
| } |
| |
| Map<Symbol, VariableMirror> get __variables { |
| if (_cachedVariables != null) return _cachedVariables; |
| var result = new Map(); |
| for (JsVariableMirror mirror in _class._getFieldsWithOwner(this)) { |
| result[mirror.simpleName] = mirror; |
| } |
| return _cachedVariables = |
| new UnmodifiableMapView<Symbol, VariableMirror>(result); |
| } |
| |
| Map<Symbol, DeclarationMirror> get __members { |
| if (_cachedMembers != null) return _cachedMembers; |
| return _cachedMembers = new UnmodifiableMapView<Symbol, DeclarationMirror>( |
| filterMembers(_methods, __variables)); |
| } |
| |
| Map<Symbol, DeclarationMirror> get declarations { |
| if (_cachedDeclarations != null) return _cachedDeclarations; |
| Map<Symbol, DeclarationMirror> result = |
| new Map<Symbol, DeclarationMirror>(); |
| result.addAll(__members); |
| result.addAll(__constructors); |
| typeVariables.forEach((tv) => result[tv.simpleName] = tv); |
| return _cachedDeclarations = |
| new UnmodifiableMapView<Symbol, DeclarationMirror>(result); |
| } |
| |
| Map<Symbol, MethodMirror> get staticMembers { |
| if (_cachedStaticMembers == null) { |
| var result = new Map<Symbol, MethodMirror>(); |
| declarations.values.forEach((decl) { |
| if (decl is MethodMirror && decl.isStatic && !decl.isConstructor) { |
| result[decl.simpleName] = decl; |
| } |
| if (decl is VariableMirror && decl.isStatic) { |
| var getterName = decl.simpleName; |
| result[getterName] = new JsSyntheticAccessor( |
| this, getterName, true, true, false, decl); |
| if (!decl.isFinal) { |
| var setterName = setterSymbol(decl.simpleName); |
| result[setterName] = new JsSyntheticAccessor( |
| this, setterName, false, true, false, decl); |
| } |
| } |
| }); |
| _cachedStaticMembers = result; |
| } |
| return _cachedStaticMembers; |
| } |
| |
| Map<Symbol, MethodMirror> get instanceMembers { |
| if (_cachedInstanceMembers == null) { |
| var result = new Map<Symbol, MethodMirror>(); |
| if (superclass != null) { |
| result.addAll(superclass.instanceMembers); |
| } |
| declarations.values.forEach((decl) { |
| if (decl is MethodMirror && !decl.isStatic && |
| !decl.isConstructor && !decl.isAbstract) { |
| result[decl.simpleName] = decl; |
| } |
| if (decl is VariableMirror && !decl.isStatic) { |
| var getterName = decl.simpleName; |
| result[getterName] = new JsSyntheticAccessor( |
| this, getterName, true, false, false, decl); |
| if (!decl.isFinal) { |
| var setterName = setterSymbol(decl.simpleName); |
| result[setterName] = new JsSyntheticAccessor( |
| this, setterName, false, false, false, decl); |
| } |
| } |
| }); |
| _cachedInstanceMembers = result; |
| } |
| return _cachedInstanceMembers; |
| } |
| |
| InstanceMirror setField(Symbol fieldName, Object arg) { |
| return _class.setField(fieldName, arg); |
| } |
| |
| InstanceMirror getField(Symbol fieldName) => _class.getField(fieldName); |
| |
| InstanceMirror newInstance(Symbol constructorName, |
| List positionalArguments, |
| [Map<Symbol, dynamic> namedArguments]) { |
| var instance = _class._getInvokedInstance(constructorName, |
| positionalArguments, |
| namedArguments); |
| return reflect(setRuntimeTypeInfo( |
| instance, typeArguments.map((t) => t._asRuntimeType()).toList())); |
| } |
| |
| _asRuntimeType() { |
| return [_class._jsConstructor].addAll( |
| typeArguments.map((t) => t._asRuntimeType())); |
| } |
| |
| JsLibraryMirror get owner => _class.owner; |
| |
| List<InstanceMirror> get metadata => _class.metadata; |
| |
| ClassMirror get superclass { |
| if (_superclass != null) return _superclass; |
| |
| List<int> typeInformation = |
| JS('List|Null', 'init.typeInformation[#]', _class._mangledName); |
| assert(typeInformation != null); |
| var type = getMetadata(typeInformation[0]); |
| return _superclass = typeMirrorFromRuntimeTypeRepresentation(this, type); |
| } |
| |
| InstanceMirror invoke(Symbol memberName, |
| List positionalArguments, |
| [Map<Symbol,dynamic> namedArguments]) { |
| return _class.invoke(memberName, positionalArguments, namedArguments); |
| } |
| |
| bool get isOriginalDeclaration => false; |
| |
| ClassMirror get originalDeclaration => _class; |
| |
| List<ClassMirror> get superinterfaces { |
| if (_cachedSuperinterfaces != null) return _cachedSuperinterfaces; |
| return _cachedSuperinterfaces = _class._getSuperinterfacesWithOwner(this); |
| } |
| |
| bool get isPrivate => _class.isPrivate; |
| |
| bool get isTopLevel => _class.isTopLevel; |
| |
| bool get isAbstract => _class.isAbstract; |
| |
| bool isSubclassOf(ClassMirror other) => _class.isSubclassOf(other); |
| |
| SourceLocation get location => _class.location; |
| |
| MirrorSystem get mirrors => _class.mirrors; |
| |
| Symbol get qualifiedName => _class.qualifiedName; |
| |
| bool get hasReflectedType => true; |
| |
| Type get reflectedType => createRuntimeType(_mangledName); |
| |
| Symbol get simpleName => _class.simpleName; |
| |
| // TODO(ahe): Implement this. |
| ClassMirror get mixin => throw new UnimplementedError(); |
| |
| bool isSubtypeOf(TypeMirror other) => throw new UnimplementedError(); |
| |
| bool isAssignableTo(TypeMirror other) => throw new UnimplementedError(); |
| } |
| |
| class JsSyntheticAccessor implements MethodMirror { |
| final DeclarationMirror owner; |
| final Symbol simpleName; |
| final bool isGetter; |
| final bool isStatic; |
| final bool isTopLevel; |
| final _target; /// The field or type that introduces the synthetic accessor. |
| |
| JsSyntheticAccessor(this.owner, |
| this.simpleName, |
| this.isGetter, |
| this.isStatic, |
| this.isTopLevel, |
| this._target); |
| |
| bool get isSynthetic => true; |
| bool get isRegularMethod => false; |
| bool get isOperator => false; |
| bool get isConstructor => false; |
| bool get isConstConstructor => false; |
| bool get isGenerativeConstructor => false; |
| bool get isFactoryConstructor => false; |
| bool get isRedirectingConstructor => false; |
| bool get isAbstract => false; |
| |
| bool get isSetter => !isGetter; |
| bool get isPrivate => n(simpleName).startsWith('_'); |
| |
| Symbol get qualifiedName => computeQualifiedName(owner, simpleName); |
| Symbol get constructorName => const Symbol(''); |
| |
| TypeMirror get returnType => _target.type; |
| List<ParameterMirror> get parameters { |
| if (isGetter) return const []; |
| return new UnmodifiableListView( |
| [new JsSyntheticSetterParameter(this, this._target)]); |
| } |
| |
| List<InstanceMirror> get metadata => const []; |
| String get source => null; |
| SourceLocation get location => throw new UnimplementedError(); |
| } |
| |
| class JsSyntheticSetterParameter implements ParameterMirror { |
| final DeclarationMirror owner; |
| final VariableMirror _target; |
| |
| JsSyntheticSetterParameter(this.owner, this._target); |
| |
| Symbol get simpleName => _target.simpleName; |
| Symbol get qualifiedName => computeQualifiedName(owner, simpleName); |
| TypeMirror get type => _target.type; |
| |
| bool get isOptional => false; |
| bool get isNamed => false; |
| bool get isStatic => false; |
| bool get isTopLevel => false; |
| bool get isConst => false; |
| bool get isFinal => true; |
| bool get isPrivate => false; |
| bool get hasDefaultValue => false; |
| InstanceMirror get defaultValue => null; |
| List<InstanceMirror> get metadata => const []; |
| SourceLocation get location => throw new UnimplementedError(); |
| } |
| |
| class JsClassMirror extends JsTypeMirror with JsObjectMirror |
| implements ClassMirror { |
| final String _mangledName; |
| final _jsConstructor; |
| final String _fieldsDescriptor; |
| final List _fieldsMetadata; |
| final _jsConstructorCache = JsCache.allocate(); |
| List _metadata; |
| ClassMirror _superclass; |
| List<JsMethodMirror> _cachedMethods; |
| List<VariableMirror> _cachedFields; |
| UnmodifiableMapView<Symbol, MethodMirror> _cachedConstructors; |
| UnmodifiableMapView<Symbol, MethodMirror> _cachedMethodsMap; |
| UnmodifiableMapView<Symbol, MethodMirror> _cachedGetters; |
| UnmodifiableMapView<Symbol, MethodMirror> _cachedSetters; |
| UnmodifiableMapView<Symbol, VariableMirror> _cachedVariables; |
| UnmodifiableMapView<Symbol, Mirror> _cachedMembers; |
| UnmodifiableMapView<Symbol, DeclarationMirror> _cachedDeclarations; |
| UnmodifiableListView<InstanceMirror> _cachedMetadata; |
| UnmodifiableListView<ClassMirror> _cachedSuperinterfaces; |
| UnmodifiableListView<TypeVariableMirror> _cachedTypeVariables; |
| Map<Symbol, MethodMirror> _cachedInstanceMembers; |
| Map<Symbol, MethodMirror> _cachedStaticMembers; |
| |
| // Set as side-effect of accessing JsLibraryMirror.classes. |
| JsLibraryMirror _owner; |
| |
| JsClassMirror(Symbol simpleName, |
| this._mangledName, |
| this._jsConstructor, |
| this._fieldsDescriptor, |
| this._fieldsMetadata) |
| : super(simpleName); |
| |
| String get _prettyName => 'ClassMirror'; |
| |
| Map<Symbol, MethodMirror> get __constructors { |
| if (_cachedConstructors != null) return _cachedConstructors; |
| return _cachedConstructors = |
| new UnmodifiableMapView<Symbol, MethodMirror>( |
| filterConstructors(_methods)); |
| } |
| |
| _asRuntimeType() { |
| if (typeVariables.isEmpty) return _jsConstructor; |
| var type = [_jsConstructor]; |
| for (int i = 0; i < typeVariables.length; i ++) { |
| type.add(JsMirrorSystem._dynamicType._asRuntimeType); |
| } |
| return type; |
| } |
| |
| List<JsMethodMirror> _getMethodsWithOwner(DeclarationMirror methodOwner) { |
| var prototype = JS('', '#.prototype', _jsConstructor); |
| List<String> keys = extractKeys(prototype); |
| var result = <JsMethodMirror>[]; |
| for (String key in keys) { |
| if (isReflectiveDataInPrototype(key)) continue; |
| String simpleName = mangledNames[key]; |
| // [simpleName] can be null if [key] represents an implementation |
| // detail, for example, a bailout method, or runtime type support. |
| // It might also be null if the user has limited what is reified for |
| // reflection with metadata. |
| if (simpleName == null) continue; |
| var function = JS('', '#[#]', prototype, key); |
| if (isNoSuchMethodStub(function)) continue; |
| var mirror = |
| new JsMethodMirror.fromUnmangledName( |
| simpleName, function, false, false); |
| result.add(mirror); |
| mirror._owner = methodOwner; |
| } |
| |
| keys = extractKeys(JS('', 'init.statics[#]', _mangledName)); |
| for (String mangledName in keys) { |
| if (isReflectiveDataInPrototype(mangledName)) continue; |
| String unmangledName = mangledName; |
| var jsFunction = JS('', '#[#]', owner._globalObject, mangledName); |
| |
| bool isConstructor = false; |
| if (hasReflectableProperty(jsFunction)) { |
| String reflectionName = |
| JS('String|Null', r'#.$reflectionName', jsFunction); |
| if (reflectionName == null) continue; |
| isConstructor = reflectionName.startsWith('new '); |
| if (isConstructor) { |
| reflectionName = reflectionName.substring(4).replaceAll(r'$', '.'); |
| } |
| unmangledName = reflectionName; |
| } else { |
| continue; |
| } |
| bool isStatic = !isConstructor; // Constructors are not static. |
| JsMethodMirror mirror = |
| new JsMethodMirror.fromUnmangledName( |
| unmangledName, jsFunction, isStatic, isConstructor); |
| result.add(mirror); |
| mirror._owner = methodOwner; |
| } |
| |
| return result; |
| } |
| |
| List<JsMethodMirror> get _methods { |
| if (_cachedMethods != null) return _cachedMethods; |
| return _cachedMethods = _getMethodsWithOwner(this); |
| } |
| |
| List<VariableMirror> _getFieldsWithOwner(DeclarationMirror fieldOwner) { |
| var result = <VariableMirror>[]; |
| |
| var instanceFieldSpecfication = _fieldsDescriptor.split(';')[1]; |
| if (_fieldsMetadata != null) { |
| instanceFieldSpecfication = |
| [instanceFieldSpecfication]..addAll(_fieldsMetadata); |
| } |
| parseCompactFieldSpecification( |
| fieldOwner, instanceFieldSpecfication, false, result); |
| |
| var staticDescriptor = JS('', 'init.statics[#]', _mangledName); |
| if (staticDescriptor != null) { |
| parseCompactFieldSpecification( |
| fieldOwner, |
| JS('', '#[#]', |
| staticDescriptor, JS_GET_NAME('CLASS_DESCRIPTOR_PROPERTY')), |
| true, result); |
| } |
| return result; |
| } |
| |
| List<VariableMirror> get _fields { |
| if (_cachedFields != null) return _cachedFields; |
| return _cachedFields = _getFieldsWithOwner(this); |
| } |
| |
| Map<Symbol, MethodMirror> get __methods { |
| if (_cachedMethodsMap != null) return _cachedMethodsMap; |
| return _cachedMethodsMap = |
| new UnmodifiableMapView<Symbol, MethodMirror>(filterMethods(_methods)); |
| } |
| |
| Map<Symbol, MethodMirror> get __getters { |
| if (_cachedGetters != null) return _cachedGetters; |
| return _cachedGetters = new UnmodifiableMapView<Symbol, MethodMirror>( |
| filterGetters(_methods, __variables)); |
| } |
| |
| Map<Symbol, MethodMirror> get __setters { |
| if (_cachedSetters != null) return _cachedSetters; |
| return _cachedSetters = new UnmodifiableMapView<Symbol, MethodMirror>( |
| filterSetters(_methods, __variables)); |
| } |
| |
| Map<Symbol, VariableMirror> get __variables { |
| if (_cachedVariables != null) return _cachedVariables; |
| var result = new Map(); |
| for (JsVariableMirror mirror in _fields) { |
| result[mirror.simpleName] = mirror; |
| } |
| return _cachedVariables = |
| new UnmodifiableMapView<Symbol, VariableMirror>(result); |
| } |
| |
| Map<Symbol, Mirror> get __members { |
| if (_cachedMembers != null) return _cachedMembers; |
| return _cachedMembers = new UnmodifiableMapView<Symbol, Mirror>( |
| filterMembers(_methods, __variables)); |
| } |
| |
| Map<Symbol, DeclarationMirror> get declarations { |
| if (_cachedDeclarations != null) return _cachedDeclarations; |
| var result = new Map<Symbol, DeclarationMirror>(); |
| addToResult(Symbol key, Mirror value) { |
| result[key] = value; |
| } |
| __members.forEach(addToResult); |
| __constructors.forEach(addToResult); |
| typeVariables.forEach((tv) => result[tv.simpleName] = tv); |
| return _cachedDeclarations = |
| new UnmodifiableMapView<Symbol, DeclarationMirror>(result); |
| } |
| |
| Map<Symbol, MethodMirror> get staticMembers { |
| if (_cachedStaticMembers == null) { |
| var result = new Map<Symbol, MethodMirror>(); |
| declarations.values.forEach((decl) { |
| if (decl is MethodMirror && decl.isStatic && !decl.isConstructor) { |
| result[decl.simpleName] = decl; |
| } |
| if (decl is VariableMirror && decl.isStatic) { |
| var getterName = decl.simpleName; |
| result[getterName] = new JsSyntheticAccessor( |
| this, getterName, true, true, false, decl); |
| if (!decl.isFinal) { |
| var setterName = setterSymbol(decl.simpleName); |
| result[setterName] = new JsSyntheticAccessor( |
| this, setterName, false, true, false, decl); |
| } |
| } |
| }); |
| _cachedStaticMembers = result; |
| } |
| return _cachedStaticMembers; |
| } |
| |
| Map<Symbol, MethodMirror> get instanceMembers { |
| if (_cachedInstanceMembers == null) { |
| var result = new Map<Symbol, MethodMirror>(); |
| if (superclass != null) { |
| result.addAll(superclass.instanceMembers); |
| } |
| declarations.values.forEach((decl) { |
| if (decl is MethodMirror && !decl.isStatic && |
| !decl.isConstructor && !decl.isAbstract) { |
| result[decl.simpleName] = decl; |
| } |
| if (decl is VariableMirror && !decl.isStatic) { |
| var getterName = decl.simpleName; |
| result[getterName] = new JsSyntheticAccessor( |
| this, getterName, true, false, false, decl); |
| if (!decl.isFinal) { |
| var setterName = setterSymbol(decl.simpleName); |
| result[setterName] = new JsSyntheticAccessor( |
| this, setterName, false, false, false, decl); |
| } |
| } |
| }); |
| _cachedInstanceMembers = result; |
| } |
| return _cachedInstanceMembers; |
| } |
| |
| InstanceMirror setField(Symbol fieldName, Object arg) { |
| JsVariableMirror mirror = __variables[fieldName]; |
| if (mirror != null && mirror.isStatic && !mirror.isFinal) { |
| // '$' (JS_CURRENT_ISOLATE()) stores state which is stored directly, so |
| // we shouldn't use [JsLibraryMirror._globalObject] here. |
| String jsName = mirror._jsName; |
| if (!JS('bool', '# in #', jsName, JS_CURRENT_ISOLATE())) { |
| throw new RuntimeError('Cannot find "$jsName" in current isolate.'); |
| } |
| JS('void', '#[#] = #', JS_CURRENT_ISOLATE(), jsName, arg); |
| return reflect(arg); |
| } |
| // TODO(ahe): What receiver to use? |
| throw new NoSuchMethodError(this, setterSymbol(fieldName), [arg], null); |
| } |
| |
| bool _staticFieldExists(Symbol fieldName) { |
| JsVariableMirror mirror = __variables[fieldName]; |
| if (mirror != null) return mirror.isStatic; |
| JsMethodMirror getter = __getters[fieldName]; |
| return getter != null && getter.isStatic; |
| } |
| |
| InstanceMirror getField(Symbol fieldName) { |
| JsVariableMirror mirror = __variables[fieldName]; |
| if (mirror != null && mirror.isStatic) { |
| String jsName = mirror._jsName; |
| // '$' (JS_CURRENT_ISOLATE()) stores state which is read directly, so |
| // we shouldn't use [JsLibraryMirror._globalObject] here. |
| if (!JS('bool', '# in #', jsName, JS_CURRENT_ISOLATE())) { |
| throw new RuntimeError('Cannot find "$jsName" in current isolate.'); |
| } |
| if (JS('bool', '# in init.lazies', jsName)) { |
| String getterName = JS('String', 'init.lazies[#]', jsName); |
| return reflect(JS('', '#[#]()', JS_CURRENT_ISOLATE(), getterName)); |
| } else { |
| return reflect(JS('', '#[#]', JS_CURRENT_ISOLATE(), jsName)); |
| } |
| } |
| JsMethodMirror getter = __getters[fieldName]; |
| if (getter != null && getter.isStatic) { |
| return reflect(getter._invoke(const [], const {})); |
| } |
| // If the fieldName designates a static function we have to return |
| // its closure. |
| JsMethodMirror method = __methods[fieldName]; |
| if (method != null && method.isStatic) { |
| // We invoke the same getter that Dart code would execute. During |
| // initialization we have stored that getter on the function (so that |
| // we can find it more easily here). |
| var getter = JS("", "#['\$getter']", method._jsFunction); |
| if (getter == null) throw new UnimplementedError(); |
| return reflect(JS("", "#()", getter)); |
| } |
| // TODO(ahe): What receiver to use? |
| throw new NoSuchMethodError(this, fieldName, null, null); |
| } |
| |
| _getInvokedInstance(Symbol constructorName, |
| List positionalArguments, |
| [Map<Symbol, dynamic> namedArguments]) { |
| if (namedArguments != null && !namedArguments.isEmpty) { |
| throw new UnsupportedError('Named arguments are not implemented.'); |
| } |
| JsMethodMirror mirror = |
| JsCache.fetch(_jsConstructorCache, n(constructorName)); |
| if (mirror == null) { |
| mirror = __constructors.values.firstWhere( |
| (m) => m.constructorName == constructorName, |
| orElse: () { |
| // TODO(ahe): What receiver to use? |
| throw new NoSuchMethodError( |
| this, constructorName, positionalArguments, namedArguments); |
| }); |
| JsCache.update(_jsConstructorCache, n(constructorName), mirror); |
| } |
| return mirror._invoke(positionalArguments, namedArguments); |
| } |
| |
| InstanceMirror newInstance(Symbol constructorName, |
| List positionalArguments, |
| [Map<Symbol, dynamic> namedArguments]) { |
| return reflect(_getInvokedInstance(constructorName, |
| positionalArguments, |
| namedArguments)); |
| } |
| |
| JsLibraryMirror get owner { |
| if (_owner == null) { |
| for (var list in JsMirrorSystem.librariesByName.values) { |
| for (JsLibraryMirror library in list) { |
| // This will set _owner field on all classes as a side |
| // effect. This gives us a fast path to reflect on a |
| // class without parsing reflection data. |
| library.__classes; |
| } |
| } |
| if (_owner == null) { |
| throw new StateError('Class "${n(simpleName)}" has no owner'); |
| } |
| } |
| return _owner; |
| } |
| |
| List<InstanceMirror> get metadata { |
| if (_cachedMetadata != null) return _cachedMetadata; |
| if (_metadata == null) { |
| _metadata = extractMetadata(JS('', '#.prototype', _jsConstructor)); |
| } |
| return _cachedMetadata = |
| new UnmodifiableListView<InstanceMirror>(_metadata.map(reflect)); |
| } |
| |
| ClassMirror get superclass { |
| if (_superclass == null) { |
| List<int> typeInformation = |
| JS('List|Null', 'init.typeInformation[#]', _mangledName); |
| if (typeInformation != null) { |
| var type = getMetadata(typeInformation[0]); |
| _superclass = typeMirrorFromRuntimeTypeRepresentation(this, type); |
| } else { |
| var superclassName = _fieldsDescriptor.split(';')[0]; |
| // TODO(zarah): Remove special handing of mixins. |
| var mixins = superclassName.split('+'); |
| if (mixins.length > 1) { |
| if (mixins.length != 2) { |
| throw new RuntimeError('Strange mixin: $_fieldsDescriptor'); |
| } |
| _superclass = reflectClassByMangledName(mixins[0]); |
| } else { |
| // Use _superclass == this to represent class with no superclass |
| // (Object). |
| _superclass = (superclassName == '') |
| ? this : reflectClassByMangledName(superclassName); |
| } |
| } |
| } |
| return _superclass == this ? null : _superclass; |
| } |
| |
| InstanceMirror invoke(Symbol memberName, |
| List positionalArguments, |
| [Map<Symbol,dynamic> namedArguments]) { |
| // Mirror API gotcha: Calling [invoke] on a ClassMirror means invoke a |
| // static method. |
| |
| if (namedArguments != null && !namedArguments.isEmpty) { |
| throw new UnsupportedError('Named arguments are not implemented.'); |
| } |
| JsMethodMirror mirror = __methods[memberName]; |
| |
| if (mirror == null && _staticFieldExists(memberName)) { |
| return getField(memberName) |
| .invoke(#call, positionalArguments, namedArguments); |
| } |
| if (mirror == null || !mirror.isStatic) { |
| // TODO(ahe): What receiver to use? |
| throw new NoSuchMethodError( |
| this, memberName, positionalArguments, namedArguments); |
| } |
| if (!mirror.canInvokeReflectively()) { |
| throwInvalidReflectionError(n(memberName)); |
| } |
| return reflect(mirror._invoke(positionalArguments, namedArguments)); |
| } |
| |
| bool get isOriginalDeclaration => true; |
| |
| ClassMirror get originalDeclaration => this; |
| |
| List<ClassMirror> _getSuperinterfacesWithOwner(DeclarationMirror owner) { |
| List<int> typeInformation = |
| JS('List|Null', 'init.typeInformation[#]', _mangledName); |
| List<ClassMirror> result = const <ClassMirror>[]; |
| if (typeInformation != null) { |
| ClassMirror lookupType(int i) { |
| var type = getMetadata(i); |
| return typeMirrorFromRuntimeTypeRepresentation(owner, type); |
| } |
| |
| //We skip the first since it is the supertype. |
| result = typeInformation.skip(1).map(lookupType).toList(); |
| } |
| |
| return new UnmodifiableListView<ClassMirror>(result); |
| } |
| |
| List<ClassMirror> get superinterfaces { |
| if (_cachedSuperinterfaces != null) return _cachedSuperinterfaces; |
| return _cachedSuperinterfaces = _getSuperinterfacesWithOwner(this); |
| } |
| |
| List<TypeVariableMirror> get typeVariables { |
| if (_cachedTypeVariables != null) return _cachedTypeVariables; |
| List result = new List(); |
| List typeVariables = |
| JS('JSExtendableArray|Null', '#.prototype["<>"]', _jsConstructor); |
| if (typeVariables == null) return result; |
| for (int i = 0; i < typeVariables.length; i++) { |
| TypeVariable typeVariable = getMetadata(typeVariables[i]); |
| result.add(new JsTypeVariableMirror(typeVariable, this, |
| typeVariables[i])); |
| } |
| return _cachedTypeVariables = new UnmodifiableListView(result); |
| } |
| |
| List<TypeMirror> get typeArguments => const <TypeMirror>[]; |
| |
| bool get hasReflectedType => typeVariables.length == 0; |
| |
| Type get reflectedType { |
| if (!hasReflectedType) { |
| throw new UnsupportedError( |
| "Declarations of generics have no reflected type"); |
| } |
| return createRuntimeType(_mangledName); |
| } |
| |
| // TODO(ahe): Implement this. |
| ClassMirror get mixin => throw new UnimplementedError(); |
| |
| bool get isAbstract => throw new UnimplementedError(); |
| |
| bool isSubclassOf(ClassMirror other) { |
| if (other is! ClassMirror) { |
| throw new ArgumentError(other); |
| } |
| if (other is JsFunctionTypeMirror) { |
| return false; |
| } if (other is JsClassMirror && |
| JS('bool', '# == #', other._jsConstructor, _jsConstructor)) { |
| return true; |
| } else if (superclass == null) { |
| return false; |
| } else { |
| return superclass.isSubclassOf(other); |
| } |
| } |
| } |
| |
| class JsVariableMirror extends JsDeclarationMirror implements VariableMirror { |
| |
| // TODO(ahe): The values in these fields are virtually untested. |
| final String _jsName; |
| final bool isFinal; |
| final bool isStatic; |
| final _metadataFunction; |
| final DeclarationMirror _owner; |
| final int _type; |
| List _metadata; |
| |
| JsVariableMirror(Symbol simpleName, |
| this._jsName, |
| this._type, |
| this.isFinal, |
| this.isStatic, |
| this._metadataFunction, |
| this._owner) |
| : super(simpleName); |
| |
| factory JsVariableMirror.from(String descriptor, |
| metadataFunction, |
| JsDeclarationMirror owner, |
| bool isStatic) { |
| List<String> fieldInformation = descriptor.split('-'); |
| if (fieldInformation.length == 1) { |
| // The field is not available for reflection. |
| // TODO(ahe): Should return an unreflectable field. |
| return null; |
| } |
| |
| String field = fieldInformation[0]; |
| int length = field.length; |
| var code = fieldCode(field.codeUnitAt(length - 1)); |
| bool isFinal = false; |
| if (code == 0) return null; // Inherited field. |
| bool hasGetter = (code & 3) != 0; |
| bool hasSetter = (code >> 2) != 0; |
| isFinal = !hasSetter; |
| length--; |
| String jsName; |
| String accessorName = jsName = field.substring(0, length); |
| int divider = field.indexOf(':'); |
| if (divider > 0) { |
| accessorName = accessorName.substring(0, divider); |
| jsName = field.substring(divider + 1); |
| } |
| var unmangledName; |
| if (isStatic) { |
| unmangledName = mangledGlobalNames[accessorName]; |
| } else { |
| String getterPrefix = JS_GET_NAME('GETTER_PREFIX'); |
| unmangledName = mangledNames['$getterPrefix$accessorName']; |
| } |
| if (unmangledName == null) unmangledName = accessorName; |
| if (!hasSetter) { |
| // TODO(ahe): This is a hack to handle checked setters in checked mode. |
| var setterName = s('$unmangledName='); |
| for (JsMethodMirror method in owner._methods) { |
| if (method.simpleName == setterName) { |
| isFinal = false; |
| break; |
| } |
| } |
| } |
| int type = int.parse(fieldInformation[1]); |
| return new JsVariableMirror(s(unmangledName), |
| jsName, |
| type, |
| isFinal, |
| isStatic, |
| metadataFunction, |
| owner); |
| } |
| |
| String get _prettyName => 'VariableMirror'; |
| |
| TypeMirror get type { |
| return typeMirrorFromRuntimeTypeRepresentation(owner, getMetadata(_type)); |
| } |
| |
| DeclarationMirror get owner => _owner; |
| |
| List<InstanceMirror> get metadata { |
| preserveMetadata(); |
| if (_metadata == null) { |
| _metadata = (_metadataFunction == null) |
| ? const [] : JS('', '#()', _metadataFunction); |
| } |
| return _metadata.map(reflect).toList(); |
| } |
| |
| static int fieldCode(int code) { |
| if (code >= 60 && code <= 64) return code - 59; |
| if (code >= 123 && code <= 126) return code - 117; |
| if (code >= 37 && code <= 43) return code - 27; |
| return 0; |
| } |
| |
| _getField(JsMirror receiver) => receiver._loadField(_jsName); |
| |
| void _setField(JsMirror receiver, Object arg) { |
| if (isFinal) { |
| throw new NoSuchMethodError(this, setterSymbol(simpleName), [arg], null); |
| } |
| receiver._storeField(_jsName, arg); |
| } |
| |
| // TODO(ahe): Implement this method. |
| bool get isConst => throw new UnimplementedError(); |
| } |
| |
| class JsClosureMirror extends JsInstanceMirror implements ClosureMirror { |
| JsClosureMirror(reflectee) |
| : super(reflectee); |
| |
| MethodMirror get function { |
| String cacheName = Primitives.mirrorFunctionCacheName; |
| JsMethodMirror cachedFunction; |
| // TODO(ahe): Restore caching. |
| //= JS('JsMethodMirror|Null', r'#.constructor[#]', reflectee, cacheName); |
| if (cachedFunction != null) return cachedFunction; |
| disableTreeShaking(); |
| // TODO(ahe): What about optional parameters (named or not). |
| String callPrefix = "${JS_GET_NAME("CALL_PREFIX")}\$"; |
| var extractCallName = JS('', r''' |
| function(reflectee) { |
| for (var property in reflectee) { |
| if (# == property.substring(0, #) && |
| property[#] >= '0' && |
| property[#] <= '9') return property; |
| } |
| return null; |
| } |
| ''', callPrefix, callPrefix.length, callPrefix.length, callPrefix.length); |
| String callName = JS('String|Null', '#(#)', extractCallName, reflectee); |
| if (callName == null) { |
| throw new RuntimeError('Cannot find callName on "$reflectee"'); |
| } |
| int parameterCount = int.parse(callName.split(r'$')[1]); |
| if (reflectee is BoundClosure) { |
| var target = BoundClosure.targetOf(reflectee); |
| var self = BoundClosure.selfOf(reflectee); |
| var name = mangledNames[BoundClosure.nameOf(reflectee)]; |
| if (name == null) { |
| throwInvalidReflectionError(name); |
| } |
| cachedFunction = new JsMethodMirror.fromUnmangledName( |
| name, target, false, false); |
| } else { |
| bool isStatic = true; // TODO(ahe): Compute isStatic correctly. |
| var jsFunction = JS('', '#[#]', reflectee, callName); |
| cachedFunction = new JsMethodMirror( |
| s(callName), jsFunction, parameterCount, |
| false, false, isStatic, false, false); |
| } |
| JS('void', r'#.constructor[#] = #', reflectee, cacheName, cachedFunction); |
| return cachedFunction; |
| } |
| |
| InstanceMirror apply(List positionalArguments, |
| [Map<Symbol, dynamic> namedArguments]) { |
| return reflect( |
| Function.apply(reflectee, positionalArguments, namedArguments)); |
| } |
| |
| String toString() => "ClosureMirror on '${Error.safeToString(reflectee)}'"; |
| |
| // TODO(ahe): Implement this method. |
| String get source => throw new UnimplementedError(); |
| |
| // TODO(ahe): Implement this method. |
| InstanceMirror findInContext(Symbol name, {ifAbsent: null}) { |
| throw new UnsupportedError("ClosureMirror.findInContext not yet supported"); |
| } |
| } |
| |
| class JsMethodMirror extends JsDeclarationMirror implements MethodMirror { |
| final _jsFunction; |
| final int _parameterCount; |
| final bool isGetter; |
| final bool isSetter; |
| final bool isStatic; |
| final bool isConstructor; |
| final bool isOperator; |
| DeclarationMirror _owner; |
| List _metadata; |
| TypeMirror _returnType; |
| UnmodifiableListView<ParameterMirror> _parameters; |
| |
| JsMethodMirror(Symbol simpleName, |
| this._jsFunction, |
| this._parameterCount, |
| this.isGetter, |
| this.isSetter, |
| this.isStatic, |
| this.isConstructor, |
| this.isOperator) |
| : super(simpleName); |
| |
| factory JsMethodMirror.fromUnmangledName(String name, |
| jsFunction, |
| bool isStatic, |
| bool isConstructor) { |
| List<String> info = name.split(':'); |
| name = info[0]; |
| bool isOperator = isOperatorName(name); |
| bool isSetter = !isOperator && name.endsWith('='); |
| int requiredParameterCount = 0; |
| int optionalParameterCount = 0; |
| bool isGetter = false; |
| if (info.length == 1) { |
| if (isSetter) { |
| requiredParameterCount = 1; |
| } else { |
| isGetter = true; |
| requiredParameterCount = 0; |
| } |
| } else { |
| requiredParameterCount = int.parse(info[1]); |
| optionalParameterCount = int.parse(info[2]); |
| } |
| return new JsMethodMirror( |
| s(name), jsFunction, requiredParameterCount + optionalParameterCount, |
| isGetter, isSetter, isStatic, isConstructor, isOperator); |
| } |
| |
| String get _prettyName => 'MethodMirror'; |
| |
| List<ParameterMirror> get parameters { |
| if (_parameters != null) return _parameters; |
| metadata; // Compute _parameters as a side-effect of extracting metadata. |
| return _parameters; |
| } |
| |
| bool canInvokeReflectively() { |
| return hasReflectableProperty(_jsFunction); |
| } |
| |
| DeclarationMirror get owner => _owner; |
| |
| TypeMirror get returnType { |
| metadata; // Compute _returnType as a side-effect of extracting metadata. |
| return _returnType; |
| } |
| |
| List<InstanceMirror> get metadata { |
| if (_metadata == null) { |
| var raw = extractMetadata(_jsFunction); |
| var formals = new List(_parameterCount); |
| ReflectionInfo info = new ReflectionInfo(_jsFunction); |
| if (info != null) { |
| assert(_parameterCount |
| == info.requiredParameterCount + info.optionalParameterCount); |
| var functionType = info.functionType; |
| var type; |
| if (functionType is int) { |
| type = new JsFunctionTypeMirror(info.computeFunctionRti(null), this); |
| assert(_parameterCount == type.parameters.length); |
| } else if (isTopLevel) { |
| type = new JsFunctionTypeMirror(info.computeFunctionRti(null), owner); |
| } else { |
| TypeMirror ownerType = owner; |
| JsClassMirror ownerClass = ownerType.originalDeclaration; |
| type = new JsFunctionTypeMirror( |
| info.computeFunctionRti(ownerClass._jsConstructor), |
| owner); |
| } |
| // Constructors aren't reified with their return type. |
| if (isConstructor) { |
| _returnType = owner; |
| } else { |
| _returnType = type.returnType; |
| } |
| int i = 0; |
| bool isNamed = info.areOptionalParametersNamed; |
| for (JsParameterMirror parameter in type.parameters) { |
| var name = info.parameterName(i); |
| List<int> annotations = info.parameterMetadataAnnotations(i); |
| var p; |
| if (i < info.requiredParameterCount) { |
| p = new JsParameterMirror(name, this, parameter._type, |
| metadataList: annotations); |
| } else { |
| var defaultValue = info.defaultValue(i); |
| p = new JsParameterMirror( |
| name, this, parameter._type, metadataList: annotations, |
| isOptional: true, isNamed: isNamed, defaultValue: defaultValue); |
| } |
| formals[i++] = p; |
| } |
| } |
| _parameters = new UnmodifiableListView<ParameterMirror>(formals); |
| _metadata = new UnmodifiableListView(raw.map(reflect)); |
| } |
| return _metadata; |
| } |
| |
| Symbol get constructorName { |
| // TODO(ahe): I believe it is more appropriate to throw an exception or |
| // return null. |
| if (!isConstructor) return const Symbol(''); |
| String name = n(simpleName); |
| int index = name.indexOf('.'); |
| if (index == -1) return const Symbol(''); |
| return s(name.substring(index + 1)); |
| } |
| |
| _invoke(List positionalArguments, Map<Symbol, dynamic> namedArguments) { |
| if (namedArguments != null && !namedArguments.isEmpty) { |
| throw new UnsupportedError('Named arguments are not implemented.'); |
| } |
| if (!isStatic && !isConstructor) { |
| throw new RuntimeError('Cannot invoke instance method without receiver.'); |
| } |
| if (_parameterCount != positionalArguments.length || _jsFunction == null) { |
| // TODO(ahe): What receiver to use? |
| throw new NoSuchMethodError( |
| owner, simpleName, positionalArguments, namedArguments); |
| } |
| // Using JS_CURRENT_ISOLATE() ('$') here is actually correct, although |
| // _jsFunction may not be a property of '$', most static functions do not |
| // care who their receiver is. But to lazy getters, it is important that |
| // 'this' is '$'. |
| return JS('', r'#.apply(#, #)', _jsFunction, JS_CURRENT_ISOLATE(), |
| new List.from(positionalArguments)); |
| } |
| |
| _getField(JsMirror receiver) { |
| if (isGetter) { |
| return _invoke([], null); |
| } else { |
| // TODO(ahe): Closurize method. |
| throw new UnimplementedError('getField on $receiver'); |
| } |
| } |
| |
| _setField(JsMirror receiver, Object arg) { |
| if (isSetter) { |
| return _invoke([arg], null); |
| } else { |
| throw new NoSuchMethodError(this, setterSymbol(simpleName), [], null); |
| } |
| } |
| |
| // Abstract methods are tree-shaken away. |
| bool get isAbstract => false; |
| |
| // TODO(ahe, 14633): This might not be true for all cases. |
| bool get isSynthetic => false; |
| |
| // TODO(ahe): Test this. |
| bool get isRegularMethod => !isGetter && !isSetter && !isConstructor; |
| |
| // TODO(ahe): Implement this method. |
| bool get isConstConstructor => throw new UnimplementedError(); |
| |
| // TODO(ahe): Implement this method. |
| bool get isGenerativeConstructor => throw new UnimplementedError(); |
| |
| // TODO(ahe): Implement this method. |
| bool get isRedirectingConstructor => throw new UnimplementedError(); |
| |
| // TODO(ahe): Implement this method. |
| bool get isFactoryConstructor => throw new UnimplementedError(); |
| |
| // TODO(ahe): Implement this method. |
| String get source => throw new UnimplementedError(); |
| } |
| |
| class JsParameterMirror extends JsDeclarationMirror implements ParameterMirror { |
| final DeclarationMirror owner; |
| // A JS object representing the type. |
| final _type; |
| |
| final bool isOptional; |
| |
| final bool isNamed; |
| |
| final int _defaultValue; |
| |
| final List<int> metadataList; |
| |
| JsParameterMirror(String unmangledName, |
| this.owner, |
| this._type, |
| {this.metadataList: const <int>[], |
| this.isOptional: false, |
| this.isNamed: false, |
| defaultValue}) |
| : _defaultValue = defaultValue, |
| super(s(unmangledName)); |
| |
| String get _prettyName => 'ParameterMirror'; |
| |
| TypeMirror get type { |
| return typeMirrorFromRuntimeTypeRepresentation(owner, _type); |
| } |
| |
| // Only true for static fields, never for a parameter. |
| bool get isStatic => false; |
| |
| // TODO(ahe): Implement this. |
| bool get isFinal => false; |
| |
| // TODO(ahe): Implement this. |
| bool get isConst => false; |
| |
| bool get hasDefaultValue => _defaultValue != null; |
| |
| get defaultValue { |
| return hasDefaultValue ? reflect(getMetadata(_defaultValue)) : null; |
| } |
| |
| List<InstanceMirror> get metadata { |
| preserveMetadata(); |
| return metadataList.map((int i) => reflect(getMetadata(i))).toList(); |
| } |
| } |
| |
| class JsTypedefMirror extends JsDeclarationMirror implements TypedefMirror { |
| final String _mangledName; |
| JsFunctionTypeMirror referent; |
| |
| JsTypedefMirror(Symbol simpleName, this._mangledName, _typeData) |
| : super(simpleName) { |
| referent = new JsFunctionTypeMirror(_typeData, this); |
| } |
| |
| JsFunctionTypeMirror get value => referent; |
| |
| String get _prettyName => 'TypedefMirror'; |
| |
| bool get hasReflectedType => throw new UnimplementedError(); |
| |
| Type get reflectedType => throw new UnimplementedError(); |
| |
| // TODO(ahe): Implement this method. |
| List<TypeVariableMirror> get typeVariables => throw new UnimplementedError(); |
| |
| // TODO(ahe): Implement this method. |
| List<TypeMirror> get typeArguments => throw new UnimplementedError(); |
| |
| // TODO(ahe): Implement this method. |
| bool get isOriginalDeclaration => throw new UnimplementedError(); |
| |
| // TODO(ahe): Implement this method. |
| TypeMirror get originalDeclaration => throw new UnimplementedError(); |
| |
| // TODO(ahe): Implement this method. |
| DeclarationMirror get owner => throw new UnimplementedError(); |
| |
| // TODO(ahe): Implement this method. |
| List<InstanceMirror> get metadata => throw new UnimplementedError(); |
| |
| bool isSubtypeOf(TypeMirror other) => throw new UnimplementedError(); |
| bool isAssignableTo(TypeMirror other) => throw new UnimplementedError(); |
| } |
| |
| // TODO(ahe): Remove this class when API is updated. |
| class BrokenClassMirror { |
| bool get hasReflectedType => throw new UnimplementedError(); |
| Type get reflectedType => throw new UnimplementedError(); |
| ClassMirror get superclass => throw new UnimplementedError(); |
| List<ClassMirror> get superinterfaces => throw new UnimplementedError(); |
| Map<Symbol, DeclarationMirror> get declarations |
| => throw new UnimplementedError(); |
| Map<Symbol, MethodMirror> get instanceMembers |
| => throw new UnimplementedError(); |
| Map<Symbol, MethodMirror> get staticMembers => throw new UnimplementedError(); |
| ClassMirror get mixin => throw new UnimplementedError(); |
| InstanceMirror newInstance( |
| Symbol constructorName, |
| List positionalArguments, |
| [Map<Symbol, dynamic> namedArguments]) => throw new UnimplementedError(); |
| InstanceMirror invoke(Symbol memberName, |
| List positionalArguments, |
| [Map<Symbol, dynamic> namedArguments]) |
| => throw new UnimplementedError(); |
| InstanceMirror getField(Symbol fieldName) => throw new UnimplementedError(); |
| InstanceMirror setField(Symbol fieldName, Object value) |
| => throw new UnimplementedError(); |
| List<TypeVariableMirror> get typeVariables => throw new UnimplementedError(); |
| List<TypeMirror> get typeArguments => throw new UnimplementedError(); |
| TypeMirror get originalDeclaration => throw new UnimplementedError(); |
| Symbol get simpleName => throw new UnimplementedError(); |
| Symbol get qualifiedName => throw new UnimplementedError(); |
| bool get isPrivate => throw new UnimplementedError(); |
| bool get isTopLevel => throw new UnimplementedError(); |
| SourceLocation get location => throw new UnimplementedError(); |
| List<InstanceMirror> get metadata => throw new UnimplementedError(); |
| } |
| |
| class JsFunctionTypeMirror extends BrokenClassMirror |
| implements FunctionTypeMirror { |
| final _typeData; |
| String _cachedToString; |
| TypeMirror _cachedReturnType; |
| UnmodifiableListView<ParameterMirror> _cachedParameters; |
| DeclarationMirror owner; |
| |
| JsFunctionTypeMirror(this._typeData, this.owner); |
| |
| bool get _hasReturnType => JS('bool', '"ret" in #', _typeData); |
| get _returnType => JS('', '#.ret', _typeData); |
| |
| bool get _isVoid => JS('bool', '!!#.void', _typeData); |
| |
| bool get _hasArguments => JS('bool', '"args" in #', _typeData); |
| List get _arguments => JS('JSExtendableArray', '#.args', _typeData); |
| |
| bool get _hasOptionalArguments => JS('bool', '"opt" in #', _typeData); |
| List get _optionalArguments => JS('JSExtendableArray', '#.opt', _typeData); |
| |
| bool get _hasNamedArguments => JS('bool', '"named" in #', _typeData); |
| get _namedArguments => JS('=Object', '#.named', _typeData); |
| bool get isOriginalDeclaration => true; |
| |
| bool get isAbstract => false; |
| |
| TypeMirror get returnType { |
| if (_cachedReturnType != null) return _cachedReturnType; |
| if (_isVoid) return _cachedReturnType = JsMirrorSystem._voidType; |
| if (!_hasReturnType) return _cachedReturnType = JsMirrorSystem._dynamicType; |
| return _cachedReturnType = |
| typeMirrorFromRuntimeTypeRepresentation(owner, _returnType); |
| } |
| |
| List<ParameterMirror> get parameters { |
| if (_cachedParameters != null) return _cachedParameters; |
| List result = []; |
| int parameterCount = 0; |
| if (_hasArguments) { |
| for (var type in _arguments) { |
| result.add( |
| new JsParameterMirror('argument${parameterCount++}', this, type)); |
| } |
| } |
| if (_hasOptionalArguments) { |
| for (var type in _optionalArguments) { |
| result.add( |
| new JsParameterMirror('argument${parameterCount++}', this, type)); |
| } |
| } |
| if (_hasNamedArguments) { |
| for (var name in extractKeys(_namedArguments)) { |
| var type = JS('', '#[#]', _namedArguments, name); |
| result.add(new JsParameterMirror(name, this, type)); |
| } |
| } |
| return _cachedParameters = new UnmodifiableListView<ParameterMirror>( |
| result); |
| } |
| |
| String _unmangleIfPreserved(String mangled) { |
| String result = unmangleGlobalNameIfPreservedAnyways(mangled); |
| if (result != null) return result; |
| return mangled; |
| } |
| |
| String toString() { |
| if (_cachedToString != null) return _cachedToString; |
| var s = "FunctionTypeMirror on '("; |
| var sep = ''; |
| if (_hasArguments) { |
| for (var argument in _arguments) { |
| s += sep; |
| s += _unmangleIfPreserved(runtimeTypeToString(argument)); |
| sep = ', '; |
| } |
| } |
| if (_hasOptionalArguments) { |
| s += '$sep['; |
| sep = ''; |
| for (var argument in _optionalArguments) { |
| s += sep; |
| s += _unmangleIfPreserved(runtimeTypeToString(argument)); |
| sep = ', '; |
| } |
| s += ']'; |
| } |
| if (_hasNamedArguments) { |
| s += '$sep{'; |
| sep = ''; |
| for (var name in extractKeys(_namedArguments)) { |
| s += sep; |
| s += '$name: '; |
| s += _unmangleIfPreserved( |
| runtimeTypeToString(JS('', '#[#]', _namedArguments, name))); |
| sep = ', '; |
| } |
| s += '}'; |
| } |
| s += ') -> '; |
| if (_isVoid) { |
| s += 'void'; |
| } else if (_hasReturnType) { |
| s += _unmangleIfPreserved(runtimeTypeToString(_returnType)); |
| } else { |
| s += 'dynamic'; |
| } |
| return _cachedToString = "$s'"; |
| } |
| |
| bool isSubclassOf(ClassMirror other) => false; |
| |
| bool isSubtypeOf(TypeMirror other) => throw new UnimplementedError(); |
| |
| bool isAssignableTo(TypeMirror other) => throw new UnimplementedError(); |
| |
| // TODO(ahe): Implement this method. |
| MethodMirror get callMethod => throw new UnimplementedError(); |
| } |
| |
| int findTypeVariableIndex(List<TypeVariableMirror> typeVariables, String name) { |
| for (int i = 0; i < typeVariables.length; i++) { |
| if (typeVariables[i].simpleName == s(name)) { |
| return i; |
| } |
| } |
| throw new ArgumentError('Type variable not present in list.'); |
| } |
| |
| TypeMirror typeMirrorFromRuntimeTypeRepresentation( |
| DeclarationMirror owner, |
| var /*int|List|JsFunction*/ type) { |
| // TODO(ahe): This method might benefit from using convertRtiToRuntimeType |
| // instead of working on strings. |
| ClassMirror ownerClass; |
| DeclarationMirror context = owner; |
| while (context != null) { |
| if (context is ClassMirror) { |
| ownerClass = context; |
| break; |
| } |
| // TODO(ahe): Get type parameters and arguments from typedefs. |
| if (context is TypedefMirror) break; |
| context = context.owner; |
| } |
| |
| String representation; |
| if (type == null) { |
| return JsMirrorSystem._dynamicType; |
| } else if (ownerClass == null) { |
| representation = runtimeTypeToString(type); |
| } else if (ownerClass.isOriginalDeclaration) { |
| if (type is num) { |
| // [type] represents a type variable so in the context of an original |
| // declaration the corresponding type variable should be returned. |
| TypeVariable typeVariable = getMetadata(type); |
| List<TypeVariableMirror> typeVariables = ownerClass.typeVariables; |
| int index = findTypeVariableIndex(typeVariables, typeVariable.name); |
| return typeVariables[index]; |
| } else { |
| // Nested type variables will be retrieved lazily (the integer |
| // representation is kept in the string) so they are not processed here. |
| representation = runtimeTypeToString(type); |
| } |
| } else { |
| TypeMirror getTypeArgument(int index) { |
| TypeVariable typeVariable = getMetadata(index); |
| int variableIndex = |
| findTypeVariableIndex(ownerClass.typeVariables, typeVariable.name); |
| return ownerClass.typeArguments[variableIndex]; |
| } |
| |
| if (type is num) { |
| // [type] represents a type variable used as type argument for example |
| // the type argument of Bar: class Foo<T> extends Bar<T> {} |
| TypeMirror typeArgument = getTypeArgument(type); |
| if (typeArgument is JsTypeVariableMirror) |
| return typeArgument; |
| } |
| String substituteTypeVariable(int index) { |
| var typeArgument = getTypeArgument(index); |
| if (typeArgument is JsTypeVariableMirror) { |
| return '${typeArgument._metadataIndex}'; |
| } |
| if (typeArgument is! JsClassMirror && |
| typeArgument is! JsTypeBoundClassMirror) { |
| if (typeArgument == JsMirrorSystem._dynamicType) { |
| return 'dynamic'; |
| } else if (typeArgument == JsMirrorSystem._voidType) { |
| return 'void'; |
| } else { |
| // TODO(ahe): This case shouldn't happen. |
| return 'dynamic'; |
| } |
| } |
| return typeArgument._mangledName; |
| } |
| representation = |
| runtimeTypeToString(type, onTypeVariable: substituteTypeVariable); |
| } |
| if (representation != null) { |
| return reflectClassByMangledName( |
| getMangledTypeName(createRuntimeType(representation))); |
| } |
| return reflectClass(Function); |
| } |
| |
| Symbol computeQualifiedName(DeclarationMirror owner, Symbol simpleName) { |
| if (owner == null) return simpleName; |
| String ownerName = n(owner.qualifiedName); |
| return s('$ownerName.${n(simpleName)}'); |
| } |
| |
| List extractMetadata(victim) { |
| preserveMetadata(); |
| var metadataFunction = JS('', '#["@"]', victim); |
| if (metadataFunction != null) return JS('', '#()', metadataFunction); |
| if (JS('bool', 'typeof # != "function"', victim)) return const []; |
| if (JS('bool', '# in #', r'$metadataIndex', victim)) { |
| return JSArray.markFixedList( |
| JS('JSExtendableArray', |
| r'#.$reflectionInfo.splice(#.$metadataIndex)', victim, victim)) |
| .map((int i) => getMetadata(i)).toList(); |
| } |
| String source = JS('String', 'Function.prototype.toString.call(#)', victim); |
| int index = source.lastIndexOf(new RegExp('"[0-9,]*";?[ \n\r]*}')); |
| if (index == -1) return const []; |
| index++; |
| int endQuote = source.indexOf('"', index); |
| return source.substring(index, endQuote).split(',').map(int.parse).map( |
| (int i) => getMetadata(i)).toList(); |
| } |
| |
| void parseCompactFieldSpecification( |
| JsDeclarationMirror owner, |
| fieldSpecification, |
| bool isStatic, |
| List<Mirror> result) { |
| List fieldsMetadata = null; |
| List<String> fields; |
| if (fieldSpecification is List) { |
| fields = splitFields(fieldSpecification[0], ','); |
| fieldsMetadata = fieldSpecification.sublist(1); |
| } else if (fieldSpecification is String) { |
| fields = splitFields(fieldSpecification, ','); |
| } else { |
| fields = []; |
| } |
| int fieldNumber = 0; |
| for (String field in fields) { |
| var metadata; |
| if (fieldsMetadata != null) { |
| metadata = fieldsMetadata[fieldNumber++]; |
| } |
| var mirror = new JsVariableMirror.from(field, metadata, owner, isStatic); |
| if (mirror != null) { |
| result.add(mirror); |
| } |
| } |
| } |
| |
| /// Similar to [String.split], but returns an empty list if [string] is empty. |
| List<String> splitFields(String string, Pattern pattern) { |
| if (string.isEmpty) return <String>[]; |
| return string.split(pattern); |
| } |
| |
| bool isOperatorName(String name) { |
| switch (name) { |
| case '==': |
| case '[]': |
| case '*': |
| case '/': |
| case '%': |
| case '~/': |
| case '+': |
| case '<<': |
| case '>>': |
| case '>=': |
| case '>': |
| case '<=': |
| case '<': |
| case '&': |
| case '^': |
| case '|': |
| case '-': |
| case 'unary-': |
| case '[]=': |
| case '~': |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| /// Returns true if the key represent ancillary reflection data, that is, not a |
| /// method. |
| bool isReflectiveDataInPrototype(String key) { |
| if (key == JS_GET_NAME('CLASS_DESCRIPTOR_PROPERTY') || |
| key == METHODS_WITH_OPTIONAL_ARGUMENTS) { |
| return true; |
| } |
| String firstChar = key[0]; |
| return firstChar == '*' || firstChar == '+'; |
| } |
| |
| bool isNoSuchMethodStub(var jsFunction) { |
| return JS('bool', r'#.$reflectable == 2', jsFunction); |
| } |
| |
| class NoSuchStaticMethodError extends Error implements NoSuchMethodError { |
| static const int MISSING_CONSTRUCTOR = 0; |
| final ClassMirror _cls; |
| final Symbol _name; |
| final List _positionalArguments; |
| final Map<Symbol, dynamic> _namedArguments; |
| final int _kind; |
| |
| NoSuchStaticMethodError.missingConstructor( |
| this._cls, |
| this._name, |
| this._positionalArguments, |
| this._namedArguments) |
| : _kind = MISSING_CONSTRUCTOR; |
| |
| String toString() { |
| switch(_kind) { |
| case MISSING_CONSTRUCTOR: |
| return |
| "NoSuchMethodError: No constructor named '${n(_name)}' in class" |
| " '${n(_cls.qualifiedName)}'."; |
| default: |
| return 'NoSuchMethodError'; |
| } |
| } |
| } |
| |
| Symbol getSymbol(String name, LibraryMirror library) { |
| if (_isPublicSymbol(name)) { |
| return new _symbol_dev.Symbol.validated(name); |
| } |
| if (library == null) { |
| throw new ArgumentError( |
| "Library required for private symbol name: $name"); |
| } |
| if (!_symbol_dev.Symbol.isValidSymbol(name)) { |
| throw new ArgumentError("Not a valid symbol name: $name"); |
| } |
| throw new UnimplementedError( |
| "MirrorSystem.getSymbol not implemented for private names"); |
| } |
| |
| bool _isPublicSymbol(String name) { |
| // A symbol is public if it doesn't start with '_' and it doesn't |
| // have a part (following a '.') that starts with '_'. |
| const int UNDERSCORE = 0x5f; |
| if (name.isEmpty) return true; |
| int index = -1; |
| do { |
| if (name.codeUnitAt(index + 1) == UNDERSCORE) return false; |
| index = name.indexOf('.', index + 1); |
| } while (index >= 0 && index + 1 < name.length); |
| return true; |
| } |