| // 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/names.dart'; |
| import '../elements/types.dart'; |
| import '../kernel/indexed.dart'; |
| |
| /// 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)); |
| } |
| |
| @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 { |
| 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)'; |
| } |