| // Copyright (c) 2017, 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 dart2js.js_model.elements; | |
| import '../common/names.dart' show Names; | |
| import '../elements/entities.dart'; | |
| import '../elements/indexed.dart'; | |
| import '../elements/names.dart'; | |
| import '../elements/types.dart'; | |
| import '../universe/class_set.dart' show ClassHierarchyNodesMapKey; | |
| /// Map from 'frontend' to 'backend' elements. | |
| /// | |
| /// Frontend elements are what we read in, these typically represents concepts | |
| /// in Dart. Backend elements are what we generate, these may include elements | |
| /// that do not correspond to a Dart concept, such as closure classes. | |
| /// | |
| /// Querying for the frontend element for a backend-only element throws an | |
| /// exception. | |
| abstract class JsToFrontendMap { | |
| LibraryEntity toBackendLibrary(LibraryEntity library); | |
| ClassEntity toBackendClass(ClassEntity cls); | |
| /// Returns the backend member corresponding to [member]. If a member isn't | |
| /// live, it doesn't have a corresponding backend member and `null` is | |
| /// returned instead. | |
| MemberEntity toBackendMember(MemberEntity member); | |
| DartType toBackendType(DartType type); | |
| Set<LibraryEntity> toBackendLibrarySet(Iterable<LibraryEntity> set) { | |
| return set.map(toBackendLibrary).toSet(); | |
| } | |
| Set<ClassEntity> toBackendClassSet(Iterable<ClassEntity> set) { | |
| // TODO(johnniwinther): Filter unused classes. | |
| return set.map(toBackendClass).toSet(); | |
| } | |
| Set<MemberEntity> toBackendMemberSet(Iterable<MemberEntity> set) { | |
| return set.map(toBackendMember).where((MemberEntity member) { | |
| // Members that are not live don't have a corresponding backend member. | |
| return member != null; | |
| }).toSet(); | |
| } | |
| Set<FunctionEntity> toBackendFunctionSet(Iterable<FunctionEntity> set) { | |
| Set<FunctionEntity> newSet = new Set<FunctionEntity>(); | |
| for (FunctionEntity element in set) { | |
| FunctionEntity backendFunction = toBackendMember(element); | |
| if (backendFunction != null) { | |
| // Members that are not live don't have a corresponding backend member. | |
| newSet.add(backendFunction); | |
| } | |
| } | |
| return newSet; | |
| } | |
| Map<LibraryEntity, V> toBackendLibraryMap<V>( | |
| Map<LibraryEntity, V> map, V convert(V value)) { | |
| return convertMap(map, toBackendLibrary, convert); | |
| } | |
| Map<ClassEntity, V> toBackendClassMap<V>( | |
| Map<ClassEntity, V> map, V convert(V value)) { | |
| return convertMap(map, toBackendClass, convert); | |
| } | |
| Map<MemberEntity, V> toBackendMemberMap<V>( | |
| Map<MemberEntity, V> map, V convert(V value)) { | |
| return convertMap(map, toBackendMember, convert); | |
| } | |
| } | |
| E identity<E>(E element) => element; | |
| Map<K, V> convertMap<K, V>( | |
| Map<K, V> map, K convertKey(K key), V convertValue(V value)) { | |
| Map<K, V> newMap = <K, V>{}; | |
| map.forEach((K key, V value) { | |
| K newKey = convertKey(key); | |
| V newValue = convertValue(value); | |
| if (newKey != null && newValue != null) { | |
| // Entities that are not used don't have a corresponding backend entity. | |
| newMap[newKey] = newValue; | |
| } | |
| }); | |
| return newMap; | |
| } | |
| abstract class JsToFrontendMapBase extends JsToFrontendMap { | |
| DartType toBackendType(DartType type) => | |
| type == null ? null : const TypeConverter().visit(type, _toBackendEntity); | |
| Entity _toBackendEntity(Entity entity) { | |
| if (entity is ClassEntity) return toBackendClass(entity); | |
| assert(entity is TypeVariableEntity); | |
| return toBackendTypeVariable(entity); | |
| } | |
| TypeVariableEntity toBackendTypeVariable(TypeVariableEntity typeVariable); | |
| } | |
| // TODO(johnniwinther): Merge this with [JsKernelToElementMap]. | |
| class JsElementCreatorMixin { | |
| IndexedLibrary createLibrary(String name, Uri canonicalUri) { | |
| return new JLibrary(name, canonicalUri); | |
| } | |
| IndexedClass createClass(LibraryEntity library, String name, | |
| {bool isAbstract}) { | |
| return new JClass(library, name, isAbstract: isAbstract); | |
| } | |
| IndexedTypedef createTypedef(LibraryEntity library, String name) { | |
| return new JTypedef(library, name); | |
| } | |
| TypeVariableEntity createTypeVariable( | |
| Entity typeDeclaration, String name, int index) { | |
| return new JTypeVariable(typeDeclaration, name, index); | |
| } | |
| IndexedConstructor createGenerativeConstructor(ClassEntity enclosingClass, | |
| Name name, ParameterStructure parameterStructure, | |
| {bool isExternal, bool isConst}) { | |
| return new JGenerativeConstructor(enclosingClass, name, parameterStructure, | |
| isExternal: isExternal, isConst: isConst); | |
| } | |
| IndexedConstructor createFactoryConstructor(ClassEntity enclosingClass, | |
| Name name, ParameterStructure parameterStructure, | |
| {bool isExternal, bool isConst, bool isFromEnvironmentConstructor}) { | |
| return new JFactoryConstructor(enclosingClass, name, parameterStructure, | |
| isExternal: isExternal, | |
| isConst: isConst, | |
| isFromEnvironmentConstructor: isFromEnvironmentConstructor); | |
| } | |
| JConstructorBody createConstructorBody(ConstructorEntity constructor) { | |
| return new JConstructorBody(constructor); | |
| } | |
| JGeneratorBody createGeneratorBody( | |
| FunctionEntity function, DartType elementType) { | |
| return new JGeneratorBody(function, elementType); | |
| } | |
| IndexedFunction createGetter(LibraryEntity library, | |
| ClassEntity enclosingClass, Name name, AsyncMarker asyncMarker, | |
| {bool isStatic, bool isExternal, bool isAbstract}) { | |
| return new JGetter(library, enclosingClass, name, asyncMarker, | |
| isStatic: isStatic, isExternal: isExternal, isAbstract: isAbstract); | |
| } | |
| IndexedFunction createMethod( | |
| LibraryEntity library, | |
| ClassEntity enclosingClass, | |
| Name name, | |
| ParameterStructure parameterStructure, | |
| AsyncMarker asyncMarker, | |
| {bool isStatic, | |
| bool isExternal, | |
| bool isAbstract}) { | |
| return new JMethod( | |
| library, enclosingClass, name, parameterStructure, asyncMarker, | |
| isStatic: isStatic, isExternal: isExternal, isAbstract: isAbstract); | |
| } | |
| IndexedFunction createSetter( | |
| LibraryEntity library, ClassEntity enclosingClass, Name name, | |
| {bool isStatic, bool isExternal, bool isAbstract}) { | |
| return new JSetter(library, enclosingClass, name, | |
| isStatic: isStatic, isExternal: isExternal, isAbstract: isAbstract); | |
| } | |
| IndexedField createField( | |
| LibraryEntity library, ClassEntity enclosingClass, Name name, | |
| {bool isStatic, bool isAssignable, bool isConst}) { | |
| return new JField(library, enclosingClass, name, | |
| isStatic: isStatic, isAssignable: isAssignable, isConst: isConst); | |
| } | |
| LibraryEntity convertLibrary(IndexedLibrary library) { | |
| return createLibrary(library.name, library.canonicalUri); | |
| } | |
| ClassEntity convertClass(LibraryEntity library, IndexedClass cls) { | |
| return createClass(library, cls.name, isAbstract: cls.isAbstract); | |
| } | |
| TypedefEntity convertTypedef(LibraryEntity library, IndexedTypedef typedef) { | |
| return createTypedef(library, typedef.name); | |
| } | |
| MemberEntity convertMember( | |
| LibraryEntity library, ClassEntity cls, IndexedMember member) { | |
| Name memberName = new Name(member.memberName.text, library, | |
| isSetter: member.memberName.isSetter); | |
| if (member.isField) { | |
| IndexedField field = member; | |
| return createField(library, cls, memberName, | |
| isStatic: field.isStatic, | |
| isAssignable: field.isAssignable, | |
| isConst: field.isConst); | |
| } else if (member.isConstructor) { | |
| IndexedConstructor constructor = member; | |
| if (constructor.isFactoryConstructor) { | |
| // TODO(redemption): This should be a JFunction. | |
| return createFactoryConstructor( | |
| cls, memberName, constructor.parameterStructure, | |
| isExternal: constructor.isExternal, | |
| isConst: constructor.isConst, | |
| isFromEnvironmentConstructor: | |
| constructor.isFromEnvironmentConstructor); | |
| } else { | |
| return createGenerativeConstructor( | |
| cls, memberName, constructor.parameterStructure, | |
| isExternal: constructor.isExternal, isConst: constructor.isConst); | |
| } | |
| } else if (member.isGetter) { | |
| IndexedFunction getter = member; | |
| return createGetter(library, cls, memberName, getter.asyncMarker, | |
| isStatic: getter.isStatic, | |
| isExternal: getter.isExternal, | |
| isAbstract: getter.isAbstract); | |
| } else if (member.isSetter) { | |
| IndexedFunction setter = member; | |
| return createSetter(library, cls, memberName, | |
| isStatic: setter.isStatic, | |
| isExternal: setter.isExternal, | |
| isAbstract: setter.isAbstract); | |
| } else { | |
| IndexedFunction function = member; | |
| return createMethod(library, cls, memberName, function.parameterStructure, | |
| function.asyncMarker, | |
| isStatic: function.isStatic, | |
| isExternal: function.isExternal, | |
| isAbstract: function.isAbstract); | |
| } | |
| } | |
| } | |
| typedef Entity EntityConverter(Entity cls); | |
| class TypeConverter implements DartTypeVisitor<DartType, EntityConverter> { | |
| const TypeConverter(); | |
| @override | |
| DartType visit(DartType type, EntityConverter converter) { | |
| return type.accept(this, converter); | |
| } | |
| List<DartType> visitList(List<DartType> types, EntityConverter converter) { | |
| List<DartType> list = <DartType>[]; | |
| for (DartType type in types) { | |
| list.add(visit(type, converter)); | |
| } | |
| return list; | |
| } | |
| @override | |
| DartType visitDynamicType(DynamicType type, EntityConverter converter) { | |
| return const DynamicType(); | |
| } | |
| @override | |
| DartType visitInterfaceType(InterfaceType type, EntityConverter converter) { | |
| return new InterfaceType( | |
| converter(type.element), visitList(type.typeArguments, converter)); | |
| } | |
| @override | |
| DartType visitTypedefType(TypedefType type, EntityConverter converter) { | |
| return new TypedefType( | |
| converter(type.element), | |
| visitList(type.typeArguments, converter), | |
| visit(type.unaliased, converter)); | |
| } | |
| @override | |
| DartType visitFunctionType(FunctionType type, EntityConverter converter) { | |
| return new FunctionType( | |
| visit(type.returnType, converter), | |
| visitList(type.parameterTypes, converter), | |
| visitList(type.optionalParameterTypes, converter), | |
| type.namedParameters, | |
| visitList(type.namedParameterTypes, converter), | |
| type.typeVariables); | |
| } | |
| @override | |
| DartType visitTypeVariableType( | |
| TypeVariableType type, EntityConverter converter) { | |
| return new TypeVariableType(converter(type.element)); | |
| } | |
| @override | |
| DartType visitFunctionTypeVariable( | |
| FunctionTypeVariable type, EntityConverter converter) { | |
| return type; | |
| } | |
| @override | |
| DartType visitVoidType(VoidType type, EntityConverter converter) { | |
| return const VoidType(); | |
| } | |
| @override | |
| DartType visitFutureOrType(FutureOrType type, EntityConverter converter) { | |
| return new FutureOrType(visit(type.typeArgument, converter)); | |
| } | |
| } | |
| const String jsElementPrefix = 'j:'; | |
| class JLibrary extends IndexedLibrary { | |
| final String name; | |
| final Uri canonicalUri; | |
| JLibrary(this.name, this.canonicalUri); | |
| String toString() => '${jsElementPrefix}library($name)'; | |
| } | |
| class JClass extends IndexedClass with ClassHierarchyNodesMapKey { | |
| final JLibrary library; | |
| final String name; | |
| final bool isAbstract; | |
| JClass(this.library, this.name, {this.isAbstract}); | |
| @override | |
| bool get isClosure => false; | |
| String toString() => '${jsElementPrefix}class($name)'; | |
| } | |
| class JTypedef extends IndexedTypedef { | |
| final JLibrary library; | |
| final String name; | |
| JTypedef(this.library, this.name); | |
| String toString() => '${jsElementPrefix}typedef($name)'; | |
| } | |
| abstract class JMember extends IndexedMember { | |
| final JLibrary library; | |
| final JClass enclosingClass; | |
| final Name _name; | |
| final bool _isStatic; | |
| JMember(this.library, this.enclosingClass, this._name, {bool isStatic: false}) | |
| : _isStatic = isStatic; | |
| String get name => _name.text; | |
| Name get memberName => _name; | |
| @override | |
| bool get isAssignable => false; | |
| @override | |
| bool get isConst => false; | |
| @override | |
| bool get isAbstract => false; | |
| @override | |
| bool get isSetter => false; | |
| @override | |
| bool get isGetter => false; | |
| @override | |
| bool get isFunction => false; | |
| @override | |
| bool get isField => false; | |
| @override | |
| bool get isConstructor => false; | |
| @override | |
| bool get isInstanceMember => enclosingClass != null && !_isStatic; | |
| @override | |
| bool get isStatic => enclosingClass != null && _isStatic; | |
| @override | |
| bool get isTopLevel => enclosingClass == null; | |
| String get _kind; | |
| String toString() => '${jsElementPrefix}$_kind' | |
| '(${enclosingClass != null ? '${enclosingClass.name}.' : ''}$name)'; | |
| } | |
| abstract class JFunction extends JMember | |
| implements FunctionEntity, IndexedFunction { | |
| final ParameterStructure parameterStructure; | |
| final bool isExternal; | |
| final AsyncMarker asyncMarker; | |
| JFunction(JLibrary library, JClass enclosingClass, Name name, | |
| this.parameterStructure, this.asyncMarker, | |
| {bool isStatic: false, this.isExternal: false}) | |
| : super(library, enclosingClass, name, isStatic: isStatic); | |
| } | |
| abstract class JConstructor extends JFunction | |
| implements ConstructorEntity, IndexedConstructor { | |
| final bool isConst; | |
| JConstructor( | |
| JClass enclosingClass, Name name, ParameterStructure parameterStructure, | |
| {bool isExternal, this.isConst}) | |
| : super(enclosingClass.library, enclosingClass, name, parameterStructure, | |
| AsyncMarker.SYNC, | |
| isExternal: isExternal); | |
| @override | |
| bool get isConstructor => true; | |
| @override | |
| bool get isInstanceMember => false; | |
| @override | |
| bool get isStatic => false; | |
| @override | |
| bool get isTopLevel => false; | |
| @override | |
| bool get isFromEnvironmentConstructor => false; | |
| String get _kind => 'constructor'; | |
| } | |
| class JGenerativeConstructor extends JConstructor { | |
| JGenerativeConstructor( | |
| JClass enclosingClass, Name name, ParameterStructure parameterStructure, | |
| {bool isExternal, bool isConst}) | |
| : super(enclosingClass, name, parameterStructure, | |
| isExternal: isExternal, isConst: isConst); | |
| @override | |
| bool get isFactoryConstructor => false; | |
| @override | |
| bool get isGenerativeConstructor => true; | |
| } | |
| class JFactoryConstructor extends JConstructor { | |
| @override | |
| final bool isFromEnvironmentConstructor; | |
| JFactoryConstructor( | |
| JClass enclosingClass, Name name, ParameterStructure parameterStructure, | |
| {bool isExternal, bool isConst, this.isFromEnvironmentConstructor}) | |
| : super(enclosingClass, name, parameterStructure, | |
| isExternal: isExternal, isConst: isConst); | |
| @override | |
| bool get isFactoryConstructor => true; | |
| @override | |
| bool get isGenerativeConstructor => false; | |
| } | |
| class JConstructorBody extends JFunction implements ConstructorBodyEntity { | |
| final ConstructorEntity constructor; | |
| JConstructorBody(this.constructor) | |
| : super( | |
| constructor.library, | |
| constructor.enclosingClass, | |
| constructor.memberName, | |
| constructor.parameterStructure, | |
| AsyncMarker.SYNC, | |
| isStatic: false, | |
| isExternal: false); | |
| String get _kind => 'constructor_body'; | |
| } | |
| class JMethod extends JFunction { | |
| final bool isAbstract; | |
| JMethod(JLibrary library, JClass enclosingClass, Name name, | |
| ParameterStructure parameterStructure, AsyncMarker asyncMarker, | |
| {bool isStatic, bool isExternal, this.isAbstract}) | |
| : super(library, enclosingClass, name, parameterStructure, asyncMarker, | |
| isStatic: isStatic, isExternal: isExternal); | |
| @override | |
| bool get isFunction => true; | |
| String get _kind => 'method'; | |
| } | |
| class JGeneratorBody extends JFunction { | |
| final FunctionEntity function; | |
| final DartType elementType; | |
| final int hashCode; | |
| JGeneratorBody(this.function, this.elementType) | |
| : hashCode = function.hashCode + 1, // Hack stabilize sort order. | |
| super(function.library, function.enclosingClass, function.memberName, | |
| function.parameterStructure, function.asyncMarker, | |
| isStatic: function.isStatic, isExternal: false); | |
| String get _kind => 'generator_body'; | |
| } | |
| class JGetter extends JFunction { | |
| final bool isAbstract; | |
| JGetter(JLibrary library, JClass enclosingClass, Name name, | |
| AsyncMarker asyncMarker, | |
| {bool isStatic, bool isExternal, this.isAbstract}) | |
| : super(library, enclosingClass, name, const ParameterStructure.getter(), | |
| asyncMarker, | |
| isStatic: isStatic, isExternal: isExternal); | |
| @override | |
| bool get isGetter => true; | |
| String get _kind => 'getter'; | |
| } | |
| class JSetter extends JFunction { | |
| final bool isAbstract; | |
| JSetter(JLibrary library, JClass enclosingClass, Name name, | |
| {bool isStatic, bool isExternal, this.isAbstract}) | |
| : super(library, enclosingClass, name, const ParameterStructure.setter(), | |
| AsyncMarker.SYNC, | |
| isStatic: isStatic, isExternal: isExternal); | |
| @override | |
| bool get isAssignable => true; | |
| @override | |
| bool get isSetter => true; | |
| String get _kind => 'setter'; | |
| } | |
| class JField extends JMember implements FieldEntity, IndexedField { | |
| final bool isAssignable; | |
| final bool isConst; | |
| JField(JLibrary library, JClass enclosingClass, Name name, | |
| {bool isStatic, this.isAssignable, this.isConst}) | |
| : super(library, enclosingClass, name, isStatic: isStatic); | |
| @override | |
| bool get isField => true; | |
| String get _kind => 'field'; | |
| } | |
| class JClosureCallMethod extends JMethod { | |
| JClosureCallMethod(ClassEntity enclosingClass, | |
| ParameterStructure parameterStructure, AsyncMarker asyncMarker) | |
| : super(enclosingClass.library, enclosingClass, Names.call, | |
| parameterStructure, asyncMarker, | |
| isStatic: false, isExternal: false, isAbstract: false); | |
| String get _kind => 'closure_call'; | |
| } | |
| /// A method that returns the signature of the Dart closure/tearoff that this | |
| /// method's parent class is representing. | |
| class JSignatureMethod extends JMethod { | |
| JSignatureMethod(LibraryEntity enclosingLibrary, ClassEntity enclosingClass, | |
| ParameterStructure parameterStructure, AsyncMarker asyncMarker) | |
| : super(enclosingLibrary, enclosingClass, Names.signature, | |
| parameterStructure, asyncMarker, | |
| isStatic: false, isExternal: false, isAbstract: false); | |
| String get _kind => 'signature'; | |
| } | |
| class JTypeVariable extends IndexedTypeVariable { | |
| final Entity typeDeclaration; | |
| final String name; | |
| final int index; | |
| JTypeVariable(this.typeDeclaration, this.name, this.index); | |
| String toString() => | |
| '${jsElementPrefix}type_variable(${typeDeclaration.name}.$name)'; | |
| } |