| // Copyright (c) 2024, 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. | |
| import 'arguments.dart'; | |
| import 'expressions.dart'; | |
| import 'references.dart'; | |
| import 'scope.dart'; | |
| import 'type_annotations.dart'; | |
| import 'util.dart'; | |
| /// A [Proto] represents a parsed substructure before it can be determined | |
| /// whether is is an expression, a type annotation, or something else. | |
| /// | |
| /// For instance when parsing `Foo` we cannot determine what it means before | |
| /// we have parsed what comes after `Foo`. If it is followed by `.bar` it could | |
| /// be the class name prefix of a static field access `Foo.bar` and if it is | |
| /// followed by `,` then it could be a type literal `Foo` as an expression. | |
| sealed class Proto { | |
| /// Creates the [Proto] corresponding to this [Proto] followed by [send]. | |
| /// | |
| /// If [send] is `null`, this corresponds to this [Proto] on its own. | |
| /// Otherwise this corresponds to this [Proto] followed by `.` or `?.`, an | |
| /// identifier, and optionally type arguments and/or arguments. | |
| Proto apply(IdentifierProto? send, {bool isNullAware = false}) { | |
| if (send == null) { | |
| return this; | |
| } else if (isNullAware) { | |
| return new InstanceAccessProto( | |
| this, | |
| send.text, | |
| isNullAware: true, | |
| ).instantiate(send.typeArguments).invoke(send.arguments); | |
| } else { | |
| return access( | |
| send.text, | |
| ).instantiate(send.typeArguments).invoke(send.arguments); | |
| } | |
| } | |
| /// Returns the [Proto] corresponding to accessing the property [name]. | |
| /// | |
| /// If [name] is `null`, the [Proto] itself is returned. This functionality | |
| /// is supported for ease of use. | |
| Proto access(String? name); | |
| /// Returns the [Proto] corresponding applying [typeArguments] to it. | |
| /// | |
| /// If [typeArguments] is `null`, the [Proto] itself is returned. This | |
| /// functionality is supported for ease of use. | |
| Proto instantiate(List<TypeAnnotation>? typeArguments); | |
| /// Returns the [Proto] corresponding invoke it with [arguments]. | |
| /// | |
| /// If [arguments] is `null`, the [Proto] itself is returned. This | |
| /// functionality is supported for ease of use. | |
| Proto invoke(List<Argument>? arguments); | |
| /// Creates the [Expression] corresponding to this [Proto]. | |
| /// | |
| /// This corresponding to this [Proto] occurring in an expression context | |
| /// on its own. | |
| Expression toExpression(); | |
| /// Creates the [TypeAnnotation] corresponding to this [Proto]. | |
| /// | |
| /// This corresponding to this [Proto] occurring in a type annotation context | |
| /// on its own. | |
| TypeAnnotation toTypeAnnotation(); | |
| /// Returns the [Proto] corresponding to this [Proto] in which all | |
| /// [UnresolvedIdentifier]s have been resolved within their scope. | |
| /// | |
| /// If this didn't create a new [Proto], `null` is returned. | |
| Proto? resolve(); | |
| } | |
| /// An unresolved part of a proto, expression or type annotation. | |
| sealed class Unresolved { | |
| /// Returns this unresolved part as an [Expression] if it can be resolved. | |
| Expression? resolveAsExpression(); | |
| /// Returns this unresolved part as a [TypeAnnotation] if it can be resolved. | |
| TypeAnnotation? resolveAsTypeAnnotation(); | |
| } | |
| /// The unresolved identifier [name] occurring in [scope]. | |
| class UnresolvedIdentifier extends Proto implements Unresolved { | |
| final Scope scope; | |
| final String name; | |
| UnresolvedIdentifier(this.scope, this.name); | |
| @override | |
| Proto access(String? name) { | |
| if (name == null) { | |
| return this; | |
| } | |
| return new UnresolvedAccess(this, name); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| if (typeArguments == null) { | |
| return this; | |
| } | |
| return new UnresolvedInstantiate(this, typeArguments); | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| if (arguments == null) { | |
| return this; | |
| } | |
| return new UnresolvedInvoke(this, arguments); | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new UnresolvedExpression(this); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() { | |
| return new UnresolvedTypeAnnotation(this); | |
| } | |
| @override | |
| String toString() => 'UnresolvedIdentifier($name)'; | |
| @override | |
| Expression? resolveAsExpression() { | |
| Proto? resolved = resolve(); | |
| return resolved == null ? null : resolved.toExpression(); | |
| } | |
| @override | |
| TypeAnnotation? resolveAsTypeAnnotation() { | |
| Proto? resolved = resolve(); | |
| return resolved == null ? null : resolved.toTypeAnnotation(); | |
| } | |
| @override | |
| bool operator ==(Object other) => | |
| identical(this, other) || | |
| other is UnresolvedIdentifier && | |
| runtimeType == other.runtimeType && | |
| name == other.name; | |
| @override | |
| int get hashCode => name.hashCode; | |
| @override | |
| Proto? resolve() { | |
| Proto resolved = scope.lookup(name); | |
| return this == resolved ? null : resolved; | |
| } | |
| } | |
| /// The unresolved access to [name] on [prefix]. | |
| class UnresolvedAccess extends Proto implements Unresolved { | |
| final Proto prefix; | |
| final String name; | |
| UnresolvedAccess(this.prefix, this.name); | |
| @override | |
| Proto access(String? name) { | |
| if (name == null) { | |
| return this; | |
| } | |
| return new UnresolvedAccess(this, name); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| if (typeArguments == null) { | |
| return this; | |
| } | |
| return new UnresolvedInstantiate(this, typeArguments); | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| if (arguments == null) { | |
| return this; | |
| } | |
| return new UnresolvedInvoke(this, arguments); | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new UnresolvedExpression(this); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() { | |
| return new UnresolvedTypeAnnotation(this); | |
| } | |
| @override | |
| String toString() => 'UnresolvedAccess($prefix,$name)'; | |
| @override | |
| Expression? resolveAsExpression() { | |
| Proto? resolved = resolve(); | |
| return resolved == null ? null : resolved.toExpression(); | |
| } | |
| @override | |
| TypeAnnotation? resolveAsTypeAnnotation() { | |
| Proto? resolved = resolve(); | |
| return resolved == null ? null : resolved.toTypeAnnotation(); | |
| } | |
| @override | |
| Proto? resolve() { | |
| Proto? newPrefix = prefix.resolve(); | |
| return newPrefix == null ? null : newPrefix.access(name); | |
| } | |
| } | |
| /// The application of [typeArguments] on [prefix]. | |
| /// | |
| /// The [prefix] is (partially) unresolved, so it cannot yet determined what | |
| /// the application of [typeArguments] means. For instance `unresolved<int>` | |
| /// could be instantiation of the generic class `unresolved` or the | |
| /// instantiation of the generic method `unresolved`. | |
| class UnresolvedInstantiate extends Proto implements Unresolved { | |
| final Proto prefix; | |
| final List<TypeAnnotation> typeArguments; | |
| UnresolvedInstantiate(this.prefix, this.typeArguments); | |
| @override | |
| Proto access(String? name) { | |
| if (name == null) { | |
| return this; | |
| } | |
| return new UnresolvedAccess(this, name); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| if (typeArguments == null) { | |
| return this; | |
| } | |
| return new UnresolvedInstantiate(this, typeArguments); | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| if (arguments == null) { | |
| return this; | |
| } | |
| return new UnresolvedInvoke(this, arguments); | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new UnresolvedExpression(this); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() { | |
| return new UnresolvedTypeAnnotation(this); | |
| } | |
| @override | |
| String toString() => 'UnresolvedInstantiate($prefix,$typeArguments)'; | |
| @override | |
| Expression? resolveAsExpression() { | |
| Proto? resolved = resolve(); | |
| return resolved == null ? null : resolved.toExpression(); | |
| } | |
| @override | |
| TypeAnnotation? resolveAsTypeAnnotation() { | |
| Proto? resolved = resolve(); | |
| return resolved == null ? null : resolved.toTypeAnnotation(); | |
| } | |
| @override | |
| Proto? resolve() { | |
| Proto? newPrefix = prefix.resolve(); | |
| List<TypeAnnotation>? newTypeArguments = typeArguments.resolve( | |
| (t) => t.resolve(), | |
| ); | |
| return newPrefix == null && newTypeArguments == null | |
| ? null | |
| : (newPrefix ?? prefix).instantiate(newTypeArguments ?? typeArguments); | |
| } | |
| } | |
| /// The invocation of [arguments] on [prefix]. | |
| /// | |
| /// The [prefix] is (partially) unresolved, so it cannot yet determined what | |
| /// the invocation of [arguments] means. For instance `unresolved(0)` | |
| /// could be the constructor invocation of the unnamed constructor of the class | |
| /// `unresolved` or the invocation of the method `unresolved`. | |
| class UnresolvedInvoke extends Proto implements Unresolved { | |
| final Proto prefix; | |
| final List<Argument> arguments; | |
| UnresolvedInvoke(this.prefix, this.arguments); | |
| @override | |
| Proto access(String? name) { | |
| if (name == null) { | |
| return this; | |
| } | |
| return new UnresolvedAccess(this, name); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| if (typeArguments == null) { | |
| return this; | |
| } | |
| return new UnresolvedInstantiate(this, typeArguments); | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| if (arguments == null) { | |
| return this; | |
| } | |
| return new UnresolvedInvoke(this, arguments); | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new UnresolvedExpression(this); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() { | |
| return new UnresolvedTypeAnnotation(this); | |
| } | |
| @override | |
| String toString() => 'UnresolvedInvoke($prefix,$arguments)'; | |
| @override | |
| Expression? resolveAsExpression() { | |
| Proto? resolved = resolve(); | |
| return resolved == null ? null : resolved.toExpression(); | |
| } | |
| @override | |
| TypeAnnotation? resolveAsTypeAnnotation() { | |
| Proto? resolved = resolve(); | |
| return resolved == null ? null : resolved.toTypeAnnotation(); | |
| } | |
| @override | |
| Proto? resolve() { | |
| Proto? newPrefix = prefix.resolve(); | |
| List<Argument>? newArguments = arguments.resolve((a) => a.resolve()); | |
| return newPrefix == null && newArguments == null | |
| ? null | |
| : (newPrefix ?? prefix).invoke(newArguments ?? arguments); | |
| } | |
| } | |
| /// A [reference] to a class. | |
| /// | |
| /// The [Proto] includes the [scope] of the class, which is used to resolve | |
| /// access to constructors and static members on the class. | |
| class ClassProto extends Proto { | |
| final ClassReference reference; | |
| final TypeDeclarationScope scope; | |
| ClassProto(this.reference, this.scope); | |
| @override | |
| String toString() => 'ClassProto($reference)'; | |
| @override | |
| Proto access(String? name) { | |
| if (name == null) { | |
| return this; | |
| } | |
| if (name == 'new') { | |
| name = ''; | |
| } | |
| return scope.lookup(name); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| return typeArguments != null | |
| ? new GenericClassProto(reference, scope, typeArguments) | |
| : this; | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments != null ? access('new').invoke(arguments) : this; | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new TypeLiteral(toTypeAnnotation()); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => new NamedTypeAnnotation(reference); | |
| @override | |
| Proto? resolve() => null; | |
| } | |
| /// A [reference] to a class instantiated with [typeArguments]. | |
| /// | |
| /// The [Proto] includes the [scope] of the class, which is used to resolve | |
| /// access to constructors on the class. | |
| class GenericClassProto extends Proto { | |
| final ClassReference reference; | |
| final TypeDeclarationScope scope; | |
| final List<TypeAnnotation> typeArguments; | |
| GenericClassProto(this.reference, this.scope, this.typeArguments); | |
| @override | |
| String toString() => 'GenericClassProto($reference,$typeArguments)'; | |
| @override | |
| Proto access(String? name) { | |
| if (name == null) { | |
| return this; | |
| } | |
| if (name == 'new') { | |
| name = ''; | |
| } | |
| return scope.lookup(name, typeArguments); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| return typeArguments != null | |
| ? throw new UnimplementedError('GenericClassProto.instantiate') | |
| : this; | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments != null ? access('new').invoke(arguments) : this; | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new TypeLiteral(toTypeAnnotation()); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => | |
| new NamedTypeAnnotation(reference, typeArguments); | |
| @override | |
| Proto? resolve() { | |
| List<TypeAnnotation>? newTypeArguments = typeArguments.resolve( | |
| (a) => a.resolve(), | |
| ); | |
| return newTypeArguments == null | |
| ? null | |
| : new GenericClassProto(reference, scope, newTypeArguments); | |
| } | |
| } | |
| /// A [reference] to an extension | |
| /// | |
| /// The [Proto] includes the [scope] of the extension, which is used to resolve | |
| /// access to static members on the extension. | |
| class ExtensionProto extends Proto { | |
| final ExtensionReference reference; | |
| final TypeDeclarationScope scope; | |
| ExtensionProto(this.reference, this.scope); | |
| @override | |
| String toString() => 'ExtensionProto($reference)'; | |
| @override | |
| Proto access(String? name) { | |
| if (name == null) { | |
| return this; | |
| } | |
| if (name == 'new') { | |
| name = ''; | |
| } | |
| return scope.lookup(name); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| return typeArguments != null | |
| ? new InvalidInstantiationProto(this, typeArguments) | |
| : this; | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments != null | |
| ? new InvalidInvocationProto(this, const [], arguments) | |
| : this; | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new InvalidExpression(); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation(); | |
| @override | |
| Proto? resolve() => null; | |
| } | |
| /// A [reference] to an enum | |
| /// | |
| /// The [Proto] includes the [scope] of the enum, which is used to resolve | |
| /// access to static members on the enum. | |
| class EnumProto extends Proto { | |
| final EnumReference reference; | |
| final TypeDeclarationScope scope; | |
| EnumProto(this.reference, this.scope); | |
| @override | |
| String toString() => 'EnumProto($reference)'; | |
| @override | |
| Proto access(String? name) { | |
| if (name == null) { | |
| return this; | |
| } | |
| if (name == 'new') { | |
| name = ''; | |
| } | |
| return scope.lookup(name); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| return typeArguments != null | |
| ? new GenericEnumProto(reference, scope, typeArguments) | |
| : this; | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments != null | |
| ? new InvalidInvocationProto(this, const [], arguments) | |
| : this; | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new TypeLiteral(toTypeAnnotation()); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => new NamedTypeAnnotation(reference); | |
| @override | |
| Proto? resolve() => null; | |
| } | |
| /// A [reference] to an enum instantiated with [typeArguments]. | |
| /// | |
| /// The [Proto] includes the [scope] of the enum, which is used to resolve | |
| /// access to constructors on the enum. | |
| class GenericEnumProto extends Proto { | |
| final EnumReference reference; | |
| final TypeDeclarationScope scope; | |
| final List<TypeAnnotation> typeArguments; | |
| GenericEnumProto(this.reference, this.scope, this.typeArguments); | |
| @override | |
| String toString() => 'GenericEnumProto($reference,$typeArguments)'; | |
| @override | |
| Proto access(String? name) { | |
| if (name == null) { | |
| return this; | |
| } | |
| if (name == 'new') { | |
| name = ''; | |
| } | |
| return scope.lookup(name, typeArguments); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| return typeArguments != null | |
| ? throw new UnimplementedError('$this.instantiate') | |
| : this; | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments != null ? access('new').invoke(arguments) : this; | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new TypeLiteral(toTypeAnnotation()); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => | |
| new NamedTypeAnnotation(reference, typeArguments); | |
| @override | |
| Proto? resolve() { | |
| List<TypeAnnotation>? newTypeArguments = typeArguments.resolve( | |
| (a) => a.resolve(), | |
| ); | |
| return newTypeArguments == null | |
| ? null | |
| : new GenericEnumProto(reference, scope, newTypeArguments); | |
| } | |
| } | |
| /// A [reference] to a mixin | |
| /// | |
| /// The [Proto] includes the [scope] of the mixin, which is used to resolve | |
| /// access to static members on the mixin. | |
| class MixinProto extends Proto { | |
| final MixinReference reference; | |
| final TypeDeclarationScope scope; | |
| MixinProto(this.reference, this.scope); | |
| @override | |
| String toString() => 'MixinProto($reference)'; | |
| @override | |
| Proto access(String? name) { | |
| if (name == null) { | |
| return this; | |
| } | |
| if (name == 'new') { | |
| name = ''; | |
| } | |
| return scope.lookup(name); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| return typeArguments != null | |
| ? new GenericMixinProto(reference, scope, typeArguments) | |
| : this; | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments != null | |
| ? new InvalidInvocationProto(this, const [], arguments) | |
| : this; | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new TypeLiteral(toTypeAnnotation()); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => new NamedTypeAnnotation(reference); | |
| @override | |
| Proto? resolve() => null; | |
| } | |
| /// A [reference] to an enum instantiated with [typeArguments]. | |
| /// | |
| /// The [Proto] includes the [scope] of the enum, which is used to resolve | |
| /// access to constructors on the enum. | |
| class GenericMixinProto extends Proto { | |
| final MixinReference reference; | |
| final TypeDeclarationScope scope; | |
| final List<TypeAnnotation> typeArguments; | |
| GenericMixinProto(this.reference, this.scope, this.typeArguments); | |
| @override | |
| String toString() => 'GenericMixinProto($reference,$typeArguments)'; | |
| @override | |
| Proto access(String? name) { | |
| if (name == null) { | |
| return this; | |
| } | |
| if (name == 'new') { | |
| name = ''; | |
| } | |
| return scope.lookup(name, typeArguments); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| return typeArguments != null | |
| ? throw new UnimplementedError('$this.instantiate') | |
| : this; | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments != null ? access('new').invoke(arguments) : this; | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new TypeLiteral(toTypeAnnotation()); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => | |
| new NamedTypeAnnotation(reference, typeArguments); | |
| @override | |
| Proto? resolve() { | |
| List<TypeAnnotation>? newTypeArguments = typeArguments.resolve( | |
| (a) => a.resolve(), | |
| ); | |
| return newTypeArguments == null | |
| ? null | |
| : new GenericMixinProto(reference, scope, newTypeArguments); | |
| } | |
| } | |
| /// A [reference] to an extension type. | |
| /// | |
| /// The [Proto] includes the [scope] of the extension type, which is used to | |
| /// resolve access to constructors and static members on the extension type. | |
| class ExtensionTypeProto extends Proto { | |
| final ExtensionTypeReference reference; | |
| final TypeDeclarationScope scope; | |
| ExtensionTypeProto(this.reference, this.scope); | |
| @override | |
| String toString() => 'ExtensionTypeProto($reference)'; | |
| @override | |
| Proto access(String? name) { | |
| if (name == null) { | |
| return this; | |
| } | |
| if (name == 'new') { | |
| name = ''; | |
| } | |
| return scope.lookup(name); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| return typeArguments != null | |
| ? new GenericExtensionTypeProto(reference, scope, typeArguments) | |
| : this; | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments != null ? access('new').invoke(arguments) : this; | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new TypeLiteral(toTypeAnnotation()); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => new NamedTypeAnnotation(reference); | |
| @override | |
| Proto? resolve() => null; | |
| } | |
| /// A [reference] to an extension type instantiated with [typeArguments]. | |
| /// | |
| /// The [Proto] includes the [scope] of the extension type, which is used to | |
| /// resolve access to constructors on the class. | |
| class GenericExtensionTypeProto extends Proto { | |
| final ExtensionTypeReference reference; | |
| final TypeDeclarationScope scope; | |
| final List<TypeAnnotation> typeArguments; | |
| GenericExtensionTypeProto(this.reference, this.scope, this.typeArguments); | |
| @override | |
| String toString() => 'GenericExtensionTypeProto($reference,$typeArguments)'; | |
| @override | |
| Proto access(String? name) { | |
| if (name == null) { | |
| return this; | |
| } | |
| if (name == 'new') { | |
| name = ''; | |
| } | |
| return scope.lookup(name, typeArguments); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| return typeArguments != null | |
| ? throw new UnimplementedError('GenericExtensionTypeProto.instantiate') | |
| : this; | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments != null ? access('new').invoke(arguments) : this; | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new TypeLiteral(toTypeAnnotation()); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => | |
| new NamedTypeAnnotation(reference, typeArguments); | |
| @override | |
| Proto? resolve() { | |
| List<TypeAnnotation>? newTypeArguments = typeArguments.resolve( | |
| (a) => a.resolve(), | |
| ); | |
| return newTypeArguments == null | |
| ? null | |
| : new GenericExtensionTypeProto(reference, scope, newTypeArguments); | |
| } | |
| } | |
| /// A [reference] to a typedef. | |
| /// | |
| /// The [Proto] includes the [scope] of the typedef, which is used to resolve | |
| /// access to constructors through the typedef. | |
| class TypedefProto extends Proto { | |
| final TypedefReference reference; | |
| final TypeDeclarationScope scope; | |
| TypedefProto(this.reference, this.scope); | |
| @override | |
| String toString() => 'TypedefProto($reference)'; | |
| @override | |
| Proto access(String? name) { | |
| if (name == null) { | |
| return this; | |
| } | |
| if (name == 'new') { | |
| name = ''; | |
| } | |
| return scope.lookup(name); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| return typeArguments != null | |
| ? new GenericTypedefProto(reference, scope, typeArguments) | |
| : this; | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments != null ? access('new').invoke(arguments) : this; | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new TypeLiteral(toTypeAnnotation()); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => new NamedTypeAnnotation(reference); | |
| @override | |
| Proto? resolve() => null; | |
| } | |
| /// A [reference] to a typedef instantiated with [typeArguments]. | |
| /// | |
| /// The [Proto] includes the [scope] of the typedef, which is used to resolve | |
| /// access to constructors through the typedef. | |
| class GenericTypedefProto extends Proto { | |
| final TypedefReference reference; | |
| final TypeDeclarationScope scope; | |
| final List<TypeAnnotation> typeArguments; | |
| GenericTypedefProto(this.reference, this.scope, this.typeArguments); | |
| @override | |
| String toString() => 'GenericTypedefProto($reference,$typeArguments)'; | |
| @override | |
| Proto access(String? name) { | |
| if (name == null) { | |
| return this; | |
| } | |
| if (name == 'new') { | |
| name = ''; | |
| } | |
| return scope.lookup(name, typeArguments); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| return typeArguments != null | |
| ? throw new UnimplementedError('GenericTypedefProto.instantiate') | |
| : this; | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments != null ? access('new').invoke(arguments) : this; | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new TypeLiteral(toTypeAnnotation()); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => | |
| new NamedTypeAnnotation(reference, typeArguments); | |
| @override | |
| Proto? resolve() { | |
| List<TypeAnnotation>? newTypeArguments = typeArguments.resolve( | |
| (a) => a.resolve(), | |
| ); | |
| return newTypeArguments == null | |
| ? null | |
| : new GenericTypedefProto(reference, scope, newTypeArguments); | |
| } | |
| } | |
| /// A [reference] to a `void`. | |
| class VoidProto extends Proto { | |
| final Reference reference; | |
| VoidProto(this.reference); | |
| @override | |
| Expression toExpression() { | |
| return new TypeLiteral(toTypeAnnotation()); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => new VoidTypeAnnotation(reference); | |
| @override | |
| Proto access(String? name) { | |
| return name == null ? this : new InvalidAccessProto(this, name); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| return typeArguments == null | |
| ? this | |
| : new InvalidInstantiationProto(this, typeArguments); | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments == null | |
| ? this | |
| : new InvalidInvocationProto(this, const [], arguments); | |
| } | |
| @override | |
| String toString() => 'VoidProto()'; | |
| @override | |
| Proto? resolve() => null; | |
| } | |
| /// A [reference] to a `dynamic`. | |
| class DynamicProto extends Proto { | |
| final Reference reference; | |
| DynamicProto(this.reference); | |
| @override | |
| Expression toExpression() { | |
| return new TypeLiteral(toTypeAnnotation()); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => new DynamicTypeAnnotation(reference); | |
| @override | |
| Proto access(String? name) { | |
| return name == null ? this : new InvalidAccessProto(this, name); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| return typeArguments == null | |
| ? this | |
| : new InvalidInstantiationProto(this, typeArguments); | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments == null | |
| ? this | |
| : new InvalidInvocationProto(this, const [], arguments); | |
| } | |
| @override | |
| String toString() => 'DynamicProto()'; | |
| @override | |
| Proto? resolve() => null; | |
| } | |
| /// A [reference] to a constructor, including a reference to the [type] on which | |
| /// it was accessed, as well as the [typeArguments] applied to [type]. | |
| class ConstructorProto extends Proto { | |
| final Reference type; | |
| final List<TypeAnnotation> typeArguments; | |
| final ConstructorReference reference; | |
| ConstructorProto(this.type, this.typeArguments, this.reference); | |
| @override | |
| String toString() => 'ConstructorProto($type,$typeArguments,$reference)'; | |
| @override | |
| Proto access(String? name) { | |
| return name != null ? new InvalidAccessProto(this, name) : this; | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| return typeArguments != null | |
| ? new ExpressionInstantiationProto(this, typeArguments) | |
| : this; | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments != null | |
| ? new ExpressionProto( | |
| new ConstructorInvocation( | |
| new NamedTypeAnnotation(type, typeArguments), | |
| reference, | |
| arguments, | |
| ), | |
| ) | |
| : this; | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new ConstructorTearOff( | |
| new NamedTypeAnnotation(type, typeArguments), | |
| reference, | |
| ); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation(); | |
| @override | |
| Proto? resolve() => null; | |
| } | |
| /// A [reference] to a static or top level field. | |
| class FieldProto extends Proto { | |
| final FieldReference reference; | |
| FieldProto(this.reference); | |
| @override | |
| String toString() => 'FieldProto($reference)'; | |
| @override | |
| Proto access(String? name) { | |
| return name != null ? new InstanceAccessProto(this, name) : this; | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| return typeArguments != null | |
| ? new ExpressionInstantiationProto(this, typeArguments) | |
| : this; | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments != null | |
| ? new InstanceInvocationProto(this, const [], arguments) | |
| : this; | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new StaticGet(reference); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation(); | |
| @override | |
| Proto? resolve() => null; | |
| } | |
| /// A [reference] to a static or top level method. | |
| class FunctionProto extends Proto { | |
| final FunctionReference reference; | |
| FunctionProto(this.reference); | |
| @override | |
| String toString() => 'FunctionProto($reference)'; | |
| @override | |
| Proto access(String? name) { | |
| return name != null ? new InstanceAccessProto(this, name) : this; | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| return typeArguments != null | |
| ? new FunctionInstantiationProto(reference, typeArguments) | |
| : this; | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments != null | |
| ? new ExpressionProto( | |
| new StaticInvocation(reference, const [], arguments), | |
| ) | |
| : this; | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new FunctionTearOff(reference); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation(); | |
| @override | |
| Proto? resolve() => null; | |
| } | |
| /// A [reference] to a static or top level method instantiated with | |
| /// [typeArguments]. | |
| class FunctionInstantiationProto extends Proto { | |
| final FunctionReference reference; | |
| final List<TypeAnnotation> typeArguments; | |
| FunctionInstantiationProto(this.reference, this.typeArguments); | |
| @override | |
| String toString() => 'FunctionInstantiationProto($reference,$typeArguments)'; | |
| @override | |
| Proto access(String? name) { | |
| return name != null ? new InstanceAccessProto(this, name) : this; | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| return typeArguments != null | |
| ? new ExpressionInstantiationProto(this, typeArguments) | |
| : this; | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments != null | |
| ? new ExpressionProto( | |
| new StaticInvocation(reference, typeArguments, arguments), | |
| ) | |
| : this; | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new Instantiation(new FunctionTearOff(reference), typeArguments); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation(); | |
| @override | |
| Proto? resolve() => null; | |
| } | |
| /// A reference to the [prefix]. | |
| /// | |
| /// The [Proto] includes the [scope] of the prefix, which is used to resolve | |
| /// access to imported members and types through the prefix. | |
| class PrefixProto extends Proto { | |
| final String prefix; | |
| final Scope scope; | |
| PrefixProto(this.prefix, this.scope); | |
| @override | |
| String toString() => 'PrefixProto($prefix)'; | |
| @override | |
| Proto access(String? name) { | |
| if (name == null) { | |
| return this; | |
| } | |
| return scope.lookup(name); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| return typeArguments != null | |
| ? new InvalidInstantiationProto(this, typeArguments) | |
| : this; | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments != null | |
| ? new InvalidInvocationProto(this, const [], arguments) | |
| : this; | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new InvalidExpression(); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation(); | |
| @override | |
| Proto? resolve() => null; | |
| } | |
| /// The suffix of an access to the property [text] including applied | |
| /// [typeArguments] and [arguments], if any. | |
| /// | |
| /// This is not really a [Proto] but just a sequence of information needed to | |
| /// call [Proto.access], [Proto.instantiate], and [Proto.invoke]. Unfortunately | |
| /// the parser currently provides these as a "send" which is not the ideal model | |
| /// for parsing Dart. | |
| class IdentifierProto extends Proto { | |
| final String text; | |
| final List<TypeAnnotation>? typeArguments; | |
| final List<Argument>? arguments; | |
| IdentifierProto(String text) : this._(text, null, null); | |
| IdentifierProto._(this.text, this.typeArguments, this.arguments); | |
| @override | |
| Proto access(String? name) { | |
| return name == null | |
| ? this | |
| : throw new UnimplementedError('IdentifierProto.access'); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| return typeArguments == null | |
| ? this | |
| : new IdentifierProto._(text, typeArguments, null); | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments == null | |
| ? this | |
| : new IdentifierProto._(text, typeArguments, arguments); | |
| } | |
| @override | |
| Expression toExpression() { | |
| throw new UnimplementedError('IdentifierProto.toExpression'); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() { | |
| throw new UnimplementedError('IdentifierProto.toTypeAnnotation'); | |
| } | |
| @override | |
| String toString() => 'IdentifierProto($text)'; | |
| @override | |
| Proto? resolve() => null; | |
| } | |
| /// An access to the [text] property on [receiver]. | |
| /// | |
| /// If [isNullAware] is `true`, this is a `?.` access, otherwise it is a '.' | |
| /// access. | |
| /// | |
| /// This is used for the when the [text] property is known to be an instance | |
| /// member of [receiver]. Either because the [receiver] cannot be the prefix | |
| /// of a static access or if [isNullAware] is `true`. | |
| class InstanceAccessProto extends Proto { | |
| final Proto receiver; | |
| final String text; | |
| final bool isNullAware; | |
| InstanceAccessProto(this.receiver, this.text, {this.isNullAware = false}); | |
| @override | |
| Proto access(String? name) { | |
| if (name == null) { | |
| return this; | |
| } | |
| throw new UnimplementedError('$this.access'); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| if (typeArguments == null) { | |
| return this; | |
| } | |
| throw new UnimplementedError('$this.instantiate'); | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| if (arguments == null) { | |
| return this; | |
| } | |
| throw new UnimplementedError('$this.invoke'); | |
| } | |
| @override | |
| Expression toExpression() { | |
| return isNullAware | |
| ? new NullAwarePropertyGet(receiver.toExpression(), text) | |
| : new PropertyGet(receiver.toExpression(), text); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation(); | |
| @override | |
| String toString() => 'InstanceAccessProto($receiver,$text)'; | |
| @override | |
| Proto? resolve() => null; | |
| } | |
| /// The application of [typeArguments] on [receiver] which is known to be an | |
| /// expression. | |
| class ExpressionInstantiationProto extends Proto { | |
| final Proto receiver; | |
| final List<TypeAnnotation> typeArguments; | |
| ExpressionInstantiationProto(this.receiver, this.typeArguments); | |
| @override | |
| Proto access(String? name) { | |
| return name != null | |
| ? new ExpressionProto(new PropertyGet(receiver.toExpression(), name)) | |
| : this; | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| throw new UnimplementedError('InstanceInstantiationProto.instantiate'); | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments != null | |
| ? new ExpressionProto( | |
| new ImplicitInvocation( | |
| receiver.toExpression(), | |
| const [], | |
| arguments, | |
| ), | |
| ) | |
| : this; | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new Instantiation(receiver.toExpression(), typeArguments); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation(); | |
| @override | |
| String toString() => 'InstanceInstantiationProto($receiver,$typeArguments)'; | |
| @override | |
| Proto? resolve() => null; | |
| } | |
| /// The application of [typeArguments] and [arguments] on [receiver] which is | |
| /// known to be an expression. | |
| class InstanceInvocationProto extends Proto { | |
| final Proto receiver; | |
| final List<TypeAnnotation> typeArguments; | |
| final List<Argument> arguments; | |
| InstanceInvocationProto(this.receiver, this.typeArguments, this.arguments); | |
| @override | |
| Proto access(String? name) { | |
| throw new UnimplementedError('InstanceInvocationProto.access'); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| throw new UnimplementedError('InstanceInvocationProto.instantiate'); | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| throw new UnimplementedError('InstanceInvocationProto.invoke'); | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new ImplicitInvocation( | |
| receiver.toExpression(), | |
| typeArguments, | |
| arguments, | |
| ); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation(); | |
| @override | |
| String toString() => | |
| 'InstanceInvocationProto($receiver,$typeArguments,$arguments)'; | |
| @override | |
| Proto? resolve() => null; | |
| } | |
| /// The access of [text] on [receiver] when this is known to be an invalid | |
| /// construct. | |
| // TODO(johnniwinther): This is not valid for non-const expressions. Expand | |
| // this if used for non-const expressions. | |
| class InvalidAccessProto extends Proto { | |
| final Proto receiver; | |
| final String text; | |
| InvalidAccessProto(this.receiver, this.text); | |
| @override | |
| Proto access(String? name) { | |
| throw new UnimplementedError('InvalidAccessProto.access'); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| throw new UnimplementedError('InvalidAccessProto.instantiate'); | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| throw new UnimplementedError('InvalidAccessProto.invoke'); | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new InvalidExpression(); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation(); | |
| @override | |
| String toString() => 'InvalidAccessProto($receiver,$text)'; | |
| @override | |
| Proto? resolve() => null; | |
| } | |
| /// The application of [typeArguments] on [receiver] when this is known to be an | |
| /// invalid construct. | |
| // TODO(johnniwinther): This might not be valid for non-const expressions. | |
| // Expand this if used for non-const expressions. | |
| class InvalidInstantiationProto extends Proto { | |
| final Proto receiver; | |
| final List<TypeAnnotation> typeArguments; | |
| InvalidInstantiationProto(this.receiver, this.typeArguments); | |
| @override | |
| Proto access(String? name) { | |
| throw new UnimplementedError('InvalidInstantiationProto.access'); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| throw new UnimplementedError('InvalidInstantiationProto.instantiate'); | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| throw new UnimplementedError('InvalidInstantiationProto.invoke'); | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new InvalidExpression(); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation(); | |
| @override | |
| String toString() => 'InvalidInstantiationProto($receiver,$typeArguments)'; | |
| @override | |
| Proto? resolve() => null; | |
| } | |
| /// The application of [typeArguments] and [arguments] on [receiver] when this | |
| /// is known to be an invalid construct. | |
| // TODO(johnniwinther): This might not be valid for non-const expressions. | |
| // Expand this if used for non-const expressions. | |
| class InvalidInvocationProto extends Proto { | |
| final Proto receiver; | |
| final List<TypeAnnotation> typeArguments; | |
| final List<Argument> arguments; | |
| InvalidInvocationProto(this.receiver, this.typeArguments, this.arguments); | |
| @override | |
| Proto access(String? name) { | |
| throw new UnimplementedError('InvalidInvocationProto.access'); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| throw new UnimplementedError('InvalidInvocationProto.instantiate'); | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| throw new UnimplementedError('InvalidInvocationProto.invoke'); | |
| } | |
| @override | |
| Expression toExpression() { | |
| return new InvalidExpression(); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation(); | |
| @override | |
| String toString() => | |
| 'InvalidInvocationProto($receiver,$typeArguments,$arguments)'; | |
| @override | |
| Proto? resolve() => null; | |
| } | |
| /// An [expression] occurring as a [Proto]. | |
| class ExpressionProto extends Proto { | |
| final Expression expression; | |
| ExpressionProto(this.expression); | |
| @override | |
| Proto access(String? name) { | |
| return name != null ? new InstanceAccessProto(this, name) : this; | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| return typeArguments != null | |
| ? new ExpressionInstantiationProto(this, typeArguments) | |
| : this; | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments != null | |
| ? new ExpressionProto( | |
| new ImplicitInvocation(expression, const [], arguments), | |
| ) | |
| : this; | |
| } | |
| @override | |
| Expression toExpression() { | |
| return expression; | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation(); | |
| @override | |
| String toString() => 'ExpressionProto($expression)'; | |
| @override | |
| Proto? resolve() => null; | |
| } | |
| /// A reference to a [functionTypeParameter]. | |
| class FunctionTypeParameterProto extends Proto { | |
| final FunctionTypeParameter functionTypeParameter; | |
| FunctionTypeParameterProto(this.functionTypeParameter); | |
| @override | |
| Proto access(String? name) { | |
| return name == null ? this : new InvalidAccessProto(this, name); | |
| } | |
| @override | |
| Proto instantiate(List<TypeAnnotation>? typeArguments) { | |
| return typeArguments == null | |
| ? this | |
| : new ExpressionInstantiationProto(this, typeArguments); | |
| } | |
| @override | |
| Proto invoke(List<Argument>? arguments) { | |
| return arguments != null | |
| ? new ExpressionProto( | |
| new ImplicitInvocation(toExpression(), const [], arguments), | |
| ) | |
| : this; | |
| } | |
| @override | |
| Proto? resolve() => null; | |
| @override | |
| Expression toExpression() { | |
| return new TypeLiteral(toTypeAnnotation()); | |
| } | |
| @override | |
| TypeAnnotation toTypeAnnotation() { | |
| return new FunctionTypeParameterType(functionTypeParameter); | |
| } | |
| } |