| // Copyright (c) 2016, 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; | |
| // ignore: implementation_imports | |
| import 'package:front_end/src/api_unstable/dart2js.dart' show AsyncModifier; | |
| import '../common.dart' show Spannable; | |
| import '../serialization/serialization.dart'; | |
| import '../universe/call_structure.dart' show CallStructure; | |
| import '../util/util.dart'; | |
| import 'names.dart'; | |
| import 'types.dart' show FunctionType; | |
| /// Abstract interface for entities. | |
| /// | |
| /// Implement this directly if the entity is not a Dart language entity. | |
| /// Entities defined within the Dart language should implement [Element]. | |
| /// | |
| /// For instance, the JavaScript backend need to create synthetic variables for | |
| /// calling intercepted classes and such variables do not correspond to an | |
| /// entity in the Dart source code nor in the terminology of the Dart language | |
| /// and should therefore implement [Entity] directly. | |
| abstract class Entity implements Spannable { | |
| // Not all entities have names. Imports with no prefix and some local | |
| // variables are unnamed. Some entities have a name that is the empty string | |
| // (e.g. the default constructor). | |
| String? get name; | |
| } | |
| /// Stripped down super interface for library like entities. | |
| /// | |
| /// Currently only [LibraryElement] but later also kernel based Dart classes | |
| /// and/or Dart-in-JS classes. | |
| abstract class LibraryEntity extends Entity { | |
| /// Return the canonical uri that identifies this library. | |
| Uri get canonicalUri; | |
| } | |
| /// Stripped down super interface for import entities. | |
| /// | |
| /// The [name] property corresponds to the prefix name, if any. | |
| class ImportEntity { | |
| final String? name; | |
| /// The canonical URI of the library where this import occurs | |
| /// (where the import is declared). | |
| final Uri enclosingLibraryUri; | |
| /// Whether the import is a deferred import. | |
| final bool isDeferred; | |
| /// The target import URI. | |
| final Uri uri; | |
| ImportEntity(this.isDeferred, this.name, this.uri, this.enclosingLibraryUri); | |
| @override | |
| String toString() => 'import($name:${isDeferred ? ' deferred' : ''})'; | |
| } | |
| /// Stripped down super interface for class like entities. | |
| /// | |
| /// Currently only [ClassElement] but later also kernel based Dart classes | |
| /// and/or Dart-in-JS classes. | |
| abstract class ClassEntity extends Entity { | |
| /// Classes always have a name. | |
| @override | |
| String get name; | |
| /// If this is a normal class, the enclosing library for this class. If this | |
| /// is a closure class, the enclosing class of the closure for which it was | |
| /// created. | |
| LibraryEntity get library; | |
| /// Whether this is a synthesized class for a closurized method or local | |
| /// function. | |
| bool get isClosure; | |
| /// Whether this is an abstract class. | |
| bool get isAbstract; | |
| } | |
| abstract class TypeVariableEntity extends Entity { | |
| /// The class or generic method that declared this type variable. | |
| Entity get typeDeclaration; | |
| /// The index of this type variable in the type variables of its | |
| /// [typeDeclaration]. | |
| int get index; | |
| } | |
| /// Stripped down super interface for member like entities, that is, | |
| /// constructors, methods, fields etc. | |
| /// | |
| /// Currently only [MemberElement] but later also kernel based Dart members | |
| /// and/or Dart-in-JS properties. | |
| abstract class MemberEntity extends Entity { | |
| /// The [Name] of member which takes privacy and getter/setter naming into | |
| /// account. | |
| Name get memberName; | |
| /// Whether this is a member of a library. | |
| bool get isTopLevel; | |
| /// Whether this is a static member of a class. | |
| bool get isStatic; | |
| /// Whether this is an instance member of a class. | |
| bool get isInstanceMember; | |
| /// Whether this is a normal method (neither constructor, getter or setter) | |
| /// or operator method. | |
| bool get isFunction; | |
| /// Whether this is a getter. | |
| bool get isGetter; | |
| /// Whether this is a setter. | |
| bool get isSetter; | |
| /// Whether this member is assignable, i.e. a non-final, non-const field. | |
| bool get isAssignable; | |
| /// Whether this member is constant, i.e. a constant field or constructor. | |
| bool get isConst; | |
| /// Whether this member is abstract, i.e. an abstract method, getter or | |
| /// setter. | |
| bool get isAbstract; | |
| /// The enclosing class if this is a constructor, instance member or | |
| /// static member of a class. | |
| ClassEntity? get enclosingClass; | |
| /// The enclosing library if this is a library member, otherwise the | |
| /// enclosing library of the [enclosingClass]. | |
| LibraryEntity get library; | |
| } | |
| /// Stripped down super interface for field like entities. | |
| /// | |
| /// Currently only [FieldElement] but later also kernel based Dart fields | |
| /// and/or Dart-in-JS field-like properties. | |
| abstract class FieldEntity extends MemberEntity {} | |
| /// An entity that defines a local entity (memory slot) in generated code. | |
| /// | |
| /// Parameters, local variables and local functions (can) define local entity | |
| /// and thus implement [Local] through [LocalElement]. For non-element locals, | |
| /// like `this` and boxes, specialized [Local] classes are created. | |
| /// | |
| /// Type variables can introduce locals in factories and constructors | |
| /// but since one type variable can introduce different locals in different | |
| /// factories and constructors it is not itself a [Local] but instead | |
| /// a non-element [Local] is created through a specialized class. | |
| abstract class Local extends Entity {} | |
| /// Stripped down super interface for function like entities. | |
| /// | |
| /// Currently only [MethodElement] but later also kernel based Dart constructors | |
| /// and methods and/or Dart-in-JS function-like properties. | |
| abstract class FunctionEntity extends MemberEntity { | |
| /// Whether this function is external, i.e. the body is not defined in terms | |
| /// of Dart code. | |
| bool get isExternal; | |
| /// The structure of the function parameters. | |
| ParameterStructure get parameterStructure; | |
| /// The synchronous/asynchronous marker on this function. | |
| AsyncMarker get asyncMarker; | |
| } | |
| /// Enum for the synchronous/asynchronous function body modifiers. | |
| enum AsyncMarker { | |
| /// The default function body marker. | |
| sync._(AsyncModifier.Sync), | |
| /// The `sync*` function body marker. | |
| syncStar._(AsyncModifier.SyncStar, isYielding: true), | |
| /// The `async` function body marker. | |
| async._(AsyncModifier.Async, isAsync: true), | |
| /// The `async*` function body marker. | |
| asyncStar._(AsyncModifier.AsyncStar, isAsync: true, isYielding: true); | |
| /// Is `true` if this marker defines the function body to have an | |
| /// asynchronous result, that is, either a [Future] or a [Stream]. | |
| final bool isAsync; | |
| /// Is `true` if this marker defines the function body to have a plural | |
| /// result, that is, either an [Iterable] or a [Stream]. | |
| final bool isYielding; | |
| final AsyncModifier asyncParserState; | |
| const AsyncMarker._( | |
| this.asyncParserState, { | |
| this.isAsync = false, | |
| this.isYielding = false, | |
| }); | |
| @override | |
| String toString() { | |
| return '${isAsync ? 'async' : 'sync'}${isYielding ? '*' : ''}'; | |
| } | |
| } | |
| /// Stripped down super interface for constructor like entities. | |
| /// | |
| /// Currently only [ConstructorElement] but later also kernel based Dart | |
| /// constructors and/or Dart-in-JS constructor-like properties. | |
| // TODO(johnniwinther): Remove factory constructors from the set of | |
| // constructors. | |
| abstract class ConstructorEntity extends FunctionEntity { | |
| // Constructors always have an enclosing class. | |
| @override | |
| ClassEntity get enclosingClass; | |
| /// Whether this is a generative constructor, possibly redirecting. | |
| bool get isGenerativeConstructor; | |
| /// Whether this is a factory constructor, possibly redirecting. | |
| bool get isFactoryConstructor; | |
| /// Whether this is a `fromEnvironment` const constructor in `int`, `bool` or | |
| /// `String`. | |
| bool get isFromEnvironmentConstructor; | |
| } | |
| /// The constructor body for a [ConstructorEntity]. | |
| /// | |
| /// This is used only in the backend to split encoding of a Dart constructor | |
| /// into two JavaScript functions; the constructor and the constructor body. | |
| // TODO(johnniwinther): Remove this when modelx is removed. Constructor bodies | |
| // should then be created directly with the J-model. | |
| abstract class ConstructorBodyEntity extends FunctionEntity { | |
| /// The constructor for which this constructor body was created. | |
| ConstructorEntity get constructor; | |
| } | |
| /// The structure of function parameters. | |
| class ParameterStructure { | |
| /// Tag used for identifying serialized [ParameterStructure] objects in a | |
| /// debugging data stream. | |
| static const String tag = 'parameter-structure'; | |
| /// The number of required positional parameters. | |
| final int requiredPositionalParameters; | |
| /// The number of positional parameters. | |
| final int positionalParameters; | |
| /// All named parameters sorted alphabetically. | |
| final List<String> namedParameters; | |
| /// The required named parameters. | |
| final Set<String> requiredNamedParameters; | |
| /// The number of type parameters. | |
| final int typeParameters; | |
| static const ParameterStructure getter = ParameterStructure._( | |
| 0, | |
| 0, | |
| [], | |
| {}, | |
| 0, | |
| ); | |
| static const ParameterStructure setter = ParameterStructure._( | |
| 1, | |
| 1, | |
| [], | |
| {}, | |
| 0, | |
| ); | |
| static const ParameterStructure zeroArguments = ParameterStructure._( | |
| 0, | |
| 0, | |
| [], | |
| {}, | |
| 0, | |
| ); | |
| static const ParameterStructure oneArgument = ParameterStructure._( | |
| 1, | |
| 1, | |
| [], | |
| {}, | |
| 0, | |
| ); | |
| static const ParameterStructure twoArguments = ParameterStructure._( | |
| 2, | |
| 2, | |
| [], | |
| {}, | |
| 0, | |
| ); | |
| static const List<ParameterStructure> _simple = [ | |
| ParameterStructure._(0, 0, [], {}, 0), | |
| ParameterStructure._(1, 1, [], {}, 0), | |
| ParameterStructure._(2, 2, [], {}, 0), | |
| ParameterStructure._(3, 3, [], {}, 0), | |
| ParameterStructure._(4, 4, [], {}, 0), | |
| ParameterStructure._(5, 5, [], {}, 0), | |
| ]; | |
| const ParameterStructure._( | |
| this.requiredPositionalParameters, | |
| this.positionalParameters, | |
| this.namedParameters, | |
| this.requiredNamedParameters, | |
| this.typeParameters, | |
| ); | |
| factory ParameterStructure( | |
| int requiredPositionalParameters, | |
| int positionalParameters, | |
| List<String> namedParameters, | |
| Set<String> requiredNamedParameters, | |
| int typeParameters, | |
| ) { | |
| // This simple canonicalization reduces the number of ParameterStructure | |
| // objects by over 90%. | |
| if (requiredPositionalParameters == positionalParameters && | |
| namedParameters.isEmpty && | |
| requiredNamedParameters.isEmpty && | |
| typeParameters == 0 && | |
| positionalParameters < _simple.length) { | |
| return _simple[positionalParameters]; | |
| } | |
| // Force sharing of empty collections. | |
| if (namedParameters.isEmpty) namedParameters = const []; | |
| if (requiredNamedParameters.isEmpty) requiredNamedParameters = const {}; | |
| return ParameterStructure._( | |
| requiredPositionalParameters, | |
| positionalParameters, | |
| namedParameters, | |
| requiredNamedParameters, | |
| typeParameters, | |
| ); | |
| } | |
| static ParameterStructure fromType(FunctionType type) { | |
| return ParameterStructure( | |
| type.parameterTypes.length, | |
| type.parameterTypes.length + type.optionalParameterTypes.length, | |
| type.namedParameters, | |
| type.requiredNamedParameters, | |
| type.typeVariables.length, | |
| ); | |
| } | |
| /// Deserializes a [ParameterStructure] object from [source]. | |
| factory ParameterStructure.readFromDataSource(DataSourceReader source) { | |
| final tag = ParameterStructure.tag; | |
| source.begin(tag); | |
| int requiredPositionalParameters = source.readInt(); | |
| int positionalParameters = source.readInt(); | |
| List<String> namedParameters = source.readStrings(); | |
| Set<String> requiredNamedParameters = | |
| source.readStringsOrNull()?.toSet() ?? const <String>{}; | |
| int typeParameters = source.readInt(); | |
| source.end(tag); | |
| return ParameterStructure( | |
| requiredPositionalParameters, | |
| positionalParameters, | |
| namedParameters, | |
| requiredNamedParameters, | |
| typeParameters, | |
| ); | |
| } | |
| /// Serializes this [ParameterStructure] to [sink]. | |
| void writeToDataSink(DataSinkWriter sink) { | |
| final tag = ParameterStructure.tag; | |
| sink.begin(tag); | |
| sink.writeInt(requiredPositionalParameters); | |
| sink.writeInt(positionalParameters); | |
| sink.writeStrings(namedParameters); | |
| sink.writeStrings(requiredNamedParameters); | |
| sink.writeInt(typeParameters); | |
| sink.end(tag); | |
| } | |
| /// The number of optional parameters (positional or named). | |
| int get optionalParameters => | |
| (positionalParameters - requiredPositionalParameters) + | |
| (namedParameters.length - requiredNamedParameters.length); | |
| /// The total number of parameters (required or optional). | |
| int get totalParameters => positionalParameters + namedParameters.length; | |
| /// Returns the [CallStructure] corresponding to a call site passing all | |
| /// parameters both required and optional. | |
| CallStructure get callStructure { | |
| return CallStructure(totalParameters, namedParameters, typeParameters); | |
| } | |
| @override | |
| int get hashCode => Hashing.listHash( | |
| namedParameters, | |
| Hashing.setHash( | |
| requiredNamedParameters, | |
| Hashing.objectHash( | |
| positionalParameters, | |
| Hashing.objectHash( | |
| requiredPositionalParameters, | |
| Hashing.objectHash(typeParameters), | |
| ), | |
| ), | |
| ), | |
| ); | |
| @override | |
| bool operator ==(other) { | |
| if (identical(this, other)) return true; | |
| if (other is! ParameterStructure) return false; | |
| if (requiredPositionalParameters != other.requiredPositionalParameters || | |
| positionalParameters != other.positionalParameters || | |
| typeParameters != other.typeParameters || | |
| namedParameters.length != other.namedParameters.length || | |
| requiredNamedParameters.length != | |
| other.requiredNamedParameters.length) { | |
| return false; | |
| } | |
| for (int i = 0; i < namedParameters.length; i++) { | |
| if (namedParameters[i] != other.namedParameters[i]) { | |
| return false; | |
| } | |
| } | |
| for (String name in requiredNamedParameters) { | |
| if (!other.requiredNamedParameters.contains(name)) return false; | |
| } | |
| return true; | |
| } | |
| /// Short textual representation use for testing. | |
| String get shortText { | |
| StringBuffer sb = StringBuffer(); | |
| if (typeParameters != 0) { | |
| sb.write('<'); | |
| sb.write(typeParameters); | |
| sb.write('>'); | |
| } | |
| sb.write('('); | |
| sb.write(positionalParameters); | |
| for (var name in namedParameters) { | |
| sb.write(','); | |
| if (requiredNamedParameters.contains(name)) sb.write('req '); | |
| sb.write(name); | |
| } | |
| sb.write(')'); | |
| return sb.toString(); | |
| } | |
| @override | |
| String toString() { | |
| StringBuffer sb = StringBuffer(); | |
| sb.write('ParameterStructure('); | |
| sb.write('requiredPositionalParameters=$requiredPositionalParameters,'); | |
| sb.write('positionalParameters=$positionalParameters,'); | |
| sb.write('namedParameters={${namedParameters.join(',')}},'); | |
| sb.write('requiredNamedParameters={${requiredNamedParameters.join(',')}},'); | |
| sb.write('typeParameters=$typeParameters)'); | |
| return sb.toString(); | |
| } | |
| int get size => totalParameters + typeParameters; | |
| } |